0024228: TKOpenGL - destroy GL context at view close
[occt.git] / src / OpenGl / OpenGl_FrameBuffer.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2011-2012 OPEN CASCADE SAS
3 //
4 // The content of this file is subject to the Open CASCADE Technology Public
5 // License Version 6.5 (the "License"). You may not use the content of this file
6 // except in compliance with the License. Please obtain a copy of the License
7 // at http://www.opencascade.org and read it completely before using this file.
8 //
9 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
10 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
11 //
12 // The Original Code and all software distributed under the License is
13 // distributed on an "AS IS" basis, without warranty of any kind, and the
14 // Initial Developer hereby disclaims all such warranties, including without
15 // limitation, any warranties of merchantability, fitness for a particular
16 // purpose or non-infringement. Please see the License for the specific terms
17 // and conditions governing the rights and limitations under the License.
18
19 #include <OpenGl_FrameBuffer.hxx>
20
21 #include <Standard_Assert.hxx>
22
23 IMPLEMENT_STANDARD_HANDLE (OpenGl_FrameBuffer, OpenGl_Resource)
24 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameBuffer, OpenGl_Resource)
25
26 static inline bool isOddNumber (const GLsizei theNumber)
27 {
28   return theNumber & 0x01;
29 }
30
31 static inline GLsizei getEvenNumber (const GLsizei theNumber)
32 {
33   return isOddNumber (theNumber) ? (theNumber + 1) : theNumber;
34 }
35
36 //! Notice - 0 is not power of two here
37 static inline bool isPowerOfTwo (const GLsizei theNumber)
38 {
39         return !(theNumber & (theNumber - 1));
40 }
41
42 // =======================================================================
43 // function : OpenGl_FrameBuffer
44 // purpose  :
45 // =======================================================================
46 OpenGl_FrameBuffer::OpenGl_FrameBuffer (GLint theTextureFormat)
47 : mySizeX (0),
48   mySizeY (0),
49   myVPSizeX (0),
50   myVPSizeY (0),
51   myTextFormat (theTextureFormat),
52   myGlTextureId (NO_TEXTURE),
53   myGlFBufferId (NO_FRAMEBUFFER),
54   myGlDepthRBId (NO_RENDERBUFFER),
55   myGlStencilRBId (NO_RENDERBUFFER)
56 {
57   //
58 }
59
60 // =======================================================================
61 // function : ~OpenGl_FrameBuffer
62 // purpose  :
63 // =======================================================================
64 OpenGl_FrameBuffer::~OpenGl_FrameBuffer()
65 {
66   Release (NULL);
67 }
68
69 // =======================================================================
70 // function : Init
71 // purpose  :
72 // =======================================================================
73 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
74                                            const GLsizei   theViewportSizeX,
75                                            const GLsizei   theViewportSizeY,
76                                            const GLboolean toForcePowerOfTwo)
77 {
78   if (theGlContext->extFBO == NULL)
79   {
80     return Standard_False;
81   }
82
83   // clean up previous state
84   Release (theGlContext.operator->());
85
86   // upscale width/height if numbers are odd
87   if (toForcePowerOfTwo)
88   {
89     mySizeX = OpenGl_Context::GetPowerOfTwo (theViewportSizeX, theGlContext->MaxTextureSize());
90     mySizeY = OpenGl_Context::GetPowerOfTwo (theViewportSizeY, theGlContext->MaxTextureSize());
91   }
92   else
93   {
94     mySizeX = getEvenNumber (theViewportSizeX);
95     mySizeY = getEvenNumber (theViewportSizeY);
96   }
97
98   // setup viewport sizes as is
99   myVPSizeX = theViewportSizeX;
100   myVPSizeY = theViewportSizeY;
101
102   // Create the texture (will be used as color buffer)
103   if (!initTrashTexture (theGlContext))
104   {
105     if (!isPowerOfTwo (mySizeX) || !isPowerOfTwo (mySizeY))
106     {
107       return Init (theGlContext, theViewportSizeX, theViewportSizeY, GL_TRUE);
108     }
109     Release (theGlContext.operator->());
110     return Standard_False;
111   }
112
113   if (!theGlContext->extPDS)
114   {
115     // Create RenderBuffer to be used as depth buffer
116     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlDepthRBId);
117     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlDepthRBId);
118     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, mySizeX, mySizeY);
119
120     // Create RenderBuffer to be used as stencil buffer
121     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlStencilRBId);
122     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlStencilRBId);
123     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, mySizeX, mySizeY);
124   }
125   else
126   {
127     // Create combined depth stencil buffer
128     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlDepthRBId);
129     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlDepthRBId);
130     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, mySizeX, mySizeY);
131     myGlStencilRBId = myGlDepthRBId;
132   }
133
134   // Build FBO and setup it as texture
135   theGlContext->extFBO->glGenFramebuffersEXT (1, &myGlFBufferId);
136   theGlContext->extFBO->glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, myGlFBufferId);
137   glEnable (GL_TEXTURE_2D);
138   glBindTexture (GL_TEXTURE_2D, myGlTextureId);
139   theGlContext->extFBO->glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, myGlTextureId, 0);
140   theGlContext->extFBO->glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myGlDepthRBId);
141   theGlContext->extFBO->glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myGlStencilRBId);
142   if (theGlContext->extFBO->glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
143   {
144     if (!isPowerOfTwo (mySizeX) || !isPowerOfTwo (mySizeY))
145     {
146       return Init (theGlContext, theViewportSizeX, theViewportSizeY, GL_TRUE);
147     }
148     Release (theGlContext.operator->());
149     return Standard_False;
150   }
151
152   UnbindBuffer  (theGlContext);
153   UnbindTexture (theGlContext);
154   theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, NO_RENDERBUFFER);
155   return Standard_True;
156 }
157
158 // =======================================================================
159 // function : Release
160 // purpose  :
161 // =======================================================================
162 void OpenGl_FrameBuffer::Release (const OpenGl_Context* theGlCtx)
163 {
164   if (isValidDepthBuffer()
165    || isValidStencilBuffer()
166    || isValidTexture()
167    || isValidFrameBuffer())
168   {
169     // application can not handle this case by exception - this is bug in code
170     Standard_ASSERT_RETURN (theGlCtx != NULL,
171       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
172   }
173   if (isValidStencilBuffer())
174   {
175     if (theGlCtx->IsValid()
176      && myGlStencilRBId != myGlDepthRBId)
177     {
178       theGlCtx->extFBO->glDeleteRenderbuffersEXT (1, &myGlStencilRBId);
179     }
180     myGlStencilRBId = NO_RENDERBUFFER;
181   }
182   if (isValidDepthBuffer())
183   {
184     if (theGlCtx->IsValid())
185     {
186       theGlCtx->extFBO->glDeleteRenderbuffersEXT (1, &myGlDepthRBId);
187     }
188     myGlDepthRBId = NO_RENDERBUFFER;
189   }
190   if (isValidTexture())
191   {
192     if (theGlCtx->IsValid())
193     {
194       glDeleteTextures (1, &myGlTextureId);
195     }
196     myGlTextureId = NO_TEXTURE;
197   }
198   mySizeX = mySizeY = myVPSizeX = myVPSizeY = 0;
199   if (isValidFrameBuffer())
200   {
201     if (theGlCtx->IsValid())
202     {
203       theGlCtx->extFBO->glDeleteFramebuffersEXT (1, &myGlFBufferId);
204     }
205     myGlFBufferId = NO_FRAMEBUFFER;
206   }
207 }
208
209 // =======================================================================
210 // function : isProxySuccess
211 // purpose  :
212 // =======================================================================
213 Standard_Boolean OpenGl_FrameBuffer::isProxySuccess() const
214 {
215   // use proxy to check texture could be created or not
216   glTexImage2D (GL_PROXY_TEXTURE_2D,
217                 0,                // LOD number: 0 - base image level; n is the nth mipmap reduction image
218                 myTextFormat,     // internalformat
219                 mySizeX, mySizeY, 0,
220                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
221   GLint aTestParamX (0), aTestParamY (0);
222   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestParamX);
223   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestParamY);
224   return aTestParamX != 0 && aTestParamY != 0;
225 }
226
227 // =======================================================================
228 // function : initTrashTexture
229 // purpose  :
230 // =======================================================================
231 Standard_Boolean OpenGl_FrameBuffer::initTrashTexture (const Handle(OpenGl_Context)& theGlContext)
232 {
233   // Check texture size is fit dimension maximum
234   GLint aMaxTexDim = 2048;
235   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &aMaxTexDim);
236   if (mySizeX > aMaxTexDim || mySizeY > aMaxTexDim)
237   {
238     return Standard_False;
239   }
240
241   // generate new id
242   glEnable (GL_TEXTURE_2D);
243   if (!isValidTexture())
244   {
245     glGenTextures (1, &myGlTextureId); // Create The Texture
246   }
247   glBindTexture (GL_TEXTURE_2D, myGlTextureId);
248
249   // texture interpolation parameters - could be overridden later
250   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
251   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
252
253   if (!isProxySuccess())
254   {
255     Release (theGlContext.operator->());
256     return Standard_False;
257   }
258
259   glTexImage2D (GL_TEXTURE_2D,
260                 0,                // LOD number: 0 - base image level; n is the nth mipmap reduction image
261                 myTextFormat,     // internalformat
262                 mySizeX, mySizeY, 0,
263                 GL_RGBA, GL_UNSIGNED_BYTE, NULL); // NULL pointer supported from OpenGL 1.1
264   return Standard_True;
265 }
266
267 // =======================================================================
268 // function : SetupViewport
269 // purpose  :
270 // =======================================================================
271 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& /*theGlCtx*/)
272 {
273   glViewport (0, 0, myVPSizeX, myVPSizeY);
274 }
275
276 // =======================================================================
277 // function : ChangeViewport
278 // purpose  :
279 // =======================================================================
280 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
281                                          const GLsizei theVPSizeY)
282 {
283   myVPSizeX = theVPSizeX;
284   myVPSizeY = theVPSizeY;
285 }
286
287 // =======================================================================
288 // function : BindBuffer
289 // purpose  :
290 // =======================================================================
291 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
292 {
293   theGlCtx->extFBO->glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, myGlFBufferId);
294 }
295
296 // =======================================================================
297 // function : UnbindBuffer
298 // purpose  :
299 // =======================================================================
300 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
301 {
302   theGlCtx->extFBO->glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, NO_FRAMEBUFFER);
303 }
304
305 // =======================================================================
306 // function : BindTexture
307 // purpose  :
308 // =======================================================================
309 void OpenGl_FrameBuffer::BindTexture (const Handle(OpenGl_Context)& /*theGlCtx*/)
310 {
311   glEnable (GL_TEXTURE_2D); // needed only for fixed pipeline rendering
312   glBindTexture (GL_TEXTURE_2D, myGlTextureId);
313 }
314
315 // =======================================================================
316 // function : UnbindTexture
317 // purpose  :
318 // =======================================================================
319 void OpenGl_FrameBuffer::UnbindTexture (const Handle(OpenGl_Context)& /*theGlCtx*/)
320 {
321   glBindTexture (GL_TEXTURE_2D, NO_TEXTURE);
322   glDisable (GL_TEXTURE_2D); // needed only for fixed pipeline rendering
323 }