ddbc0b54a2a6ae2d157e2404cb388a0cb12784ed
[occt.git] / src / OpenGl / OpenGl_FrameBuffer.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2011-2012 OPEN CASCADE SAS
3 //
4 // The content of this file is subject to the Open CASCADE Technology Public
5 // License Version 6.5 (the "License"). You may not use the content of this file
6 // except in compliance with the License. Please obtain a copy of the License
7 // at http://www.opencascade.org and read it completely before using this file.
8 //
9 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
10 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
11 //
12 // The Original Code and all software distributed under the License is
13 // distributed on an "AS IS" basis, without warranty of any kind, and the
14 // Initial Developer hereby disclaims all such warranties, including without
15 // limitation, any warranties of merchantability, fitness for a particular
16 // purpose or non-infringement. Please see the License for the specific terms
17 // and conditions governing the rights and limitations under the License.
18
19
20 #include <OpenGl_FrameBuffer.hxx>
21
22 #ifdef DEB
23   #include <iostream>
24 #endif
25
26 static inline bool isOddNumber (const GLsizei theNumber)
27 {
28   return theNumber & 0x01;
29 }
30
31 static inline GLsizei getEvenNumber (const GLsizei theNumber)
32 {
33   return isOddNumber (theNumber) ? (theNumber + 1) : theNumber;
34 }
35
36 //! Notice - 0 is not power of two here
37 static inline bool isPowerOfTwo (const GLsizei theNumber)
38 {
39         return !(theNumber & (theNumber - 1));
40 }
41
42 OpenGl_FrameBuffer::OpenGl_FrameBuffer (GLint theTextureFormat)
43 : mySizeX (0),
44   mySizeY (0),
45   myVPSizeX (0),
46   myVPSizeY (0),
47   myTextFormat (theTextureFormat),
48   myGlTextureId (NO_TEXTURE),
49   myGlFBufferId (NO_FRAMEBUFFER),
50   myGlDepthRBId (NO_RENDERBUFFER),
51   myGlStencilRBId (NO_RENDERBUFFER)
52 {
53   //
54 }
55
56 Standard_Boolean OpenGl_FrameBuffer::Init (const Handle(OpenGl_Context)& theGlContext,
57                                            GLsizei theViewportSizeX,
58                                            GLsizei theViewportSizeY,
59                                            GLboolean toForcePowerOfTwo)
60 {
61   if (theGlContext->extFBO == NULL)
62   {
63   #ifdef DEB
64     std::cerr << "OpenGl_FrameBuffer, FBO extension not supported!\n";
65   #endif
66     return Standard_False;
67   }
68
69   // clean up previous state
70   Release (theGlContext);
71
72   // upscale width/height if numbers are odd
73   if (toForcePowerOfTwo)
74   {
75     mySizeX = OpenGl_Context::GetPowerOfTwo (theViewportSizeX, theGlContext->MaxTextureSize());
76     mySizeY = OpenGl_Context::GetPowerOfTwo (theViewportSizeY, theGlContext->MaxTextureSize());
77   }
78   else
79   {
80     mySizeX = getEvenNumber (theViewportSizeX);
81     mySizeY = getEvenNumber (theViewportSizeY);
82   }
83
84   // setup viewport sizes as is
85   myVPSizeX = theViewportSizeX;
86   myVPSizeY = theViewportSizeY;
87
88   // Create the texture (will be used as color buffer)
89   if (!InitTrashTexture (theGlContext))
90   {
91     if (!isPowerOfTwo (mySizeX) || !isPowerOfTwo (mySizeY))
92     {
93       return Init (theGlContext, theViewportSizeX, theViewportSizeY, GL_TRUE);
94     }
95     Release (theGlContext);
96     return Standard_False;
97   }
98
99   if (!theGlContext->extPDS)
100   {
101     // Create RenderBuffer to be used as depth buffer
102     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlDepthRBId);
103     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlDepthRBId);
104     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, mySizeX, mySizeY);
105
106     // Create RenderBuffer to be used as stencil buffer
107     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlStencilRBId);
108     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlStencilRBId);
109     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_STENCIL_INDEX, mySizeX, mySizeY);
110   }
111   else
112   {
113     // Create combined depth stencil buffer
114     theGlContext->extFBO->glGenRenderbuffersEXT (1, &myGlDepthRBId);
115     theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, myGlDepthRBId);
116     theGlContext->extFBO->glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, mySizeX, mySizeY);
117     myGlStencilRBId = myGlDepthRBId;
118   }
119
120   // Build FBO and setup it as texture
121   theGlContext->extFBO->glGenFramebuffersEXT (1, &myGlFBufferId);
122   theGlContext->extFBO->glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, myGlFBufferId);
123   glEnable (GL_TEXTURE_2D);
124   glBindTexture (GL_TEXTURE_2D, myGlTextureId);
125   theGlContext->extFBO->glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, myGlTextureId, 0);
126   theGlContext->extFBO->glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myGlDepthRBId);
127   theGlContext->extFBO->glFramebufferRenderbufferEXT (GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, myGlStencilRBId);
128   if (theGlContext->extFBO->glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT)
129   {
130     if (!isPowerOfTwo (mySizeX) || !isPowerOfTwo (mySizeY))
131     {
132       return Init (theGlContext, theViewportSizeX, theViewportSizeY, GL_TRUE);
133     }
134     Release (theGlContext);
135     return Standard_False;
136   }
137
138   UnbindBuffer (theGlContext);
139   UnbindTexture();
140   theGlContext->extFBO->glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, NO_RENDERBUFFER);
141
142   #ifdef DEB
143     std::cerr << "OpenGl_FrameBuffer, created FBO " << mySizeX << "x" << mySizeY
144               << " for viewport " << theViewportSizeX << "x" << theViewportSizeY << "\n";
145   #endif
146   return Standard_True;
147 }
148
149 void OpenGl_FrameBuffer::Release (const Handle(OpenGl_Context)& theGlContext)
150 {
151   if (IsValidDepthBuffer())
152   {
153     if (!theGlContext.IsNull() && theGlContext->extFBO != NULL)
154     {
155       theGlContext->extFBO->glDeleteRenderbuffersEXT (1, &myGlDepthRBId);
156       myGlDepthRBId = NO_RENDERBUFFER;
157     }
158     else
159     {
160       std::cerr << "OpenGl_FrameBuffer::Release() called with invalid OpenGl_Context!\n";
161     }
162   }
163   if (IsValidStencilBuffer())
164   {
165     if (!theGlContext.IsNull() && theGlContext->extFBO != NULL)
166     {
167       theGlContext->extFBO->glDeleteRenderbuffersEXT (1, &myGlStencilRBId);
168       myGlStencilRBId = NO_RENDERBUFFER;
169     }
170     else
171     {
172       std::cerr << "OpenGl_FrameBuffer::Release() called with invalid OpenGl_Context!\n";
173     }
174   }
175   if (IsValidTexture())
176   {
177     glDeleteTextures (1, &myGlTextureId);
178     myGlTextureId = NO_TEXTURE;
179   }
180   mySizeX = mySizeY = myVPSizeX = myVPSizeY = 0;
181   if (IsValidFrameBuffer())
182   {
183     if (!theGlContext.IsNull() && theGlContext->extFBO != NULL)
184     {
185       theGlContext->extFBO->glDeleteFramebuffersEXT (1, &myGlFBufferId);
186       myGlFBufferId = NO_FRAMEBUFFER;
187     }
188     else
189     {
190       std::cerr << "OpenGl_FrameBuffer::Release() called with invalid OpenGl_Context!\n";
191     }
192   }
193 }
194
195 Standard_Boolean OpenGl_FrameBuffer::IsProxySuccess() const
196 {
197   // use proxy to check texture could be created or not
198   glTexImage2D (GL_PROXY_TEXTURE_2D,
199                 0,                // LOD number: 0 - base image level; n is the nth mipmap reduction image
200                 myTextFormat,     // internalformat
201                 mySizeX, mySizeY, 0,
202                 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
203   GLint aTestParamX (0), aTestParamY (0);
204   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestParamX);
205   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestParamY);
206   return aTestParamX != 0 && aTestParamY != 0;
207 }
208
209 Standard_Boolean OpenGl_FrameBuffer::InitTrashTexture (const Handle(OpenGl_Context)& theGlContext)
210 {
211   // Check texture size is fit dimension maximum
212   GLint aMaxTexDim = 2048;
213   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &aMaxTexDim);
214   if (mySizeX > aMaxTexDim || mySizeY > aMaxTexDim)
215   {
216     return Standard_False;
217   }
218
219   // generate new id
220   glEnable (GL_TEXTURE_2D);
221   if (!IsValidTexture())
222   {
223     glGenTextures (1, &myGlTextureId); // Create The Texture
224   }
225   glBindTexture (GL_TEXTURE_2D, myGlTextureId);
226
227   // texture interpolation parameters - could be overridden later
228   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
229   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
230
231   if (!IsProxySuccess())
232   {
233     Release (theGlContext);
234     return Standard_False;
235   }
236
237   glTexImage2D (GL_TEXTURE_2D,
238                 0,                // LOD number: 0 - base image level; n is the nth mipmap reduction image
239                 myTextFormat,     // internalformat
240                 mySizeX, mySizeY, 0,
241                 GL_RGBA, GL_UNSIGNED_BYTE, NULL); // NULL pointer supported from OpenGL 1.1
242   return Standard_True;
243 }