blob: 7f50c5b53b92d854362c5b2e703adca9e1edbcf4 [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.com01868132010-08-24 19:21:17 +000026 : width(0), height(0), dirty(false), surface(NULL), format(GL_NONE)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +000027{
28}
29
30Texture::Image::~Image()
31{
32 if (surface) surface->Release();
33}
34
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +000035Texture::Texture(GLuint id) : RefCountObject(id)
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.com31273552010-08-04 13:42:44 +000042 mWidth = 0;
43 mHeight = 0;
44
daniel@transgaming.com00c75962010-03-11 20:36:15 +000045 mDirtyMetaData = true;
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000046 mDirty = true;
daniel@transgaming.com93a81472010-04-20 18:52:58 +000047 mIsRenderable = false;
daniel@transgaming.com0a311a42010-05-17 09:58:33 +000048 mBaseTexture = NULL;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000049}
50
51Texture::~Texture()
52{
53}
54
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000055Blit *Texture::getBlitter()
56{
daniel@transgaming.comda13f3e2010-07-28 19:20:56 +000057 Context *context = getContext();
58 return context->getBlitter();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +000059}
60
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000061// Returns true on successful filter state update (valid enum parameter)
62bool Texture::setMinFilter(GLenum filter)
63{
64 switch (filter)
65 {
66 case GL_NEAREST:
67 case GL_LINEAR:
68 case GL_NEAREST_MIPMAP_NEAREST:
69 case GL_LINEAR_MIPMAP_NEAREST:
70 case GL_NEAREST_MIPMAP_LINEAR:
71 case GL_LINEAR_MIPMAP_LINEAR:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000072 {
73 if (mMinFilter != filter)
74 {
75 mMinFilter = filter;
76 mDirty = true;
77 }
78 return true;
79 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000080 default:
81 return false;
daniel@transgaming.comfab5a1a2010-03-11 19:22:30 +000082 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +000083}
84
85// Returns true on successful filter state update (valid enum parameter)
86bool Texture::setMagFilter(GLenum filter)
87{
88 switch (filter)
89 {
90 case GL_NEAREST:
91 case GL_LINEAR:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +000092 {
93 if (mMagFilter != filter)
94 {
95 mMagFilter = filter;
96 mDirty = true;
97 }
98 return true;
99 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000100 default:
101 return false;
102 }
103}
104
105// Returns true on successful wrap state update (valid enum parameter)
106bool Texture::setWrapS(GLenum wrap)
107{
108 switch (wrap)
109 {
110 case GL_REPEAT:
111 case GL_CLAMP_TO_EDGE:
112 case GL_MIRRORED_REPEAT:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000113 {
114 if (mWrapS != wrap)
115 {
116 mWrapS = wrap;
117 mDirty = true;
118 }
119 return true;
120 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000121 default:
122 return false;
123 }
124}
125
126// Returns true on successful wrap state update (valid enum parameter)
127bool Texture::setWrapT(GLenum wrap)
128{
129 switch (wrap)
130 {
131 case GL_REPEAT:
132 case GL_CLAMP_TO_EDGE:
133 case GL_MIRRORED_REPEAT:
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000134 {
135 if (mWrapT != wrap)
136 {
137 mWrapT = wrap;
138 mDirty = true;
139 }
140 return true;
141 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000142 default:
143 return false;
144 }
145}
146
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000147GLenum Texture::getMinFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000148{
149 return mMinFilter;
150}
151
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000152GLenum Texture::getMagFilter() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000153{
154 return mMagFilter;
155}
156
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000157GLenum Texture::getWrapS() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000158{
159 return mWrapS;
160}
161
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000162GLenum Texture::getWrapT() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000163{
164 return mWrapT;
165}
166
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000167GLuint Texture::getWidth() const
168{
169 return mWidth;
170}
171
172GLuint Texture::getHeight() const
173{
174 return mHeight;
175}
176
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000177// Selects an internal Direct3D 9 format for storing an Image
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000178D3DFORMAT Texture::selectFormat(GLenum format)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000179{
daniel@transgaming.com01868132010-08-24 19:21:17 +0000180 if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
181 format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
182 {
183 return D3DFMT_DXT1;
184 }
185 else
186 {
187 return D3DFMT_A8R8G8B8;
188 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000189}
190
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000191int Texture::imagePitch(const Image &img) const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000192{
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000193 return img.width * 4;
194}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000195
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000196// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
197// into the BGRA8 pixel rectangle at output with outputPitch bytes in between each line.
198void Texture::loadImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type,
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000199 GLint unpackAlignment, const void *input, size_t outputPitch, void *output) const
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000200{
daniel@transgaming.com713914b2010-05-04 03:35:17 +0000201 GLsizei inputPitch = ComputePitch(width, format, type, unpackAlignment);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000202
203 for (int y = 0; y < height; y++)
204 {
205 const unsigned char *source = static_cast<const unsigned char*>(input) + y * inputPitch;
206 const unsigned short *source16 = reinterpret_cast<const unsigned short*>(source);
207 unsigned char *dest = static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 4;
208
daniel@transgaming.coma9198d92010-08-08 04:49:56 +0000209 // fast path for EXT_texture_format_BGRA8888
210 if (format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE) {
211 memcpy(dest, source, width*4);
212 continue;
213 }
214
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000215 for (int x = 0; x < width; x++)
216 {
217 unsigned char r;
218 unsigned char g;
219 unsigned char b;
220 unsigned char a;
221
222 switch (format)
223 {
224 case GL_ALPHA:
225 a = source[x];
226 r = 0;
227 g = 0;
228 b = 0;
229 break;
230
231 case GL_LUMINANCE:
232 r = source[x];
233 g = source[x];
234 b = source[x];
235 a = 0xFF;
236 break;
237
238 case GL_LUMINANCE_ALPHA:
239 r = source[2*x+0];
240 g = source[2*x+0];
241 b = source[2*x+0];
242 a = source[2*x+1];
243 break;
244
245 case GL_RGB:
246 switch (type)
247 {
248 case GL_UNSIGNED_BYTE:
249 r = source[x * 3 + 0];
daniel@transgaming.com5ac52152010-04-13 19:53:38 +0000250 g = source[x * 3 + 1];
251 b = source[x * 3 + 2];
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000252 a = 0xFF;
253 break;
254
255 case GL_UNSIGNED_SHORT_5_6_5:
256 {
257 unsigned short rgba = source16[x];
258
259 a = 0xFF;
260 b = ((rgba & 0x001F) << 3) | ((rgba & 0x001F) >> 2);
261 g = ((rgba & 0x07E0) >> 3) | ((rgba & 0x07E0) >> 9);
262 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
263 }
264 break;
265
266 default: UNREACHABLE();
267 }
268 break;
269
270 case GL_RGBA:
271 switch (type)
272 {
273 case GL_UNSIGNED_BYTE:
274 r = source[x * 4 + 0];
275 g = source[x * 4 + 1];
276 b = source[x * 4 + 2];
277 a = source[x * 4 + 3];
278 break;
279
280 case GL_UNSIGNED_SHORT_4_4_4_4:
281 {
282 unsigned short rgba = source16[x];
283
284 a = ((rgba & 0x000F) << 4) | ((rgba & 0x000F) >> 0);
285 b = ((rgba & 0x00F0) << 0) | ((rgba & 0x00F0) >> 4);
286 g = ((rgba & 0x0F00) >> 4) | ((rgba & 0x0F00) >> 8);
287 r = ((rgba & 0xF000) >> 8) | ((rgba & 0xF000) >> 12);
288 }
289 break;
290
291 case GL_UNSIGNED_SHORT_5_5_5_1:
292 {
293 unsigned short rgba = source16[x];
294
295 a = (rgba & 0x0001) ? 0xFF : 0;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000296 b = ((rgba & 0x003E) << 2) | ((rgba & 0x003E) >> 3);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000297 g = ((rgba & 0x07C0) >> 3) | ((rgba & 0x07C0) >> 8);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000298 r = ((rgba & 0xF800) >> 8) | ((rgba & 0xF800) >> 13);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000299 }
300 break;
301
302 default: UNREACHABLE();
303 }
304 break;
305 default: UNREACHABLE();
306 }
307
308 dest[4 * x + 0] = b;
309 dest[4 * x + 1] = g;
310 dest[4 * x + 2] = r;
311 dest[4 * x + 3] = a;
312 }
313 }
314}
315
daniel@transgaming.com01868132010-08-24 19:21:17 +0000316void Texture::createSurface(GLsizei width, GLsizei height, GLenum format, Image *img)
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000317{
daniel@transgaming.comf5cde482010-08-24 19:21:11 +0000318 IDirect3DTexture9 *newTexture = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000319 IDirect3DSurface9 *newSurface = NULL;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000320
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000321 if (width != 0 && height != 0)
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000322 {
daniel@transgaming.com1dcea9f2010-08-24 19:21:27 +0000323 int levelToFetch = 0;
324 GLsizei requestWidth = width;
325 GLsizei requestHeight = height;
326 if (IsCompressed(format) && (width % 4 != 0 || height % 4 != 0))
327 {
328 bool isMult4 = false;
329 int upsampleCount = 0;
330 while (!isMult4)
331 {
332 requestWidth <<= 1;
333 requestHeight <<= 1;
334 upsampleCount++;
335 if (requestWidth % 4 == 0 && requestHeight % 4 == 0)
336 {
337 isMult4 = true;
338 }
339 }
340 levelToFetch = upsampleCount;
341 }
342
343 HRESULT result = getDevice()->CreateTexture(requestWidth, requestHeight, levelToFetch + 1, NULL, selectFormat(format),
344 D3DPOOL_SYSTEMMEM, &newTexture, NULL);
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000345
346 if (FAILED(result))
347 {
348 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
349 return error(GL_OUT_OF_MEMORY);
350 }
daniel@transgaming.comf5cde482010-08-24 19:21:11 +0000351
daniel@transgaming.com1dcea9f2010-08-24 19:21:27 +0000352 newTexture->GetSurfaceLevel(levelToFetch, &newSurface);
daniel@transgaming.comf5cde482010-08-24 19:21:11 +0000353 newTexture->Release();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000354 }
355
356 if (img->surface) img->surface->Release();
357 img->surface = newSurface;
358
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000359 img->width = width;
360 img->height = height;
361 img->format = format;
daniel@transgaming.com01868132010-08-24 19:21:17 +0000362}
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000363
daniel@transgaming.com01868132010-08-24 19:21:17 +0000364void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
365{
366 createSurface(width, height, format, img);
367
368 if (pixels != NULL && img->surface != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000369 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000370 D3DLOCKED_RECT locked;
daniel@transgaming.com01868132010-08-24 19:21:17 +0000371 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000372
373 ASSERT(SUCCEEDED(result));
374
375 if (SUCCEEDED(result))
376 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000377 loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com01868132010-08-24 19:21:17 +0000378 img->surface->UnlockRect();
379 }
380
381 img->dirty = true;
382 }
383
384 mDirtyMetaData = true;
385}
386
387void Texture::setCompressedImage(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
388{
389 createSurface(width, height, format, img);
390
391 if (pixels != NULL && img->surface != NULL)
392 {
393 D3DLOCKED_RECT locked;
394 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
395
396 ASSERT(SUCCEEDED(result));
397
398 if (SUCCEEDED(result))
399 {
400 memcpy(locked.pBits, pixels, imageSize);
401 img->surface->UnlockRect();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000402 }
403
404 img->dirty = true;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000405 }
406
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000407 mDirtyMetaData = true;
408}
409
daniel@transgaming.com31273552010-08-04 13:42:44 +0000410bool 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 +0000411{
daniel@transgaming.com31273552010-08-04 13:42:44 +0000412 if (width + xoffset > img->width || height + yoffset > img->height)
413 {
414 error(GL_INVALID_VALUE);
415 return false;
416 }
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000417
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000418 D3DLOCKED_RECT locked;
419 HRESULT result = img->surface->LockRect(&locked, NULL, 0);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000420
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000421 ASSERT(SUCCEEDED(result));
422
423 if (SUCCEEDED(result))
424 {
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000425 loadImageData(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000426 img->surface->UnlockRect();
427 }
428
429 img->dirty = true;
daniel@transgaming.com31273552010-08-04 13:42:44 +0000430 return true;
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000431}
432
daniel@transgaming.com01868132010-08-24 19:21:17 +0000433bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
434{
435 if (width + xoffset > img->width || height + yoffset > img->height)
436 {
437 error(GL_INVALID_VALUE);
438 return false;
439 }
440
441 if (format != getFormat())
442 {
443 error(GL_INVALID_OPERATION);
444 return false;
445 }
446
447 RECT updateRegion;
448 updateRegion.left = xoffset;
449 updateRegion.right = xoffset + width;
450 updateRegion.bottom = yoffset + height;
451 updateRegion.top = yoffset;
452
453 D3DLOCKED_RECT locked;
454 HRESULT result = img->surface->LockRect(&locked, &updateRegion, 0);
455
456 ASSERT(SUCCEEDED(result));
457
458 if (SUCCEEDED(result))
459 {
460 GLsizei inputPitch = ComputeCompressedPitch(width, format);
461 int rows = imageSize / inputPitch;
462 for (int i = 0; i < rows; ++i)
463 {
464 memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)pixels + i * inputPitch), inputPitch);
465 }
466 img->surface->UnlockRect();
467 }
468
469 img->dirty = true;
470 return true;
471}
472
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000473IDirect3DBaseTexture9 *Texture::getTexture()
474{
475 if (!isComplete())
476 {
477 return NULL;
478 }
479
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000480 if (mDirtyMetaData)
481 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000482 mBaseTexture = createTexture();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000483 mIsRenderable = false;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000484 }
485
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000486 if (mDirtyMetaData || dirtyImageData())
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000487 {
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000488 updateTexture();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000489 }
490
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000491 mDirtyMetaData = false;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000492 ASSERT(!dirtyImageData());
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000493
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000494 return mBaseTexture;
495}
496
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000497bool Texture::isDirty() const
498{
499 return (mDirty || mDirtyMetaData || dirtyImageData());
500}
501
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000502// Returns the top-level texture surface as a render target
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000503void Texture::needRenderTarget()
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000504{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000505 if (!mIsRenderable)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000506 {
507 mBaseTexture = convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000508 mIsRenderable = true;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000509 }
510
511 if (dirtyImageData())
512 {
513 updateTexture();
514 }
515
516 mDirtyMetaData = false;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000517}
518
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000519void Texture::dropTexture()
520{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000521 if (mBaseTexture)
522 {
523 mBaseTexture = NULL;
524 }
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000525
526 mIsRenderable = false;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000527}
528
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000529void Texture::pushTexture(IDirect3DBaseTexture9 *newTexture, bool renderable)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000530{
531 mBaseTexture = newTexture;
532 mDirtyMetaData = false;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000533 mIsRenderable = renderable;
daniel@transgaming.com5a0b0a82010-05-12 03:45:07 +0000534 mDirty = true;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000535}
536
537
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000538GLint Texture::creationLevels(GLsizei width, GLsizei height, GLint maxlevel) const
539{
540 if (isPow2(width) && isPow2(height))
541 {
542 return maxlevel;
543 }
544 else
545 {
daniel@transgaming.com4c03fa62010-06-24 13:02:16 +0000546 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
547 return 1;
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000548 }
549}
550
551GLint Texture::creationLevels(GLsizei size, GLint maxlevel) const
552{
553 return creationLevels(size, size, maxlevel);
554}
555
556int Texture::levelCount() const
557{
558 return mBaseTexture ? mBaseTexture->GetLevelCount() : 0;
559}
560
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000561Texture2D::Texture2D(GLuint id) : Texture(id)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000562{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000563 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000564 mColorbufferProxy = NULL;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000565}
566
567Texture2D::~Texture2D()
568{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000569 delete mColorbufferProxy;
570
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000571 if (mTexture)
572 {
573 mTexture->Release();
574 mTexture = NULL;
575 }
576}
577
578GLenum Texture2D::getTarget() const
579{
580 return GL_TEXTURE_2D;
581}
582
daniel@transgaming.com01868132010-08-24 19:21:17 +0000583GLenum Texture2D::getFormat() const
584{
585 return mImageArray[0].format;
586}
587
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000588// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
589// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
590// Call this when a particular level of the texture must be defined with a specific format, width and height.
591//
592// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
593// a new height and width for the texture by working backwards from the given width and height.
594bool Texture2D::redefineTexture(GLint level, GLenum internalFormat, GLsizei width, GLsizei height)
595{
596 bool widthOkay = (mWidth >> level == width);
597 bool heightOkay = (mHeight >> level == height);
598
599 bool sizeOkay = ((widthOkay && heightOkay)
600 || (widthOkay && mHeight >> level == 0 && height == 1)
601 || (heightOkay && mWidth >> level == 0 && width == 1));
602
603 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0].format);
604
605 if (!textureOkay)
606 {
607 TRACE("Redefining 2D texture (%d, 0x%04X, %d, %d => 0x%04X, %d, %d).", level,
608 mImageArray[0].format, mWidth, mHeight,
609 internalFormat, width, height);
610
611 // Purge all the levels and the texture.
612
613 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
614 {
615 if (mImageArray[i].surface != NULL)
616 {
617 mImageArray[i].dirty = false;
618
619 mImageArray[i].surface->Release();
620 mImageArray[i].surface = NULL;
621 }
622 }
623
624 if (mTexture != NULL)
625 {
626 mTexture->Release();
627 mTexture = NULL;
628 dropTexture();
629 }
630
631 mWidth = width << level;
632 mHeight = height << level;
633 mImageArray[0].format = internalFormat;
634 }
635
636 return !textureOkay;
637}
638
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000639void 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 +0000640{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000641 redefineTexture(level, internalFormat, width, height);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000642
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000643 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000644}
645
daniel@transgaming.com01868132010-08-24 19:21:17 +0000646void Texture2D::setCompressedImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
647{
648 redefineTexture(level, internalFormat, width, height);
649
650 Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[level]);
651}
652
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000653void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
654{
655 ASSERT(mImageArray[level].surface != NULL);
656
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000657 if (level < levelCount())
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000658 {
659 IDirect3DSurface9 *destLevel = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000660 HRESULT result = mTexture->GetSurfaceLevel(level, &destLevel);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000661
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000662 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000663
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000664 if (SUCCEEDED(result))
665 {
666 Image *img = &mImageArray[level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000667
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000668 RECT sourceRect;
669 sourceRect.left = xoffset;
670 sourceRect.top = yoffset;
671 sourceRect.right = xoffset + width;
672 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000673
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000674 POINT destPoint;
675 destPoint.x = xoffset;
676 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000677
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000678 result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
679 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000680
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000681 destLevel->Release();
682
683 img->dirty = false;
684 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000685 }
686}
687
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000688void 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 +0000689{
daniel@transgaming.com31273552010-08-04 13:42:44 +0000690 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
691 {
692 commitRect(level, xoffset, yoffset, width, height);
693 }
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000694}
695
daniel@transgaming.com01868132010-08-24 19:21:17 +0000696void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
697{
698 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
699 {
700 commitRect(level, xoffset, yoffset, width, height);
701 }
702}
703
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000704void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000705{
706 if (redefineTexture(level, internalFormat, width, height))
707 {
708 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000709 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000710 }
daniel@transgaming.combc3699d2010-08-05 14:48:49 +0000711 else
712 {
713 needRenderTarget();
714 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000715
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000716 if (width != 0 && height != 0 && level < levelCount())
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000717 {
718 RECT sourceRect;
719 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000720 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +0000721 sourceRect.top = y;
722 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000723
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000724 IDirect3DSurface9 *dest;
725 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000726
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000727 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
728 dest->Release();
729 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000730
731 mImageArray[level].width = width;
732 mImageArray[level].height = height;
733 mImageArray[level].format = internalFormat;
734}
735
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +0000736void Texture2D::copySubImage(GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000737{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000738 if (xoffset + width > mImageArray[level].width || yoffset + height > mImageArray[level].height)
739 {
740 return error(GL_INVALID_VALUE);
741 }
742
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000743 if (redefineTexture(0, mImageArray[0].format, mImageArray[0].width, mImageArray[0].height))
744 {
745 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000746 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000747 }
748 else
749 {
daniel@transgaming.comfc23fe22010-05-05 18:48:17 +0000750 needRenderTarget();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000751 }
752
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000753 if (level < levelCount())
754 {
755 RECT sourceRect;
756 sourceRect.left = x;
757 sourceRect.right = x + width;
758 sourceRect.top = y;
759 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000760
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000761 IDirect3DSurface9 *dest;
762 HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000763
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000764 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0].format, xoffset, yoffset, dest);
765 dest->Release();
766 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000767}
768
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000769// Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
770bool Texture2D::isComplete() const
771{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000772 GLsizei width = mImageArray[0].width;
773 GLsizei height = mImageArray[0].height;
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000774
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000775 if (width <= 0 || height <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000776 {
777 return false;
778 }
779
daniel@transgaming.com4c03fa62010-06-24 13:02:16 +0000780 bool mipmapping = false;
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000781
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000782 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000783 {
784 case GL_NEAREST:
785 case GL_LINEAR:
786 mipmapping = false;
787 break;
788 case GL_NEAREST_MIPMAP_NEAREST:
789 case GL_LINEAR_MIPMAP_NEAREST:
790 case GL_NEAREST_MIPMAP_LINEAR:
791 case GL_LINEAR_MIPMAP_LINEAR:
792 mipmapping = true;
793 break;
794 default: UNREACHABLE();
795 }
796
daniel@transgaming.com4c03fa62010-06-24 13:02:16 +0000797 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
798 || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
799 {
800 return false;
801 }
802
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000803 if (mipmapping)
804 {
daniel@transgaming.com4c03fa62010-06-24 13:02:16 +0000805 if (!isPow2(width) || !isPow2(height))
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000806 {
807 return false;
808 }
809
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000810 int q = log2(std::max(width, height));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000811
812 for (int level = 1; level <= q; level++)
813 {
814 if (mImageArray[level].format != mImageArray[0].format)
815 {
816 return false;
817 }
818
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000819 if (mImageArray[level].width != std::max(1, width >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000820 {
821 return false;
822 }
823
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000824 if (mImageArray[level].height != std::max(1, height >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000825 {
826 return false;
827 }
828 }
829 }
830
831 return true;
832}
833
daniel@transgaming.com01868132010-08-24 19:21:17 +0000834bool Texture2D::isCompressed() const
835{
836 return IsCompressed(getFormat());
837}
838
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000839// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000840IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000841{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000842 IDirect3DTexture9 *texture;
843
844 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000845 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000846
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000847 HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000848
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000849 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000850 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000851 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000852 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000853 }
854
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000855 if (mTexture) mTexture->Release();
856 mTexture = texture;
857 return texture;
858}
859
860void Texture2D::updateTexture()
861{
862 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000863
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000864 int levels = levelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000865
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000866 for (int level = 0; level < levels; level++)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000867 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000868 if (mImageArray[level].dirty)
869 {
870 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000871 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000872
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000873 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000874
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000875 if (SUCCEEDED(result))
876 {
877 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
878 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000879
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000880 levelSurface->Release();
881
882 mImageArray[level].dirty = false;
883 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000884 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000885 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000886}
887
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000888IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000889{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000890 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000891
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000892 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000893 {
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000894 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000895 IDirect3DDevice9 *device = getDevice();
896 D3DFORMAT format = selectFormat(mImageArray[0].format);
897
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000898 HRESULT result = device->CreateTexture(mWidth, mHeight, creationLevels(mWidth, mHeight, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000899
900 if (FAILED(result))
901 {
902 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
903 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
904 }
905
906 if (mTexture != NULL)
907 {
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +0000908 int levels = levelCount();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000909 for (int i = 0; i < levels; i++)
910 {
911 IDirect3DSurface9 *source;
912 result = mTexture->GetSurfaceLevel(i, &source);
913
914 if (FAILED(result))
915 {
916 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
917
918 texture->Release();
919
920 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
921 }
922
923 IDirect3DSurface9 *dest;
924 result = texture->GetSurfaceLevel(i, &dest);
925
926 if (FAILED(result))
927 {
928 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
929
930 texture->Release();
931 source->Release();
932
933 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
934 }
935
daniel@transgaming.comae072af2010-05-05 18:47:28 +0000936 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000937 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
938
939 if (FAILED(result))
940 {
941 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
942
943 texture->Release();
944 source->Release();
945 dest->Release();
946
947 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
948 }
949
950 source->Release();
951 dest->Release();
952 }
953 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000954 }
955
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000956 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000957 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000958 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000959 }
960
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000961 mTexture = texture;
962 return mTexture;
963}
964
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000965bool Texture2D::dirtyImageData() const
966{
967 int q = log2(std::max(mWidth, mHeight));
968
969 for (int i = 0; i <= q; i++)
970 {
971 if (mImageArray[i].dirty) return true;
972 }
973
974 return false;
975}
976
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000977void Texture2D::generateMipmaps()
978{
979 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
980 {
981 return error(GL_INVALID_OPERATION);
982 }
983
984 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
985 unsigned int q = log2(std::max(mWidth, mHeight));
986 for (unsigned int i = 1; i <= q; i++)
987 {
988 if (mImageArray[i].surface != NULL)
989 {
990 mImageArray[i].surface->Release();
991 mImageArray[i].surface = NULL;
992 }
993
994 mImageArray[i].dirty = false;
995
996 mImageArray[i].format = mImageArray[0].format;
997 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
998 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
999 }
1000
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001001 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001002
1003 for (unsigned int i = 1; i <= q; i++)
1004 {
1005 IDirect3DSurface9 *upper = NULL;
1006 IDirect3DSurface9 *lower = NULL;
1007
1008 mTexture->GetSurfaceLevel(i-1, &upper);
1009 mTexture->GetSurfaceLevel(i, &lower);
1010
1011 if (upper != NULL && lower != NULL)
1012 {
1013 getBlitter()->boxFilter(upper, lower);
1014 }
1015
1016 if (upper != NULL) upper->Release();
1017 if (lower != NULL) lower->Release();
1018 }
1019}
1020
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001021Renderbuffer *Texture2D::getColorbuffer(GLenum target)
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001022{
1023 if (target != GL_TEXTURE_2D)
1024 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001025 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001026 }
1027
1028 if (mColorbufferProxy == NULL)
1029 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001030 mColorbufferProxy = new Renderbuffer(id(), new TextureColorbufferProxy(this, target));
1031 mColorbufferProxy->addRef();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001032 }
1033
1034 return mColorbufferProxy;
1035}
1036
1037IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
1038{
1039 ASSERT(target == GL_TEXTURE_2D);
1040
1041 needRenderTarget();
1042
1043 IDirect3DSurface9 *renderTarget = NULL;
1044 mTexture->GetSurfaceLevel(0, &renderTarget);
1045
1046 return renderTarget;
1047}
1048
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001049TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001050{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001051 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001052
1053 for (int i = 0; i < 6; i++)
1054 {
1055 mFaceProxies[i] = NULL;
1056 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001057}
1058
1059TextureCubeMap::~TextureCubeMap()
1060{
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001061 for (int i = 0; i < 6; i++)
1062 {
1063 delete mFaceProxies[i];
1064 }
1065
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001066 if (mTexture)
1067 {
1068 mTexture->Release();
1069 mTexture = NULL;
1070 }
1071}
1072
1073GLenum TextureCubeMap::getTarget() const
1074{
1075 return GL_TEXTURE_CUBE_MAP;
1076}
1077
daniel@transgaming.com01868132010-08-24 19:21:17 +00001078GLenum TextureCubeMap::getFormat() const
1079{
1080 return mImageArray[0][0].format;
1081}
1082
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001083void 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 +00001084{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001085 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001086}
1087
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001088void 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 +00001089{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001090 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001091}
1092
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001093void 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 +00001094{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001095 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001096}
1097
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001098void 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 +00001099{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001100 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001101}
1102
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001103void 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 +00001104{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001105 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001106}
1107
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001108void 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 +00001109{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001110 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001111}
1112
daniel@transgaming.com01868132010-08-24 19:21:17 +00001113void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1114{
1115 redefineTexture(level, internalFormat, width);
1116
1117 Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[faceIndex(face)][level]);
1118}
1119
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001120void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1121{
1122 int face = faceIndex(faceTarget);
1123
1124 ASSERT(mImageArray[face][level].surface != NULL);
1125
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001126 if (level < levelCount())
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001127 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001128 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
1129 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001130
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001131 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001132 {
1133 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001134
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001135 RECT sourceRect;
1136 sourceRect.left = xoffset;
1137 sourceRect.top = yoffset;
1138 sourceRect.right = xoffset + width;
1139 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001140
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001141 POINT destPoint;
1142 destPoint.x = xoffset;
1143 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001144
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001145 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001146 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001147
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001148 destLevel->Release();
1149
1150 img->dirty = false;
1151 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001152 }
1153}
1154
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001155void 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 +00001156{
daniel@transgaming.com31273552010-08-04 13:42:44 +00001157 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]))
1158 {
1159 commitRect(face, level, xoffset, yoffset, width, height);
1160 }
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001161}
1162
daniel@transgaming.com01868132010-08-24 19:21:17 +00001163void TextureCubeMap::subImageCompressed(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1164{
1165 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(face)][level]))
1166 {
1167 commitRect(face, level, xoffset, yoffset, width, height);
1168 }
1169}
1170
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001171// 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 +00001172bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001173{
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001174 int size = mImageArray[0][0].width;
1175
1176 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001177 {
1178 return false;
1179 }
1180
1181 bool mipmapping;
1182
daniel@transgaming.com12d54072010-03-16 06:23:26 +00001183 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001184 {
1185 case GL_NEAREST:
1186 case GL_LINEAR:
1187 mipmapping = false;
1188 break;
1189 case GL_NEAREST_MIPMAP_NEAREST:
1190 case GL_LINEAR_MIPMAP_NEAREST:
1191 case GL_NEAREST_MIPMAP_LINEAR:
1192 case GL_LINEAR_MIPMAP_LINEAR:
1193 mipmapping = true;
1194 break;
1195 default: UNREACHABLE();
1196 }
1197
1198 for (int face = 0; face < 6; face++)
1199 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001200 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001201 {
1202 return false;
1203 }
1204 }
1205
1206 if (mipmapping)
1207 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001208 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
1209 {
1210 return false;
1211 }
1212
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001213 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001214
1215 for (int face = 0; face < 6; face++)
1216 {
1217 for (int level = 1; level <= q; level++)
1218 {
1219 if (mImageArray[face][level].format != mImageArray[0][0].format)
1220 {
1221 return false;
1222 }
1223
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001224 if (mImageArray[face][level].width != std::max(1, size >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001225 {
1226 return false;
1227 }
1228
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001229 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001230 }
1231 }
1232 }
1233
1234 return true;
1235}
1236
daniel@transgaming.com01868132010-08-24 19:21:17 +00001237bool TextureCubeMap::isCompressed() const
1238{
1239 return IsCompressed(getFormat());
1240}
1241
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001242// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001243IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001244{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001245 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001246 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001247
1248 IDirect3DCubeTexture9 *texture;
1249
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001250 HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001251
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001252 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001253 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001254 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001255 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001256 }
1257
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001258 if (mTexture) mTexture->Release();
1259
1260 mTexture = texture;
1261 return mTexture;
1262}
1263
1264void TextureCubeMap::updateTexture()
1265{
1266 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001267
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001268 for (int face = 0; face < 6; face++)
1269 {
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001270 int levels = levelCount();
1271 for (int level = 0; level < levels; level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001272 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001273 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001274
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001275 if (img->dirty)
1276 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001277 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1278 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001279
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001280 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001281 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001282 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001283 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001284
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001285 levelSurface->Release();
1286
1287 img->dirty = false;
1288 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001289 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001290 }
1291 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001292}
1293
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001294IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1295{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001296 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001297
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001298 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001299 {
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001300 egl::Display *display = getDisplay();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001301 IDirect3DDevice9 *device = getDevice();
1302 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1303
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001304 HRESULT result = device->CreateCubeTexture(mWidth, creationLevels(mWidth, 0), D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001305
1306 if (FAILED(result))
1307 {
1308 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1309 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1310 }
1311
1312 if (mTexture != NULL)
1313 {
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001314 int levels = levelCount();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001315 for (int f = 0; f < 6; f++)
1316 {
1317 for (int i = 0; i < levels; i++)
1318 {
1319 IDirect3DSurface9 *source;
1320 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1321
1322 if (FAILED(result))
1323 {
1324 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1325
1326 texture->Release();
1327
1328 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1329 }
1330
1331 IDirect3DSurface9 *dest;
1332 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1333
1334 if (FAILED(result))
1335 {
1336 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1337
1338 texture->Release();
1339 source->Release();
1340
1341 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1342 }
1343
daniel@transgaming.comae072af2010-05-05 18:47:28 +00001344 display->endScene();
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001345 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1346
1347 if (FAILED(result))
1348 {
1349 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1350
1351 texture->Release();
1352 source->Release();
1353 dest->Release();
1354
1355 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1356 }
1357 }
1358 }
1359 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001360 }
1361
1362 if (mTexture != NULL)
1363 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001364 mTexture->Release();
1365 }
1366
1367 mTexture = texture;
1368 return mTexture;
1369}
1370
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001371void 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 +00001372{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001373 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001374
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001375 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001376}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001377
1378unsigned int TextureCubeMap::faceIndex(GLenum face)
1379{
1380 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1381 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1382 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1383 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1384 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1385
1386 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1387}
1388
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001389bool TextureCubeMap::dirtyImageData() const
1390{
1391 int q = log2(mWidth);
1392
1393 for (int f = 0; f < 6; f++)
1394 {
1395 for (int i = 0; i <= q; i++)
1396 {
1397 if (mImageArray[f][i].dirty) return true;
1398 }
1399 }
1400
1401 return false;
1402}
1403
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001404// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1405// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1406// Call this when a particular level of the texture must be defined with a specific format, width and height.
1407//
1408// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1409// a new size for the texture by working backwards from the given size.
1410bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1411{
1412 // Are these settings compatible with level 0?
1413 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1414
1415 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1416
1417 if (!textureOkay)
1418 {
1419 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1420 mImageArray[0][0].format, mImageArray[0][0].width,
1421 internalFormat, width);
1422
1423 // Purge all the levels and the texture.
1424 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1425 {
1426 for (int f = 0; f < 6; f++)
1427 {
1428 if (mImageArray[f][i].surface != NULL)
1429 {
1430 mImageArray[f][i].dirty = false;
1431
1432 mImageArray[f][i].surface->Release();
1433 mImageArray[f][i].surface = NULL;
1434 }
1435 }
1436 }
1437
1438 if (mTexture != NULL)
1439 {
1440 mTexture->Release();
1441 mTexture = NULL;
1442 dropTexture();
1443 }
1444
1445 mWidth = width << level;
1446 mImageArray[0][0].width = width << level;
1447 mHeight = width << level;
1448 mImageArray[0][0].height = width << level;
1449
1450 mImageArray[0][0].format = internalFormat;
1451 }
1452
1453 return !textureOkay;
1454}
1455
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001456void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001457{
1458 unsigned int faceindex = faceIndex(face);
1459
1460 if (redefineTexture(level, internalFormat, width))
1461 {
1462 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001463 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001464 }
daniel@transgaming.combc3699d2010-08-05 14:48:49 +00001465 else
1466 {
1467 needRenderTarget();
1468 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001469
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001470 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001471
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001472 if (width > 0 && level < levelCount())
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001473 {
1474 RECT sourceRect;
1475 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001476 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001477 sourceRect.top = y;
1478 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001479
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001480 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1481
1482 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1483 dest->Release();
1484 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001485
1486 mImageArray[faceindex][level].width = width;
1487 mImageArray[faceindex][level].height = height;
1488 mImageArray[faceindex][level].format = internalFormat;
1489}
1490
1491IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1492{
1493 unsigned int faceIndex;
1494
1495 if (faceIdentifier < 6)
1496 {
1497 faceIndex = faceIdentifier;
1498 }
1499 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1500 {
1501 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1502 }
1503 else
1504 {
1505 UNREACHABLE();
1506 faceIndex = 0;
1507 }
1508
1509 if (mTexture == NULL)
1510 {
1511 UNREACHABLE();
1512 return NULL;
1513 }
1514
1515 IDirect3DSurface9 *surface = NULL;
1516
1517 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1518
1519 return (SUCCEEDED(hr)) ? surface : NULL;
1520}
1521
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001522void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001523{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001524 GLsizei size = mImageArray[faceIndex(face)][level].width;
1525
1526 if (xoffset + width > size || yoffset + height > size)
1527 {
1528 return error(GL_INVALID_VALUE);
1529 }
1530
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001531 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1532 {
1533 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001534 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001535 }
1536 else
1537 {
daniel@transgaming.combc3699d2010-08-05 14:48:49 +00001538 needRenderTarget();
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001539 }
1540
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001541 if (level < levelCount())
1542 {
1543 RECT sourceRect;
1544 sourceRect.left = x;
1545 sourceRect.right = x + width;
1546 sourceRect.top = y;
1547 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001548
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001549 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001550
daniel@transgaming.comc808c5a2010-05-14 17:31:01 +00001551 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1552 dest->Release();
1553 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001554}
1555
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001556bool TextureCubeMap::isCubeComplete() const
1557{
1558 if (mImageArray[0][0].width == 0)
1559 {
1560 return false;
1561 }
1562
1563 for (unsigned int f = 1; f < 6; f++)
1564 {
1565 if (mImageArray[f][0].width != mImageArray[0][0].width
1566 || mImageArray[f][0].format != mImageArray[0][0].format)
1567 {
1568 return false;
1569 }
1570 }
1571
1572 return true;
1573}
1574
1575void TextureCubeMap::generateMipmaps()
1576{
1577 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1578 {
1579 return error(GL_INVALID_OPERATION);
1580 }
1581
1582 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1583 unsigned int q = log2(mImageArray[0][0].width);
1584 for (unsigned int f = 0; f < 6; f++)
1585 {
1586 for (unsigned int i = 1; i <= q; i++)
1587 {
1588 if (mImageArray[f][i].surface != NULL)
1589 {
1590 mImageArray[f][i].surface->Release();
1591 mImageArray[f][i].surface = NULL;
1592 }
1593
1594 mImageArray[f][i].dirty = false;
1595
1596 mImageArray[f][i].format = mImageArray[f][0].format;
1597 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1598 mImageArray[f][i].height = mImageArray[f][i].width;
1599 }
1600 }
1601
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001602 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001603
1604 for (unsigned int f = 0; f < 6; f++)
1605 {
1606 for (unsigned int i = 1; i <= q; i++)
1607 {
1608 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1609 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1610
1611 if (upper != NULL && lower != NULL)
1612 {
1613 getBlitter()->boxFilter(upper, lower);
1614 }
1615
1616 if (upper != NULL) upper->Release();
1617 if (lower != NULL) lower->Release();
1618 }
1619 }
1620}
1621
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001622Renderbuffer *TextureCubeMap::getColorbuffer(GLenum target)
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001623{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001624 if (!IsCubemapTextureTarget(target))
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001625 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001626 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001627 }
1628
1629 unsigned int face = faceIndex(target);
1630
1631 if (mFaceProxies[face] == NULL)
1632 {
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001633 mFaceProxies[face] = new Renderbuffer(id(), new TextureColorbufferProxy(this, target));
1634 mFaceProxies[face]->addRef();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001635 }
1636
1637 return mFaceProxies[face];
1638}
1639
1640IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1641{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001642 ASSERT(IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001643
1644 needRenderTarget();
1645
1646 IDirect3DSurface9 *renderTarget = NULL;
1647 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1648
1649 return renderTarget;
1650}
1651
1652Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1653 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1654{
daniel@transgaming.com19ffc242010-05-04 03:35:21 +00001655 ASSERT(target == GL_TEXTURE_2D || IsCubemapTextureTarget(target));
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001656}
1657
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001658void Texture::TextureColorbufferProxy::addRef() const
1659{
1660 mTexture->addRef();
1661}
1662
1663void Texture::TextureColorbufferProxy::release() const
1664{
1665 mTexture->release();
1666}
1667
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001668IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1669{
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001670 if (mRenderTarget) mRenderTarget->Release();
1671
1672 mRenderTarget = mTexture->getRenderTarget(mTarget);
1673
1674 return mRenderTarget;
1675}
1676
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001677int Texture::TextureColorbufferProxy::getWidth() const
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001678{
daniel@transgaming.com866f3182010-05-20 19:28:22 +00001679 return mTexture->getWidth();
1680}
1681
daniel@transgaming.com9ecb9f92010-07-28 19:21:12 +00001682int Texture::TextureColorbufferProxy::getHeight() const
daniel@transgaming.com866f3182010-05-20 19:28:22 +00001683{
1684 return mTexture->getHeight();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001685}
1686
daniel@transgaming.com01868132010-08-24 19:21:17 +00001687GLenum Texture::TextureColorbufferProxy::getFormat() const
1688{
1689 return mTexture->getFormat();
1690}
1691
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001692}