0024023: Revamp the OCCT Handle -- automatic
[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 <Standard_Assert.hxx>
19 #include <TCollection_ExtendedString.hxx>
20
21
22 // =======================================================================
23 // function : OpenGl_FrameBuffer
24 // purpose  :
25 // =======================================================================
26 OpenGl_FrameBuffer::OpenGl_FrameBuffer (GLint theTextureFormat)
27 : myVPSizeX (0),
28   myVPSizeY (0),
29   myTextFormat (theTextureFormat),
30   myGlFBufferId (NO_FRAMEBUFFER),
31   myGlColorRBufferId (NO_RENDERBUFFER),
32   myGlDepthRBufferId (NO_RENDERBUFFER),
33   myIsOwnBuffer  (false),
34   myColorTexture (new OpenGl_Texture()),
35   myDepthStencilTexture (new OpenGl_Texture())
36 {
37   //
38 }
39
40 // =======================================================================
41 // function : ~OpenGl_FrameBuffer
42 // purpose  :
43 // =======================================================================
44 OpenGl_FrameBuffer::~OpenGl_FrameBuffer()
45 {
46   Release (NULL);
47 }
48
49 // =======================================================================
50 // function : Init
51 // purpose  :
52 // =======================================================================
53 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
54                                            const GLsizei   theViewportSizeX,
55                                            const GLsizei   theViewportSizeY)
56 {
57   if (theGlContext->arbFBO == NULL)
58   {
59     return Standard_False;
60   }
61
62   // clean up previous state
63   Release (theGlContext.operator->());
64
65   // setup viewport sizes as is
66   myVPSizeX = theViewportSizeX;
67   myVPSizeY = theViewportSizeY;
68
69   // Create the textures (will be used as color buffer and depth-stencil buffer)
70   if (!myColorTexture->Init (theGlContext, myTextFormat,
71                              GL_RGBA, GL_UNSIGNED_BYTE,
72                              myVPSizeX, myVPSizeY, Graphic3d_TOT_2D))
73   {
74     Release (theGlContext.operator->());
75     return Standard_False;
76   }
77
78   // extensions (GL_OES_packed_depth_stencil, GL_OES_depth_texture) + GL version might be used to determine supported formats
79   // instead of just trying to create such texture
80   if (!myDepthStencilTexture->Init (theGlContext, GL_DEPTH24_STENCIL8,
81                                     GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
82                                     myVPSizeX, myVPSizeY, Graphic3d_TOT_2D))
83   {
84     TCollection_ExtendedString aMsg = TCollection_ExtendedString()
85       + "Warning! Depth textures are not supported by hardware!";
86     theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
87                                GL_DEBUG_TYPE_PORTABILITY_ARB,
88                                0,
89                                GL_DEBUG_SEVERITY_HIGH_ARB,
90                                aMsg);
91
92     theGlContext->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
93     theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
94     theGlContext->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, myVPSizeX, myVPSizeY);
95     theGlContext->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
96   }
97
98   // Build FBO and setup it as texture
99   theGlContext->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
100   theGlContext->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
101   theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
102                                                 GL_TEXTURE_2D, myColorTexture->TextureId(), 0);
103   if (myDepthStencilTexture->IsValid())
104   {
105   #ifdef GL_DEPTH_STENCIL_ATTACHMENT
106     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
107                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
108   #else
109     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
110                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
111     theGlContext->arbFBO->glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
112                                                   GL_TEXTURE_2D, myDepthStencilTexture->TextureId(), 0);
113   #endif
114   }
115   else if (myGlDepthRBufferId != NO_RENDERBUFFER)
116   {
117     theGlContext->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
118                                                      GL_RENDERBUFFER, myGlDepthRBufferId);
119   }
120   if (theGlContext->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
121   {
122     Release (theGlContext.operator->());
123     return Standard_False;
124   }
125
126   UnbindBuffer (theGlContext);
127   return Standard_True;
128 }
129
130 // =======================================================================
131 // function : Init
132 // purpose  :
133 // =======================================================================
134 Standard_Boolean OpenGl_FrameBuffer::InitLazy (const Handle(OpenGl_Context)& theGlContext,
135                                                const GLsizei                 theViewportSizeX,
136                                                const GLsizei                 theViewportSizeY)
137 {
138   if (myVPSizeX == theViewportSizeX
139    && myVPSizeY == theViewportSizeY)
140
141   {
142     return IsValid();
143   }
144
145   return Init (theGlContext, theViewportSizeX, theViewportSizeY);
146 }
147
148 // =======================================================================
149 // function : InitWithRB
150 // purpose  :
151 // =======================================================================
152 Standard_Boolean OpenGl_FrameBuffer::InitWithRB (const Handle(OpenGl_Context)& theGlCtx,
153                                                  const GLsizei                 theViewportSizeX,
154                                                  const GLsizei                 theViewportSizeY,
155                                                  const GLuint                  theColorRBufferFromWindow)
156 {
157   if (theGlCtx->arbFBO == NULL)
158   {
159     return Standard_False;
160   }
161
162   // clean up previous state
163   Release (theGlCtx.operator->());
164
165   // setup viewport sizes as is
166   myVPSizeX = theViewportSizeX;
167   myVPSizeY = theViewportSizeY;
168
169   // Create the render-buffers
170   if (theColorRBufferFromWindow != NO_RENDERBUFFER)
171   {
172     myGlColorRBufferId = theColorRBufferFromWindow;
173   }
174   else
175   {
176     theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlColorRBufferId);
177     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlColorRBufferId);
178     theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_RGBA8, myVPSizeX, myVPSizeY);
179   }
180
181   theGlCtx->arbFBO->glGenRenderbuffers (1, &myGlDepthRBufferId);
182   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, myGlDepthRBufferId);
183   theGlCtx->arbFBO->glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, myVPSizeX, myVPSizeY);
184
185   theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
186
187   // create FBO
188   theGlCtx->arbFBO->glGenFramebuffers (1, &myGlFBufferId);
189   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
190   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
191                                                GL_RENDERBUFFER, myGlColorRBufferId);
192 #ifdef GL_DEPTH_STENCIL_ATTACHMENT
193   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
194                                                GL_RENDERBUFFER, myGlDepthRBufferId);
195 #else
196   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
197                                                GL_RENDERBUFFER, myGlDepthRBufferId);
198   theGlCtx->arbFBO->glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
199                                                GL_RENDERBUFFER, myGlDepthRBufferId);
200 #endif
201   if (theGlCtx->arbFBO->glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
202   {
203     UnbindBuffer (theGlCtx);
204     Release (theGlCtx.operator->());
205     return Standard_False;
206   }
207
208   UnbindBuffer (theGlCtx);
209   return Standard_True;
210 }
211
212 // =======================================================================
213 // function : InitWrapper
214 // purpose  :
215 // =======================================================================
216 Standard_Boolean OpenGl_FrameBuffer::InitWrapper (const Handle(OpenGl_Context)& theGlCtx)
217 {
218   if (theGlCtx->arbFBO == NULL)
219   {
220     return Standard_False;
221   }
222
223   // clean up previous state
224   Release (theGlCtx.operator->());
225
226   GLint anFbo = GLint(NO_FRAMEBUFFER);
227   ::glGetIntegerv (GL_FRAMEBUFFER_BINDING, &anFbo);
228   if (anFbo == GLint(NO_FRAMEBUFFER))
229   {
230     return Standard_False;
231   }
232
233   GLint aColorType = 0;
234   GLint aColorId   = 0;
235   GLint aDepthType = 0;
236   GLint aDepthId   = 0;
237   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aColorType);
238   theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &aDepthType);
239
240   myGlFBufferId = GLuint(anFbo);
241   myIsOwnBuffer = false;
242   if (aColorType == GL_RENDERBUFFER)
243   {
244     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aColorId);
245     myGlColorRBufferId = aColorId;
246   }
247   else if (aColorType != GL_NONE)
248   {
249     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), color attachment of unsupported type has been skipped!";
250     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
251                            GL_DEBUG_TYPE_ERROR_ARB,
252                            0,
253                            GL_DEBUG_SEVERITY_HIGH_ARB,
254                            aMsg);
255   }
256
257   if (aDepthType == GL_RENDERBUFFER)
258   {
259     theGlCtx->arbFBO->glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,  GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &aDepthId);
260     myGlDepthRBufferId = aDepthId;
261   }
262   else if (aDepthType != GL_NONE)
263   {
264     TCollection_ExtendedString aMsg = "OpenGl_FrameBuffer::InitWrapper(), depth attachment of unsupported type has been skipped!";
265     theGlCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION_ARB,
266                            GL_DEBUG_TYPE_ERROR_ARB,
267                            0,
268                            GL_DEBUG_SEVERITY_HIGH_ARB,
269                            aMsg);
270   }
271
272   // retrieve dimensions
273   GLuint aRBuffer = myGlColorRBufferId != NO_RENDERBUFFER ? myGlColorRBufferId : myGlDepthRBufferId;
274   if (aRBuffer != NO_RENDERBUFFER)
275   {
276     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, aRBuffer);
277     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &myVPSizeX);
278     theGlCtx->arbFBO->glGetRenderbufferParameteriv (GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &myVPSizeY);
279     theGlCtx->arbFBO->glBindRenderbuffer (GL_RENDERBUFFER, NO_RENDERBUFFER);
280   }
281
282   return aRBuffer != NO_RENDERBUFFER;
283 }
284
285 // =======================================================================
286 // function : Release
287 // purpose  :
288 // =======================================================================
289 void OpenGl_FrameBuffer::Release (OpenGl_Context* theGlCtx)
290 {
291   if (isValidFrameBuffer())
292   {
293     // application can not handle this case by exception - this is bug in code
294     Standard_ASSERT_RETURN (theGlCtx != NULL,
295       "OpenGl_FrameBuffer destroyed without GL context! Possible GPU memory leakage...",);
296     if (theGlCtx->IsValid()
297      && myIsOwnBuffer)
298     {
299       theGlCtx->arbFBO->glDeleteFramebuffers (1, &myGlFBufferId);
300       if (myGlColorRBufferId != NO_RENDERBUFFER)
301       {
302         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlColorRBufferId);
303       }
304       if (myGlDepthRBufferId != NO_RENDERBUFFER)
305       {
306         theGlCtx->arbFBO->glDeleteRenderbuffers (1, &myGlDepthRBufferId);
307       }
308     }
309     myGlFBufferId      = NO_FRAMEBUFFER;
310     myGlColorRBufferId = NO_RENDERBUFFER;
311     myGlDepthRBufferId = NO_RENDERBUFFER;
312     myIsOwnBuffer      = false;
313   }
314
315   myColorTexture->Release (theGlCtx);
316   myDepthStencilTexture->Release (theGlCtx);
317 }
318
319 // =======================================================================
320 // function : SetupViewport
321 // purpose  :
322 // =======================================================================
323 void OpenGl_FrameBuffer::SetupViewport (const Handle(OpenGl_Context)& /*theGlCtx*/)
324 {
325   glViewport (0, 0, myVPSizeX, myVPSizeY);
326 }
327
328 // =======================================================================
329 // function : ChangeViewport
330 // purpose  :
331 // =======================================================================
332 void OpenGl_FrameBuffer::ChangeViewport (const GLsizei theVPSizeX,
333                                          const GLsizei theVPSizeY)
334 {
335   myVPSizeX = theVPSizeX;
336   myVPSizeY = theVPSizeY;
337 }
338
339 // =======================================================================
340 // function : BindBuffer
341 // purpose  :
342 // =======================================================================
343 void OpenGl_FrameBuffer::BindBuffer (const Handle(OpenGl_Context)& theGlCtx)
344 {
345   theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, myGlFBufferId);
346 }
347
348 // =======================================================================
349 // function : BindDrawBuffer
350 // purpose  :
351 // =======================================================================
352 void OpenGl_FrameBuffer::BindDrawBuffer (const Handle(OpenGl_Context)& theGlCtx)
353 {
354   theGlCtx->arbFBO->glBindFramebuffer (GL_DRAW_FRAMEBUFFER, myGlFBufferId);
355 }
356
357 // =======================================================================
358 // function : BindReadBuffer
359 // purpose  :
360 // =======================================================================
361 void OpenGl_FrameBuffer::BindReadBuffer (const Handle(OpenGl_Context)& theGlCtx)
362 {
363   theGlCtx->arbFBO->glBindFramebuffer (GL_READ_FRAMEBUFFER, myGlFBufferId);
364 }
365
366 // =======================================================================
367 // function : UnbindBuffer
368 // purpose  :
369 // =======================================================================
370 void OpenGl_FrameBuffer::UnbindBuffer (const Handle(OpenGl_Context)& theGlCtx)
371 {
372   if (!theGlCtx->DefaultFrameBuffer().IsNull()
373    &&  theGlCtx->DefaultFrameBuffer().operator->() != this)
374   {
375     theGlCtx->DefaultFrameBuffer()->BindBuffer (theGlCtx);
376   }
377   else
378   {
379     theGlCtx->arbFBO->glBindFramebuffer (GL_FRAMEBUFFER, NO_FRAMEBUFFER);
380   }
381 }