2e0c192c84fbe4e48009bf40c1931201db11023f
[occt.git] / src / OpenGl / OpenGl_FrameBuffer.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2011-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <OpenGl_FrameBuffer.hxx>
16 #include <OpenGl_ArbFBO.hxx>
17
18 #include <Standard_Assert.hxx>
19 #include <TCollection_ExtendedString.hxx>
20
21 IMPLEMENT_STANDARD_HANDLE (OpenGl_FrameBuffer, OpenGl_Resource)
22 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameBuffer, OpenGl_Resource)
23
24 // =======================================================================
25 // function : OpenGl_FrameBuffer
26 // purpose  :
27 // =======================================================================
28 OpenGl_FrameBuffer::OpenGl_FrameBuffer (GLint theTextureFormat)
29 : myVPSizeX (0),
30   myVPSizeY (0),
31   myTextFormat (theTextureFormat),
32   myGlFBufferId (NO_FRAMEBUFFER),
33   myGlColorRBufferId (NO_RENDERBUFFER),
34   myGlDepthRBufferId (NO_RENDERBUFFER),
35   myIsOwnBuffer  (false),
36   myColorTexture (new OpenGl_Texture()),
37   myDepthStencilTexture (new OpenGl_Texture())
38 {
39   //
40 }
41
42 // =======================================================================
43 // function : ~OpenGl_FrameBuffer
44 // purpose  :
45 // =======================================================================
46 OpenGl_FrameBuffer::~OpenGl_FrameBuffer()
47 {
48   Release (NULL);
49 }
50
51 // =======================================================================
52 // function : Init
53 // purpose  :
54 // =======================================================================
55 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
56                                            const GLsizei   theViewportSizeX,
57                                            const GLsizei   theViewportSizeY)
58 {
59   if (theGlContext->arbFBO == NULL)
60   {
61     return Standard_False;
62   }
63
64   // clean up previous state
65   Release (theGlContext.operator->());
66
67   // setup viewport sizes as is
68   myVPSizeX = theViewportSizeX;
69   myVPSizeY = theViewportSizeY;
70
71   // Create the textures (will be used as color buffer and depth-stencil buffer)
72   if (!myColorTexture->Init (theGlContext, myTextFormat,
73                              GL_RGBA, GL_UNSIGNED_BYTE,
74                              myVPSizeX, myVPSizeY, Graphic3d_TOT_2D))
75   {
76     Release (theGlContext.operator->());
77     return Standard_False;
78   }
79
80   // extensions (GL_OES_packed_depth_stencil, GL_OES_depth_texture) + GL version might be used to determine supported formats
81   // instead of just trying to create such texture
82   if (!myDepthStencilTexture->Init (theGlContext, GL_DEPTH24_STENCIL8,
83                                     GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
84                                     myVPSizeX, myVPSizeY, Graphic3d_TOT_2D))
85   {
86     TCollection_ExtendedString aMsg = TCollection_ExtendedString()
87       + "Warning! Depth textures are not supported by hardware!";
88     theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
89                                GL_DEBUG_TYPE_PORTABILITY_ARB,
90                                0,
91                                GL_DEBUG_SEVERITY_HIGH_ARB,
92                                aMsg);
93
94     theGlContext->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
95     theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
96     theGlContext->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, myVPSizeX, myVPSizeY);
97     theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
98   }
99
100   // Build FBO and setup it as texture
101   theGlContext->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
102   theGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
103   theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
104                                                 GL_TEXTURE_2D, myColorTexture->TextureId(), 0);
105   if (myDepthStencilTexture->IsValid())
106   {
107   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
108     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
109                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
110   #else
111     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
112                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
113     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
114                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
115   #endif
116   }
117   else if (myGlDepthRBufferId != NO_RENDERBUFFER)
118   {
119     theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
120                                                      GL_RENDERBUFFER, myGlDepthRBufferId);
121   }
122   if (theGlContext->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
123   {
124     Release (theGlContext.operator->());
125     return Standard_False;
126   }
127
128   UnbindBuffer (theGlContext);
129   return Standard_True;
130 }
131
132 // =======================================================================
133 // function : Init
134 // purpose  :
135 // =======================================================================
136 Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
137                                                const GLsizei                 theViewportSizeX,
138                                                const GLsizei                 theViewportSizeY)
139 {
140   if (myVPSizeX == theViewportSizeX
141    && myVPSizeY == theViewportSizeY)
142   {
143     return IsValid();
144   }
145
146   return Init (theGlContext, theViewportSizeX, theViewportSizeY);
147 }
148
149 // =======================================================================
150 // function : InitWithRB
151 // purpose  :
152 // =======================================================================
153 Standard_Boolean OpenGl_FrameBuffer::InitWithRB (const Handle(OpenGl_Context)& theGlCtx,
154                                                  const GLsizei                 theViewportSizeX,
155                                                  const GLsizei                 theViewportSizeY,
156                                                  const GLuint                  theColorRBufferFromWindow)
157 {
158   if (theGlCtx->arbFBO == NULL)
159   {
160     return Standard_False;
161   }
162
163   // clean up previous state
164   Release (theGlCtx.operator->());
165
166   // setup viewport sizes as is
167   myVPSizeX = theViewportSizeX;
168   myVPSizeY = theViewportSizeY;
169
170   // Create the render-buffers
171   if (theColorRBufferFromWindow != NO_RENDERBUFFER)
172   {
173     myGlColorRBufferId = theColorRBufferFromWindow;
174   }
175   else
176   {
177     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlColorRBufferId);
178     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlColorRBufferId);
179     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA8, myVPSizeX, myVPSizeY);
180   }
181
182   theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
183   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
184   theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, myVPSizeX, myVPSizeY);
185
186   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
187
188   // create FBO
189   theGlCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
190   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
191   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
192                                                GL_RENDERBUFFER, myGlColorRBufferId);
193 #ifdef GL_DEPTH_STENCIL_ATTACHMENT
194   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
195                                                GL_RENDERBUFFER, myGlDepthRBufferId);
196 #else
197   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
198                                                GL_RENDERBUFFER, myGlDepthRBufferId);
199   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
200                                                GL_RENDERBUFFER, myGlDepthRBufferId);
201 #endif
202   if (theGlCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
203   {
204     UnbindBuffer (theGlCtx);
205     Release (theGlCtx.operator->());
206     return Standard_False;
207   }
208
209   UnbindBuffer (theGlCtx);
210   return Standard_True;
211 }
212
213 // =======================================================================
214 // function : InitWrapper
215 // purpose  :
216 // =======================================================================
217 Standard_Boolean OpenGl_FrameBuffer::InitWrapper (const Handle(OpenGl_Context)& theGlCtx)
218 {
219   if (theGlCtx->arbFBO == NULL)
220   {
221     return Standard_False;
222   }
223
224   // clean up previous state
225   Release (theGlCtx.operator->());
226
227   GLint anFbo = GLint(NO_FRAMEBUFFER);
228   ::glGetIntegerv (GL_FRAMEBUFFER_BINDING, &anFbo);
229   if (anFbo == GLint(NO_FRAMEBUFFER))
230   {
231     return Standard_False;
232   }
233
234   GLint aColorType = 0;
235   GLint aColorId   = 0;
236   GLint aDepthType = 0;
237   GLint aDepthId   = 0;
238   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aColorType);
239   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aDepthType);
240
241   myGlFBufferId = GLuint(anFbo);
242   myIsOwnBuffer = false;
243   if (aColorType == GL_RENDERBUFFER)
244   {
245     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aColorId);
246     myGlColorRBufferId = aColorId;
247   }
248   else if (aColorType != GL_NONE)
249   {
250     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), color attachment of unsupported type has been skipped!";
251     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
252                            GL_DEBUG_TYPE_ERROR_ARB,
253                            0,
254                            GL_DEBUG_SEVERITY_HIGH_ARB,
255                            aMsg);
256   }
257
258   if (aDepthType == GL_RENDERBUFFER)
259   {
260     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aDepthId);
261     myGlDepthRBufferId = aDepthId;
262   }
263   else if (aDepthType != GL_NONE)
264   {
265     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), depth attachment of unsupported type has been skipped!";
266     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
267                            GL_DEBUG_TYPE_ERROR_ARB,
268                            0,
269                            GL_DEBUG_SEVERITY_HIGH_ARB,
270                            aMsg);
271   }
272
273   // retrieve dimensions
274   GLuint aRBuffer = myGlColorRBufferId != NO_RENDERBUFFER ? myGlColorRBufferId : myGlDepthRBufferId;
275   if (aRBuffer != NO_RENDERBUFFER)
276   {
277     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, aRBuffer);
278     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myVPSizeX);
279     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myVPSizeY);
280     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
281   }
282
283   return aRBuffer != NO_RENDERBUFFER;
284 }
285
286 // =======================================================================
287 // function : Release
288 // purpose  :
289 // =======================================================================
290 void OpenGl_FrameBuffer::Release (OpenGl_Context* theGlCtx)
291 {
292   if (isValidFrameBuffer())
293   {
294     // application can not handle this case by exception - this is bug in code
295     Standard_ASSERT_RETURN (theGlCtx != NULL,
296       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
297     if (theGlCtx->IsValid()
298      && myIsOwnBuffer)
299     {
300       theGlCtx->arbFBO->glDeleteFramebuffers (1, &myGlFBufferId);
301       if (myGlColorRBufferId != NO_RENDERBUFFER)
302       {
303         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlColorRBufferId);
304       }
305       if (myGlDepthRBufferId != NO_RENDERBUFFER)
306       {
307         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlDepthRBufferId);
308       }
309     }
310     myGlFBufferId      = NO_FRAMEBUFFER;
311     myGlColorRBufferId = NO_RENDERBUFFER;
312     myGlDepthRBufferId = NO_RENDERBUFFER;
313     myIsOwnBuffer      = false;
314   }
315
316   myColorTexture->Release (theGlCtx);
317   myDepthStencilTexture->Release (theGlCtx);
318 }
319
320 // =======================================================================
321 // function : SetupViewport
322 // purpose  :
323 // =======================================================================
324 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& /*theGlCtx*/)
325 {
326   glViewport (0, 0, myVPSizeX, myVPSizeY);
327 }
328
329 // =======================================================================
330 // function : ChangeViewport
331 // purpose  :
332 // =======================================================================
333 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
334                                          const GLsizei theVPSizeY)
335 {
336   myVPSizeX = theVPSizeX;
337   myVPSizeY = theVPSizeY;
338 }
339
340 // =======================================================================
341 // function : BindBuffer
342 // purpose  :
343 // =======================================================================
344 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
345 {
346   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
347 }
348
349 // =======================================================================
350 // function : BindDrawBuffer
351 // purpose  :
352 // =======================================================================
353 void OpenGl_FrameBuffer::BindDrawBuffer (const Handle(OpenGl_Context)& theGlCtx)
354 {
355   theGlCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myGlFBufferId);
356 }
357
358 // =======================================================================
359 // function : BindReadBuffer
360 // purpose  :
361 // =======================================================================
362 void OpenGl_FrameBuffer::BindReadBuffer (const Handle(OpenGl_Context)& theGlCtx)
363 {
364   theGlCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, myGlFBufferId);
365 }
366
367 // =======================================================================
368 // function : UnbindBuffer
369 // purpose  :
370 // =======================================================================
371 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
372 {
373   if (!theGlCtx->DefaultFrameBuffer().IsNull()
374    &&  theGlCtx->DefaultFrameBuffer().operator->() != this)
375   {
376     theGlCtx->DefaultFrameBuffer()->BindBuffer (theGlCtx);
377   }
378   else
379   {
380     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
381   }
382 }