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