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