blob: 101207970c73f752b1a395d65283fa9b5f9bedad [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.com3489e3a2010-03-21 04:31:11 +0000166GLsizei Texture::computePitch(GLsizei width, GLenum format, GLenum type, GLint alignment) const
167{
168 ASSERT(alignment > 0 && isPow2(alignment));
169
170 GLsizei rawPitch = pixelSize(format, type) * width;
171 return (rawPitch + alignment - 1) & ~(alignment - 1);
172}
173
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000174// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
175// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
176void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000177 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000178{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000179 GLsizei inputPitch = computePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000180
181 for (int y = 0; y < height; y++)
182 {
183 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
184 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
185 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
186
187 for (int x = 0; x < width; x++)
188 {
189 unsigned char r;
190 unsigned char g;
191 unsigned char b;
192 unsigned char a;
193
194 switch (format)
195 {
196 case GL_ALPHA:
197 a = source[x];
198 r = 0;
199 g = 0;
200 b = 0;
201 break;
202
203 case GL_LUMINANCE:
204 r = source[x];
205 g = source[x];
206 b = source[x];
207 a = 0xFF;
208 break;
209
210 case GL_LUMINANCE_ALPHA:
211 r = source[2*x+0];
212 g = source[2*x+0];
213 b = source[2*x+0];
214 a = source[2*x+1];
215 break;
216
217 case GL_RGB:
218 switch (type)
219 {
220 case GL_UNSIGNED_BYTE:
221 r = source[x * 3 + 0];
222 b = source[x * 3 + 1];
223 g = source[x * 3 + 2];
224 a = 0xFF;
225 break;
226
227 case GL_UNSIGNED_SHORT_5_6_5:
228 {
229 unsigned short rgba = source16[x];
230
231 a = 0xFF;
232 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
233 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
234 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
235 }
236 break;
237
238 default: UNREACHABLE();
239 }
240 break;
241
242 case GL_RGBA:
243 switch (type)
244 {
245 case GL_UNSIGNED_BYTE:
246 r = source[x * 4 + 0];
247 g = source[x * 4 + 1];
248 b = source[x * 4 + 2];
249 a = source[x * 4 + 3];
250 break;
251
252 case GL_UNSIGNED_SHORT_4_4_4_4:
253 {
254 unsigned short rgba = source16[x];
255
256 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
257 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
258 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
259 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
260 }
261 break;
262
263 case GL_UNSIGNED_SHORT_5_5_5_1:
264 {
265 unsigned short rgba = source16[x];
266
267 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000268 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000269 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000270 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000271 }
272 break;
273
274 default: UNREACHABLE();
275 }
276 break;
277 default: UNREACHABLE();
278 }
279
280 dest[4 * x + 0] = b;
281 dest[4 * x + 1] = g;
282 dest[4 * x + 2] = r;
283 dest[4 * x + 3] = a;
284 }
285 }
286}
287
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000288void 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 +0000289{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000290 IDirect3DSurface9 *newSurface = NULL;
291 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
292
293 if (FAILED(result))
294 {
295 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
296 return error(GL_OUT_OF_MEMORY);
297 }
298
299 if (img->surface) img->surface->Release();
300 img->surface = newSurface;
301
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000302 img->width = width;
303 img->height = height;
304 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000305
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000306 if (pixels != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000307 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000308 D3DLOCKED_RECT locked;
309 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
310
311 ASSERT(SUCCEEDED(result));
312
313 if (SUCCEEDED(result))
314 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000315 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000316 newSurface->UnlockRect();
317 }
318
319 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000320 }
321
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000322 mDirtyMetaData = true;
323}
324
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000325void 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 +0000326{
327 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
328
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000329 D3DLOCKED_RECT locked;
330 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000331
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000332 ASSERT(SUCCEEDED(result));
333
334 if (SUCCEEDED(result))
335 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000336 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000337 img->surface->UnlockRect();
338 }
339
340 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000341}
342
343IDirect3DBaseTexture9 *Texture::getTexture()
344{
345 if (!isComplete())
346 {
347 return NULL;
348 }
349
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000350 if (mDirtyMetaData)
351 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000352 mBaseTexture = createTexture();
353 }
354
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000355 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000356 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000357 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000358 }
359
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000360 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000361 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000362
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000363 return mBaseTexture;
364}
365
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000366// Returns the top-level texture surface as a render target
367IDirect3DSurface9 *Texture::getRenderTarget(GLenum target)
368{
369 if (mDirtyMetaData && mRenderTarget)
370 {
371 mRenderTarget->Release();
372 mRenderTarget = NULL;
373 }
374
375 if (!mRenderTarget)
376 {
377 mBaseTexture = convertToRenderTarget();
378 mRenderTarget = getSurface(target);
379 }
380
381 if (dirtyImageData())
382 {
383 updateTexture();
384 }
385
386 mDirtyMetaData = false;
387
388 return mRenderTarget;
389}
390
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000391Texture2D::Texture2D()
392{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000393 mTexture = NULL;
394}
395
396Texture2D::~Texture2D()
397{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000398 if (mTexture)
399 {
400 mTexture->Release();
401 mTexture = NULL;
402 }
403}
404
405GLenum Texture2D::getTarget() const
406{
407 return GL_TEXTURE_2D;
408}
409
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000410void 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 +0000411{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000412 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000413
414 if (level == 0)
415 {
416 mWidth = width;
417 mHeight = height;
418 }
419}
420
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000421void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
422{
423 ASSERT(mImageArray[level].surface != NULL);
424
425 if (mTexture != NULL)
426 {
427 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000428 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000429
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000430 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000431
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000432 if (SUCCEEDED(result))
433 {
434 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000435
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000436 RECT sourceRect;
437 sourceRect.left = xoffset;
438 sourceRect.top = yoffset;
439 sourceRect.right = xoffset + width;
440 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000441
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000442 POINT destPoint;
443 destPoint.x = xoffset;
444 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000445
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000446 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
447 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000448
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000449 destLevel->Release();
450
451 img->dirty = false;
452 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000453 }
454}
455
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000456void 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 +0000457{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000458 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000459 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000460}
461
462// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
463bool Texture2D::isComplete() const
464{
465 ASSERT(mWidth == mImageArray[0].width && mHeight == mImageArray[0].height);
466
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000467 if (mWidth <= 0 || mHeight <= 0)
468 {
469 return false;
470 }
471
472 bool mipmapping;
473
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000474 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000475 {
476 case GL_NEAREST:
477 case GL_LINEAR:
478 mipmapping = false;
479 break;
480 case GL_NEAREST_MIPMAP_NEAREST:
481 case GL_LINEAR_MIPMAP_NEAREST:
482 case GL_NEAREST_MIPMAP_LINEAR:
483 case GL_LINEAR_MIPMAP_LINEAR:
484 mipmapping = true;
485 break;
486 default: UNREACHABLE();
487 }
488
489 if (mipmapping)
490 {
daniel@transgaming.com16973022010-03-11 19:22:19 +0000491 int q = log2(std::max(mWidth, mHeight));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000492
493 for (int level = 1; level <= q; level++)
494 {
495 if (mImageArray[level].format != mImageArray[0].format)
496 {
497 return false;
498 }
499
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000500 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
501 {
502 return false;
503 }
504
505 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
506 {
507 return false;
508 }
509 }
510 }
511
512 return true;
513}
514
515// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000516IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000517{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000518 IDirect3DTexture9 *texture;
519
520 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000521 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000522
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000523 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000524
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000525 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000526 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000527 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000528 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000529 }
530
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000531 if (mTexture) mTexture->Release();
532 mTexture = texture;
533 return texture;
534}
535
536void Texture2D::updateTexture()
537{
538 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000539
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000540 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000541
542 for (int level = 0; level < levelCount; level++)
543 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000544 if (mImageArray[level].dirty)
545 {
546 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000547 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000548
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000549 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000550
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000551 if (SUCCEEDED(result))
552 {
553 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
554 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000555
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000556 levelSurface->Release();
557
558 mImageArray[level].dirty = false;
559 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000560 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000561 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000562}
563
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000564IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000565{
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000566 IDirect3DTexture9 *texture;
567
568 IDirect3DDevice9 *device = getDevice();
569 D3DFORMAT format = selectFormat(mImageArray[0].format);
570
571 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
572
573 if (FAILED(result))
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000574 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000575 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
576 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000577 }
578
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000579 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000580 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000581 int levels = texture->GetLevelCount();
582 for (int i = 0; i < levels; i++)
583 {
584 IDirect3DSurface9 *source;
585 result = mTexture->GetSurfaceLevel(i, &source);
586
587 if (FAILED(result))
588 {
589 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
590
591 texture->Release();
592
593 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
594 }
595
596 IDirect3DSurface9 *dest;
597 result = texture->GetSurfaceLevel(i, &dest);
598
599 if (FAILED(result))
600 {
601 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
602
603 texture->Release();
604 source->Release();
605
606 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
607 }
608
609 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
610
611 if (FAILED(result))
612 {
613 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
614
615 texture->Release();
616 source->Release();
617 dest->Release();
618
619 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
620 }
621
622 source->Release();
623 dest->Release();
624 }
625
626 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000627 }
628
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000629 mTexture = texture;
630 return mTexture;
631}
632
633IDirect3DSurface9 *Texture2D::getSurface(GLenum target)
634{
635 ASSERT(target == GL_TEXTURE_2D);
636
637 IDirect3DSurface9 *surface = NULL;
638 HRESULT result = mTexture->GetSurfaceLevel(0, &surface);
639
640 ASSERT(SUCCEEDED(result));
641
642 return surface;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000643}
644
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000645bool Texture2D::dirtyImageData() const
646{
647 int q = log2(std::max(mWidth, mHeight));
648
649 for (int i = 0; i <= q; i++)
650 {
651 if (mImageArray[i].dirty) return true;
652 }
653
654 return false;
655}
656
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000657TextureCubeMap::TextureCubeMap()
658{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000659 mTexture = NULL;
660}
661
662TextureCubeMap::~TextureCubeMap()
663{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000664 if (mTexture)
665 {
666 mTexture->Release();
667 mTexture = NULL;
668 }
669}
670
671GLenum TextureCubeMap::getTarget() const
672{
673 return GL_TEXTURE_CUBE_MAP;
674}
675
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000676void 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 +0000677{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000678 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000679}
680
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000681void 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 +0000682{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000683 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000684}
685
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000686void 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 +0000687{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000688 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000689}
690
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000691void 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 +0000692{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000693 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000694}
695
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000696void 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 +0000697{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000698 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000699}
700
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000701void 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 +0000702{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000703 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000704}
705
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000706void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
707{
708 int face = faceIndex(faceTarget);
709
710 ASSERT(mImageArray[face][level].surface != NULL);
711
712 if (mTexture != NULL)
713 {
714 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000715 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000716
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000717 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000718
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000719 if (SUCCEEDED(result))
720 {
721 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000722
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000723 RECT sourceRect;
724 sourceRect.left = xoffset;
725 sourceRect.top = yoffset;
726 sourceRect.right = xoffset + width;
727 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000728
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000729 POINT destPoint;
730 destPoint.x = xoffset;
731 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000732
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000733 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
734 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000735
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000736 destLevel->Release();
737
738 img->dirty = false;
739 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000740 }
741}
742
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000743void 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 +0000744{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000745 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000746 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000747}
748
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000749// 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 +0000750bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000751{
752 if (mWidth <= 0 || mHeight <= 0 || mWidth != mHeight)
753 {
754 return false;
755 }
756
757 bool mipmapping;
758
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000759 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000760 {
761 case GL_NEAREST:
762 case GL_LINEAR:
763 mipmapping = false;
764 break;
765 case GL_NEAREST_MIPMAP_NEAREST:
766 case GL_LINEAR_MIPMAP_NEAREST:
767 case GL_NEAREST_MIPMAP_LINEAR:
768 case GL_LINEAR_MIPMAP_LINEAR:
769 mipmapping = true;
770 break;
771 default: UNREACHABLE();
772 }
773
774 for (int face = 0; face < 6; face++)
775 {
776 if (mImageArray[face][0].width != mWidth || mImageArray[face][0].height != mHeight)
777 {
778 return false;
779 }
780 }
781
782 if (mipmapping)
783 {
784 int q = log2(mWidth);
785
786 for (int face = 0; face < 6; face++)
787 {
788 for (int level = 1; level <= q; level++)
789 {
790 if (mImageArray[face][level].format != mImageArray[0][0].format)
791 {
792 return false;
793 }
794
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000795 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
796 {
797 return false;
798 }
799
800 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
801 {
802 return false;
803 }
804 }
805 }
806 }
807
808 return true;
809}
810
811// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000812IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000813{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000814 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000815 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000816
817 IDirect3DCubeTexture9 *texture;
818
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000819 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000820
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000821 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000822 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000823 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000824 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000825 }
826
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000827 if (mTexture) mTexture->Release();
828
829 mTexture = texture;
830 return mTexture;
831}
832
833void TextureCubeMap::updateTexture()
834{
835 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000836
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000837 for (int face = 0; face < 6; face++)
838 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000839 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000840 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000841 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000842
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000843 if (img->dirty)
844 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000845 IDirect3DSurface9 *levelSurface;
846 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(face), level, &levelSurface);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000847
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000848 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000849
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000850 if (SUCCEEDED(result))
851 {
852 result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
853 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000854
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000855 levelSurface->Release();
856
857 img->dirty = false;
858 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000859 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000860 }
861 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000862}
863
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000864IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
865{
866 IDirect3DCubeTexture9 *texture;
867
868 IDirect3DDevice9 *device = getDevice();
869 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
870
871 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
872
873 if (FAILED(result))
874 {
875 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
876 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
877 }
878
879 if (mTexture != NULL)
880 {
881 int levels = texture->GetLevelCount();
882 for (int f = 0; f < 6; f++)
883 {
884 for (int i = 0; i < levels; i++)
885 {
886 IDirect3DSurface9 *source;
887 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
888
889 if (FAILED(result))
890 {
891 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
892
893 texture->Release();
894
895 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
896 }
897
898 IDirect3DSurface9 *dest;
899 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
900
901 if (FAILED(result))
902 {
903 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
904
905 texture->Release();
906 source->Release();
907
908 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
909 }
910
911 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
912
913 if (FAILED(result))
914 {
915 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
916
917 texture->Release();
918 source->Release();
919 dest->Release();
920
921 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
922 }
923 }
924 }
925
926 mTexture->Release();
927 }
928
929 mTexture = texture;
930 return mTexture;
931}
932
933IDirect3DSurface9 *TextureCubeMap::getSurface(GLenum target)
934{
935 ASSERT(es2dx::IsCubemapTextureTarget(target));
936
937 IDirect3DSurface9 *surface = NULL;
938 HRESULT result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &surface);
939 ASSERT(SUCCEEDED(result));
940 return surface;
941}
942
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000943void 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 +0000944{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000945 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000946
947 if (face == 0 && level == 0)
948 {
949 mWidth = width;
950 mHeight = height;
951 }
952}
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000953
954unsigned int TextureCubeMap::faceIndex(GLenum face)
955{
956 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
957 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
958 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
959 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
960 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
961
962 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
963}
964
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000965bool TextureCubeMap::dirtyImageData() const
966{
967 int q = log2(mWidth);
968
969 for (int f = 0; f < 6; f++)
970 {
971 for (int i = 0; i <= q; i++)
972 {
973 if (mImageArray[f][i].dirty) return true;
974 }
975 }
976
977 return false;
978}
979
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000980}