a6553ac40505ba8d1b99716ae134b6f9d5e0275b
[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     if (Handle(Image_PixMap) anImage = theCubeMap->Reset().Value())
815     {
816       theSize   = anImage->SizeX();
817       theFormat = anImage->Format();
818     }
819     else
820     {
821       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
822                            "Unable to get the first side of cubemap");
823       Release(theCtx.get());
824       return false;
825     }
826   }
827
828   OpenGl_TextureFormat aFormat = OpenGl_TextureFormat::FindFormat (theCtx, theFormat, theIsColorMap);
829   if (!aFormat.IsValid())
830   {
831     Unbind(theCtx);
832     Release(theCtx.get());
833     return false;
834   }
835
836   myTarget = GL_TEXTURE_CUBE_MAP;
837   myHasMipmaps = theToGenMipmap;
838   myNbSamples = 1;
839   Bind (theCtx);
840   applyDefaultSamplerParams (theCtx);
841
842   for (Standard_Integer i = 0; i < 6; ++i)
843   {
844     const void* aData = NULL;
845     Handle(Image_PixMap) anImage;
846
847     if (!theCubeMap.IsNull())
848     {
849       anImage = theCubeMap->Value();
850       if (!anImage.IsNull())
851       {
852 #if !defined(GL_ES_VERSION_2_0)
853         const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
854         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
855
856         // notice that GL_UNPACK_ROW_LENGTH is not available on OpenGL ES 2.0 without GL_EXT_unpack_subimage extension
857         const GLint anExtraBytes = GLint(anImage->RowExtraBytes());
858         const GLint aPixelsWidth = GLint(anImage->SizeRowBytes() / anImage->SizePixelBytes());
859         const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
860         glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
861 #else
862         Handle(Image_PixMap) aCopyImage = new Image_PixMap();
863         aCopyImage->InitTrash (theFormat, theSize, theSize);
864         for (unsigned int y = 0; y < theSize; ++y)
865         {
866           for (unsigned int x = 0; x < theSize; ++x)
867           {
868             for (unsigned int aByte = 0; aByte < anImage->SizePixelBytes(); ++aByte)
869             {
870               aCopyImage->ChangeRawValue (y, x)[aByte] = anImage->RawValue (y, x)[aByte];
871             }
872           }
873         }
874         anImage = aCopyImage;
875         const GLint anAligment = Min((GLint)anImage->MaxRowAligmentBytes(), 8); // OpenGL supports alignment upto 8 bytes
876         glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
877 #endif
878         aData = anImage->Data();
879       }
880       else
881       {
882         theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
883                              TCollection_AsciiString() + "Unable to get [" + i + "] side of cubemap");
884         Unbind (theCtx);
885         Release (theCtx.get());
886         return false;
887       }
888       theCubeMap->Next();
889     }
890
891     glTexImage2D (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
892                   aFormat.InternalFormat(),
893                   GLsizei(theSize), GLsizei(theSize),
894                   0, aFormat.PixelFormat(), aFormat.DataType(),
895                   aData);
896
897     OpenGl_UnpackAlignmentSentry::Reset();
898
899     const GLenum anErr = glGetError();
900     if (anErr != GL_NO_ERROR)
901     {
902       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
903                            TCollection_AsciiString ("Unable to initialize side of cubemap. Error #") + int(anErr));
904       Unbind (theCtx);
905       Release (theCtx.get());
906       return false;
907     }
908   }
909
910   if (theToGenMipmap && theCtx->arbFBO != NULL)
911   {
912     theCtx->arbFBO->glGenerateMipmap (myTarget);
913     const GLenum anErr = glGetError();
914     if (anErr != GL_NO_ERROR)
915     {
916       theCtx->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH,
917                            TCollection_AsciiString ("Unable to generate mipmap of cubemap. Error #") + int(anErr));
918       Unbind (theCtx);
919       Release (theCtx.get());
920       return false;
921     }
922   }
923
924   Unbind (theCtx.get());
925   return true;
926 }
927
928 // =======================================================================
929 // function : PixelSizeOfPixelFormat
930 // purpose  :
931 // =======================================================================
932 Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
933 {
934   switch(theInternalFormat)
935   {
936     // RED variations (GL_RED, OpenGL 3.0+)
937     case GL_RED:
938     case GL_R8:       return 1;
939     case GL_R16:      return 2;
940     case GL_R16F:     return 2;
941     case GL_R32F:     return 4;
942     // RGB variations
943     case GL_RGB:      return 3;
944     case GL_RGB8:     return 3;
945     case GL_RGB16:    return 6;
946     case GL_RGB16F:   return 6;
947     case GL_RGB32F:   return 12;
948     // RGBA variations
949     case GL_RGBA:     return 4;
950     case GL_RGBA8:    return 4;
951     case GL_RGB10_A2: return 4;
952     case GL_RGBA12:   return 6;
953     case GL_RGBA16:   return 8;
954     case GL_RGBA16F:  return 8;
955     case GL_RGBA32F:  return 16;
956     //
957     case GL_BGRA_EXT:  return 4;
958     // ALPHA variations (deprecated)
959     case GL_ALPHA:
960     case GL_ALPHA8:    return 1;
961     case GL_ALPHA16:   return 2;
962     case GL_LUMINANCE: return 1;
963     case GL_LUMINANCE_ALPHA: return 2;
964     // depth-stencil
965     case GL_DEPTH24_STENCIL8:   return 4;
966     case GL_DEPTH32F_STENCIL8:  return 8;
967     case GL_DEPTH_COMPONENT16:  return 2;
968     case GL_DEPTH_COMPONENT24:  return 3;
969     case GL_DEPTH_COMPONENT32F: return 4;
970   }
971   return 0;
972 }
973
974 // =======================================================================
975 // function : EstimatedDataSize
976 // purpose  :
977 // =======================================================================
978 Standard_Size OpenGl_Texture::EstimatedDataSize() const
979 {
980   if (!IsValid())
981   {
982     return 0;
983   }
984
985   Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySizeX * myNbSamples;
986   if (mySizeY != 0)
987   {
988     aSize *= Standard_Size(mySizeY);
989   }
990   if (mySizeZ != 0)
991   {
992     aSize *= Standard_Size(mySizeZ);
993   }
994   if (myHasMipmaps)
995   {
996     aSize = aSize + aSize / 3;
997   }
998   return aSize;
999 }