0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[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_GlCore45.hxx>
20 #include <OpenGl_Sampler.hxx>
21 #include <Graphic3d_TextureParams.hxx>
22 #include <Standard_Assert.hxx>
23 #include <Image_CompressedPixMap.hxx>
24 #include <Image_PixMap.hxx>
25 #include <Image_SupportedFormats.hxx>
26
27 #include <algorithm>
28
29 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_NamedResource)
30
31 namespace
32 {
33
34 //! Simple class to reset unpack alignment settings
35 struct OpenGl_UnpackAlignmentSentry
36 {
37   //! Reset unpack alignment settings to safe values
38   static void Reset (const OpenGl_Context& theCtx)
39   {
40     theCtx.core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
41     if (theCtx.hasUnpackRowLength)
42     {
43       theCtx.core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
44     }
45   }
46
47   OpenGl_UnpackAlignmentSentry (const Handle(OpenGl_Context)& theCtx)
48   : myCtx (theCtx.get()) {}
49
50   ~OpenGl_UnpackAlignmentSentry()
51   {
52     Reset (*myCtx);
53   }
54
55 private:
56   OpenGl_Context* myCtx;
57 };
58
59 //! Compute the upper mipmap level for complete mipmap set (e.g. till the 1x1 level).
60 static Standard_Integer computeUpperMipMapLevel (Standard_Integer theSize)
61 {
62   for (Standard_Integer aMipIter = 0;; ++aMipIter, theSize /= 2)
63   {
64     if (theSize <= 1)
65     {
66       return aMipIter;
67     }
68   }
69 }
70
71 //! Compute size of the smallest defined mipmap level (for verbose messages).
72 static Graphic3d_Vec2i computeSmallestMipMapSize (const Graphic3d_Vec2i& theBaseSize, Standard_Integer theMaxLevel)
73 {
74   Graphic3d_Vec2i aMipSizeXY = theBaseSize;
75   for (Standard_Integer aMipIter = 0;; ++aMipIter)
76   {
77     if (aMipIter > theMaxLevel)
78     {
79       return aMipSizeXY;
80     }
81
82     aMipSizeXY /= 2;
83     if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
84     if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
85   }
86 }
87
88 }
89
90 // =======================================================================
91 // function : OpenGl_Texture
92 // purpose  :
93 // =======================================================================
94 OpenGl_Texture::OpenGl_Texture (const TCollection_AsciiString& theResourceId,
95                                 const Handle(Graphic3d_TextureParams)& theParams)
96 : OpenGl_NamedResource (theResourceId),
97   mySampler (new OpenGl_Sampler (theParams)),
98   myRevision (0),
99   myTextureId (NO_TEXTURE),
100   myTarget (GL_TEXTURE_2D),
101   myTextFormat (GL_RGBA),
102   mySizedFormat(GL_RGBA8),
103   myNbSamples  (1),
104   myMaxMipLevel(0),
105   myIsAlpha    (false),
106   myIsTopDown  (true)
107 {
108   //
109 }
110
111 // =======================================================================
112 // function : ~OpenGl_Texture
113 // purpose  :
114 // =======================================================================
115 OpenGl_Texture::~OpenGl_Texture()
116 {
117   Release (NULL);
118 }
119
120 // =======================================================================
121 // function : Create
122 // purpose  :
123 // =======================================================================
124 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& theCtx)
125 {
126   if (myTextureId != NO_TEXTURE)
127   {
128     return true;
129   }
130
131   theCtx->core11fwd->glGenTextures (1, &myTextureId);
132   if (myTextureId == NO_TEXTURE)
133   {
134     return false;
135   }
136
137   //mySampler->Create (theCtx); // do not create sampler object by default
138   return true;
139 }
140
141 // =======================================================================
142 // function : Release
143 // purpose  :
144 // =======================================================================
145 void OpenGl_Texture::Release (OpenGl_Context* theGlCtx)
146 {
147   mySampler->Release (theGlCtx);
148   if (myTextureId == NO_TEXTURE)
149   {
150     return;
151   }
152
153   // application can not handle this case by exception - this is bug in code
154   Standard_ASSERT_RETURN (theGlCtx != NULL,
155     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
156
157   if (theGlCtx->IsValid())
158   {
159     theGlCtx->core11fwd->glDeleteTextures (1, &myTextureId);
160   }
161   myTextureId = NO_TEXTURE;
162   mySize.SetValues (0, 0, 0);
163 }
164
165 // =======================================================================
166 // function : applyDefaultSamplerParams
167 // purpose  :
168 // =======================================================================
169 void OpenGl_Texture::applyDefaultSamplerParams (const Handle(OpenGl_Context)& theCtx)
170 {
171   OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), NULL, myTarget, myMaxMipLevel);
172   if (mySampler->IsValid() && !mySampler->IsImmutable())
173   {
174     OpenGl_Sampler::applySamplerParams (theCtx, mySampler->Parameters(), mySampler.get(), myTarget, myMaxMipLevel);
175   }
176 }
177
178 // =======================================================================
179 // function : Bind
180 // purpose  :
181 // =======================================================================
182 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
183                            const Graphic3d_TextureUnit   theTextureUnit) const
184 {
185   if (theCtx->core15fwd != NULL)
186   {
187     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
188   }
189   mySampler->Bind (theCtx, theTextureUnit);
190   theCtx->core11fwd->glBindTexture (myTarget, myTextureId);
191 }
192
193 // =======================================================================
194 // function : Unbind
195 // purpose  :
196 // =======================================================================
197 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
198                              const Graphic3d_TextureUnit   theTextureUnit) const
199 {
200   if (theCtx->core15fwd != NULL)
201   {
202     theCtx->core15fwd->glActiveTexture (GL_TEXTURE0 + theTextureUnit);
203   }
204   mySampler->Unbind (theCtx, theTextureUnit);
205   theCtx->core11fwd->glBindTexture (myTarget, NO_TEXTURE);
206 }
207
208 //=======================================================================
209 //function : InitSamplerObject
210 //purpose  :
211 //=======================================================================
212 bool OpenGl_Texture::InitSamplerObject (const Handle(OpenGl_Context)& theCtx)
213 {
214   return myTextureId != NO_TEXTURE
215       && mySampler->Init (theCtx, *this);
216 }
217
218 // =======================================================================
219 // function : Init
220 // purpose  :
221 // =======================================================================
222 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
223                            const OpenGl_TextureFormat&   theFormat,
224                            const Graphic3d_Vec3i&        theSizeXYZ,
225                            const Graphic3d_TypeOfTexture theType,
226                            const Image_PixMap*           theImage)
227 {
228   if (theSizeXYZ.x() < 1
229    || theSizeXYZ.y() < 1
230    || theSizeXYZ.z() < 1)
231   {
232     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
233                          TCollection_AsciiString ("Error: texture of 0 size cannot be created [") + myResourceId +"]");
234     Release (theCtx.get());
235     return false;
236   }
237
238   GLenum aTarget = GL_TEXTURE_2D;
239   switch (theType)
240   {
241     case Graphic3d_TypeOfTexture_1D:
242     {
243       aTarget = theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
244               ? GL_TEXTURE_1D
245               : GL_TEXTURE_2D;
246       break;
247     }
248     case Graphic3d_TypeOfTexture_2D:
249     case Graphic3d_TOT_2D_MIPMAP:
250     {
251       aTarget = GL_TEXTURE_2D;
252       break;
253     }
254     case Graphic3d_TypeOfTexture_3D:
255     {
256       aTarget = GL_TEXTURE_3D;
257       break;
258     }
259     case Graphic3d_TypeOfTexture_CUBEMAP:
260     {
261       aTarget = GL_TEXTURE_CUBE_MAP;
262       break;
263     }
264   }
265   const bool toPatchExisting = IsValid()
266                             && myTextFormat == theFormat.PixelFormat()
267                             && myTarget == aTarget
268                             && mySize.x()  == theSizeXYZ.x()
269                             && (mySize.y() == theSizeXYZ.y() || theType == Graphic3d_TypeOfTexture_1D)
270                             && mySize.z()  == theSizeXYZ.z();
271   if (!Create (theCtx))
272   {
273     Release (theCtx.get());
274     return false;
275   }
276
277   if (theImage != NULL)
278   {
279     myIsAlpha = theImage->Format() == Image_Format_Alpha
280              || theImage->Format() == Image_Format_AlphaF;
281     myIsTopDown = theImage->IsTopDown();
282   }
283   else
284   {
285     myIsAlpha = theFormat.PixelFormat() == GL_ALPHA;
286   }
287
288   myMaxMipLevel = 0;
289   myTextFormat  = theFormat.PixelFormat();
290   mySizedFormat = theFormat.InternalFormat();
291   myNbSamples   = 1;
292
293   // ES 2.0 does not support sized formats and format conversions - them detected from data type
294   const GLint anIntFormat  = (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
295                            || theCtx->IsGlGreaterEqual (3, 0))
296                            ? theFormat.InternalFormat()
297                            : theFormat.PixelFormat();
298
299   if (theFormat.DataType() == GL_FLOAT
300   && !theCtx->arbTexFloat)
301   {
302     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
303                          TCollection_AsciiString ("Error: floating-point textures are not supported by hardware [") + myResourceId +"]");
304     Release (theCtx.get());
305     return false;
306   }
307
308   const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
309   if (theSizeXYZ.maxComp() > aMaxSize)
310   {
311     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
312                          TCollection_AsciiString ("Error: Texture dimension - ") + theSizeXYZ.x() + "x" + theSizeXYZ.y()
313                        + (theSizeXYZ.z() > 1 ? TCollection_AsciiString ("x") + theSizeXYZ.z() : TCollection_AsciiString())
314                        + " exceeds hardware limits (" + aMaxSize + "x" + aMaxSize + ")"
315                        + " [" + myResourceId +"]");
316     Release (theCtx.get());
317     return false;
318   }
319   else if (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGL
320        && !theCtx->IsGlGreaterEqual (3, 0)
321        && !theCtx->arbNPTW)
322   {
323     // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
324     // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
325     // Trying to create NPOT textures on such hardware will not fail
326     // but driver will fall back into software rendering,
327     const Graphic3d_Vec2i aSizeP2 (OpenGl_Context::GetPowerOfTwo (theSizeXYZ.x(), aMaxSize),
328                                    OpenGl_Context::GetPowerOfTwo (theSizeXYZ.y(), aMaxSize));
329     if (theSizeXYZ.x() != aSizeP2.x()
330      || (theType != Graphic3d_TypeOfTexture_1D && theSizeXYZ.y() != aSizeP2.y()))
331     {
332       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
333                            TCollection_AsciiString ("Error: NPOT Textures (") + theSizeXYZ.x() + "x" + theSizeXYZ.y() + ")"
334                            " are not supported by hardware [" + myResourceId +"]");
335       Release (theCtx.get());
336       return false;
337     }
338   }
339
340   GLint aTestWidth = 0, aTestHeight = 0;
341   GLvoid* aDataPtr = (theImage != NULL) ? (GLvoid* )theImage->Data() : NULL;
342
343   // setup the alignment
344   OpenGl_UnpackAlignmentSentry anUnpackSentry (theCtx);
345   (void)anUnpackSentry; // avoid compiler warning
346
347   if (aDataPtr != NULL)
348   {
349     const GLint anAligment = Min ((GLint )theImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
350     theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
351     const GLint anExtraBytes = GLint(theImage->RowExtraBytes());
352     const GLint aPixelsWidth = GLint(theImage->SizeRowBytes() / theImage->SizePixelBytes());
353     if (theCtx->hasUnpackRowLength)
354     {
355       theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, (anExtraBytes >= anAligment) ? aPixelsWidth : 0);
356     }
357     else if (anExtraBytes >= anAligment)
358     {
359       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
360                            TCollection_AsciiString ("Error: unsupported image stride within OpenGL ES 2.0 [") + myResourceId +"]");
361       Release (theCtx.get());
362       return false;
363     }
364   }
365
366   myTarget = aTarget;
367   switch (theType)
368   {
369     case Graphic3d_TypeOfTexture_1D:
370     {
371       if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES)
372       {
373         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
374                              TCollection_AsciiString ( "Error: 1D textures are not supported by hardware [") + myResourceId +"]");
375         Release (theCtx.get());
376         return false;
377       }
378
379       Bind (theCtx);
380       applyDefaultSamplerParams (theCtx);
381       if (toPatchExisting)
382       {
383         theCtx->core11fwd->glTexSubImage1D (GL_TEXTURE_1D, 0, 0,
384                                             theSizeXYZ.x(), theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
385         break;
386       }
387
388       // use proxy to check texture could be created or not
389       theCtx->core11fwd->glTexImage1D (GL_PROXY_TEXTURE_1D, 0, anIntFormat,
390                                        theSizeXYZ.x(), 0,
391                                        theFormat.PixelFormat(), theFormat.DataType(), NULL);
392       theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
393       theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
394       if (aTestWidth == 0)
395       {
396         // no memory or broken input parameters
397         Unbind (theCtx);
398         Release (theCtx.operator->());
399         return false;
400       }
401
402       theCtx->core11fwd->glTexImage1D (GL_TEXTURE_1D, 0, anIntFormat,
403                                        theSizeXYZ.x(), 0,
404                                        theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
405       if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
406       {
407         Unbind (theCtx);
408         Release (theCtx.get());
409         return false;
410       }
411
412       mySize.SetValues (theSizeXYZ.x(), 1, 1);
413       break;
414     }
415     case Graphic3d_TypeOfTexture_2D:
416     case Graphic3d_TOT_2D_MIPMAP:
417     {
418       Bind (theCtx);
419       applyDefaultSamplerParams (theCtx);
420       if (toPatchExisting)
421       {
422         theCtx->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0,
423                                             0, 0,
424                                             theSizeXYZ.x(), theSizeXYZ.y(),
425                                             theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
426         break;
427       }
428
429       if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
430       {
431         // use proxy to check texture could be created or not
432         theCtx->core11fwd->glTexImage2D (GL_PROXY_TEXTURE_2D, 0, anIntFormat,
433                                          theSizeXYZ.x(), theSizeXYZ.y(), 0,
434                                          theFormat.PixelFormat(), theFormat.DataType(), NULL);
435         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
436         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
437         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
438         if (aTestWidth == 0 || aTestHeight == 0)
439         {
440           // no memory or broken input parameters
441           Unbind (theCtx);
442           Release (theCtx.get());
443           return false;
444         }
445       }
446
447       theCtx->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, anIntFormat,
448                                        theSizeXYZ.x(), theSizeXYZ.y(), 0,
449                                        theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
450       GLenum anErr = theCtx->core11fwd->glGetError();
451       if (anErr != GL_NO_ERROR)
452       {
453         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
454                              TCollection_AsciiString ("Error: 2D texture ") + theSizeXYZ.x() + "x" + theSizeXYZ.y()
455                                                    + " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
456                                                    + " PF: " + OpenGl_TextureFormat::FormatFormat (theFormat.PixelFormat())
457                                                    + " DT: " + OpenGl_TextureFormat::FormatDataType (theFormat.DataType())
458                                                    + " can not be created with error " + OpenGl_Context::FormatGlError (anErr)
459                                                    + " [" + myResourceId +"]");
460         Unbind (theCtx);
461         Release (theCtx.get());
462         return false;
463       }
464
465       mySize.SetValues (theSizeXYZ.xy(), 1);
466       break;
467     }
468     case Graphic3d_TypeOfTexture_3D:
469     {
470       if (theCtx->Functions()->glTexImage3D == nullptr)
471       {
472         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
473                              "Error: three-dimensional textures are not supported by hardware.");
474         Unbind (theCtx);
475         Release (theCtx.get());
476         return false;
477       }
478
479       Bind (theCtx);
480       applyDefaultSamplerParams (theCtx);
481       if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
482       {
483         theCtx->Functions()->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, anIntFormat,
484                                            theSizeXYZ.x(), theSizeXYZ.y(), theSizeXYZ.z(), 0,
485                                            theFormat.PixelFormat(), theFormat.DataType(),  nullptr);
486
487         NCollection_Vec3<GLint> aTestSizeXYZ;
488         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH,  &aTestSizeXYZ.x());
489         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
490         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH,  &aTestSizeXYZ.z());
491         theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
492         if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
493         {
494           Unbind (theCtx);
495           Release (theCtx.get());
496           return false;
497         }
498       }
499
500       theCtx->Functions()->glTexImage3D (GL_TEXTURE_3D, 0, anIntFormat,
501                                          theSizeXYZ.x(), theSizeXYZ.y(), theSizeXYZ.z(), 0,
502                                          theFormat.PixelFormat(), theFormat.DataType(), aDataPtr);
503       GLenum anErr = theCtx->core11fwd->glGetError();
504       if (anErr != GL_NO_ERROR)
505       {
506         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
507                              TCollection_AsciiString ("Error: 3D texture ") + theSizeXYZ.x() + "x" + theSizeXYZ.y() + "x" + theSizeXYZ.z()
508                                                    + " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
509                                                    + " PF: " + OpenGl_TextureFormat::FormatFormat (theFormat.PixelFormat())
510                                                    + " DT: " + OpenGl_TextureFormat::FormatDataType (theFormat.DataType())
511                                                    + " can not be created with error " + OpenGl_Context::FormatGlError (anErr)
512                                                    + " [" + myResourceId +"]");
513         Unbind (theCtx);
514         Release (theCtx.get());
515         return false;
516       }
517
518       mySize = theSizeXYZ;
519       break;
520     }
521     case Graphic3d_TypeOfTexture_CUBEMAP:
522     {
523       Unbind (theCtx);
524       Release (theCtx.get());
525       return false;
526     }
527   }
528
529   Unbind (theCtx);
530   return true;
531 }
532
533 // =======================================================================
534 // function : GenerateMipmaps
535 // purpose  :
536 // =======================================================================
537 bool OpenGl_Texture::GenerateMipmaps (const Handle(OpenGl_Context)& theCtx)
538 {
539   if (theCtx->arbFBO == nullptr
540   || !IsValid())
541   {
542     return false;
543   }
544
545   myMaxMipLevel = computeUpperMipMapLevel (mySize.maxComp());
546
547   const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
548   if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
549   && !theCtx->IsGlGreaterEqual (3, 0))
550   {
551     // Mipmap NPOT textures are not supported by OpenGL ES 2.0.
552     const Graphic3d_Vec2i aSizeP2 (OpenGl_Context::GetPowerOfTwo (mySize.x(), aMaxSize),
553                                    OpenGl_Context::GetPowerOfTwo (mySize.y(), aMaxSize));
554     if (mySize.xy() != aSizeP2)
555     {
556       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
557                            TCollection_AsciiString ("Warning: Mipmap NPOT Textures (") + mySize.x() + "x" + mySize.y() + ")"
558                            " are not supported by OpenGL ES 2.0 [" + myResourceId +"]");
559       myMaxMipLevel = 0;
560     }
561   }
562
563   if (myMaxMipLevel <= 0)
564   {
565     return false;
566   }
567
568   //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
569   Bind (theCtx);
570   if (theCtx->HasTextureBaseLevel()
571   && !mySampler->isValidSampler())
572   {
573     const Standard_Integer aMaxLevel = Min (myMaxMipLevel, mySampler->Parameters()->MaxLevel());
574     mySampler->SetParameter (theCtx, myTarget, GL_TEXTURE_MAX_LEVEL, aMaxLevel);
575   }
576   theCtx->arbFBO->glGenerateMipmap (myTarget);
577   GLenum anErr = theCtx->core11fwd->glGetError();
578   if (anErr != GL_NO_ERROR)
579   {
580     myMaxMipLevel = 0;
581     if (theCtx->HasTextureBaseLevel()
582     && !mySampler->isValidSampler())
583     {
584       mySampler->SetParameter (theCtx, myTarget, GL_TEXTURE_MAX_LEVEL, 0);
585     }
586
587     if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
588       && (mySizedFormat == GL_RGB8
589        || mySizedFormat == GL_SRGB8))
590     {
591       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
592                             TCollection_AsciiString ("Warning: generating mipmaps requires color-renderable format, while giving ")
593                             + OpenGl_TextureFormat::FormatFormat (mySizedFormat) + " [" + myResourceId +"]");
594     }
595     else
596     {
597       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
598                             TCollection_AsciiString ("Warning: generating mipmaps has failed [") + myResourceId +"]");
599     }
600   }
601
602   applyDefaultSamplerParams (theCtx);
603   Unbind (theCtx);
604   return true;
605 }
606
607 // =======================================================================
608 // function : Init
609 // purpose  :
610 // =======================================================================
611 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
612                            const Image_PixMap&           theImage,
613                            const Graphic3d_TypeOfTexture theType,
614                            const Standard_Boolean        theIsColorMap)
615 {
616   if (theImage.IsEmpty())
617   {
618     Release (theCtx.get());
619     return false;
620   }
621
622   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theImage.Format(), theIsColorMap);
623   if (!aFormat.IsValid())
624   {
625     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
626                          TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theImage.Format()) + " image format"
627                          + " [" + myResourceId +"]");
628     Release (theCtx.get());
629     return false;
630   }
631
632   return Init (theCtx, aFormat, Graphic3d_Vec3i (theImage.SizeXYZ()), theType, &theImage);
633 }
634
635 // =======================================================================
636 // function : Init
637 // purpose  :
638 // =======================================================================
639 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
640                            const Handle(Graphic3d_TextureRoot)& theTextureMap)
641 {
642   if (theTextureMap.IsNull())
643   {
644     return false;
645   }
646
647   switch (theTextureMap->Type())
648   {
649     case Graphic3d_TypeOfTexture_CUBEMAP:
650     {
651       return InitCubeMap (theCtx, Handle(Graphic3d_CubeMap)::DownCast(theTextureMap),
652                           0, Image_Format_RGB, false, theTextureMap->IsColorMap());
653     }
654     default:
655     {
656       if (theCtx->SupportedTextureFormats()->HasCompressed()
657       && !theCtx->caps->compressedTexturesDisable)
658       {
659         if (Handle(Image_CompressedPixMap) aCompressed = theTextureMap->GetCompressedImage (theCtx->SupportedTextureFormats()))
660         {
661           return InitCompressed (theCtx, *aCompressed, theTextureMap->IsColorMap());
662         }
663       }
664
665       Handle(Image_PixMap) anImage = theTextureMap->GetImage (theCtx->SupportedTextureFormats());
666       if (anImage.IsNull())
667       {
668         return false;
669       }
670       if (!Init (theCtx, *anImage, theTextureMap->Type(), theTextureMap->IsColorMap()))
671       {
672         return false;
673       }
674       if (theTextureMap->HasMipmaps())
675       {
676         GenerateMipmaps (theCtx);
677       }
678       return true;
679     }
680   }
681 }
682
683 // =======================================================================
684 // function : InitCompressed
685 // purpose  :
686 // =======================================================================
687 bool OpenGl_Texture::InitCompressed (const Handle(OpenGl_Context)& theCtx,
688                                      const Image_CompressedPixMap& theImage,
689                                      const Standard_Boolean        theIsColorMap)
690 {
691   if (theImage.SizeX() < 1
692    || theImage.SizeY() < 1
693    || theImage.FaceData().IsNull())
694   {
695     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
696                          "Error: texture of 0 size cannot be created.");
697     Release (theCtx.get());
698     return false;
699   }
700   if (theImage.SizeX() > theCtx->MaxTextureSize()
701    || theImage.SizeY() > theCtx->MaxTextureSize())
702   {
703     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
704                          TCollection_AsciiString ("Error: Texture dimension - ") + theImage.SizeX() + "x" + theImage.SizeY()
705                        + " exceeds hardware limits (" + theCtx->MaxTextureSize() + "x" + theCtx->MaxTextureSize() + ")");
706     Release (theCtx.get());
707     return false;
708   }
709
710   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, theImage.CompressedFormat(), theIsColorMap);
711   if (!aFormat.IsValid())
712   {
713     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
714                          TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theImage.CompressedFormat()) + " image format "
715                          + " [" + myResourceId +"]");
716     Release (theCtx.get());
717     return false;
718   }
719
720   if (!Create (theCtx))
721   {
722     return false;
723   }
724
725   myTarget = GL_TEXTURE_2D;
726   myNbSamples = 1;
727   myTextFormat  = aFormat.Format();
728   mySizedFormat = aFormat.Internal();
729   myIsTopDown = theImage.IsTopDown();
730   mySize.SetValues (theImage.SizeX(), theImage.SizeY(), 1);
731   myMaxMipLevel = Max (theImage.MipMaps().Size() - 1, 0);
732   if (myMaxMipLevel > 0
733   && !theImage.IsCompleteMipMapSet())
734   {
735     const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (mySize.xy(), myMaxMipLevel);
736     if (!theCtx->HasTextureBaseLevel())
737     {
738       myMaxMipLevel = 0;
739       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
740                            TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySize.x() + "x" + mySize.y()
741                            + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
742     }
743     else
744     {
745       Message::SendTrace (TCollection_AsciiString ("Warning: compressed 2D texture ") + myResourceId + " " + mySize.x() + "x" + mySize.y()
746                           + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
747     }
748   }
749
750   Bind (theCtx);
751   applyDefaultSamplerParams (theCtx);
752
753   // setup the alignment
754   OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
755
756   Graphic3d_Vec2i aMipSizeXY (theImage.SizeX(), theImage.SizeY());
757   const Standard_Byte* aData = theImage.FaceData()->Data();
758   for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
759   {
760     const Standard_Integer aMipLength = theImage.MipMaps().Value (aMipIter);
761     theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_2D, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
762     const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
763     if (aTexImgErr != GL_NO_ERROR)
764     {
765       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
766                            TCollection_AsciiString ("Error: 2D compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
767                            + " IF: " + OpenGl_TextureFormat::FormatFormat (aFormat.Internal())
768                            + " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
769                            + " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
770                            + " can not be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
771       Unbind (theCtx);
772       Release (theCtx.get());
773       return false;
774     }
775
776     aData += aMipLength;
777     aMipSizeXY /= 2;
778     if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
779     if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
780   }
781
782   Unbind (theCtx);
783   return true;
784 }
785
786 // =======================================================================
787 // function : Init2DMultisample
788 // purpose  :
789 // =======================================================================
790 bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
791                                         const Standard_Integer theNbSamples,
792                                         const Standard_Integer theTextFormat,
793                                         const Standard_Integer theSizeX,
794                                         const Standard_Integer theSizeY)
795 {
796   if (!Create (theCtx)
797    ||  theNbSamples > theCtx->MaxMsaaSamples()
798    ||  theNbSamples < 1)
799   {
800     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
801                          TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
802                          + " exceeds samples limit: " + theCtx->MaxMsaaSamples() + ".");
803     return false;
804   }
805
806   myNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
807   myTarget = GL_TEXTURE_2D_MULTISAMPLE;
808   myMaxMipLevel = 0;
809   if(theSizeX > theCtx->MaxTextureSize()
810   || theSizeY > theCtx->MaxTextureSize())
811   {
812     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
813                          TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
814                          + " exceeds size limit: " + theCtx->MaxTextureSize() + "x" + theCtx->MaxTextureSize() + ".");
815     return false;
816   }
817
818   Bind (theCtx);
819   //myTextFormat = theTextFormat;
820   mySizedFormat = theTextFormat;
821   if (theCtx->HasTextureMultisampling()
822    && theCtx->Functions()->glTexStorage2DMultisample != NULL)   // OpenGL 4.3
823   {
824     theCtx->Functions()->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
825   }
826   else if (theCtx->HasTextureMultisampling()
827         && theCtx->Functions()->glTexImage2DMultisample != NULL) // OpenGL 3.2
828   {
829     theCtx->Functions()->glTexImage2DMultisample   (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
830   }
831   else
832   {
833     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
834                          "Error: MSAA textures are not supported by hardware.");
835     Unbind (theCtx);
836     return false;
837   }
838
839   const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
840   if (aTexImgErr != GL_NO_ERROR)
841   {
842     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
843                          TCollection_AsciiString ("Error: MSAA texture ") + theSizeX + "x" + theSizeY + "@" + myNbSamples
844                          + " IF: " + OpenGl_TextureFormat::FormatFormat (theTextFormat)
845                          + " cannot be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
846     Unbind (theCtx);
847     return false;
848   }
849
850   mySize.SetValues (theSizeX, theSizeY, 1);
851
852   Unbind (theCtx);
853   return true;
854 }
855
856 // =======================================================================
857 // function : InitRectangle
858 // purpose  :
859 // =======================================================================
860 bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
861                                     const Standard_Integer        theSizeX,
862                                     const Standard_Integer        theSizeY,
863                                     const OpenGl_TextureFormat&   theFormat)
864 {
865   if (!theCtx->IsGlGreaterEqual (3, 0)
866    || !Create (theCtx)
867    ||  theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES)
868   {
869     return false;
870   }
871
872   myTarget = GL_TEXTURE_RECTANGLE;
873   myNbSamples = 1;
874   myMaxMipLevel = 0;
875
876   const GLsizei aSizeX = Min (theCtx->MaxTextureSize(), theSizeX);
877   const GLsizei aSizeY = Min (theCtx->MaxTextureSize(), theSizeY);
878
879   Bind (theCtx);
880   applyDefaultSamplerParams (theCtx);
881
882   myTextFormat  = theFormat.Format();
883   mySizedFormat = theFormat.Internal();
884
885   // setup the alignment
886   OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
887
888   theCtx->core11fwd->glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE, 0, mySizedFormat,
889                                    aSizeX, aSizeY, 0,
890                                    myTextFormat, GL_FLOAT, NULL);
891
892   GLint aTestSizeX = 0, aTestSizeY = 0;
893   theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH,  &aTestSizeX);
894   theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
895   theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
896   if (aTestSizeX == 0 || aTestSizeY == 0)
897   {
898     Unbind (theCtx);
899     return false;
900   }
901
902   theCtx->core11fwd->glTexImage2D (myTarget, 0, mySizedFormat,
903                                    aSizeX, aSizeY, 0,
904                                    myTextFormat, GL_FLOAT, NULL);
905   if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
906   {
907     Unbind (theCtx);
908     return false;
909   }
910
911   mySize.SetValues (aSizeX, aSizeY, 1);
912   Unbind (theCtx);
913   return true;
914 }
915
916 // =======================================================================
917 // function : Init3D
918 // purpose  :
919 // =======================================================================
920 bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
921                              const OpenGl_TextureFormat&   theFormat,
922                              const Graphic3d_Vec3i&        theSizeXYZ,
923                              const void*                   thePixels)
924 {
925   if (theCtx->Functions()->glTexImage3D == NULL)
926   {
927     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
928                          "Error: three-dimensional textures are not supported by hardware.");
929     return false;
930   }
931
932   if (!Create(theCtx))
933   {
934     return false;
935   }
936
937   myTarget = GL_TEXTURE_3D;
938   myNbSamples = 1;
939   myMaxMipLevel = 0;
940
941   const Graphic3d_Vec3i aSizeXYZ = theSizeXYZ.cwiseMin (Graphic3d_Vec3i (theCtx->MaxTextureSize()));
942   if (aSizeXYZ != theSizeXYZ)
943   {
944     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
945                          "Error: 3D texture dimensions exceed hardware limits.");
946     Release (theCtx.get());
947     Unbind (theCtx);
948     return false;
949   }
950   Bind (theCtx);
951
952   if (theFormat.DataType() == GL_FLOAT
953   && !theCtx->arbTexFloat)
954   {
955     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
956                          "Error: floating-point textures are not supported by hardware.");
957     Release (theCtx.get());
958     Unbind (theCtx);
959     return false;
960   }
961
962   mySizedFormat = theFormat.InternalFormat();
963
964   // setup the alignment
965   OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
966
967   if (theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGL)
968   {
969     theCtx->Functions()->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, mySizedFormat,
970                                        aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
971                                        theFormat.PixelFormat(), theFormat.DataType(), NULL);
972
973     NCollection_Vec3<GLint> aTestSizeXYZ;
974     theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH,  &aTestSizeXYZ.x());
975     theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeXYZ.y());
976     theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH,  &aTestSizeXYZ.z());
977     theCtx->core11fwd->glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
978     if (aTestSizeXYZ.x() == 0 || aTestSizeXYZ.y() == 0 || aTestSizeXYZ.z() == 0)
979     {
980       Unbind (theCtx);
981       Release (theCtx.get());
982       return false;
983     }
984   }
985
986   applyDefaultSamplerParams (theCtx);
987   theCtx->Functions()->glTexImage3D (myTarget, 0, mySizedFormat,
988                                      aSizeXYZ.x(), aSizeXYZ.y(), aSizeXYZ.z(), 0,
989                                      theFormat.PixelFormat(), theFormat.DataType(), thePixels);
990
991   if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
992   {
993     Unbind (theCtx);
994     Release (theCtx.get());
995     return false;
996   }
997
998   mySize = aSizeXYZ;
999
1000   Unbind (theCtx);
1001   return true;
1002 }
1003
1004 // =======================================================================
1005 // function : InitCubeMap
1006 // purpose  :
1007 // =======================================================================
1008 bool OpenGl_Texture::InitCubeMap (const Handle(OpenGl_Context)&    theCtx,
1009                                   const Handle(Graphic3d_CubeMap)& theCubeMap,
1010                                   Standard_Size    theSize,
1011                                   Image_Format     theFormat,
1012                                   Standard_Boolean theToGenMipmap,
1013                                   Standard_Boolean theIsColorMap)
1014 {
1015   if (!Create (theCtx))
1016   {
1017     Release (theCtx.get());
1018     return false;
1019   }
1020
1021   Handle(Image_PixMap) anImage;
1022   Handle(Image_CompressedPixMap) aCompImage;
1023   OpenGl_TextureFormat aFormat;
1024   myMaxMipLevel = 0;
1025   if (!theCubeMap.IsNull())
1026   {
1027     theCubeMap->Reset();
1028     if (theCtx->SupportedTextureFormats()->HasCompressed()
1029     && !theCtx->caps->compressedTexturesDisable)
1030     {
1031       aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
1032     }
1033     if (!aCompImage.IsNull())
1034     {
1035       aFormat = OpenGl_TextureFormat::FindCompressedFormat (theCtx, aCompImage->CompressedFormat(), theIsColorMap);
1036       if (aFormat.IsValid())
1037       {
1038         theToGenMipmap = false;
1039         theSize   = aCompImage->SizeX();
1040         theFormat = aCompImage->BaseFormat();
1041         myMaxMipLevel = Max (aCompImage->MipMaps().Size() - 1, 0);
1042         if (myMaxMipLevel > 0
1043         && !aCompImage->IsCompleteMipMapSet())
1044         {
1045           const Graphic3d_Vec2i aMipSize = computeSmallestMipMapSize (Graphic3d_Vec2i (aCompImage->SizeX(), aCompImage->SizeY()), myMaxMipLevel);
1046           if (!theCtx->HasTextureBaseLevel())
1047           {
1048             myMaxMipLevel = 0;
1049             theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PERFORMANCE, 0, GL_DEBUG_SEVERITY_MEDIUM,
1050                                  TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
1051                                  + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y() + "; mipmaps will be ignored");
1052           }
1053           else
1054           {
1055             Message::SendTrace (TCollection_AsciiString ("Warning: Cubemap compressed texture ") + theCubeMap->GetId() + " " + aCompImage->SizeX() + "x" + aCompImage->SizeX()
1056                                 + " has smallest mipmap " + aMipSize.x() + "x" + aMipSize.y());
1057           }
1058         }
1059
1060         OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
1061       }
1062       else
1063       {
1064         aCompImage.Nullify();
1065       }
1066     }
1067
1068     if (!aFormat.IsValid())
1069     {
1070       anImage = theCubeMap->Reset().Value (theCtx->SupportedTextureFormats());
1071       if (anImage.IsNull())
1072       {
1073         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1074                              "Unable to get the first side of cubemap");
1075         Release(theCtx.get());
1076         return false;
1077       }
1078
1079       theSize   = anImage->SizeX();
1080       theFormat = anImage->Format();
1081       theToGenMipmap = theCubeMap->HasMipmaps();
1082     }
1083
1084     myIsTopDown = theCubeMap->IsTopDown();
1085   }
1086
1087   if (!aFormat.IsValid())
1088   {
1089     aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theFormat, theIsColorMap);
1090   }
1091   if (!aFormat.IsValid())
1092   {
1093     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1094                          TCollection_AsciiString ("Error: No suitable texture format for ") + Image_PixMap::ImageFormatToString (theFormat) + " image format"
1095                          + " [" + myResourceId +"]");
1096     Unbind(theCtx);
1097     Release(theCtx.get());
1098     return false;
1099   }
1100
1101   if (theToGenMipmap
1102   &&  theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES
1103   && !theCtx->IsGlGreaterEqual (3, 0)
1104   &&  (aFormat.PixelFormat() == GL_SRGB_EXT
1105     || aFormat.PixelFormat() == GL_SRGB_ALPHA_EXT))
1106   {
1107     theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_HIGH,
1108                          TCollection_AsciiString ("Warning, GL_EXT_sRGB disallows generation of mipmaps - fallback using non-sRGB format")
1109                          + " [" + myResourceId +"]");
1110     aFormat.SetPixelFormat   (aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB  : GL_RGBA);
1111     aFormat.SetInternalFormat(aFormat.PixelFormat() == GL_SRGB_EXT ? GL_RGB8 : GL_RGBA8);
1112   }
1113
1114   myTarget = GL_TEXTURE_CUBE_MAP;
1115   myNbSamples = 1;
1116   mySize.SetValues ((GLsizei )theSize, (GLsizei )theSize, 1);
1117   myTextFormat  = aFormat.Format();
1118   mySizedFormat = aFormat.Internal();
1119
1120   // ES 2.0 does not support sized formats and format conversions - them detected from data type
1121   const GLint anIntFormat = (theCtx->GraphicsLibrary() != Aspect_GraphicsLibrary_OpenGLES
1122                           || theCtx->IsGlGreaterEqual (3, 0))
1123                           ? aFormat.InternalFormat()
1124                           : aFormat.PixelFormat();
1125
1126   Bind (theCtx);
1127   applyDefaultSamplerParams (theCtx);
1128
1129   for (Standard_Integer i = 0; i < 6; ++i)
1130   {
1131     const Standard_Byte* aData = NULL;
1132
1133     if (!theCubeMap.IsNull())
1134     {
1135       if (i != 0)
1136       {
1137         if (!aCompImage.IsNull())
1138         {
1139           aCompImage = theCubeMap->CompressedValue (theCtx->SupportedTextureFormats());
1140         }
1141         else
1142         {
1143           anImage = theCubeMap->Value (theCtx->SupportedTextureFormats());
1144         }
1145       }
1146       if (!aCompImage.IsNull())
1147       {
1148         Graphic3d_Vec2i aMipSizeXY = mySize.xy();
1149         aData = aCompImage->FaceData()->Data();
1150         for (Standard_Integer aMipIter = 0; aMipIter <= myMaxMipLevel; ++aMipIter)
1151         {
1152           const Standard_Integer aMipLength = aCompImage->MipMaps().Value (aMipIter);
1153           theCtx->Functions()->glCompressedTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, aMipIter, mySizedFormat, aMipSizeXY.x(), aMipSizeXY.y(), 0, aMipLength, aData);
1154           const GLenum aTexImgErr = theCtx->core11fwd->glGetError();
1155           if (aTexImgErr != GL_NO_ERROR)
1156           {
1157             theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1158                                  TCollection_AsciiString ("Error: cubemap compressed texture ") + aMipSizeXY.x() + "x" + aMipSizeXY.y()
1159                                  + " IF: " + OpenGl_TextureFormat::FormatFormat (aFormat.Internal())
1160                                  + " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
1161                                  + " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
1162                                  + " can not be created with error " + OpenGl_Context::FormatGlError (aTexImgErr) + ".");
1163             Unbind (theCtx);
1164             Release (theCtx.get());
1165             return false;
1166           }
1167
1168           aData += aMipLength;
1169           aMipSizeXY /= 2;
1170           if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
1171           if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
1172         }
1173
1174         theCubeMap->Next();
1175         continue;
1176       }
1177
1178       if (!anImage.IsNull())
1179       {
1180         const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
1181         const GLint anExtraBytes = GLint(anImage->RowExtraBytes());
1182         const GLint aPixelsWidth = GLint(anImage->SizeRowBytes() / anImage->SizePixelBytes());
1183         const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
1184         if (theCtx->hasUnpackRowLength)
1185         {
1186           theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
1187         }
1188
1189         if (aRowLength > 0
1190         && !theCtx->hasUnpackRowLength)
1191         {
1192           Handle(Image_PixMap) aCopyImage = new Image_PixMap();
1193           aCopyImage->InitTrash (theFormat, theSize, theSize);
1194           const Standard_Size aRowBytesPacked = std::min (aCopyImage->SizeRowBytes(), anImage->SizeRowBytes());
1195           for (unsigned int y = 0; y < theSize; ++y)
1196           {
1197             memcpy (aCopyImage->ChangeRow (y), anImage->ChangeRow (y), aRowBytesPacked);
1198           }
1199           anImage = aCopyImage;
1200           const GLint anAligment2 = Min((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
1201           theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment2);
1202         }
1203         else
1204         {
1205           theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
1206         }
1207
1208         aData = anImage->Data();
1209       }
1210       else
1211       {
1212         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1213                              TCollection_AsciiString() + "Unable to get [" + i + "] side of cubemap");
1214         Unbind (theCtx);
1215         Release (theCtx.get());
1216         return false;
1217       }
1218       theCubeMap->Next();
1219     }
1220
1221     theCtx->core11fwd->glTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
1222                                      anIntFormat,
1223                                      GLsizei(theSize), GLsizei(theSize),
1224                                      0, aFormat.PixelFormat(), aFormat.DataType(),
1225                                      aData);
1226
1227     OpenGl_UnpackAlignmentSentry::Reset (*theCtx);
1228
1229     const GLenum anErr = theCtx->core11fwd->glGetError();
1230     if (anErr != GL_NO_ERROR)
1231     {
1232       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
1233                            TCollection_AsciiString ("Error: cubemap side  ") + (int )theSize + "x" + (int )theSize
1234                            + " IF: " + OpenGl_TextureFormat::FormatFormat (anIntFormat)
1235                            + " PF: " + OpenGl_TextureFormat::FormatFormat (aFormat.PixelFormat())
1236                            + " DT: " + OpenGl_TextureFormat::FormatDataType (aFormat.DataType())
1237                            + " can not be created with error " + OpenGl_Context::FormatGlError (anErr) + ".");
1238       Unbind (theCtx);
1239       Release (theCtx.get());
1240       return false;
1241     }
1242   }
1243
1244   if (theToGenMipmap && theCtx->arbFBO != NULL)
1245   {
1246     GenerateMipmaps (theCtx);
1247   }
1248
1249   Unbind (theCtx.get());
1250   return true;
1251 }
1252
1253 // =======================================================================
1254 // function : PixelSizeOfPixelFormat
1255 // purpose  :
1256 // =======================================================================
1257 Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
1258 {
1259   switch(theInternalFormat)
1260   {
1261     // RED variations (GL_RED, OpenGL 3.0+)
1262     case GL_RED:
1263     case GL_R8:       return 1;
1264     case GL_R16:      return 2;
1265     case GL_R16F:     return 2;
1266     case GL_R32F:     return 4;
1267     // RGB variations
1268     case GL_RGB:      return 3;
1269     case GL_RGB8:     return 3;
1270     case GL_RGB16:    return 6;
1271     case GL_RGB16F:   return 6;
1272     case GL_RGB32F:   return 12;
1273     // RGBA variations
1274     case GL_RGBA:     return 4;
1275     case GL_RGBA8:    return 4;
1276     case GL_RGB10_A2: return 4;
1277     case GL_RGBA12:   return 6;
1278     case GL_RGBA16:   return 8;
1279     case GL_RGBA16F:  return 8;
1280     case GL_RGBA32F:  return 16;
1281     //
1282     case GL_BGRA_EXT:  return 4;
1283     // ALPHA variations (deprecated)
1284     case GL_ALPHA:
1285     case GL_ALPHA8:    return 1;
1286     case GL_ALPHA16:   return 2;
1287     case GL_LUMINANCE: return 1;
1288     case GL_LUMINANCE_ALPHA: return 2;
1289     // depth-stencil
1290     case GL_DEPTH24_STENCIL8:   return 4;
1291     case GL_DEPTH32F_STENCIL8:  return 8;
1292     case GL_DEPTH_COMPONENT16:  return 2;
1293     case GL_DEPTH_COMPONENT24:  return 3;
1294     case GL_DEPTH_COMPONENT32F: return 4;
1295     // compressed
1296     case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // DXT1 uses circa half a byte per pixel (64 bits per 4x4 block)
1297     case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
1298     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1299     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
1300     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // DXT3/5 uses circa 1 byte per pixel (128 bits per 4x4 block)
1301     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
1302     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1303     case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
1304       return 1;
1305   }
1306   return 1;
1307 }
1308
1309 // =======================================================================
1310 // function : EstimatedDataSize
1311 // purpose  :
1312 // =======================================================================
1313 Standard_Size OpenGl_Texture::EstimatedDataSize() const
1314 {
1315   if (!IsValid())
1316   {
1317     return 0;
1318   }
1319
1320   Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySize.x() * myNbSamples;
1321   if (mySize.y() != 0)
1322   {
1323     aSize *= Standard_Size(mySize.y());
1324   }
1325   if (mySize.z() != 0)
1326   {
1327     aSize *= Standard_Size(mySize.z());
1328   }
1329   if (myTarget == GL_TEXTURE_CUBE_MAP)
1330   {
1331     aSize *= 6; // cube sides
1332   }
1333   if (myMaxMipLevel > 0)
1334   {
1335     aSize = aSize + aSize / 3;
1336   }
1337   return aSize;
1338 }
1339
1340 // =======================================================================
1341 // function : ImageDump
1342 // purpose  :
1343 // =======================================================================
1344 bool OpenGl_Texture::ImageDump (Image_PixMap& theImage,
1345                                 const Handle(OpenGl_Context)& theCtx,
1346                                 Graphic3d_TextureUnit theTexUnit,
1347                                 Standard_Integer theLevel,
1348                                 Standard_Integer theCubeSide) const
1349 {
1350   const OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindSizedFormat (theCtx, mySizedFormat);
1351   if (theCtx.IsNull()
1352   || !IsValid()
1353   ||  theCtx->GraphicsLibrary() == Aspect_GraphicsLibrary_OpenGLES // glGetTexImage() is unavailable in OpenGL ES
1354   ||  theLevel < 0
1355   || !aFormat.IsValid()
1356   ||  aFormat.ImageFormat() == Image_Format_UNKNOWN
1357   || (myTarget == GL_TEXTURE_CUBE_MAP
1358    && (theCubeSide < 0 || theCubeSide > 5)))
1359   {
1360     return false;
1361   }
1362
1363   GLenum aTarget = myTarget;
1364   Graphic3d_Vec2i aSize = mySize.xy();
1365   if (myTarget == GL_TEXTURE_CUBE_MAP)
1366   {
1367     aTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + theCubeSide;
1368   }
1369   for (Standard_Integer aMipIter = 0; aMipIter < theLevel; ++aMipIter)
1370   {
1371     aSize /= 2;
1372     if (aSize.x() == 0) { aSize.x() = 1; }
1373     if (aSize.y() == 0) { aSize.y() = 1; }
1374   }
1375   if (!theImage.InitTrash (aFormat.ImageFormat(), aSize.x(), aSize.y()))
1376   {
1377     return false;
1378   }
1379
1380   const GLint anAligment = Min (GLint(theImage.MaxRowAligmentBytes()), 8); // limit to 8 bytes for OpenGL
1381   theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, anAligment);
1382   if (theCtx->hasPackRowLength)
1383   {
1384     theCtx->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
1385   }
1386   // glGetTextureImage() allows avoiding to binding texture id, but apparently requires clean FBO binding state...
1387   //if (theCtx->core45 != NULL) { theCtx->core45->glGetTextureImage (myTextureId, theLevel, aFormat.PixelFormat(), aFormat.DataType(), (GLsizei )theImage.SizeBytes(), theImage.ChangeData()); } else
1388   {
1389     Bind (theCtx, theTexUnit);
1390     theCtx->core11fwd->glGetTexImage (aTarget, theLevel, aFormat.PixelFormat(), aFormat.DataType(), theImage.ChangeData());
1391     Unbind (theCtx, theTexUnit);
1392   }
1393   if (theImage.Format() != aFormat.ImageFormat())
1394   {
1395     Image_PixMap::SwapRgbaBgra (theImage);
1396   }
1397
1398   const bool hasErrors = theCtx->ResetErrors (true);
1399   theCtx->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1);
1400   return !hasErrors;
1401 }