blob: 1d6662d375d9241d5446c2998332a50c7516100a [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000011#include "libGLESv2/Texture.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000012
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
alokp@chromium.orgea0e1af2010-03-22 19:33:14 +000015#include "common/debug.h"
daniel@transgaming.combbf56f72010-04-20 18:52:13 +000016
17#include "libGLESv2/main.h"
18#include "libGLESv2/mathutil.h"
19#include "libGLESv2/utilities.h"
20#include "libGLESv2/Blit.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000021
22namespace gl
23{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000024
25Texture::Image::Image()
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000026 : width(0), height(0), dirty(false), surface(NULL)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000027{
28}
29
30Texture::Image::~Image()
31{
32 if (surface) surface->Release();
33}
34
daniel@transgaming.com93a81472010-04-20 18:52:58 +000035Texture::Texture(Context *context) : mContext(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000036{
37 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
38 mMagFilter = GL_LINEAR;
39 mWrapS = GL_REPEAT;
40 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000041
daniel@transgaming.com00c75962010-03-11 20:36:15 +000042 mDirtyMetaData = true;
daniel@transgaming.com93a81472010-04-20 18:52:58 +000043 mIsRenderable = false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000044}
45
46Texture::~Texture()
47{
48}
49
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000050Blit *Texture::getBlitter()
51{
52 return mContext->getBlitter();
53}
54
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000055// Returns true on successful filter state update (valid enum parameter)
56bool Texture::setMinFilter(GLenum filter)
57{
58 switch (filter)
59 {
60 case GL_NEAREST:
61 case GL_LINEAR:
62 case GL_NEAREST_MIPMAP_NEAREST:
63 case GL_LINEAR_MIPMAP_NEAREST:
64 case GL_NEAREST_MIPMAP_LINEAR:
65 case GL_LINEAR_MIPMAP_LINEAR:
66 mMinFilter = filter;
67 return true;
68 default:
69 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000070 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000071}
72
73// Returns true on successful filter state update (valid enum parameter)
74bool Texture::setMagFilter(GLenum filter)
75{
76 switch (filter)
77 {
78 case GL_NEAREST:
79 case GL_LINEAR:
80 mMagFilter = filter;
81 return true;
82 default:
83 return false;
84 }
85}
86
87// Returns true on successful wrap state update (valid enum parameter)
88bool Texture::setWrapS(GLenum wrap)
89{
90 switch (wrap)
91 {
92 case GL_REPEAT:
93 case GL_CLAMP_TO_EDGE:
94 case GL_MIRRORED_REPEAT:
95 mWrapS = wrap;
96 return true;
97 default:
98 return false;
99 }
100}
101
102// Returns true on successful wrap state update (valid enum parameter)
103bool Texture::setWrapT(GLenum wrap)
104{
105 switch (wrap)
106 {
107 case GL_REPEAT:
108 case GL_CLAMP_TO_EDGE:
109 case GL_MIRRORED_REPEAT:
110 mWrapT = wrap;
111 return true;
112 default:
113 return false;
114 }
115}
116
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000117GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000118{
119 return mMinFilter;
120}
121
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000122GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000123{
124 return mMagFilter;
125}
126
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000127GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000128{
129 return mWrapS;
130}
131
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000132GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000133{
134 return mWrapT;
135}
136
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000137GLuint Texture::getWidth() const
138{
139 return mWidth;
140}
141
142GLuint Texture::getHeight() const
143{
144 return mHeight;
145}
146
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000147// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000148D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000149{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000150 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000151}
152
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000153int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000154{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000155 return img.width * 4;
156}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000157
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000158// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
159// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
160void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000161 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000162{
daniel@transgaming.com713914b2010-05-04 03:35:17 +0000163 GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000164
165 for (int y = 0; y < height; y++)
166 {
167 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
168 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
169 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
170
171 for (int x = 0; x < width; x++)
172 {
173 unsigned char r;
174 unsigned char g;
175 unsigned char b;
176 unsigned char a;
177
178 switch (format)
179 {
180 case GL_ALPHA:
181 a = source[x];
182 r = 0;
183 g = 0;
184 b = 0;
185 break;
186
187 case GL_LUMINANCE:
188 r = source[x];
189 g = source[x];
190 b = source[x];
191 a = 0xFF;
192 break;
193
194 case GL_LUMINANCE_ALPHA:
195 r = source[2*x+0];
196 g = source[2*x+0];
197 b = source[2*x+0];
198 a = source[2*x+1];
199 break;
200
201 case GL_RGB:
202 switch (type)
203 {
204 case GL_UNSIGNED_BYTE:
205 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000206 g = source[x * 3 + 1];
207 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000208 a = 0xFF;
209 break;
210
211 case GL_UNSIGNED_SHORT_5_6_5:
212 {
213 unsigned short rgba = source16[x];
214
215 a = 0xFF;
216 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
217 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
218 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
219 }
220 break;
221
222 default: UNREACHABLE();
223 }
224 break;
225
226 case GL_RGBA:
227 switch (type)
228 {
229 case GL_UNSIGNED_BYTE:
230 r = source[x * 4 + 0];
231 g = source[x * 4 + 1];
232 b = source[x * 4 + 2];
233 a = source[x * 4 + 3];
234 break;
235
236 case GL_UNSIGNED_SHORT_4_4_4_4:
237 {
238 unsigned short rgba = source16[x];
239
240 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
241 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
242 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
243 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
244 }
245 break;
246
247 case GL_UNSIGNED_SHORT_5_5_5_1:
248 {
249 unsigned short rgba = source16[x];
250
251 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000252 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000253 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000254 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000255 }
256 break;
257
258 default: UNREACHABLE();
259 }
260 break;
261 default: UNREACHABLE();
262 }
263
264 dest[4 * x + 0] = b;
265 dest[4 * x + 1] = g;
266 dest[4 * x + 2] = r;
267 dest[4 * x + 3] = a;
268 }
269 }
270}
271
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000272void 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 +0000273{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000274 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000275
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000276 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000277 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000278 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
279
280 if (FAILED(result))
281 {
282 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
283 return error(GL_OUT_OF_MEMORY);
284 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000285 }
286
287 if (img->surface) img->surface->Release();
288 img->surface = newSurface;
289
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000290 img->width = width;
291 img->height = height;
292 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000293
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000294 if (pixels != NULL && newSurface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000295 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000296 D3DLOCKED_RECT locked;
297 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
298
299 ASSERT(SUCCEEDED(result));
300
301 if (SUCCEEDED(result))
302 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000303 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000304 newSurface->UnlockRect();
305 }
306
307 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000308 }
309
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000310 mDirtyMetaData = true;
311}
312
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000313void 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 +0000314{
315 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
316
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000317 D3DLOCKED_RECT locked;
318 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000319
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000320 ASSERT(SUCCEEDED(result));
321
322 if (SUCCEEDED(result))
323 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000324 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000325 img->surface->UnlockRect();
326 }
327
328 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000329}
330
331IDirect3DBaseTexture9 *Texture::getTexture()
332{
333 if (!isComplete())
334 {
335 return NULL;
336 }
337
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000338 if (mDirtyMetaData)
339 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000340 mBaseTexture = createTexture();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000341 mIsRenderable = false;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000342 }
343
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000344 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000345 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000346 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000347 }
348
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000349 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000350 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000351
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000352 return mBaseTexture;
353}
354
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000355// Returns the top-level texture surface as a render target
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000356void Texture::needRenderTarget()
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000357{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000358 if (!mIsRenderable)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000359 {
360 mBaseTexture = convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000361 mIsRenderable = true;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000362 }
363
364 if (dirtyImageData())
365 {
366 updateTexture();
367 }
368
369 mDirtyMetaData = false;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000370}
371
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000372void Texture::dropTexture()
373{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000374 if (mBaseTexture)
375 {
376 mBaseTexture = NULL;
377 }
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000378
379 mIsRenderable = false;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000380}
381
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000382void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000383{
384 mBaseTexture = newTexture;
385 mDirtyMetaData = false;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000386 mIsRenderable = renderable;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000387}
388
389
390Texture2D::Texture2D(Context *context) : Texture(context)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000391{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000392 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000393 mColorbufferProxy = NULL;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000394}
395
396Texture2D::~Texture2D()
397{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000398 delete mColorbufferProxy;
399
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000400 if (mTexture)
401 {
402 mTexture->Release();
403 mTexture = NULL;
404 }
405}
406
407GLenum Texture2D::getTarget() const
408{
409 return GL_TEXTURE_2D;
410}
411
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000412// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
413// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
414// Call this when a particular level of the texture must be defined with a specific format, width and height.
415//
416// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
417// a new height and width for the texture by working backwards from the given width and height.
418bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
419{
420 bool widthOkay = (mWidth >> level == width);
421 bool heightOkay = (mHeight >> level == height);
422
423 bool sizeOkay = ((widthOkay && heightOkay)
424 || (widthOkay && mHeight >> level == 0 && height == 1)
425 || (heightOkay && mWidth >> level == 0 && width == 1));
426
427 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
428
429 if (!textureOkay)
430 {
431 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
432 mImageArray[0].format, mWidth, mHeight,
433 internalFormat, width, height);
434
435 // Purge all the levels and the texture.
436
437 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
438 {
439 if (mImageArray[i].surface != NULL)
440 {
441 mImageArray[i].dirty = false;
442
443 mImageArray[i].surface->Release();
444 mImageArray[i].surface = NULL;
445 }
446 }
447
448 if (mTexture != NULL)
449 {
450 mTexture->Release();
451 mTexture = NULL;
452 dropTexture();
453 }
454
455 mWidth = width << level;
456 mHeight = height << level;
457 mImageArray[0].format = internalFormat;
458 }
459
460 return !textureOkay;
461}
462
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000463void 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 +0000464{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000465 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000466
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000467 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000468}
469
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000470void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
471{
472 ASSERT(mImageArray[level].surface != NULL);
473
474 if (mTexture != NULL)
475 {
476 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000477 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000478
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000479 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000480
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000481 if (SUCCEEDED(result))
482 {
483 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000484
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000485 RECT sourceRect;
486 sourceRect.left = xoffset;
487 sourceRect.top = yoffset;
488 sourceRect.right = xoffset + width;
489 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000490
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000491 POINT destPoint;
492 destPoint.x = xoffset;
493 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000494
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000495 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
496 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000497
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000498 destLevel->Release();
499
500 img->dirty = false;
501 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000502 }
503}
504
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000505void 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 +0000506{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000507 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000508 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000509}
510
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000511void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
512{
513 if (redefineTexture(level, internalFormat, width, height))
514 {
515 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000516 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000517 }
518
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000519 if (width != 0 && height != 0)
520 {
521 RECT sourceRect;
522 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000523 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000524 sourceRect.top = y;
525 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000526
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000527 IDirect3DSurface9 *dest;
528 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000529
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000530 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
531 dest->Release();
532 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000533
534 mImageArray[level].width = width;
535 mImageArray[level].height = height;
536 mImageArray[level].format = internalFormat;
537}
538
539void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
540{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000541 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
542 {
543 return error(GL_INVALID_VALUE);
544 }
545
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000546 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
547 {
548 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000549 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000550 }
551 else
552 {
553 getRenderTarget(GL_TEXTURE_2D);
554 }
555
556 RECT sourceRect;
557 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000558 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000559 sourceRect.top = y;
560 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000561
562 IDirect3DSurface9 *dest;
563 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
564
565 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
566 dest->Release();
567}
568
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000569// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
570bool Texture2D::isComplete() const
571{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000572 GLsizei width = mImageArray[0].width;
573 GLsizei height = mImageArray[0].height;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000574
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000575 if (width <= 0 || height <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000576 {
577 return false;
578 }
579
580 bool mipmapping;
581
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000582 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000583 {
584 case GL_NEAREST:
585 case GL_LINEAR:
586 mipmapping = false;
587 break;
588 case GL_NEAREST_MIPMAP_NEAREST:
589 case GL_LINEAR_MIPMAP_NEAREST:
590 case GL_NEAREST_MIPMAP_LINEAR:
591 case GL_LINEAR_MIPMAP_LINEAR:
592 mipmapping = true;
593 break;
594 default: UNREACHABLE();
595 }
596
597 if (mipmapping)
598 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000599 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
600 || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
601 {
602 return false;
603 }
604
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000605 int q = log2(std::max(width, height));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000606
607 for (int level = 1; level <= q; level++)
608 {
609 if (mImageArray[level].format != mImageArray[0].format)
610 {
611 return false;
612 }
613
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000614 if (mImageArray[level].width != std::max(1, width >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000615 {
616 return false;
617 }
618
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000619 if (mImageArray[level].height != std::max(1, height >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000620 {
621 return false;
622 }
623 }
624 }
625
626 return true;
627}
628
629// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000630IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000631{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000632 IDirect3DTexture9 *texture;
633
634 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000635 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000636
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000637 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000638
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000639 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000640 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000641 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000642 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000643 }
644
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000645 if (mTexture) mTexture->Release();
646 mTexture = texture;
647 return texture;
648}
649
650void Texture2D::updateTexture()
651{
652 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000653
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000654 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000655
656 for (int level = 0; level < levelCount; level++)
657 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000658 if (mImageArray[level].dirty)
659 {
660 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000661 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000662
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000663 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000664
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000665 if (SUCCEEDED(result))
666 {
667 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
668 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000669
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000670 levelSurface->Release();
671
672 mImageArray[level].dirty = false;
673 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000674 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000675 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000676}
677
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000678IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000679{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000680 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000681
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000682 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000683 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000684 IDirect3DDevice9 *device = getDevice();
685 D3DFORMAT format = selectFormat(mImageArray[0].format);
686
687 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
688
689 if (FAILED(result))
690 {
691 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
692 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
693 }
694
695 if (mTexture != NULL)
696 {
697 int levels = texture->GetLevelCount();
698 for (int i = 0; i < levels; i++)
699 {
700 IDirect3DSurface9 *source;
701 result = mTexture->GetSurfaceLevel(i, &source);
702
703 if (FAILED(result))
704 {
705 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
706
707 texture->Release();
708
709 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
710 }
711
712 IDirect3DSurface9 *dest;
713 result = texture->GetSurfaceLevel(i, &dest);
714
715 if (FAILED(result))
716 {
717 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
718
719 texture->Release();
720 source->Release();
721
722 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
723 }
724
725 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
726
727 if (FAILED(result))
728 {
729 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
730
731 texture->Release();
732 source->Release();
733 dest->Release();
734
735 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
736 }
737
738 source->Release();
739 dest->Release();
740 }
741 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000742 }
743
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000744 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000745 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000746 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000747 }
748
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000749 mTexture = texture;
750 return mTexture;
751}
752
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000753bool Texture2D::dirtyImageData() const
754{
755 int q = log2(std::max(mWidth, mHeight));
756
757 for (int i = 0; i <= q; i++)
758 {
759 if (mImageArray[i].dirty) return true;
760 }
761
762 return false;
763}
764
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000765void Texture2D::generateMipmaps()
766{
767 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
768 {
769 return error(GL_INVALID_OPERATION);
770 }
771
772 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
773 unsigned int q = log2(std::max(mWidth, mHeight));
774 for (unsigned int i = 1; i <= q; i++)
775 {
776 if (mImageArray[i].surface != NULL)
777 {
778 mImageArray[i].surface->Release();
779 mImageArray[i].surface = NULL;
780 }
781
782 mImageArray[i].dirty = false;
783
784 mImageArray[i].format = mImageArray[0].format;
785 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
786 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
787 }
788
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000789 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000790
791 for (unsigned int i = 1; i <= q; i++)
792 {
793 IDirect3DSurface9 *upper = NULL;
794 IDirect3DSurface9 *lower = NULL;
795
796 mTexture->GetSurfaceLevel(i-1, &upper);
797 mTexture->GetSurfaceLevel(i, &lower);
798
799 if (upper != NULL && lower != NULL)
800 {
801 getBlitter()->boxFilter(upper, lower);
802 }
803
804 if (upper != NULL) upper->Release();
805 if (lower != NULL) lower->Release();
806 }
807}
808
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000809Colorbuffer *Texture2D::getColorbuffer(GLenum target)
810{
811 if (target != GL_TEXTURE_2D)
812 {
813 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
814 }
815
816 if (mColorbufferProxy == NULL)
817 {
818 mColorbufferProxy = new TextureColorbufferProxy(this, target);
819 }
820
821 return mColorbufferProxy;
822}
823
824IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
825{
826 ASSERT(target == GL_TEXTURE_2D);
827
828 needRenderTarget();
829
830 IDirect3DSurface9 *renderTarget = NULL;
831 mTexture->GetSurfaceLevel(0, &renderTarget);
832
833 return renderTarget;
834}
835
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000836TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000837{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000838 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000839
840 for (int i = 0; i < 6; i++)
841 {
842 mFaceProxies[i] = NULL;
843 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000844}
845
846TextureCubeMap::~TextureCubeMap()
847{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000848 for (int i = 0; i < 6; i++)
849 {
850 delete mFaceProxies[i];
851 }
852
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000853 if (mTexture)
854 {
855 mTexture->Release();
856 mTexture = NULL;
857 }
858}
859
860GLenum TextureCubeMap::getTarget() const
861{
862 return GL_TEXTURE_CUBE_MAP;
863}
864
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000865void 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 +0000866{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000867 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000868}
869
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000870void 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 +0000871{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000872 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000873}
874
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000875void 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 +0000876{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000877 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000878}
879
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000880void 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 +0000881{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000882 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000883}
884
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000885void 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 +0000886{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000887 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000888}
889
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000890void 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 +0000891{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000892 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000893}
894
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000895void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
896{
897 int face = faceIndex(faceTarget);
898
899 ASSERT(mImageArray[face][level].surface != NULL);
900
901 if (mTexture != NULL)
902 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000903 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
904 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000905
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000906 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000907 {
908 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000909
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000910 RECT sourceRect;
911 sourceRect.left = xoffset;
912 sourceRect.top = yoffset;
913 sourceRect.right = xoffset + width;
914 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000915
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000916 POINT destPoint;
917 destPoint.x = xoffset;
918 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000919
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000920 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000921 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000922
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000923 destLevel->Release();
924
925 img->dirty = false;
926 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000927 }
928}
929
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000930void 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 +0000931{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000932 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000933 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000934}
935
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000936// 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 +0000937bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000938{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000939 int size = mImageArray[0][0].width;
940
941 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000942 {
943 return false;
944 }
945
946 bool mipmapping;
947
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000948 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000949 {
950 case GL_NEAREST:
951 case GL_LINEAR:
952 mipmapping = false;
953 break;
954 case GL_NEAREST_MIPMAP_NEAREST:
955 case GL_LINEAR_MIPMAP_NEAREST:
956 case GL_NEAREST_MIPMAP_LINEAR:
957 case GL_LINEAR_MIPMAP_LINEAR:
958 mipmapping = true;
959 break;
960 default: UNREACHABLE();
961 }
962
963 for (int face = 0; face < 6; face++)
964 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000965 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000966 {
967 return false;
968 }
969 }
970
971 if (mipmapping)
972 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000973 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
974 {
975 return false;
976 }
977
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000978 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000979
980 for (int face = 0; face < 6; face++)
981 {
982 for (int level = 1; level <= q; level++)
983 {
984 if (mImageArray[face][level].format != mImageArray[0][0].format)
985 {
986 return false;
987 }
988
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000989 if (mImageArray[face][level].width != std::max(1, size >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000990 {
991 return false;
992 }
993
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000994 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000995 }
996 }
997 }
998
999 return true;
1000}
1001
1002// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001003IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001004{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001005 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001006 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001007
1008 IDirect3DCubeTexture9 *texture;
1009
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001010 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001011
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001012 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001013 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001014 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001015 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001016 }
1017
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001018 if (mTexture) mTexture->Release();
1019
1020 mTexture = texture;
1021 return mTexture;
1022}
1023
1024void TextureCubeMap::updateTexture()
1025{
1026 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001027
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001028 for (int face = 0; face < 6; face++)
1029 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001030 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001031 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001032 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001033
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001034 if (img->dirty)
1035 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001036 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1037 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001038
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001039 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001040 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001041 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001042 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001043
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001044 levelSurface->Release();
1045
1046 img->dirty = false;
1047 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001048 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001049 }
1050 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001051}
1052
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001053IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1054{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001055 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001056
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001057 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001058 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001059 IDirect3DDevice9 *device = getDevice();
1060 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1061
1062 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1063
1064 if (FAILED(result))
1065 {
1066 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1067 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1068 }
1069
1070 if (mTexture != NULL)
1071 {
1072 int levels = texture->GetLevelCount();
1073 for (int f = 0; f < 6; f++)
1074 {
1075 for (int i = 0; i < levels; i++)
1076 {
1077 IDirect3DSurface9 *source;
1078 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1079
1080 if (FAILED(result))
1081 {
1082 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1083
1084 texture->Release();
1085
1086 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1087 }
1088
1089 IDirect3DSurface9 *dest;
1090 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1091
1092 if (FAILED(result))
1093 {
1094 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1095
1096 texture->Release();
1097 source->Release();
1098
1099 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1100 }
1101
1102 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1103
1104 if (FAILED(result))
1105 {
1106 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1107
1108 texture->Release();
1109 source->Release();
1110 dest->Release();
1111
1112 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1113 }
1114 }
1115 }
1116 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001117 }
1118
1119 if (mTexture != NULL)
1120 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001121 mTexture->Release();
1122 }
1123
1124 mTexture = texture;
1125 return mTexture;
1126}
1127
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001128void 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 +00001129{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001130 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001131
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001132 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001133}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001134
1135unsigned int TextureCubeMap::faceIndex(GLenum face)
1136{
1137 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1138 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1139 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1140 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1141 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1142
1143 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1144}
1145
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001146bool TextureCubeMap::dirtyImageData() const
1147{
1148 int q = log2(mWidth);
1149
1150 for (int f = 0; f < 6; f++)
1151 {
1152 for (int i = 0; i <= q; i++)
1153 {
1154 if (mImageArray[f][i].dirty) return true;
1155 }
1156 }
1157
1158 return false;
1159}
1160
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001161// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1162// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1163// Call this when a particular level of the texture must be defined with a specific format, width and height.
1164//
1165// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1166// a new size for the texture by working backwards from the given size.
1167bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1168{
1169 // Are these settings compatible with level 0?
1170 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1171
1172 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1173
1174 if (!textureOkay)
1175 {
1176 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1177 mImageArray[0][0].format, mImageArray[0][0].width,
1178 internalFormat, width);
1179
1180 // Purge all the levels and the texture.
1181 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1182 {
1183 for (int f = 0; f < 6; f++)
1184 {
1185 if (mImageArray[f][i].surface != NULL)
1186 {
1187 mImageArray[f][i].dirty = false;
1188
1189 mImageArray[f][i].surface->Release();
1190 mImageArray[f][i].surface = NULL;
1191 }
1192 }
1193 }
1194
1195 if (mTexture != NULL)
1196 {
1197 mTexture->Release();
1198 mTexture = NULL;
1199 dropTexture();
1200 }
1201
1202 mWidth = width << level;
1203 mImageArray[0][0].width = width << level;
1204 mHeight = width << level;
1205 mImageArray[0][0].height = width << level;
1206
1207 mImageArray[0][0].format = internalFormat;
1208 }
1209
1210 return !textureOkay;
1211}
1212
1213void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1214{
1215 unsigned int faceindex = faceIndex(face);
1216
1217 if (redefineTexture(level, internalFormat, width))
1218 {
1219 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001220 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001221 }
1222
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001223 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001224
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001225 if (width > 0)
1226 {
1227 RECT sourceRect;
1228 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001229 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001230 sourceRect.top = y;
1231 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001232
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001233 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1234
1235 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1236 dest->Release();
1237 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001238
1239 mImageArray[faceindex][level].width = width;
1240 mImageArray[faceindex][level].height = height;
1241 mImageArray[faceindex][level].format = internalFormat;
1242}
1243
1244IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1245{
1246 unsigned int faceIndex;
1247
1248 if (faceIdentifier < 6)
1249 {
1250 faceIndex = faceIdentifier;
1251 }
1252 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1253 {
1254 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1255 }
1256 else
1257 {
1258 UNREACHABLE();
1259 faceIndex = 0;
1260 }
1261
1262 if (mTexture == NULL)
1263 {
1264 UNREACHABLE();
1265 return NULL;
1266 }
1267
1268 IDirect3DSurface9 *surface = NULL;
1269
1270 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1271
1272 return (SUCCEEDED(hr)) ? surface : NULL;
1273}
1274
1275void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1276{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001277 GLsizei size = mImageArray[faceIndex(face)][level].width;
1278
1279 if (xoffset + width > size || yoffset + height > size)
1280 {
1281 return error(GL_INVALID_VALUE);
1282 }
1283
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001284 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1285 {
1286 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001287 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001288 }
1289 else
1290 {
1291 getRenderTarget(face);
1292 }
1293
1294 RECT sourceRect;
1295 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001296 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001297 sourceRect.top = y;
1298 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001299
1300 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1301
1302 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1303 dest->Release();
1304}
1305
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001306bool TextureCubeMap::isCubeComplete() const
1307{
1308 if (mImageArray[0][0].width == 0)
1309 {
1310 return false;
1311 }
1312
1313 for (unsigned int f = 1; f < 6; f++)
1314 {
1315 if (mImageArray[f][0].width != mImageArray[0][0].width
1316 || mImageArray[f][0].format != mImageArray[0][0].format)
1317 {
1318 return false;
1319 }
1320 }
1321
1322 return true;
1323}
1324
1325void TextureCubeMap::generateMipmaps()
1326{
1327 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1328 {
1329 return error(GL_INVALID_OPERATION);
1330 }
1331
1332 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1333 unsigned int q = log2(mImageArray[0][0].width);
1334 for (unsigned int f = 0; f < 6; f++)
1335 {
1336 for (unsigned int i = 1; i <= q; i++)
1337 {
1338 if (mImageArray[f][i].surface != NULL)
1339 {
1340 mImageArray[f][i].surface->Release();
1341 mImageArray[f][i].surface = NULL;
1342 }
1343
1344 mImageArray[f][i].dirty = false;
1345
1346 mImageArray[f][i].format = mImageArray[f][0].format;
1347 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1348 mImageArray[f][i].height = mImageArray[f][i].width;
1349 }
1350 }
1351
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001352 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001353
1354 for (unsigned int f = 0; f < 6; f++)
1355 {
1356 for (unsigned int i = 1; i <= q; i++)
1357 {
1358 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1359 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1360
1361 if (upper != NULL && lower != NULL)
1362 {
1363 getBlitter()->boxFilter(upper, lower);
1364 }
1365
1366 if (upper != NULL) upper->Release();
1367 if (lower != NULL) lower->Release();
1368 }
1369 }
1370}
1371
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001372Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
1373{
1374 if (!es2dx::IsCubemapTextureTarget(target))
1375 {
1376 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
1377 }
1378
1379 unsigned int face = faceIndex(target);
1380
1381 if (mFaceProxies[face] == NULL)
1382 {
1383 mFaceProxies[face] = new TextureColorbufferProxy(this, target);
1384 }
1385
1386 return mFaceProxies[face];
1387}
1388
1389IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1390{
1391 ASSERT(es2dx::IsCubemapTextureTarget(target));
1392
1393 needRenderTarget();
1394
1395 IDirect3DSurface9 *renderTarget = NULL;
1396 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1397
1398 return renderTarget;
1399}
1400
1401Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1402 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1403{
1404 ASSERT(target == GL_TEXTURE_2D || es2dx::IsCubemapTextureTarget(target));
1405 latchTextureInfo();
1406}
1407
1408IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1409{
1410 latchTextureInfo();
1411
1412 if (mRenderTarget) mRenderTarget->Release();
1413
1414 mRenderTarget = mTexture->getRenderTarget(mTarget);
1415
1416 return mRenderTarget;
1417}
1418
1419void Texture::TextureColorbufferProxy::latchTextureInfo()
1420{
1421 mWidth = mTexture->getWidth();
1422 mHeight = mTexture->getHeight();
1423}
1424
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001425}