0030700: Visualization, TKOpenGl - support PBR Metallic-Roughness shading model
[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 <NCollection_AlignedAllocator.hxx>
19 #include <Standard_Assert.hxx>
20 #include <TCollection_ExtendedString.hxx>
21
22 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameBuffer,OpenGl_Resource)
23
24 namespace
25 {
26   //! Checks whether two format arrays are equal or not.
27   static bool operator== (const OpenGl_ColorFormats& theFmt1,
28                           const OpenGl_ColorFormats& theFmt2)
29   {
30     if (theFmt1.Length() != theFmt2.Length())
31       return false;
32     OpenGl_ColorFormats::Iterator anIt1 (theFmt1);
33     OpenGl_ColorFormats::Iterator anIt2 (theFmt1);
34     for (; anIt1.More(); anIt1.Next(), anIt2.Next())
35     {
36       if (anIt1.Value() != anIt2.Value())
37         return false;
38     }
39     return true;
40   }
41
42   //! Return TRUE if GL_DEPTH_STENCIL_ATTACHMENT can be used.
43   static bool hasDepthStencilAttach (const Handle(OpenGl_Context)& theCtx)
44   {
45   #ifdef __EMSCRIPTEN__
46     // supported since WebGL 2.0,
47     // while WebGL 1.0 + GL_WEBGL_depth_texture needs GL_DEPTH_STENCIL_ATTACHMENT
48     // and NOT separate GL_DEPTH_ATTACHMENT+GL_STENCIL_ATTACHMENT calls which is different to OpenGL ES 2.0 + extension
49     return theCtx->IsGlGreaterEqual (3, 0) || theCtx->extPDS;
50   #elif defined(GL_ES_VERSION_2_0)
51     // supported since OpenGL ES 3.0,
52     // while OpenGL ES 2.0 + GL_EXT_packed_depth_stencil needs separate GL_DEPTH_ATTACHMENT+GL_STENCIL_ATTACHMENT calls
53     return theCtx->IsGlGreaterEqual (3, 0);
54   #else
55     // available on desktop since OpenGL 3.0
56     // or OpenGL 2.0 + GL_ARB_framebuffer_object (GL_EXT_framebuffer_object is unsupported by OCCT)
57     (void )theCtx;
58     return true;
59   #endif
60   }
61 }
62
63 // =======================================================================
64 // function : OpenGl_FrameBuffer
65 // purpose  :
66 // =======================================================================
67 OpenGl_FrameBuffer::OpenGl_FrameBuffer()
68 : myInitVPSizeX (0),
69   myInitVPSizeY (0),
70   myVPSizeX (0),
71   myVPSizeY (0),
72   myNbSamples (0),
73   myDepthFormat (GL_DEPTH24_STENCIL8),
74   myGlFBufferId (NO_FRAMEBUFFER),
75   myGlColorRBufferId (NO_RENDERBUFFER),
76   myGlDepthRBufferId (NO_RENDERBUFFER),
77   myIsOwnBuffer  (false),
78   myIsOwnDepth  (false),
79   myDepthStencilTexture (new OpenGl_Texture())
80 {
81   myColorFormats.Append (GL_RGBA8);
82   myColorTextures.Append (new OpenGl_Texture());
83 }
84
85 // =======================================================================
86 // function : ~OpenGl_FrameBuffer
87 // purpose  :
88 // =======================================================================
89 OpenGl_FrameBuffer::~OpenGl_FrameBuffer()
90 {
91   Release (NULL);
92 }
93
94 // =======================================================================
95 // function : Init
96 // purpose  :
97 // =======================================================================
98 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
99                                            const GLsizei                 theSizeX,
100                                            const GLsizei                 theSizeY,
101                                            const GLint                   theColorFormat,
102                                            const GLint                   theDepthFormat,
103                                            const GLsizei                 theNbSamples)
104 {
105   OpenGl_ColorFormats aColorFormats;
106
107   aColorFormats.Append (theColorFormat);
108
109   return Init (theGlContext, theSizeX, theSizeY, aColorFormats, theDepthFormat, theNbSamples);
110 }
111
112 // =======================================================================
113 // function : Init
114 // purpose  :
115 // =======================================================================
116 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
117                                            const GLsizei                 theSizeX,
118                                            const GLsizei                 theSizeY,
119                                            const OpenGl_ColorFormats&    theColorFormats,
120                                            const Handle(OpenGl_Texture)& theDepthStencilTexture,
121                                            const GLsizei                 theNbSamples)
122 {
123   myColorFormats = theColorFormats;
124
125   OpenGl_TextureArray aTextures (myColorTextures);
126   if (!myColorTextures.IsEmpty())
127   {
128     for (OpenGl_TextureArray::Iterator aTextureIt (myColorTextures); aTextureIt.More(); aTextureIt.Next())
129     {
130       aTextureIt.Value()->Release (theGlContext.operator->());
131     }
132     myColorTextures.Clear();
133   }
134   for (Standard_Integer aLength = 0; aLength < myColorFormats.Length(); ++aLength)
135   {
136     myColorTextures.Append (aLength < aTextures.Length() ? aTextures.Value (aLength) : new OpenGl_Texture());
137   }
138
139   myDepthFormat = theDepthStencilTexture->GetFormat();
140   myNbSamples   = theNbSamples;
141   if (theGlContext->arbFBO == NULL)
142   {
143     return Standard_False;
144   }
145
146   // clean up previous state
147   Release (theGlContext.operator->());
148   if (myColorFormats.IsEmpty()
149    && myDepthFormat == 0)
150   {
151     return Standard_False;
152   }
153
154   myDepthStencilTexture = theDepthStencilTexture;
155   myIsOwnDepth  = false;
156   myIsOwnBuffer = true;
157
158   // setup viewport sizes as is
159   myVPSizeX = theSizeX;
160   myVPSizeY = theSizeY;
161   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
162   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
163
164   // Create the textures (will be used as color buffer and depth-stencil buffer)
165   if (theNbSamples != 0)
166   {
167     for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
168     {
169       const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
170       const GLint                   aColorFormat  = myColorFormats  (aColorBufferIdx);
171       if (aColorFormat != 0
172       && !aColorTexture->Init2DMultisample (theGlContext, theNbSamples,
173                                             aColorFormat, aSizeX, aSizeY))
174       {
175         Release (theGlContext.get());
176         return Standard_False;
177       }
178     }
179   }
180   else
181   {
182     for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
183     {
184       const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
185       const GLint                   aColorFormat  = myColorFormats  (aColorBufferIdx);
186       const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindSizedFormat (theGlContext, aColorFormat);
187       if (aFormat.IsValid()
188       && !aColorTexture->Init (theGlContext, aFormat, Graphic3d_Vec2i (aSizeX, aSizeY), Graphic3d_TOT_2D))
189       {
190         Release (theGlContext.get());
191         return Standard_False;
192       }
193     }
194   }
195
196   // Build FBO and setup it as texture
197   theGlContext->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
198   theGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
199
200   for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
201   {
202     const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
203     if (aColorTexture->IsValid())
204     {
205       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + aColorBufferIdx,
206                                                     aColorTexture->GetTarget(), aColorTexture->TextureId(), 0);
207     }
208   }
209   if (myDepthStencilTexture->IsValid())
210   {
211     if (hasDepthStencilAttach (theGlContext))
212     {
213       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
214                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
215     }
216     else
217     {
218       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
219                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
220       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
221                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
222     }
223   }
224   if (theGlContext->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
225   {
226     Release (theGlContext.operator->());
227     return Standard_False;
228   }
229
230   UnbindBuffer (theGlContext);
231   return Standard_True;
232 }
233
234 // =======================================================================
235 // function : Init
236 // purpose  :
237 // =======================================================================
238 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
239                                            const GLsizei                 theSizeX,
240                                            const GLsizei                 theSizeY,
241                                            const OpenGl_ColorFormats&    theColorFormats,
242                                            const GLint                   theDepthFormat,
243                                            const GLsizei                 theNbSamples)
244 {
245   myColorFormats = theColorFormats;
246
247   OpenGl_TextureArray aTextures (myColorTextures);
248   if (!myColorTextures.IsEmpty())
249   {
250     for (OpenGl_TextureArray::Iterator aTextureIt (myColorTextures); aTextureIt.More(); aTextureIt.Next())
251     {
252       aTextureIt.Value()->Release (theGlContext.operator->());
253     }
254     myColorTextures.Clear();
255   }
256   for (Standard_Integer aLength = 0; aLength < myColorFormats.Length(); ++aLength)
257   {
258     myColorTextures.Append (aLength < aTextures.Length() ? aTextures.Value (aLength) : new OpenGl_Texture());
259   }
260
261   myDepthFormat = theDepthFormat;
262   myNbSamples   = theNbSamples;
263   myInitVPSizeX = theSizeX;
264   myInitVPSizeY = theSizeY;
265   if (theGlContext->arbFBO == NULL)
266   {
267     return Standard_False;
268   }
269
270   // clean up previous state
271   Release (theGlContext.operator->());
272   if (myColorFormats.IsEmpty()
273    && myDepthFormat == 0)
274   {
275     return Standard_False;
276   }
277
278   myIsOwnBuffer = true;
279   myIsOwnDepth  = true;
280
281   // setup viewport sizes as is
282   myVPSizeX = theSizeX;
283   myVPSizeY = theSizeY;
284   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
285   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
286   bool hasStencilRB = false;
287
288   // Create the textures (will be used as color buffer and depth-stencil buffer)
289   if (theNbSamples != 0)
290   {
291     for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
292     {
293       const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
294       const GLint                   aColorFormat  = myColorFormats  (aColorBufferIdx);
295       if (aColorFormat != 0
296       && !aColorTexture->Init2DMultisample (theGlContext, theNbSamples, aColorFormat, aSizeX, aSizeY))
297       {
298         Release (theGlContext.operator->());
299         return Standard_False;
300       }
301     }
302     if (myDepthFormat != 0
303     && !myDepthStencilTexture->Init2DMultisample (theGlContext, theNbSamples, myDepthFormat, aSizeX, aSizeY))
304     {
305       Release (theGlContext.operator->());
306       return Standard_False;
307     }
308   }
309   else
310   {
311     for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
312     {
313       const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
314       const GLint                   aColorFormat  = myColorFormats  (aColorBufferIdx);
315       const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindSizedFormat (theGlContext, aColorFormat);
316       if (aFormat.IsValid()
317       && !aColorTexture->Init (theGlContext, aFormat, Graphic3d_Vec2i (aSizeX, aSizeY), Graphic3d_TOT_2D))
318       {
319         Release (theGlContext.operator->());
320         return Standard_False;
321       }
322     }
323
324     // extensions (GL_OES_packed_depth_stencil, GL_OES_depth_texture) + GL version might be used to determine supported formats
325     // instead of just trying to create such texture
326     const OpenGl_TextureFormat aDepthFormat = OpenGl_TextureFormat::FindSizedFormat (theGlContext, myDepthFormat);
327     if (aDepthFormat.IsValid()
328     && !myDepthStencilTexture->Init (theGlContext, aDepthFormat, Graphic3d_Vec2i (aSizeX, aSizeY), Graphic3d_TOT_2D))
329     {
330       theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
331                                  "Warning! Depth textures are not supported by hardware!");
332
333       hasStencilRB = aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL
334                   && theGlContext->extPDS;
335       GLint aDepthStencilFormat = hasStencilRB
336                                 ? GL_DEPTH24_STENCIL8
337                                 : GL_DEPTH_COMPONENT16;
338
339       theGlContext->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
340       theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
341       theGlContext->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, aDepthStencilFormat, aSizeX, aSizeY);
342       theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
343     }
344   }
345
346   // Build FBO and setup it as texture
347   theGlContext->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
348   theGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
349   for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
350   {
351     const Handle(OpenGl_Texture)& aColorTexture = myColorTextures (aColorBufferIdx);
352     if (aColorTexture->IsValid())
353     {
354       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + aColorBufferIdx,
355                                                     aColorTexture->GetTarget(), aColorTexture->TextureId(), 0);
356     }
357   }
358
359   if (myDepthStencilTexture->IsValid())
360   {
361     if (hasDepthStencilAttach (theGlContext))
362     {
363       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
364                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
365     }
366     else
367     {
368       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
369                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
370       theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
371                                                     myDepthStencilTexture->GetTarget(), myDepthStencilTexture->TextureId(), 0);
372     }
373   }
374   else if (myGlDepthRBufferId != NO_RENDERBUFFER)
375   {
376     if (hasDepthStencilAttach (theGlContext) && hasStencilRB)
377     {
378       theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
379                                                        GL_RENDERBUFFER, myGlDepthRBufferId);
380     }
381     else
382     {
383       theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
384                                                        GL_RENDERBUFFER, myGlDepthRBufferId);
385       if (hasStencilRB)
386       {
387         theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
388                                                          GL_RENDERBUFFER, myGlDepthRBufferId);
389       }
390     }
391   }
392   if (theGlContext->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
393   {
394     Release (theGlContext.operator->());
395     return Standard_False;
396   }
397
398   UnbindBuffer (theGlContext);
399   return Standard_True;
400 }
401
402 // =======================================================================
403 // function : InitLazy
404 // purpose  :
405 // =======================================================================
406 Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
407                                                const GLsizei                 theViewportSizeX,
408                                                const GLsizei                 theViewportSizeY,
409                                                const GLint                   theColorFormat,
410                                                const GLint                   theDepthFormat,
411                                                const GLsizei                 theNbSamples)
412 {
413   OpenGl_ColorFormats aColorFormats;
414
415   aColorFormats.Append (theColorFormat);
416
417   return InitLazy (theGlContext, theViewportSizeX, theViewportSizeY, aColorFormats, theDepthFormat, theNbSamples);
418 }
419
420 // =======================================================================
421 // function : InitLazy
422 // purpose  :
423 // =======================================================================
424 Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
425                                                const GLsizei                 theViewportSizeX,
426                                                const GLsizei                 theViewportSizeY,
427                                                const OpenGl_ColorFormats&    theColorFormats,
428                                                const GLint                   theDepthFormat,
429                                                const GLsizei                 theNbSamples)
430 {
431   if (myVPSizeX      == theViewportSizeX
432    && myVPSizeY      == theViewportSizeY
433    && myColorFormats == theColorFormats
434    && myDepthFormat  == theDepthFormat
435    && myNbSamples    == theNbSamples)
436   {
437     return IsValid();
438   }
439
440   return Init (theGlContext, theViewportSizeX, theViewportSizeY, theColorFormats, theDepthFormat, theNbSamples);
441 }
442
443 // =======================================================================
444 // function : InitWithRB
445 // purpose  :
446 // =======================================================================
447 Standard_Boolean OpenGl_FrameBuffer::InitWithRB (const Handle(OpenGl_Context)& theGlCtx,
448                                                  const GLsizei                 theSizeX,
449                                                  const GLsizei                 theSizeY,
450                                                  const GLint                   theColorFormat,
451                                                  const GLint                   theDepthFormat,
452                                                  const GLuint                  theColorRBufferFromWindow)
453 {
454   myColorFormats.Clear();
455   myColorFormats.Append (theColorFormat);
456   if (!myColorTextures.IsEmpty())
457   {
458     Handle(OpenGl_Texture) aTexutre = myColorTextures.First();
459     for (OpenGl_TextureArray::Iterator aTextureIt (myColorTextures); aTextureIt.More(); aTextureIt.Next())
460     {
461       aTextureIt.Value()->Release (theGlCtx.operator->());
462     }
463     myColorTextures.Clear();
464     myColorTextures.Append (aTexutre);
465   }
466
467   myDepthFormat = theDepthFormat;
468   myNbSamples   = 0;
469   myInitVPSizeX = theSizeX;
470   myInitVPSizeY = theSizeY;
471   if (theGlCtx->arbFBO == NULL)
472   {
473     return Standard_False;
474   }
475
476   // clean up previous state
477   Release (theGlCtx.operator->());
478
479   myIsOwnBuffer = true;
480   myIsOwnDepth  = true;
481
482   // setup viewport sizes as is
483   myVPSizeX = theSizeX;
484   myVPSizeY = theSizeY;
485   const Standard_Integer aSizeX = theSizeX > 0 ? theSizeX : 2;
486   const Standard_Integer aSizeY = theSizeY > 0 ? theSizeY : 2;
487
488   // Create the render-buffers
489   if (theColorRBufferFromWindow != NO_RENDERBUFFER)
490   {
491     myGlColorRBufferId = theColorRBufferFromWindow;
492   }
493   else if (theColorFormat != 0)
494   {
495     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlColorRBufferId);
496     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlColorRBufferId);
497     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, theColorFormat, aSizeX, aSizeY);
498   }
499
500   bool hasStencilRB = false;
501   if (myDepthFormat != 0)
502   {
503     const OpenGl_TextureFormat aDepthFormat = OpenGl_TextureFormat::FindSizedFormat (theGlCtx, myDepthFormat);
504     hasStencilRB = aDepthFormat.PixelFormat() == GL_DEPTH_STENCIL;
505
506     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
507     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
508     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, myDepthFormat, aSizeX, aSizeY);
509     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
510   }
511
512   // create FBO
513   theGlCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
514   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
515   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
516                                                GL_RENDERBUFFER, myGlColorRBufferId);
517   if (myGlDepthRBufferId != NO_RENDERBUFFER)
518   {
519     if (hasDepthStencilAttach (theGlCtx) && hasStencilRB)
520     {
521       theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
522                                                    GL_RENDERBUFFER, myGlDepthRBufferId);
523     }
524     else
525     {
526       theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
527                                                    GL_RENDERBUFFER, myGlDepthRBufferId);
528       if (hasStencilRB)
529       {
530         theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
531                                                      GL_RENDERBUFFER, myGlDepthRBufferId);
532       }
533     }
534   }
535   if (theGlCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
536   {
537     UnbindBuffer (theGlCtx);
538     Release (theGlCtx.operator->());
539     return Standard_False;
540   }
541
542   UnbindBuffer (theGlCtx);
543   return Standard_True;
544 }
545
546 // =======================================================================
547 // function : InitWrapper
548 // purpose  :
549 // =======================================================================
550 Standard_Boolean OpenGl_FrameBuffer::InitWrapper (const Handle(OpenGl_Context)& theGlCtx)
551 {
552   myNbSamples = 0;
553   if (theGlCtx->arbFBO == NULL)
554   {
555     return Standard_False;
556   }
557
558   // clean up previous state
559   Release (theGlCtx.operator->());
560
561   GLint anFbo = GLint(NO_FRAMEBUFFER);
562   ::glGetIntegerv (GL_FRAMEBUFFER_BINDING, &anFbo);
563   if (anFbo == GLint(NO_FRAMEBUFFER))
564   {
565     return Standard_False;
566   }
567
568   GLint aColorType = 0;
569   GLint aColorId   = 0;
570   GLint aDepthType = 0;
571   GLint aDepthId   = 0;
572   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aColorType);
573   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aDepthType);
574
575   myGlFBufferId = GLuint(anFbo);
576   myIsOwnBuffer = false;
577   myIsOwnDepth  = false;
578   if (aColorType == GL_RENDERBUFFER)
579   {
580     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aColorId);
581     myGlColorRBufferId = aColorId;
582   }
583   else if (aColorType != GL_NONE)
584   {
585     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), color attachment of unsupported type has been skipped!";
586     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION,
587                            GL_DEBUG_TYPE_ERROR,
588                            0,
589                            GL_DEBUG_SEVERITY_HIGH,
590                            aMsg);
591   }
592
593   if (aDepthType == GL_RENDERBUFFER)
594   {
595     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aDepthId);
596     myGlDepthRBufferId = aDepthId;
597   }
598   else if (aDepthType != GL_NONE)
599   {
600     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), depth attachment of unsupported type has been skipped!";
601     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION,
602                            GL_DEBUG_TYPE_ERROR,
603                            0,
604                            GL_DEBUG_SEVERITY_HIGH,
605                            aMsg);
606   }
607
608   // retrieve dimensions
609   GLuint aRBuffer = myGlColorRBufferId != NO_RENDERBUFFER ? myGlColorRBufferId : myGlDepthRBufferId;
610   if (aRBuffer != NO_RENDERBUFFER)
611   {
612     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, aRBuffer);
613     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myVPSizeX);
614     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myVPSizeY);
615     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
616   }
617
618   return aRBuffer != NO_RENDERBUFFER;
619 }
620
621 // =======================================================================
622 // function : Release
623 // purpose  :
624 // =======================================================================
625 void OpenGl_FrameBuffer::Release (OpenGl_Context* theGlCtx)
626 {
627   if (isValidFrameBuffer())
628   {
629     // application can not handle this case by exception - this is bug in code
630     Standard_ASSERT_RETURN (theGlCtx != NULL,
631       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
632     if (theGlCtx->IsValid()
633      && myIsOwnBuffer)
634     {
635       theGlCtx->arbFBO->glDeleteFramebuffers (1, &myGlFBufferId);
636       if (myGlColorRBufferId != NO_RENDERBUFFER)
637       {
638         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlColorRBufferId);
639       }
640       if (myGlDepthRBufferId != NO_RENDERBUFFER)
641       {
642         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlDepthRBufferId);
643       }
644     }
645     myGlFBufferId      = NO_FRAMEBUFFER;
646     myGlColorRBufferId = NO_RENDERBUFFER;
647     myGlDepthRBufferId = NO_RENDERBUFFER;
648     myIsOwnBuffer      = false;
649   }
650
651   for (Standard_Integer aColorBufferIdx = 0; aColorBufferIdx < myColorTextures.Length(); ++aColorBufferIdx)
652   {
653     myColorTextures (aColorBufferIdx)->Release (theGlCtx);
654   }
655
656   if (myIsOwnDepth)
657   {
658     myDepthStencilTexture->Release (theGlCtx);
659     myIsOwnDepth = false;
660   }
661
662   myVPSizeX = 0;
663   myVPSizeY = 0;
664 }
665
666 // =======================================================================
667 // function : SetupViewport
668 // purpose  :
669 // =======================================================================
670 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& theGlCtx)
671 {
672   const Standard_Integer aViewport[4] = { 0, 0, myVPSizeX, myVPSizeY };
673   theGlCtx->ResizeViewport (aViewport);
674 }
675
676 // =======================================================================
677 // function : ChangeViewport
678 // purpose  :
679 // =======================================================================
680 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
681                                          const GLsizei theVPSizeY)
682 {
683   myVPSizeX = theVPSizeX;
684   myVPSizeY = theVPSizeY;
685 }
686
687 // =======================================================================
688 // function : BindBuffer
689 // purpose  :
690 // =======================================================================
691 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
692 {
693   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
694   theGlCtx->SetFrameBufferSRGB (true);
695 }
696
697 // =======================================================================
698 // function : BindDrawBuffer
699 // purpose  :
700 // =======================================================================
701 void OpenGl_FrameBuffer::BindDrawBuffer (const Handle(OpenGl_Context)& theGlCtx)
702 {
703   theGlCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myGlFBufferId);
704   theGlCtx->SetFrameBufferSRGB (true);
705 }
706
707 // =======================================================================
708 // function : BindReadBuffer
709 // purpose  :
710 // =======================================================================
711 void OpenGl_FrameBuffer::BindReadBuffer (const Handle(OpenGl_Context)& theGlCtx)
712 {
713   theGlCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, myGlFBufferId);
714 }
715
716 // =======================================================================
717 // function : UnbindBuffer
718 // purpose  :
719 // =======================================================================
720 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
721 {
722   if (!theGlCtx->DefaultFrameBuffer().IsNull()
723    &&  theGlCtx->DefaultFrameBuffer().operator->() != this)
724   {
725     theGlCtx->DefaultFrameBuffer()->BindBuffer (theGlCtx);
726   }
727   else
728   {
729     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
730     theGlCtx->SetFrameBufferSRGB (false);
731   }
732 }
733
734 // =======================================================================
735 // function : getAligned
736 // purpose  :
737 // =======================================================================
738 inline Standard_Size getAligned (const Standard_Size theNumber,
739                                  const Standard_Size theAlignment)
740 {
741   return theNumber + theAlignment - 1 - (theNumber - 1) % theAlignment;
742 }
743
744 template<typename T>
745 inline void convertRowFromRgba (T* theRgbRow,
746                                 const Image_ColorRGBA* theRgbaRow,
747                                 const Standard_Size theWidth)
748 {
749   for (Standard_Size aCol = 0; aCol < theWidth; ++aCol)
750   {
751     const Image_ColorRGBA& anRgba = theRgbaRow[aCol];
752     T& anRgb = theRgbRow[aCol];
753     anRgb.r() = anRgba.r();
754     anRgb.g() = anRgba.g();
755     anRgb.b() = anRgba.b();
756   }
757 }
758
759 // =======================================================================
760 // function : BufferDump
761 // purpose  :
762 // =======================================================================
763 Standard_Boolean OpenGl_FrameBuffer::BufferDump (const Handle(OpenGl_Context)& theGlCtx,
764                                                  const Handle(OpenGl_FrameBuffer)& theFbo,
765                                                  Image_PixMap& theImage,
766                                                  Graphic3d_BufferType theBufferType)
767 {
768   if (theGlCtx.IsNull()
769    || theImage.IsEmpty())
770   {
771     return Standard_False;
772   }
773
774   GLenum aFormat = 0;
775   GLenum aType   = 0;
776   bool toSwapRgbaBgra = false;
777   bool toConvRgba2Rgb = false;
778   switch (theImage.Format())
779   {
780   #if !defined(GL_ES_VERSION_2_0)
781     case Image_Format_Gray:
782       aFormat = GL_DEPTH_COMPONENT;
783       aType   = GL_UNSIGNED_BYTE;
784       break;
785     case Image_Format_GrayF:
786       aFormat = GL_DEPTH_COMPONENT;
787       aType   = GL_FLOAT;
788       break;
789     case Image_Format_RGF:
790       aFormat = GL_RG;
791       aType   = GL_FLOAT;
792       break;
793     case Image_Format_RGB:
794       aFormat = GL_RGB;
795       aType   = GL_UNSIGNED_BYTE;
796       break;
797     case Image_Format_BGR:
798       aFormat = GL_BGR;
799       aType   = GL_UNSIGNED_BYTE;
800       break;
801     case Image_Format_BGRA:
802     case Image_Format_BGR32:
803       aFormat = GL_BGRA;
804       aType   = GL_UNSIGNED_BYTE;
805       break;
806     case Image_Format_BGRF:
807       aFormat = GL_BGR;
808       aType   = GL_FLOAT;
809       break;
810     case Image_Format_BGRAF:
811       aFormat = GL_BGRA;
812       aType   = GL_FLOAT;
813       break;
814   #else
815     case Image_Format_Gray:
816     case Image_Format_GrayF:
817     case Image_Format_BGRF:
818     case Image_Format_BGRAF:
819     case Image_Format_RGF:
820       return Standard_False;
821     case Image_Format_BGRA:
822     case Image_Format_BGR32:
823       aFormat = GL_RGBA;
824       aType   = GL_UNSIGNED_BYTE;
825       toSwapRgbaBgra = true;
826       break;
827     case Image_Format_BGR:
828     case Image_Format_RGB:
829       aFormat = GL_RGBA;
830       aType   = GL_UNSIGNED_BYTE;
831       toConvRgba2Rgb = true;
832       break;
833   #endif
834     case Image_Format_RGBA:
835     case Image_Format_RGB32:
836       aFormat = GL_RGBA;
837       aType   = GL_UNSIGNED_BYTE;
838       break;
839     case Image_Format_RGBF:
840       aFormat = GL_RGB;
841       aType   = GL_FLOAT;
842       break;
843     case Image_Format_RGBAF:
844       aFormat = GL_RGBA;
845       aType   = GL_FLOAT;
846       break;
847     case Image_Format_Alpha:
848     case Image_Format_AlphaF:
849       return Standard_False; // GL_ALPHA is no more supported in core context
850     case Image_Format_UNKNOWN:
851       return Standard_False;
852   }
853
854   if (aFormat == 0)
855   {
856     return Standard_False;
857   }
858
859 #if !defined(GL_ES_VERSION_2_0)
860   GLint aReadBufferPrev = GL_BACK;
861   if (theBufferType == Graphic3d_BT_Depth
862    && aFormat != GL_DEPTH_COMPONENT)
863   {
864     return Standard_False;
865   }
866 #else
867   (void )theBufferType;
868 #endif
869
870   // bind FBO if used
871   if (!theFbo.IsNull() && theFbo->IsValid())
872   {
873     theFbo->BindBuffer (theGlCtx);
874   }
875   else
876   {
877   #if !defined(GL_ES_VERSION_2_0)
878     glGetIntegerv (GL_READ_BUFFER, &aReadBufferPrev);
879     GLint aDrawBufferPrev = GL_BACK;
880     glGetIntegerv (GL_DRAW_BUFFER, &aDrawBufferPrev);
881     glReadBuffer (aDrawBufferPrev);
882   #endif
883   }
884
885   // setup alignment
886   const GLint anAligment   = Min (GLint(theImage.MaxRowAligmentBytes()), 8); // limit to 8 bytes for OpenGL
887   glPixelStorei (GL_PACK_ALIGNMENT, anAligment);
888   bool isBatchCopy = !theImage.IsTopDown();
889
890   const GLint   anExtraBytes       = GLint(theImage.RowExtraBytes());
891   GLint         aPixelsWidth       = GLint(theImage.SizeRowBytes() / theImage.SizePixelBytes());
892   Standard_Size aSizeRowBytesEstim = getAligned (theImage.SizePixelBytes() * aPixelsWidth, anAligment);
893   if (anExtraBytes < anAligment)
894   {
895     aPixelsWidth = 0;
896   }
897   else if (aSizeRowBytesEstim != theImage.SizeRowBytes())
898   {
899     aPixelsWidth = 0;
900     isBatchCopy  = false;
901   }
902 #if !defined(GL_ES_VERSION_2_0)
903   glPixelStorei (GL_PACK_ROW_LENGTH, aPixelsWidth);
904 #else
905   if (aPixelsWidth != 0)
906   {
907     isBatchCopy = false;
908   }
909 #endif
910   if (toConvRgba2Rgb)
911   {
912     Handle(NCollection_BaseAllocator) anAlloc = new NCollection_AlignedAllocator (16);
913     const Standard_Size aRowSize = theImage.SizeX() * 4;
914     NCollection_Buffer aRowBuffer (anAlloc);
915     if (!aRowBuffer.Allocate (aRowSize))
916     {
917       return Standard_False;
918     }
919
920     for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow)
921     {
922       // Image_PixMap rows indexation always starts from the upper corner
923       // while order in memory depends on the flag and processed by ChangeRow() method
924       glReadPixels (0, GLint(theImage.SizeY() - aRow - 1), GLsizei (theImage.SizeX()), 1, aFormat, aType, aRowBuffer.ChangeData());
925       const Image_ColorRGBA* aRowDataRgba = (const Image_ColorRGBA* )aRowBuffer.Data();
926       if (theImage.Format() == Image_Format_BGR)
927       {
928         convertRowFromRgba ((Image_ColorBGR* )theImage.ChangeRow (aRow), aRowDataRgba, theImage.SizeX());
929       }
930       else
931       {
932         convertRowFromRgba ((Image_ColorRGB* )theImage.ChangeRow (aRow), aRowDataRgba, theImage.SizeX());
933       }
934     }
935   }
936   else if (!isBatchCopy)
937   {
938     // copy row by row
939     for (Standard_Size aRow = 0; aRow < theImage.SizeY(); ++aRow)
940     {
941       // Image_PixMap rows indexation always starts from the upper corner
942       // while order in memory depends on the flag and processed by ChangeRow() method
943       glReadPixels (0, GLint(theImage.SizeY() - aRow - 1), GLsizei (theImage.SizeX()), 1, aFormat, aType, theImage.ChangeRow (aRow));
944     }
945   }
946   else
947   {
948     glReadPixels (0, 0, GLsizei (theImage.SizeX()), GLsizei (theImage.SizeY()), aFormat, aType, theImage.ChangeData());
949   }
950   const bool hasErrors = theGlCtx->ResetErrors (true);
951
952   glPixelStorei (GL_PACK_ALIGNMENT,  1);
953 #if !defined(GL_ES_VERSION_2_0)
954   glPixelStorei (GL_PACK_ROW_LENGTH, 0);
955 #endif
956
957   if (!theFbo.IsNull() && theFbo->IsValid())
958   {
959     theFbo->UnbindBuffer (theGlCtx);
960   }
961   else
962   {
963   #if !defined(GL_ES_VERSION_2_0)
964     glReadBuffer (aReadBufferPrev);
965   #endif
966   }
967
968   if (toSwapRgbaBgra)
969   {
970     Image_PixMap::SwapRgbaBgra (theImage);
971   }
972
973   return !hasErrors;
974 }
975
976 // =======================================================================
977 // function : EstimatedDataSize
978 // purpose  :
979 // =======================================================================
980 Standard_Size OpenGl_FrameBuffer::EstimatedDataSize() const
981 {
982   if (!IsValid())
983   {
984     return 0;
985   }
986
987   Standard_Size aSize = 0;
988   for (OpenGl_TextureArray::Iterator aTextureIt (myColorTextures); aTextureIt.More(); aTextureIt.Next())
989   {
990     aSize += aTextureIt.Value()->EstimatedDataSize();
991   }
992   if (!myDepthStencilTexture.IsNull())
993   {
994     aSize += myDepthStencilTexture->EstimatedDataSize();
995   }
996   if (myGlColorRBufferId != NO_RENDERBUFFER
997   && !myColorFormats.IsEmpty())
998   {
999     aSize += OpenGl_Texture::PixelSizeOfPixelFormat (myColorFormats.First()) * myInitVPSizeX * myInitVPSizeY;
1000   }
1001   if (myGlDepthRBufferId != NO_RENDERBUFFER)
1002   {
1003     aSize += OpenGl_Texture::PixelSizeOfPixelFormat (myDepthFormat) * myInitVPSizeX * myInitVPSizeY;
1004   }
1005   return aSize;
1006 }