blob: f5303078b9b4901275de8d63139dbce327a53f7b [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;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000552 sourceRect.right = x + width;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000553 sourceRect.top = source->getHeight() - (y + height);
554 sourceRect.bottom = source->getHeight() - 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;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000587 sourceRect.right = x + width;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000588 sourceRect.top = source->getHeight() - (y + height);
589 sourceRect.bottom = source->getHeight() - y;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000590
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.com8fd99e22010-04-20 18:52:00 +0000799void Texture2D::generateMipmaps()
800{
801 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
802 {
803 return error(GL_INVALID_OPERATION);
804 }
805
806 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
807 unsigned int q = log2(std::max(mWidth, mHeight));
808 for (unsigned int i = 1; i <= q; i++)
809 {
810 if (mImageArray[i].surface != NULL)
811 {
812 mImageArray[i].surface->Release();
813 mImageArray[i].surface = NULL;
814 }
815
816 mImageArray[i].dirty = false;
817
818 mImageArray[i].format = mImageArray[0].format;
819 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
820 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
821 }
822
823 getRenderTarget();
824
825 for (unsigned int i = 1; i <= q; i++)
826 {
827 IDirect3DSurface9 *upper = NULL;
828 IDirect3DSurface9 *lower = NULL;
829
830 mTexture->GetSurfaceLevel(i-1, &upper);
831 mTexture->GetSurfaceLevel(i, &lower);
832
833 if (upper != NULL && lower != NULL)
834 {
835 getBlitter()->boxFilter(upper, lower);
836 }
837
838 if (upper != NULL) upper->Release();
839 if (lower != NULL) lower->Release();
840 }
841}
842
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000843TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000844{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000845 mTexture = NULL;
846}
847
848TextureCubeMap::~TextureCubeMap()
849{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000850 if (mTexture)
851 {
852 mTexture->Release();
853 mTexture = NULL;
854 }
855}
856
857GLenum TextureCubeMap::getTarget() const
858{
859 return GL_TEXTURE_CUBE_MAP;
860}
861
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000862void 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 +0000863{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000864 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000865}
866
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000867void 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 +0000868{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000869 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000870}
871
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000872void 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 +0000873{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000874 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000875}
876
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000877void 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 +0000878{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000879 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000880}
881
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000882void 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 +0000883{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000884 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000885}
886
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000887void 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 +0000888{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000889 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000890}
891
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000892void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
893{
894 int face = faceIndex(faceTarget);
895
896 ASSERT(mImageArray[face][level].surface != NULL);
897
898 if (mTexture != NULL)
899 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000900 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
901 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000902
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000903 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000904 {
905 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000906
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000907 RECT sourceRect;
908 sourceRect.left = xoffset;
909 sourceRect.top = yoffset;
910 sourceRect.right = xoffset + width;
911 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000912
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000913 POINT destPoint;
914 destPoint.x = xoffset;
915 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000916
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000917 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000918 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000919
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000920 destLevel->Release();
921
922 img->dirty = false;
923 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000924 }
925}
926
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000927void 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 +0000928{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000929 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000930 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000931}
932
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000933// 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 +0000934bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000935{
936 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
937 {
938 return false;
939 }
940
941 bool mipmapping;
942
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000943 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000944 {
945 case GL_NEAREST:
946 case GL_LINEAR:
947 mipmapping = false;
948 break;
949 case GL_NEAREST_MIPMAP_NEAREST:
950 case GL_LINEAR_MIPMAP_NEAREST:
951 case GL_NEAREST_MIPMAP_LINEAR:
952 case GL_LINEAR_MIPMAP_LINEAR:
953 mipmapping = true;
954 break;
955 default: UNREACHABLE();
956 }
957
958 for (int face = 0; face < 6; face++)
959 {
960 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
961 {
962 return false;
963 }
964 }
965
966 if (mipmapping)
967 {
968 int q = log2(mWidth);
969
970 for (int face = 0; face < 6; face++)
971 {
972 for (int level = 1; level <= q; level++)
973 {
974 if (mImageArray[face][level].format != mImageArray[0][0].format)
975 {
976 return false;
977 }
978
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000979 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
980 {
981 return false;
982 }
983
984 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
985 {
986 return false;
987 }
988 }
989 }
990 }
991
992 return true;
993}
994
995// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000996IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000997{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000998 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000999 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001000
1001 IDirect3DCubeTexture9 *texture;
1002
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001003 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001004
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001005 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001006 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001007 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001008 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001009 }
1010
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001011 if (mTexture) mTexture->Release();
1012
1013 mTexture = texture;
1014 return mTexture;
1015}
1016
1017void TextureCubeMap::updateTexture()
1018{
1019 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001020
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001021 for (int face = 0; face < 6; face++)
1022 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001023 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001024 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001025 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001026
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001027 if (img->dirty)
1028 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001029 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1030 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001031
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001032 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001033 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001034 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001035 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001036
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001037 levelSurface->Release();
1038
1039 img->dirty = false;
1040 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001041 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001042 }
1043 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001044}
1045
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001046IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1047{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001048 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001049
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001050 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001051 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001052 IDirect3DDevice9 *device = getDevice();
1053 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1054
1055 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1056
1057 if (FAILED(result))
1058 {
1059 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1060 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1061 }
1062
1063 if (mTexture != NULL)
1064 {
1065 int levels = texture->GetLevelCount();
1066 for (int f = 0; f < 6; f++)
1067 {
1068 for (int i = 0; i < levels; i++)
1069 {
1070 IDirect3DSurface9 *source;
1071 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1072
1073 if (FAILED(result))
1074 {
1075 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1076
1077 texture->Release();
1078
1079 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1080 }
1081
1082 IDirect3DSurface9 *dest;
1083 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1084
1085 if (FAILED(result))
1086 {
1087 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1088
1089 texture->Release();
1090 source->Release();
1091
1092 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1093 }
1094
1095 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1096
1097 if (FAILED(result))
1098 {
1099 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1100
1101 texture->Release();
1102 source->Release();
1103 dest->Release();
1104
1105 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1106 }
1107 }
1108 }
1109 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001110 }
1111
1112 if (mTexture != NULL)
1113 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001114 mTexture->Release();
1115 }
1116
1117 mTexture = texture;
1118 return mTexture;
1119}
1120
1121IDirect3DSurface9 *TextureCubeMap::getSurface(GLenum target)
1122{
1123 ASSERT(es2dx::IsCubemapTextureTarget(target));
1124
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001125 IDirect3DSurface9 *surface = getCubeMapSurface(target, 0);
1126 ASSERT(surface != NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001127 return surface;
1128}
1129
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001130void 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 +00001131{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001132 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001133
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001134 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001135}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001136
1137unsigned int TextureCubeMap::faceIndex(GLenum face)
1138{
1139 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1140 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1141 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1142 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1143 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1144
1145 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1146}
1147
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001148bool TextureCubeMap::dirtyImageData() const
1149{
1150 int q = log2(mWidth);
1151
1152 for (int f = 0; f < 6; f++)
1153 {
1154 for (int i = 0; i <= q; i++)
1155 {
1156 if (mImageArray[f][i].dirty) return true;
1157 }
1158 }
1159
1160 return false;
1161}
1162
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001163// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1164// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1165// Call this when a particular level of the texture must be defined with a specific format, width and height.
1166//
1167// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1168// a new size for the texture by working backwards from the given size.
1169bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1170{
1171 // Are these settings compatible with level 0?
1172 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1173
1174 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1175
1176 if (!textureOkay)
1177 {
1178 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1179 mImageArray[0][0].format, mImageArray[0][0].width,
1180 internalFormat, width);
1181
1182 // Purge all the levels and the texture.
1183 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1184 {
1185 for (int f = 0; f < 6; f++)
1186 {
1187 if (mImageArray[f][i].surface != NULL)
1188 {
1189 mImageArray[f][i].dirty = false;
1190
1191 mImageArray[f][i].surface->Release();
1192 mImageArray[f][i].surface = NULL;
1193 }
1194 }
1195 }
1196
1197 if (mTexture != NULL)
1198 {
1199 mTexture->Release();
1200 mTexture = NULL;
1201 dropTexture();
1202 }
1203
1204 mWidth = width << level;
1205 mImageArray[0][0].width = width << level;
1206 mHeight = width << level;
1207 mImageArray[0][0].height = width << level;
1208
1209 mImageArray[0][0].format = internalFormat;
1210 }
1211
1212 return !textureOkay;
1213}
1214
1215void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1216{
1217 unsigned int faceindex = faceIndex(face);
1218
1219 if (redefineTexture(level, internalFormat, width))
1220 {
1221 convertToRenderTarget();
1222 pushTexture(mTexture);
1223 }
1224
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001225 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001226
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001227 if (width > 0)
1228 {
1229 RECT sourceRect;
1230 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001231 sourceRect.right = x + width;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001232 sourceRect.top = source->getHeight() - (y + height);
1233 sourceRect.bottom = source->getHeight() - y;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001234
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001235 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1236
1237 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1238 dest->Release();
1239 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001240
1241 mImageArray[faceindex][level].width = width;
1242 mImageArray[faceindex][level].height = height;
1243 mImageArray[faceindex][level].format = internalFormat;
1244}
1245
1246IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1247{
1248 unsigned int faceIndex;
1249
1250 if (faceIdentifier < 6)
1251 {
1252 faceIndex = faceIdentifier;
1253 }
1254 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1255 {
1256 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1257 }
1258 else
1259 {
1260 UNREACHABLE();
1261 faceIndex = 0;
1262 }
1263
1264 if (mTexture == NULL)
1265 {
1266 UNREACHABLE();
1267 return NULL;
1268 }
1269
1270 IDirect3DSurface9 *surface = NULL;
1271
1272 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1273
1274 return (SUCCEEDED(hr)) ? surface : NULL;
1275}
1276
1277void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1278{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001279 GLsizei size = mImageArray[faceIndex(face)][level].width;
1280
1281 if (xoffset + width > size || yoffset + height > size)
1282 {
1283 return error(GL_INVALID_VALUE);
1284 }
1285
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001286 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1287 {
1288 convertToRenderTarget();
1289 pushTexture(mTexture);
1290 }
1291 else
1292 {
1293 getRenderTarget(face);
1294 }
1295
1296 RECT sourceRect;
1297 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001298 sourceRect.right = x + width;
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001299 sourceRect.top = source->getHeight() - (y + height);
1300 sourceRect.bottom = source->getHeight() - y;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001301
1302 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1303
1304 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1305 dest->Release();
1306}
1307
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001308bool TextureCubeMap::isCubeComplete() const
1309{
1310 if (mImageArray[0][0].width == 0)
1311 {
1312 return false;
1313 }
1314
1315 for (unsigned int f = 1; f < 6; f++)
1316 {
1317 if (mImageArray[f][0].width != mImageArray[0][0].width
1318 || mImageArray[f][0].format != mImageArray[0][0].format)
1319 {
1320 return false;
1321 }
1322 }
1323
1324 return true;
1325}
1326
1327void TextureCubeMap::generateMipmaps()
1328{
1329 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1330 {
1331 return error(GL_INVALID_OPERATION);
1332 }
1333
1334 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1335 unsigned int q = log2(mImageArray[0][0].width);
1336 for (unsigned int f = 0; f < 6; f++)
1337 {
1338 for (unsigned int i = 1; i <= q; i++)
1339 {
1340 if (mImageArray[f][i].surface != NULL)
1341 {
1342 mImageArray[f][i].surface->Release();
1343 mImageArray[f][i].surface = NULL;
1344 }
1345
1346 mImageArray[f][i].dirty = false;
1347
1348 mImageArray[f][i].format = mImageArray[f][0].format;
1349 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1350 mImageArray[f][i].height = mImageArray[f][i].width;
1351 }
1352 }
1353
1354 getRenderTarget();
1355
1356 for (unsigned int f = 0; f < 6; f++)
1357 {
1358 for (unsigned int i = 1; i <= q; i++)
1359 {
1360 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1361 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1362
1363 if (upper != NULL && lower != NULL)
1364 {
1365 getBlitter()->boxFilter(upper, lower);
1366 }
1367
1368 if (upper != NULL) upper->Release();
1369 if (lower != NULL) lower->Release();
1370 }
1371 }
1372}
1373
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001374}