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