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