37bb42c39f9854d6ab54a9e738480a78f3dc6c2b
[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   // setup viewport sizes as is
66   myVPSizeX = theSizeX;
67   myVPSizeY = theSizeY;
68   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
69   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
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                              aSizeX, aSizeY, 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                                     aSizeX, aSizeY, 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, aSizeX, aSizeY);
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   {
144     return IsValid();
145   }
146
147   return Init (theGlContext, theViewportSizeX, theViewportSizeY);
148 }
149
150 // =======================================================================
151 // function : InitWithRB
152 // purpose  :
153 // =======================================================================
154 Standard_Boolean OpenGl_FrameBuffer::InitWithRB (const Handle(OpenGl_Context)& theGlCtx,
155                                                  const GLsizei                 theSizeX,
156                                                  const GLsizei                 theSizeY,
157                                                  const GLuint                  theColorRBufferFromWindow)
158 {
159   if (theGlCtx->arbFBO == NULL)
160   {
161     return Standard_False;
162   }
163
164   // clean up previous state
165   Release (theGlCtx.operator->());
166
167   // setup viewport sizes as is
168   myVPSizeX = theSizeX;
169   myVPSizeY = theSizeY;
170   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
171   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
172
173   // Create the render-buffers
174   if (theColorRBufferFromWindow != NO_RENDERBUFFER)
175   {
176     myGlColorRBufferId = theColorRBufferFromWindow;
177   }
178   else
179   {
180     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlColorRBufferId);
181     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlColorRBufferId);
182     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA8, aSizeX, aSizeY);
183   }
184
185   theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
186   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
187   theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, aSizeX, aSizeY);
188
189   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
190
191   // create FBO
192   theGlCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
193   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
194   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
195                                                GL_RENDERBUFFER, myGlColorRBufferId);
196 #ifdef GL_DEPTH_STENCIL_ATTACHMENT
197   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
198                                                GL_RENDERBUFFER, myGlDepthRBufferId);
199 #else
200   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
201                                                GL_RENDERBUFFER, myGlDepthRBufferId);
202   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
203                                                GL_RENDERBUFFER, myGlDepthRBufferId);
204 #endif
205   if (theGlCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
206   {
207     UnbindBuffer (theGlCtx);
208     Release (theGlCtx.operator->());
209     return Standard_False;
210   }
211
212   UnbindBuffer (theGlCtx);
213   return Standard_True;
214 }
215
216 // =======================================================================
217 // function : InitWrapper
218 // purpose  :
219 // =======================================================================
220 Standard_Boolean OpenGl_FrameBuffer::InitWrapper (const Handle(OpenGl_Context)& theGlCtx)
221 {
222   if (theGlCtx->arbFBO == NULL)
223   {
224     return Standard_False;
225   }
226
227   // clean up previous state
228   Release (theGlCtx.operator->());
229
230   GLint anFbo = GLint(NO_FRAMEBUFFER);
231   ::glGetIntegerv (GL_FRAMEBUFFER_BINDING, &anFbo);
232   if (anFbo == GLint(NO_FRAMEBUFFER))
233   {
234     return Standard_False;
235   }
236
237   GLint aColorType = 0;
238   GLint aColorId   = 0;
239   GLint aDepthType = 0;
240   GLint aDepthId   = 0;
241   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aColorType);
242   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aDepthType);
243
244   myGlFBufferId = GLuint(anFbo);
245   myIsOwnBuffer = false;
246   if (aColorType == GL_RENDERBUFFER)
247   {
248     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aColorId);
249     myGlColorRBufferId = aColorId;
250   }
251   else if (aColorType != GL_NONE)
252   {
253     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), color attachment of unsupported type has been skipped!";
254     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
255                            GL_DEBUG_TYPE_ERROR_ARB,
256                            0,
257                            GL_DEBUG_SEVERITY_HIGH_ARB,
258                            aMsg);
259   }
260
261   if (aDepthType == GL_RENDERBUFFER)
262   {
263     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aDepthId);
264     myGlDepthRBufferId = aDepthId;
265   }
266   else if (aDepthType != GL_NONE)
267   {
268     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), depth attachment of unsupported type has been skipped!";
269     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
270                            GL_DEBUG_TYPE_ERROR_ARB,
271                            0,
272                            GL_DEBUG_SEVERITY_HIGH_ARB,
273                            aMsg);
274   }
275
276   // retrieve dimensions
277   GLuint aRBuffer = myGlColorRBufferId != NO_RENDERBUFFER ? myGlColorRBufferId : myGlDepthRBufferId;
278   if (aRBuffer != NO_RENDERBUFFER)
279   {
280     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, aRBuffer);
281     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myVPSizeX);
282     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myVPSizeY);
283     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
284   }
285
286   return aRBuffer != NO_RENDERBUFFER;
287 }
288
289 // =======================================================================
290 // function : Release
291 // purpose  :
292 // =======================================================================
293 void OpenGl_FrameBuffer::Release (OpenGl_Context* theGlCtx)
294 {
295   if (isValidFrameBuffer())
296   {
297     // application can not handle this case by exception - this is bug in code
298     Standard_ASSERT_RETURN (theGlCtx != NULL,
299       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
300     if (theGlCtx->IsValid()
301      && myIsOwnBuffer)
302     {
303       theGlCtx->arbFBO->glDeleteFramebuffers (1, &myGlFBufferId);
304       if (myGlColorRBufferId != NO_RENDERBUFFER)
305       {
306         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlColorRBufferId);
307       }
308       if (myGlDepthRBufferId != NO_RENDERBUFFER)
309       {
310         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlDepthRBufferId);
311       }
312     }
313     myGlFBufferId      = NO_FRAMEBUFFER;
314     myGlColorRBufferId = NO_RENDERBUFFER;
315     myGlDepthRBufferId = NO_RENDERBUFFER;
316     myIsOwnBuffer      = false;
317   }
318
319   myColorTexture->Release (theGlCtx);
320   myDepthStencilTexture->Release (theGlCtx);
321 }
322
323 // =======================================================================
324 // function : SetupViewport
325 // purpose  :
326 // =======================================================================
327 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& /*theGlCtx*/)
328 {
329   glViewport (0, 0, myVPSizeX, myVPSizeY);
330 }
331
332 // =======================================================================
333 // function : ChangeViewport
334 // purpose  :
335 // =======================================================================
336 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
337                                          const GLsizei theVPSizeY)
338 {
339   myVPSizeX = theVPSizeX;
340   myVPSizeY = theVPSizeY;
341 }
342
343 // =======================================================================
344 // function : BindBuffer
345 // purpose  :
346 // =======================================================================
347 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
348 {
349   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
350 }
351
352 // =======================================================================
353 // function : BindDrawBuffer
354 // purpose  :
355 // =======================================================================
356 void OpenGl_FrameBuffer::BindDrawBuffer (const Handle(OpenGl_Context)& theGlCtx)
357 {
358   theGlCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myGlFBufferId);
359 }
360
361 // =======================================================================
362 // function : BindReadBuffer
363 // purpose  :
364 // =======================================================================
365 void OpenGl_FrameBuffer::BindReadBuffer (const Handle(OpenGl_Context)& theGlCtx)
366 {
367   theGlCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, myGlFBufferId);
368 }
369
370 // =======================================================================
371 // function : UnbindBuffer
372 // purpose  :
373 // =======================================================================
374 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
375 {
376   if (!theGlCtx->DefaultFrameBuffer().IsNull()
377    &&  theGlCtx->DefaultFrameBuffer().operator->() != this)
378   {
379     theGlCtx->DefaultFrameBuffer()->BindBuffer (theGlCtx);
380   }
381   else
382   {
383     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
384   }
385 }