gfx/angle/src/libGLESv2/Texture.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:992d41cc93f3
1 #include "precompiled.h"
2 //
3 // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
6 //
7
8 // Texture.cpp: Implements the gl::Texture class and its derived classes
9 // Texture2D and TextureCubeMap. Implements GL texture objects and related
10 // functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
11
12 #include "libGLESv2/Texture.h"
13
14 #include "libGLESv2/main.h"
15 #include "libGLESv2/mathutil.h"
16 #include "libGLESv2/utilities.h"
17 #include "libGLESv2/renderer/Blit.h"
18 #include "libGLESv2/Renderbuffer.h"
19 #include "libGLESv2/renderer/Image.h"
20 #include "libGLESv2/renderer/Renderer.h"
21 #include "libGLESv2/renderer/TextureStorage.h"
22 #include "libEGL/Surface.h"
23
24 namespace gl
25 {
26
27 Texture::Texture(rx::Renderer *renderer, GLuint id) : RefCountObject(id)
28 {
29 mRenderer = renderer;
30
31 mSamplerState.minFilter = GL_NEAREST_MIPMAP_LINEAR;
32 mSamplerState.magFilter = GL_LINEAR;
33 mSamplerState.wrapS = GL_REPEAT;
34 mSamplerState.wrapT = GL_REPEAT;
35 mSamplerState.maxAnisotropy = 1.0f;
36 mSamplerState.lodOffset = 0;
37 mUsage = GL_NONE;
38
39 mDirtyImages = true;
40
41 mImmutable = false;
42 }
43
44 Texture::~Texture()
45 {
46 }
47
48 // Returns true on successful filter state update (valid enum parameter)
49 bool 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 mSamplerState.minFilter = filter;
60 return true;
61 default:
62 return false;
63 }
64 }
65
66 // Returns true on successful filter state update (valid enum parameter)
67 bool Texture::setMagFilter(GLenum filter)
68 {
69 switch (filter)
70 {
71 case GL_NEAREST:
72 case GL_LINEAR:
73 mSamplerState.magFilter = filter;
74 return true;
75 default:
76 return false;
77 }
78 }
79
80 // Returns true on successful wrap state update (valid enum parameter)
81 bool Texture::setWrapS(GLenum wrap)
82 {
83 switch (wrap)
84 {
85 case GL_REPEAT:
86 case GL_CLAMP_TO_EDGE:
87 case GL_MIRRORED_REPEAT:
88 mSamplerState.wrapS = wrap;
89 return true;
90 default:
91 return false;
92 }
93 }
94
95 // Returns true on successful wrap state update (valid enum parameter)
96 bool Texture::setWrapT(GLenum wrap)
97 {
98 switch (wrap)
99 {
100 case GL_REPEAT:
101 case GL_CLAMP_TO_EDGE:
102 case GL_MIRRORED_REPEAT:
103 mSamplerState.wrapT = wrap;
104 return true;
105 default:
106 return false;
107 }
108 }
109
110 // Returns true on successful max anisotropy update (valid anisotropy value)
111 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy, float contextMaxAnisotropy)
112 {
113 textureMaxAnisotropy = std::min(textureMaxAnisotropy, contextMaxAnisotropy);
114 if (textureMaxAnisotropy < 1.0f)
115 {
116 return false;
117 }
118
119 mSamplerState.maxAnisotropy = textureMaxAnisotropy;
120
121 return true;
122 }
123
124 // Returns true on successful usage state update (valid enum parameter)
125 bool Texture::setUsage(GLenum usage)
126 {
127 switch (usage)
128 {
129 case GL_NONE:
130 case GL_FRAMEBUFFER_ATTACHMENT_ANGLE:
131 mUsage = usage;
132 return true;
133 default:
134 return false;
135 }
136 }
137
138 GLenum Texture::getMinFilter() const
139 {
140 return mSamplerState.minFilter;
141 }
142
143 GLenum Texture::getMagFilter() const
144 {
145 return mSamplerState.magFilter;
146 }
147
148 GLenum Texture::getWrapS() const
149 {
150 return mSamplerState.wrapS;
151 }
152
153 GLenum Texture::getWrapT() const
154 {
155 return mSamplerState.wrapT;
156 }
157
158 float Texture::getMaxAnisotropy() const
159 {
160 return mSamplerState.maxAnisotropy;
161 }
162
163 int Texture::getLodOffset()
164 {
165 rx::TextureStorageInterface *texture = getStorage(false);
166 return texture ? texture->getLodOffset() : 0;
167 }
168
169 void Texture::getSamplerState(SamplerState *sampler)
170 {
171 *sampler = mSamplerState;
172 sampler->lodOffset = getLodOffset();
173 }
174
175 GLenum Texture::getUsage() const
176 {
177 return mUsage;
178 }
179
180 bool Texture::isMipmapFiltered() const
181 {
182 switch (mSamplerState.minFilter)
183 {
184 case GL_NEAREST:
185 case GL_LINEAR:
186 return false;
187 case GL_NEAREST_MIPMAP_NEAREST:
188 case GL_LINEAR_MIPMAP_NEAREST:
189 case GL_NEAREST_MIPMAP_LINEAR:
190 case GL_LINEAR_MIPMAP_LINEAR:
191 return true;
192 default: UNREACHABLE();
193 return false;
194 }
195 }
196
197 void Texture::setImage(GLint unpackAlignment, const void *pixels, rx::Image *image)
198 {
199 if (pixels != NULL)
200 {
201 image->loadData(0, 0, image->getWidth(), image->getHeight(), unpackAlignment, pixels);
202 mDirtyImages = true;
203 }
204 }
205
206 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, rx::Image *image)
207 {
208 if (pixels != NULL)
209 {
210 image->loadCompressedData(0, 0, image->getWidth(), image->getHeight(), pixels);
211 mDirtyImages = true;
212 }
213 }
214
215 bool Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, rx::Image *image)
216 {
217 if (pixels != NULL)
218 {
219 image->loadData(xoffset, yoffset, width, height, unpackAlignment, pixels);
220 mDirtyImages = true;
221 }
222
223 return true;
224 }
225
226 bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, rx::Image *image)
227 {
228 if (pixels != NULL)
229 {
230 image->loadCompressedData(xoffset, yoffset, width, height, pixels);
231 mDirtyImages = true;
232 }
233
234 return true;
235 }
236
237 rx::TextureStorageInterface *Texture::getNativeTexture()
238 {
239 // ensure the underlying texture is created
240
241 rx::TextureStorageInterface *storage = getStorage(false);
242 if (storage)
243 {
244 updateTexture();
245 }
246
247 return storage;
248 }
249
250 bool Texture::hasDirtyImages() const
251 {
252 return mDirtyImages;
253 }
254
255 void Texture::resetDirty()
256 {
257 mDirtyImages = false;
258 }
259
260 unsigned int Texture::getTextureSerial()
261 {
262 rx::TextureStorageInterface *texture = getStorage(false);
263 return texture ? texture->getTextureSerial() : 0;
264 }
265
266 unsigned int Texture::getRenderTargetSerial(GLenum target)
267 {
268 rx::TextureStorageInterface *texture = getStorage(true);
269 return texture ? texture->getRenderTargetSerial(target) : 0;
270 }
271
272 bool Texture::isImmutable() const
273 {
274 return mImmutable;
275 }
276
277 GLint Texture::creationLevels(GLsizei width, GLsizei height) const
278 {
279 if ((isPow2(width) && isPow2(height)) || mRenderer->getNonPower2TextureSupport())
280 {
281 return 0; // Maximum number of levels
282 }
283 else
284 {
285 // OpenGL ES 2.0 without GL_OES_texture_npot does not permit NPOT mipmaps.
286 return 1;
287 }
288 }
289
290 GLint Texture::creationLevels(GLsizei size) const
291 {
292 return creationLevels(size, size);
293 }
294
295 Texture2D::Texture2D(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
296 {
297 mTexStorage = NULL;
298 mSurface = NULL;
299 mColorbufferProxy = NULL;
300 mProxyRefs = 0;
301
302 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
303 {
304 mImageArray[i] = renderer->createImage();
305 }
306 }
307
308 Texture2D::~Texture2D()
309 {
310 mColorbufferProxy = NULL;
311
312 delete mTexStorage;
313 mTexStorage = NULL;
314
315 if (mSurface)
316 {
317 mSurface->setBoundTexture(NULL);
318 mSurface = NULL;
319 }
320
321 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++i)
322 {
323 delete mImageArray[i];
324 }
325 }
326
327 // We need to maintain a count of references to renderbuffers acting as
328 // proxies for this texture, so that we do not attempt to use a pointer
329 // to a renderbuffer proxy which has been deleted.
330 void Texture2D::addProxyRef(const Renderbuffer *proxy)
331 {
332 mProxyRefs++;
333 }
334
335 void Texture2D::releaseProxy(const Renderbuffer *proxy)
336 {
337 if (mProxyRefs > 0)
338 mProxyRefs--;
339
340 if (mProxyRefs == 0)
341 mColorbufferProxy = NULL;
342 }
343
344 GLenum Texture2D::getTarget() const
345 {
346 return GL_TEXTURE_2D;
347 }
348
349 GLsizei Texture2D::getWidth(GLint level) const
350 {
351 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
352 return mImageArray[level]->getWidth();
353 else
354 return 0;
355 }
356
357 GLsizei Texture2D::getHeight(GLint level) const
358 {
359 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
360 return mImageArray[level]->getHeight();
361 else
362 return 0;
363 }
364
365 GLenum Texture2D::getInternalFormat(GLint level) const
366 {
367 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
368 return mImageArray[level]->getInternalFormat();
369 else
370 return GL_NONE;
371 }
372
373 GLenum Texture2D::getActualFormat(GLint level) const
374 {
375 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
376 return mImageArray[level]->getActualFormat();
377 else
378 return D3DFMT_UNKNOWN;
379 }
380
381 void Texture2D::redefineImage(GLint level, GLint internalformat, GLsizei width, GLsizei height)
382 {
383 releaseTexImage();
384
385 // If there currently is a corresponding storage texture image, it has these parameters
386 const int storageWidth = std::max(1, mImageArray[0]->getWidth() >> level);
387 const int storageHeight = std::max(1, mImageArray[0]->getHeight() >> level);
388 const int storageFormat = mImageArray[0]->getInternalFormat();
389
390 mImageArray[level]->redefine(mRenderer, internalformat, width, height, false);
391
392 if (mTexStorage)
393 {
394 const int storageLevels = mTexStorage->levelCount();
395
396 if ((level >= storageLevels && storageLevels != 0) ||
397 width != storageWidth ||
398 height != storageHeight ||
399 internalformat != storageFormat) // Discard mismatched storage
400 {
401 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
402 {
403 mImageArray[i]->markDirty();
404 }
405
406 delete mTexStorage;
407 mTexStorage = NULL;
408 mDirtyImages = true;
409 }
410 }
411 }
412
413 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
414 {
415 GLint internalformat = ConvertSizedInternalFormat(format, type);
416 redefineImage(level, internalformat, width, height);
417
418 Texture::setImage(unpackAlignment, pixels, mImageArray[level]);
419 }
420
421 void Texture2D::bindTexImage(egl::Surface *surface)
422 {
423 releaseTexImage();
424
425 GLint internalformat = surface->getFormat();
426
427 mImageArray[0]->redefine(mRenderer, internalformat, surface->getWidth(), surface->getHeight(), true);
428
429 delete mTexStorage;
430 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, surface->getSwapChain());
431
432 mDirtyImages = true;
433 mSurface = surface;
434 mSurface->setBoundTexture(this);
435 }
436
437 void Texture2D::releaseTexImage()
438 {
439 if (mSurface)
440 {
441 mSurface->setBoundTexture(NULL);
442 mSurface = NULL;
443
444 if (mTexStorage)
445 {
446 delete mTexStorage;
447 mTexStorage = NULL;
448 }
449
450 for (int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
451 {
452 mImageArray[i]->redefine(mRenderer, GL_NONE, 0, 0, true);
453 }
454 }
455 }
456
457 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
458 {
459 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
460 redefineImage(level, format, width, height);
461
462 Texture::setCompressedImage(imageSize, pixels, mImageArray[level]);
463 }
464
465 void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
466 {
467 if (level < levelCount())
468 {
469 rx::Image *image = mImageArray[level];
470 if (image->updateSurface(mTexStorage, level, xoffset, yoffset, width, height))
471 {
472 image->markClean();
473 }
474 }
475 }
476
477 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
478 {
479 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[level]))
480 {
481 commitRect(level, xoffset, yoffset, width, height);
482 }
483 }
484
485 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
486 {
487 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[level]))
488 {
489 commitRect(level, xoffset, yoffset, width, height);
490 }
491 }
492
493 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
494 {
495 GLint internalformat = ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
496 redefineImage(level, internalformat, width, height);
497
498 if (!mImageArray[level]->isRenderableFormat())
499 {
500 mImageArray[level]->copy(0, 0, x, y, width, height, source);
501 mDirtyImages = true;
502 }
503 else
504 {
505 if (!mTexStorage || !mTexStorage->isRenderTarget())
506 {
507 convertToRenderTarget();
508 }
509
510 mImageArray[level]->markClean();
511
512 if (width != 0 && height != 0 && level < levelCount())
513 {
514 gl::Rectangle sourceRect;
515 sourceRect.x = x;
516 sourceRect.width = width;
517 sourceRect.y = y;
518 sourceRect.height = height;
519
520 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, level);
521 }
522 }
523 }
524
525 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
526 {
527 if (xoffset + width > mImageArray[level]->getWidth() || yoffset + height > mImageArray[level]->getHeight())
528 {
529 return gl::error(GL_INVALID_VALUE);
530 }
531
532 if (!mImageArray[level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
533 {
534 mImageArray[level]->copy(xoffset, yoffset, x, y, width, height, source);
535 mDirtyImages = true;
536 }
537 else
538 {
539 if (!mTexStorage || !mTexStorage->isRenderTarget())
540 {
541 convertToRenderTarget();
542 }
543
544 updateTexture();
545
546 if (level < levelCount())
547 {
548 gl::Rectangle sourceRect;
549 sourceRect.x = x;
550 sourceRect.width = width;
551 sourceRect.y = y;
552 sourceRect.height = height;
553
554 mRenderer->copyImage(source, sourceRect,
555 gl::ExtractFormat(mImageArray[0]->getInternalFormat()),
556 xoffset, yoffset, mTexStorage, level);
557 }
558 }
559 }
560
561 void Texture2D::storage(GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
562 {
563 delete mTexStorage;
564 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height);
565 mImmutable = true;
566
567 for (int level = 0; level < levels; level++)
568 {
569 mImageArray[level]->redefine(mRenderer, internalformat, width, height, true);
570 width = std::max(1, width >> 1);
571 height = std::max(1, height >> 1);
572 }
573
574 for (int level = levels; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
575 {
576 mImageArray[level]->redefine(mRenderer, GL_NONE, 0, 0, true);
577 }
578
579 if (mTexStorage->isManaged())
580 {
581 int levels = levelCount();
582
583 for (int level = 0; level < levels; level++)
584 {
585 mImageArray[level]->setManagedSurface(mTexStorage, level);
586 }
587 }
588 }
589
590 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
591 bool Texture2D::isSamplerComplete() const
592 {
593 GLsizei width = mImageArray[0]->getWidth();
594 GLsizei height = mImageArray[0]->getHeight();
595
596 if (width <= 0 || height <= 0)
597 {
598 return false;
599 }
600
601 bool mipmapping = isMipmapFiltered();
602 bool filtering, renderable;
603
604 if ((IsFloat32Format(getInternalFormat(0)) && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) ||
605 (IsFloat16Format(getInternalFormat(0)) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable)))
606 {
607 if (mSamplerState.magFilter != GL_NEAREST ||
608 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
609 {
610 return false;
611 }
612 }
613
614 bool npotSupport = mRenderer->getNonPower2TextureSupport();
615
616 if (!npotSupport)
617 {
618 if ((mSamplerState.wrapS != GL_CLAMP_TO_EDGE && !isPow2(width)) ||
619 (mSamplerState.wrapT != GL_CLAMP_TO_EDGE && !isPow2(height)))
620 {
621 return false;
622 }
623 }
624
625 if (mipmapping)
626 {
627 if (!npotSupport)
628 {
629 if (!isPow2(width) || !isPow2(height))
630 {
631 return false;
632 }
633 }
634
635 if (!isMipmapComplete())
636 {
637 return false;
638 }
639 }
640
641 return true;
642 }
643
644 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
645 bool Texture2D::isMipmapComplete() const
646 {
647 if (isImmutable())
648 {
649 return true;
650 }
651
652 GLsizei width = mImageArray[0]->getWidth();
653 GLsizei height = mImageArray[0]->getHeight();
654
655 if (width <= 0 || height <= 0)
656 {
657 return false;
658 }
659
660 int q = log2(std::max(width, height));
661
662 for (int level = 1; level <= q; level++)
663 {
664 if (mImageArray[level]->getInternalFormat() != mImageArray[0]->getInternalFormat())
665 {
666 return false;
667 }
668
669 if (mImageArray[level]->getWidth() != std::max(1, width >> level))
670 {
671 return false;
672 }
673
674 if (mImageArray[level]->getHeight() != std::max(1, height >> level))
675 {
676 return false;
677 }
678 }
679
680 return true;
681 }
682
683 bool Texture2D::isCompressed(GLint level) const
684 {
685 return IsCompressed(getInternalFormat(level));
686 }
687
688 bool Texture2D::isDepth(GLint level) const
689 {
690 return IsDepthTexture(getInternalFormat(level));
691 }
692
693 // Constructs a native texture resource from the texture images
694 void Texture2D::createTexture()
695 {
696 GLsizei width = mImageArray[0]->getWidth();
697 GLsizei height = mImageArray[0]->getHeight();
698
699 if (!(width > 0 && height > 0))
700 return; // do not attempt to create native textures for nonexistant data
701
702 GLint levels = creationLevels(width, height);
703 GLenum internalformat = mImageArray[0]->getInternalFormat();
704
705 delete mTexStorage;
706 mTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, mUsage, false, width, height);
707
708 if (mTexStorage->isManaged())
709 {
710 int levels = levelCount();
711
712 for (int level = 0; level < levels; level++)
713 {
714 mImageArray[level]->setManagedSurface(mTexStorage, level);
715 }
716 }
717
718 mDirtyImages = true;
719 }
720
721 void Texture2D::updateTexture()
722 {
723 bool mipmapping = (isMipmapFiltered() && isMipmapComplete());
724
725 int levels = (mipmapping ? levelCount() : 1);
726
727 for (int level = 0; level < levels; level++)
728 {
729 rx::Image *image = mImageArray[level];
730
731 if (image->isDirty())
732 {
733 commitRect(level, 0, 0, mImageArray[level]->getWidth(), mImageArray[level]->getHeight());
734 }
735 }
736 }
737
738 void Texture2D::convertToRenderTarget()
739 {
740 rx::TextureStorageInterface2D *newTexStorage = NULL;
741
742 if (mImageArray[0]->getWidth() != 0 && mImageArray[0]->getHeight() != 0)
743 {
744 GLsizei width = mImageArray[0]->getWidth();
745 GLsizei height = mImageArray[0]->getHeight();
746 GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(width, height);
747 GLenum internalformat = mImageArray[0]->getInternalFormat();
748
749 newTexStorage = new rx::TextureStorageInterface2D(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, width, height);
750
751 if (mTexStorage != NULL)
752 {
753 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
754 {
755 delete newTexStorage;
756 return gl::error(GL_OUT_OF_MEMORY);
757 }
758 }
759 }
760
761 delete mTexStorage;
762 mTexStorage = newTexStorage;
763
764 mDirtyImages = true;
765 }
766
767 void Texture2D::generateMipmaps()
768 {
769 if (!mRenderer->getNonPower2TextureSupport())
770 {
771 if (!isPow2(mImageArray[0]->getWidth()) || !isPow2(mImageArray[0]->getHeight()))
772 {
773 return gl::error(GL_INVALID_OPERATION);
774 }
775 }
776
777 // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
778 unsigned int q = log2(std::max(mImageArray[0]->getWidth(), mImageArray[0]->getHeight()));
779 for (unsigned int i = 1; i <= q; i++)
780 {
781 redefineImage(i, mImageArray[0]->getInternalFormat(),
782 std::max(mImageArray[0]->getWidth() >> i, 1),
783 std::max(mImageArray[0]->getHeight() >> i, 1));
784 }
785
786 if (mTexStorage && mTexStorage->isRenderTarget())
787 {
788 for (unsigned int i = 1; i <= q; i++)
789 {
790 mTexStorage->generateMipmap(i);
791
792 mImageArray[i]->markClean();
793 }
794 }
795 else
796 {
797 for (unsigned int i = 1; i <= q; i++)
798 {
799 mRenderer->generateMipmap(mImageArray[i], mImageArray[i - 1]);
800 }
801 }
802 }
803
804 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
805 {
806 if (target != GL_TEXTURE_2D)
807 {
808 return gl::error(GL_INVALID_OPERATION, (Renderbuffer *)NULL);
809 }
810
811 if (mColorbufferProxy == NULL)
812 {
813 mColorbufferProxy = new Renderbuffer(mRenderer, id(), new RenderbufferTexture2D(this, target));
814 }
815
816 return mColorbufferProxy;
817 }
818
819 rx::RenderTarget *Texture2D::getRenderTarget(GLenum target)
820 {
821 ASSERT(target == GL_TEXTURE_2D);
822
823 // ensure the underlying texture is created
824 if (getStorage(true) == NULL)
825 {
826 return NULL;
827 }
828
829 updateTexture();
830
831 // ensure this is NOT a depth texture
832 if (isDepth(0))
833 {
834 return NULL;
835 }
836
837 return mTexStorage->getRenderTarget();
838 }
839
840 rx::RenderTarget *Texture2D::getDepthStencil(GLenum target)
841 {
842 ASSERT(target == GL_TEXTURE_2D);
843
844 // ensure the underlying texture is created
845 if (getStorage(true) == NULL)
846 {
847 return NULL;
848 }
849
850 updateTexture();
851
852 // ensure this is actually a depth texture
853 if (!isDepth(0))
854 {
855 return NULL;
856 }
857 return mTexStorage->getRenderTarget();
858 }
859
860 int Texture2D::levelCount()
861 {
862 return mTexStorage ? mTexStorage->levelCount() : 0;
863 }
864
865 rx::TextureStorageInterface *Texture2D::getStorage(bool renderTarget)
866 {
867 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
868 {
869 if (renderTarget)
870 {
871 convertToRenderTarget();
872 }
873 else
874 {
875 createTexture();
876 }
877 }
878
879 return mTexStorage;
880 }
881
882 TextureCubeMap::TextureCubeMap(rx::Renderer *renderer, GLuint id) : Texture(renderer, id)
883 {
884 mTexStorage = NULL;
885 for (int i = 0; i < 6; i++)
886 {
887 mFaceProxies[i] = NULL;
888 mFaceProxyRefs[i] = 0;
889
890 for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
891 {
892 mImageArray[i][j] = renderer->createImage();
893 }
894 }
895 }
896
897 TextureCubeMap::~TextureCubeMap()
898 {
899 for (int i = 0; i < 6; i++)
900 {
901 mFaceProxies[i] = NULL;
902
903 for (int j = 0; j < IMPLEMENTATION_MAX_TEXTURE_LEVELS; ++j)
904 {
905 delete mImageArray[i][j];
906 }
907 }
908
909 delete mTexStorage;
910 mTexStorage = NULL;
911 }
912
913 // We need to maintain a count of references to renderbuffers acting as
914 // proxies for this texture, so that the texture is not deleted while
915 // proxy references still exist. If the reference count drops to zero,
916 // we set our proxy pointer NULL, so that a new attempt at referencing
917 // will cause recreation.
918 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
919 {
920 for (int i = 0; i < 6; i++)
921 {
922 if (mFaceProxies[i] == proxy)
923 mFaceProxyRefs[i]++;
924 }
925 }
926
927 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
928 {
929 for (int i = 0; i < 6; i++)
930 {
931 if (mFaceProxies[i] == proxy)
932 {
933 if (mFaceProxyRefs[i] > 0)
934 mFaceProxyRefs[i]--;
935
936 if (mFaceProxyRefs[i] == 0)
937 mFaceProxies[i] = NULL;
938 }
939 }
940 }
941
942 GLenum TextureCubeMap::getTarget() const
943 {
944 return GL_TEXTURE_CUBE_MAP;
945 }
946
947 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
948 {
949 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
950 return mImageArray[faceIndex(target)][level]->getWidth();
951 else
952 return 0;
953 }
954
955 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
956 {
957 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
958 return mImageArray[faceIndex(target)][level]->getHeight();
959 else
960 return 0;
961 }
962
963 GLenum TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
964 {
965 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
966 return mImageArray[faceIndex(target)][level]->getInternalFormat();
967 else
968 return GL_NONE;
969 }
970
971 GLenum TextureCubeMap::getActualFormat(GLenum target, GLint level) const
972 {
973 if (level < IMPLEMENTATION_MAX_TEXTURE_LEVELS)
974 return mImageArray[faceIndex(target)][level]->getActualFormat();
975 else
976 return D3DFMT_UNKNOWN;
977 }
978
979 void TextureCubeMap::setImagePosX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
980 {
981 setImage(0, level, width, height, format, type, unpackAlignment, pixels);
982 }
983
984 void TextureCubeMap::setImageNegX(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
985 {
986 setImage(1, level, width, height, format, type, unpackAlignment, pixels);
987 }
988
989 void TextureCubeMap::setImagePosY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
990 {
991 setImage(2, level, width, height, format, type, unpackAlignment, pixels);
992 }
993
994 void TextureCubeMap::setImageNegY(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
995 {
996 setImage(3, level, width, height, format, type, unpackAlignment, pixels);
997 }
998
999 void TextureCubeMap::setImagePosZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1000 {
1001 setImage(4, level, width, height, format, type, unpackAlignment, pixels);
1002 }
1003
1004 void TextureCubeMap::setImageNegZ(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1005 {
1006 setImage(5, level, width, height, format, type, unpackAlignment, pixels);
1007 }
1008
1009 void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1010 {
1011 // compressed formats don't have separate sized internal formats-- we can just use the compressed format directly
1012 redefineImage(faceIndex(face), level, format, width, height);
1013
1014 Texture::setCompressedImage(imageSize, pixels, mImageArray[faceIndex(face)][level]);
1015 }
1016
1017 void TextureCubeMap::commitRect(int face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
1018 {
1019 if (level < levelCount())
1020 {
1021 rx::Image *image = mImageArray[face][level];
1022 if (image->updateSurface(mTexStorage, face, level, xoffset, yoffset, width, height))
1023 image->markClean();
1024 }
1025 }
1026
1027 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1028 {
1029 if (Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, mImageArray[faceIndex(target)][level]))
1030 {
1031 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1032 }
1033 }
1034
1035 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1036 {
1037 if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, mImageArray[faceIndex(target)][level]))
1038 {
1039 commitRect(faceIndex(target), level, xoffset, yoffset, width, height);
1040 }
1041 }
1042
1043 // Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
1044 bool TextureCubeMap::isSamplerComplete() const
1045 {
1046 int size = mImageArray[0][0]->getWidth();
1047
1048 bool mipmapping = isMipmapFiltered();
1049 bool filtering, renderable;
1050
1051 if ((gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0)) == GL_FLOAT && !mRenderer->getFloat32TextureSupport(&filtering, &renderable)) ||
1052 (gl::ExtractType(getInternalFormat(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0) == GL_HALF_FLOAT_OES) && !mRenderer->getFloat16TextureSupport(&filtering, &renderable)))
1053 {
1054 if (mSamplerState.magFilter != GL_NEAREST ||
1055 (mSamplerState.minFilter != GL_NEAREST && mSamplerState.minFilter != GL_NEAREST_MIPMAP_NEAREST))
1056 {
1057 return false;
1058 }
1059 }
1060
1061 if (!isPow2(size) && !mRenderer->getNonPower2TextureSupport())
1062 {
1063 if (mSamplerState.wrapS != GL_CLAMP_TO_EDGE || mSamplerState.wrapT != GL_CLAMP_TO_EDGE || mipmapping)
1064 {
1065 return false;
1066 }
1067 }
1068
1069 if (!mipmapping)
1070 {
1071 if (!isCubeComplete())
1072 {
1073 return false;
1074 }
1075 }
1076 else
1077 {
1078 if (!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1079 {
1080 return false;
1081 }
1082 }
1083
1084 return true;
1085 }
1086
1087 // Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
1088 bool TextureCubeMap::isCubeComplete() const
1089 {
1090 if (mImageArray[0][0]->getWidth() <= 0 || mImageArray[0][0]->getHeight() != mImageArray[0][0]->getWidth())
1091 {
1092 return false;
1093 }
1094
1095 for (unsigned int face = 1; face < 6; face++)
1096 {
1097 if (mImageArray[face][0]->getWidth() != mImageArray[0][0]->getWidth() ||
1098 mImageArray[face][0]->getWidth() != mImageArray[0][0]->getHeight() ||
1099 mImageArray[face][0]->getInternalFormat() != mImageArray[0][0]->getInternalFormat())
1100 {
1101 return false;
1102 }
1103 }
1104
1105 return true;
1106 }
1107
1108 bool TextureCubeMap::isMipmapCubeComplete() const
1109 {
1110 if (isImmutable())
1111 {
1112 return true;
1113 }
1114
1115 if (!isCubeComplete())
1116 {
1117 return false;
1118 }
1119
1120 GLsizei size = mImageArray[0][0]->getWidth();
1121
1122 int q = log2(size);
1123
1124 for (int face = 0; face < 6; face++)
1125 {
1126 for (int level = 1; level <= q; level++)
1127 {
1128 if (mImageArray[face][level]->getInternalFormat() != mImageArray[0][0]->getInternalFormat())
1129 {
1130 return false;
1131 }
1132
1133 if (mImageArray[face][level]->getWidth() != std::max(1, size >> level))
1134 {
1135 return false;
1136 }
1137 }
1138 }
1139
1140 return true;
1141 }
1142
1143 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1144 {
1145 return IsCompressed(getInternalFormat(target, level));
1146 }
1147
1148 // Constructs a native texture resource from the texture images, or returns an existing one
1149 void TextureCubeMap::createTexture()
1150 {
1151 GLsizei size = mImageArray[0][0]->getWidth();
1152
1153 if (!(size > 0))
1154 return; // do not attempt to create native textures for nonexistant data
1155
1156 GLint levels = creationLevels(size);
1157 GLenum internalformat = mImageArray[0][0]->getInternalFormat();
1158
1159 delete mTexStorage;
1160 mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size);
1161
1162 if (mTexStorage->isManaged())
1163 {
1164 int levels = levelCount();
1165
1166 for (int face = 0; face < 6; face++)
1167 {
1168 for (int level = 0; level < levels; level++)
1169 {
1170 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level);
1171 }
1172 }
1173 }
1174
1175 mDirtyImages = true;
1176 }
1177
1178 void TextureCubeMap::updateTexture()
1179 {
1180 bool mipmapping = isMipmapFiltered() && isMipmapCubeComplete();
1181
1182 for (int face = 0; face < 6; face++)
1183 {
1184 int levels = (mipmapping ? levelCount() : 1);
1185
1186 for (int level = 0; level < levels; level++)
1187 {
1188 rx::Image *image = mImageArray[face][level];
1189
1190 if (image->isDirty())
1191 {
1192 commitRect(face, level, 0, 0, image->getWidth(), image->getHeight());
1193 }
1194 }
1195 }
1196 }
1197
1198 void TextureCubeMap::convertToRenderTarget()
1199 {
1200 rx::TextureStorageInterfaceCube *newTexStorage = NULL;
1201
1202 if (mImageArray[0][0]->getWidth() != 0)
1203 {
1204 GLsizei size = mImageArray[0][0]->getWidth();
1205 GLint levels = mTexStorage != NULL ? mTexStorage->levelCount() : creationLevels(size);
1206 GLenum internalformat = mImageArray[0][0]->getInternalFormat();
1207
1208 newTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, GL_FRAMEBUFFER_ATTACHMENT_ANGLE, true, size);
1209
1210 if (mTexStorage != NULL)
1211 {
1212 if (!mRenderer->copyToRenderTarget(newTexStorage, mTexStorage))
1213 {
1214 delete newTexStorage;
1215 return gl::error(GL_OUT_OF_MEMORY);
1216 }
1217 }
1218 }
1219
1220 delete mTexStorage;
1221 mTexStorage = newTexStorage;
1222
1223 mDirtyImages = true;
1224 }
1225
1226 void TextureCubeMap::setImage(int faceIndex, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
1227 {
1228 GLint internalformat = ConvertSizedInternalFormat(format, type);
1229 redefineImage(faceIndex, level, internalformat, width, height);
1230
1231 Texture::setImage(unpackAlignment, pixels, mImageArray[faceIndex][level]);
1232 }
1233
1234 unsigned int TextureCubeMap::faceIndex(GLenum face)
1235 {
1236 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_X - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 1);
1237 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 2);
1238 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 3);
1239 META_ASSERT(GL_TEXTURE_CUBE_MAP_POSITIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 4);
1240 META_ASSERT(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z - GL_TEXTURE_CUBE_MAP_POSITIVE_X == 5);
1241
1242 return face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
1243 }
1244
1245 void TextureCubeMap::redefineImage(int face, GLint level, GLint internalformat, GLsizei width, GLsizei height)
1246 {
1247 // If there currently is a corresponding storage texture image, it has these parameters
1248 const int storageWidth = std::max(1, mImageArray[0][0]->getWidth() >> level);
1249 const int storageHeight = std::max(1, mImageArray[0][0]->getHeight() >> level);
1250 const int storageFormat = mImageArray[0][0]->getInternalFormat();
1251
1252 mImageArray[face][level]->redefine(mRenderer, internalformat, width, height, false);
1253
1254 if (mTexStorage)
1255 {
1256 const int storageLevels = mTexStorage->levelCount();
1257
1258 if ((level >= storageLevels && storageLevels != 0) ||
1259 width != storageWidth ||
1260 height != storageHeight ||
1261 internalformat != storageFormat) // Discard mismatched storage
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 }
1278
1279 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
1280 {
1281 unsigned int faceindex = faceIndex(target);
1282 GLint internalformat = gl::ConvertSizedInternalFormat(format, GL_UNSIGNED_BYTE);
1283 redefineImage(faceindex, level, internalformat, width, height);
1284
1285 if (!mImageArray[faceindex][level]->isRenderableFormat())
1286 {
1287 mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source);
1288 mDirtyImages = true;
1289 }
1290 else
1291 {
1292 if (!mTexStorage || !mTexStorage->isRenderTarget())
1293 {
1294 convertToRenderTarget();
1295 }
1296
1297 mImageArray[faceindex][level]->markClean();
1298
1299 ASSERT(width == height);
1300
1301 if (width > 0 && level < levelCount())
1302 {
1303 gl::Rectangle sourceRect;
1304 sourceRect.x = x;
1305 sourceRect.width = width;
1306 sourceRect.y = y;
1307 sourceRect.height = height;
1308
1309 mRenderer->copyImage(source, sourceRect, format, 0, 0, mTexStorage, target, level);
1310 }
1311 }
1312 }
1313
1314 void 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 gl::error(GL_INVALID_VALUE);
1321 }
1322
1323 unsigned int faceindex = faceIndex(target);
1324
1325 if (!mImageArray[faceindex][level]->isRenderableFormat() || (!mTexStorage && !isSamplerComplete()))
1326 {
1327 mImageArray[faceindex][level]->copy(0, 0, x, y, width, height, source);
1328 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 gl::Rectangle sourceRect;
1342 sourceRect.x = x;
1343 sourceRect.width = width;
1344 sourceRect.y = y;
1345 sourceRect.height = height;
1346
1347 mRenderer->copyImage(source, sourceRect, gl::ExtractFormat(mImageArray[0][0]->getInternalFormat()),
1348 xoffset, yoffset, mTexStorage, target, level);
1349 }
1350 }
1351 }
1352
1353 void TextureCubeMap::storage(GLsizei levels, GLenum internalformat, GLsizei size)
1354 {
1355 delete mTexStorage;
1356 mTexStorage = new rx::TextureStorageInterfaceCube(mRenderer, levels, internalformat, mUsage, false, size);
1357 mImmutable = true;
1358
1359 for (int level = 0; level < levels; level++)
1360 {
1361 for (int face = 0; face < 6; face++)
1362 {
1363 mImageArray[face][level]->redefine(mRenderer, internalformat, size, size, true);
1364 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 {
1372 mImageArray[face][level]->redefine(mRenderer, GL_NONE, 0, 0, true);
1373 }
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 {
1384 mImageArray[face][level]->setManagedSurface(mTexStorage, face, level);
1385 }
1386 }
1387 }
1388 }
1389
1390 void TextureCubeMap::generateMipmaps()
1391 {
1392 if (!isCubeComplete())
1393 {
1394 return gl::error(GL_INVALID_OPERATION);
1395 }
1396
1397 if (!mRenderer->getNonPower2TextureSupport())
1398 {
1399 if (!isPow2(mImageArray[0][0]->getWidth()))
1400 {
1401 return gl::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 {
1411 redefineImage(f, i, mImageArray[f][0]->getInternalFormat(),
1412 std::max(mImageArray[f][0]->getWidth() >> i, 1),
1413 std::max(mImageArray[f][0]->getWidth() >> i, 1));
1414 }
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 {
1423 mTexStorage->generateMipmap(f, i);
1424
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 {
1435 mRenderer->generateMipmap(mImageArray[f][i], mImageArray[f][i - 1]);
1436 }
1437 }
1438 }
1439 }
1440
1441 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
1442 {
1443 if (!IsCubemapTextureTarget(target))
1444 {
1445 return gl::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(mRenderer, id(), new RenderbufferTextureCubeMap(this, target));
1453 }
1454
1455 return mFaceProxies[face];
1456 }
1457
1458 rx::RenderTarget *TextureCubeMap::getRenderTarget(GLenum target)
1459 {
1460 ASSERT(IsCubemapTextureTarget(target));
1461
1462 // ensure the underlying texture is created
1463 if (getStorage(true) == NULL)
1464 {
1465 return NULL;
1466 }
1467
1468 updateTexture();
1469
1470 return mTexStorage->getRenderTarget(target);
1471 }
1472
1473 int TextureCubeMap::levelCount()
1474 {
1475 return mTexStorage ? mTexStorage->levelCount() - getLodOffset() : 0;
1476 }
1477
1478 rx::TextureStorageInterface *TextureCubeMap::getStorage(bool renderTarget)
1479 {
1480 if (!mTexStorage || (renderTarget && !mTexStorage->isRenderTarget()))
1481 {
1482 if (renderTarget)
1483 {
1484 convertToRenderTarget();
1485 }
1486 else
1487 {
1488 createTexture();
1489 }
1490 }
1491
1492 return mTexStorage;
1493 }
1494
1495 }

mercurial