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