blob: 5f14c3148cf41187e335dfae164d2f05f9d9dbd2 [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
153// Returns the size, in bytes, of a single texel in an Image
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000154int Texture::pixelSize(GLenum format, GLenum type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000155{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000156 switch (type)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000157 {
158 case GL_UNSIGNED_BYTE:
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000159 switch (format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000160 {
161 case GL_ALPHA: return sizeof(unsigned char);
162 case GL_LUMINANCE: return sizeof(unsigned char);
163 case GL_LUMINANCE_ALPHA: return sizeof(unsigned char) * 2;
164 case GL_RGB: return sizeof(unsigned char) * 3;
165 case GL_RGBA: return sizeof(unsigned char) * 4;
166 default: UNREACHABLE();
167 }
168 break;
169 case GL_UNSIGNED_SHORT_4_4_4_4:
170 case GL_UNSIGNED_SHORT_5_5_5_1:
171 case GL_UNSIGNED_SHORT_5_6_5:
172 return sizeof(unsigned short);
173 default: UNREACHABLE();
174 }
175
176 return 0;
177}
178
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000179int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000180{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000181 return img.width * 4;
182}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000183
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000184GLsizei Texture::computePitch(GLsizei width, GLenum format, GLenum type, GLint alignment) const
185{
186 ASSERT(alignment > 0 && isPow2(alignment));
187
188 GLsizei rawPitch = pixelSize(format, type) * width;
189 return (rawPitch + alignment - 1) & ~(alignment - 1);
190}
191
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000192// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
193// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
194void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000195 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000196{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000197 GLsizei inputPitch = computePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000198
199 for (int y = 0; y < height; y++)
200 {
201 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
202 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
203 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
204
205 for (int x = 0; x < width; x++)
206 {
207 unsigned char r;
208 unsigned char g;
209 unsigned char b;
210 unsigned char a;
211
212 switch (format)
213 {
214 case GL_ALPHA:
215 a = source[x];
216 r = 0;
217 g = 0;
218 b = 0;
219 break;
220
221 case GL_LUMINANCE:
222 r = source[x];
223 g = source[x];
224 b = source[x];
225 a = 0xFF;
226 break;
227
228 case GL_LUMINANCE_ALPHA:
229 r = source[2*x+0];
230 g = source[2*x+0];
231 b = source[2*x+0];
232 a = source[2*x+1];
233 break;
234
235 case GL_RGB:
236 switch (type)
237 {
238 case GL_UNSIGNED_BYTE:
239 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000240 g = source[x * 3 + 1];
241 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000242 a = 0xFF;
243 break;
244
245 case GL_UNSIGNED_SHORT_5_6_5:
246 {
247 unsigned short rgba = source16[x];
248
249 a = 0xFF;
250 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
251 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
252 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
253 }
254 break;
255
256 default: UNREACHABLE();
257 }
258 break;
259
260 case GL_RGBA:
261 switch (type)
262 {
263 case GL_UNSIGNED_BYTE:
264 r = source[x * 4 + 0];
265 g = source[x * 4 + 1];
266 b = source[x * 4 + 2];
267 a = source[x * 4 + 3];
268 break;
269
270 case GL_UNSIGNED_SHORT_4_4_4_4:
271 {
272 unsigned short rgba = source16[x];
273
274 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
275 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
276 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
277 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
278 }
279 break;
280
281 case GL_UNSIGNED_SHORT_5_5_5_1:
282 {
283 unsigned short rgba = source16[x];
284
285 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000286 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000287 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000288 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000289 }
290 break;
291
292 default: UNREACHABLE();
293 }
294 break;
295 default: UNREACHABLE();
296 }
297
298 dest[4 * x + 0] = b;
299 dest[4 * x + 1] = g;
300 dest[4 * x + 2] = r;
301 dest[4 * x + 3] = a;
302 }
303 }
304}
305
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000306void 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 +0000307{
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000308 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000309
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000310 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000311 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000312 HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
313
314 if (FAILED(result))
315 {
316 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
317 return error(GL_OUT_OF_MEMORY);
318 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000319 }
320
321 if (img->surface) img->surface->Release();
322 img->surface = newSurface;
323
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000324 img->width = width;
325 img->height = height;
326 img->format = format;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000327
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000328 if (pixels != NULL && newSurface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000329 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000330 D3DLOCKED_RECT locked;
331 HRESULT result = newSurface->LockRect(&locked, NULL, 0);
332
333 ASSERT(SUCCEEDED(result));
334
335 if (SUCCEEDED(result))
336 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000337 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000338 newSurface->UnlockRect();
339 }
340
341 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000342 }
343
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000344 mDirtyMetaData = true;
345}
346
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000347void 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 +0000348{
349 if (width + xoffset > img->width || height + yoffset > img->height) return error(GL_INVALID_VALUE);
350
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000351 D3DLOCKED_RECT locked;
352 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000353
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000354 ASSERT(SUCCEEDED(result));
355
356 if (SUCCEEDED(result))
357 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000358 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000359 img->surface->UnlockRect();
360 }
361
362 img->dirty = true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000363}
364
365IDirect3DBaseTexture9 *Texture::getTexture()
366{
367 if (!isComplete())
368 {
369 return NULL;
370 }
371
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000372 if (mDirtyMetaData)
373 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000374 mBaseTexture = createTexture();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000375 mIsRenderable = false;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000376 }
377
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000378 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000379 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000380 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000381 }
382
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000383 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000384 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000385
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000386 return mBaseTexture;
387}
388
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000389// Returns the top-level texture surface as a render target
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000390void Texture::needRenderTarget()
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000391{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000392 if (!mIsRenderable)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000393 {
394 mBaseTexture = convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000395 mIsRenderable = true;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000396 }
397
398 if (dirtyImageData())
399 {
400 updateTexture();
401 }
402
403 mDirtyMetaData = false;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000404}
405
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000406void Texture::dropTexture()
407{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000408 if (mBaseTexture)
409 {
410 mBaseTexture = NULL;
411 }
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000412
413 mIsRenderable = false;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000414}
415
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000416void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000417{
418 mBaseTexture = newTexture;
419 mDirtyMetaData = false;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000420 mIsRenderable = renderable;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000421}
422
423
424Texture2D::Texture2D(Context *context) : Texture(context)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000425{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000426 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000427 mColorbufferProxy = NULL;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000428}
429
430Texture2D::~Texture2D()
431{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000432 delete mColorbufferProxy;
433
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000434 if (mTexture)
435 {
436 mTexture->Release();
437 mTexture = NULL;
438 }
439}
440
441GLenum Texture2D::getTarget() const
442{
443 return GL_TEXTURE_2D;
444}
445
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000446// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
447// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
448// Call this when a particular level of the texture must be defined with a specific format, width and height.
449//
450// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
451// a new height and width for the texture by working backwards from the given width and height.
452bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
453{
454 bool widthOkay = (mWidth >> level == width);
455 bool heightOkay = (mHeight >> level == height);
456
457 bool sizeOkay = ((widthOkay && heightOkay)
458 || (widthOkay && mHeight >> level == 0 && height == 1)
459 || (heightOkay && mWidth >> level == 0 && width == 1));
460
461 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
462
463 if (!textureOkay)
464 {
465 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
466 mImageArray[0].format, mWidth, mHeight,
467 internalFormat, width, height);
468
469 // Purge all the levels and the texture.
470
471 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
472 {
473 if (mImageArray[i].surface != NULL)
474 {
475 mImageArray[i].dirty = false;
476
477 mImageArray[i].surface->Release();
478 mImageArray[i].surface = NULL;
479 }
480 }
481
482 if (mTexture != NULL)
483 {
484 mTexture->Release();
485 mTexture = NULL;
486 dropTexture();
487 }
488
489 mWidth = width << level;
490 mHeight = height << level;
491 mImageArray[0].format = internalFormat;
492 }
493
494 return !textureOkay;
495}
496
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000497void 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 +0000498{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000499 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000500
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000501 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000502}
503
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000504void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
505{
506 ASSERT(mImageArray[level].surface != NULL);
507
508 if (mTexture != NULL)
509 {
510 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000511 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000512
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000513 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000514
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000515 if (SUCCEEDED(result))
516 {
517 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000518
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000519 RECT sourceRect;
520 sourceRect.left = xoffset;
521 sourceRect.top = yoffset;
522 sourceRect.right = xoffset + width;
523 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000524
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000525 POINT destPoint;
526 destPoint.x = xoffset;
527 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000528
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000529 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
530 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000531
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000532 destLevel->Release();
533
534 img->dirty = false;
535 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000536 }
537}
538
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000539void 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 +0000540{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000541 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000542 commitRect(level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000543}
544
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000545void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
546{
547 if (redefineTexture(level, internalFormat, width, height))
548 {
549 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000550 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000551 }
552
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000553 if (width != 0 && height != 0)
554 {
555 RECT sourceRect;
556 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000557 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000558 sourceRect.top = y;
559 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000560
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000561 IDirect3DSurface9 *dest;
562 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000563
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000564 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
565 dest->Release();
566 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000567
568 mImageArray[level].width = width;
569 mImageArray[level].height = height;
570 mImageArray[level].format = internalFormat;
571}
572
573void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
574{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000575 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
576 {
577 return error(GL_INVALID_VALUE);
578 }
579
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000580 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
581 {
582 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000583 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000584 }
585 else
586 {
587 getRenderTarget(GL_TEXTURE_2D);
588 }
589
590 RECT sourceRect;
591 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000592 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000593 sourceRect.top = y;
594 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000595
596 IDirect3DSurface9 *dest;
597 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
598
599 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
600 dest->Release();
601}
602
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000603// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
604bool Texture2D::isComplete() const
605{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000606 GLsizei width = mImageArray[0].width;
607 GLsizei height = mImageArray[0].height;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000608
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000609 if (width <= 0 || height <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000610 {
611 return false;
612 }
613
614 bool mipmapping;
615
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000616 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000617 {
618 case GL_NEAREST:
619 case GL_LINEAR:
620 mipmapping = false;
621 break;
622 case GL_NEAREST_MIPMAP_NEAREST:
623 case GL_LINEAR_MIPMAP_NEAREST:
624 case GL_NEAREST_MIPMAP_LINEAR:
625 case GL_LINEAR_MIPMAP_LINEAR:
626 mipmapping = true;
627 break;
628 default: UNREACHABLE();
629 }
630
631 if (mipmapping)
632 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000633 int q = log2(std::max(width, height));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000634
635 for (int level = 1; level <= q; level++)
636 {
637 if (mImageArray[level].format != mImageArray[0].format)
638 {
639 return false;
640 }
641
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000642 if (mImageArray[level].width != (mImageArray[level - 1].width + 1) / 2)
643 {
644 return false;
645 }
646
647 if (mImageArray[level].height != (mImageArray[level - 1].height + 1) / 2)
648 {
649 return false;
650 }
651 }
652 }
653
654 return true;
655}
656
657// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000658IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000659{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000660 IDirect3DTexture9 *texture;
661
662 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000663 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000664
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000665 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000666
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000667 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000668 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000669 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000670 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000671 }
672
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000673 if (mTexture) mTexture->Release();
674 mTexture = texture;
675 return texture;
676}
677
678void Texture2D::updateTexture()
679{
680 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000681
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000682 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000683
684 for (int level = 0; level < levelCount; level++)
685 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000686 if (mImageArray[level].dirty)
687 {
688 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000689 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000690
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000691 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000692
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000693 if (SUCCEEDED(result))
694 {
695 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
696 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000697
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000698 levelSurface->Release();
699
700 mImageArray[level].dirty = false;
701 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000702 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000703 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000704}
705
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000706IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000707{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000708 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000709
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000710 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000711 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000712 IDirect3DDevice9 *device = getDevice();
713 D3DFORMAT format = selectFormat(mImageArray[0].format);
714
715 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
716
717 if (FAILED(result))
718 {
719 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
720 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
721 }
722
723 if (mTexture != NULL)
724 {
725 int levels = texture->GetLevelCount();
726 for (int i = 0; i < levels; i++)
727 {
728 IDirect3DSurface9 *source;
729 result = mTexture->GetSurfaceLevel(i, &source);
730
731 if (FAILED(result))
732 {
733 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
734
735 texture->Release();
736
737 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
738 }
739
740 IDirect3DSurface9 *dest;
741 result = texture->GetSurfaceLevel(i, &dest);
742
743 if (FAILED(result))
744 {
745 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
746
747 texture->Release();
748 source->Release();
749
750 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
751 }
752
753 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
754
755 if (FAILED(result))
756 {
757 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
758
759 texture->Release();
760 source->Release();
761 dest->Release();
762
763 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
764 }
765
766 source->Release();
767 dest->Release();
768 }
769 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000770 }
771
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000772 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000773 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000774 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000775 }
776
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000777 mTexture = texture;
778 return mTexture;
779}
780
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000781bool Texture2D::dirtyImageData() const
782{
783 int q = log2(std::max(mWidth, mHeight));
784
785 for (int i = 0; i <= q; i++)
786 {
787 if (mImageArray[i].dirty) return true;
788 }
789
790 return false;
791}
792
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000793void Texture2D::generateMipmaps()
794{
795 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
796 {
797 return error(GL_INVALID_OPERATION);
798 }
799
800 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
801 unsigned int q = log2(std::max(mWidth, mHeight));
802 for (unsigned int i = 1; i <= q; i++)
803 {
804 if (mImageArray[i].surface != NULL)
805 {
806 mImageArray[i].surface->Release();
807 mImageArray[i].surface = NULL;
808 }
809
810 mImageArray[i].dirty = false;
811
812 mImageArray[i].format = mImageArray[0].format;
813 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
814 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
815 }
816
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000817 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000818
819 for (unsigned int i = 1; i <= q; i++)
820 {
821 IDirect3DSurface9 *upper = NULL;
822 IDirect3DSurface9 *lower = NULL;
823
824 mTexture->GetSurfaceLevel(i-1, &upper);
825 mTexture->GetSurfaceLevel(i, &lower);
826
827 if (upper != NULL && lower != NULL)
828 {
829 getBlitter()->boxFilter(upper, lower);
830 }
831
832 if (upper != NULL) upper->Release();
833 if (lower != NULL) lower->Release();
834 }
835}
836
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000837Colorbuffer *Texture2D::getColorbuffer(GLenum target)
838{
839 if (target != GL_TEXTURE_2D)
840 {
841 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
842 }
843
844 if (mColorbufferProxy == NULL)
845 {
846 mColorbufferProxy = new TextureColorbufferProxy(this, target);
847 }
848
849 return mColorbufferProxy;
850}
851
852IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
853{
854 ASSERT(target == GL_TEXTURE_2D);
855
856 needRenderTarget();
857
858 IDirect3DSurface9 *renderTarget = NULL;
859 mTexture->GetSurfaceLevel(0, &renderTarget);
860
861 return renderTarget;
862}
863
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000864TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000865{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000866 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000867
868 for (int i = 0; i < 6; i++)
869 {
870 mFaceProxies[i] = NULL;
871 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000872}
873
874TextureCubeMap::~TextureCubeMap()
875{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000876 for (int i = 0; i < 6; i++)
877 {
878 delete mFaceProxies[i];
879 }
880
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000881 if (mTexture)
882 {
883 mTexture->Release();
884 mTexture = NULL;
885 }
886}
887
888GLenum TextureCubeMap::getTarget() const
889{
890 return GL_TEXTURE_CUBE_MAP;
891}
892
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000893void 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 +0000894{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000895 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000896}
897
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000898void 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 +0000899{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000900 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000901}
902
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000903void 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 +0000904{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000905 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000906}
907
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000908void 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 +0000909{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000910 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000911}
912
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000913void 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 +0000914{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000915 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000916}
917
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000918void 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 +0000919{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000920 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000921}
922
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000923void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
924{
925 int face = faceIndex(faceTarget);
926
927 ASSERT(mImageArray[face][level].surface != NULL);
928
929 if (mTexture != NULL)
930 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000931 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
932 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000933
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000934 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000935 {
936 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000937
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000938 RECT sourceRect;
939 sourceRect.left = xoffset;
940 sourceRect.top = yoffset;
941 sourceRect.right = xoffset + width;
942 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000943
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000944 POINT destPoint;
945 destPoint.x = xoffset;
946 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000947
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000948 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000949 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000950
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000951 destLevel->Release();
952
953 img->dirty = false;
954 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000955 }
956}
957
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000958void 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 +0000959{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000960 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000961 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000962}
963
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000964// 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 +0000965bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000966{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000967 int size = mImageArray[0][0].width;
968
969 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000970 {
971 return false;
972 }
973
974 bool mipmapping;
975
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000976 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000977 {
978 case GL_NEAREST:
979 case GL_LINEAR:
980 mipmapping = false;
981 break;
982 case GL_NEAREST_MIPMAP_NEAREST:
983 case GL_LINEAR_MIPMAP_NEAREST:
984 case GL_NEAREST_MIPMAP_LINEAR:
985 case GL_LINEAR_MIPMAP_LINEAR:
986 mipmapping = true;
987 break;
988 default: UNREACHABLE();
989 }
990
991 for (int face = 0; face < 6; face++)
992 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000993 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000994 {
995 return false;
996 }
997 }
998
999 if (mipmapping)
1000 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001001 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001002
1003 for (int face = 0; face < 6; face++)
1004 {
1005 for (int level = 1; level <= q; level++)
1006 {
1007 if (mImageArray[face][level].format != mImageArray[0][0].format)
1008 {
1009 return false;
1010 }
1011
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001012 if (mImageArray[face][level].width != (mImageArray[0][level - 1].width + 1) / 2)
1013 {
1014 return false;
1015 }
1016
1017 if (mImageArray[face][level].height != (mImageArray[0][level - 1].height + 1) / 2)
1018 {
1019 return false;
1020 }
1021 }
1022 }
1023 }
1024
1025 return true;
1026}
1027
1028// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001029IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001030{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001031 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001032 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001033
1034 IDirect3DCubeTexture9 *texture;
1035
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001036 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001037
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001038 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001039 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001040 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001041 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001042 }
1043
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001044 if (mTexture) mTexture->Release();
1045
1046 mTexture = texture;
1047 return mTexture;
1048}
1049
1050void TextureCubeMap::updateTexture()
1051{
1052 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001053
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001054 for (int face = 0; face < 6; face++)
1055 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001056 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001057 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001058 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001059
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001060 if (img->dirty)
1061 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001062 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1063 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001064
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001065 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001066 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001067 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001068 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001069
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001070 levelSurface->Release();
1071
1072 img->dirty = false;
1073 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001074 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001075 }
1076 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001077}
1078
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001079IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1080{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001081 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001082
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001083 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001084 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001085 IDirect3DDevice9 *device = getDevice();
1086 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1087
1088 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1089
1090 if (FAILED(result))
1091 {
1092 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1093 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1094 }
1095
1096 if (mTexture != NULL)
1097 {
1098 int levels = texture->GetLevelCount();
1099 for (int f = 0; f < 6; f++)
1100 {
1101 for (int i = 0; i < levels; i++)
1102 {
1103 IDirect3DSurface9 *source;
1104 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1105
1106 if (FAILED(result))
1107 {
1108 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1109
1110 texture->Release();
1111
1112 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1113 }
1114
1115 IDirect3DSurface9 *dest;
1116 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1117
1118 if (FAILED(result))
1119 {
1120 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1121
1122 texture->Release();
1123 source->Release();
1124
1125 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1126 }
1127
1128 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1129
1130 if (FAILED(result))
1131 {
1132 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1133
1134 texture->Release();
1135 source->Release();
1136 dest->Release();
1137
1138 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1139 }
1140 }
1141 }
1142 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001143 }
1144
1145 if (mTexture != NULL)
1146 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001147 mTexture->Release();
1148 }
1149
1150 mTexture = texture;
1151 return mTexture;
1152}
1153
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001154void 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 +00001155{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001156 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001157
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001158 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001159}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001160
1161unsigned int TextureCubeMap::faceIndex(GLenum face)
1162{
1163 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1164 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1165 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1166 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1167 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1168
1169 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1170}
1171
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001172bool TextureCubeMap::dirtyImageData() const
1173{
1174 int q = log2(mWidth);
1175
1176 for (int f = 0; f < 6; f++)
1177 {
1178 for (int i = 0; i <= q; i++)
1179 {
1180 if (mImageArray[f][i].dirty) return true;
1181 }
1182 }
1183
1184 return false;
1185}
1186
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001187// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1188// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1189// Call this when a particular level of the texture must be defined with a specific format, width and height.
1190//
1191// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1192// a new size for the texture by working backwards from the given size.
1193bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1194{
1195 // Are these settings compatible with level 0?
1196 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1197
1198 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1199
1200 if (!textureOkay)
1201 {
1202 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1203 mImageArray[0][0].format, mImageArray[0][0].width,
1204 internalFormat, width);
1205
1206 // Purge all the levels and the texture.
1207 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1208 {
1209 for (int f = 0; f < 6; f++)
1210 {
1211 if (mImageArray[f][i].surface != NULL)
1212 {
1213 mImageArray[f][i].dirty = false;
1214
1215 mImageArray[f][i].surface->Release();
1216 mImageArray[f][i].surface = NULL;
1217 }
1218 }
1219 }
1220
1221 if (mTexture != NULL)
1222 {
1223 mTexture->Release();
1224 mTexture = NULL;
1225 dropTexture();
1226 }
1227
1228 mWidth = width << level;
1229 mImageArray[0][0].width = width << level;
1230 mHeight = width << level;
1231 mImageArray[0][0].height = width << level;
1232
1233 mImageArray[0][0].format = internalFormat;
1234 }
1235
1236 return !textureOkay;
1237}
1238
1239void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1240{
1241 unsigned int faceindex = faceIndex(face);
1242
1243 if (redefineTexture(level, internalFormat, width))
1244 {
1245 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001246 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001247 }
1248
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001249 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001250
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001251 if (width > 0)
1252 {
1253 RECT sourceRect;
1254 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001255 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001256 sourceRect.top = y;
1257 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001258
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001259 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1260
1261 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1262 dest->Release();
1263 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001264
1265 mImageArray[faceindex][level].width = width;
1266 mImageArray[faceindex][level].height = height;
1267 mImageArray[faceindex][level].format = internalFormat;
1268}
1269
1270IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1271{
1272 unsigned int faceIndex;
1273
1274 if (faceIdentifier < 6)
1275 {
1276 faceIndex = faceIdentifier;
1277 }
1278 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1279 {
1280 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1281 }
1282 else
1283 {
1284 UNREACHABLE();
1285 faceIndex = 0;
1286 }
1287
1288 if (mTexture == NULL)
1289 {
1290 UNREACHABLE();
1291 return NULL;
1292 }
1293
1294 IDirect3DSurface9 *surface = NULL;
1295
1296 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1297
1298 return (SUCCEEDED(hr)) ? surface : NULL;
1299}
1300
1301void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1302{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001303 GLsizei size = mImageArray[faceIndex(face)][level].width;
1304
1305 if (xoffset + width > size || yoffset + height > size)
1306 {
1307 return error(GL_INVALID_VALUE);
1308 }
1309
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001310 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1311 {
1312 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001313 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001314 }
1315 else
1316 {
1317 getRenderTarget(face);
1318 }
1319
1320 RECT sourceRect;
1321 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001322 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001323 sourceRect.top = y;
1324 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001325
1326 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1327
1328 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1329 dest->Release();
1330}
1331
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001332bool TextureCubeMap::isCubeComplete() const
1333{
1334 if (mImageArray[0][0].width == 0)
1335 {
1336 return false;
1337 }
1338
1339 for (unsigned int f = 1; f < 6; f++)
1340 {
1341 if (mImageArray[f][0].width != mImageArray[0][0].width
1342 || mImageArray[f][0].format != mImageArray[0][0].format)
1343 {
1344 return false;
1345 }
1346 }
1347
1348 return true;
1349}
1350
1351void TextureCubeMap::generateMipmaps()
1352{
1353 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1354 {
1355 return error(GL_INVALID_OPERATION);
1356 }
1357
1358 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1359 unsigned int q = log2(mImageArray[0][0].width);
1360 for (unsigned int f = 0; f < 6; f++)
1361 {
1362 for (unsigned int i = 1; i <= q; i++)
1363 {
1364 if (mImageArray[f][i].surface != NULL)
1365 {
1366 mImageArray[f][i].surface->Release();
1367 mImageArray[f][i].surface = NULL;
1368 }
1369
1370 mImageArray[f][i].dirty = false;
1371
1372 mImageArray[f][i].format = mImageArray[f][0].format;
1373 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1374 mImageArray[f][i].height = mImageArray[f][i].width;
1375 }
1376 }
1377
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001378 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001379
1380 for (unsigned int f = 0; f < 6; f++)
1381 {
1382 for (unsigned int i = 1; i <= q; i++)
1383 {
1384 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1385 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1386
1387 if (upper != NULL && lower != NULL)
1388 {
1389 getBlitter()->boxFilter(upper, lower);
1390 }
1391
1392 if (upper != NULL) upper->Release();
1393 if (lower != NULL) lower->Release();
1394 }
1395 }
1396}
1397
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001398Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
1399{
1400 if (!es2dx::IsCubemapTextureTarget(target))
1401 {
1402 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
1403 }
1404
1405 unsigned int face = faceIndex(target);
1406
1407 if (mFaceProxies[face] == NULL)
1408 {
1409 mFaceProxies[face] = new TextureColorbufferProxy(this, target);
1410 }
1411
1412 return mFaceProxies[face];
1413}
1414
1415IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1416{
1417 ASSERT(es2dx::IsCubemapTextureTarget(target));
1418
1419 needRenderTarget();
1420
1421 IDirect3DSurface9 *renderTarget = NULL;
1422 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1423
1424 return renderTarget;
1425}
1426
1427Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1428 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1429{
1430 ASSERT(target == GL_TEXTURE_2D || es2dx::IsCubemapTextureTarget(target));
1431 latchTextureInfo();
1432}
1433
1434IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1435{
1436 latchTextureInfo();
1437
1438 if (mRenderTarget) mRenderTarget->Release();
1439
1440 mRenderTarget = mTexture->getRenderTarget(mTarget);
1441
1442 return mRenderTarget;
1443}
1444
1445void Texture::TextureColorbufferProxy::latchTextureInfo()
1446{
1447 mWidth = mTexture->getWidth();
1448 mHeight = mTexture->getHeight();
1449}
1450
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001451}