blob: 4bb017207d73d9db794b37c67bc0b5089549d614 [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.comd99bd452010-04-22 13:35:25 +0000633 if ((getWrapS() != GL_CLAMP_TO_EDGE && !isPow2(width))
634 || (getWrapT() != GL_CLAMP_TO_EDGE && !isPow2(height)))
635 {
636 return false;
637 }
638
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000639 int q = log2(std::max(width, height));
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000640
641 for (int level = 1; level <= q; level++)
642 {
643 if (mImageArray[level].format != mImageArray[0].format)
644 {
645 return false;
646 }
647
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000648 if (mImageArray[level].width != std::max(1, width >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000649 {
650 return false;
651 }
652
daniel@transgaming.comd99bd452010-04-22 13:35:25 +0000653 if (mImageArray[level].height != std::max(1, height >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000654 {
655 return false;
656 }
657 }
658 }
659
660 return true;
661}
662
663// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000664IDirect3DBaseTexture9 *Texture2D::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000665{
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000666 IDirect3DTexture9 *texture;
667
668 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000669 D3DFORMAT format = selectFormat(mImageArray[0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000670
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000671 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000672
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000673 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000674 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000675 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000676 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000677 }
678
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000679 if (mTexture) mTexture->Release();
680 mTexture = texture;
681 return texture;
682}
683
684void Texture2D::updateTexture()
685{
686 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000687
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000688 int levelCount = mTexture->GetLevelCount();
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000689
690 for (int level = 0; level < levelCount; level++)
691 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000692 if (mImageArray[level].dirty)
693 {
694 IDirect3DSurface9 *levelSurface = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000695 HRESULT result = mTexture->GetSurfaceLevel(level, &levelSurface);
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000696
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000697 ASSERT(SUCCEEDED(result));
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000698
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000699 if (SUCCEEDED(result))
700 {
701 result = device->UpdateSurface(mImageArray[level].surface, NULL, levelSurface, NULL);
702 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000703
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000704 levelSurface->Release();
705
706 mImageArray[level].dirty = false;
707 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000708 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000709 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000710}
711
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000712IDirect3DBaseTexture9 *Texture2D::convertToRenderTarget()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000713{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000714 IDirect3DTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000715
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000716 if (mWidth != 0 && mHeight != 0)
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000717 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +0000718 IDirect3DDevice9 *device = getDevice();
719 D3DFORMAT format = selectFormat(mImageArray[0].format);
720
721 HRESULT result = device->CreateTexture(mWidth, mHeight, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
722
723 if (FAILED(result))
724 {
725 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
726 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
727 }
728
729 if (mTexture != NULL)
730 {
731 int levels = texture->GetLevelCount();
732 for (int i = 0; i < levels; i++)
733 {
734 IDirect3DSurface9 *source;
735 result = mTexture->GetSurfaceLevel(i, &source);
736
737 if (FAILED(result))
738 {
739 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
740
741 texture->Release();
742
743 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
744 }
745
746 IDirect3DSurface9 *dest;
747 result = texture->GetSurfaceLevel(i, &dest);
748
749 if (FAILED(result))
750 {
751 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
752
753 texture->Release();
754 source->Release();
755
756 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
757 }
758
759 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
760
761 if (FAILED(result))
762 {
763 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
764
765 texture->Release();
766 source->Release();
767 dest->Release();
768
769 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
770 }
771
772 source->Release();
773 dest->Release();
774 }
775 }
daniel@transgaming.com29d27002010-03-11 19:41:22 +0000776 }
777
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000778 if (mTexture != NULL)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000779 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000780 mTexture->Release();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000781 }
782
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000783 mTexture = texture;
784 return mTexture;
785}
786
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000787bool Texture2D::dirtyImageData() const
788{
789 int q = log2(std::max(mWidth, mHeight));
790
791 for (int i = 0; i <= q; i++)
792 {
793 if (mImageArray[i].dirty) return true;
794 }
795
796 return false;
797}
798
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000799void Texture2D::generateMipmaps()
800{
801 if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
802 {
803 return error(GL_INVALID_OPERATION);
804 }
805
806 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
807 unsigned int q = log2(std::max(mWidth, mHeight));
808 for (unsigned int i = 1; i <= q; i++)
809 {
810 if (mImageArray[i].surface != NULL)
811 {
812 mImageArray[i].surface->Release();
813 mImageArray[i].surface = NULL;
814 }
815
816 mImageArray[i].dirty = false;
817
818 mImageArray[i].format = mImageArray[0].format;
819 mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
820 mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
821 }
822
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000823 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +0000824
825 for (unsigned int i = 1; i <= q; i++)
826 {
827 IDirect3DSurface9 *upper = NULL;
828 IDirect3DSurface9 *lower = NULL;
829
830 mTexture->GetSurfaceLevel(i-1, &upper);
831 mTexture->GetSurfaceLevel(i, &lower);
832
833 if (upper != NULL && lower != NULL)
834 {
835 getBlitter()->boxFilter(upper, lower);
836 }
837
838 if (upper != NULL) upper->Release();
839 if (lower != NULL) lower->Release();
840 }
841}
842
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000843Colorbuffer *Texture2D::getColorbuffer(GLenum target)
844{
845 if (target != GL_TEXTURE_2D)
846 {
847 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
848 }
849
850 if (mColorbufferProxy == NULL)
851 {
852 mColorbufferProxy = new TextureColorbufferProxy(this, target);
853 }
854
855 return mColorbufferProxy;
856}
857
858IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
859{
860 ASSERT(target == GL_TEXTURE_2D);
861
862 needRenderTarget();
863
864 IDirect3DSurface9 *renderTarget = NULL;
865 mTexture->GetSurfaceLevel(0, &renderTarget);
866
867 return renderTarget;
868}
869
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000870TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000871{
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000872 mTexture = NULL;
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000873
874 for (int i = 0; i < 6; i++)
875 {
876 mFaceProxies[i] = NULL;
877 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000878}
879
880TextureCubeMap::~TextureCubeMap()
881{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000882 for (int i = 0; i < 6; i++)
883 {
884 delete mFaceProxies[i];
885 }
886
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000887 if (mTexture)
888 {
889 mTexture->Release();
890 mTexture = NULL;
891 }
892}
893
894GLenum TextureCubeMap::getTarget() const
895{
896 return GL_TEXTURE_CUBE_MAP;
897}
898
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000899void 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 +0000900{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000901 setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000902}
903
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000904void 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 +0000905{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000906 setImage(1, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000907}
908
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000909void 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 +0000910{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000911 setImage(2, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000912}
913
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000914void 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 +0000915{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000916 setImage(3, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000917}
918
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000919void 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 +0000920{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000921 setImage(4, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000922}
923
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000924void 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 +0000925{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000926 setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000927}
928
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000929void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
930{
931 int face = faceIndex(faceTarget);
932
933 ASSERT(mImageArray[face][level].surface != NULL);
934
935 if (mTexture != NULL)
936 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000937 IDirect3DSurface9 *destLevel = getCubeMapSurface(face, level);
938 ASSERT(destLevel != NULL);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000939
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000940 if (destLevel != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000941 {
942 Image *img = &mImageArray[face][level];
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000943
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000944 RECT sourceRect;
945 sourceRect.left = xoffset;
946 sourceRect.top = yoffset;
947 sourceRect.right = xoffset + width;
948 sourceRect.bottom = yoffset + height;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000949
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000950 POINT destPoint;
951 destPoint.x = xoffset;
952 destPoint.y = yoffset;
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000953
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +0000954 HRESULT result = getDevice()->UpdateSurface(img->surface, &sourceRect, destLevel, &destPoint);
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000955 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000956
daniel@transgaming.com7051b972010-03-21 04:31:07 +0000957 destLevel->Release();
958
959 img->dirty = false;
960 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000961 }
962}
963
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000964void 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 +0000965{
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +0000966 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(face)][level]);
daniel@transgaming.com842f7a42010-03-21 04:31:03 +0000967 commitRect(face, level, xoffset, yoffset, width, height);
daniel@transgaming.com00c75962010-03-11 20:36:15 +0000968}
969
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000970// 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 +0000971bool TextureCubeMap::isComplete() const
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000972{
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000973 int size = mImageArray[0][0].width;
974
975 if (size <= 0)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000976 {
977 return false;
978 }
979
980 bool mipmapping;
981
daniel@transgaming.com12d54072010-03-16 06:23:26 +0000982 switch (mMinFilter)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +0000983 {
984 case GL_NEAREST:
985 case GL_LINEAR:
986 mipmapping = false;
987 break;
988 case GL_NEAREST_MIPMAP_NEAREST:
989 case GL_LINEAR_MIPMAP_NEAREST:
990 case GL_NEAREST_MIPMAP_LINEAR:
991 case GL_LINEAR_MIPMAP_LINEAR:
992 mipmapping = true;
993 break;
994 default: UNREACHABLE();
995 }
996
997 for (int face = 0; face < 6; face++)
998 {
daniel@transgaming.com93a81472010-04-20 18:52:58 +0000999 if (mImageArray[face][0].width != size || mImageArray[face][0].height != size)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001000 {
1001 return false;
1002 }
1003 }
1004
1005 if (mipmapping)
1006 {
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001007 if (!isPow2(size) && (getWrapS() != GL_CLAMP_TO_EDGE || getWrapT() != GL_CLAMP_TO_EDGE))
1008 {
1009 return false;
1010 }
1011
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001012 int q = log2(size);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001013
1014 for (int face = 0; face < 6; face++)
1015 {
1016 for (int level = 1; level <= q; level++)
1017 {
1018 if (mImageArray[face][level].format != mImageArray[0][0].format)
1019 {
1020 return false;
1021 }
1022
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001023 if (mImageArray[face][level].width != std::max(1, size >> level))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001024 {
1025 return false;
1026 }
1027
daniel@transgaming.comd99bd452010-04-22 13:35:25 +00001028 ASSERT(mImageArray[face][level].height == mImageArray[face][level].width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001029 }
1030 }
1031 }
1032
1033 return true;
1034}
1035
1036// Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001037IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001038{
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001039 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001040 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001041
1042 IDirect3DCubeTexture9 *texture;
1043
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001044 HRESULT result = device->CreateCubeTexture(mWidth, 0, 0, format, D3DPOOL_DEFAULT, &texture, NULL);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001045
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001046 if (FAILED(result))
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001047 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001048 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001049 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001050 }
1051
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001052 if (mTexture) mTexture->Release();
1053
1054 mTexture = texture;
1055 return mTexture;
1056}
1057
1058void TextureCubeMap::updateTexture()
1059{
1060 IDirect3DDevice9 *device = getDevice();
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001061
daniel@transgaming.com29d27002010-03-11 19:41:22 +00001062 for (int face = 0; face < 6; face++)
1063 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001064 for (int level = 0; level <= log2(mWidth); level++)
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001065 {
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001066 Image *img = &mImageArray[face][level];
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001067
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001068 if (img->dirty)
1069 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001070 IDirect3DSurface9 *levelSurface = getCubeMapSurface(face, level);
1071 ASSERT(levelSurface != NULL);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001072
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001073 if (levelSurface != NULL)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001074 {
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001075 HRESULT result = device->UpdateSurface(img->surface, NULL, levelSurface, NULL);
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001076 ASSERT(SUCCEEDED(result));
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001077
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001078 levelSurface->Release();
1079
1080 img->dirty = false;
1081 }
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001082 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001083 }
1084 }
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001085}
1086
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001087IDirect3DBaseTexture9 *TextureCubeMap::convertToRenderTarget()
1088{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001089 IDirect3DCubeTexture9 *texture = NULL;
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001090
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001091 if (mWidth != 0)
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001092 {
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001093 IDirect3DDevice9 *device = getDevice();
1094 D3DFORMAT format = selectFormat(mImageArray[0][0].format);
1095
1096 HRESULT result = device->CreateCubeTexture(mWidth, 0, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture, NULL);
1097
1098 if (FAILED(result))
1099 {
1100 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1101 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1102 }
1103
1104 if (mTexture != NULL)
1105 {
1106 int levels = texture->GetLevelCount();
1107 for (int f = 0; f < 6; f++)
1108 {
1109 for (int i = 0; i < levels; i++)
1110 {
1111 IDirect3DSurface9 *source;
1112 result = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &source);
1113
1114 if (FAILED(result))
1115 {
1116 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1117
1118 texture->Release();
1119
1120 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1121 }
1122
1123 IDirect3DSurface9 *dest;
1124 result = texture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(f), i, &dest);
1125
1126 if (FAILED(result))
1127 {
1128 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1129
1130 texture->Release();
1131 source->Release();
1132
1133 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1134 }
1135
1136 result = device->StretchRect(source, NULL, dest, NULL, D3DTEXF_NONE);
1137
1138 if (FAILED(result))
1139 {
1140 ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY);
1141
1142 texture->Release();
1143 source->Release();
1144 dest->Release();
1145
1146 return error(GL_OUT_OF_MEMORY, (IDirect3DBaseTexture9*)NULL);
1147 }
1148 }
1149 }
1150 }
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001151 }
1152
1153 if (mTexture != NULL)
1154 {
daniel@transgaming.com7051b972010-03-21 04:31:07 +00001155 mTexture->Release();
1156 }
1157
1158 mTexture = texture;
1159 return mTexture;
1160}
1161
daniel@transgaming.com3489e3a2010-03-21 04:31:11 +00001162void 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 +00001163{
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001164 redefineTexture(level, internalFormat, width);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001165
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001166 Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[face][level]);
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001167}
daniel@transgaming.com00c75962010-03-11 20:36:15 +00001168
1169unsigned int TextureCubeMap::faceIndex(GLenum face)
1170{
1171 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1172 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1173 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1174 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1175 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1176
1177 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1178}
1179
daniel@transgaming.com842f7a42010-03-21 04:31:03 +00001180bool TextureCubeMap::dirtyImageData() const
1181{
1182 int q = log2(mWidth);
1183
1184 for (int f = 0; f < 6; f++)
1185 {
1186 for (int i = 0; i <= q; i++)
1187 {
1188 if (mImageArray[f][i].dirty) return true;
1189 }
1190 }
1191
1192 return false;
1193}
1194
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001195// While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
1196// for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels & faces.
1197// Call this when a particular level of the texture must be defined with a specific format, width and height.
1198//
1199// Returns true if the existing texture was unsuitable had to be destroyed. If so, it will also set
1200// a new size for the texture by working backwards from the given size.
1201bool TextureCubeMap::redefineTexture(GLint level, GLenum internalFormat, GLsizei width)
1202{
1203 // Are these settings compatible with level 0?
1204 bool sizeOkay = (mImageArray[0][0].width >> level == width);
1205
1206 bool textureOkay = (sizeOkay && internalFormat == mImageArray[0][0].format);
1207
1208 if (!textureOkay)
1209 {
1210 TRACE("Redefining cube texture (%d, 0x%04X, %d => 0x%04X, %d).", level,
1211 mImageArray[0][0].format, mImageArray[0][0].width,
1212 internalFormat, width);
1213
1214 // Purge all the levels and the texture.
1215 for (int i = 0; i < MAX_TEXTURE_LEVELS; i++)
1216 {
1217 for (int f = 0; f < 6; f++)
1218 {
1219 if (mImageArray[f][i].surface != NULL)
1220 {
1221 mImageArray[f][i].dirty = false;
1222
1223 mImageArray[f][i].surface->Release();
1224 mImageArray[f][i].surface = NULL;
1225 }
1226 }
1227 }
1228
1229 if (mTexture != NULL)
1230 {
1231 mTexture->Release();
1232 mTexture = NULL;
1233 dropTexture();
1234 }
1235
1236 mWidth = width << level;
1237 mImageArray[0][0].width = width << level;
1238 mHeight = width << level;
1239 mImageArray[0][0].height = width << level;
1240
1241 mImageArray[0][0].format = internalFormat;
1242 }
1243
1244 return !textureOkay;
1245}
1246
1247void TextureCubeMap::copyImage(GLenum face, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1248{
1249 unsigned int faceindex = faceIndex(face);
1250
1251 if (redefineTexture(level, internalFormat, width))
1252 {
1253 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001254 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001255 }
1256
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001257 ASSERT(width == height);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001258
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001259 if (width > 0)
1260 {
1261 RECT sourceRect;
1262 sourceRect.left = x;
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001263 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001264 sourceRect.top = y;
1265 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001266
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001267 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1268
1269 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, internalFormat, 0, 0, dest);
1270 dest->Release();
1271 }
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001272
1273 mImageArray[faceindex][level].width = width;
1274 mImageArray[faceindex][level].height = height;
1275 mImageArray[faceindex][level].format = internalFormat;
1276}
1277
1278IDirect3DSurface9 *TextureCubeMap::getCubeMapSurface(unsigned int faceIdentifier, unsigned int level)
1279{
1280 unsigned int faceIndex;
1281
1282 if (faceIdentifier < 6)
1283 {
1284 faceIndex = faceIdentifier;
1285 }
1286 else if (faceIdentifier >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && faceIdentifier <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
1287 {
1288 faceIndex = faceIdentifier - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1289 }
1290 else
1291 {
1292 UNREACHABLE();
1293 faceIndex = 0;
1294 }
1295
1296 if (mTexture == NULL)
1297 {
1298 UNREACHABLE();
1299 return NULL;
1300 }
1301
1302 IDirect3DSurface9 *surface = NULL;
1303
1304 HRESULT hr = mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex), level, &surface);
1305
1306 return (SUCCEEDED(hr)) ? surface : NULL;
1307}
1308
1309void TextureCubeMap::copySubImage(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1310{
daniel@transgaming.com34dc3e82010-04-15 20:45:02 +00001311 GLsizei size = mImageArray[faceIndex(face)][level].width;
1312
1313 if (xoffset + width > size || yoffset + height > size)
1314 {
1315 return error(GL_INVALID_VALUE);
1316 }
1317
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001318 if (redefineTexture(0, mImageArray[0][0].format, mImageArray[0][0].width))
1319 {
1320 convertToRenderTarget();
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001321 pushTexture(mTexture, true);
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001322 }
1323 else
1324 {
1325 getRenderTarget(face);
1326 }
1327
1328 RECT sourceRect;
1329 sourceRect.left = x;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001330 sourceRect.right = x + width;
daniel@transgaming.com18b426b2010-04-20 18:52:44 +00001331 sourceRect.top = y;
1332 sourceRect.bottom = y + height;
daniel@transgaming.comb8c28ed2010-04-13 03:26:32 +00001333
1334 IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
1335
1336 getBlitter()->formatConvert(source->getRenderTarget(), sourceRect, mImageArray[0][0].format, xoffset, yoffset, dest);
1337 dest->Release();
1338}
1339
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001340bool TextureCubeMap::isCubeComplete() const
1341{
1342 if (mImageArray[0][0].width == 0)
1343 {
1344 return false;
1345 }
1346
1347 for (unsigned int f = 1; f < 6; f++)
1348 {
1349 if (mImageArray[f][0].width != mImageArray[0][0].width
1350 || mImageArray[f][0].format != mImageArray[0][0].format)
1351 {
1352 return false;
1353 }
1354 }
1355
1356 return true;
1357}
1358
1359void TextureCubeMap::generateMipmaps()
1360{
1361 if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
1362 {
1363 return error(GL_INVALID_OPERATION);
1364 }
1365
1366 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1367 unsigned int q = log2(mImageArray[0][0].width);
1368 for (unsigned int f = 0; f < 6; f++)
1369 {
1370 for (unsigned int i = 1; i <= q; i++)
1371 {
1372 if (mImageArray[f][i].surface != NULL)
1373 {
1374 mImageArray[f][i].surface->Release();
1375 mImageArray[f][i].surface = NULL;
1376 }
1377
1378 mImageArray[f][i].dirty = false;
1379
1380 mImageArray[f][i].format = mImageArray[f][0].format;
1381 mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
1382 mImageArray[f][i].height = mImageArray[f][i].width;
1383 }
1384 }
1385
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001386 needRenderTarget();
daniel@transgaming.com8fd99e22010-04-20 18:52:00 +00001387
1388 for (unsigned int f = 0; f < 6; f++)
1389 {
1390 for (unsigned int i = 1; i <= q; i++)
1391 {
1392 IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
1393 IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
1394
1395 if (upper != NULL && lower != NULL)
1396 {
1397 getBlitter()->boxFilter(upper, lower);
1398 }
1399
1400 if (upper != NULL) upper->Release();
1401 if (lower != NULL) lower->Release();
1402 }
1403 }
1404}
1405
daniel@transgaming.com93a81472010-04-20 18:52:58 +00001406Colorbuffer *TextureCubeMap::getColorbuffer(GLenum target)
1407{
1408 if (!es2dx::IsCubemapTextureTarget(target))
1409 {
1410 return error(GL_INVALID_OPERATION, (Colorbuffer *)NULL);
1411 }
1412
1413 unsigned int face = faceIndex(target);
1414
1415 if (mFaceProxies[face] == NULL)
1416 {
1417 mFaceProxies[face] = new TextureColorbufferProxy(this, target);
1418 }
1419
1420 return mFaceProxies[face];
1421}
1422
1423IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1424{
1425 ASSERT(es2dx::IsCubemapTextureTarget(target));
1426
1427 needRenderTarget();
1428
1429 IDirect3DSurface9 *renderTarget = NULL;
1430 mTexture->GetCubeMapSurface(static_cast<D3DCUBEMAP_FACES>(faceIndex(target)), 0, &renderTarget);
1431
1432 return renderTarget;
1433}
1434
1435Texture::TextureColorbufferProxy::TextureColorbufferProxy(Texture *texture, GLenum target)
1436 : Colorbuffer(NULL), mTexture(texture), mTarget(target)
1437{
1438 ASSERT(target == GL_TEXTURE_2D || es2dx::IsCubemapTextureTarget(target));
1439 latchTextureInfo();
1440}
1441
1442IDirect3DSurface9 *Texture::TextureColorbufferProxy::getRenderTarget()
1443{
1444 latchTextureInfo();
1445
1446 if (mRenderTarget) mRenderTarget->Release();
1447
1448 mRenderTarget = mTexture->getRenderTarget(mTarget);
1449
1450 return mRenderTarget;
1451}
1452
1453void Texture::TextureColorbufferProxy::latchTextureInfo()
1454{
1455 mWidth = mTexture->getWidth();
1456 mHeight = mTexture->getHeight();
1457}
1458
daniel@transgaming.com4f39fd92010-03-08 20:26:45 +00001459}