blob: 720136ece6aaf060dc58cbafa33882c7c3440460 [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"
22#include "libGLESv2/Blit.h"
23#include "libGLESv2/Framebuffer.h"
24
25namespace gl
26{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000027
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000028Texture::Texture(GLuint id) : RefCountObject(id)
29{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000030 mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR;
31 mSamplerState.magFilter = GL_LINEAR;
32 mSamplerState.wrapS = GL_REPEAT;
33 mSamplerState.wrapT = GL_REPEAT;
34 mSamplerState.maxAnisotropy = 1.0f;
35 mSamplerState.lodOffset = 0;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000036 mDirtyParameters = true;
37 mUsage = GL_NONE;
38
39 mDirtyImages = true;
40
41 mImmutable = false;
42}
43
44Texture::~Texture()
45{
46}
47
48// Returns true on successful filter state update (valid enum parameter)
49bool Texture::setMinFilter(GLenum filter)
50{
51 switch (filter)
52 {
53 case GL_NEAREST:
54 case GL_LINEAR:
55 case GL_NEAREST_MIPMAP_NEAREST:
56 case GL_LINEAR_MIPMAP_NEAREST:
57 case GL_NEAREST_MIPMAP_LINEAR:
58 case GL_LINEAR_MIPMAP_LINEAR:
59 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000060 if (mSamplerState.minFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000061 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000062 mSamplerState.minFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000063 mDirtyParameters = true;
64 }
65 return true;
66 }
67 default:
68 return false;
69 }
70}
71
72// Returns true on successful filter state update (valid enum parameter)
73bool Texture::setMagFilter(GLenum filter)
74{
75 switch (filter)
76 {
77 case GL_NEAREST:
78 case GL_LINEAR:
79 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000080 if (mSamplerState.magFilter != filter)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000081 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +000082 mSamplerState.magFilter = filter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +000083 mDirtyParameters = true;
84 }
85 return true;
86 }
87 default:
88 return false;
89 }
90}
91
92// Returns true on successful wrap state update (valid enum parameter)
93bool Texture::setWrapS(GLenum wrap)
94{
95 switch (wrap)
96 {
97 case GL_REPEAT:
98 case GL_CLAMP_TO_EDGE:
99 case GL_MIRRORED_REPEAT:
100 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000101 if (mSamplerState.wrapS != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000102 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000103 mSamplerState.wrapS = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000104 mDirtyParameters = true;
105 }
106 return true;
107 }
108 default:
109 return false;
110 }
111}
112
113// Returns true on successful wrap state update (valid enum parameter)
114bool Texture::setWrapT(GLenum wrap)
115{
116 switch (wrap)
117 {
118 case GL_REPEAT:
119 case GL_CLAMP_TO_EDGE:
120 case GL_MIRRORED_REPEAT:
121 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000122 if (mSamplerState.wrapT != wrap)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000123 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000124 mSamplerState.wrapT = wrap;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000125 mDirtyParameters = true;
126 }
127 return true;
128 }
129 default:
130 return false;
131 }
132}
133
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000134// Returns true on successful max anisotropy update (valid anisotropy value)
135bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
136{
137 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
138 if (textureMaxAnisotropy < 1.0f)
139 {
140 return false;
141 }
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000142 if (mSamplerState.maxAnisotropy != textureMaxAnisotropy)
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000143 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000144 mSamplerState.maxAnisotropy = textureMaxAnisotropy;
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000145 mDirtyParameters = true;
146 }
147 return true;
148}
149
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000150// Returns true on successful usage state update (valid enum parameter)
151bool Texture::setUsage(GLenum usage)
152{
153 switch (usage)
154 {
155 case GL_NONE:
156 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
157 mUsage = usage;
158 return true;
159 default:
160 return false;
161 }
162}
163
164GLenum Texture::getMinFilter() const
165{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000166 return mSamplerState.minFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000167}
168
169GLenum Texture::getMagFilter() const
170{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000171 return mSamplerState.magFilter;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000172}
173
174GLenum Texture::getWrapS() const
175{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000176 return mSamplerState.wrapS;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000177}
178
179GLenum Texture::getWrapT() const
180{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000181 return mSamplerState.wrapT;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000182}
183
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000184float Texture::getMaxAnisotropy() const
185{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000186 return mSamplerState.maxAnisotropy;
187}
188
189int Texture::getLodOffset()
190{
191 TextureStorage *texture = getStorage(false);
192 return texture ? texture->getLodOffset() : 0;
193}
194
195void Texture::getSamplerState(SamplerState *sampler)
196{
197 *sampler = mSamplerState;
198 sampler->lodOffset = getLodOffset();
daniel@transgaming.com07ab8412012-07-12 15:17:09 +0000199}
200
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000201GLenum Texture::getUsage() const
202{
203 return mUsage;
204}
205
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000206bool Texture::isMipmapFiltered() const
207{
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000208 switch (mSamplerState.minFilter)
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000209 {
210 case GL_NEAREST:
211 case GL_LINEAR:
212 return false;
213 case GL_NEAREST_MIPMAP_NEAREST:
214 case GL_LINEAR_MIPMAP_NEAREST:
215 case GL_NEAREST_MIPMAP_LINEAR:
216 case GL_LINEAR_MIPMAP_LINEAR:
217 return true;
218 default: UNREACHABLE();
219 return false;
220 }
221}
222
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000223void Texture::setImage(GLint unpackAlignment, const void *pixels, Image *image)
224{
225 if (pixels != NULL)
226 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000227 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000228 mDirtyImages = true;
229 }
230}
231
232void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
233{
234 if (pixels != NULL)
235 {
236 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
237 mDirtyImages = true;
238 }
239}
240
241bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
242{
243 if (pixels != NULL)
244 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000245 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000246 mDirtyImages = true;
247 }
248
249 return true;
250}
251
252bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
253{
254 if (pixels != NULL)
255 {
256 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
257 mDirtyImages = true;
258 }
259
260 return true;
261}
262
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000263TextureStorage *Texture::getNativeTexture()
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000264{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000265 // ensure the underlying texture is created
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000266
267 TextureStorage *storage = getStorage(false);
268 if (storage)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000269 {
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000270 updateTexture();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000271 }
272
daniel@transgaming.com9d4346f2012-10-31 19:52:04 +0000273 return storage;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000274}
275
276bool Texture::hasDirtyParameters() const
277{
278 return mDirtyParameters;
279}
280
281bool Texture::hasDirtyImages() const
282{
283 return mDirtyImages;
284}
285
286void Texture::resetDirty()
287{
288 mDirtyParameters = false;
289 mDirtyImages = false;
290}
291
292unsigned int Texture::getTextureSerial()
293{
294 TextureStorage *texture = getStorage(false);
295 return texture ? texture->getTextureSerial() : 0;
296}
297
298unsigned int Texture::getRenderTargetSerial(GLenum target)
299{
300 TextureStorage *texture = getStorage(true);
301 return texture ? texture->getRenderTargetSerial(target) : 0;
302}
303
304bool Texture::isImmutable() const
305{
306 return mImmutable;
307}
308
309GLint Texture::creationLevels(GLsizei width, GLsizei height) const
310{
311 if ((isPow2(width) && isPow2(height)) || getContext()->supportsNonPower2Texture())
312 {
313 return 0; // Maximum number of levels
314 }
315 else
316 {
317 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
318 return 1;
319 }
320}
321
322GLint Texture::creationLevels(GLsizei size) const
323{
324 return creationLevels(size, size);
325}
326
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000327Blit *Texture::getBlitter()
328{
329 Context *context = getContext();
330 return context->getBlitter();
331}
332
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000333Texture2D::Texture2D(GLuint id) : Texture(id)
334{
335 mTexStorage = NULL;
336 mSurface = NULL;
337 mColorbufferProxy = NULL;
338 mProxyRefs = 0;
339}
340
341Texture2D::~Texture2D()
342{
343 mColorbufferProxy = NULL;
344
345 delete mTexStorage;
346 mTexStorage = NULL;
347
348 if (mSurface)
349 {
350 mSurface->setBoundTexture(NULL);
351 mSurface = NULL;
352 }
353}
354
355// We need to maintain a count of references to renderbuffers acting as
356// proxies for this texture, so that we do not attempt to use a pointer
357// to a renderbuffer proxy which has been deleted.
358void Texture2D::addProxyRef(const Renderbuffer *proxy)
359{
360 mProxyRefs++;
361}
362
363void Texture2D::releaseProxy(const Renderbuffer *proxy)
364{
365 if (mProxyRefs > 0)
366 mProxyRefs--;
367
368 if (mProxyRefs == 0)
369 mColorbufferProxy = NULL;
370}
371
372GLenum Texture2D::getTarget() const
373{
374 return GL_TEXTURE_2D;
375}
376
377GLsizei Texture2D::getWidth(GLint level) const
378{
379 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
380 return mImageArray[level].getWidth();
381 else
382 return 0;
383}
384
385GLsizei Texture2D::getHeight(GLint level) const
386{
387 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
388 return mImageArray[level].getHeight();
389 else
390 return 0;
391}
392
393GLenum Texture2D::getInternalFormat(GLint level) const
394{
395 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000396 return mImageArray[level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000397 else
398 return GL_NONE;
399}
400
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000401GLenum Texture2D::getActualFormat(GLint level) const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000402{
403 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000404 return mImageArray[level].getActualFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000405 else
406 return D3DFMT_UNKNOWN;
407}
408
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000409void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000410{
411 releaseTexImage();
412
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000413 bool redefined = mImageArray[level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000414
415 if (mTexStorage && redefined)
416 {
417 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
418 {
419 mImageArray[i].markDirty();
420 }
421
422 delete mTexStorage;
423 mTexStorage = NULL;
424 mDirtyImages = true;
425 }
426}
427
428void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
429{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000430 GLint internalformat = ConvertSizedInternalFormat(format, type);
431 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000432
433 Texture::setImage(unpackAlignment, pixels, &mImageArray[level]);
434}
435
436void Texture2D::bindTexImage(egl::Surface *surface)
437{
438 releaseTexImage();
439
daniel@transgaming.com106e1f72012-10-31 18:38:36 +0000440 GLint internalformat = surface->getFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000441
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000442 mImageArray[0].redefine(internalformat, surface->getWidth(), surface->getHeight(), true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000443
444 delete mTexStorage;
daniel@transgaming.com3c720782012-10-31 18:42:34 +0000445 renderer::SwapChain *swapchain = surface->getSwapChain(); // D3D9_REPLACE
daniel@transgaming.com25ee7442012-10-31 19:51:56 +0000446 mTexStorage = new TextureStorage2D(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
466 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
467 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000468 mImageArray[i].redefine(GL_RGBA8_OES, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000469 }
470 }
471}
472
473void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
474{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000475 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
476 redefineImage(level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000477
478 Texture::setCompressedImage(imageSize, pixels, &mImageArray[level]);
479}
480
481void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
482{
483 ASSERT(mImageArray[level].getSurface() != NULL);
484
485 if (level < levelCount())
486 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000487 Image *image = &mImageArray[level];
488 if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000489 {
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000490 image->markClean();
491 }
492 }
493}
494
495void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
496{
497 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[level]))
498 {
499 commitRect(level, xoffset, yoffset, width, height);
500 }
501}
502
503void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
504{
505 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
506 {
507 commitRect(level, xoffset, yoffset, width, height);
508 }
509}
510
511void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
512{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000513 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
514 redefineImage(level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000515
516 if (!mImageArray[level].isRenderableFormat())
517 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000518 mImageArray[level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000519 mDirtyImages = true;
520 }
521 else
522 {
523 if (!mTexStorage || !mTexStorage->isRenderTarget())
524 {
525 convertToRenderTarget();
526 }
527
528 mImageArray[level].markClean();
529
530 if (width != 0 && height != 0 && level < levelCount())
531 {
532 RECT sourceRect;
533 sourceRect.left = x;
534 sourceRect.right = x + width;
535 sourceRect.top = y;
536 sourceRect.bottom = y + height;
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000537
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000538 getBlitter()->copy(source, sourceRect, format, 0, 0, mTexStorage, level);
539
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000540 }
541 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000542}
543
544void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
545{
546 if (xoffset + width > mImageArray[level].getWidth() || yoffset + height > mImageArray[level].getHeight())
547 {
548 return error(GL_INVALID_VALUE);
549 }
550
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000551 if (!mImageArray[level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
552 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000553 mImageArray[level].copy(xoffset, yoffset, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000554 mDirtyImages = true;
555 }
556 else
557 {
558 if (!mTexStorage || !mTexStorage->isRenderTarget())
559 {
560 convertToRenderTarget();
561 }
562
563 updateTexture();
564
565 if (level < levelCount())
566 {
567 RECT sourceRect;
568 sourceRect.left = x;
569 sourceRect.right = x + width;
570 sourceRect.top = y;
571 sourceRect.bottom = y + height;
572
daniel@transgaming.com3cef5392012-10-31 19:52:15 +0000573 getBlitter()->copy(source, sourceRect,
574 gl::ExtractFormat(mImageArray[0].getInternalFormat()),
575 xoffset, yoffset, mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000576 }
577 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000578}
579
580void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
581{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000582 delete mTexStorage;
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000583 mTexStorage = new TextureStorage2D(levels, internalformat, mUsage, false, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000584 mImmutable = true;
585
586 for (int level = 0; level < levels; level++)
587 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000588 mImageArray[level].redefine(internalformat, width, height, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000589 width = std::max(1, width >> 1);
590 height = std::max(1, height >> 1);
591 }
592
593 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
594 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000595 mImageArray[level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000596 }
597
598 if (mTexStorage->isManaged())
599 {
600 int levels = levelCount();
601
602 for (int level = 0; level < levels; level++)
603 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000604 mImageArray[level].setManagedSurface(mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000605 }
606 }
607}
608
609// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
610bool Texture2D::isSamplerComplete() const
611{
612 GLsizei width = mImageArray[0].getWidth();
613 GLsizei height = mImageArray[0].getHeight();
614
615 if (width <= 0 || height <= 0)
616 {
617 return false;
618 }
619
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000620 bool mipmapping = isMipmapFiltered();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000621
daniel@transgaming.com6b1a0a02012-10-17 18:22:47 +0000622 if ((IsFloat32Format(getInternalFormat(0)) && !getContext()->supportsFloat32LinearFilter()) ||
623 (IsFloat16Format(getInternalFormat(0)) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000624 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000625 if (mSamplerState.magFilter != GL_NEAREST ||
626 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000627 {
628 return false;
629 }
630 }
631
632 bool npotSupport = getContext()->supportsNonPower2Texture();
633
634 if (!npotSupport)
635 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +0000636 if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
637 (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height)))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000638 {
639 return false;
640 }
641 }
642
643 if (mipmapping)
644 {
645 if (!npotSupport)
646 {
647 if (!isPow2(width) || !isPow2(height))
648 {
649 return false;
650 }
651 }
652
653 if (!isMipmapComplete())
654 {
655 return false;
656 }
657 }
658
659 return true;
660}
661
662// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
663bool Texture2D::isMipmapComplete() const
664{
665 if (isImmutable())
666 {
667 return true;
668 }
669
670 GLsizei width = mImageArray[0].getWidth();
671 GLsizei height = mImageArray[0].getHeight();
672
673 if (width <= 0 || height <= 0)
674 {
675 return false;
676 }
677
678 int q = log2(std::max(width, height));
679
680 for (int level = 1; level <= q; level++)
681 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000682 if (mImageArray[level].getInternalFormat() != mImageArray[0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000683 {
684 return false;
685 }
686
687 if (mImageArray[level].getWidth() != std::max(1, width >> level))
688 {
689 return false;
690 }
691
692 if (mImageArray[level].getHeight() != std::max(1, height >> level))
693 {
694 return false;
695 }
696 }
697
698 return true;
699}
700
701bool Texture2D::isCompressed(GLint level) const
702{
703 return IsCompressed(getInternalFormat(level));
704}
705
706bool Texture2D::isDepth(GLint level) const
707{
708 return IsDepthTexture(getInternalFormat(level));
709}
710
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000711// Constructs a native texture resource from the texture images
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000712void Texture2D::createTexture()
713{
714 GLsizei width = mImageArray[0].getWidth();
715 GLsizei height = mImageArray[0].getHeight();
daniel@transgaming.come6a09842012-09-17 21:28:55 +0000716
717 if (!(width > 0 && height > 0))
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000718 return; // do not attempt to create native textures for nonexistant data
daniel@transgaming.come6a09842012-09-17 21:28:55 +0000719
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000720 GLint levels = creationLevels(width, height);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000721 GLenum internalformat = mImageArray[0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000722
723 delete mTexStorage;
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000724 mTexStorage = new TextureStorage2D(levels, internalformat, mUsage, false, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000725
726 if (mTexStorage->isManaged())
727 {
728 int levels = levelCount();
729
730 for (int level = 0; level < levels; level++)
731 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +0000732 mImageArray[level].setManagedSurface(mTexStorage, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000733 }
734 }
735
736 mDirtyImages = true;
737}
738
739void Texture2D::updateTexture()
740{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +0000741 bool mipmapping = (isMipmapFiltered() && isMipmapComplete());
742
743 int levels = (mipmapping ? levelCount() : 1);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000744
745 for (int level = 0; level < levels; level++)
746 {
747 Image *image = &mImageArray[level];
748
749 if (image->isDirty())
750 {
751 commitRect(level, 0, 0, mImageArray[level].getWidth(), mImageArray[level].getHeight());
752 }
753 }
754}
755
756void Texture2D::convertToRenderTarget()
757{
758 TextureStorage2D *newTexStorage = NULL;
759
760 if (mImageArray[0].getWidth() != 0 && mImageArray[0].getHeight() != 0)
761 {
762 GLsizei width = mImageArray[0].getWidth();
763 GLsizei height = mImageArray[0].getHeight();
764 GLint levels = creationLevels(width, height);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000765 GLenum internalformat = mImageArray[0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000766
daniel@transgaming.comf032cb82012-10-31 19:51:52 +0000767 newTexStorage = new TextureStorage2D(levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000768
769 if (mTexStorage != NULL)
770 {
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +0000771 if (!TextureStorage2D::copyToRenderTarget(newTexStorage, mTexStorage))
772 {
773 delete newTexStorage;
774 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000775 }
776 }
777 }
778
779 delete mTexStorage;
780 mTexStorage = newTexStorage;
781
782 mDirtyImages = true;
783}
784
785void Texture2D::generateMipmaps()
786{
787 if (!getContext()->supportsNonPower2Texture())
788 {
789 if (!isPow2(mImageArray[0].getWidth()) || !isPow2(mImageArray[0].getHeight()))
790 {
791 return error(GL_INVALID_OPERATION);
792 }
793 }
794
795 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
796 unsigned int q = log2(std::max(mImageArray[0].getWidth(), mImageArray[0].getHeight()));
797 for (unsigned int i = 1; i <= q; i++)
798 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000799 redefineImage(i, mImageArray[0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000800 std::max(mImageArray[0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000801 std::max(mImageArray[0].getHeight() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000802 }
803
804 if (mTexStorage && mTexStorage->isRenderTarget())
805 {
806 for (unsigned int i = 1; i <= q; i++)
807 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +0000808 mTexStorage->generateMipmap(i);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000809
810 mImageArray[i].markClean();
811 }
812 }
813 else
814 {
815 for (unsigned int i = 1; i <= q; i++)
816 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +0000817 Image::GenerateMipmap(&mImageArray[i], &mImageArray[i - 1]);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000818 }
819 }
820}
821
822Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
823{
824 if (target != GL_TEXTURE_2D)
825 {
826 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
827 }
828
829 if (mColorbufferProxy == NULL)
830 {
831 mColorbufferProxy = new Renderbuffer(id(), new RenderbufferTexture2D(this, target));
832 }
833
834 return mColorbufferProxy;
835}
836
837// Increments refcount on surface.
838// caller must Release() the returned surface
839IDirect3DSurface9 *Texture2D::getRenderTarget(GLenum target)
840{
841 ASSERT(target == GL_TEXTURE_2D);
842
843 // ensure the underlying texture is created
844 if (getStorage(true) == NULL)
845 {
846 return NULL;
847 }
848
849 updateTexture();
850
851 // ensure this is NOT a depth texture
852 if (isDepth(0))
853 {
854 return NULL;
855 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +0000856 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000857}
858
859// Increments refcount on surface.
860// caller must Release() the returned surface
861IDirect3DSurface9 *Texture2D::getDepthStencil(GLenum target)
862{
863 ASSERT(target == GL_TEXTURE_2D);
864
865 // ensure the underlying texture is created
866 if (getStorage(true) == NULL)
867 {
868 return NULL;
869 }
870
871 updateTexture();
872
873 // ensure this is actually a depth texture
874 if (!isDepth(0))
875 {
876 return NULL;
877 }
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +0000878 return mTexStorage->getSurfaceLevel(0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000879}
880
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +0000881int Texture2D::levelCount()
882{
883 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
884}
885
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000886TextureStorage *Texture2D::getStorage(bool renderTarget)
887{
888 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
889 {
890 if (renderTarget)
891 {
892 convertToRenderTarget();
893 }
894 else
895 {
896 createTexture();
897 }
898 }
899
900 return mTexStorage;
901}
902
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000903TextureCubeMap::TextureCubeMap(GLuint id) : Texture(id)
904{
905 mTexStorage = NULL;
906 for (int i = 0; i < 6; i++)
907 {
908 mFaceProxies[i] = NULL;
909 mFaceProxyRefs[i] = 0;
910 }
911}
912
913TextureCubeMap::~TextureCubeMap()
914{
915 for (int i = 0; i < 6; i++)
916 {
917 mFaceProxies[i] = NULL;
918 }
919
920 delete mTexStorage;
921 mTexStorage = NULL;
922}
923
924// We need to maintain a count of references to renderbuffers acting as
925// proxies for this texture, so that the texture is not deleted while
926// proxy references still exist. If the reference count drops to zero,
927// we set our proxy pointer NULL, so that a new attempt at referencing
928// will cause recreation.
929void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
930{
931 for (int i = 0; i < 6; i++)
932 {
933 if (mFaceProxies[i] == proxy)
934 mFaceProxyRefs[i]++;
935 }
936}
937
938void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
939{
940 for (int i = 0; i < 6; i++)
941 {
942 if (mFaceProxies[i] == proxy)
943 {
944 if (mFaceProxyRefs[i] > 0)
945 mFaceProxyRefs[i]--;
946
947 if (mFaceProxyRefs[i] == 0)
948 mFaceProxies[i] = NULL;
949 }
950 }
951}
952
953GLenum TextureCubeMap::getTarget() const
954{
955 return GL_TEXTURE_CUBE_MAP;
956}
957
958GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
959{
960 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
961 return mImageArray[faceIndex(target)][level].getWidth();
962 else
963 return 0;
964}
965
966GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
967{
968 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
969 return mImageArray[faceIndex(target)][level].getHeight();
970 else
971 return 0;
972}
973
974GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
975{
976 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com6452adf2012-10-17 18:22:35 +0000977 return mImageArray[faceIndex(target)][level].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000978 else
979 return GL_NONE;
980}
981
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000982GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000983{
984 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
daniel@transgaming.com20d36662012-10-31 19:51:43 +0000985 return mImageArray[faceIndex(target)][level].getActualFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +0000986 else
987 return D3DFMT_UNKNOWN;
988}
989
990void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
991{
992 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
993}
994
995void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
996{
997 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
998}
999
1000void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1001{
1002 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
1003}
1004
1005void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1006{
1007 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
1008}
1009
1010void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1011{
1012 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
1013}
1014
1015void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1016{
1017 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
1018}
1019
1020void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1021{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001022 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1023 redefineImage(faceIndex(face), level, format, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001024
1025 Texture::setCompressedImage(imageSize, pixels, &mImageArray[faceIndex(face)][level]);
1026}
1027
1028void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1029{
1030 ASSERT(mImageArray[face][level].getSurface() != NULL);
1031
1032 if (level < levelCount())
1033 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001034 Image *image = &mImageArray[face][level];
1035 if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001036 image->markClean();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001037 }
1038}
1039
1040void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1041{
1042 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, &mImageArray[faceIndex(target)][level]))
1043 {
1044 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1045 }
1046}
1047
1048void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1049{
1050 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(target)][level]))
1051 {
1052 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1053 }
1054}
1055
1056// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
1057bool TextureCubeMap::isSamplerComplete() const
1058{
1059 int size = mImageArray[0][0].getWidth();
1060
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001061 bool mipmapping = isMipmapFiltered();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001062
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001063 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !getContext()->supportsFloat32LinearFilter()) ||
1064 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !getContext()->supportsFloat16LinearFilter()))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001065 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001066 if (mSamplerState.magFilter != GL_NEAREST ||
1067 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001068 {
1069 return false;
1070 }
1071 }
1072
1073 if (!isPow2(size) && !getContext()->supportsNonPower2Texture())
1074 {
daniel@transgaming.comebf139f2012-10-31 18:07:32 +00001075 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001076 {
1077 return false;
1078 }
1079 }
1080
1081 if (!mipmapping)
1082 {
1083 if (!isCubeComplete())
1084 {
1085 return false;
1086 }
1087 }
1088 else
1089 {
1090 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1091 {
1092 return false;
1093 }
1094 }
1095
1096 return true;
1097}
1098
1099// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1100bool TextureCubeMap::isCubeComplete() const
1101{
1102 if (mImageArray[0][0].getWidth() <= 0 || mImageArray[0][0].getHeight() != mImageArray[0][0].getWidth())
1103 {
1104 return false;
1105 }
1106
1107 for (unsigned int face = 1; face < 6; face++)
1108 {
1109 if (mImageArray[face][0].getWidth() != mImageArray[0][0].getWidth() ||
1110 mImageArray[face][0].getWidth() != mImageArray[0][0].getHeight() ||
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001111 mImageArray[face][0].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001112 {
1113 return false;
1114 }
1115 }
1116
1117 return true;
1118}
1119
1120bool TextureCubeMap::isMipmapCubeComplete() const
1121{
1122 if (isImmutable())
1123 {
1124 return true;
1125 }
1126
1127 if (!isCubeComplete())
1128 {
1129 return false;
1130 }
1131
1132 GLsizei size = mImageArray[0][0].getWidth();
1133
1134 int q = log2(size);
1135
1136 for (int face = 0; face < 6; face++)
1137 {
1138 for (int level = 1; level <= q; level++)
1139 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001140 if (mImageArray[face][level].getInternalFormat() != mImageArray[0][0].getInternalFormat())
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001141 {
1142 return false;
1143 }
1144
1145 if (mImageArray[face][level].getWidth() != std::max(1, size >> level))
1146 {
1147 return false;
1148 }
1149 }
1150 }
1151
1152 return true;
1153}
1154
1155bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1156{
1157 return IsCompressed(getInternalFormat(target, level));
1158}
1159
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001160// Constructs a native texture resource from the texture images, or returns an existing one
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001161void TextureCubeMap::createTexture()
1162{
1163 GLsizei size = mImageArray[0][0].getWidth();
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001164
1165 if (!(size > 0))
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001166 return; // do not attempt to create native textures for nonexistant data
daniel@transgaming.come6a09842012-09-17 21:28:55 +00001167
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001168 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001169 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001170
1171 delete mTexStorage;
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001172 mTexStorage = new TextureStorageCubeMap(levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001173
1174 if (mTexStorage->isManaged())
1175 {
1176 int levels = levelCount();
1177
1178 for (int face = 0; face < 6; face++)
1179 {
1180 for (int level = 0; level < levels; level++)
1181 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001182 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001183 }
1184 }
1185 }
1186
1187 mDirtyImages = true;
1188}
1189
1190void TextureCubeMap::updateTexture()
1191{
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001192 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
1193
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001194 for (int face = 0; face < 6; face++)
1195 {
daniel@transgaming.comca9a3c82012-10-26 18:55:07 +00001196 int levels = (mipmapping ? levelCount() : 1);
1197
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001198 for (int level = 0; level < levels; level++)
1199 {
1200 Image *image = &mImageArray[face][level];
1201
1202 if (image->isDirty())
1203 {
1204 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
1205 }
1206 }
1207 }
1208}
1209
1210void TextureCubeMap::convertToRenderTarget()
1211{
1212 TextureStorageCubeMap *newTexStorage = NULL;
1213
1214 if (mImageArray[0][0].getWidth() != 0)
1215 {
1216 GLsizei size = mImageArray[0][0].getWidth();
sminns@adobe.comce1189b2012-09-18 20:06:35 +00001217 GLint levels = creationLevels(size);
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001218 GLenum internalformat = mImageArray[0][0].getInternalFormat();
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001219
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001220 newTexStorage = new TextureStorageCubeMap(levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001221
1222 if (mTexStorage != NULL)
1223 {
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001224 if (!TextureStorageCubeMap::copyToRenderTarget(newTexStorage, mTexStorage))
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001225 {
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001226 delete newTexStorage;
1227 return error(GL_OUT_OF_MEMORY);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001228 }
1229 }
1230 }
1231
1232 delete mTexStorage;
1233 mTexStorage = newTexStorage;
1234
1235 mDirtyImages = true;
1236}
1237
1238void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1239{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001240 GLint internalformat = ConvertSizedInternalFormat(format, type);
1241 redefineImage(faceIndex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001242
1243 Texture::setImage(unpackAlignment, pixels, &mImageArray[faceIndex][level]);
1244}
1245
1246unsigned int TextureCubeMap::faceIndex(GLenum face)
1247{
1248 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1249 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1250 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1251 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1252 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1253
1254 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1255}
1256
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001257void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001258{
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001259 bool redefined = mImageArray[face][level].redefine(internalformat, width, height, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001260
1261 if (mTexStorage && redefined)
1262 {
1263 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1264 {
1265 for (int f = 0; f < 6; f++)
1266 {
1267 mImageArray[f][i].markDirty();
1268 }
1269 }
1270
1271 delete mTexStorage;
1272 mTexStorage = NULL;
1273
1274 mDirtyImages = true;
1275 }
1276}
1277
1278void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1279{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001280 unsigned int faceindex = faceIndex(target);
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001281 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1282 redefineImage(faceindex, level, internalformat, width, height);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001283
1284 if (!mImageArray[faceindex][level].isRenderableFormat())
1285 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001286 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001287 mDirtyImages = true;
1288 }
1289 else
1290 {
1291 if (!mTexStorage || !mTexStorage->isRenderTarget())
1292 {
1293 convertToRenderTarget();
1294 }
1295
1296 mImageArray[faceindex][level].markClean();
1297
1298 ASSERT(width == height);
1299
1300 if (width > 0 && level < levelCount())
1301 {
1302 RECT sourceRect;
1303 sourceRect.left = x;
1304 sourceRect.right = x + width;
1305 sourceRect.top = y;
1306 sourceRect.bottom = y + height;
1307
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001308 getBlitter()->copy(source, sourceRect, format, 0, 0, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001309
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001310 }
1311 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001312}
1313
1314void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1315{
1316 GLsizei size = mImageArray[faceIndex(target)][level].getWidth();
1317
1318 if (xoffset + width > size || yoffset + height > size)
1319 {
1320 return error(GL_INVALID_VALUE);
1321 }
1322
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001323 unsigned int faceindex = faceIndex(target);
1324
1325 if (!mImageArray[faceindex][level].isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1326 {
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001327 mImageArray[faceindex][level].copy(0, 0, x, y, width, height, source);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001328 mDirtyImages = true;
1329 }
1330 else
1331 {
1332 if (!mTexStorage || !mTexStorage->isRenderTarget())
1333 {
1334 convertToRenderTarget();
1335 }
1336
1337 updateTexture();
1338
1339 if (level < levelCount())
1340 {
1341 RECT sourceRect;
1342 sourceRect.left = x;
1343 sourceRect.right = x + width;
1344 sourceRect.top = y;
1345 sourceRect.bottom = y + height;
1346
daniel@transgaming.com3cef5392012-10-31 19:52:15 +00001347 getBlitter()->copy(source, sourceRect, gl::ExtractFormat(mImageArray[0][0].getInternalFormat()), xoffset, yoffset, mTexStorage, target, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001348
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001349 }
1350 }
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001351}
1352
1353void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
1354{
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001355 delete mTexStorage;
daniel@transgaming.comf032cb82012-10-31 19:51:52 +00001356 mTexStorage = new TextureStorageCubeMap(levels, internalformat, mUsage, false, size);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001357 mImmutable = true;
1358
1359 for (int level = 0; level < levels; level++)
1360 {
1361 for (int face = 0; face < 6; face++)
1362 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001363 mImageArray[face][level].redefine(internalformat, size, size, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001364 size = std::max(1, size >> 1);
1365 }
1366 }
1367
1368 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1369 {
1370 for (int face = 0; face < 6; face++)
1371 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001372 mImageArray[face][level].redefine(GL_NONE, 0, 0, true);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001373 }
1374 }
1375
1376 if (mTexStorage->isManaged())
1377 {
1378 int levels = levelCount();
1379
1380 for (int face = 0; face < 6; face++)
1381 {
1382 for (int level = 0; level < levels; level++)
1383 {
daniel@transgaming.com0f195ad2012-10-31 19:51:59 +00001384 mImageArray[face][level].setManagedSurface(mTexStorage, face, level);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001385 }
1386 }
1387 }
1388}
1389
1390void TextureCubeMap::generateMipmaps()
1391{
1392 if (!isCubeComplete())
1393 {
1394 return error(GL_INVALID_OPERATION);
1395 }
1396
1397 if (!getContext()->supportsNonPower2Texture())
1398 {
1399 if (!isPow2(mImageArray[0][0].getWidth()))
1400 {
1401 return error(GL_INVALID_OPERATION);
1402 }
1403 }
1404
1405 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
1406 unsigned int q = log2(mImageArray[0][0].getWidth());
1407 for (unsigned int f = 0; f < 6; f++)
1408 {
1409 for (unsigned int i = 1; i <= q; i++)
1410 {
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001411 redefineImage(f, i, mImageArray[f][0].getInternalFormat(),
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001412 std::max(mImageArray[f][0].getWidth() >> i, 1),
daniel@transgaming.com6452adf2012-10-17 18:22:35 +00001413 std::max(mImageArray[f][0].getWidth() >> i, 1));
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001414 }
1415 }
1416
1417 if (mTexStorage && mTexStorage->isRenderTarget())
1418 {
1419 for (unsigned int f = 0; f < 6; f++)
1420 {
1421 for (unsigned int i = 1; i <= q; i++)
1422 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +00001423 mTexStorage->generateMipmap(f, i);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001424
1425 mImageArray[f][i].markClean();
1426 }
1427 }
1428 }
1429 else
1430 {
1431 for (unsigned int f = 0; f < 6; f++)
1432 {
1433 for (unsigned int i = 1; i <= q; i++)
1434 {
daniel@transgaming.com0ad830b2012-10-31 19:52:12 +00001435 Image::GenerateMipmap(&mImageArray[f][i], &mImageArray[f][i - 1]);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001436 }
1437 }
1438 }
1439}
1440
1441Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
1442{
1443 if (!IsCubemapTextureTarget(target))
1444 {
1445 return error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
1446 }
1447
1448 unsigned int face = faceIndex(target);
1449
1450 if (mFaceProxies[face] == NULL)
1451 {
1452 mFaceProxies[face] = new Renderbuffer(id(), new RenderbufferTextureCubeMap(this, target));
1453 }
1454
1455 return mFaceProxies[face];
1456}
1457
1458// Increments refcount on surface.
1459// caller must Release() the returned surface
1460IDirect3DSurface9 *TextureCubeMap::getRenderTarget(GLenum target)
1461{
1462 ASSERT(IsCubemapTextureTarget(target));
1463
1464 // ensure the underlying texture is created
1465 if (getStorage(true) == NULL)
1466 {
1467 return NULL;
1468 }
1469
1470 updateTexture();
1471
daniel@transgaming.com2b5af7b2012-09-27 17:46:15 +00001472 return mTexStorage->getCubeMapSurface(target, 0, false);
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001473}
1474
daniel@transgaming.com690d8ae2012-10-31 19:52:08 +00001475int TextureCubeMap::levelCount()
1476{
1477 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
1478}
1479
daniel@transgaming.com95a758f2012-07-12 15:17:06 +00001480TextureStorage *TextureCubeMap::getStorage(bool renderTarget)
1481{
1482 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
1483 {
1484 if (renderTarget)
1485 {
1486 convertToRenderTarget();
1487 }
1488 else
1489 {
1490 createTexture();
1491 }
1492 }
1493
1494 return mTexStorage;
1495}
1496
1497}