blob: 8dee2a9d0673c657bf478bdd03457f6b16f3cfda [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
daniel@transgaming.comd186dc72012-11-28 19:40:16 +0000846rx::RenderTarget *Texture2D::getRenderTarget(GLenum target)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000847{
848 ASSERT(target == GL_TEXTURE_2D);
849
850 // ensure the underlying texture is created
851 if (getStorage(true) == NULL)
852 {
853 return NULL;
854 }
855
856 updateTexture();
857
858 // ensure this is NOT a depth texture
859 if (isDepth(0))
860 {
861 return NULL;
862 }
daniel@transgaming.comd186dc72012-11-28 19:40:16 +0000863 return mTexStorage->getRenderTarget();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000864}
865
daniel@transgaming.comd186dc72012-11-28 19:40:16 +0000866rx::RenderTarget *Texture2D::getDepthStencil(GLenum target)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000867{
868 ASSERT(target == GL_TEXTURE_2D);
869
870 // ensure the underlying texture is created
871 if (getStorage(true) == NULL)
872 {
873 return NULL;
874 }
875
876 updateTexture();
877
878 // ensure this is actually a depth texture
879 if (!isDepth(0))
880 {
881 return NULL;
882 }
daniel@transgaming.comd186dc72012-11-28 19:40:16 +0000883 return mTexStorage->getRenderTarget();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000884}
885
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +0000886int Texture2D::levelCount()
887{
888 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
889}
890
daniel@transgaming.com31b13e12012-11-28 19:34:30 +0000891rx::TextureStorage *Texture2D::getStorage(bool renderTarget)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000892{
893 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
894 {
895 if (renderTarget)
896 {
897 convertToRenderTarget();
898 }
899 else
900 {
901 createTexture();
902 }
903 }
904
905 return mTexStorage;
906}
907
daniel@transgaming.com370482e2012-11-28 19:32:13 +0000908TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000909{
910 mTexStorage = NULL;
911 for (int i = 0; i < 6; i++)
912 {
913 mFaceProxies[i] = NULL;
914 mFaceProxyRefs[i] = 0;
915 }
916}
917
918TextureCubeMap::~TextureCubeMap()
919{
920 for (int i = 0; i < 6; i++)
921 {
922 mFaceProxies[i] = NULL;
923 }
924
925 delete mTexStorage;
926 mTexStorage = NULL;
927}
928
929// We need to maintain a count of references to renderbuffers acting as
930// proxies for this texture, so that the texture is not deleted while
931// proxy references still exist. If the reference count drops to zero,
932// we set our proxy pointer NULL, so that a new attempt at referencing
933// will cause recreation.
934void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
935{
936 for (int i = 0; i < 6; i++)
937 {
938 if (mFaceProxies[i] == proxy)
939 mFaceProxyRefs[i]++;
940 }
941}
942
943void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
944{
945 for (int i = 0; i < 6; i++)
946 {
947 if (mFaceProxies[i] == proxy)
948 {
949 if (mFaceProxyRefs[i] > 0)
950 mFaceProxyRefs[i]--;
951
952 if (mFaceProxyRefs[i] == 0)
953 mFaceProxies[i] = NULL;
954 }
955 }
956}
957
958GLenum TextureCubeMap::getTarget() const
959{
960 return GL_TEXTURE_CUBE_MAP;
961}
962
963GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
964{
965 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
966 return mImageArray[faceIndex(target)][level].getWidth();
967 else
968 return 0;
969}
970
971GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
972{
973 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
974 return mImageArray[faceIndex(target)][level].getHeight();
975 else
976 return 0;
977}
978
979GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
980{
981 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000982 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000983 else
984 return GL_NONE;
985}
986
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000987GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000988{
989 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000990 return mImageArray[faceIndex(target)][level].getActualFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000991 else
992 return D3DFMT_UNKNOWN;
993}
994
995void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
996{
997 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
998}
999
1000void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1001{
1002 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
1003}
1004
1005void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1006{
1007 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
1008}
1009
1010void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1011{
1012 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
1013}
1014
1015void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1016{
1017 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
1018}
1019
1020void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1021{
1022 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
1023}
1024
1025void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1026{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001027 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1028 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001029
1030 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
1031}
1032
1033void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1034{
1035 ASSERT(mImageArray[face][level].getSurface() != NULL);
1036
1037 if (level < levelCount())
1038 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001039 rx::Image *image = &mImageArray[face][level];
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001040 if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001041 image->markClean();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001042 }
1043}
1044
1045void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1046{
1047 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
1048 {
1049 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1050 }
1051}
1052
1053void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1054{
1055 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
1056 {
1057 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1058 }
1059}
1060
1061// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
1062bool TextureCubeMap::isSamplerComplete() const
1063{
1064 int size = mImageArray[0][0].getWidth();
1065
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001066 bool mipmapping = isMipmapFiltered();
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001067 bool filtering, renderable;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001068
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001069 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) ||
1070 (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 +00001071 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001072 if (mSamplerState.magFilter != GL_NEAREST ||
1073 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001074 {
1075 return false;
1076 }
1077 }
1078
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001079 if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001080 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001081 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001082 {
1083 return false;
1084 }
1085 }
1086
1087 if (!mipmapping)
1088 {
1089 if (!isCubeComplete())
1090 {
1091 return false;
1092 }
1093 }
1094 else
1095 {
1096 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1097 {
1098 return false;
1099 }
1100 }
1101
1102 return true;
1103}
1104
1105// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1106bool TextureCubeMap::isCubeComplete() const
1107{
1108 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
1109 {
1110 return false;
1111 }
1112
1113 for (unsigned int face = 1; face < 6; face++)
1114 {
1115 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
1116 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001117 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001118 {
1119 return false;
1120 }
1121 }
1122
1123 return true;
1124}
1125
1126bool TextureCubeMap::isMipmapCubeComplete() const
1127{
1128 if (isImmutable())
1129 {
1130 return true;
1131 }
1132
1133 if (!isCubeComplete())
1134 {
1135 return false;
1136 }
1137
1138 GLsizei size = mImageArray[0][0].getWidth();
1139
1140 int q = log2(size);
1141
1142 for (int face = 0; face < 6; face++)
1143 {
1144 for (int level = 1; level <= q; level++)
1145 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001146 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001147 {
1148 return false;
1149 }
1150
1151 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
1152 {
1153 return false;
1154 }
1155 }
1156 }
1157
1158 return true;
1159}
1160
1161bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1162{
1163 return IsCompressed(getInternalFormat(target, level));
1164}
1165
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001166// Constructs a native texture resource from the texture images, or returns an existing one
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001167void TextureCubeMap::createTexture()
1168{
1169 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001170
1171 if (!(size > 0))
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001172 return; // do not attempt to create native textures for nonexistant data
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001173
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001174 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001175 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001176
1177 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001178 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1179 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001180 mTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001181
1182 if (mTexStorage->isManaged())
1183 {
1184 int levels = levelCount();
1185
1186 for (int face = 0; face < 6; face++)
1187 {
1188 for (int level = 0; level < levels; level++)
1189 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001190 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001191 }
1192 }
1193 }
1194
1195 mDirtyImages = true;
1196}
1197
1198void TextureCubeMap::updateTexture()
1199{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001200 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
1201
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001202 for (int face = 0; face < 6; face++)
1203 {
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001204 int levels = (mipmapping ? levelCount() : 1);
1205
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001206 for (int level = 0; level < levels; level++)
1207 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001208 rx::Image *image = &mImageArray[face][level];
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001209
1210 if (image->isDirty())
1211 {
1212 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
1213 }
1214 }
1215 }
1216}
1217
1218void TextureCubeMap::convertToRenderTarget()
1219{
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001220 rx::TextureStorageCubeMap *newTexStorage = NULL;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001221
1222 if (mImageArray[0][0].getWidth() != 0)
1223 {
1224 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001225 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001226 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001227
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001228 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1229 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001230 newTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001231
1232 if (mTexStorage != NULL)
1233 {
daniel@transgaming.com1d80eee2012-11-28 19:33:31 +00001234 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001235 {
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001236 delete newTexStorage;
1237 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001238 }
1239 }
1240 }
1241
1242 delete mTexStorage;
1243 mTexStorage = newTexStorage;
1244
1245 mDirtyImages = true;
1246}
1247
1248void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1249{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001250 GLint internalformat = ConvertSizedInternalFormat(format, type);
1251 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001252
1253 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
1254}
1255
1256unsigned int TextureCubeMap::faceIndex(GLenum face)
1257{
1258 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1259 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1260 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1261 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1262 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1263
1264 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1265}
1266
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001267void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001268{
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001269 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1270 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
1271 bool redefined = mImageArray[face][level].redefine(renderer9, internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001272
1273 if (mTexStorage && redefined)
1274 {
1275 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1276 {
1277 for (int f = 0; f < 6; f++)
1278 {
1279 mImageArray[f][i].markDirty();
1280 }
1281 }
1282
1283 delete mTexStorage;
1284 mTexStorage = NULL;
1285
1286 mDirtyImages = true;
1287 }
1288}
1289
1290void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1291{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001292 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001293 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1294 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001295
1296 if (!mImageArray[faceindex][level].isRenderableFormat())
1297 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001298 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001299 mDirtyImages = true;
1300 }
1301 else
1302 {
1303 if (!mTexStorage || !mTexStorage->isRenderTarget())
1304 {
1305 convertToRenderTarget();
1306 }
1307
1308 mImageArray[faceindex][level].markClean();
1309
1310 ASSERT(width == height);
1311
1312 if (width > 0 && level < levelCount())
1313 {
1314 RECT sourceRect;
1315 sourceRect.left = x;
1316 sourceRect.right = x + width;
1317 sourceRect.top = y;
1318 sourceRect.bottom = y + height;
1319
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +00001320 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001321
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001322 }
1323 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001324}
1325
1326void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1327{
1328 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
1329
1330 if (xoffset + width > size || yoffset + height > size)
1331 {
1332 return error(GL_INVALID_VALUE);
1333 }
1334
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001335 unsigned int faceindex = faceIndex(target);
1336
1337 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1338 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001339 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001340 mDirtyImages = true;
1341 }
1342 else
1343 {
1344 if (!mTexStorage || !mTexStorage->isRenderTarget())
1345 {
1346 convertToRenderTarget();
1347 }
1348
1349 updateTexture();
1350
1351 if (level < levelCount())
1352 {
1353 RECT sourceRect;
1354 sourceRect.left = x;
1355 sourceRect.right = x + width;
1356 sourceRect.top = y;
1357 sourceRect.bottom = y + height;
1358
daniel@transgaming.comde8a7ff2012-11-28 19:34:13 +00001359 mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()),
1360 xoffset, yoffset, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001361
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001362 }
1363 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001364}
1365
1366void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
1367{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001368 delete mTexStorage;
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001369 assert(dynamic_cast<rx::Renderer9*>(mRenderer) != NULL); // D3D9_REPLACE
1370 rx::Renderer9 *renderer9 = static_cast<rx::Renderer9*>(mRenderer); // D3D9_REPLACE
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001371 mTexStorage = new rx::TextureStorageCubeMap(renderer9, levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001372 mImmutable = true;
1373
1374 for (int level = 0; level < levels; level++)
1375 {
1376 for (int face = 0; face < 6; face++)
1377 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001378 mImageArray[face][level].redefine(renderer9, internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001379 size = std::max(1, size >> 1);
1380 }
1381 }
1382
1383 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1384 {
1385 for (int face = 0; face < 6; face++)
1386 {
daniel@transgaming.coma9571682012-11-28 19:33:08 +00001387 mImageArray[face][level].redefine(renderer9, GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001388 }
1389 }
1390
1391 if (mTexStorage->isManaged())
1392 {
1393 int levels = levelCount();
1394
1395 for (int face = 0; face < 6; face++)
1396 {
1397 for (int level = 0; level < levels; level++)
1398 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001399 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001400 }
1401 }
1402 }
1403}
1404
1405void TextureCubeMap::generateMipmaps()
1406{
1407 if (!isCubeComplete())
1408 {
1409 return error(GL_INVALID_OPERATION);
1410 }
1411
daniel@transgaming.comea32d482012-11-28 19:33:18 +00001412 if (!mRenderer->getNonPower2TextureSupport())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001413 {
1414 if (!isPow2(mImageArray[0][0].getWidth()))
1415 {
1416 return error(GL_INVALID_OPERATION);
1417 }
1418 }
1419
1420 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1421 unsigned int q = log2(mImageArray[0][0].getWidth());
1422 for (unsigned int f = 0; f < 6; f++)
1423 {
1424 for (unsigned int i = 1; i <= q; i++)
1425 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001426 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001427 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001428 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001429 }
1430 }
1431
1432 if (mTexStorage && mTexStorage->isRenderTarget())
1433 {
1434 for (unsigned int f = 0; f < 6; f++)
1435 {
1436 for (unsigned int i = 1; i <= q; i++)
1437 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +00001438 mTexStorage->generateMipmap(f, i);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001439
1440 mImageArray[f][i].markClean();
1441 }
1442 }
1443 }
1444 else
1445 {
1446 for (unsigned int f = 0; f < 6; f++)
1447 {
1448 for (unsigned int i = 1; i <= q; i++)
1449 {
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001450 rx::Image::GenerateMipmap(&mImageArray[f][i], &mImageArray[f][i - 1]);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001451 }
1452 }
1453 }
1454}
1455
1456Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
1457{
1458 if (!IsCubemapTextureTarget(target))
1459 {
1460 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
1461 }
1462
1463 unsigned int face = faceIndex(target);
1464
1465 if (mFaceProxies[face] == NULL)
1466 {
daniel@transgaming.com70062c92012-11-28 19:32:30 +00001467 mFaceProxies[face] = new Renderbuffer(mRenderer, id(), new RenderbufferTextureCubeMap(this, target));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001468 }
1469
1470 return mFaceProxies[face];
1471}
1472
daniel@transgaming.comd186dc72012-11-28 19:40:16 +00001473rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001474{
1475 ASSERT(IsCubemapTextureTarget(target));
1476
1477 // ensure the underlying texture is created
1478 if (getStorage(true) == NULL)
1479 {
1480 return NULL;
1481 }
1482
1483 updateTexture();
1484
daniel@transgaming.comd186dc72012-11-28 19:40:16 +00001485 return mTexStorage->getRenderTarget(target);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001486}
1487
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001488int TextureCubeMap::levelCount()
1489{
1490 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
1491}
1492
daniel@transgaming.com31b13e12012-11-28 19:34:30 +00001493rx::TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001494{
1495 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
1496 {
1497 if (renderTarget)
1498 {
1499 convertToRenderTarget();
1500 }
1501 else
1502 {
1503 createTexture();
1504 }
1505 }
1506
1507 return mTexStorage;
1508}
1509
1510}