blob: 309298b94ab762e07edd2eb5258e313468a42965 [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
11#include "Texture.h"
12
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015#include "main.h"
16#include "mathutil.h"
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000017#include "common/debug.h"
daniel@transgaming.com7051b972010-03-21 04:31:07 +000018#include "utilities.h"
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000019#include "Blit.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000020
21namespace gl
22{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000023
24Texture::Image::Image()
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000025 : width(0), height(0), dirty(false), surface(NULL)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000026{
27}
28
29Texture::Image::~Image()
30{
31 if (surface) surface->Release();
32}
33
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000034Texture::Texture(Context *context) : Colorbuffer(0), mContext(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000035{
36 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
37 mMagFilter = GL_LINEAR;
38 mWrapS = GL_REPEAT;
39 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000040
daniel@transgaming.com00c75962010-03-11 20:36:15 +000041 mDirtyMetaData = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000042}
43
44Texture::~Texture()
45{
46}
47
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000048Blit *Texture::getBlitter()
49{
50 return mContext->getBlitter();
51}
52
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000053// Returns true on successful filter state update (valid enum parameter)
54bool Texture::setMinFilter(GLenum filter)
55{
56 switch (filter)
57 {
58 case GL_NEAREST:
59 case GL_LINEAR:
60 case GL_NEAREST_MIPMAP_NEAREST:
61 case GL_LINEAR_MIPMAP_NEAREST:
62 case GL_NEAREST_MIPMAP_LINEAR:
63 case GL_LINEAR_MIPMAP_LINEAR:
64 mMinFilter = filter;
65 return true;
66 default:
67 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000068 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000069}
70
71// Returns true on successful filter state update (valid enum parameter)
72bool Texture::setMagFilter(GLenum filter)
73{
74 switch (filter)
75 {
76 case GL_NEAREST:
77 case GL_LINEAR:
78 mMagFilter = filter;
79 return true;
80 default:
81 return false;
82 }
83}
84
85// Returns true on successful wrap state update (valid enum parameter)
86bool Texture::setWrapS(GLenum wrap)
87{
88 switch (wrap)
89 {
90 case GL_REPEAT:
91 case GL_CLAMP_TO_EDGE:
92 case GL_MIRRORED_REPEAT:
93 mWrapS = wrap;
94 return true;
95 default:
96 return false;
97 }
98}
99
100// Returns true on successful wrap state update (valid enum parameter)
101bool Texture::setWrapT(GLenum wrap)
102{
103 switch (wrap)
104 {
105 case GL_REPEAT:
106 case GL_CLAMP_TO_EDGE:
107 case GL_MIRRORED_REPEAT:
108 mWrapT = wrap;
109 return true;
110 default:
111 return false;
112 }
113}
114
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000115GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000116{
117 return mMinFilter;
118}
119
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000120GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000121{
122 return mMagFilter;
123}
124
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000125GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000126{
127 return mWrapS;
128}
129
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000130GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000131{
132 return mWrapT;
133}
134
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000135// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000136D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000137{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000138 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000139}
140
141// Returns the size, in bytes, of a single texel in an Image
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000142int Texture::pixelSize(GLenum format, GLenum type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000143{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000144 switch (type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000145 {
146 case GL_UNSIGNED_BYTE:
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000147 switch (format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148 {
149 case GL_ALPHA: return sizeof(unsigned char);
150 case GL_LUMINANCE: return sizeof(unsigned char);
151 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
152 case GL_RGB: return sizeof(unsigned char) * 3;
153 case GL_RGBA: return sizeof(unsigned char) * 4;
154 default: UNREACHABLE();
155 }
156 break;
157 case GL_UNSIGNED_SHORT_4_4_4_4:
158 case GL_UNSIGNED_SHORT_5_5_5_1:
159 case GL_UNSIGNED_SHORT_5_6_5:
160 return sizeof(unsigned short);
161 default: UNREACHABLE();
162 }
163
164 return 0;
165}
166
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000167int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000168{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000169 return img.width * 4;
170}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000171
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000172GLsizei Texture::computePitch(GLsizei width, GLenum format, GLenum type, GLint alignment) const
173{
174 ASSERT(alignment > 0 && isPow2(alignment));
175
176 GLsizei rawPitch = pixelSize(format, type) * width;
177 return (rawPitch + alignment - 1) & ~(alignment - 1);
178}
179
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000180// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
181// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
182void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000183 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000184{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000185 GLsizei inputPitch = computePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000186
187 for (int y = 0; y < height; y++)
188 {
189 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
190 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
191 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
192
193 for (int x = 0; x < width; x++)
194 {
195 unsigned char r;
196 unsigned char g;
197 unsigned char b;
198 unsigned char a;
199
200 switch (format)
201 {
202 case GL_ALPHA:
203 a = source[x];
204 r = 0;
205 g = 0;
206 b = 0;
207 break;
208
209 case GL_LUMINANCE:
210 r = source[x];
211 g = source[x];
212 b = source[x];
213 a = 0xFF;
214 break;
215
216 case GL_LUMINANCE_ALPHA:
217 r = source[2*x+0];
218 g = source[2*x+0];
219 b = source[2*x+0];
220 a = source[2*x+1];
221 break;
222
223 case GL_RGB:
224 switch (type)
225 {
226 case GL_UNSIGNED_BYTE:
227 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000228 g = source[x * 3 + 1];
229 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000230 a = 0xFF;
231 break;
232
233 case GL_UNSIGNED_SHORT_5_6_5:
234 {
235 unsigned short rgba = source16[x];
236
237 a = 0xFF;
238 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
239 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
240 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
241 }
242 break;
243
244 default: UNREACHABLE();
245 }
246 break;
247
248 case GL_RGBA:
249 switch (type)
250 {
251 case GL_UNSIGNED_BYTE:
252 r = source[x * 4 + 0];
253 g = source[x * 4 + 1];
254 b = source[x * 4 + 2];
255 a = source[x * 4 + 3];
256 break;
257
258 case GL_UNSIGNED_SHORT_4_4_4_4:
259 {
260 unsigned short rgba = source16[x];
261
262 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
263 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
264 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
265 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
266 }
267 break;
268
269 case GL_UNSIGNED_SHORT_5_5_5_1:
270 {
271 unsigned short rgba = source16[x];
272
273 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000274 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000275 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000276 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000277 }
278 break;
279
280 default: UNREACHABLE();
281 }
282 break;
283 default: UNREACHABLE();
284 }
285
286 dest[4 * x + 0] = b;
287 dest[4 * x + 1] = g;
288 dest[4 * x + 2] = r;
289 dest[4 * x + 3] = a;
290 }
291 }
292}
293
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000294void 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 +0000295{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000296 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000297
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000298 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000299 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000300 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
301
302 if (FAILED(result))
303 {
304 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
305 return error(GL_OUT_OF_MEMORY);
306 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000307 }
308
309 if (img->surface) img->surface->Release();
310 img->surface = newSurface;
311
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000312 img->width = width;
313 img->height = height;
314 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000315
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000316 if (pixels != NULL && newSurface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000317 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000318 D3DLOCKED_RECT locked;
319 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
320
321 ASSERT(SUCCEEDED(result));
322
323 if (SUCCEEDED(result))
324 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000325 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000326 newSurface->UnlockRect();
327 }
328
329 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000330 }
331
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000332 mDirtyMetaData = true;
333}
334
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000335void 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 +0000336{
337 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
338
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000339 D3DLOCKED_RECT locked;
340 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000341
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000342 ASSERT(SUCCEEDED(result));
343
344 if (SUCCEEDED(result))
345 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000346 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000347 img->surface->UnlockRect();
348 }
349
350 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000351}
352
353IDirect3DBaseTexture9 *Texture::getTexture()
354{
355 if (!isComplete())
356 {
357 return NULL;
358 }
359
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000360 if (mDirtyMetaData)
361 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000362 mBaseTexture = createTexture();
363 }
364
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000365 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000366 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000367 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000368 }
369
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000370 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000371 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000372
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000373 return mBaseTexture;
374}
375
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000376// Returns the top-level texture surface as a render target
377IDirect3DSurface9 *Texture::getRenderTarget(GLenum target)
378{
379 if (mDirtyMetaData && mRenderTarget)
380 {
381 mRenderTarget->Release();
382 mRenderTarget = NULL;
383 }
384
385 if (!mRenderTarget)
386 {
387 mBaseTexture = convertToRenderTarget();
388 mRenderTarget = getSurface(target);
389 }
390
391 if (dirtyImageData())
392 {
393 updateTexture();
394 }
395
396 mDirtyMetaData = false;
397
398 return mRenderTarget;
399}
400
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000401void Texture::dropTexture()
402{
403 if (mRenderTarget)
404 {
405 mRenderTarget->Release();
406 mRenderTarget = NULL;
407 }
408
409 if (mBaseTexture)
410 {
411 mBaseTexture = NULL;
412 }
413}
414
415void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture)
416{
417 mBaseTexture = newTexture;
418 mDirtyMetaData = false;
419}
420
421
422Texture2D::Texture2D(Context *context) : Texture(context)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000423{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000424 mTexture = NULL;
425}
426
427Texture2D::~Texture2D()
428{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000429 if (mTexture)
430 {
431 mTexture->Release();
432 mTexture = NULL;
433 }
434}
435
436GLenum Texture2D::getTarget() const
437{
438 return GL_TEXTURE_2D;
439}
440
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000441// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
442// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
443// Call this when a particular level of the texture must be defined with a specific format, width and height.
444//
445// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
446// a new height and width for the texture by working backwards from the given width and height.
447bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
448{
449 bool widthOkay = (mWidth >> level == width);
450 bool heightOkay = (mHeight >> level == height);
451
452 bool sizeOkay = ((widthOkay && heightOkay)
453 || (widthOkay && mHeight >> level == 0 && height == 1)
454 || (heightOkay && mWidth >> level == 0 && width == 1));
455
456 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
457
458 if (!textureOkay)
459 {
460 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
461 mImageArray[0].format, mWidth, mHeight,
462 internalFormat, width, height);
463
464 // Purge all the levels and the texture.
465
466 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
467 {
468 if (mImageArray[i].surface != NULL)
469 {
470 mImageArray[i].dirty = false;
471
472 mImageArray[i].surface->Release();
473 mImageArray[i].surface = NULL;
474 }
475 }
476
477 if (mTexture != NULL)
478 {
479 mTexture->Release();
480 mTexture = NULL;
481 dropTexture();
482 }
483
484 mWidth = width << level;
485 mHeight = height << level;
486 mImageArray[0].format = internalFormat;
487 }
488
489 return !textureOkay;
490}
491
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000492void 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 +0000493{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000494 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000495
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000496 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000497}
498
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000499void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
500{
501 ASSERT(mImageArray[level].surface != NULL);
502
503 if (mTexture != NULL)
504 {
505 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000506 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000507
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000508 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000509
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000510 if (SUCCEEDED(result))
511 {
512 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000513
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000514 RECT sourceRect;
515 sourceRect.left = xoffset;
516 sourceRect.top = yoffset;
517 sourceRect.right = xoffset + width;
518 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000519
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000520 POINT destPoint;
521 destPoint.x = xoffset;
522 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000523
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000524 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
525 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000526
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000527 destLevel->Release();
528
529 img->dirty = false;
530 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000531 }
532}
533
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000534void 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 +0000535{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000536 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000537 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000538}
539
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000540void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
541{
542 if (redefineTexture(level, internalFormat, width, height))
543 {
544 convertToRenderTarget();
545 pushTexture(mTexture);
546 }
547
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000548 if (width != 0 && height != 0)
549 {
550 RECT sourceRect;
551 sourceRect.left = x;
552 sourceRect.top = y + height;
553 sourceRect.right = x + width;
554 sourceRect.bottom = y;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000555
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000556 IDirect3DSurface9 *dest;
557 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000558
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000559 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
560 dest->Release();
561 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000562
563 mImageArray[level].width = width;
564 mImageArray[level].height = height;
565 mImageArray[level].format = internalFormat;
566}
567
568void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
569{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000570 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
571 {
572 return error(GL_INVALID_VALUE);
573 }
574
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000575 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
576 {
577 convertToRenderTarget();
578 pushTexture(mTexture);
579 }
580 else
581 {
582 getRenderTarget(GL_TEXTURE_2D);
583 }
584
585 RECT sourceRect;
586 sourceRect.left = x;
587 sourceRect.top = y + height;
588 sourceRect.right = x + width;
589 sourceRect.bottom = y;
590
591 IDirect3DSurface9 *dest;
592 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
593
594 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
595 dest->Release();
596}
597
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000598// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
599bool Texture2D::isComplete() const
600{
601 ASSERT(mWidth == mImageArray[0].width && mHeight == mImageArray[0].height);
602
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000603 if (mWidth <= 0 || mHeight <= 0)
604 {
605 return false;
606 }
607
608 bool mipmapping;
609
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000610 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000611 {
612 case GL_NEAREST:
613 case GL_LINEAR:
614 mipmapping = false;
615 break;
616 case GL_NEAREST_MIPMAP_NEAREST:
617 case GL_LINEAR_MIPMAP_NEAREST:
618 case GL_NEAREST_MIPMAP_LINEAR:
619 case GL_LINEAR_MIPMAP_LINEAR:
620 mipmapping = true;
621 break;
622 default: UNREACHABLE();
623 }
624
625 if (mipmapping)
626 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000627 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000628
629 for (int level = 1; level <= q; level++)
630 {
631 if (mImageArray[level].format != mImageArray[0].format)
632 {
633 return false;
634 }
635
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000636 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
637 {
638 return false;
639 }
640
641 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
642 {
643 return false;
644 }
645 }
646 }
647
648 return true;
649}
650
651// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000652IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000653{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000654 IDirect3DTexture9 *texture;
655
656 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000657 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000658
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000659 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000660
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000661 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000662 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000663 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000664 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000665 }
666
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000667 if (mTexture) mTexture->Release();
668 mTexture = texture;
669 return texture;
670}
671
672void Texture2D::updateTexture()
673{
674 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000675
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000676 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000677
678 for (int level = 0; level < levelCount; level++)
679 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000680 if (mImageArray[level].dirty)
681 {
682 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000683 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000684
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000685 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000686
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000687 if (SUCCEEDED(result))
688 {
689 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
690 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000691
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000692 levelSurface->Release();
693
694 mImageArray[level].dirty = false;
695 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000696 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000697 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000698}
699
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000700IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000701{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000702 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000703
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000704 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000705 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000706 IDirect3DDevice9 *device = getDevice();
707 D3DFORMAT format = selectFormat(mImageArray[0].format);
708
709 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
710
711 if (FAILED(result))
712 {
713 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
714 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
715 }
716
717 if (mTexture != NULL)
718 {
719 int levels = texture->GetLevelCount();
720 for (int i = 0; i < levels; i++)
721 {
722 IDirect3DSurface9 *source;
723 result = mTexture->GetSurfaceLevel(i, &source);
724
725 if (FAILED(result))
726 {
727 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
728
729 texture->Release();
730
731 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
732 }
733
734 IDirect3DSurface9 *dest;
735 result = texture->GetSurfaceLevel(i, &dest);
736
737 if (FAILED(result))
738 {
739 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
740
741 texture->Release();
742 source->Release();
743
744 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
745 }
746
747 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
748
749 if (FAILED(result))
750 {
751 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
752
753 texture->Release();
754 source->Release();
755 dest->Release();
756
757 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
758 }
759
760 source->Release();
761 dest->Release();
762 }
763 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000764 }
765
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000766 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000767 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000768 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000769 }
770
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000771 mTexture = texture;
772 return mTexture;
773}
774
775IDirect3DSurface9 *Texture2D::getSurface(GLenum target)
776{
777 ASSERT(target == GL_TEXTURE_2D);
778
779 IDirect3DSurface9 *surface = NULL;
780 HRESULT result = mTexture->GetSurfaceLevel(0, &surface);
781
782 ASSERT(SUCCEEDED(result));
783
784 return surface;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000785}
786
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000787bool Texture2D::dirtyImageData() const
788{
789 int q = log2(std::max(mWidth, mHeight));
790
791 for (int i = 0; i <= q; i++)
792 {
793 if (mImageArray[i].dirty) return true;
794 }
795
796 return false;
797}
798
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000799TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000800{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000801 mTexture = NULL;
802}
803
804TextureCubeMap::~TextureCubeMap()
805{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000806 if (mTexture)
807 {
808 mTexture->Release();
809 mTexture = NULL;
810 }
811}
812
813GLenum TextureCubeMap::getTarget() const
814{
815 return GL_TEXTURE_CUBE_MAP;
816}
817
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000818void 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 +0000819{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000820 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000821}
822
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000823void 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 +0000824{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000825 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000826}
827
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000828void 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 +0000829{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000830 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000831}
832
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000833void 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 +0000834{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000835 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000836}
837
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000838void 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 +0000839{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000840 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000841}
842
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000843void 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 +0000844{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000845 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000846}
847
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000848void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
849{
850 int face = faceIndex(faceTarget);
851
852 ASSERT(mImageArray[face][level].surface != NULL);
853
854 if (mTexture != NULL)
855 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000856 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
857 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000858
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000859 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000860 {
861 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000862
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000863 RECT sourceRect;
864 sourceRect.left = xoffset;
865 sourceRect.top = yoffset;
866 sourceRect.right = xoffset + width;
867 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000868
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000869 POINT destPoint;
870 destPoint.x = xoffset;
871 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000872
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000873 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000874 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000875
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000876 destLevel->Release();
877
878 img->dirty = false;
879 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000880 }
881}
882
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000883void 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 +0000884{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000885 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000886 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000887}
888
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000889// 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 +0000890bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000891{
892 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
893 {
894 return false;
895 }
896
897 bool mipmapping;
898
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000899 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000900 {
901 case GL_NEAREST:
902 case GL_LINEAR:
903 mipmapping = false;
904 break;
905 case GL_NEAREST_MIPMAP_NEAREST:
906 case GL_LINEAR_MIPMAP_NEAREST:
907 case GL_NEAREST_MIPMAP_LINEAR:
908 case GL_LINEAR_MIPMAP_LINEAR:
909 mipmapping = true;
910 break;
911 default: UNREACHABLE();
912 }
913
914 for (int face = 0; face < 6; face++)
915 {
916 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
917 {
918 return false;
919 }
920 }
921
922 if (mipmapping)
923 {
924 int q = log2(mWidth);
925
926 for (int face = 0; face < 6; face++)
927 {
928 for (int level = 1; level <= q; level++)
929 {
930 if (mImageArray[face][level].format != mImageArray[0][0].format)
931 {
932 return false;
933 }
934
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000935 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
936 {
937 return false;
938 }
939
940 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
941 {
942 return false;
943 }
944 }
945 }
946 }
947
948 return true;
949}
950
951// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000952IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000953{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000954 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000955 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000956
957 IDirect3DCubeTexture9 *texture;
958
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000959 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000960
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000961 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000962 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000963 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000964 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000965 }
966
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000967 if (mTexture) mTexture->Release();
968
969 mTexture = texture;
970 return mTexture;
971}
972
973void TextureCubeMap::updateTexture()
974{
975 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000976
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000977 for (int face = 0; face < 6; face++)
978 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000979 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000980 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000981 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000982
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000983 if (img->dirty)
984 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000985 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
986 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000987
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000988 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000989 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000990 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000991 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000992
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000993 levelSurface->Release();
994
995 img->dirty = false;
996 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000997 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000998 }
999 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001000}
1001
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001002IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1003{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001004 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001005
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001006 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001007 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001008 IDirect3DDevice9 *device = getDevice();
1009 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1010
1011 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1012
1013 if (FAILED(result))
1014 {
1015 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1016 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1017 }
1018
1019 if (mTexture != NULL)
1020 {
1021 int levels = texture->GetLevelCount();
1022 for (int f = 0; f < 6; f++)
1023 {
1024 for (int i = 0; i < levels; i++)
1025 {
1026 IDirect3DSurface9 *source;
1027 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1028
1029 if (FAILED(result))
1030 {
1031 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1032
1033 texture->Release();
1034
1035 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1036 }
1037
1038 IDirect3DSurface9 *dest;
1039 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1040
1041 if (FAILED(result))
1042 {
1043 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1044
1045 texture->Release();
1046 source->Release();
1047
1048 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1049 }
1050
1051 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1052
1053 if (FAILED(result))
1054 {
1055 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1056
1057 texture->Release();
1058 source->Release();
1059 dest->Release();
1060
1061 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1062 }
1063 }
1064 }
1065 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001066 }
1067
1068 if (mTexture != NULL)
1069 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001070 mTexture->Release();
1071 }
1072
1073 mTexture = texture;
1074 return mTexture;
1075}
1076
1077IDirect3DSurface9 *TextureCubeMap::getSurface(GLenum target)
1078{
1079 ASSERT(es2dx::IsCubemapTextureTarget(target));
1080
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001081 IDirect3DSurface9 *surface = getCubeMapSurface(target, 0);
1082 ASSERT(surface != NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001083 return surface;
1084}
1085
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001086void 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 +00001087{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001088 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001089
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001090 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001091}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001092
1093unsigned int TextureCubeMap::faceIndex(GLenum face)
1094{
1095 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1096 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1097 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1098 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1099 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1100
1101 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1102}
1103
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001104bool TextureCubeMap::dirtyImageData() const
1105{
1106 int q = log2(mWidth);
1107
1108 for (int f = 0; f < 6; f++)
1109 {
1110 for (int i = 0; i <= q; i++)
1111 {
1112 if (mImageArray[f][i].dirty) return true;
1113 }
1114 }
1115
1116 return false;
1117}
1118
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001119// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1120// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1121// Call this when a particular level of the texture must be defined with a specific format, width and height.
1122//
1123// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1124// a new size for the texture by working backwards from the given size.
1125bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1126{
1127 // Are these settings compatible with level 0?
1128 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1129
1130 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1131
1132 if (!textureOkay)
1133 {
1134 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1135 mImageArray[0][0].format, mImageArray[0][0].width,
1136 internalFormat, width);
1137
1138 // Purge all the levels and the texture.
1139 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1140 {
1141 for (int f = 0; f < 6; f++)
1142 {
1143 if (mImageArray[f][i].surface != NULL)
1144 {
1145 mImageArray[f][i].dirty = false;
1146
1147 mImageArray[f][i].surface->Release();
1148 mImageArray[f][i].surface = NULL;
1149 }
1150 }
1151 }
1152
1153 if (mTexture != NULL)
1154 {
1155 mTexture->Release();
1156 mTexture = NULL;
1157 dropTexture();
1158 }
1159
1160 mWidth = width << level;
1161 mImageArray[0][0].width = width << level;
1162 mHeight = width << level;
1163 mImageArray[0][0].height = width << level;
1164
1165 mImageArray[0][0].format = internalFormat;
1166 }
1167
1168 return !textureOkay;
1169}
1170
1171void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1172{
1173 unsigned int faceindex = faceIndex(face);
1174
1175 if (redefineTexture(level, internalFormat, width))
1176 {
1177 convertToRenderTarget();
1178 pushTexture(mTexture);
1179 }
1180
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001181 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001182
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001183 if (width > 0)
1184 {
1185 RECT sourceRect;
1186 sourceRect.left = x;
1187 sourceRect.top = y + height;
1188 sourceRect.right = x + width;
1189 sourceRect.bottom = y;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001190
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001191 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1192
1193 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1194 dest->Release();
1195 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001196
1197 mImageArray[faceindex][level].width = width;
1198 mImageArray[faceindex][level].height = height;
1199 mImageArray[faceindex][level].format = internalFormat;
1200}
1201
1202IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1203{
1204 unsigned int faceIndex;
1205
1206 if (faceIdentifier < 6)
1207 {
1208 faceIndex = faceIdentifier;
1209 }
1210 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1211 {
1212 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1213 }
1214 else
1215 {
1216 UNREACHABLE();
1217 faceIndex = 0;
1218 }
1219
1220 if (mTexture == NULL)
1221 {
1222 UNREACHABLE();
1223 return NULL;
1224 }
1225
1226 IDirect3DSurface9 *surface = NULL;
1227
1228 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1229
1230 return (SUCCEEDED(hr)) ? surface : NULL;
1231}
1232
1233void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1234{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001235 GLsizei size = mImageArray[faceIndex(face)][level].width;
1236
1237 if (xoffset + width > size || yoffset + height > size)
1238 {
1239 return error(GL_INVALID_VALUE);
1240 }
1241
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001242 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1243 {
1244 convertToRenderTarget();
1245 pushTexture(mTexture);
1246 }
1247 else
1248 {
1249 getRenderTarget(face);
1250 }
1251
1252 RECT sourceRect;
1253 sourceRect.left = x;
1254 sourceRect.top = y + height;
1255 sourceRect.right = x + width;
1256 sourceRect.bottom = y;
1257
1258 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1259
1260 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1261 dest->Release();
1262}
1263
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001264}