0024131: TKOpenGL redesign GPU memory management for markers presentation
[occt.git] / src / OpenGl / OpenGl_Texture.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 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 #include <OpenGl_Texture.hxx>
20
21 #include <OpenGl_ExtFBO.hxx>
22 #include <OpenGl_Context.hxx>
23 #include <Graphic3d_TextureParams.hxx>
24 #include <Standard_Assert.hxx>
25 #include <Image_PixMap.hxx>
26
27 IMPLEMENT_STANDARD_HANDLE (OpenGl_Texture, OpenGl_Resource)
28 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_Resource)
29
30 // =======================================================================
31 // function : OpenGl_Texture
32 // purpose  :
33 // =======================================================================
34 OpenGl_Texture::OpenGl_Texture (const Handle(Graphic3d_TextureParams)& theParams)
35 : OpenGl_Resource(),
36   myTextureId (NO_TEXTURE),
37   myTarget (GL_TEXTURE_2D),
38   mySizeX (0),
39   mySizeY (0),
40   myTextFormat (GL_FLOAT),
41   myHasMipmaps (Standard_False),
42   myParams     (theParams)
43 {
44   if (myParams.IsNull())
45   {
46     myParams = new Graphic3d_TextureParams();
47   }
48 }
49
50 // =======================================================================
51 // function : ~OpenGl_Texture
52 // purpose  :
53 // =======================================================================
54 OpenGl_Texture::~OpenGl_Texture()
55 {
56   Release (NULL);
57 }
58
59 // =======================================================================
60 // function : HasMipmaps
61 // purpose  :
62 // =======================================================================
63 const Standard_Boolean OpenGl_Texture::HasMipmaps() const
64 {
65   return myHasMipmaps;
66 }
67
68 // =======================================================================
69 // function : GetParams
70 // purpose  :
71 // =======================================================================
72 const Handle(Graphic3d_TextureParams)& OpenGl_Texture::GetParams() const
73 {
74   return myParams;
75 }
76
77 // =======================================================================
78 // function : SetParams
79 // purpose  :
80 // =======================================================================
81 void OpenGl_Texture::SetParams (const Handle(Graphic3d_TextureParams)& theParams)
82 {
83   myParams = theParams;
84 }
85
86 // =======================================================================
87 // function : Create
88 // purpose  :
89 // =======================================================================
90 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& )
91 {
92   if (myTextureId == NO_TEXTURE)
93   {
94     glGenTextures (1, &myTextureId);
95   }
96   return myTextureId != NO_TEXTURE;
97 }
98
99 // =======================================================================
100 // function : Release
101 // purpose  :
102 // =======================================================================
103 void OpenGl_Texture::Release (const OpenGl_Context* theGlCtx)
104 {
105   if (myTextureId == NO_TEXTURE)
106   {
107     return;
108   }
109
110   // application can not handle this case by exception - this is bug in code
111   Standard_ASSERT_RETURN (theGlCtx != NULL,
112     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
113
114   glDeleteTextures (1, &myTextureId);
115   myTextureId = NO_TEXTURE;
116   mySizeX = mySizeY = 0;
117 }
118
119 // =======================================================================
120 // function : Bind
121 // purpose  :
122 // =======================================================================
123 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
124                            const GLenum theTextureUnit) const
125 {
126   if (theCtx->IsGlGreaterEqual (1, 3))
127   {
128     theCtx->core13->glActiveTexture (theTextureUnit);
129   }
130   glBindTexture (myTarget, myTextureId);
131 }
132
133 // =======================================================================
134 // function : Unbind
135 // purpose  :
136 // =======================================================================
137 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
138                              const GLenum theTextureUnit) const
139 {
140   if (theCtx->IsGlGreaterEqual (1, 3))
141   {
142     theCtx->core13->glActiveTexture (theTextureUnit);
143   }
144   glBindTexture (myTarget, NO_TEXTURE);
145 }
146
147 // =======================================================================
148 // function : Init
149 // purpose  :
150 // =======================================================================
151 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
152                            const Image_PixMap&           theImage,
153                            const Graphic3d_TypeOfTexture theType)
154 {
155   myHasMipmaps = Standard_False;
156   if (theImage.IsEmpty() || !Create (theCtx))
157   {
158     return false;
159   }
160
161   myTextFormat = GL_RGBA8;
162   GLenum aPixelFormat = 0;
163   GLenum aDataType    = 0;
164   switch (theImage.Format())
165   {
166     case Image_PixMap::ImgGrayF:
167     {
168       myTextFormat = GL_ALPHA8; // GL_R8, GL_R32F
169       aPixelFormat = GL_ALPHA;  // GL_RED
170       aDataType    = GL_FLOAT;
171       break;
172     }
173     case Image_PixMap::ImgRGBAF:
174     {
175       myTextFormat = GL_RGBA8; // GL_RGBA32F
176       aPixelFormat = GL_RGBA;
177       aDataType    = GL_FLOAT;
178       break;
179     }
180     case Image_PixMap::ImgBGRAF:
181     {
182       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
183       {
184         return false;
185       }
186       myTextFormat = GL_RGBA8; // GL_RGBA32F
187       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
188       aDataType    = GL_FLOAT;
189       break;
190     }
191     case Image_PixMap::ImgRGBF:
192     {
193       myTextFormat = GL_RGB8; // GL_RGB32F
194       aPixelFormat = GL_RGB;
195       aDataType    = GL_FLOAT;
196       break;
197     }
198     case Image_PixMap::ImgBGRF:
199     {
200       myTextFormat = GL_RGB8; // GL_RGB32F
201       aPixelFormat = GL_BGR;  // equals to GL_BGR_EXT
202       aDataType    = GL_FLOAT;
203       break;
204     }
205     case Image_PixMap::ImgRGBA:
206     {
207       myTextFormat = GL_RGBA8;
208       aPixelFormat = GL_RGBA;
209       aDataType    = GL_UNSIGNED_BYTE;
210       break;
211     }
212     case Image_PixMap::ImgBGRA:
213     {
214       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
215       {
216         return false;
217       }
218       myTextFormat = GL_RGBA8;
219       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
220       aDataType    = GL_UNSIGNED_BYTE;
221       break;
222     }
223     case Image_PixMap::ImgRGB32:
224     {
225       myTextFormat = GL_RGB8;
226       aPixelFormat = GL_RGBA;
227       aDataType    = GL_UNSIGNED_BYTE;
228       break;
229     }
230     case Image_PixMap::ImgBGR32:
231     {
232       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
233       {
234         return false;
235       }
236       myTextFormat = GL_RGB8;
237       aPixelFormat = GL_BGRA;  // equals to GL_BGRA_EXT
238       aDataType    = GL_UNSIGNED_BYTE;
239       break;
240     }
241     case Image_PixMap::ImgRGB:
242     {
243       myTextFormat = GL_RGB8;
244       aPixelFormat = GL_RGB;
245       aDataType    = GL_UNSIGNED_BYTE;
246       break;
247     }
248     case Image_PixMap::ImgBGR:
249     {
250       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
251       {
252         return false;
253       }
254       myTextFormat = GL_RGB8;
255       aPixelFormat = GL_BGR;  // equals to GL_BGR_EXT
256       aDataType    = GL_UNSIGNED_BYTE;
257       break;
258     }
259     case Image_PixMap::ImgGray:
260     {
261       myTextFormat = GL_ALPHA8; // GL_R8
262       aPixelFormat = GL_ALPHA;  // GL_RED
263       aDataType    = GL_UNSIGNED_BYTE;
264       break;
265     }
266     default:
267     {
268       return false;
269     }
270   }
271
272   const GLsizei aMaxSize   = theCtx->MaxTextureSize();
273   const GLsizei aWidth     = (GLsizei )theImage.SizeX();
274   const GLsizei aHeight    = (GLsizei )theImage.SizeY();
275
276   // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
277   // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
278   // Trying to create NPOT rextures on such hardware will not fail
279   // but driver will fall back into software rendering,
280   const bool    toForceP2  = !theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW;
281   const GLsizei aWidthOut  = toForceP2 ? OpenGl_Context::GetPowerOfTwo (aWidth,  aMaxSize) : Min (aWidth,  aMaxSize);
282   const GLsizei aHeightOut = toForceP2 ? OpenGl_Context::GetPowerOfTwo (aHeight, aMaxSize) : Min (aHeight, aMaxSize);
283
284   GLint aTestWidth  = 0;
285   GLint aTestHeight = 0;
286
287   glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // ensure alignment will not screw up the party
288   switch (theType)
289   {
290     case Graphic3d_TOT_1D:
291     {
292       myTarget = GL_TEXTURE_1D;
293       Bind (theCtx);
294       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
295       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
296
297       Image_PixMap aCopy;
298       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
299       if (aWidth != aWidthOut)
300       {
301         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), 1)
302           || gluScaleImage (aPixelFormat,
303                             aWidth,    1, aDataType, theImage.Data(),
304                             aWidthOut, 1, aDataType, aCopy.ChangeData()) != 0)
305         {
306           Unbind (theCtx);
307           return false;
308         }
309
310         aDataPtr = (GLvoid* )aCopy.Data();
311       }
312
313       // use proxy to check texture could be created or not
314       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, myTextFormat,
315                     aWidthOut, 0,
316                     aPixelFormat, aDataType, NULL);
317       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
318       if (aTestWidth == 0)
319       {
320         // no memory or broken input parameters
321         Unbind (theCtx);
322         return false;
323       }
324
325       glTexImage1D (GL_TEXTURE_1D, 0, myTextFormat,
326                     aWidthOut, 0,
327                     aPixelFormat, aDataType, aDataPtr);
328       if (glGetError() != GL_NO_ERROR)
329       {
330         Unbind (theCtx);
331         return false;
332       }
333
334       mySizeX = aWidthOut;
335       mySizeY = 1;
336
337       Unbind (theCtx);
338       return true;
339     }
340     case Graphic3d_TOT_2D:
341     {
342       myTarget = GL_TEXTURE_2D;
343       Bind (theCtx);
344       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
345       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
346
347       Image_PixMap aCopy;
348       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
349       if (aWidth != aWidthOut || aHeight != aHeightOut)
350       {
351         // scale texture
352         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), Standard_Size(aHeightOut))
353           || gluScaleImage (aPixelFormat,
354                             aWidth,    aHeight,    aDataType, theImage.Data(),
355                             aWidthOut, aHeightOut, aDataType, aCopy.ChangeData()) != 0)
356         {
357           Unbind (theCtx);
358           return false;
359         }
360
361         aDataPtr = (GLvoid* )aCopy.Data();
362       }
363
364       // use proxy to check texture could be created or not
365       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, myTextFormat,
366                     aWidthOut, aHeightOut, 0,
367                     aPixelFormat, aDataType, NULL);
368       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
369       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
370       if (aTestWidth == 0 || aTestHeight == 0)
371       {
372         // no memory or broken input parameters
373         Unbind (theCtx);
374         return false;
375       }
376
377       glTexImage2D (GL_TEXTURE_2D, 0, myTextFormat,
378                     aWidthOut, aHeightOut, 0,
379                     aPixelFormat, aDataType, aDataPtr);
380       if (glGetError() != GL_NO_ERROR)
381       {
382         Unbind (theCtx);
383         return false;
384       }
385
386       mySizeX = aWidthOut;
387       mySizeY = aHeightOut;
388
389       Unbind (theCtx);
390       return true;
391     }
392     case Graphic3d_TOT_2D_MIPMAP:
393     {
394       myTarget     = GL_TEXTURE_2D;
395       myHasMipmaps = Standard_True;
396       Bind (theCtx);
397       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
398       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
399
400       if (theCtx->extFBO != NULL
401        && aWidth == aWidthOut && aHeight == aHeightOut)
402       {
403         // use proxy to check texture could be created or not
404         glTexImage2D (GL_PROXY_TEXTURE_2D, 0, myTextFormat,
405                       aWidthOut, aHeightOut, 0,
406                       aPixelFormat, aDataType, NULL);
407         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
408         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
409         if (aTestWidth == 0 || aTestHeight == 0)
410         {
411           // no memory or broken input parameters
412           Unbind (theCtx);
413           return false;
414         }
415
416         // upload main picture
417         glTexImage2D (GL_TEXTURE_2D, 0, myTextFormat,
418                       aWidthOut, aHeightOut, 0,
419                       aPixelFormat, aDataType, theImage.Data());
420         if (glGetError() != GL_NO_ERROR)
421         {
422           Unbind (theCtx);
423           return false;
424         }
425
426         mySizeX = aWidthOut;
427         mySizeY = aHeightOut;
428
429         // generate mipmaps
430         //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
431         theCtx->extFBO->glGenerateMipmapEXT (GL_TEXTURE_2D);
432
433         Unbind (theCtx);
434         return true;
435       }
436       else
437       {
438         bool isCreated = gluBuild2DMipmaps (GL_TEXTURE_2D, myTextFormat,
439                                             aWidth, aHeight,
440                                             aPixelFormat, aDataType, theImage.Data()) == 0;
441         if (isCreated)
442         {
443           glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &mySizeX);
444           glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &mySizeY);
445         }
446
447         Unbind (theCtx);
448         return isCreated;
449       }
450     }
451     default:
452     {
453       return false;
454     }
455   }
456 }