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