blob: 5d6d8ebd76648fbeb85b885ff015035a1faebff9 [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000011#include "libGLESv2/Texture.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000015#include "common/debug.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000016
17#include "libGLESv2/main.h"
18#include "libGLESv2/mathutil.h"
19#include "libGLESv2/utilities.h"
20#include "libGLESv2/Blit.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021
22namespace gl
23{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000024
25Texture::Image::Image()
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000026 : width(0), height(0), dirty(false), surface(NULL)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000027{
28}
29
30Texture::Image::~Image()
31{
32 if (surface) surface->Release();
33}
34
daniel@transgaming.com93a81472010-04-20 18:52:58 +000035Texture::Texture(Context *context) : mContext(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000036{
37 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
38 mMagFilter = GL_LINEAR;
39 mWrapS = GL_REPEAT;
40 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000041
daniel@transgaming.com00c75962010-03-11 20:36:15 +000042 mDirtyMetaData = true;
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000043 mDirty = true;
daniel@transgaming.com93a81472010-04-20 18:52:58 +000044 mIsRenderable = false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000045}
46
47Texture::~Texture()
48{
49}
50
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000051Blit *Texture::getBlitter()
52{
53 return mContext->getBlitter();
54}
55
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000056// Returns true on successful filter state update (valid enum parameter)
57bool Texture::setMinFilter(GLenum filter)
58{
59 switch (filter)
60 {
61 case GL_NEAREST:
62 case GL_LINEAR:
63 case GL_NEAREST_MIPMAP_NEAREST:
64 case GL_LINEAR_MIPMAP_NEAREST:
65 case GL_NEAREST_MIPMAP_LINEAR:
66 case GL_LINEAR_MIPMAP_LINEAR:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000067 {
68 if (mMinFilter != filter)
69 {
70 mMinFilter = filter;
71 mDirty = true;
72 }
73 return true;
74 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000075 default:
76 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000077 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000078}
79
80// Returns true on successful filter state update (valid enum parameter)
81bool Texture::setMagFilter(GLenum filter)
82{
83 switch (filter)
84 {
85 case GL_NEAREST:
86 case GL_LINEAR:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000087 {
88 if (mMagFilter != filter)
89 {
90 mMagFilter = filter;
91 mDirty = true;
92 }
93 return true;
94 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000095 default:
96 return false;
97 }
98}
99
100// Returns true on successful wrap state update (valid enum parameter)
101bool Texture::setWrapS(GLenum wrap)
102{
103 switch (wrap)
104 {
105 case GL_REPEAT:
106 case GL_CLAMP_TO_EDGE:
107 case GL_MIRRORED_REPEAT:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000108 {
109 if (mWrapS != wrap)
110 {
111 mWrapS = wrap;
112 mDirty = true;
113 }
114 return true;
115 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000116 default:
117 return false;
118 }
119}
120
121// Returns true on successful wrap state update (valid enum parameter)
122bool Texture::setWrapT(GLenum wrap)
123{
124 switch (wrap)
125 {
126 case GL_REPEAT:
127 case GL_CLAMP_TO_EDGE:
128 case GL_MIRRORED_REPEAT:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000129 {
130 if (mWrapT != wrap)
131 {
132 mWrapT = wrap;
133 mDirty = true;
134 }
135 return true;
136 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000137 default:
138 return false;
139 }
140}
141
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000142GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000143{
144 return mMinFilter;
145}
146
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000147GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148{
149 return mMagFilter;
150}
151
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000152GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000153{
154 return mWrapS;
155}
156
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000157GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000158{
159 return mWrapT;
160}
161
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000162GLuint Texture::getWidth() const
163{
164 return mWidth;
165}
166
167GLuint Texture::getHeight() const
168{
169 return mHeight;
170}
171
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000172// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000173D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000174{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000175 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000176}
177
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000178int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000179{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000180 return img.width * 4;
181}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000182
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000183// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
184// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
185void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000186 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000187{
daniel@transgaming.com713914b2010-05-04 03:35:17 +0000188 GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000189
190 for (int y = 0; y < height; y++)
191 {
192 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
193 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
194 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
195
196 for (int x = 0; x < width; x++)
197 {
198 unsigned char r;
199 unsigned char g;
200 unsigned char b;
201 unsigned char a;
202
203 switch (format)
204 {
205 case GL_ALPHA:
206 a = source[x];
207 r = 0;
208 g = 0;
209 b = 0;
210 break;
211
212 case GL_LUMINANCE:
213 r = source[x];
214 g = source[x];
215 b = source[x];
216 a = 0xFF;
217 break;
218
219 case GL_LUMINANCE_ALPHA:
220 r = source[2*x+0];
221 g = source[2*x+0];
222 b = source[2*x+0];
223 a = source[2*x+1];
224 break;
225
226 case GL_RGB:
227 switch (type)
228 {
229 case GL_UNSIGNED_BYTE:
230 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000231 g = source[x * 3 + 1];
232 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000233 a = 0xFF;
234 break;
235
236 case GL_UNSIGNED_SHORT_5_6_5:
237 {
238 unsigned short rgba = source16[x];
239
240 a = 0xFF;
241 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
242 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
243 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
244 }
245 break;
246
247 default: UNREACHABLE();
248 }
249 break;
250
251 case GL_RGBA:
252 switch (type)
253 {
254 case GL_UNSIGNED_BYTE:
255 r = source[x * 4 + 0];
256 g = source[x * 4 + 1];
257 b = source[x * 4 + 2];
258 a = source[x * 4 + 3];
259 break;
260
261 case GL_UNSIGNED_SHORT_4_4_4_4:
262 {
263 unsigned short rgba = source16[x];
264
265 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
266 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
267 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
268 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
269 }
270 break;
271
272 case GL_UNSIGNED_SHORT_5_5_5_1:
273 {
274 unsigned short rgba = source16[x];
275
276 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000277 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000278 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000279 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000280 }
281 break;
282
283 default: UNREACHABLE();
284 }
285 break;
286 default: UNREACHABLE();
287 }
288
289 dest[4 * x + 0] = b;
290 dest[4 * x + 1] = g;
291 dest[4 * x + 2] = r;
292 dest[4 * x + 3] = a;
293 }
294 }
295}
296
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000297void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000298{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000299 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000300
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000301 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000302 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000303 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
304
305 if (FAILED(result))
306 {
307 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
308 return error(GL_OUT_OF_MEMORY);
309 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000310 }
311
312 if (img->surface) img->surface->Release();
313 img->surface = newSurface;
314
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000315 img->width = width;
316 img->height = height;
317 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000318
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000319 if (pixels != NULL && newSurface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000320 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000321 D3DLOCKED_RECT locked;
322 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
323
324 ASSERT(SUCCEEDED(result));
325
326 if (SUCCEEDED(result))
327 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000328 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000329 newSurface->UnlockRect();
330 }
331
332 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000333 }
334
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000335 mDirtyMetaData = true;
336}
337
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000338void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000339{
340 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
341
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000342 D3DLOCKED_RECT locked;
343 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000344
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000345 ASSERT(SUCCEEDED(result));
346
347 if (SUCCEEDED(result))
348 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000349 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000350 img->surface->UnlockRect();
351 }
352
353 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000354}
355
356IDirect3DBaseTexture9 *Texture::getTexture()
357{
358 if (!isComplete())
359 {
360 return NULL;
361 }
362
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000363 if (mDirtyMetaData)
364 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000365 mBaseTexture = createTexture();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000366 mIsRenderable = false;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000367 }
368
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000369 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000370 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000371 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000372 }
373
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000374 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000375 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000376
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000377 return mBaseTexture;
378}
379
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000380bool Texture::isDirty() const
381{
382 return (mDirty || mDirtyMetaData || dirtyImageData());
383}
384
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000385// Returns the top-level texture surface as a render target
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000386void Texture::needRenderTarget()
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000387{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000388 if (!mIsRenderable)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000389 {
390 mBaseTexture = convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000391 mIsRenderable = true;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000392 }
393
394 if (dirtyImageData())
395 {
396 updateTexture();
397 }
398
399 mDirtyMetaData = false;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000400}
401
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000402void Texture::dropTexture()
403{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000404 if (mBaseTexture)
405 {
406 mBaseTexture = NULL;
407 }
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000408
409 mIsRenderable = false;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000410}
411
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000412void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000413{
414 mBaseTexture = newTexture;
415 mDirtyMetaData = false;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000416 mIsRenderable = renderable;
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000417 mDirty = true;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000418}
419
420
421Texture2D::Texture2D(Context *context) : Texture(context)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000422{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000423 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000424 mColorbufferProxy = NULL;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000425}
426
427Texture2D::~Texture2D()
428{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000429 delete mColorbufferProxy;
430
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000431 if (mTexture)
432 {
433 mTexture->Release();
434 mTexture = NULL;
435 }
436}
437
438GLenum Texture2D::getTarget() const
439{
440 return GL_TEXTURE_2D;
441}
442
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000443// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
444// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
445// Call this when a particular level of the texture must be defined with a specific format, width and height.
446//
447// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
448// a new height and width for the texture by working backwards from the given width and height.
449bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
450{
451 bool widthOkay = (mWidth >> level == width);
452 bool heightOkay = (mHeight >> level == height);
453
454 bool sizeOkay = ((widthOkay && heightOkay)
455 || (widthOkay && mHeight >> level == 0 && height == 1)
456 || (heightOkay && mWidth >> level == 0 && width == 1));
457
458 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
459
460 if (!textureOkay)
461 {
462 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
463 mImageArray[0].format, mWidth, mHeight,
464 internalFormat, width, height);
465
466 // Purge all the levels and the texture.
467
468 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
469 {
470 if (mImageArray[i].surface != NULL)
471 {
472 mImageArray[i].dirty = false;
473
474 mImageArray[i].surface->Release();
475 mImageArray[i].surface = NULL;
476 }
477 }
478
479 if (mTexture != NULL)
480 {
481 mTexture->Release();
482 mTexture = NULL;
483 dropTexture();
484 }
485
486 mWidth = width << level;
487 mHeight = height << level;
488 mImageArray[0].format = internalFormat;
489 }
490
491 return !textureOkay;
492}
493
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000494void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000495{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000496 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000497
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000498 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000499}
500
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000501void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
502{
503 ASSERT(mImageArray[level].surface != NULL);
504
505 if (mTexture != NULL)
506 {
507 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000508 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000509
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000510 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000511
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000512 if (SUCCEEDED(result))
513 {
514 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000515
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000516 RECT sourceRect;
517 sourceRect.left = xoffset;
518 sourceRect.top = yoffset;
519 sourceRect.right = xoffset + width;
520 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000521
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000522 POINT destPoint;
523 destPoint.x = xoffset;
524 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000525
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000526 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
527 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000528
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000529 destLevel->Release();
530
531 img->dirty = false;
532 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000533 }
534}
535
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000536void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000537{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000538 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000539 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000540}
541
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000542void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
543{
544 if (redefineTexture(level, internalFormat, width, height))
545 {
546 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000547 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000548 }
549
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000550 if (width != 0 && height != 0)
551 {
552 RECT sourceRect;
553 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000554 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000555 sourceRect.top = y;
556 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000557
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000558 IDirect3DSurface9 *dest;
559 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000560
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000561 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
562 dest->Release();
563 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000564
565 mImageArray[level].width = width;
566 mImageArray[level].height = height;
567 mImageArray[level].format = internalFormat;
568}
569
570void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
571{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000572 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
573 {
574 return error(GL_INVALID_VALUE);
575 }
576
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000577 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
578 {
579 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000580 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000581 }
582 else
583 {
daniel@transgaming.comfc23fe22010-05-05 18:48:17 +0000584 needRenderTarget();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000585 }
586
587 RECT sourceRect;
588 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000589 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000590 sourceRect.top = y;
591 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000592
593 IDirect3DSurface9 *dest;
594 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
595
596 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
597 dest->Release();
598}
599
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000600// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
601bool Texture2D::isComplete() const
602{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000603 GLsizei width = mImageArray[0].width;
604 GLsizei height = mImageArray[0].height;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000605
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000606 if (width <= 0 || height <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000607 {
608 return false;
609 }
610
611 bool mipmapping;
612
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000613 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000614 {
615 case GL_NEAREST:
616 case GL_LINEAR:
617 mipmapping = false;
618 break;
619 case GL_NEAREST_MIPMAP_NEAREST:
620 case GL_LINEAR_MIPMAP_NEAREST:
621 case GL_NEAREST_MIPMAP_LINEAR:
622 case GL_LINEAR_MIPMAP_LINEAR:
623 mipmapping = true;
624 break;
625 default: UNREACHABLE();
626 }
627
628 if (mipmapping)
629 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000630 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
631 || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
632 {
633 return false;
634 }
635
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000636 int q = log2(std::max(width, height));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000637
638 for (int level = 1; level <= q; level++)
639 {
640 if (mImageArray[level].format != mImageArray[0].format)
641 {
642 return false;
643 }
644
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000645 if (mImageArray[level].width != std::max(1, width >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000646 {
647 return false;
648 }
649
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000650 if (mImageArray[level].height != std::max(1, height >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000651 {
652 return false;
653 }
654 }
655 }
656
657 return true;
658}
659
660// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000661IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000662{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000663 IDirect3DTexture9 *texture;
664
665 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000666 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000667
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000668 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000669
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000670 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000671 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000672 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000673 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000674 }
675
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000676 if (mTexture) mTexture->Release();
677 mTexture = texture;
678 return texture;
679}
680
681void Texture2D::updateTexture()
682{
683 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000684
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000685 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000686
687 for (int level = 0; level < levelCount; level++)
688 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000689 if (mImageArray[level].dirty)
690 {
691 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000692 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000693
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000694 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000695
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000696 if (SUCCEEDED(result))
697 {
698 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
699 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000700
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000701 levelSurface->Release();
702
703 mImageArray[level].dirty = false;
704 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000705 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000706 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000707}
708
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000709IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000710{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000711 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000712
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000713 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000714 {
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000715 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000716 IDirect3DDevice9 *device = getDevice();
717 D3DFORMAT format = selectFormat(mImageArray[0].format);
718
719 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
720
721 if (FAILED(result))
722 {
723 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
724 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
725 }
726
727 if (mTexture != NULL)
728 {
729 int levels = texture->GetLevelCount();
730 for (int i = 0; i < levels; i++)
731 {
732 IDirect3DSurface9 *source;
733 result = mTexture->GetSurfaceLevel(i, &source);
734
735 if (FAILED(result))
736 {
737 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
738
739 texture->Release();
740
741 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
742 }
743
744 IDirect3DSurface9 *dest;
745 result = texture->GetSurfaceLevel(i, &dest);
746
747 if (FAILED(result))
748 {
749 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
750
751 texture->Release();
752 source->Release();
753
754 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
755 }
756
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000757 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000758 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
759
760 if (FAILED(result))
761 {
762 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
763
764 texture->Release();
765 source->Release();
766 dest->Release();
767
768 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
769 }
770
771 source->Release();
772 dest->Release();
773 }
774 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000775 }
776
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000777 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000778 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000779 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000780 }
781
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000782 mTexture = texture;
783 return mTexture;
784}
785
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000786bool Texture2D::dirtyImageData() const
787{
788 int q = log2(std::max(mWidth, mHeight));
789
790 for (int i = 0; i <= q; i++)
791 {
792 if (mImageArray[i].dirty) return true;
793 }
794
795 return false;
796}
797
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000798void Texture2D::generateMipmaps()
799{
800 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
801 {
802 return error(GL_INVALID_OPERATION);
803 }
804
805 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
806 unsigned int q = log2(std::max(mWidth, mHeight));
807 for (unsigned int i = 1; i <= q; i++)
808 {
809 if (mImageArray[i].surface != NULL)
810 {
811 mImageArray[i].surface->Release();
812 mImageArray[i].surface = NULL;
813 }
814
815 mImageArray[i].dirty = false;
816
817 mImageArray[i].format = mImageArray[0].format;
818 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
819 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
820 }
821
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000822 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000823
824 for (unsigned int i = 1; i <= q; i++)
825 {
826 IDirect3DSurface9 *upper = NULL;
827 IDirect3DSurface9 *lower = NULL;
828
829 mTexture->GetSurfaceLevel(i-1, &upper);
830 mTexture->GetSurfaceLevel(i, &lower);
831
832 if (upper != NULL && lower != NULL)
833 {
834 getBlitter()->boxFilter(upper, lower);
835 }
836
837 if (upper != NULL) upper->Release();
838 if (lower != NULL) lower->Release();
839 }
840}
841
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000842Colorbuffer *Texture2D::getColorbuffer(GLenum target)
843{
844 if (target != GL_TEXTURE_2D)
845 {
846 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
847 }
848
849 if (mColorbufferProxy == NULL)
850 {
851 mColorbufferProxy = new TextureColorbufferProxy(this, target);
852 }
853
854 return mColorbufferProxy;
855}
856
857IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
858{
859 ASSERT(target == GL_TEXTURE_2D);
860
861 needRenderTarget();
862
863 IDirect3DSurface9 *renderTarget = NULL;
864 mTexture->GetSurfaceLevel(0, &renderTarget);
865
866 return renderTarget;
867}
868
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000869TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000870{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000871 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000872
873 for (int i = 0; i < 6; i++)
874 {
875 mFaceProxies[i] = NULL;
876 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000877}
878
879TextureCubeMap::~TextureCubeMap()
880{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000881 for (int i = 0; i < 6; i++)
882 {
883 delete mFaceProxies[i];
884 }
885
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000886 if (mTexture)
887 {
888 mTexture->Release();
889 mTexture = NULL;
890 }
891}
892
893GLenum TextureCubeMap::getTarget() const
894{
895 return GL_TEXTURE_CUBE_MAP;
896}
897
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000898void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000899{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000900 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000901}
902
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000903void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000904{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000905 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000906}
907
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000908void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000909{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000910 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000911}
912
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000913void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000914{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000915 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000916}
917
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000918void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000919{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000920 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000921}
922
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000923void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000924{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000925 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000926}
927
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000928void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
929{
930 int face = faceIndex(faceTarget);
931
932 ASSERT(mImageArray[face][level].surface != NULL);
933
934 if (mTexture != NULL)
935 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000936 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
937 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000938
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000939 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000940 {
941 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000942
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000943 RECT sourceRect;
944 sourceRect.left = xoffset;
945 sourceRect.top = yoffset;
946 sourceRect.right = xoffset + width;
947 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000948
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000949 POINT destPoint;
950 destPoint.x = xoffset;
951 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000952
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000953 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000954 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000955
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000956 destLevel->Release();
957
958 img->dirty = false;
959 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000960 }
961}
962
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000963void TextureCubeMap::subImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000964{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000965 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000966 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000967}
968
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000969// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000970bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000971{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000972 int size = mImageArray[0][0].width;
973
974 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000975 {
976 return false;
977 }
978
979 bool mipmapping;
980
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000981 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000982 {
983 case GL_NEAREST:
984 case GL_LINEAR:
985 mipmapping = false;
986 break;
987 case GL_NEAREST_MIPMAP_NEAREST:
988 case GL_LINEAR_MIPMAP_NEAREST:
989 case GL_NEAREST_MIPMAP_LINEAR:
990 case GL_LINEAR_MIPMAP_LINEAR:
991 mipmapping = true;
992 break;
993 default: UNREACHABLE();
994 }
995
996 for (int face = 0; face < 6; face++)
997 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000998 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000999 {
1000 return false;
1001 }
1002 }
1003
1004 if (mipmapping)
1005 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001006 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
1007 {
1008 return false;
1009 }
1010
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001011 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001012
1013 for (int face = 0; face < 6; face++)
1014 {
1015 for (int level = 1; level <= q; level++)
1016 {
1017 if (mImageArray[face][level].format != mImageArray[0][0].format)
1018 {
1019 return false;
1020 }
1021
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001022 if (mImageArray[face][level].width != std::max(1, size >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001023 {
1024 return false;
1025 }
1026
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001027 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001028 }
1029 }
1030 }
1031
1032 return true;
1033}
1034
1035// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001036IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001037{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001038 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001039 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001040
1041 IDirect3DCubeTexture9 *texture;
1042
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001043 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001044
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001045 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001046 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001047 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001048 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001049 }
1050
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001051 if (mTexture) mTexture->Release();
1052
1053 mTexture = texture;
1054 return mTexture;
1055}
1056
1057void TextureCubeMap::updateTexture()
1058{
1059 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001060
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001061 for (int face = 0; face < 6; face++)
1062 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001063 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001064 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001065 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001066
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001067 if (img->dirty)
1068 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001069 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1070 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001071
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001072 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001073 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001074 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001075 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001076
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001077 levelSurface->Release();
1078
1079 img->dirty = false;
1080 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001081 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001082 }
1083 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001084}
1085
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001086IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1087{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001088 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001089
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001090 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001091 {
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001092 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001093 IDirect3DDevice9 *device = getDevice();
1094 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1095
1096 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1097
1098 if (FAILED(result))
1099 {
1100 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1101 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1102 }
1103
1104 if (mTexture != NULL)
1105 {
1106 int levels = texture->GetLevelCount();
1107 for (int f = 0; f < 6; f++)
1108 {
1109 for (int i = 0; i < levels; i++)
1110 {
1111 IDirect3DSurface9 *source;
1112 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1113
1114 if (FAILED(result))
1115 {
1116 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1117
1118 texture->Release();
1119
1120 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1121 }
1122
1123 IDirect3DSurface9 *dest;
1124 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1125
1126 if (FAILED(result))
1127 {
1128 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1129
1130 texture->Release();
1131 source->Release();
1132
1133 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1134 }
1135
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001136 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001137 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1138
1139 if (FAILED(result))
1140 {
1141 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1142
1143 texture->Release();
1144 source->Release();
1145 dest->Release();
1146
1147 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1148 }
1149 }
1150 }
1151 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001152 }
1153
1154 if (mTexture != NULL)
1155 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001156 mTexture->Release();
1157 }
1158
1159 mTexture = texture;
1160 return mTexture;
1161}
1162
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001163void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001164{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001165 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001166
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001167 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001168}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001169
1170unsigned int TextureCubeMap::faceIndex(GLenum face)
1171{
1172 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1173 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1174 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1175 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1176 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1177
1178 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1179}
1180
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001181bool TextureCubeMap::dirtyImageData() const
1182{
1183 int q = log2(mWidth);
1184
1185 for (int f = 0; f < 6; f++)
1186 {
1187 for (int i = 0; i <= q; i++)
1188 {
1189 if (mImageArray[f][i].dirty) return true;
1190 }
1191 }
1192
1193 return false;
1194}
1195
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001196// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1197// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1198// Call this when a particular level of the texture must be defined with a specific format, width and height.
1199//
1200// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1201// a new size for the texture by working backwards from the given size.
1202bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1203{
1204 // Are these settings compatible with level 0?
1205 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1206
1207 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1208
1209 if (!textureOkay)
1210 {
1211 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1212 mImageArray[0][0].format, mImageArray[0][0].width,
1213 internalFormat, width);
1214
1215 // Purge all the levels and the texture.
1216 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1217 {
1218 for (int f = 0; f < 6; f++)
1219 {
1220 if (mImageArray[f][i].surface != NULL)
1221 {
1222 mImageArray[f][i].dirty = false;
1223
1224 mImageArray[f][i].surface->Release();
1225 mImageArray[f][i].surface = NULL;
1226 }
1227 }
1228 }
1229
1230 if (mTexture != NULL)
1231 {
1232 mTexture->Release();
1233 mTexture = NULL;
1234 dropTexture();
1235 }
1236
1237 mWidth = width << level;
1238 mImageArray[0][0].width = width << level;
1239 mHeight = width << level;
1240 mImageArray[0][0].height = width << level;
1241
1242 mImageArray[0][0].format = internalFormat;
1243 }
1244
1245 return !textureOkay;
1246}
1247
1248void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1249{
1250 unsigned int faceindex = faceIndex(face);
1251
1252 if (redefineTexture(level, internalFormat, width))
1253 {
1254 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001255 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001256 }
1257
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001258 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001259
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001260 if (width > 0)
1261 {
1262 RECT sourceRect;
1263 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001264 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001265 sourceRect.top = y;
1266 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001267
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001268 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1269
1270 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1271 dest->Release();
1272 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001273
1274 mImageArray[faceindex][level].width = width;
1275 mImageArray[faceindex][level].height = height;
1276 mImageArray[faceindex][level].format = internalFormat;
1277}
1278
1279IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1280{
1281 unsigned int faceIndex;
1282
1283 if (faceIdentifier < 6)
1284 {
1285 faceIndex = faceIdentifier;
1286 }
1287 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1288 {
1289 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1290 }
1291 else
1292 {
1293 UNREACHABLE();
1294 faceIndex = 0;
1295 }
1296
1297 if (mTexture == NULL)
1298 {
1299 UNREACHABLE();
1300 return NULL;
1301 }
1302
1303 IDirect3DSurface9 *surface = NULL;
1304
1305 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1306
1307 return (SUCCEEDED(hr)) ? surface : NULL;
1308}
1309
1310void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1311{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001312 GLsizei size = mImageArray[faceIndex(face)][level].width;
1313
1314 if (xoffset + width > size || yoffset + height > size)
1315 {
1316 return error(GL_INVALID_VALUE);
1317 }
1318
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001319 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1320 {
1321 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001322 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001323 }
1324 else
1325 {
1326 getRenderTarget(face);
1327 }
1328
1329 RECT sourceRect;
1330 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001331 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001332 sourceRect.top = y;
1333 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001334
1335 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1336
1337 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1338 dest->Release();
1339}
1340
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001341bool TextureCubeMap::isCubeComplete() const
1342{
1343 if (mImageArray[0][0].width == 0)
1344 {
1345 return false;
1346 }
1347
1348 for (unsigned int f = 1; f < 6; f++)
1349 {
1350 if (mImageArray[f][0].width != mImageArray[0][0].width
1351 || mImageArray[f][0].format != mImageArray[0][0].format)
1352 {
1353 return false;
1354 }
1355 }
1356
1357 return true;
1358}
1359
1360void TextureCubeMap::generateMipmaps()
1361{
1362 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1363 {
1364 return error(GL_INVALID_OPERATION);
1365 }
1366
1367 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1368 unsigned int q = log2(mImageArray[0][0].width);
1369 for (unsigned int f = 0; f < 6; f++)
1370 {
1371 for (unsigned int i = 1; i <= q; i++)
1372 {
1373 if (mImageArray[f][i].surface != NULL)
1374 {
1375 mImageArray[f][i].surface->Release();
1376 mImageArray[f][i].surface = NULL;
1377 }
1378
1379 mImageArray[f][i].dirty = false;
1380
1381 mImageArray[f][i].format = mImageArray[f][0].format;
1382 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1383 mImageArray[f][i].height = mImageArray[f][i].width;
1384 }
1385 }
1386
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001387 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001388
1389 for (unsigned int f = 0; f < 6; f++)
1390 {
1391 for (unsigned int i = 1; i <= q; i++)
1392 {
1393 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1394 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1395
1396 if (upper != NULL && lower != NULL)
1397 {
1398 getBlitter()->boxFilter(upper, lower);
1399 }
1400
1401 if (upper != NULL) upper->Release();
1402 if (lower != NULL) lower->Release();
1403 }
1404 }
1405}
1406
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001407Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
1408{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001409 if (!IsCubemapTextureTarget(target))
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001410 {
1411 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
1412 }
1413
1414 unsigned int face = faceIndex(target);
1415
1416 if (mFaceProxies[face] == NULL)
1417 {
1418 mFaceProxies[face] = new TextureColorbufferProxy(this, target);
1419 }
1420
1421 return mFaceProxies[face];
1422}
1423
1424IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1425{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001426 ASSERT(IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001427
1428 needRenderTarget();
1429
1430 IDirect3DSurface9 *renderTarget = NULL;
1431 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1432
1433 return renderTarget;
1434}
1435
1436Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1437 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1438{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001439 ASSERT(target == GL_TEXTURE_2D || IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001440 latchTextureInfo();
1441}
1442
1443IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1444{
1445 latchTextureInfo();
1446
1447 if (mRenderTarget) mRenderTarget->Release();
1448
1449 mRenderTarget = mTexture->getRenderTarget(mTarget);
1450
1451 return mRenderTarget;
1452}
1453
1454void Texture::TextureColorbufferProxy::latchTextureInfo()
1455{
1456 mWidth = mTexture->getWidth();
1457 mHeight = mTexture->getHeight();
1458}
1459
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001460}