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