blob: 9ee9ddc459b903156239a9f0b34683f0e28e2a4e [file] [log] [blame]
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001//
2// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
11#include "Texture.h"
12
daniel@transgaming.com16973022010-03-11 19:22:19 +000013#include <algorithm>
14
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000015#include "main.h"
16#include "mathutil.h"
17#include "debug.h"
daniel@transgaming.com7051b972010-03-21 04:31:07 +000018#include "utilities.h"
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000019
20namespace gl
21{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000022
23Texture::Image::Image()
24 : dirty(false), surface(NULL)
25{
26}
27
28Texture::Image::~Image()
29{
30 if (surface) surface->Release();
31}
32
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000033Texture::Texture() : Colorbuffer(0)
34{
35 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
36 mMagFilter = GL_LINEAR;
37 mWrapS = GL_REPEAT;
38 mWrapT = GL_REPEAT;
daniel@transgaming.com29d27002010-03-11 19:41:22 +000039
daniel@transgaming.com00c75962010-03-11 20:36:15 +000040 mDirtyMetaData = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000041}
42
43Texture::~Texture()
44{
45}
46
47// Returns true on successful filter state update (valid enum parameter)
48bool Texture::setMinFilter(GLenum filter)
49{
50 switch (filter)
51 {
52 case GL_NEAREST:
53 case GL_LINEAR:
54 case GL_NEAREST_MIPMAP_NEAREST:
55 case GL_LINEAR_MIPMAP_NEAREST:
56 case GL_NEAREST_MIPMAP_LINEAR:
57 case GL_LINEAR_MIPMAP_LINEAR:
58 mMinFilter = filter;
59 return true;
60 default:
61 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000062 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000063}
64
65// Returns true on successful filter state update (valid enum parameter)
66bool Texture::setMagFilter(GLenum filter)
67{
68 switch (filter)
69 {
70 case GL_NEAREST:
71 case GL_LINEAR:
72 mMagFilter = filter;
73 return true;
74 default:
75 return false;
76 }
77}
78
79// Returns true on successful wrap state update (valid enum parameter)
80bool Texture::setWrapS(GLenum wrap)
81{
82 switch (wrap)
83 {
84 case GL_REPEAT:
85 case GL_CLAMP_TO_EDGE:
86 case GL_MIRRORED_REPEAT:
87 mWrapS = wrap;
88 return true;
89 default:
90 return false;
91 }
92}
93
94// Returns true on successful wrap state update (valid enum parameter)
95bool Texture::setWrapT(GLenum wrap)
96{
97 switch (wrap)
98 {
99 case GL_REPEAT:
100 case GL_CLAMP_TO_EDGE:
101 case GL_MIRRORED_REPEAT:
102 mWrapT = wrap;
103 return true;
104 default:
105 return false;
106 }
107}
108
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000109GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000110{
111 return mMinFilter;
112}
113
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000114GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000115{
116 return mMagFilter;
117}
118
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000119GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000120{
121 return mWrapS;
122}
123
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000124GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000125{
126 return mWrapT;
127}
128
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000129// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000130D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000131{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000132 return D3DFMT_A8R8G8B8;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000133}
134
135// Returns the size, in bytes, of a single texel in an Image
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000136int Texture::pixelSize(GLenum format, GLenum type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000137{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000138 switch (type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000139 {
140 case GL_UNSIGNED_BYTE:
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000141 switch (format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000142 {
143 case GL_ALPHA: return sizeof(unsigned char);
144 case GL_LUMINANCE: return sizeof(unsigned char);
145 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
146 case GL_RGB: return sizeof(unsigned char) * 3;
147 case GL_RGBA: return sizeof(unsigned char) * 4;
148 default: UNREACHABLE();
149 }
150 break;
151 case GL_UNSIGNED_SHORT_4_4_4_4:
152 case GL_UNSIGNED_SHORT_5_5_5_1:
153 case GL_UNSIGNED_SHORT_5_6_5:
154 return sizeof(unsigned short);
155 default: UNREACHABLE();
156 }
157
158 return 0;
159}
160
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000161int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000162{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000163 return img.width * 4;
164}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000165
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000166// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
167// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
168void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
169 const void *input, size_t outputPitch, void *output) const
170{
171 size_t inputPitch = width * pixelSize(format, type);
172
173 for (int y = 0; y < height; y++)
174 {
175 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
176 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
177 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
178
179 for (int x = 0; x < width; x++)
180 {
181 unsigned char r;
182 unsigned char g;
183 unsigned char b;
184 unsigned char a;
185
186 switch (format)
187 {
188 case GL_ALPHA:
189 a = source[x];
190 r = 0;
191 g = 0;
192 b = 0;
193 break;
194
195 case GL_LUMINANCE:
196 r = source[x];
197 g = source[x];
198 b = source[x];
199 a = 0xFF;
200 break;
201
202 case GL_LUMINANCE_ALPHA:
203 r = source[2*x+0];
204 g = source[2*x+0];
205 b = source[2*x+0];
206 a = source[2*x+1];
207 break;
208
209 case GL_RGB:
210 switch (type)
211 {
212 case GL_UNSIGNED_BYTE:
213 r = source[x * 3 + 0];
214 b = source[x * 3 + 1];
215 g = source[x * 3 + 2];
216 a = 0xFF;
217 break;
218
219 case GL_UNSIGNED_SHORT_5_6_5:
220 {
221 unsigned short rgba = source16[x];
222
223 a = 0xFF;
224 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
225 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
226 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
227 }
228 break;
229
230 default: UNREACHABLE();
231 }
232 break;
233
234 case GL_RGBA:
235 switch (type)
236 {
237 case GL_UNSIGNED_BYTE:
238 r = source[x * 4 + 0];
239 g = source[x * 4 + 1];
240 b = source[x * 4 + 2];
241 a = source[x * 4 + 3];
242 break;
243
244 case GL_UNSIGNED_SHORT_4_4_4_4:
245 {
246 unsigned short rgba = source16[x];
247
248 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
249 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
250 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
251 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
252 }
253 break;
254
255 case GL_UNSIGNED_SHORT_5_5_5_1:
256 {
257 unsigned short rgba = source16[x];
258
259 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000260 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000261 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000262 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000263 }
264 break;
265
266 default: UNREACHABLE();
267 }
268 break;
269 default: UNREACHABLE();
270 }
271
272 dest[4 * x + 0] = b;
273 dest[4 * x + 1] = g;
274 dest[4 * x + 2] = r;
275 dest[4 * x + 3] = a;
276 }
277 }
278}
279
280void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
281{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000282 IDirect3DSurface9 *newSurface = NULL;
283 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
284
285 if (FAILED(result))
286 {
287 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
288 return error(GL_OUT_OF_MEMORY);
289 }
290
291 if (img->surface) img->surface->Release();
292 img->surface = newSurface;
293
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000294 img->width = width;
295 img->height = height;
296 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000297
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000298 if (pixels != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000299 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000300 D3DLOCKED_RECT locked;
301 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
302
303 ASSERT(SUCCEEDED(result));
304
305 if (SUCCEEDED(result))
306 {
307 loadImageData(0, 0, width, height, format, type, pixels, locked.Pitch, locked.pBits);
308 newSurface->UnlockRect();
309 }
310
311 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000312 }
313
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000314 mDirtyMetaData = true;
315}
316
317void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels, Image *img)
318{
319 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
320
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000321 D3DLOCKED_RECT locked;
322 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000323
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000324 ASSERT(SUCCEEDED(result));
325
326 if (SUCCEEDED(result))
327 {
328 loadImageData(xoffset, yoffset, width, height, format, type, pixels, locked.Pitch, locked.pBits);
329 img->surface->UnlockRect();
330 }
331
332 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000333}
334
335IDirect3DBaseTexture9 *Texture::getTexture()
336{
337 if (!isComplete())
338 {
339 return NULL;
340 }
341
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000342 if (mDirtyMetaData)
343 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000344 mBaseTexture = createTexture();
345 }
346
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000347 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000348 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000349 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000350 }
351
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000352 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000353 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000354
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000355 return mBaseTexture;
356}
357
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000358// Returns the top-level texture surface as a render target
359IDirect3DSurface9 *Texture::getRenderTarget(GLenum target)
360{
361 if (mDirtyMetaData && mRenderTarget)
362 {
363 mRenderTarget->Release();
364 mRenderTarget = NULL;
365 }
366
367 if (!mRenderTarget)
368 {
369 mBaseTexture = convertToRenderTarget();
370 mRenderTarget = getSurface(target);
371 }
372
373 if (dirtyImageData())
374 {
375 updateTexture();
376 }
377
378 mDirtyMetaData = false;
379
380 return mRenderTarget;
381}
382
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000383Texture2D::Texture2D()
384{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000385 mTexture = NULL;
386}
387
388Texture2D::~Texture2D()
389{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000390 if (mTexture)
391 {
392 mTexture->Release();
393 mTexture = NULL;
394 }
395}
396
397GLenum Texture2D::getTarget() const
398{
399 return GL_TEXTURE_2D;
400}
401
402void Texture2D::setImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
403{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000404 Texture::setImage(width, height, format, type, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000405
406 if (level == 0)
407 {
408 mWidth = width;
409 mHeight = height;
410 }
411}
412
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000413void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
414{
415 ASSERT(mImageArray[level].surface != NULL);
416
417 if (mTexture != NULL)
418 {
419 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000420 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000421
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000422 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000423
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000424 if (SUCCEEDED(result))
425 {
426 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000427
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000428 RECT sourceRect;
429 sourceRect.left = xoffset;
430 sourceRect.top = yoffset;
431 sourceRect.right = xoffset + width;
432 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000433
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000434 POINT destPoint;
435 destPoint.x = xoffset;
436 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000437
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000438 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
439 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000440
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000441 destLevel->Release();
442
443 img->dirty = false;
444 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000445 }
446}
447
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000448void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000449{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000450 Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000451 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000452}
453
454// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
455bool Texture2D::isComplete() const
456{
457 ASSERT(mWidth == mImageArray[0].width && mHeight == mImageArray[0].height);
458
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000459 if (mWidth <= 0 || mHeight <= 0)
460 {
461 return false;
462 }
463
464 bool mipmapping;
465
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000466 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000467 {
468 case GL_NEAREST:
469 case GL_LINEAR:
470 mipmapping = false;
471 break;
472 case GL_NEAREST_MIPMAP_NEAREST:
473 case GL_LINEAR_MIPMAP_NEAREST:
474 case GL_NEAREST_MIPMAP_LINEAR:
475 case GL_LINEAR_MIPMAP_LINEAR:
476 mipmapping = true;
477 break;
478 default: UNREACHABLE();
479 }
480
481 if (mipmapping)
482 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000483 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000484
485 for (int level = 1; level <= q; level++)
486 {
487 if (mImageArray[level].format != mImageArray[0].format)
488 {
489 return false;
490 }
491
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000492 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
493 {
494 return false;
495 }
496
497 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
498 {
499 return false;
500 }
501 }
502 }
503
504 return true;
505}
506
507// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000508IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000509{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000510 IDirect3DTexture9 *texture;
511
512 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000513 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000514
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000515 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000516
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000517 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000518 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000519 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000520 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000521 }
522
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000523 if (mTexture) mTexture->Release();
524 mTexture = texture;
525 return texture;
526}
527
528void Texture2D::updateTexture()
529{
530 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000531
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000532 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000533
534 for (int level = 0; level < levelCount; level++)
535 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000536 if (mImageArray[level].dirty)
537 {
538 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000539 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000540
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000541 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000542
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000543 if (SUCCEEDED(result))
544 {
545 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
546 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000547
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000548 levelSurface->Release();
549
550 mImageArray[level].dirty = false;
551 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000552 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000553 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000554}
555
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000556IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000557{
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000558 IDirect3DTexture9 *texture;
559
560 IDirect3DDevice9 *device = getDevice();
561 D3DFORMAT format = selectFormat(mImageArray[0].format);
562
563 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
564
565 if (FAILED(result))
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000566 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000567 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
568 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000569 }
570
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000571 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000572 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000573 int levels = texture->GetLevelCount();
574 for (int i = 0; i < levels; i++)
575 {
576 IDirect3DSurface9 *source;
577 result = mTexture->GetSurfaceLevel(i, &source);
578
579 if (FAILED(result))
580 {
581 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
582
583 texture->Release();
584
585 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
586 }
587
588 IDirect3DSurface9 *dest;
589 result = texture->GetSurfaceLevel(i, &dest);
590
591 if (FAILED(result))
592 {
593 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
594
595 texture->Release();
596 source->Release();
597
598 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
599 }
600
601 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
602
603 if (FAILED(result))
604 {
605 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
606
607 texture->Release();
608 source->Release();
609 dest->Release();
610
611 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
612 }
613
614 source->Release();
615 dest->Release();
616 }
617
618 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000619 }
620
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000621 mTexture = texture;
622 return mTexture;
623}
624
625IDirect3DSurface9 *Texture2D::getSurface(GLenum target)
626{
627 ASSERT(target == GL_TEXTURE_2D);
628
629 IDirect3DSurface9 *surface = NULL;
630 HRESULT result = mTexture->GetSurfaceLevel(0, &surface);
631
632 ASSERT(SUCCEEDED(result));
633
634 return surface;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000635}
636
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000637bool Texture2D::dirtyImageData() const
638{
639 int q = log2(std::max(mWidth, mHeight));
640
641 for (int i = 0; i <= q; i++)
642 {
643 if (mImageArray[i].dirty) return true;
644 }
645
646 return false;
647}
648
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000649TextureCubeMap::TextureCubeMap()
650{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000651 mTexture = NULL;
652}
653
654TextureCubeMap::~TextureCubeMap()
655{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000656 if (mTexture)
657 {
658 mTexture->Release();
659 mTexture = NULL;
660 }
661}
662
663GLenum TextureCubeMap::getTarget() const
664{
665 return GL_TEXTURE_CUBE_MAP;
666}
667
668void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
669{
670 setImage(0, level, internalFormat, width, height, format, type, pixels);
671}
672
673void TextureCubeMap::setImageNegX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
674{
675 setImage(1, level, internalFormat, width, height, format, type, pixels);
676}
677
678void TextureCubeMap::setImagePosY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
679{
680 setImage(2, level, internalFormat, width, height, format, type, pixels);
681}
682
683void TextureCubeMap::setImageNegY(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
684{
685 setImage(3, level, internalFormat, width, height, format, type, pixels);
686}
687
688void TextureCubeMap::setImagePosZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
689{
690 setImage(4, level, internalFormat, width, height, format, type, pixels);
691}
692
693void TextureCubeMap::setImageNegZ(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
694{
695 setImage(5, level, internalFormat, width, height, format, type, pixels);
696}
697
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000698void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
699{
700 int face = faceIndex(faceTarget);
701
702 ASSERT(mImageArray[face][level].surface != NULL);
703
704 if (mTexture != NULL)
705 {
706 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000707 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000708
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000709 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000710
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000711 if (SUCCEEDED(result))
712 {
713 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000714
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000715 RECT sourceRect;
716 sourceRect.left = xoffset;
717 sourceRect.top = yoffset;
718 sourceRect.right = xoffset + width;
719 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000720
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000721 POINT destPoint;
722 destPoint.x = xoffset;
723 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000724
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000725 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
726 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000727
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000728 destLevel->Release();
729
730 img->dirty = false;
731 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000732 }
733}
734
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000735void TextureCubeMap::subImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
736{
737 Texture::subImage(xoffset, yoffset, width, height, format, type, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000738 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000739}
740
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000741// 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 +0000742bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000743{
744 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
745 {
746 return false;
747 }
748
749 bool mipmapping;
750
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000751 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000752 {
753 case GL_NEAREST:
754 case GL_LINEAR:
755 mipmapping = false;
756 break;
757 case GL_NEAREST_MIPMAP_NEAREST:
758 case GL_LINEAR_MIPMAP_NEAREST:
759 case GL_NEAREST_MIPMAP_LINEAR:
760 case GL_LINEAR_MIPMAP_LINEAR:
761 mipmapping = true;
762 break;
763 default: UNREACHABLE();
764 }
765
766 for (int face = 0; face < 6; face++)
767 {
768 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
769 {
770 return false;
771 }
772 }
773
774 if (mipmapping)
775 {
776 int q = log2(mWidth);
777
778 for (int face = 0; face < 6; face++)
779 {
780 for (int level = 1; level <= q; level++)
781 {
782 if (mImageArray[face][level].format != mImageArray[0][0].format)
783 {
784 return false;
785 }
786
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000787 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
788 {
789 return false;
790 }
791
792 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
793 {
794 return false;
795 }
796 }
797 }
798 }
799
800 return true;
801}
802
803// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000804IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000805{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000806 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000807 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000808
809 IDirect3DCubeTexture9 *texture;
810
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000811 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000812
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000813 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000814 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000815 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000816 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000817 }
818
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000819 if (mTexture) mTexture->Release();
820
821 mTexture = texture;
822 return mTexture;
823}
824
825void TextureCubeMap::updateTexture()
826{
827 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000828
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000829 for (int face = 0; face < 6; face++)
830 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000831 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000832 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000833 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000834
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000835 if (img->dirty)
836 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000837 IDirect3DSurface9 *levelSurface;
838 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &levelSurface);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000839
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000840 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000841
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000842 if (SUCCEEDED(result))
843 {
844 result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
845 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000846
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000847 levelSurface->Release();
848
849 img->dirty = false;
850 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000851 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000852 }
853 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000854}
855
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000856IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
857{
858 IDirect3DCubeTexture9 *texture;
859
860 IDirect3DDevice9 *device = getDevice();
861 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
862
863 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
864
865 if (FAILED(result))
866 {
867 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
868 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
869 }
870
871 if (mTexture != NULL)
872 {
873 int levels = texture->GetLevelCount();
874 for (int f = 0; f < 6; f++)
875 {
876 for (int i = 0; i < levels; i++)
877 {
878 IDirect3DSurface9 *source;
879 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
880
881 if (FAILED(result))
882 {
883 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
884
885 texture->Release();
886
887 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
888 }
889
890 IDirect3DSurface9 *dest;
891 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
892
893 if (FAILED(result))
894 {
895 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
896
897 texture->Release();
898 source->Release();
899
900 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
901 }
902
903 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
904
905 if (FAILED(result))
906 {
907 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
908
909 texture->Release();
910 source->Release();
911 dest->Release();
912
913 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
914 }
915 }
916 }
917
918 mTexture->Release();
919 }
920
921 mTexture = texture;
922 return mTexture;
923}
924
925IDirect3DSurface9 *TextureCubeMap::getSurface(GLenum target)
926{
927 ASSERT(es2dx::IsCubemapTextureTarget(target));
928
929 IDirect3DSurface9 *surface = NULL;
930 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &surface);
931 ASSERT(SUCCEEDED(result));
932 return surface;
933}
934
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000935void TextureCubeMap::setImage(int face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)
936{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000937 Texture::setImage(width, height, format, type, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000938
939 if (face == 0 && level == 0)
940 {
941 mWidth = width;
942 mHeight = height;
943 }
944}
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000945
946unsigned int TextureCubeMap::faceIndex(GLenum face)
947{
948 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
949 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
950 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
951 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
952 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
953
954 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
955}
956
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000957bool TextureCubeMap::dirtyImageData() const
958{
959 int q = log2(mWidth);
960
961 for (int f = 0; f < 6; f++)
962 {
963 for (int i = 0; i <= q; i++)
964 {
965 if (mImageArray[f][i].dirty) return true;
966 }
967 }
968
969 return false;
970}
971
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000972}