0030700: Visualization, TKOpenGl - support PBR Metallic-Roughness shading model
[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_GlCore32.hxx>
20 #include <OpenGl_Sampler.hxx>
21 #include <Graphic3d_TextureParams.hxx>
22 #include <TCollection_ExtendedString.hxx>
23 #include <Standard_Assert.hxx>
24 #include <Image_PixMap.hxx>
25
26 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_NamedResource)
27
28 //! Simple class to reset unpack alignment settings
29 struct OpenGl_UnpackAlignmentSentry
30 {
31
32   //! Reset unpack alignment settings to safe values
33   static void Reset()
34   {
35     glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
36   #if !defined(GL_ES_VERSION_2_0)
37     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
38   #endif
39   }
40
41   OpenGl_UnpackAlignmentSentry() {}
42
43   ~OpenGl_UnpackAlignmentSentry()
44   {
45     Reset();
46   }
47
48 };
49
50 // =======================================================================
51 // function : OpenGl_Texture
52 // purpose  :
53 // =======================================================================
54 OpenGl_Texture::OpenGl_Texture (const TCollection_AsciiString& theResourceId,
55                                 const Handle(Graphic3d_TextureParams)& theParams)
56 : OpenGl_NamedResource (theResourceId),
57   mySampler (new OpenGl_Sampler (theParams)),
58   myRevision (0),
59   myTextureId (NO_TEXTURE),
60   myTarget (GL_TEXTURE_2D),
61   mySizeX (0),
62   mySizeY (0),
63   mySizeZ (0),
64   myTextFormat (GL_RGBA),
65   mySizedFormat(GL_RGBA8),
66   myNbSamples  (1),
67   myHasMipmaps (Standard_False),
68   myIsAlpha    (false)
69 {
70   //
71 }
72
73 // =======================================================================
74 // function : ~OpenGl_Texture
75 // purpose  :
76 // =======================================================================
77 OpenGl_Texture::~OpenGl_Texture()
78 {
79   Release (NULL);
80 }
81
82 // =======================================================================
83 // function : Create
84 // purpose  :
85 // =======================================================================
86 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& theCtx)
87 {
88   if (myTextureId != NO_TEXTURE)
89   {
90     return true;
91   }
92
93   theCtx->core11fwd->glGenTextures (1, &myTextureId);
94   if (myTextureId == NO_TEXTURE)
95   {
96     return false;
97   }
98
99   //mySampler->Create (theCtx); // do not create sampler object by default
100   return true;
101 }
102
103 // =======================================================================
104 // function : Release
105 // purpose  :
106 // =======================================================================
107 void OpenGl_Texture::Release (OpenGl_Context* theGlCtx)
108 {
109   mySampler->Release (theGlCtx);
110   if (myTextureId == NO_TEXTURE)
111   {
112     return;
113   }
114
115   // application can not handle this case by exception - this is bug in code
116   Standard_ASSERT_RETURN (theGlCtx != NULL,
117     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
118
119   if (theGlCtx->IsValid())
120   {
121     glDeleteTextures (1, &myTextureId);
122   }
123   myTextureId = NO_TEXTURE;
124   mySizeX = mySizeY = mySizeZ = 0;
125 }
126
127 // =======================================================================
128 // function : applyDefaultSamplerParams
129 // purpose  :
130 // =======================================================================
131 void OpenGl_Texture::applyDefaultSamplerParams (const Handle(OpenGl_Context)& theCtx)
132 {
133   OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), NULL, myTarget, myHasMipmaps);
134   if (mySampler->IsValid() && !mySampler->IsImmutable())
135   {
136     OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), mySampler.get(), myTarget, myHasMipmaps);
137   }
138 }
139
140 // =======================================================================
141 // function : Bind
142 // purpose  :
143 // =======================================================================
144 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
145                            const Graphic3d_TextureUnit   theTextureUnit) const
146 {
147   if (theCtx->core15fwd != NULL)
148   {
149     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
150   }
151   mySampler->Bind (theCtx, theTextureUnit);
152   glBindTexture (myTarget, myTextureId);
153 }
154
155 // =======================================================================
156 // function : Unbind
157 // purpose  :
158 // =======================================================================
159 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
160                              const Graphic3d_TextureUnit   theTextureUnit) const
161 {
162   if (theCtx->core15fwd != NULL)
163   {
164     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
165   }
166   mySampler->Unbind (theCtx, theTextureUnit);
167   glBindTexture (myTarget, NO_TEXTURE);
168 }
169
170 //=======================================================================
171 //function : InitSamplerObject
172 //purpose  :
173 //=======================================================================
174 bool OpenGl_Texture::InitSamplerObject (const Handle(OpenGl_Context)& theCtx)
175 {
176   return myTextureId != NO_TEXTURE
177       && mySampler->Init (theCtx, *this);
178 }
179
180 // =======================================================================
181 // function : Init
182 // purpose  :
183 // =======================================================================
184 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
185                            const OpenGl_TextureFormat&   theFormat,
186                            const Graphic3d_Vec2i&        theSizeXY,
187                            const Graphic3d_TypeOfTexture theType,
188                            const Image_PixMap*           theImage)
189 {
190   if (theSizeXY.x() < 1
191    || theSizeXY.y() < 1)
192   {
193     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
194                          "Error: texture of 0 size cannot be created.");
195     Release (theCtx.get());
196     return false;
197   }
198
199 #if !defined(GL_ES_VERSION_2_0)
200   const GLenum aTarget = theType == Graphic3d_TOT_1D
201                        ? GL_TEXTURE_1D
202                        : GL_TEXTURE_2D;
203 #else
204   const GLenum aTarget = GL_TEXTURE_2D;
205 #endif
206   const Standard_Boolean toCreateMipMaps = (theType == Graphic3d_TOT_2D_MIPMAP);
207   const bool toPatchExisting = IsValid()
208                             && myTextFormat == theFormat.PixelFormat()
209                             && myTarget == aTarget
210                             && myHasMipmaps == toCreateMipMaps
211                             && mySizeX  == theSizeXY.x()
212                             && (mySizeY == theSizeXY.y() || theType == Graphic3d_TOT_1D);
213   if (!Create (theCtx))
214   {
215     Release (theCtx.get());
216     return false;
217   }
218
219   if (theImage != NULL)
220   {
221     myIsAlpha = theImage->Format() == Image_Format_Alpha
222              || theImage->Format() == Image_Format_AlphaF;
223   }
224   else
225   {
226     myIsAlpha = theFormat.PixelFormat() == GL_ALPHA;
227   }
228
229   myHasMipmaps             = toCreateMipMaps;
230   myTextFormat             = theFormat.PixelFormat();
231   mySizedFormat            = theFormat.InternalFormat();
232   myNbSamples              = 1;
233 #if !defined(GL_ES_VERSION_2_0)
234   const GLint anIntFormat  = theFormat.InternalFormat();
235 #else
236   // ES 2.0 does not support sized formats and format conversions - them detected from data type
237   const GLint anIntFormat  = theCtx->IsGlGreaterEqual (3, 0) ? theFormat.InternalFormat() : theFormat.PixelFormat();
238 #endif
239
240   if (theFormat.DataType() == GL_FLOAT
241   && !theCtx->arbTexFloat)
242   {
243     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
244                          "Error: floating-point textures are not supported by hardware.");
245     Release (theCtx.get());
246     return false;
247   }
248
249   const GLsizei aMaxSize = theCtx->MaxTextureSize();
250   if (theSizeXY.x() > aMaxSize
251    || theSizeXY.y() > aMaxSize)
252   {
253     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
254                          TCollection_AsciiString ("Error: Texture dimension - ") + theSizeXY.x() + "x" + theSizeXY.y()
255                        + " exceeds hardware limits (" + aMaxSize + "x" + aMaxSize + ")");
256     Release (theCtx.get());
257     return false;
258   }
259 #if !defined(GL_ES_VERSION_2_0)
260   else if (!theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW)
261   {
262     // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
263     // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
264     // Trying to create NPOT textures on such hardware will not fail
265     // but driver will fall back into software rendering,
266     const GLsizei aWidthP2  = OpenGl_Context::GetPowerOfTwo (theSizeXY.x(), aMaxSize);
267     const GLsizei aHeightP2 = OpenGl_Context::GetPowerOfTwo (theSizeXY.y(), aMaxSize);
268     if (theSizeXY.x() != aWidthP2
269      || (theType != Graphic3d_TOT_1D && theSizeXY.y() != aHeightP2))
270     {
271       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
272                            TCollection_AsciiString ("Error: NPOT Textures (") + theSizeXY.x() + "x" + theSizeXY.y() + ")"
273                            " are not supported by hardware.");
274       Release (theCtx.get());
275       return false;
276     }
277   }
278 #else
279   else if (!theCtx->IsGlGreaterEqual (3, 0) && theType == Graphic3d_TOT_2D_MIPMAP)
280   {
281     // Mipmap NPOT textures are not supported by OpenGL ES 2.0.
282     const GLsizei aWidthP2  = OpenGl_Context::GetPowerOfTwo (theSizeXY.x(), aMaxSize);
283     const GLsizei aHeightP2 = OpenGl_Context::GetPowerOfTwo (theSizeXY.y(), aMaxSize);
284     if (theSizeXY.x() != aWidthP2
285      || theSizeXY.y() != aHeightP2)
286     {
287       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
288                            TCollection_AsciiString ("Error: Mipmap NPOT Textures (") + theSizeXY.x() + "x" + theSizeXY.y() + ")"
289                            " are not supported by OpenGL ES 2.0");
290       Release (theCtx.get());
291       return false;
292     }
293   }
294 #endif
295
296 #if !defined(GL_ES_VERSION_2_0)
297   GLint aTestWidth  = 0;
298   GLint aTestHeight = 0;
299 #endif
300   GLvoid* aDataPtr = (theImage != NULL) ? (GLvoid* )theImage->Data() : NULL;
301
302   // setup the alignment
303   OpenGl_UnpackAlignmentSentry anUnpackSentry;
304   (void)anUnpackSentry; // avoid compiler warning
305
306   if (aDataPtr != NULL)
307   {
308     const GLint anAligment = Min ((GLint )theImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
309     glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
310
311   #if !defined(GL_ES_VERSION_2_0)
312     // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
313     const GLint anExtraBytes = GLint(theImage->RowExtraBytes());
314     const GLint aPixelsWidth = GLint(theImage->SizeRowBytes() / theImage->SizePixelBytes());
315     glPixelStorei (GL_UNPACK_ROW_LENGTH, (anExtraBytes >= anAligment) ? aPixelsWidth : 0);
316   #endif
317   }
318
319   myTarget = aTarget;
320   switch (theType)
321   {
322     case Graphic3d_TOT_1D:
323     {
324     #if !defined(GL_ES_VERSION_2_0)
325       Bind (theCtx);
326       applyDefaultSamplerParams (theCtx);
327       if (toPatchExisting)
328       {
329         glTexSubImage1D (GL_TEXTURE_1D, 0, 0,
330                          theSizeXY.x(), theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
331         Unbind (theCtx);
332         return true;
333       }
334
335       // use proxy to check texture could be created or not
336       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, anIntFormat,
337                     theSizeXY.x(), 0,
338                     theFormat.PixelFormat(), theFormat.DataType(), NULL);
339       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
340       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
341       if (aTestWidth == 0)
342       {
343         // no memory or broken input parameters
344         Unbind (theCtx);
345         Release (theCtx.operator->());
346         return false;
347       }
348
349       glTexImage1D (GL_TEXTURE_1D, 0, anIntFormat,
350                     theSizeXY.x(), 0,
351                     theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
352       if (glGetError() != GL_NO_ERROR)
353       {
354         Unbind (theCtx);
355         Release (theCtx.get());
356         return false;
357       }
358
359       mySizeX = theSizeXY.x();
360       mySizeY = 1;
361
362       Unbind (theCtx);
363       return true;
364     #else
365       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
366                            "Error: 1D textures are not supported by hardware.");
367       Release (theCtx.get());
368       return false;
369     #endif
370     }
371     case Graphic3d_TOT_2D:
372     {
373       Bind (theCtx);
374       applyDefaultSamplerParams (theCtx);
375       if (toPatchExisting)
376       {
377         glTexSubImage2D (GL_TEXTURE_2D, 0,
378                          0, 0,
379                          theSizeXY.x(), theSizeXY.y(),
380                          theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
381         Unbind (theCtx);
382         return true;
383       }
384
385     #if !defined(GL_ES_VERSION_2_0)
386       // use proxy to check texture could be created or not
387       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, anIntFormat,
388                     theSizeXY.x(), theSizeXY.y(), 0,
389                     theFormat.PixelFormat(), theFormat.DataType(), NULL);
390       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
391       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
392       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
393       if (aTestWidth == 0 || aTestHeight == 0)
394       {
395         // no memory or broken input parameters
396         Unbind (theCtx);
397         Release (theCtx.get());
398         return false;
399       }
400     #endif
401
402       glTexImage2D (GL_TEXTURE_2D, 0, anIntFormat,
403                     theSizeXY.x(), theSizeXY.y(), 0,
404                     theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
405       const GLenum anErr = glGetError();
406       if (anErr != GL_NO_ERROR)
407       {
408         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
409                              TCollection_AsciiString ("Error: 2D texture ") + theSizeXY.x() + "x" + theSizeXY.y()
410                                                    + " IF: " + int(anIntFormat) + " PF: " + int(theFormat.PixelFormat())
411                                                    + " DT: " + int(theFormat.DataType())
412                                                    + " can not be created with error " + int(anErr) + ".");
413         Unbind (theCtx);
414         Release (theCtx.get());
415         return false;
416       }
417
418       mySizeX = theSizeXY.x();
419       mySizeY = theSizeXY.y();
420
421       Unbind (theCtx);
422       return true;
423     }
424     case Graphic3d_TOT_2D_MIPMAP:
425     {
426       Bind (theCtx);
427       applyDefaultSamplerParams (theCtx);
428       if (toPatchExisting)
429       {
430         glTexSubImage2D (GL_TEXTURE_2D, 0,
431                          0, 0,
432                          theSizeXY.x(), theSizeXY.y(),
433                          theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
434         if (theCtx->arbFBO != NULL)
435         {
436           // generate mipmaps
437           theCtx->arbFBO->glGenerateMipmap (GL_TEXTURE_2D);
438           if (glGetError() != GL_NO_ERROR)
439           {
440             Unbind (theCtx);
441             Release (theCtx.get());
442             return false;
443           }
444         }
445
446         Unbind (theCtx);
447         return true;
448       }
449
450     #if !defined(GL_ES_VERSION_2_0)
451       // use proxy to check texture could be created or not
452       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, anIntFormat,
453                     theSizeXY.x(), theSizeXY.y(), 0,
454                     theFormat.PixelFormat(), theFormat.DataType(), NULL);
455       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
456       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
457       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
458       if (aTestWidth == 0 || aTestHeight == 0)
459       {
460         // no memory or broken input parameters
461         Unbind (theCtx);
462         Release (theCtx.get());
463         return false;
464       }
465     #endif
466
467       // upload main picture
468       glTexImage2D (GL_TEXTURE_2D, 0, anIntFormat,
469                     theSizeXY.x(), theSizeXY.y(), 0,
470                     theFormat.PixelFormat(), theFormat.DataType(), theImage->Data());
471       const GLenum aTexImgErr = glGetError();
472       if (aTexImgErr != GL_NO_ERROR)
473       {
474         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
475                              TCollection_AsciiString ("Error: 2D texture ") + theSizeXY.x() + "x" + theSizeXY.y()
476                                                     + " IF: " + int(anIntFormat) + " PF: " + int(theFormat.PixelFormat())
477                                                     + " DT: " + int(theFormat.DataType())
478                                                     + " can not be created with error " + int(aTexImgErr) + ".");
479         Unbind (theCtx);
480         Release (theCtx.get());
481         return false;
482       }
483
484       mySizeX = theSizeXY.x();
485       mySizeY = theSizeXY.y();
486
487       if (theCtx->arbFBO != NULL)
488       {
489         // generate mipmaps
490         //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
491         theCtx->arbFBO->glGenerateMipmap (GL_TEXTURE_2D);
492
493         if (glGetError() != GL_NO_ERROR)
494         {
495           Unbind (theCtx);
496           Release (theCtx.get());
497           return false;
498         }
499       }
500       else
501       {
502         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
503                              "Warning: generating mipmaps requires GL_ARB_framebuffer_object extension which is missing.");
504         Unbind (theCtx);
505         Release (theCtx.get());
506         return false;
507       }
508
509       Unbind (theCtx);
510       return true;
511     }
512     case Graphic3d_TOT_CUBEMAP:
513     {
514       Unbind (theCtx);
515       Release (theCtx.get());
516       return false;
517     }
518   }
519
520   Release (theCtx.get());
521   return false;
522 }
523
524 // =======================================================================
525 // function : Init
526 // purpose  :
527 // =======================================================================
528 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
529                            const Image_PixMap&           theImage,
530                            const Graphic3d_TypeOfTexture theType,
531                            const Standard_Boolean        theIsColorMap)
532 {
533   if (theImage.IsEmpty())
534   {
535     Release (theCtx.get());
536     return false;
537   }
538
539   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theImage.Format(), theIsColorMap);
540   if (!aFormat.IsValid())
541   {
542     Release (theCtx.get());
543     return false;
544   }
545
546   return Init (theCtx, aFormat, Graphic3d_Vec2i ((Standard_Integer)theImage.SizeX(), (Standard_Integer)theImage.SizeY()),
547                theType, &theImage);
548 }
549
550 // =======================================================================
551 // function : Init
552 // purpose  :
553 // =======================================================================
554 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)&       theCtx,
555                            const Handle(Graphic3d_TextureMap)& theTextureMap)
556 {
557   if (theTextureMap.IsNull())
558   {
559     return false;
560   }
561
562   switch (theTextureMap->Type())
563   {
564     case Graphic3d_TOT_CUBEMAP:
565     {
566       return InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)::DownCast(theTextureMap),
567                           0, Image_Format_RGB, false, theTextureMap->IsColorMap());
568     }
569     default:
570     {
571       Handle(Image_PixMap) anImage = theTextureMap->GetImage();
572       if (anImage.IsNull())
573       {
574         return false;
575       }
576       return Init (theCtx, *anImage, theTextureMap->Type(), theTextureMap->IsColorMap());
577     }
578   }
579 }
580
581 // =======================================================================
582 // function : Init2DMultisample
583 // purpose  :
584 // =======================================================================
585 bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
586                                         const GLsizei                 theNbSamples,
587                                         const GLint                   theTextFormat,
588                                         const GLsizei                 theSizeX,
589                                         const GLsizei                 theSizeY)
590 {
591   if (!Create (theCtx)
592     || theNbSamples > theCtx->MaxMsaaSamples()
593     || theNbSamples < 1)
594   {
595     return false;
596   }
597
598   myNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
599   myTarget = GL_TEXTURE_2D_MULTISAMPLE;
600   myHasMipmaps = false;
601   if(theSizeX > theCtx->MaxTextureSize()
602   || theSizeY > theCtx->MaxTextureSize())
603   {
604     return false;
605   }
606
607   Bind (theCtx);
608   //myTextFormat = theTextFormat;
609   mySizedFormat = theTextFormat;
610 #if !defined(GL_ES_VERSION_2_0)
611   if (theCtx->Functions()->glTexStorage2DMultisample != NULL)
612   {
613     theCtx->Functions()->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
614   }
615   else
616   {
617     theCtx->Functions()->glTexImage2DMultisample   (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
618   }
619 #else
620   theCtx->Functions()  ->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
621 #endif
622   if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
623   {
624     Unbind (theCtx);
625     return false;
626   }
627
628   mySizeX = theSizeX;
629   mySizeY = theSizeY;
630
631   Unbind (theCtx);
632   return true;
633 }
634
635 // =======================================================================
636 // function : InitRectangle
637 // purpose  :
638 // =======================================================================
639 bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
640                                     const Standard_Integer        theSizeX,
641                                     const Standard_Integer        theSizeY,
642                                     const OpenGl_TextureFormat&   theFormat)
643 {
644   if (!Create (theCtx) || !theCtx->IsGlGreaterEqual (3, 0))
645   {
646     return false;
647   }
648
649 #if !defined(GL_ES_VERSION_2_0)
650   myTarget = GL_TEXTURE_RECTANGLE;
651   myNbSamples = 1;
652   myHasMipmaps = false;
653
654   const GLsizei aSizeX    = Min (theCtx->MaxTextureSize(), theSizeX);
655   const GLsizei aSizeY    = Min (theCtx->MaxTextureSize(), theSizeY);
656
657   Bind (theCtx);
658   applyDefaultSamplerParams (theCtx);
659
660   myTextFormat  = theFormat.Format();
661   mySizedFormat = theFormat.Internal();
662
663   // setup the alignment
664   OpenGl_UnpackAlignmentSentry::Reset();
665
666   glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE, 0, mySizedFormat,
667                 aSizeX, aSizeY, 0,
668                 myTextFormat, GL_FLOAT, NULL);
669
670   GLint aTestSizeX = 0;
671   GLint aTestSizeY = 0;
672
673   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH,  &aTestSizeX);
674   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
675   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
676
677   if (aTestSizeX == 0 || aTestSizeY == 0)
678   {
679     Unbind (theCtx);
680     return false;
681   }
682
683   glTexImage2D (myTarget, 0, mySizedFormat,
684                 aSizeX, aSizeY, 0,
685                 myTextFormat, GL_FLOAT, NULL);
686
687   if (glGetError() != GL_NO_ERROR)
688   {
689     Unbind (theCtx);
690     return false;
691   }
692
693   mySizeX = aSizeX;
694   mySizeY = aSizeY;
695
696   Unbind (theCtx);
697   return true;
698 #else
699   (void )theSizeX;
700   (void )theSizeY;
701   (void )theFormat;
702   return false;
703 #endif
704 }
705
706 // =======================================================================
707 // function : Init3D
708 // purpose  :
709 // =======================================================================
710 bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
711                              const OpenGl_TextureFormat&   theFormat,
712                              const Graphic3d_Vec3i&        theSizeXYZ,
713                              const void*                   thePixels)
714 {
715   if (theCtx->Functions()->glTexImage3D == NULL)
716   {
717     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
718                          "Error: three-dimensional textures are not supported by hardware.");
719     return false;
720   }
721
722   if (!Create(theCtx))
723   {
724     return false;
725   }
726
727   myTarget = GL_TEXTURE_3D;
728   myNbSamples = 1;
729   myHasMipmaps = false;
730
731   const Graphic3d_Vec3i aSizeXYZ = theSizeXYZ.cwiseMin (Graphic3d_Vec3i (theCtx->MaxTextureSize()));
732   if (aSizeXYZ != theSizeXYZ)
733   {
734     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
735                          "Error: 3D texture dimensions exceed hardware limits.");
736     Release (theCtx.get());
737     Unbind (theCtx);
738     return false;
739   }
740   Bind (theCtx);
741
742   if (theFormat.DataType() == GL_FLOAT
743   && !theCtx->arbTexFloat)
744   {
745     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
746                          "Error: floating-point textures are not supported by hardware.");
747     Release (theCtx.get());
748     Unbind (theCtx);
749     return false;
750   }
751
752   mySizedFormat = theFormat.InternalFormat();
753
754   // setup the alignment
755   OpenGl_UnpackAlignmentSentry::Reset();
756
757 #if !defined (GL_ES_VERSION_2_0)
758   theCtx->core15fwd->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, mySizedFormat,
759                                    aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
760                                    theFormat.PixelFormat(), theFormat.DataType(), NULL);
761
762   NCollection_Vec3<GLint> aTestSizeXYZ;
763   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH,  &aTestSizeXYZ.x());
764   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
765   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH,  &aTestSizeXYZ.z());
766   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
767   if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
768   {
769     Unbind (theCtx);
770     Release (theCtx.get());
771     return false;
772   }
773 #endif
774
775   applyDefaultSamplerParams (theCtx);
776   theCtx->Functions()->glTexImage3D (myTarget, 0, mySizedFormat,
777                                      aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
778                                      theFormat.PixelFormat(), theFormat.DataType(), thePixels);
779
780   if (glGetError() != GL_NO_ERROR)
781   {
782     Unbind (theCtx);
783     Release (theCtx.get());
784     return false;
785   }
786
787   mySizeX = aSizeXYZ.x();
788   mySizeY = aSizeXYZ.y();
789   mySizeZ = aSizeXYZ.z();
790
791   Unbind (theCtx);
792   return true;
793 }
794
795 // =======================================================================
796 // function : InitCubeMap
797 // purpose  :
798 // =======================================================================
799 bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)&    theCtx,
800                                   const Handle(Graphic3d_CubeMap)& theCubeMap,
801                                   Standard_Size    theSize,
802                                   Image_Format     theFormat,
803                                   Standard_Boolean theToGenMipmap,
804                                   Standard_Boolean theIsColorMap)
805 {
806   if (!Create (theCtx))
807   {
808     Release (theCtx.get());
809     return false;
810   }
811
812   if (!theCubeMap.IsNull())
813   {
814     theToGenMipmap = theCubeMap->HasMipmaps();
815     if (Handle(Image_PixMap) anImage = theCubeMap->Reset().Value())
816     {
817       theSize   = anImage->SizeX();
818       theFormat = anImage->Format();
819     }
820     else
821     {
822       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
823                            "Unable to get the first side of cubemap");
824       Release(theCtx.get());
825       return false;
826     }
827   }
828
829   OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theFormat, theIsColorMap);
830   if (!aFormat.IsValid())
831   {
832     Unbind(theCtx);
833     Release(theCtx.get());
834     return false;
835   }
836
837   myTarget = GL_TEXTURE_CUBE_MAP;
838   myHasMipmaps = theToGenMipmap;
839   myNbSamples = 1;
840   mySizeX = (GLsizei )theSize;
841   mySizeY = (GLsizei )theSize;
842   Bind (theCtx);
843   applyDefaultSamplerParams (theCtx);
844
845   for (Standard_Integer i = 0; i < 6; ++i)
846   {
847     const void* aData = NULL;
848     Handle(Image_PixMap) anImage;
849
850     if (!theCubeMap.IsNull())
851     {
852       anImage = theCubeMap->Value();
853       if (!anImage.IsNull())
854       {
855 #if !defined(GL_ES_VERSION_2_0)
856         const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
857         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
858
859         // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
860         const GLint anExtraBytes = GLint(anImage->RowExtraBytes());
861         const GLint aPixelsWidth = GLint(anImage->SizeRowBytes() / anImage->SizePixelBytes());
862         const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
863         glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
864 #else
865         Handle(Image_PixMap) aCopyImage = new Image_PixMap();
866         aCopyImage->InitTrash (theFormat, theSize, theSize);
867         for (unsigned int y = 0; y < theSize; ++y)
868         {
869           for (unsigned int x = 0; x < theSize; ++x)
870           {
871             for (unsigned int aByte = 0; aByte < anImage->SizePixelBytes(); ++aByte)
872             {
873               aCopyImage->ChangeRawValue (y, x)[aByte] = anImage->RawValue (y, x)[aByte];
874             }
875           }
876         }
877         anImage = aCopyImage;
878         const GLint anAligment = Min((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
879         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
880 #endif
881         aData = anImage->Data();
882       }
883       else
884       {
885         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
886                              TCollection_AsciiString() + "Unable to get [" + i + "] side of cubemap");
887         Unbind (theCtx);
888         Release (theCtx.get());
889         return false;
890       }
891       theCubeMap->Next();
892     }
893
894     glTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
895                   aFormat.InternalFormat(),
896                   GLsizei(theSize), GLsizei(theSize),
897                   0, aFormat.PixelFormat(), aFormat.DataType(),
898                   aData);
899
900     OpenGl_UnpackAlignmentSentry::Reset();
901
902     const GLenum anErr = glGetError();
903     if (anErr != GL_NO_ERROR)
904     {
905       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
906                            TCollection_AsciiString ("Unable to initialize side of cubemap. Error #") + int(anErr));
907       Unbind (theCtx);
908       Release (theCtx.get());
909       return false;
910     }
911   }
912
913   if (theToGenMipmap && theCtx->arbFBO != NULL)
914   {
915     theCtx->arbFBO->glGenerateMipmap (myTarget);
916     const GLenum anErr = glGetError();
917     if (anErr != GL_NO_ERROR)
918     {
919       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
920                            TCollection_AsciiString ("Unable to generate mipmap of cubemap. Error #") + int(anErr));
921       Unbind (theCtx);
922       Release (theCtx.get());
923       return false;
924     }
925   }
926
927   Unbind (theCtx.get());
928   return true;
929 }
930
931 // =======================================================================
932 // function : PixelSizeOfPixelFormat
933 // purpose  :
934 // =======================================================================
935 Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
936 {
937   switch(theInternalFormat)
938   {
939     // RED variations (GL_RED, OpenGL 3.0+)
940     case GL_RED:
941     case GL_R8:       return 1;
942     case GL_R16:      return 2;
943     case GL_R16F:     return 2;
944     case GL_R32F:     return 4;
945     // RGB variations
946     case GL_RGB:      return 3;
947     case GL_RGB8:     return 3;
948     case GL_RGB16:    return 6;
949     case GL_RGB16F:   return 6;
950     case GL_RGB32F:   return 12;
951     // RGBA variations
952     case GL_RGBA:     return 4;
953     case GL_RGBA8:    return 4;
954     case GL_RGB10_A2: return 4;
955     case GL_RGBA12:   return 6;
956     case GL_RGBA16:   return 8;
957     case GL_RGBA16F:  return 8;
958     case GL_RGBA32F:  return 16;
959     //
960     case GL_BGRA_EXT:  return 4;
961     // ALPHA variations (deprecated)
962     case GL_ALPHA:
963     case GL_ALPHA8:    return 1;
964     case GL_ALPHA16:   return 2;
965     case GL_LUMINANCE: return 1;
966     case GL_LUMINANCE_ALPHA: return 2;
967     // depth-stencil
968     case GL_DEPTH24_STENCIL8:   return 4;
969     case GL_DEPTH32F_STENCIL8:  return 8;
970     case GL_DEPTH_COMPONENT16:  return 2;
971     case GL_DEPTH_COMPONENT24:  return 3;
972     case GL_DEPTH_COMPONENT32F: return 4;
973   }
974   return 0;
975 }
976
977 // =======================================================================
978 // function : EstimatedDataSize
979 // purpose  :
980 // =======================================================================
981 Standard_Size OpenGl_Texture::EstimatedDataSize() const
982 {
983   if (!IsValid())
984   {
985     return 0;
986   }
987
988   Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySizeX * myNbSamples;
989   if (mySizeY != 0)
990   {
991     aSize *= Standard_Size(mySizeY);
992   }
993   if (mySizeZ != 0)
994   {
995     aSize *= Standard_Size(mySizeZ);
996   }
997   if (myTarget == GL_TEXTURE_CUBE_MAP)
998   {
999     aSize *= 6; // cube sides
1000   }
1001   if (myHasMipmaps)
1002   {
1003     aSize = aSize + aSize / 3;
1004   }
1005   return aSize;
1006 }