blob: 2d0bc4c04c72f42d14b50ef846bb7811b685cb55 [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000011#include "libGLESv2/Texture.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000015#include "common/debug.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000016
17#include "libGLESv2/main.h"
18#include "libGLESv2/mathutil.h"
19#include "libGLESv2/utilities.h"
20#include "libGLESv2/Blit.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021
22namespace gl
23{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000024
25Texture::Image::Image()
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000026 : width(0), height(0), dirty(false), surface(NULL)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000027{
28}
29
30Texture::Image::~Image()
31{
32 if (surface) surface->Release();
33}
34
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000035Texture::Texture(Context *context) : Colorbuffer(0), mContext(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000036{
37 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
38 mMagFilter = GL_LINEAR;
39 mWrapS = GL_REPEAT;
40 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000041
daniel@transgaming.com00c75962010-03-11 20:36:15 +000042 mDirtyMetaData = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000043}
44
45Texture::~Texture()
46{
47}
48
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000049Blit *Texture::getBlitter()
50{
51 return mContext->getBlitter();
52}
53
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000054// Returns true on successful filter state update (valid enum parameter)
55bool Texture::setMinFilter(GLenum filter)
56{
57 switch (filter)
58 {
59 case GL_NEAREST:
60 case GL_LINEAR:
61 case GL_NEAREST_MIPMAP_NEAREST:
62 case GL_LINEAR_MIPMAP_NEAREST:
63 case GL_NEAREST_MIPMAP_LINEAR:
64 case GL_LINEAR_MIPMAP_LINEAR:
65 mMinFilter = filter;
66 return true;
67 default:
68 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000069 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000070}
71
72// Returns true on successful filter state update (valid enum parameter)
73bool Texture::setMagFilter(GLenum filter)
74{
75 switch (filter)
76 {
77 case GL_NEAREST:
78 case GL_LINEAR:
79 mMagFilter = filter;
80 return true;
81 default:
82 return false;
83 }
84}
85
86// Returns true on successful wrap state update (valid enum parameter)
87bool Texture::setWrapS(GLenum wrap)
88{
89 switch (wrap)
90 {
91 case GL_REPEAT:
92 case GL_CLAMP_TO_EDGE:
93 case GL_MIRRORED_REPEAT:
94 mWrapS = wrap;
95 return true;
96 default:
97 return false;
98 }
99}
100
101// Returns true on successful wrap state update (valid enum parameter)
102bool Texture::setWrapT(GLenum wrap)
103{
104 switch (wrap)
105 {
106 case GL_REPEAT:
107 case GL_CLAMP_TO_EDGE:
108 case GL_MIRRORED_REPEAT:
109 mWrapT = wrap;
110 return true;
111 default:
112 return false;
113 }
114}
115
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000116GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000117{
118 return mMinFilter;
119}
120
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000121GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000122{
123 return mMagFilter;
124}
125
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000126GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000127{
128 return mWrapS;
129}
130
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000131GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000132{
133 return mWrapT;
134}
135
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000136// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000137D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000138{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000139 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000140}
141
142// Returns the size, in bytes, of a single texel in an Image
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000143int Texture::pixelSize(GLenum format, GLenum type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000144{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000145 switch (type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000146 {
147 case GL_UNSIGNED_BYTE:
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000148 switch (format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000149 {
150 case GL_ALPHA: return sizeof(unsigned char);
151 case GL_LUMINANCE: return sizeof(unsigned char);
152 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
153 case GL_RGB: return sizeof(unsigned char) * 3;
154 case GL_RGBA: return sizeof(unsigned char) * 4;
155 default: UNREACHABLE();
156 }
157 break;
158 case GL_UNSIGNED_SHORT_4_4_4_4:
159 case GL_UNSIGNED_SHORT_5_5_5_1:
160 case GL_UNSIGNED_SHORT_5_6_5:
161 return sizeof(unsigned short);
162 default: UNREACHABLE();
163 }
164
165 return 0;
166}
167
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000168int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000169{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000170 return img.width * 4;
171}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000172
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000173GLsizei Texture::computePitch(GLsizei width, GLenum format, GLenum type, GLint alignment) const
174{
175 ASSERT(alignment > 0 && isPow2(alignment));
176
177 GLsizei rawPitch = pixelSize(format, type) * width;
178 return (rawPitch + alignment - 1) & ~(alignment - 1);
179}
180
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000181// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
182// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
183void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000184 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000185{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000186 GLsizei inputPitch = computePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000187
188 for (int y = 0; y < height; y++)
189 {
190 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
191 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
192 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
193
194 for (int x = 0; x < width; x++)
195 {
196 unsigned char r;
197 unsigned char g;
198 unsigned char b;
199 unsigned char a;
200
201 switch (format)
202 {
203 case GL_ALPHA:
204 a = source[x];
205 r = 0;
206 g = 0;
207 b = 0;
208 break;
209
210 case GL_LUMINANCE:
211 r = source[x];
212 g = source[x];
213 b = source[x];
214 a = 0xFF;
215 break;
216
217 case GL_LUMINANCE_ALPHA:
218 r = source[2*x+0];
219 g = source[2*x+0];
220 b = source[2*x+0];
221 a = source[2*x+1];
222 break;
223
224 case GL_RGB:
225 switch (type)
226 {
227 case GL_UNSIGNED_BYTE:
228 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000229 g = source[x * 3 + 1];
230 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000231 a = 0xFF;
232 break;
233
234 case GL_UNSIGNED_SHORT_5_6_5:
235 {
236 unsigned short rgba = source16[x];
237
238 a = 0xFF;
239 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
240 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
241 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
242 }
243 break;
244
245 default: UNREACHABLE();
246 }
247 break;
248
249 case GL_RGBA:
250 switch (type)
251 {
252 case GL_UNSIGNED_BYTE:
253 r = source[x * 4 + 0];
254 g = source[x * 4 + 1];
255 b = source[x * 4 + 2];
256 a = source[x * 4 + 3];
257 break;
258
259 case GL_UNSIGNED_SHORT_4_4_4_4:
260 {
261 unsigned short rgba = source16[x];
262
263 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
264 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
265 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
266 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
267 }
268 break;
269
270 case GL_UNSIGNED_SHORT_5_5_5_1:
271 {
272 unsigned short rgba = source16[x];
273
274 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000275 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000276 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000277 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000278 }
279 break;
280
281 default: UNREACHABLE();
282 }
283 break;
284 default: UNREACHABLE();
285 }
286
287 dest[4 * x + 0] = b;
288 dest[4 * x + 1] = g;
289 dest[4 * x + 2] = r;
290 dest[4 * x + 3] = a;
291 }
292 }
293}
294
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000295void 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 +0000296{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000297 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000298
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000299 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000300 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000301 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
302
303 if (FAILED(result))
304 {
305 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
306 return error(GL_OUT_OF_MEMORY);
307 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000308 }
309
310 if (img->surface) img->surface->Release();
311 img->surface = newSurface;
312
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000313 img->width = width;
314 img->height = height;
315 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000316
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000317 if (pixels != NULL && newSurface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000318 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000319 D3DLOCKED_RECT locked;
320 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
321
322 ASSERT(SUCCEEDED(result));
323
324 if (SUCCEEDED(result))
325 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000326 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000327 newSurface->UnlockRect();
328 }
329
330 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000331 }
332
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000333 mDirtyMetaData = true;
334}
335
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000336void 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 +0000337{
338 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
339
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000340 D3DLOCKED_RECT locked;
341 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000342
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000343 ASSERT(SUCCEEDED(result));
344
345 if (SUCCEEDED(result))
346 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000347 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000348 img->surface->UnlockRect();
349 }
350
351 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000352}
353
354IDirect3DBaseTexture9 *Texture::getTexture()
355{
356 if (!isComplete())
357 {
358 return NULL;
359 }
360
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000361 if (mDirtyMetaData)
362 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000363 mBaseTexture = createTexture();
364 }
365
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000366 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000367 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000368 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000369 }
370
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000371 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000372 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000373
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000374 return mBaseTexture;
375}
376
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000377// Returns the top-level texture surface as a render target
378IDirect3DSurface9 *Texture::getRenderTarget(GLenum target)
379{
380 if (mDirtyMetaData && mRenderTarget)
381 {
382 mRenderTarget->Release();
383 mRenderTarget = NULL;
384 }
385
386 if (!mRenderTarget)
387 {
388 mBaseTexture = convertToRenderTarget();
389 mRenderTarget = getSurface(target);
390 }
391
392 if (dirtyImageData())
393 {
394 updateTexture();
395 }
396
397 mDirtyMetaData = false;
398
399 return mRenderTarget;
400}
401
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000402void Texture::dropTexture()
403{
404 if (mRenderTarget)
405 {
406 mRenderTarget->Release();
407 mRenderTarget = NULL;
408 }
409
410 if (mBaseTexture)
411 {
412 mBaseTexture = NULL;
413 }
414}
415
416void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture)
417{
418 mBaseTexture = newTexture;
419 mDirtyMetaData = false;
420}
421
422
423Texture2D::Texture2D(Context *context) : Texture(context)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000424{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000425 mTexture = NULL;
426}
427
428Texture2D::~Texture2D()
429{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000430 if (mTexture)
431 {
432 mTexture->Release();
433 mTexture = NULL;
434 }
435}
436
437GLenum Texture2D::getTarget() const
438{
439 return GL_TEXTURE_2D;
440}
441
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000442// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
443// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
444// Call this when a particular level of the texture must be defined with a specific format, width and height.
445//
446// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
447// a new height and width for the texture by working backwards from the given width and height.
448bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
449{
450 bool widthOkay = (mWidth >> level == width);
451 bool heightOkay = (mHeight >> level == height);
452
453 bool sizeOkay = ((widthOkay && heightOkay)
454 || (widthOkay && mHeight >> level == 0 && height == 1)
455 || (heightOkay && mWidth >> level == 0 && width == 1));
456
457 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
458
459 if (!textureOkay)
460 {
461 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
462 mImageArray[0].format, mWidth, mHeight,
463 internalFormat, width, height);
464
465 // Purge all the levels and the texture.
466
467 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
468 {
469 if (mImageArray[i].surface != NULL)
470 {
471 mImageArray[i].dirty = false;
472
473 mImageArray[i].surface->Release();
474 mImageArray[i].surface = NULL;
475 }
476 }
477
478 if (mTexture != NULL)
479 {
480 mTexture->Release();
481 mTexture = NULL;
482 dropTexture();
483 }
484
485 mWidth = width << level;
486 mHeight = height << level;
487 mImageArray[0].format = internalFormat;
488 }
489
490 return !textureOkay;
491}
492
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000493void 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 +0000494{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000495 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000496
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000497 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000498}
499
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000500void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
501{
502 ASSERT(mImageArray[level].surface != NULL);
503
504 if (mTexture != NULL)
505 {
506 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000507 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000508
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000509 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000510
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000511 if (SUCCEEDED(result))
512 {
513 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000514
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000515 RECT sourceRect;
516 sourceRect.left = xoffset;
517 sourceRect.top = yoffset;
518 sourceRect.right = xoffset + width;
519 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000520
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000521 POINT destPoint;
522 destPoint.x = xoffset;
523 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000524
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000525 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
526 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000527
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000528 destLevel->Release();
529
530 img->dirty = false;
531 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000532 }
533}
534
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000535void 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 +0000536{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000537 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000538 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000539}
540
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000541void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
542{
543 if (redefineTexture(level, internalFormat, width, height))
544 {
545 convertToRenderTarget();
546 pushTexture(mTexture);
547 }
548
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000549 if (width != 0 && height != 0)
550 {
551 RECT sourceRect;
552 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000553 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000554 sourceRect.top = y;
555 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000556
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000557 IDirect3DSurface9 *dest;
558 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000559
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000560 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
561 dest->Release();
562 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000563
564 mImageArray[level].width = width;
565 mImageArray[level].height = height;
566 mImageArray[level].format = internalFormat;
567}
568
569void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
570{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000571 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
572 {
573 return error(GL_INVALID_VALUE);
574 }
575
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000576 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
577 {
578 convertToRenderTarget();
579 pushTexture(mTexture);
580 }
581 else
582 {
583 getRenderTarget(GL_TEXTURE_2D);
584 }
585
586 RECT sourceRect;
587 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000588 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000589 sourceRect.top = y;
590 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000591
592 IDirect3DSurface9 *dest;
593 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
594
595 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
596 dest->Release();
597}
598
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000599// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
600bool Texture2D::isComplete() const
601{
602 ASSERT(mWidth == mImageArray[0].width && mHeight == mImageArray[0].height);
603
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000604 if (mWidth <= 0 || mHeight <= 0)
605 {
606 return false;
607 }
608
609 bool mipmapping;
610
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000611 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000612 {
613 case GL_NEAREST:
614 case GL_LINEAR:
615 mipmapping = false;
616 break;
617 case GL_NEAREST_MIPMAP_NEAREST:
618 case GL_LINEAR_MIPMAP_NEAREST:
619 case GL_NEAREST_MIPMAP_LINEAR:
620 case GL_LINEAR_MIPMAP_LINEAR:
621 mipmapping = true;
622 break;
623 default: UNREACHABLE();
624 }
625
626 if (mipmapping)
627 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000628 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000629
630 for (int level = 1; level <= q; level++)
631 {
632 if (mImageArray[level].format != mImageArray[0].format)
633 {
634 return false;
635 }
636
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000637 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
638 {
639 return false;
640 }
641
642 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
643 {
644 return false;
645 }
646 }
647 }
648
649 return true;
650}
651
652// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000653IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000654{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000655 IDirect3DTexture9 *texture;
656
657 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000658 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000659
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000660 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000661
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000662 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000663 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000664 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000665 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000666 }
667
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000668 if (mTexture) mTexture->Release();
669 mTexture = texture;
670 return texture;
671}
672
673void Texture2D::updateTexture()
674{
675 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000676
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000677 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000678
679 for (int level = 0; level < levelCount; level++)
680 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000681 if (mImageArray[level].dirty)
682 {
683 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000684 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000685
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000686 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000687
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000688 if (SUCCEEDED(result))
689 {
690 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
691 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000692
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000693 levelSurface->Release();
694
695 mImageArray[level].dirty = false;
696 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000697 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000698 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000699}
700
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000701IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000702{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000703 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000704
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000705 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000706 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000707 IDirect3DDevice9 *device = getDevice();
708 D3DFORMAT format = selectFormat(mImageArray[0].format);
709
710 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
711
712 if (FAILED(result))
713 {
714 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
715 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
716 }
717
718 if (mTexture != NULL)
719 {
720 int levels = texture->GetLevelCount();
721 for (int i = 0; i < levels; i++)
722 {
723 IDirect3DSurface9 *source;
724 result = mTexture->GetSurfaceLevel(i, &source);
725
726 if (FAILED(result))
727 {
728 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
729
730 texture->Release();
731
732 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
733 }
734
735 IDirect3DSurface9 *dest;
736 result = texture->GetSurfaceLevel(i, &dest);
737
738 if (FAILED(result))
739 {
740 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
741
742 texture->Release();
743 source->Release();
744
745 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
746 }
747
748 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
749
750 if (FAILED(result))
751 {
752 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
753
754 texture->Release();
755 source->Release();
756 dest->Release();
757
758 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
759 }
760
761 source->Release();
762 dest->Release();
763 }
764 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000765 }
766
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000767 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000768 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000769 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000770 }
771
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000772 mTexture = texture;
773 return mTexture;
774}
775
776IDirect3DSurface9 *Texture2D::getSurface(GLenum target)
777{
778 ASSERT(target == GL_TEXTURE_2D);
779
780 IDirect3DSurface9 *surface = NULL;
781 HRESULT result = mTexture->GetSurfaceLevel(0, &surface);
782
783 ASSERT(SUCCEEDED(result));
784
785 return surface;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000786}
787
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000788bool Texture2D::dirtyImageData() const
789{
790 int q = log2(std::max(mWidth, mHeight));
791
792 for (int i = 0; i <= q; i++)
793 {
794 if (mImageArray[i].dirty) return true;
795 }
796
797 return false;
798}
799
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000800void Texture2D::generateMipmaps()
801{
802 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
803 {
804 return error(GL_INVALID_OPERATION);
805 }
806
807 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
808 unsigned int q = log2(std::max(mWidth, mHeight));
809 for (unsigned int i = 1; i <= q; i++)
810 {
811 if (mImageArray[i].surface != NULL)
812 {
813 mImageArray[i].surface->Release();
814 mImageArray[i].surface = NULL;
815 }
816
817 mImageArray[i].dirty = false;
818
819 mImageArray[i].format = mImageArray[0].format;
820 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
821 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
822 }
823
824 getRenderTarget();
825
826 for (unsigned int i = 1; i <= q; i++)
827 {
828 IDirect3DSurface9 *upper = NULL;
829 IDirect3DSurface9 *lower = NULL;
830
831 mTexture->GetSurfaceLevel(i-1, &upper);
832 mTexture->GetSurfaceLevel(i, &lower);
833
834 if (upper != NULL && lower != NULL)
835 {
836 getBlitter()->boxFilter(upper, lower);
837 }
838
839 if (upper != NULL) upper->Release();
840 if (lower != NULL) lower->Release();
841 }
842}
843
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000844TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000845{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000846 mTexture = NULL;
847}
848
849TextureCubeMap::~TextureCubeMap()
850{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000851 if (mTexture)
852 {
853 mTexture->Release();
854 mTexture = NULL;
855 }
856}
857
858GLenum TextureCubeMap::getTarget() const
859{
860 return GL_TEXTURE_CUBE_MAP;
861}
862
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000863void 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 +0000864{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000865 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000866}
867
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000868void 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 +0000869{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000870 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000871}
872
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000873void 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 +0000874{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000875 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000876}
877
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000878void 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 +0000879{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000880 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000881}
882
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000883void 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 +0000884{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000885 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000886}
887
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000888void 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 +0000889{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000890 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000891}
892
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000893void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
894{
895 int face = faceIndex(faceTarget);
896
897 ASSERT(mImageArray[face][level].surface != NULL);
898
899 if (mTexture != NULL)
900 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000901 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
902 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000903
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000904 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000905 {
906 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000907
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000908 RECT sourceRect;
909 sourceRect.left = xoffset;
910 sourceRect.top = yoffset;
911 sourceRect.right = xoffset + width;
912 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000913
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000914 POINT destPoint;
915 destPoint.x = xoffset;
916 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000917
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000918 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000919 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000920
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000921 destLevel->Release();
922
923 img->dirty = false;
924 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000925 }
926}
927
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000928void 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 +0000929{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000930 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000931 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000932}
933
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000934// 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 +0000935bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000936{
937 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
938 {
939 return false;
940 }
941
942 bool mipmapping;
943
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000944 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000945 {
946 case GL_NEAREST:
947 case GL_LINEAR:
948 mipmapping = false;
949 break;
950 case GL_NEAREST_MIPMAP_NEAREST:
951 case GL_LINEAR_MIPMAP_NEAREST:
952 case GL_NEAREST_MIPMAP_LINEAR:
953 case GL_LINEAR_MIPMAP_LINEAR:
954 mipmapping = true;
955 break;
956 default: UNREACHABLE();
957 }
958
959 for (int face = 0; face < 6; face++)
960 {
961 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
962 {
963 return false;
964 }
965 }
966
967 if (mipmapping)
968 {
969 int q = log2(mWidth);
970
971 for (int face = 0; face < 6; face++)
972 {
973 for (int level = 1; level <= q; level++)
974 {
975 if (mImageArray[face][level].format != mImageArray[0][0].format)
976 {
977 return false;
978 }
979
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000980 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
981 {
982 return false;
983 }
984
985 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
986 {
987 return false;
988 }
989 }
990 }
991 }
992
993 return true;
994}
995
996// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000997IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000998{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000999 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001000 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001001
1002 IDirect3DCubeTexture9 *texture;
1003
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001004 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001005
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001006 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001007 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001008 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001009 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001010 }
1011
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001012 if (mTexture) mTexture->Release();
1013
1014 mTexture = texture;
1015 return mTexture;
1016}
1017
1018void TextureCubeMap::updateTexture()
1019{
1020 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001021
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001022 for (int face = 0; face < 6; face++)
1023 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001024 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001025 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001026 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001027
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001028 if (img->dirty)
1029 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001030 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1031 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001032
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001033 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001034 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001035 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001036 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001037
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001038 levelSurface->Release();
1039
1040 img->dirty = false;
1041 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001042 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001043 }
1044 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001045}
1046
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001047IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1048{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001049 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001050
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001051 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001052 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001053 IDirect3DDevice9 *device = getDevice();
1054 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1055
1056 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1057
1058 if (FAILED(result))
1059 {
1060 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1061 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1062 }
1063
1064 if (mTexture != NULL)
1065 {
1066 int levels = texture->GetLevelCount();
1067 for (int f = 0; f < 6; f++)
1068 {
1069 for (int i = 0; i < levels; i++)
1070 {
1071 IDirect3DSurface9 *source;
1072 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1073
1074 if (FAILED(result))
1075 {
1076 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1077
1078 texture->Release();
1079
1080 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1081 }
1082
1083 IDirect3DSurface9 *dest;
1084 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1085
1086 if (FAILED(result))
1087 {
1088 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1089
1090 texture->Release();
1091 source->Release();
1092
1093 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1094 }
1095
1096 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1097
1098 if (FAILED(result))
1099 {
1100 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1101
1102 texture->Release();
1103 source->Release();
1104 dest->Release();
1105
1106 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1107 }
1108 }
1109 }
1110 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001111 }
1112
1113 if (mTexture != NULL)
1114 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001115 mTexture->Release();
1116 }
1117
1118 mTexture = texture;
1119 return mTexture;
1120}
1121
1122IDirect3DSurface9 *TextureCubeMap::getSurface(GLenum target)
1123{
1124 ASSERT(es2dx::IsCubemapTextureTarget(target));
1125
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001126 IDirect3DSurface9 *surface = getCubeMapSurface(target, 0);
1127 ASSERT(surface != NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001128 return surface;
1129}
1130
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001131void 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 +00001132{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001133 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001134
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001135 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001136}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001137
1138unsigned int TextureCubeMap::faceIndex(GLenum face)
1139{
1140 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1141 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1142 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1143 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1144 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1145
1146 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1147}
1148
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001149bool TextureCubeMap::dirtyImageData() const
1150{
1151 int q = log2(mWidth);
1152
1153 for (int f = 0; f < 6; f++)
1154 {
1155 for (int i = 0; i <= q; i++)
1156 {
1157 if (mImageArray[f][i].dirty) return true;
1158 }
1159 }
1160
1161 return false;
1162}
1163
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001164// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1165// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1166// Call this when a particular level of the texture must be defined with a specific format, width and height.
1167//
1168// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1169// a new size for the texture by working backwards from the given size.
1170bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1171{
1172 // Are these settings compatible with level 0?
1173 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1174
1175 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1176
1177 if (!textureOkay)
1178 {
1179 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1180 mImageArray[0][0].format, mImageArray[0][0].width,
1181 internalFormat, width);
1182
1183 // Purge all the levels and the texture.
1184 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1185 {
1186 for (int f = 0; f < 6; f++)
1187 {
1188 if (mImageArray[f][i].surface != NULL)
1189 {
1190 mImageArray[f][i].dirty = false;
1191
1192 mImageArray[f][i].surface->Release();
1193 mImageArray[f][i].surface = NULL;
1194 }
1195 }
1196 }
1197
1198 if (mTexture != NULL)
1199 {
1200 mTexture->Release();
1201 mTexture = NULL;
1202 dropTexture();
1203 }
1204
1205 mWidth = width << level;
1206 mImageArray[0][0].width = width << level;
1207 mHeight = width << level;
1208 mImageArray[0][0].height = width << level;
1209
1210 mImageArray[0][0].format = internalFormat;
1211 }
1212
1213 return !textureOkay;
1214}
1215
1216void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1217{
1218 unsigned int faceindex = faceIndex(face);
1219
1220 if (redefineTexture(level, internalFormat, width))
1221 {
1222 convertToRenderTarget();
1223 pushTexture(mTexture);
1224 }
1225
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001226 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001227
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001228 if (width > 0)
1229 {
1230 RECT sourceRect;
1231 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001232 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001233 sourceRect.top = y;
1234 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001235
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001236 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1237
1238 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1239 dest->Release();
1240 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001241
1242 mImageArray[faceindex][level].width = width;
1243 mImageArray[faceindex][level].height = height;
1244 mImageArray[faceindex][level].format = internalFormat;
1245}
1246
1247IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1248{
1249 unsigned int faceIndex;
1250
1251 if (faceIdentifier < 6)
1252 {
1253 faceIndex = faceIdentifier;
1254 }
1255 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1256 {
1257 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1258 }
1259 else
1260 {
1261 UNREACHABLE();
1262 faceIndex = 0;
1263 }
1264
1265 if (mTexture == NULL)
1266 {
1267 UNREACHABLE();
1268 return NULL;
1269 }
1270
1271 IDirect3DSurface9 *surface = NULL;
1272
1273 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1274
1275 return (SUCCEEDED(hr)) ? surface : NULL;
1276}
1277
1278void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1279{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001280 GLsizei size = mImageArray[faceIndex(face)][level].width;
1281
1282 if (xoffset + width > size || yoffset + height > size)
1283 {
1284 return error(GL_INVALID_VALUE);
1285 }
1286
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001287 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1288 {
1289 convertToRenderTarget();
1290 pushTexture(mTexture);
1291 }
1292 else
1293 {
1294 getRenderTarget(face);
1295 }
1296
1297 RECT sourceRect;
1298 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001299 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001300 sourceRect.top = y;
1301 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001302
1303 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1304
1305 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1306 dest->Release();
1307}
1308
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001309bool TextureCubeMap::isCubeComplete() const
1310{
1311 if (mImageArray[0][0].width == 0)
1312 {
1313 return false;
1314 }
1315
1316 for (unsigned int f = 1; f < 6; f++)
1317 {
1318 if (mImageArray[f][0].width != mImageArray[0][0].width
1319 || mImageArray[f][0].format != mImageArray[0][0].format)
1320 {
1321 return false;
1322 }
1323 }
1324
1325 return true;
1326}
1327
1328void TextureCubeMap::generateMipmaps()
1329{
1330 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1331 {
1332 return error(GL_INVALID_OPERATION);
1333 }
1334
1335 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1336 unsigned int q = log2(mImageArray[0][0].width);
1337 for (unsigned int f = 0; f < 6; f++)
1338 {
1339 for (unsigned int i = 1; i <= q; i++)
1340 {
1341 if (mImageArray[f][i].surface != NULL)
1342 {
1343 mImageArray[f][i].surface->Release();
1344 mImageArray[f][i].surface = NULL;
1345 }
1346
1347 mImageArray[f][i].dirty = false;
1348
1349 mImageArray[f][i].format = mImageArray[f][0].format;
1350 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1351 mImageArray[f][i].height = mImageArray[f][i].width;
1352 }
1353 }
1354
1355 getRenderTarget();
1356
1357 for (unsigned int f = 0; f < 6; f++)
1358 {
1359 for (unsigned int i = 1; i <= q; i++)
1360 {
1361 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1362 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1363
1364 if (upper != NULL && lower != NULL)
1365 {
1366 getBlitter()->boxFilter(upper, lower);
1367 }
1368
1369 if (upper != NULL) upper->Release();
1370 if (lower != NULL) lower->Release();
1371 }
1372 }
1373}
1374
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001375}