0026711: Visualization, TKOpenGl - support creation of multisampling off-screen FBOs
[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 namespace
22 {
23
24   //! Determine data type from texture sized format.
25   static bool getDepthDataFormat (GLint   theTextFormat,
26                                   GLenum& thePixelFormat,
27                                   GLenum& theDataType)
28   {
29     switch (theTextFormat)
30     {
31       case GL_DEPTH24_STENCIL8:
32       {
33         thePixelFormat = GL_DEPTH_STENCIL;
34         theDataType    = GL_UNSIGNED_INT_24_8;
35         return true;
36       }
37       case GL_DEPTH32F_STENCIL8:
38       {
39         thePixelFormat = GL_DEPTH_STENCIL;
40         theDataType    = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
41         return true;
42       }
43       case GL_DEPTH_COMPONENT16:
44       {
45         thePixelFormat = GL_DEPTH;
46         theDataType    = GL_UNSIGNED_SHORT;
47         return true;
48       }
49       case GL_DEPTH_COMPONENT24:
50       {
51         thePixelFormat = GL_DEPTH;
52         theDataType    = GL_UNSIGNED_INT;
53         return true;
54       }
55       case GL_DEPTH_COMPONENT32F:
56       {
57         thePixelFormat = GL_DEPTH;
58         theDataType    = GL_FLOAT;
59         return true;
60       }
61     }
62     return false;
63   }
64
65 }
66
67 // =======================================================================
68 // function : OpenGl_FrameBuffer
69 // purpose  :
70 // =======================================================================
71 OpenGl_FrameBuffer::OpenGl_FrameBuffer()
72 : myVPSizeX (0),
73   myVPSizeY (0),
74   myNbSamples (0),
75   myColorFormat (GL_RGBA8),
76   myDepthFormat (GL_DEPTH24_STENCIL8),
77   myGlFBufferId (NO_FRAMEBUFFER),
78   myGlColorRBufferId (NO_RENDERBUFFER),
79   myGlDepthRBufferId (NO_RENDERBUFFER),
80   myIsOwnBuffer  (false),
81   myColorTexture (new OpenGl_Texture()),
82   myDepthStencilTexture (new OpenGl_Texture())
83 {
84   //
85 }
86
87 // =======================================================================
88 // function : ~OpenGl_FrameBuffer
89 // purpose  :
90 // =======================================================================
91 OpenGl_FrameBuffer::~OpenGl_FrameBuffer()
92 {
93   Release (NULL);
94 }
95
96 // =======================================================================
97 // function : Init
98 // purpose  :
99 // =======================================================================
100 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
101                                            const GLsizei   theSizeX,
102                                            const GLsizei   theSizeY,
103                                            const GLint     theColorFormat,
104                                            const GLint     theDepthFormat,
105                                            const GLsizei   theNbSamples)
106 {
107   myColorFormat = theColorFormat;
108   myDepthFormat = theDepthFormat;
109   myNbSamples   = theNbSamples;
110   if (theGlContext->arbFBO == NULL)
111   {
112     return Standard_False;
113   }
114
115   // clean up previous state
116   Release (theGlContext.operator->());
117   if (myColorFormat == 0
118    && myDepthFormat == 0)
119   {
120     return Standard_False;
121   }
122
123   myIsOwnBuffer = true;
124
125   // setup viewport sizes as is
126   myVPSizeX = theSizeX;
127   myVPSizeY = theSizeY;
128   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
129   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
130
131   // Create the textures (will be used as color buffer and depth-stencil buffer)
132   if (theNbSamples != 0)
133   {
134     if (myColorFormat != 0
135     && !myColorTexture       ->Init2DMultisample (theGlContext, theNbSamples, myColorFormat, aSizeX, aSizeY))
136     {
137       Release (theGlContext.operator->());
138       return Standard_False;
139     }
140     if (myDepthFormat != 0
141     && !myDepthStencilTexture->Init2DMultisample (theGlContext, theNbSamples, myDepthFormat, aSizeX, aSizeY))
142     {
143       Release (theGlContext.operator->());
144       return Standard_False;
145     }
146   }
147   else
148   {
149     if (myColorFormat != 0
150     && !myColorTexture->Init (theGlContext, myColorFormat,
151                               GL_RGBA, GL_UNSIGNED_BYTE,
152                               aSizeX, aSizeY, Graphic3d_TOT_2D))
153     {
154       Release (theGlContext.operator->());
155       return Standard_False;
156     }
157
158     // extensions (GL_OES_packed_depth_stencil, GL_OES_depth_texture) + GL version might be used to determine supported formats
159     // instead of just trying to create such texture
160     GLenum aPixelFormat = 0;
161     GLenum aDataType    = 0;
162     if (myDepthFormat != 0
163     &&  getDepthDataFormat (myDepthFormat, aPixelFormat, aDataType)
164     && !myDepthStencilTexture->Init (theGlContext, myDepthFormat,
165                                       aPixelFormat, aDataType,
166                                       aSizeX, aSizeY, Graphic3d_TOT_2D))
167     {
168       TCollection_ExtendedString aMsg = TCollection_ExtendedString()
169         + "Warning! Depth textures are not supported by hardware!";
170       theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
171                                  GL_DEBUG_TYPE_PORTABILITY_ARB,
172                                  0,
173                                  GL_DEBUG_SEVERITY_HIGH_ARB,
174                                  aMsg);
175
176       theGlContext->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
177       theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
178       theGlContext->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, aSizeX, aSizeY);
179       theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
180     }
181   }
182
183   // Build FBO and setup it as texture
184   theGlContext->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
185   theGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
186   if (myColorTexture->IsValid())
187   {
188     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
189                                                   myColorTexture->GetTarget(), myColorTexture->TextureId(), 0);
190   }
191   if (myDepthStencilTexture->IsValid())
192   {
193   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
194     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
195                                                   myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
196   #else
197     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
198                                                   myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
199     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
200                                                   myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
201   #endif
202   }
203   else if (myGlDepthRBufferId != NO_RENDERBUFFER)
204   {
205     theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
206                                                      GL_RENDERBUFFER, myGlDepthRBufferId);
207   }
208   if (theGlContext->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
209   {
210     Release (theGlContext.operator->());
211     return Standard_False;
212   }
213
214   UnbindBuffer (theGlContext);
215   return Standard_True;
216 }
217
218 // =======================================================================
219 // function : Init
220 // purpose  :
221 // =======================================================================
222 Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
223                                                const GLsizei                 theViewportSizeX,
224                                                const GLsizei                 theViewportSizeY,
225                                                const GLint                   theColorFormat,
226                                                const GLint                   theDepthFormat,
227                                                const GLsizei                 theNbSamples)
228 {
229   if (myVPSizeX     == theViewportSizeX
230    && myVPSizeY     == theViewportSizeY
231    && myColorFormat == theColorFormat
232    && myDepthFormat == theDepthFormat
233    && myNbSamples   == theNbSamples)
234   {
235     return IsValid();
236   }
237
238   return Init (theGlContext, theViewportSizeX, theViewportSizeY, theColorFormat, theDepthFormat, theNbSamples);
239 }
240
241 // =======================================================================
242 // function : InitWithRB
243 // purpose  :
244 // =======================================================================
245 Standard_Boolean OpenGl_FrameBuffer::InitWithRB (const Handle(OpenGl_Context)& theGlCtx,
246                                                  const GLsizei                 theSizeX,
247                                                  const GLsizei                 theSizeY,
248                                                  const GLint                   theColorFormat,
249                                                  const GLint                   theDepthFormat,
250                                                  const GLuint                  theColorRBufferFromWindow)
251 {
252   myColorFormat = theColorFormat;
253   myDepthFormat = theDepthFormat;
254   myNbSamples   = 0;
255   if (theGlCtx->arbFBO == NULL)
256   {
257     return Standard_False;
258   }
259
260   // clean up previous state
261   Release (theGlCtx.operator->());
262
263   myIsOwnBuffer = true;
264
265   // setup viewport sizes as is
266   myVPSizeX = theSizeX;
267   myVPSizeY = theSizeY;
268   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
269   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
270
271   // Create the render-buffers
272   if (theColorRBufferFromWindow != NO_RENDERBUFFER)
273   {
274     myGlColorRBufferId = theColorRBufferFromWindow;
275   }
276   else if (myColorFormat != 0)
277   {
278     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlColorRBufferId);
279     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlColorRBufferId);
280     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, myColorFormat, aSizeX, aSizeY);
281   }
282
283   if (myDepthFormat != 0)
284   {
285     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
286     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
287     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, myDepthFormat, aSizeX, aSizeY);
288     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
289   }
290
291   // create FBO
292   theGlCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
293   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
294   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
295                                                GL_RENDERBUFFER, myGlColorRBufferId);
296   if (myGlDepthRBufferId != NO_RENDERBUFFER)
297   {
298   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
299     theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
300                                                  GL_RENDERBUFFER, myGlDepthRBufferId);
301   #else
302     theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
303                                                  GL_RENDERBUFFER, myGlDepthRBufferId);
304     theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
305                                                  GL_RENDERBUFFER, myGlDepthRBufferId);
306   #endif
307   }
308   if (theGlCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
309   {
310     UnbindBuffer (theGlCtx);
311     Release (theGlCtx.operator->());
312     return Standard_False;
313   }
314
315   UnbindBuffer (theGlCtx);
316   return Standard_True;
317 }
318
319 // =======================================================================
320 // function : InitWrapper
321 // purpose  :
322 // =======================================================================
323 Standard_Boolean OpenGl_FrameBuffer::InitWrapper (const Handle(OpenGl_Context)& theGlCtx)
324 {
325   myNbSamples = 0;
326   if (theGlCtx->arbFBO == NULL)
327   {
328     return Standard_False;
329   }
330
331   // clean up previous state
332   Release (theGlCtx.operator->());
333
334   GLint anFbo = GLint(NO_FRAMEBUFFER);
335   ::glGetIntegerv (GL_FRAMEBUFFER_BINDING, &anFbo);
336   if (anFbo == GLint(NO_FRAMEBUFFER))
337   {
338     return Standard_False;
339   }
340
341   GLint aColorType = 0;
342   GLint aColorId   = 0;
343   GLint aDepthType = 0;
344   GLint aDepthId   = 0;
345   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aColorType);
346   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aDepthType);
347
348   myGlFBufferId = GLuint(anFbo);
349   myIsOwnBuffer = false;
350   if (aColorType == GL_RENDERBUFFER)
351   {
352     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aColorId);
353     myGlColorRBufferId = aColorId;
354   }
355   else if (aColorType != GL_NONE)
356   {
357     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), color attachment of unsupported type has been skipped!";
358     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
359                            GL_DEBUG_TYPE_ERROR_ARB,
360                            0,
361                            GL_DEBUG_SEVERITY_HIGH_ARB,
362                            aMsg);
363   }
364
365   if (aDepthType == GL_RENDERBUFFER)
366   {
367     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aDepthId);
368     myGlDepthRBufferId = aDepthId;
369   }
370   else if (aDepthType != GL_NONE)
371   {
372     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), depth attachment of unsupported type has been skipped!";
373     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
374                            GL_DEBUG_TYPE_ERROR_ARB,
375                            0,
376                            GL_DEBUG_SEVERITY_HIGH_ARB,
377                            aMsg);
378   }
379
380   // retrieve dimensions
381   GLuint aRBuffer = myGlColorRBufferId != NO_RENDERBUFFER ? myGlColorRBufferId : myGlDepthRBufferId;
382   if (aRBuffer != NO_RENDERBUFFER)
383   {
384     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, aRBuffer);
385     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myVPSizeX);
386     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myVPSizeY);
387     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
388   }
389
390   return aRBuffer != NO_RENDERBUFFER;
391 }
392
393 // =======================================================================
394 // function : Release
395 // purpose  :
396 // =======================================================================
397 void OpenGl_FrameBuffer::Release (OpenGl_Context* theGlCtx)
398 {
399   if (isValidFrameBuffer())
400   {
401     // application can not handle this case by exception - this is bug in code
402     Standard_ASSERT_RETURN (theGlCtx != NULL,
403       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
404     if (theGlCtx->IsValid()
405      && myIsOwnBuffer)
406     {
407       theGlCtx->arbFBO->glDeleteFramebuffers (1, &myGlFBufferId);
408       if (myGlColorRBufferId != NO_RENDERBUFFER)
409       {
410         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlColorRBufferId);
411       }
412       if (myGlDepthRBufferId != NO_RENDERBUFFER)
413       {
414         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlDepthRBufferId);
415       }
416     }
417     myGlFBufferId      = NO_FRAMEBUFFER;
418     myGlColorRBufferId = NO_RENDERBUFFER;
419     myGlDepthRBufferId = NO_RENDERBUFFER;
420     myIsOwnBuffer      = false;
421   }
422
423   myColorTexture->Release (theGlCtx);
424   myDepthStencilTexture->Release (theGlCtx);
425
426   myVPSizeX = 0;
427   myVPSizeY = 0;
428 }
429
430 // =======================================================================
431 // function : SetupViewport
432 // purpose  :
433 // =======================================================================
434 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& /*theGlCtx*/)
435 {
436   glViewport (0, 0, myVPSizeX, myVPSizeY);
437 }
438
439 // =======================================================================
440 // function : ChangeViewport
441 // purpose  :
442 // =======================================================================
443 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
444                                          const GLsizei theVPSizeY)
445 {
446   myVPSizeX = theVPSizeX;
447   myVPSizeY = theVPSizeY;
448 }
449
450 // =======================================================================
451 // function : BindBuffer
452 // purpose  :
453 // =======================================================================
454 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
455 {
456   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
457 }
458
459 // =======================================================================
460 // function : BindDrawBuffer
461 // purpose  :
462 // =======================================================================
463 void OpenGl_FrameBuffer::BindDrawBuffer (const Handle(OpenGl_Context)& theGlCtx)
464 {
465   theGlCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myGlFBufferId);
466 }
467
468 // =======================================================================
469 // function : BindReadBuffer
470 // purpose  :
471 // =======================================================================
472 void OpenGl_FrameBuffer::BindReadBuffer (const Handle(OpenGl_Context)& theGlCtx)
473 {
474   theGlCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, myGlFBufferId);
475 }
476
477 // =======================================================================
478 // function : UnbindBuffer
479 // purpose  :
480 // =======================================================================
481 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
482 {
483   if (!theGlCtx->DefaultFrameBuffer().IsNull()
484    &&  theGlCtx->DefaultFrameBuffer().operator->() != this)
485   {
486     theGlCtx->DefaultFrameBuffer()->BindBuffer (theGlCtx);
487   }
488   else
489   {
490     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
491   }
492 }