0023544: Texture management in TKOpenGl should be redesigned
[occt.git] / src / OpenGl / OpenGl_Texture.cxx
CommitLineData
bf75be98 1// Created by: Kirill GAVRILOV
2// Copyright (c) 2012 OPEN CASCADE SAS
3//
4// The content of this file is subject to the Open CASCADE Technology Public
5// License Version 6.5 (the "License"). You may not use the content of this file
6// except in compliance with the License. Please obtain a copy of the License
7// at http://www.opencascade.org and read it completely before using this file.
8//
9// The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
10// main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
11//
12// The Original Code and all software distributed under the License is
13// distributed on an "AS IS" basis, without warranty of any kind, and the
14// Initial Developer hereby disclaims all such warranties, including without
15// limitation, any warranties of merchantability, fitness for a particular
16// purpose or non-infringement. Please see the License for the specific terms
17// and conditions governing the rights and limitations under the License.
18
19#include <OpenGl_Texture.hxx>
20
21#include <OpenGl_Context.hxx>
22#include <Graphic3d_TextureParams.hxx>
23#include <Standard_Assert.hxx>
24#include <Image_PixMap.hxx>
25
26IMPLEMENT_STANDARD_HANDLE (OpenGl_Texture, OpenGl_Resource)
27IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_Resource)
28
29//! Function for getting power of to number larger or equal to input number.
30//! @param theNumber number to 'power of two'
31//! @param theThreshold upper threshold
32//! @return power of two number
33inline GLsizei getPowerOfTwo (const GLsizei theNumber,
34 const GLsizei theThreshold)
35{
36 for (GLsizei p2 = 2; p2 <= theThreshold; p2 <<= 1)
37 {
38 if (theNumber <= p2)
39 {
40 return p2;
41 }
42 }
43 return theThreshold;
44}
45
46// =======================================================================
47// function : OpenGl_Texture
48// purpose :
49// =======================================================================
50OpenGl_Texture::OpenGl_Texture (const Handle(Graphic3d_TextureParams)& theParams)
51: OpenGl_Resource(),
52 myTextureId (NO_TEXTURE),
53 myTarget (GL_TEXTURE_2D),
54 mySizeX (0),
55 mySizeY (0),
56 myTextFormat (GL_FLOAT),
57 myHasMipmaps (Standard_False),
58 myParams (theParams)
59{
60 if (myParams.IsNull())
61 {
62 myParams = new Graphic3d_TextureParams();
63 }
64}
65
66// =======================================================================
67// function : ~OpenGl_Texture
68// purpose :
69// =======================================================================
70OpenGl_Texture::~OpenGl_Texture()
71{
72 Release (NULL);
73}
74
75// =======================================================================
76// function : HasMipmaps
77// purpose :
78// =======================================================================
79const Standard_Boolean OpenGl_Texture::HasMipmaps() const
80{
81 return myHasMipmaps;
82}
83
84// =======================================================================
85// function : GetParams
86// purpose :
87// =======================================================================
88const Handle(Graphic3d_TextureParams)& OpenGl_Texture::GetParams() const
89{
90 return myParams;
91}
92
93// =======================================================================
94// function : SetParams
95// purpose :
96// =======================================================================
97void OpenGl_Texture::SetParams (const Handle(Graphic3d_TextureParams)& theParams)
98{
99 myParams = theParams;
100}
101
102// =======================================================================
103// function : Create
104// purpose :
105// =======================================================================
106bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& )
107{
108 if (myTextureId == NO_TEXTURE)
109 {
110 glGenTextures (1, &myTextureId);
111 }
112 return myTextureId != NO_TEXTURE;
113}
114
115// =======================================================================
116// function : Release
117// purpose :
118// =======================================================================
119void OpenGl_Texture::Release (const OpenGl_Context* theGlCtx)
120{
121 if (myTextureId == NO_TEXTURE)
122 {
123 return;
124 }
125
126 // application can not handle this case by exception - this is bug in code
127 Standard_ASSERT_RETURN (theGlCtx != NULL,
128 "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
129
130 glDeleteTextures (1, &myTextureId);
131 myTextureId = NO_TEXTURE;
132 mySizeX = mySizeY = 0;
133}
134
135// =======================================================================
136// function : Bind
137// purpose :
138// =======================================================================
139void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
140 const GLenum theTextureUnit) const
141{
142 if (theCtx->IsGlGreaterEqual (1, 3))
143 {
144 theCtx->core13->glActiveTexture (theTextureUnit);
145 }
146 glBindTexture (myTarget, myTextureId);
147}
148
149// =======================================================================
150// function : Unbind
151// purpose :
152// =======================================================================
153void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
154 const GLenum theTextureUnit) const
155{
156 if (theCtx->IsGlGreaterEqual (1, 3))
157 {
158 theCtx->core13->glActiveTexture (theTextureUnit);
159 }
160 glBindTexture (myTarget, NO_TEXTURE);
161}
162
163// =======================================================================
164// function : Init
165// purpose :
166// =======================================================================
167bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
168 const Image_PixMap& theImage,
169 const Graphic3d_TypeOfTexture theType)
170{
171 myHasMipmaps = Standard_False;
172 if (theImage.IsEmpty() || !Create (theCtx))
173 {
174 return false;
175 }
176
177 GLenum aTextureFormat = GL_RGBA8;
178 GLenum aPixelFormat = 0;
179 GLenum aDataType = 0;
180 switch (theImage.Format())
181 {
182 case Image_PixMap::ImgGrayF:
183 {
184 aTextureFormat = GL_ALPHA8; // GL_R8, GL_R32F
185 aPixelFormat = GL_ALPHA; // GL_RED
186 aDataType = GL_FLOAT;
187 break;
188 }
189 case Image_PixMap::ImgRGBAF:
190 {
191 aTextureFormat = GL_RGBA8; // GL_RGBA32F
192 aPixelFormat = GL_RGBA;
193 aDataType = GL_FLOAT;
194 break;
195 }
196 case Image_PixMap::ImgBGRAF:
197 {
198 if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
199 {
200 return false;
201 }
202 aTextureFormat = GL_RGBA8; // GL_RGBA32F
203 aPixelFormat = GL_BGRA; // equals to GL_BGRA_EXT
204 aDataType = GL_FLOAT;
205 break;
206 }
207 case Image_PixMap::ImgRGBF:
208 {
209 aTextureFormat = GL_RGB8; // GL_RGB32F
210 aPixelFormat = GL_RGB;
211 aDataType = GL_FLOAT;
212 break;
213 }
214 case Image_PixMap::ImgBGRF:
215 {
216 aTextureFormat = GL_RGB8; // GL_RGB32F
217 aPixelFormat = GL_BGR; // equals to GL_BGR_EXT
218 aDataType = GL_FLOAT;
219 break;
220 }
221 case Image_PixMap::ImgRGBA:
222 {
223 aTextureFormat = GL_RGBA8;
224 aPixelFormat = GL_RGBA;
225 aDataType = GL_UNSIGNED_BYTE;
226 break;
227 }
228 case Image_PixMap::ImgBGRA:
229 {
230 if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
231 {
232 return false;
233 }
234 aTextureFormat = GL_RGBA8;
235 aPixelFormat = GL_BGRA; // equals to GL_BGRA_EXT
236 aDataType = GL_UNSIGNED_BYTE;
237 break;
238 }
239 case Image_PixMap::ImgRGB32:
240 {
241 aTextureFormat = GL_RGB8;
242 aPixelFormat = GL_RGBA;
243 aDataType = GL_UNSIGNED_BYTE;
244 break;
245 }
246 case Image_PixMap::ImgBGR32:
247 {
248 if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
249 {
250 return false;
251 }
252 aTextureFormat = GL_RGB8;
253 aPixelFormat = GL_BGRA; // equals to GL_BGRA_EXT
254 aDataType = GL_UNSIGNED_BYTE;
255 break;
256 }
257 case Image_PixMap::ImgRGB:
258 {
259 aTextureFormat = GL_RGB8;
260 aPixelFormat = GL_RGB;
261 aDataType = GL_UNSIGNED_BYTE;
262 break;
263 }
264 case Image_PixMap::ImgBGR:
265 {
266 if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
267 {
268 return false;
269 }
270 aTextureFormat = GL_RGB8;
271 aPixelFormat = GL_BGR; // equals to GL_BGR_EXT
272 aDataType = GL_UNSIGNED_BYTE;
273 break;
274 }
275 case Image_PixMap::ImgGray:
276 {
277 aTextureFormat = GL_ALPHA8; // GL_R8
278 aPixelFormat = GL_ALPHA; // GL_RED
279 aDataType = GL_UNSIGNED_BYTE;
280 break;
281 }
282 default:
283 {
284 return false;
285 }
286 }
287
288 const GLsizei aMaxSize = theCtx->MaxTextureSize();
289 const GLsizei aWidth = (GLsizei )theImage.SizeX();
290 const GLsizei aHeight = (GLsizei )theImage.SizeY();
291
292 // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
293 // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
294 // Trying to create NPOT rextures on such hardware will not fail
295 // but driver will fall back into software rendering,
296 const bool toForceP2 = !theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW;
297 const GLsizei aWidthOut = toForceP2 ? getPowerOfTwo (aWidth, aMaxSize) : Min (aWidth, aMaxSize);
298 const GLsizei aHeightOut = toForceP2 ? getPowerOfTwo (aHeight, aMaxSize) : Min (aHeight, aMaxSize);
299
300 GLint aTestWidth = 0;
301 GLint aTestHeight = 0;
302
303 glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // ensure alignment will not screw up the party
304 switch (theType)
305 {
306 case Graphic3d_TOT_1D:
307 {
308 myTarget = GL_TEXTURE_1D;
309 Bind (theCtx);
310 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
311 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
312
313 Image_PixMap aCopy;
314 GLvoid* aDataPtr = (GLvoid* )theImage.Data();
315 if (aWidth != aWidthOut)
316 {
317 if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), 1)
318 || gluScaleImage (aPixelFormat,
319 aWidth, 1, aDataType, theImage.Data(),
320 aWidthOut, 1, aDataType, aCopy.ChangeData()) != 0)
321 {
322 Unbind (theCtx);
323 return false;
324 }
325
326 aDataPtr = (GLvoid* )aCopy.Data();
327 }
328
329 // use proxy to check texture could be created or not
330 glTexImage1D (GL_PROXY_TEXTURE_1D, 0, aTextureFormat,
331 aWidthOut, 0,
332 aPixelFormat, aDataType, NULL);
333 glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
334 if (aTestWidth == 0)
335 {
336 // no memory or broken input parameters
337 Unbind (theCtx);
338 return false;
339 }
340
341 glTexImage1D (GL_TEXTURE_1D, 0, aTextureFormat,
342 aWidthOut, 0,
343 aPixelFormat, aDataType, aDataPtr);
344
345 Unbind (theCtx);
346 return true;
347 }
348 case Graphic3d_TOT_2D:
349 {
350 myTarget = GL_TEXTURE_2D;
351 Bind (theCtx);
352 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
353 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
354
355 Image_PixMap aCopy;
356 GLvoid* aDataPtr = (GLvoid* )theImage.Data();
357 if (aWidth != aWidthOut || aHeight != aHeightOut)
358 {
359 // scale texture
360
361 if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), Standard_Size(aHeightOut))
362 || gluScaleImage (aPixelFormat,
363 aWidth, aHeight, aDataType, theImage.Data(),
364 aWidthOut, aHeightOut, aDataType, aCopy.ChangeData()) != 0)
365 {
366 Unbind (theCtx);
367 return false;
368 }
369
370 aDataPtr = (GLvoid* )aCopy.Data();
371 }
372
373 // use proxy to check texture could be created or not
374 glTexImage2D (GL_PROXY_TEXTURE_2D, 0, aTextureFormat,
375 aWidthOut, aHeightOut, 0,
376 aPixelFormat, aDataType, NULL);
377 glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
378 glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
379 if (aTestWidth == 0 || aTestHeight == 0)
380 {
381 // no memory or broken input parameters
382 Unbind (theCtx);
383 return false;
384 }
385
386 glTexImage2D (GL_TEXTURE_2D, 0, aTextureFormat,
387 aWidthOut, aHeightOut, 0,
388 aPixelFormat, aDataType, aDataPtr);
389
390 Unbind (theCtx);
391 return true;
392 }
393 case Graphic3d_TOT_2D_MIPMAP:
394 {
395 myTarget = GL_TEXTURE_2D;
396 myHasMipmaps = Standard_True;
397 Bind (theCtx);
398 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
399 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
400
401 bool isCreated = gluBuild2DMipmaps (GL_TEXTURE_2D, aTextureFormat,
402 aWidth, aHeight,
403 aPixelFormat, aDataType, theImage.Data()) == 0;
404 Unbind (theCtx);
405 return isCreated;
406 }
407 default:
408 {
409 return false;
410 }
411 }
412}