blob: 2f48f527c8041ee564b5b96a2d79eba3cecfb07b [file] [log] [blame]
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001//
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00002// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Texture.cpp: Implements the gl::Texture class and its derived classes
8// Texture2D and TextureCubeMap. Implements GL texture objects and related
9// functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
10
11#include "libGLESv2/Texture.h"
12
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000013#include <algorithm>
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000014
15#include "common/debug.h"
16
17#include "libEGL/Display.h"
18
19#include "libGLESv2/main.h"
20#include "libGLESv2/mathutil.h"
21#include "libGLESv2/utilities.h"
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +000022#include "libGLESv2/renderer/Blit.h"
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +000023#include "libGLESv2/renderer/SwapChain9.h"
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000024#include "libGLESv2/Framebuffer.h"
25
26namespace gl
27{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000028
daniel@transgaming.com370482e2012-11-28 19:32:13 +000029Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000030{
daniel@transgaming.com370482e2012-11-28 19:32:13 +000031 mRenderer = renderer;
32
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000033 mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR;
34 mSamplerState.magFilter = GL_LINEAR;
35 mSamplerState.wrapS = GL_REPEAT;
36 mSamplerState.wrapT = GL_REPEAT;
37 mSamplerState.maxAnisotropy = 1.0f;
38 mSamplerState.lodOffset = 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000039 mDirtyParameters = true;
40 mUsage = GL_NONE;
41
42 mDirtyImages = true;
43
44 mImmutable = false;
45}
46
47Texture::~Texture()
48{
49}
50
51// Returns true on successful filter state update (valid enum parameter)
52bool Texture::setMinFilter(GLenum filter)
53{
54 switch (filter)
55 {
56 case GL_NEAREST:
57 case GL_LINEAR:
58 case GL_NEAREST_MIPMAP_NEAREST:
59 case GL_LINEAR_MIPMAP_NEAREST:
60 case GL_NEAREST_MIPMAP_LINEAR:
61 case GL_LINEAR_MIPMAP_LINEAR:
62 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000063 if (mSamplerState.minFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000064 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000065 mSamplerState.minFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000066 mDirtyParameters = true;
67 }
68 return true;
69 }
70 default:
71 return false;
72 }
73}
74
75// Returns true on successful filter state update (valid enum parameter)
76bool Texture::setMagFilter(GLenum filter)
77{
78 switch (filter)
79 {
80 case GL_NEAREST:
81 case GL_LINEAR:
82 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000083 if (mSamplerState.magFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000084 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000085 mSamplerState.magFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000086 mDirtyParameters = true;
87 }
88 return true;
89 }
90 default:
91 return false;
92 }
93}
94
95// Returns true on successful wrap state update (valid enum parameter)
96bool Texture::setWrapS(GLenum wrap)
97{
98 switch (wrap)
99 {
100 case GL_REPEAT:
101 case GL_CLAMP_TO_EDGE:
102 case GL_MIRRORED_REPEAT:
103 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000104 if (mSamplerState.wrapS != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000105 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000106 mSamplerState.wrapS = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000107 mDirtyParameters = true;
108 }
109 return true;
110 }
111 default:
112 return false;
113 }
114}
115
116// Returns true on successful wrap state update (valid enum parameter)
117bool Texture::setWrapT(GLenum wrap)
118{
119 switch (wrap)
120 {
121 case GL_REPEAT:
122 case GL_CLAMP_TO_EDGE:
123 case GL_MIRRORED_REPEAT:
124 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000125 if (mSamplerState.wrapT != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000126 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000127 mSamplerState.wrapT = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000128 mDirtyParameters = true;
129 }
130 return true;
131 }
132 default:
133 return false;
134 }
135}
136
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000137// Returns true on successful max anisotropy update (valid anisotropy value)
138bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
139{
140 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
141 if (textureMaxAnisotropy < 1.0f)
142 {
143 return false;
144 }
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000145 if (mSamplerState.maxAnisotropy != textureMaxAnisotropy)
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000146 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000147 mSamplerState.maxAnisotropy = textureMaxAnisotropy;
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000148 mDirtyParameters = true;
149 }
150 return true;
151}
152
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000153// Returns true on successful usage state update (valid enum parameter)
154bool Texture::setUsage(GLenum usage)
155{
156 switch (usage)
157 {
158 case GL_NONE:
159 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
160 mUsage = usage;
161 return true;
162 default:
163 return false;
164 }
165}
166
167GLenum Texture::getMinFilter() const
168{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000169 return mSamplerState.minFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000170}
171
172GLenum Texture::getMagFilter() const
173{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000174 return mSamplerState.magFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000175}
176
177GLenum Texture::getWrapS() const
178{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000179 return mSamplerState.wrapS;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000180}
181
182GLenum Texture::getWrapT() const
183{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000184 return mSamplerState.wrapT;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000185}
186
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000187float Texture::getMaxAnisotropy() const
188{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000189 return mSamplerState.maxAnisotropy;
190}
191
192int Texture::getLodOffset()
193{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000194 rx::TextureStorage *texture = getStorage(false);
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000195 return texture ? texture->getLodOffset() : 0;
196}
197
198void Texture::getSamplerState(SamplerState *sampler)
199{
200 *sampler = mSamplerState;
201 sampler->lodOffset = getLodOffset();
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000202}
203
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000204GLenum Texture::getUsage() const
205{
206 return mUsage;
207}
208
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000209bool Texture::isMipmapFiltered() const
210{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000211 switch (mSamplerState.minFilter)
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000212 {
213 case GL_NEAREST:
214 case GL_LINEAR:
215 return false;
216 case GL_NEAREST_MIPMAP_NEAREST:
217 case GL_LINEAR_MIPMAP_NEAREST:
218 case GL_NEAREST_MIPMAP_LINEAR:
219 case GL_LINEAR_MIPMAP_LINEAR:
220 return true;
221 default: UNREACHABLE();
222 return false;
223 }
224}
225
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000226void Texture::setImage(GLint unpackAlignment, const void *pixels, rx::Image *image)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000227{
228 if (pixels != NULL)
229 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000230 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000231 mDirtyImages = true;
232 }
233}
234
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000235void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, rx::Image *image)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000236{
237 if (pixels != NULL)
238 {
239 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
240 mDirtyImages = true;
241 }
242}
243
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000244bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, rx::Image *image)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000245{
246 if (pixels != NULL)
247 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000248 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000249 mDirtyImages = true;
250 }
251
252 return true;
253}
254
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000255bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000256{
257 if (pixels != NULL)
258 {
259 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
260 mDirtyImages = true;
261 }
262
263 return true;
264}
265
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000266rx::TextureStorage *Texture::getNativeTexture()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000267{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000268 // ensure the underlying texture is created
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000269
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000270 rx::TextureStorage *storage = getStorage(false);
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000271 if (storage)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000272 {
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000273 updateTexture();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000274 }
275
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000276 return storage;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000277}
278
279bool Texture::hasDirtyParameters() const
280{
281 return mDirtyParameters;
282}
283
284bool Texture::hasDirtyImages() const
285{
286 return mDirtyImages;
287}
288
289void Texture::resetDirty()
290{
291 mDirtyParameters = false;
292 mDirtyImages = false;
293}
294
295unsigned int Texture::getTextureSerial()
296{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000297 rx::TextureStorage *texture = getStorage(false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000298 return texture ? texture->getTextureSerial() : 0;
299}
300
301unsigned int Texture::getRenderTargetSerial(GLenum target)
302{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000303 rx::TextureStorage *texture = getStorage(true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000304 return texture ? texture->getRenderTargetSerial(target) : 0;
305}
306
307bool Texture::isImmutable() const
308{
309 return mImmutable;
310}
311
312GLint Texture::creationLevels(GLsizei width, GLsizei height) const
313{
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000314 if ((isPow2(width) && isPow2(height)) || mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000315 {
316 return 0; // Maximum number of levels
317 }
318 else
319 {
320 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
321 return 1;
322 }
323}
324
325GLint Texture::creationLevels(GLsizei size) const
326{
327 return creationLevels(size, size);
328}
329
daniel@transgaming.com370482e2012-11-28 19:32:13 +0000330Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000331{
332 mTexStorage = NULL;
333 mSurface = NULL;
334 mColorbufferProxy = NULL;
335 mProxyRefs = 0;
336}
337
338Texture2D::~Texture2D()
339{
340 mColorbufferProxy = NULL;
341
342 delete mTexStorage;
343 mTexStorage = NULL;
344
345 if (mSurface)
346 {
347 mSurface->setBoundTexture(NULL);
348 mSurface = NULL;
349 }
350}
351
352// We need to maintain a count of references to renderbuffers acting as
353// proxies for this texture, so that we do not attempt to use a pointer
354// to a renderbuffer proxy which has been deleted.
355void Texture2D::addProxyRef(const Renderbuffer *proxy)
356{
357 mProxyRefs++;
358}
359
360void Texture2D::releaseProxy(const Renderbuffer *proxy)
361{
362 if (mProxyRefs > 0)
363 mProxyRefs--;
364
365 if (mProxyRefs == 0)
366 mColorbufferProxy = NULL;
367}
368
369GLenum Texture2D::getTarget() const
370{
371 return GL_TEXTURE_2D;
372}
373
374GLsizei Texture2D::getWidth(GLint level) const
375{
376 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
377 return mImageArray[level].getWidth();
378 else
379 return 0;
380}
381
382GLsizei Texture2D::getHeight(GLint level) const
383{
384 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
385 return mImageArray[level].getHeight();
386 else
387 return 0;
388}
389
390GLenum Texture2D::getInternalFormat(GLint level) const
391{
392 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000393 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000394 else
395 return GL_NONE;
396}
397
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000398GLenum Texture2D::getActualFormat(GLint level) const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000399{
400 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000401 return mImageArray[level].getActualFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000402 else
403 return D3DFMT_UNKNOWN;
404}
405
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000406void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000407{
408 releaseTexImage();
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000409 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
410 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
411 bool redefined = mImageArray[level].redefine(renderer9, internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000412
413 if (mTexStorage && redefined)
414 {
415 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
416 {
417 mImageArray[i].markDirty();
418 }
419
420 delete mTexStorage;
421 mTexStorage = NULL;
422 mDirtyImages = true;
423 }
424}
425
426void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
427{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000428 GLint internalformat = ConvertSizedInternalFormat(format, type);
429 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000430
431 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
432}
433
434void Texture2D::bindTexImage(egl::Surface *surface)
435{
436 releaseTexImage();
437
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000438 GLint internalformat = surface->getFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000439
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000440 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
441 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
442 mImageArray[0].redefine(renderer9, internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000443
444 delete mTexStorage;
daniel@transgaming.coma27e05b2012-11-28 19:39:42 +0000445 rx::SwapChain9 *swapchain = static_cast<rx::SwapChain9*>(surface->getSwapChain()); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000446 mTexStorage = new rx::TextureStorage2D(renderer9, swapchain);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000447
448 mDirtyImages = true;
449 mSurface = surface;
450 mSurface->setBoundTexture(this);
451}
452
453void Texture2D::releaseTexImage()
454{
455 if (mSurface)
456 {
457 mSurface->setBoundTexture(NULL);
458 mSurface = NULL;
459
460 if (mTexStorage)
461 {
462 delete mTexStorage;
463 mTexStorage = NULL;
464 }
465
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000466 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
467 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000468 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
469 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000470 mImageArray[i].redefine(renderer9, GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000471 }
472 }
473}
474
475void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
476{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000477 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
478 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000479
480 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
481}
482
483void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
484{
485 ASSERT(mImageArray[level].getSurface() != NULL);
486
487 if (level < levelCount())
488 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000489 rx::Image *image = &mImageArray[level];
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000490 if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000491 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000492 image->markClean();
493 }
494 }
495}
496
497void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
498{
499 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
500 {
501 commitRect(level, xoffset, yoffset, width, height);
502 }
503}
504
505void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
506{
507 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
508 {
509 commitRect(level, xoffset, yoffset, width, height);
510 }
511}
512
513void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
514{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000515 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
516 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000517
518 if (!mImageArray[level].isRenderableFormat())
519 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000520 mImageArray[level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000521 mDirtyImages = true;
522 }
523 else
524 {
525 if (!mTexStorage || !mTexStorage->isRenderTarget())
526 {
527 convertToRenderTarget();
528 }
529
530 mImageArray[level].markClean();
531
532 if (width != 0 && height != 0 && level < levelCount())
533 {
534 RECT sourceRect;
535 sourceRect.left = x;
536 sourceRect.right = x + width;
537 sourceRect.top = y;
538 sourceRect.bottom = y + height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000539
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +0000540 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level);
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000541
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000542 }
543 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000544}
545
546void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
547{
548 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
549 {
550 return error(GL_INVALID_VALUE);
551 }
552
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000553 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
554 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000555 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000556 mDirtyImages = true;
557 }
558 else
559 {
560 if (!mTexStorage || !mTexStorage->isRenderTarget())
561 {
562 convertToRenderTarget();
563 }
564
565 updateTexture();
566
567 if (level < levelCount())
568 {
569 RECT sourceRect;
570 sourceRect.left = x;
571 sourceRect.right = x + width;
572 sourceRect.top = y;
573 sourceRect.bottom = y + height;
574
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +0000575 mRenderer->copyImage(source, sourceRect,
576 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
577 xoffset, yoffset, mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000578 }
579 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000580}
581
582void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
583{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000584 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000585 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
586 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000587 mTexStorage = new rx::TextureStorage2D(renderer9, levels, internalformat, mUsage, false, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000588 mImmutable = true;
589
590 for (int level = 0; level < levels; level++)
591 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000592 mImageArray[level].redefine(renderer9, internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000593 width = std::max(1, width >> 1);
594 height = std::max(1, height >> 1);
595 }
596
597 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
598 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000599 mImageArray[level].redefine(renderer9, GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000600 }
601
602 if (mTexStorage->isManaged())
603 {
604 int levels = levelCount();
605
606 for (int level = 0; level < levels; level++)
607 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000608 mImageArray[level].setManagedSurface(mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000609 }
610 }
611}
612
613// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
614bool Texture2D::isSamplerComplete() const
615{
616 GLsizei width = mImageArray[0].getWidth();
617 GLsizei height = mImageArray[0].getHeight();
618
619 if (width <= 0 || height <= 0)
620 {
621 return false;
622 }
623
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000624 bool mipmapping = isMipmapFiltered();
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000625 bool filtering, renderable;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000626
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000627 if ((IsFloat32Format(getInternalFormat(0)) && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) ||
628 (IsFloat16Format(getInternalFormat(0)) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable)))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000629 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000630 if (mSamplerState.magFilter != GL_NEAREST ||
631 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000632 {
633 return false;
634 }
635 }
636
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000637 bool npotSupport = mRenderer->getNonPower2TextureSupport();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000638
639 if (!npotSupport)
640 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000641 if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
642 (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height)))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000643 {
644 return false;
645 }
646 }
647
648 if (mipmapping)
649 {
650 if (!npotSupport)
651 {
652 if (!isPow2(width) || !isPow2(height))
653 {
654 return false;
655 }
656 }
657
658 if (!isMipmapComplete())
659 {
660 return false;
661 }
662 }
663
664 return true;
665}
666
667// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
668bool Texture2D::isMipmapComplete() const
669{
670 if (isImmutable())
671 {
672 return true;
673 }
674
675 GLsizei width = mImageArray[0].getWidth();
676 GLsizei height = mImageArray[0].getHeight();
677
678 if (width <= 0 || height <= 0)
679 {
680 return false;
681 }
682
683 int q = log2(std::max(width, height));
684
685 for (int level = 1; level <= q; level++)
686 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000687 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000688 {
689 return false;
690 }
691
692 if (mImageArray[level].getWidth() != std::max(1, width >> level))
693 {
694 return false;
695 }
696
697 if (mImageArray[level].getHeight() != std::max(1, height >> level))
698 {
699 return false;
700 }
701 }
702
703 return true;
704}
705
706bool Texture2D::isCompressed(GLint level) const
707{
708 return IsCompressed(getInternalFormat(level));
709}
710
711bool Texture2D::isDepth(GLint level) const
712{
713 return IsDepthTexture(getInternalFormat(level));
714}
715
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000716// Constructs a native texture resource from the texture images
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000717void Texture2D::createTexture()
718{
719 GLsizei width = mImageArray[0].getWidth();
720 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +0000721
722 if (!(width > 0 && height > 0))
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000723 return; // do not attempt to create native textures for nonexistant data
daniel@transgaming.come6a09842012-09-17 21:28:55 +0000724
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000725 GLint levels = creationLevels(width, height);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000726 GLenum internalformat = mImageArray[0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000727
728 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000729 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
730 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000731 mTexStorage = new rx::TextureStorage2D(renderer9, levels, internalformat, mUsage, false, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000732
733 if (mTexStorage->isManaged())
734 {
735 int levels = levelCount();
736
737 for (int level = 0; level < levels; level++)
738 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000739 mImageArray[level].setManagedSurface(mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000740 }
741 }
742
743 mDirtyImages = true;
744}
745
746void Texture2D::updateTexture()
747{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000748 bool mipmapping = (isMipmapFiltered() && isMipmapComplete());
749
750 int levels = (mipmapping ? levelCount() : 1);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000751
752 for (int level = 0; level < levels; level++)
753 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000754 rx::Image *image = &mImageArray[level];
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000755
756 if (image->isDirty())
757 {
758 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
759 }
760 }
761}
762
763void Texture2D::convertToRenderTarget()
764{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000765 rx::TextureStorage2D *newTexStorage = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000766
767 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
768 {
769 GLsizei width = mImageArray[0].getWidth();
770 GLsizei height = mImageArray[0].getHeight();
771 GLint levels = creationLevels(width, height);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000772 GLenum internalformat = mImageArray[0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000773
daniel@transgaming.coma9571682012-11-28 19:33:08 +0000774 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
775 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000776 newTexStorage = new rx::TextureStorage2D(renderer9, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000777
778 if (mTexStorage != NULL)
779 {
daniel@transgaming.com1d80eee2012-11-28 19:33:31 +0000780 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +0000781 {
782 delete newTexStorage;
783 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000784 }
785 }
786 }
787
788 delete mTexStorage;
789 mTexStorage = newTexStorage;
790
791 mDirtyImages = true;
792}
793
794void Texture2D::generateMipmaps()
795{
daniel@transgaming.comea32d482012-11-28 19:33:18 +0000796 if (!mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000797 {
798 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
799 {
800 return error(GL_INVALID_OPERATION);
801 }
802 }
803
804 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
805 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
806 for (unsigned int i = 1; i <= q; i++)
807 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000808 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000809 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000810 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000811 }
812
813 if (mTexStorage && mTexStorage->isRenderTarget())
814 {
815 for (unsigned int i = 1; i <= q; i++)
816 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +0000817 mTexStorage->generateMipmap(i);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000818
819 mImageArray[i].markClean();
820 }
821 }
822 else
823 {
824 for (unsigned int i = 1; i <= q; i++)
825 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000826 rx::Image::GenerateMipmap(&mImageArray[i], &mImageArray[i - 1]);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000827 }
828 }
829}
830
831Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
832{
833 if (target != GL_TEXTURE_2D)
834 {
835 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
836 }
837
838 if (mColorbufferProxy == NULL)
839 {
daniel@transgaming.com70062c92012-11-28 19:32:30 +0000840 mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000841 }
842
843 return mColorbufferProxy;
844}
845
846// Increments refcount on surface.
847// caller must Release() the returned surface
848IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
849{
850 ASSERT(target == GL_TEXTURE_2D);
851
852 // ensure the underlying texture is created
853 if (getStorage(true) == NULL)
854 {
855 return NULL;
856 }
857
858 updateTexture();
859
860 // ensure this is NOT a depth texture
861 if (isDepth(0))
862 {
863 return NULL;
864 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +0000865 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000866}
867
868// Increments refcount on surface.
869// caller must Release() the returned surface
870IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
871{
872 ASSERT(target == GL_TEXTURE_2D);
873
874 // ensure the underlying texture is created
875 if (getStorage(true) == NULL)
876 {
877 return NULL;
878 }
879
880 updateTexture();
881
882 // ensure this is actually a depth texture
883 if (!isDepth(0))
884 {
885 return NULL;
886 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +0000887 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000888}
889
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +0000890int Texture2D::levelCount()
891{
892 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
893}
894
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000895rx::TextureStorage *Texture2D::getStorage(bool renderTarget)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000896{
897 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
898 {
899 if (renderTarget)
900 {
901 convertToRenderTarget();
902 }
903 else
904 {
905 createTexture();
906 }
907 }
908
909 return mTexStorage;
910}
911
daniel@transgaming.com370482e2012-11-28 19:32:13 +0000912TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000913{
914 mTexStorage = NULL;
915 for (int i = 0; i < 6; i++)
916 {
917 mFaceProxies[i] = NULL;
918 mFaceProxyRefs[i] = 0;
919 }
920}
921
922TextureCubeMap::~TextureCubeMap()
923{
924 for (int i = 0; i < 6; i++)
925 {
926 mFaceProxies[i] = NULL;
927 }
928
929 delete mTexStorage;
930 mTexStorage = NULL;
931}
932
933// We need to maintain a count of references to renderbuffers acting as
934// proxies for this texture, so that the texture is not deleted while
935// proxy references still exist. If the reference count drops to zero,
936// we set our proxy pointer NULL, so that a new attempt at referencing
937// will cause recreation.
938void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
939{
940 for (int i = 0; i < 6; i++)
941 {
942 if (mFaceProxies[i] == proxy)
943 mFaceProxyRefs[i]++;
944 }
945}
946
947void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
948{
949 for (int i = 0; i < 6; i++)
950 {
951 if (mFaceProxies[i] == proxy)
952 {
953 if (mFaceProxyRefs[i] > 0)
954 mFaceProxyRefs[i]--;
955
956 if (mFaceProxyRefs[i] == 0)
957 mFaceProxies[i] = NULL;
958 }
959 }
960}
961
962GLenum TextureCubeMap::getTarget() const
963{
964 return GL_TEXTURE_CUBE_MAP;
965}
966
967GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
968{
969 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
970 return mImageArray[faceIndex(target)][level].getWidth();
971 else
972 return 0;
973}
974
975GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
976{
977 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
978 return mImageArray[faceIndex(target)][level].getHeight();
979 else
980 return 0;
981}
982
983GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
984{
985 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000986 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000987 else
988 return GL_NONE;
989}
990
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000991GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000992{
993 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000994 return mImageArray[faceIndex(target)][level].getActualFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000995 else
996 return D3DFMT_UNKNOWN;
997}
998
999void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1000{
1001 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
1002}
1003
1004void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1005{
1006 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
1007}
1008
1009void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1010{
1011 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
1012}
1013
1014void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1015{
1016 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
1017}
1018
1019void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1020{
1021 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
1022}
1023
1024void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1025{
1026 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
1027}
1028
1029void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1030{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001031 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1032 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001033
1034 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
1035}
1036
1037void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1038{
1039 ASSERT(mImageArray[face][level].getSurface() != NULL);
1040
1041 if (level < levelCount())
1042 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001043 rx::Image *image = &mImageArray[face][level];
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001044 if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001045 image->markClean();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001046 }
1047}
1048
1049void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1050{
1051 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
1052 {
1053 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1054 }
1055}
1056
1057void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1058{
1059 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
1060 {
1061 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1062 }
1063}
1064
1065// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
1066bool TextureCubeMap::isSamplerComplete() const
1067{
1068 int size = mImageArray[0][0].getWidth();
1069
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001070 bool mipmapping = isMipmapFiltered();
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001071 bool filtering, renderable;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001072
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001073 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) ||
1074 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable)))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001075 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001076 if (mSamplerState.magFilter != GL_NEAREST ||
1077 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001078 {
1079 return false;
1080 }
1081 }
1082
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001083 if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001084 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001085 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001086 {
1087 return false;
1088 }
1089 }
1090
1091 if (!mipmapping)
1092 {
1093 if (!isCubeComplete())
1094 {
1095 return false;
1096 }
1097 }
1098 else
1099 {
1100 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1101 {
1102 return false;
1103 }
1104 }
1105
1106 return true;
1107}
1108
1109// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1110bool TextureCubeMap::isCubeComplete() const
1111{
1112 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
1113 {
1114 return false;
1115 }
1116
1117 for (unsigned int face = 1; face < 6; face++)
1118 {
1119 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
1120 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001121 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001122 {
1123 return false;
1124 }
1125 }
1126
1127 return true;
1128}
1129
1130bool TextureCubeMap::isMipmapCubeComplete() const
1131{
1132 if (isImmutable())
1133 {
1134 return true;
1135 }
1136
1137 if (!isCubeComplete())
1138 {
1139 return false;
1140 }
1141
1142 GLsizei size = mImageArray[0][0].getWidth();
1143
1144 int q = log2(size);
1145
1146 for (int face = 0; face < 6; face++)
1147 {
1148 for (int level = 1; level <= q; level++)
1149 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001150 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001151 {
1152 return false;
1153 }
1154
1155 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
1156 {
1157 return false;
1158 }
1159 }
1160 }
1161
1162 return true;
1163}
1164
1165bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1166{
1167 return IsCompressed(getInternalFormat(target, level));
1168}
1169
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001170// Constructs a native texture resource from the texture images, or returns an existing one
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001171void TextureCubeMap::createTexture()
1172{
1173 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001174
1175 if (!(size > 0))
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001176 return; // do not attempt to create native textures for nonexistant data
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001177
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001178 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001179 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001180
1181 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001182 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1183 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001184 mTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001185
1186 if (mTexStorage->isManaged())
1187 {
1188 int levels = levelCount();
1189
1190 for (int face = 0; face < 6; face++)
1191 {
1192 for (int level = 0; level < levels; level++)
1193 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001194 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001195 }
1196 }
1197 }
1198
1199 mDirtyImages = true;
1200}
1201
1202void TextureCubeMap::updateTexture()
1203{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001204 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
1205
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001206 for (int face = 0; face < 6; face++)
1207 {
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001208 int levels = (mipmapping ? levelCount() : 1);
1209
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001210 for (int level = 0; level < levels; level++)
1211 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001212 rx::Image *image = &mImageArray[face][level];
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001213
1214 if (image->isDirty())
1215 {
1216 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
1217 }
1218 }
1219 }
1220}
1221
1222void TextureCubeMap::convertToRenderTarget()
1223{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001224 rx::TextureStorageCubeMap *newTexStorage = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001225
1226 if (mImageArray[0][0].getWidth() != 0)
1227 {
1228 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001229 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001230 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001231
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001232 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1233 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001234 newTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001235
1236 if (mTexStorage != NULL)
1237 {
daniel@transgaming.com1d80eee2012-11-28 19:33:31 +00001238 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001239 {
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001240 delete newTexStorage;
1241 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001242 }
1243 }
1244 }
1245
1246 delete mTexStorage;
1247 mTexStorage = newTexStorage;
1248
1249 mDirtyImages = true;
1250}
1251
1252void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1253{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001254 GLint internalformat = ConvertSizedInternalFormat(format, type);
1255 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001256
1257 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
1258}
1259
1260unsigned int TextureCubeMap::faceIndex(GLenum face)
1261{
1262 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1263 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1264 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1265 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1266 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1267
1268 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1269}
1270
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001271void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001272{
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001273 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1274 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
1275 bool redefined = mImageArray[face][level].redefine(renderer9, internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001276
1277 if (mTexStorage && redefined)
1278 {
1279 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1280 {
1281 for (int f = 0; f < 6; f++)
1282 {
1283 mImageArray[f][i].markDirty();
1284 }
1285 }
1286
1287 delete mTexStorage;
1288 mTexStorage = NULL;
1289
1290 mDirtyImages = true;
1291 }
1292}
1293
1294void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1295{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001296 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001297 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1298 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001299
1300 if (!mImageArray[faceindex][level].isRenderableFormat())
1301 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001302 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001303 mDirtyImages = true;
1304 }
1305 else
1306 {
1307 if (!mTexStorage || !mTexStorage->isRenderTarget())
1308 {
1309 convertToRenderTarget();
1310 }
1311
1312 mImageArray[faceindex][level].markClean();
1313
1314 ASSERT(width == height);
1315
1316 if (width > 0 && level < levelCount())
1317 {
1318 RECT sourceRect;
1319 sourceRect.left = x;
1320 sourceRect.right = x + width;
1321 sourceRect.top = y;
1322 sourceRect.bottom = y + height;
1323
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +00001324 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001325
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001326 }
1327 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001328}
1329
1330void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1331{
1332 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
1333
1334 if (xoffset + width > size || yoffset + height > size)
1335 {
1336 return error(GL_INVALID_VALUE);
1337 }
1338
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001339 unsigned int faceindex = faceIndex(target);
1340
1341 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1342 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001343 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001344 mDirtyImages = true;
1345 }
1346 else
1347 {
1348 if (!mTexStorage || !mTexStorage->isRenderTarget())
1349 {
1350 convertToRenderTarget();
1351 }
1352
1353 updateTexture();
1354
1355 if (level < levelCount())
1356 {
1357 RECT sourceRect;
1358 sourceRect.left = x;
1359 sourceRect.right = x + width;
1360 sourceRect.top = y;
1361 sourceRect.bottom = y + height;
1362
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +00001363 mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()),
1364 xoffset, yoffset, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001365
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001366 }
1367 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001368}
1369
1370void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
1371{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001372 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001373 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1374 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001375 mTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001376 mImmutable = true;
1377
1378 for (int level = 0; level < levels; level++)
1379 {
1380 for (int face = 0; face < 6; face++)
1381 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001382 mImageArray[face][level].redefine(renderer9, internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001383 size = std::max(1, size >> 1);
1384 }
1385 }
1386
1387 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1388 {
1389 for (int face = 0; face < 6; face++)
1390 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001391 mImageArray[face][level].redefine(renderer9, GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001392 }
1393 }
1394
1395 if (mTexStorage->isManaged())
1396 {
1397 int levels = levelCount();
1398
1399 for (int face = 0; face < 6; face++)
1400 {
1401 for (int level = 0; level < levels; level++)
1402 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001403 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001404 }
1405 }
1406 }
1407}
1408
1409void TextureCubeMap::generateMipmaps()
1410{
1411 if (!isCubeComplete())
1412 {
1413 return error(GL_INVALID_OPERATION);
1414 }
1415
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001416 if (!mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001417 {
1418 if (!isPow2(mImageArray[0][0].getWidth()))
1419 {
1420 return error(GL_INVALID_OPERATION);
1421 }
1422 }
1423
1424 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1425 unsigned int q = log2(mImageArray[0][0].getWidth());
1426 for (unsigned int f = 0; f < 6; f++)
1427 {
1428 for (unsigned int i = 1; i <= q; i++)
1429 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001430 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001431 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001432 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001433 }
1434 }
1435
1436 if (mTexStorage && mTexStorage->isRenderTarget())
1437 {
1438 for (unsigned int f = 0; f < 6; f++)
1439 {
1440 for (unsigned int i = 1; i <= q; i++)
1441 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +00001442 mTexStorage->generateMipmap(f, i);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001443
1444 mImageArray[f][i].markClean();
1445 }
1446 }
1447 }
1448 else
1449 {
1450 for (unsigned int f = 0; f < 6; f++)
1451 {
1452 for (unsigned int i = 1; i <= q; i++)
1453 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001454 rx::Image::GenerateMipmap(&mImageArray[f][i], &mImageArray[f][i - 1]);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001455 }
1456 }
1457 }
1458}
1459
1460Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
1461{
1462 if (!IsCubemapTextureTarget(target))
1463 {
1464 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
1465 }
1466
1467 unsigned int face = faceIndex(target);
1468
1469 if (mFaceProxies[face] == NULL)
1470 {
daniel@transgaming.com70062c92012-11-28 19:32:30 +00001471 mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001472 }
1473
1474 return mFaceProxies[face];
1475}
1476
1477// Increments refcount on surface.
1478// caller must Release() the returned surface
1479IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1480{
1481 ASSERT(IsCubemapTextureTarget(target));
1482
1483 // ensure the underlying texture is created
1484 if (getStorage(true) == NULL)
1485 {
1486 return NULL;
1487 }
1488
1489 updateTexture();
1490
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001491 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001492}
1493
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001494int TextureCubeMap::levelCount()
1495{
1496 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
1497}
1498
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001499rx::TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001500{
1501 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
1502 {
1503 if (renderTarget)
1504 {
1505 convertToRenderTarget();
1506 }
1507 else
1508 {
1509 createTexture();
1510 }
1511 }
1512
1513 return mTexStorage;
1514}
1515
1516}