blob: af5b1d743dad81e9fe5d26a3054302997a70cb2e [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 {
daniel@transgaming.comfc23fe22010-05-05 18:48:17 +0000553 needRenderTarget();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000554 }
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.comae072af2010-05-05 18:47:28 +0000684 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000685 IDirect3DDevice9 *device = getDevice();
686 D3DFORMAT format = selectFormat(mImageArray[0].format);
687
688 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
689
690 if (FAILED(result))
691 {
692 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
693 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
694 }
695
696 if (mTexture != NULL)
697 {
698 int levels = texture->GetLevelCount();
699 for (int i = 0; i < levels; i++)
700 {
701 IDirect3DSurface9 *source;
702 result = mTexture->GetSurfaceLevel(i, &source);
703
704 if (FAILED(result))
705 {
706 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
707
708 texture->Release();
709
710 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
711 }
712
713 IDirect3DSurface9 *dest;
714 result = texture->GetSurfaceLevel(i, &dest);
715
716 if (FAILED(result))
717 {
718 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
719
720 texture->Release();
721 source->Release();
722
723 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
724 }
725
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000726 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000727 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
728
729 if (FAILED(result))
730 {
731 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
732
733 texture->Release();
734 source->Release();
735 dest->Release();
736
737 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
738 }
739
740 source->Release();
741 dest->Release();
742 }
743 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000744 }
745
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000746 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000747 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000748 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000749 }
750
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000751 mTexture = texture;
752 return mTexture;
753}
754
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000755bool Texture2D::dirtyImageData() const
756{
757 int q = log2(std::max(mWidth, mHeight));
758
759 for (int i = 0; i <= q; i++)
760 {
761 if (mImageArray[i].dirty) return true;
762 }
763
764 return false;
765}
766
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000767void Texture2D::generateMipmaps()
768{
769 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
770 {
771 return error(GL_INVALID_OPERATION);
772 }
773
774 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
775 unsigned int q = log2(std::max(mWidth, mHeight));
776 for (unsigned int i = 1; i <= q; i++)
777 {
778 if (mImageArray[i].surface != NULL)
779 {
780 mImageArray[i].surface->Release();
781 mImageArray[i].surface = NULL;
782 }
783
784 mImageArray[i].dirty = false;
785
786 mImageArray[i].format = mImageArray[0].format;
787 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
788 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
789 }
790
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000791 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000792
793 for (unsigned int i = 1; i <= q; i++)
794 {
795 IDirect3DSurface9 *upper = NULL;
796 IDirect3DSurface9 *lower = NULL;
797
798 mTexture->GetSurfaceLevel(i-1, &upper);
799 mTexture->GetSurfaceLevel(i, &lower);
800
801 if (upper != NULL && lower != NULL)
802 {
803 getBlitter()->boxFilter(upper, lower);
804 }
805
806 if (upper != NULL) upper->Release();
807 if (lower != NULL) lower->Release();
808 }
809}
810
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000811Colorbuffer *Texture2D::getColorbuffer(GLenum target)
812{
813 if (target != GL_TEXTURE_2D)
814 {
815 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
816 }
817
818 if (mColorbufferProxy == NULL)
819 {
820 mColorbufferProxy = new TextureColorbufferProxy(this, target);
821 }
822
823 return mColorbufferProxy;
824}
825
826IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
827{
828 ASSERT(target == GL_TEXTURE_2D);
829
830 needRenderTarget();
831
832 IDirect3DSurface9 *renderTarget = NULL;
833 mTexture->GetSurfaceLevel(0, &renderTarget);
834
835 return renderTarget;
836}
837
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000838TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000839{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000840 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000841
842 for (int i = 0; i < 6; i++)
843 {
844 mFaceProxies[i] = NULL;
845 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000846}
847
848TextureCubeMap::~TextureCubeMap()
849{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000850 for (int i = 0; i < 6; i++)
851 {
852 delete mFaceProxies[i];
853 }
854
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000855 if (mTexture)
856 {
857 mTexture->Release();
858 mTexture = NULL;
859 }
860}
861
862GLenum TextureCubeMap::getTarget() const
863{
864 return GL_TEXTURE_CUBE_MAP;
865}
866
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000867void 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 +0000868{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000869 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000870}
871
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000872void TextureCubeMap::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 +0000873{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000874 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000875}
876
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000877void TextureCubeMap::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 +0000878{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000879 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000880}
881
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000882void TextureCubeMap::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 +0000883{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000884 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000885}
886
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000887void TextureCubeMap::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 +0000888{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000889 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000890}
891
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000892void 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 +0000893{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000894 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000895}
896
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000897void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
898{
899 int face = faceIndex(faceTarget);
900
901 ASSERT(mImageArray[face][level].surface != NULL);
902
903 if (mTexture != NULL)
904 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000905 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
906 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000907
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000908 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000909 {
910 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000911
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000912 RECT sourceRect;
913 sourceRect.left = xoffset;
914 sourceRect.top = yoffset;
915 sourceRect.right = xoffset + width;
916 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000917
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000918 POINT destPoint;
919 destPoint.x = xoffset;
920 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000921
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000922 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000923 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000924
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000925 destLevel->Release();
926
927 img->dirty = false;
928 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000929 }
930}
931
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000932void 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 +0000933{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000934 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000935 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000936}
937
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000938// 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 +0000939bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000940{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000941 int size = mImageArray[0][0].width;
942
943 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000944 {
945 return false;
946 }
947
948 bool mipmapping;
949
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000950 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000951 {
952 case GL_NEAREST:
953 case GL_LINEAR:
954 mipmapping = false;
955 break;
956 case GL_NEAREST_MIPMAP_NEAREST:
957 case GL_LINEAR_MIPMAP_NEAREST:
958 case GL_NEAREST_MIPMAP_LINEAR:
959 case GL_LINEAR_MIPMAP_LINEAR:
960 mipmapping = true;
961 break;
962 default: UNREACHABLE();
963 }
964
965 for (int face = 0; face < 6; face++)
966 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000967 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000968 {
969 return false;
970 }
971 }
972
973 if (mipmapping)
974 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000975 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
976 {
977 return false;
978 }
979
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000980 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000981
982 for (int face = 0; face < 6; face++)
983 {
984 for (int level = 1; level <= q; level++)
985 {
986 if (mImageArray[face][level].format != mImageArray[0][0].format)
987 {
988 return false;
989 }
990
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000991 if (mImageArray[face][level].width != std::max(1, size >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000992 {
993 return false;
994 }
995
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000996 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000997 }
998 }
999 }
1000
1001 return true;
1002}
1003
1004// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001005IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001006{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001007 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001008 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001009
1010 IDirect3DCubeTexture9 *texture;
1011
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001012 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001013
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001014 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001015 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001016 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001017 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001018 }
1019
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001020 if (mTexture) mTexture->Release();
1021
1022 mTexture = texture;
1023 return mTexture;
1024}
1025
1026void TextureCubeMap::updateTexture()
1027{
1028 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001029
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001030 for (int face = 0; face < 6; face++)
1031 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001032 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001033 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001034 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001035
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001036 if (img->dirty)
1037 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001038 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1039 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001040
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001041 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001042 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001043 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001044 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001045
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001046 levelSurface->Release();
1047
1048 img->dirty = false;
1049 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001050 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001051 }
1052 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001053}
1054
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001055IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1056{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001057 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001058
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001059 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001060 {
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001061 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001062 IDirect3DDevice9 *device = getDevice();
1063 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1064
1065 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1066
1067 if (FAILED(result))
1068 {
1069 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1070 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1071 }
1072
1073 if (mTexture != NULL)
1074 {
1075 int levels = texture->GetLevelCount();
1076 for (int f = 0; f < 6; f++)
1077 {
1078 for (int i = 0; i < levels; i++)
1079 {
1080 IDirect3DSurface9 *source;
1081 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1082
1083 if (FAILED(result))
1084 {
1085 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1086
1087 texture->Release();
1088
1089 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1090 }
1091
1092 IDirect3DSurface9 *dest;
1093 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1094
1095 if (FAILED(result))
1096 {
1097 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1098
1099 texture->Release();
1100 source->Release();
1101
1102 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1103 }
1104
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001105 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001106 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1107
1108 if (FAILED(result))
1109 {
1110 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1111
1112 texture->Release();
1113 source->Release();
1114 dest->Release();
1115
1116 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1117 }
1118 }
1119 }
1120 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001121 }
1122
1123 if (mTexture != NULL)
1124 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001125 mTexture->Release();
1126 }
1127
1128 mTexture = texture;
1129 return mTexture;
1130}
1131
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001132void 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 +00001133{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001134 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001135
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001136 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001137}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001138
1139unsigned int TextureCubeMap::faceIndex(GLenum face)
1140{
1141 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1142 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1143 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1144 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1145 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1146
1147 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1148}
1149
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001150bool TextureCubeMap::dirtyImageData() const
1151{
1152 int q = log2(mWidth);
1153
1154 for (int f = 0; f < 6; f++)
1155 {
1156 for (int i = 0; i <= q; i++)
1157 {
1158 if (mImageArray[f][i].dirty) return true;
1159 }
1160 }
1161
1162 return false;
1163}
1164
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001165// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1166// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1167// Call this when a particular level of the texture must be defined with a specific format, width and height.
1168//
1169// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1170// a new size for the texture by working backwards from the given size.
1171bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1172{
1173 // Are these settings compatible with level 0?
1174 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1175
1176 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1177
1178 if (!textureOkay)
1179 {
1180 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1181 mImageArray[0][0].format, mImageArray[0][0].width,
1182 internalFormat, width);
1183
1184 // Purge all the levels and the texture.
1185 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1186 {
1187 for (int f = 0; f < 6; f++)
1188 {
1189 if (mImageArray[f][i].surface != NULL)
1190 {
1191 mImageArray[f][i].dirty = false;
1192
1193 mImageArray[f][i].surface->Release();
1194 mImageArray[f][i].surface = NULL;
1195 }
1196 }
1197 }
1198
1199 if (mTexture != NULL)
1200 {
1201 mTexture->Release();
1202 mTexture = NULL;
1203 dropTexture();
1204 }
1205
1206 mWidth = width << level;
1207 mImageArray[0][0].width = width << level;
1208 mHeight = width << level;
1209 mImageArray[0][0].height = width << level;
1210
1211 mImageArray[0][0].format = internalFormat;
1212 }
1213
1214 return !textureOkay;
1215}
1216
1217void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1218{
1219 unsigned int faceindex = faceIndex(face);
1220
1221 if (redefineTexture(level, internalFormat, width))
1222 {
1223 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001224 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001225 }
1226
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001227 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001228
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001229 if (width > 0)
1230 {
1231 RECT sourceRect;
1232 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001233 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001234 sourceRect.top = y;
1235 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001236
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001237 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1238
1239 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1240 dest->Release();
1241 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001242
1243 mImageArray[faceindex][level].width = width;
1244 mImageArray[faceindex][level].height = height;
1245 mImageArray[faceindex][level].format = internalFormat;
1246}
1247
1248IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1249{
1250 unsigned int faceIndex;
1251
1252 if (faceIdentifier < 6)
1253 {
1254 faceIndex = faceIdentifier;
1255 }
1256 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1257 {
1258 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1259 }
1260 else
1261 {
1262 UNREACHABLE();
1263 faceIndex = 0;
1264 }
1265
1266 if (mTexture == NULL)
1267 {
1268 UNREACHABLE();
1269 return NULL;
1270 }
1271
1272 IDirect3DSurface9 *surface = NULL;
1273
1274 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1275
1276 return (SUCCEEDED(hr)) ? surface : NULL;
1277}
1278
1279void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1280{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001281 GLsizei size = mImageArray[faceIndex(face)][level].width;
1282
1283 if (xoffset + width > size || yoffset + height > size)
1284 {
1285 return error(GL_INVALID_VALUE);
1286 }
1287
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001288 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1289 {
1290 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001291 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001292 }
1293 else
1294 {
1295 getRenderTarget(face);
1296 }
1297
1298 RECT sourceRect;
1299 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001300 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001301 sourceRect.top = y;
1302 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001303
1304 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1305
1306 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1307 dest->Release();
1308}
1309
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001310bool TextureCubeMap::isCubeComplete() const
1311{
1312 if (mImageArray[0][0].width == 0)
1313 {
1314 return false;
1315 }
1316
1317 for (unsigned int f = 1; f < 6; f++)
1318 {
1319 if (mImageArray[f][0].width != mImageArray[0][0].width
1320 || mImageArray[f][0].format != mImageArray[0][0].format)
1321 {
1322 return false;
1323 }
1324 }
1325
1326 return true;
1327}
1328
1329void TextureCubeMap::generateMipmaps()
1330{
1331 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1332 {
1333 return error(GL_INVALID_OPERATION);
1334 }
1335
1336 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1337 unsigned int q = log2(mImageArray[0][0].width);
1338 for (unsigned int f = 0; f < 6; f++)
1339 {
1340 for (unsigned int i = 1; i <= q; i++)
1341 {
1342 if (mImageArray[f][i].surface != NULL)
1343 {
1344 mImageArray[f][i].surface->Release();
1345 mImageArray[f][i].surface = NULL;
1346 }
1347
1348 mImageArray[f][i].dirty = false;
1349
1350 mImageArray[f][i].format = mImageArray[f][0].format;
1351 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1352 mImageArray[f][i].height = mImageArray[f][i].width;
1353 }
1354 }
1355
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001356 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001357
1358 for (unsigned int f = 0; f < 6; f++)
1359 {
1360 for (unsigned int i = 1; i <= q; i++)
1361 {
1362 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1363 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1364
1365 if (upper != NULL && lower != NULL)
1366 {
1367 getBlitter()->boxFilter(upper, lower);
1368 }
1369
1370 if (upper != NULL) upper->Release();
1371 if (lower != NULL) lower->Release();
1372 }
1373 }
1374}
1375
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001376Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
1377{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001378 if (!IsCubemapTextureTarget(target))
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001379 {
1380 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
1381 }
1382
1383 unsigned int face = faceIndex(target);
1384
1385 if (mFaceProxies[face] == NULL)
1386 {
1387 mFaceProxies[face] = new TextureColorbufferProxy(this, target);
1388 }
1389
1390 return mFaceProxies[face];
1391}
1392
1393IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1394{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001395 ASSERT(IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001396
1397 needRenderTarget();
1398
1399 IDirect3DSurface9 *renderTarget = NULL;
1400 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1401
1402 return renderTarget;
1403}
1404
1405Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1406 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1407{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001408 ASSERT(target == GL_TEXTURE_2D || IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001409 latchTextureInfo();
1410}
1411
1412IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1413{
1414 latchTextureInfo();
1415
1416 if (mRenderTarget) mRenderTarget->Release();
1417
1418 mRenderTarget = mTexture->getRenderTarget(mTarget);
1419
1420 return mRenderTarget;
1421}
1422
1423void Texture::TextureColorbufferProxy::latchTextureInfo()
1424{
1425 mWidth = mTexture->getWidth();
1426 mHeight = mTexture->getHeight();
1427}
1428
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001429}