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