1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // Texture.cpp: Implements the Texture class and its derived classes
16 // Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects
17 // and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
18
19 #include "Texture.h"
20
21 #include "main.h"
22 #include "mathutil.h"
23 #include "Framebuffer.h"
24 #include "Device.hpp"
25 #include "Sampler.h"
26 #include "Shader.h"
27 #include "libEGL/Display.h"
28 #include "common/Surface.hpp"
29 #include "common/debug.h"
30
31 #include <algorithm>
32
33 namespace es2
34 {
35
getNullImage()36 egl::Image*& ImageLevels::getNullImage()
37 {
38 static egl::Image* nullImage;
39 nullImage = nullptr;
40 return nullImage;
41 }
42
Texture(GLuint name)43 Texture::Texture(GLuint name) : egl::Texture(name)
44 {
45 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
46 mMagFilter = GL_LINEAR;
47 mWrapS = GL_REPEAT;
48 mWrapT = GL_REPEAT;
49 mWrapR = GL_REPEAT;
50 mMaxAnisotropy = 1.0f;
51 mBaseLevel = 0;
52 mCompareFunc = GL_LEQUAL;
53 mCompareMode = GL_NONE;
54 mImmutableFormat = GL_FALSE;
55 mImmutableLevels = 0;
56 mMaxLevel = 1000;
57 mMaxLOD = 1000;
58 mMinLOD = -1000;
59 mSwizzleR = GL_RED;
60 mSwizzleG = GL_GREEN;
61 mSwizzleB = GL_BLUE;
62 mSwizzleA = GL_ALPHA;
63
64 resource = new sw::Resource(0);
65 }
66
~Texture()67 Texture::~Texture()
68 {
69 resource->destruct();
70 }
71
getResource() const72 sw::Resource *Texture::getResource() const
73 {
74 return resource;
75 }
76
77 // Returns true on successful filter state update (valid enum parameter)
setMinFilter(GLenum filter)78 bool Texture::setMinFilter(GLenum filter)
79 {
80 switch(filter)
81 {
82 case GL_NEAREST_MIPMAP_NEAREST:
83 case GL_LINEAR_MIPMAP_NEAREST:
84 case GL_NEAREST_MIPMAP_LINEAR:
85 case GL_LINEAR_MIPMAP_LINEAR:
86 if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
87 {
88 return false;
89 }
90 // Fall through
91 case GL_NEAREST:
92 case GL_LINEAR:
93 mMinFilter = filter;
94 return true;
95 default:
96 return false;
97 }
98 }
99
100 // Returns true on successful filter state update (valid enum parameter)
setMagFilter(GLenum filter)101 bool Texture::setMagFilter(GLenum filter)
102 {
103 switch(filter)
104 {
105 case GL_NEAREST:
106 case GL_LINEAR:
107 mMagFilter = filter;
108 return true;
109 default:
110 return false;
111 }
112 }
113
114 // Returns true on successful wrap state update (valid enum parameter)
setWrapS(GLenum wrap)115 bool Texture::setWrapS(GLenum wrap)
116 {
117 switch(wrap)
118 {
119 case GL_REPEAT:
120 case GL_MIRRORED_REPEAT:
121 if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
122 {
123 return false;
124 }
125 // Fall through
126 case GL_CLAMP_TO_EDGE:
127 mWrapS = wrap;
128 return true;
129 default:
130 return false;
131 }
132 }
133
134 // Returns true on successful wrap state update (valid enum parameter)
setWrapT(GLenum wrap)135 bool Texture::setWrapT(GLenum wrap)
136 {
137 switch(wrap)
138 {
139 case GL_REPEAT:
140 case GL_MIRRORED_REPEAT:
141 if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
142 {
143 return false;
144 }
145 // Fall through
146 case GL_CLAMP_TO_EDGE:
147 mWrapT = wrap;
148 return true;
149 default:
150 return false;
151 }
152 }
153
154 // Returns true on successful wrap state update (valid enum parameter)
setWrapR(GLenum wrap)155 bool Texture::setWrapR(GLenum wrap)
156 {
157 switch(wrap)
158 {
159 case GL_REPEAT:
160 case GL_MIRRORED_REPEAT:
161 if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
162 {
163 return false;
164 }
165 // Fall through
166 case GL_CLAMP_TO_EDGE:
167 mWrapR = wrap;
168 return true;
169 default:
170 return false;
171 }
172 }
173
174 // Returns true on successful max anisotropy update (valid anisotropy value)
setMaxAnisotropy(float textureMaxAnisotropy)175 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)
176 {
177 textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);
178
179 if(textureMaxAnisotropy < 1.0f)
180 {
181 return false;
182 }
183
184 if(mMaxAnisotropy != textureMaxAnisotropy)
185 {
186 mMaxAnisotropy = textureMaxAnisotropy;
187 }
188
189 return true;
190 }
191
setBaseLevel(GLint baseLevel)192 bool Texture::setBaseLevel(GLint baseLevel)
193 {
194 if(baseLevel < 0)
195 {
196 return false;
197 }
198
199 mBaseLevel = baseLevel;
200 return true;
201 }
202
setCompareFunc(GLenum compareFunc)203 bool Texture::setCompareFunc(GLenum compareFunc)
204 {
205 switch(compareFunc)
206 {
207 case GL_LEQUAL:
208 case GL_GEQUAL:
209 case GL_LESS:
210 case GL_GREATER:
211 case GL_EQUAL:
212 case GL_NOTEQUAL:
213 case GL_ALWAYS:
214 case GL_NEVER:
215 mCompareFunc = compareFunc;
216 return true;
217 default:
218 return false;
219 }
220 }
221
setCompareMode(GLenum compareMode)222 bool Texture::setCompareMode(GLenum compareMode)
223 {
224 switch(compareMode)
225 {
226 case GL_COMPARE_REF_TO_TEXTURE:
227 case GL_NONE:
228 mCompareMode = compareMode;
229 return true;
230 default:
231 return false;
232 }
233 }
234
makeImmutable(GLsizei levels)235 void Texture::makeImmutable(GLsizei levels)
236 {
237 mImmutableFormat = GL_TRUE;
238 mImmutableLevels = levels;
239 }
240
setMaxLevel(GLint maxLevel)241 bool Texture::setMaxLevel(GLint maxLevel)
242 {
243 mMaxLevel = maxLevel;
244 return true;
245 }
246
setMaxLOD(GLfloat maxLOD)247 bool Texture::setMaxLOD(GLfloat maxLOD)
248 {
249 mMaxLOD = maxLOD;
250 return true;
251 }
252
setMinLOD(GLfloat minLOD)253 bool Texture::setMinLOD(GLfloat minLOD)
254 {
255 mMinLOD = minLOD;
256 return true;
257 }
258
setSwizzleR(GLenum swizzleR)259 bool Texture::setSwizzleR(GLenum swizzleR)
260 {
261 switch(swizzleR)
262 {
263 case GL_RED:
264 case GL_GREEN:
265 case GL_BLUE:
266 case GL_ALPHA:
267 case GL_ZERO:
268 case GL_ONE:
269 mSwizzleR = swizzleR;
270 return true;
271 default:
272 return false;
273 }
274 }
275
setSwizzleG(GLenum swizzleG)276 bool Texture::setSwizzleG(GLenum swizzleG)
277 {
278 switch(swizzleG)
279 {
280 case GL_RED:
281 case GL_GREEN:
282 case GL_BLUE:
283 case GL_ALPHA:
284 case GL_ZERO:
285 case GL_ONE:
286 mSwizzleG = swizzleG;
287 return true;
288 default:
289 return false;
290 }
291 }
292
setSwizzleB(GLenum swizzleB)293 bool Texture::setSwizzleB(GLenum swizzleB)
294 {
295 switch(swizzleB)
296 {
297 case GL_RED:
298 case GL_GREEN:
299 case GL_BLUE:
300 case GL_ALPHA:
301 case GL_ZERO:
302 case GL_ONE:
303 mSwizzleB = swizzleB;
304 return true;
305 default:
306 return false;
307 }
308 }
309
setSwizzleA(GLenum swizzleA)310 bool Texture::setSwizzleA(GLenum swizzleA)
311 {
312 switch(swizzleA)
313 {
314 case GL_RED:
315 case GL_GREEN:
316 case GL_BLUE:
317 case GL_ALPHA:
318 case GL_ZERO:
319 case GL_ONE:
320 mSwizzleA = swizzleA;
321 return true;
322 default:
323 return false;
324 }
325 }
326
getDepth(GLenum target,GLint level) const327 GLsizei Texture::getDepth(GLenum target, GLint level) const
328 {
329 return 1;
330 }
331
createSharedImage(GLenum target,unsigned int level)332 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)
333 {
334 egl::Image *image = getRenderTarget(target, level); // Increments reference count
335
336 if(image)
337 {
338 image->markShared();
339 }
340
341 return image;
342 }
343
setImage(GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels,egl::Image * image)344 void Texture::setImage(GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels, egl::Image *image)
345 {
346 if(pixels && image)
347 {
348 GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;
349 image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackParameters, pixels);
350 }
351 }
352
setCompressedImage(GLsizei imageSize,const void * pixels,egl::Image * image)353 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
354 {
355 if(pixels && image && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
356 {
357 GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;
358 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);
359 }
360 }
361
subImage(GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels,egl::Image * image)362 void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels, egl::Image *image)
363 {
364 if(!image)
365 {
366 return error(GL_INVALID_OPERATION);
367 }
368
369 if(pixels && width > 0 && height > 0 && depth > 0)
370 {
371 image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackParameters, pixels);
372 }
373 }
374
subImageCompressed(GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLsizei imageSize,const void * pixels,egl::Image * image)375 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)
376 {
377 if(!image)
378 {
379 return error(GL_INVALID_OPERATION);
380 }
381
382 if(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
383 {
384 image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);
385 }
386 }
387
copy(egl::Image * source,const sw::SliceRect & sourceRect,GLint xoffset,GLint yoffset,GLint zoffset,egl::Image * dest)388 bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest)
389 {
390 Device *device = getDevice();
391
392 sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);
393 sw::SliceRectF sourceRectF(static_cast<float>(sourceRect.x0),
394 static_cast<float>(sourceRect.y0),
395 static_cast<float>(sourceRect.x1),
396 static_cast<float>(sourceRect.y1),
397 sourceRect.slice);
398 bool success = device->stretchRect(source, &sourceRectF, dest, &destRect, Device::ALL_BUFFERS);
399
400 if(!success)
401 {
402 return error(GL_OUT_OF_MEMORY, false);
403 }
404
405 return true;
406 }
407
isMipmapFiltered(Sampler * sampler) const408 bool Texture::isMipmapFiltered(Sampler *sampler) const
409 {
410 GLenum minFilter = sampler ? sampler->getMinFilter() : mMinFilter;
411
412 switch(minFilter)
413 {
414 case GL_NEAREST:
415 case GL_LINEAR:
416 return false;
417 case GL_NEAREST_MIPMAP_NEAREST:
418 case GL_LINEAR_MIPMAP_NEAREST:
419 case GL_NEAREST_MIPMAP_LINEAR:
420 case GL_LINEAR_MIPMAP_LINEAR:
421 return true;
422 default: UNREACHABLE(minFilter);
423 }
424
425 return false;
426 }
427
Texture2D(GLuint name)428 Texture2D::Texture2D(GLuint name) : Texture(name)
429 {
430 mSurface = nullptr;
431
432 mColorbufferProxy = nullptr;
433 mProxyRefs = 0;
434 }
435
~Texture2D()436 Texture2D::~Texture2D()
437 {
438 image.unbind(this);
439
440 if(mSurface)
441 {
442 mSurface->setBoundTexture(nullptr);
443 mSurface = nullptr;
444 }
445
446 mColorbufferProxy = nullptr;
447 }
448
449 // We need to maintain a count of references to renderbuffers acting as
450 // proxies for this texture, so that we do not attempt to use a pointer
451 // to a renderbuffer proxy which has been deleted.
addProxyRef(const Renderbuffer * proxy)452 void Texture2D::addProxyRef(const Renderbuffer *proxy)
453 {
454 mProxyRefs++;
455 }
456
releaseProxy(const Renderbuffer * proxy)457 void Texture2D::releaseProxy(const Renderbuffer *proxy)
458 {
459 if(mProxyRefs > 0)
460 {
461 mProxyRefs--;
462 }
463
464 if(mProxyRefs == 0)
465 {
466 mColorbufferProxy = nullptr;
467 }
468 }
469
sweep()470 void Texture2D::sweep()
471 {
472 int imageCount = 0;
473
474 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
475 {
476 if(image[i] && image[i]->isChildOf(this))
477 {
478 if(!image[i]->hasSingleReference())
479 {
480 return;
481 }
482
483 imageCount++;
484 }
485 }
486
487 if(imageCount == referenceCount)
488 {
489 destroy();
490 }
491 }
492
getTarget() const493 GLenum Texture2D::getTarget() const
494 {
495 return GL_TEXTURE_2D;
496 }
497
getWidth(GLenum target,GLint level) const498 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
499 {
500 ASSERT(target == getTarget());
501 return image[level] ? image[level]->getWidth() : 0;
502 }
503
getHeight(GLenum target,GLint level) const504 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
505 {
506 ASSERT(target == getTarget());
507 return image[level] ? image[level]->getHeight() : 0;
508 }
509
getFormat(GLenum target,GLint level) const510 GLint Texture2D::getFormat(GLenum target, GLint level) const
511 {
512 ASSERT(target == getTarget());
513 return image[level] ? image[level]->getFormat() : GL_NONE;
514 }
515
getTopLevel() const516 int Texture2D::getTopLevel() const
517 {
518 int level = mBaseLevel;
519
520 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level])
521 {
522 level++;
523 }
524
525 return level - 1;
526 }
527
hasNonBaseLevels() const528 bool Texture2D::hasNonBaseLevels() const
529 {
530 for(int level = 1; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
531 {
532 if (image[level])
533 {
534 return true;
535 }
536 }
537
538 return false;
539 }
540
requiresSync() const541 bool Texture2D::requiresSync() const
542 {
543 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
544 {
545 if(image[level] && image[level]->requiresSync())
546 {
547 return true;
548 }
549 }
550
551 return false;
552 }
553
setImage(GLint level,GLsizei width,GLsizei height,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)554 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
555 {
556 if(image[level])
557 {
558 image[level]->release();
559 }
560
561 image[level] = egl::Image::create(this, width, height, internalformat);
562
563 if(!image[level])
564 {
565 return error(GL_OUT_OF_MEMORY);
566 }
567
568 Texture::setImage(format, type, unpackParameters, pixels, image[level]);
569 }
570
bindTexImage(gl::Surface * surface)571 void Texture2D::bindTexImage(gl::Surface *surface)
572 {
573 image.release();
574
575 image[0] = surface->getRenderTarget();
576
577 assert(!mSurface); // eglBindTexImage called before eglReleaseTexImage
578
579 mSurface = surface;
580 mSurface->setBoundTexture(this);
581 }
582
releaseTexImage()583 void Texture2D::releaseTexImage()
584 {
585 image.release();
586
587 if(mSurface)
588 {
589 mSurface->setBoundTexture(nullptr);
590 mSurface = nullptr;
591 }
592 }
593
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)594 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
595 {
596 if(image[level])
597 {
598 image[level]->release();
599 }
600
601 image[level] = egl::Image::create(this, width, height, format);
602
603 if(!image[level])
604 {
605 return error(GL_OUT_OF_MEMORY);
606 }
607
608 Texture::setCompressedImage(imageSize, pixels, image[level]);
609 }
610
subImage(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)611 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
612 {
613 Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels, image[level]);
614 }
615
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)616 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
617 {
618 Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);
619 }
620
copyImage(GLint level,GLenum internalformat,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)621 void Texture2D::copyImage(GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
622 {
623 if(image[level])
624 {
625 image[level]->release();
626 }
627
628 image[level] = egl::Image::create(this, width, height, internalformat);
629
630 if(!image[level])
631 {
632 return error(GL_OUT_OF_MEMORY);
633 }
634
635 if(width != 0 && height != 0)
636 {
637 egl::Image *renderTarget = source->getRenderTarget();
638
639 if(!renderTarget)
640 {
641 ERR("Failed to retrieve the render target.");
642 return error(GL_OUT_OF_MEMORY);
643 }
644
645 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
646 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
647
648 copy(renderTarget, sourceRect, 0, 0, 0, image[level]);
649
650 renderTarget->release();
651 }
652 }
653
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)654 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
655 {
656 if(!image[level])
657 {
658 return error(GL_INVALID_OPERATION);
659 }
660
661 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)
662 {
663 return error(GL_INVALID_VALUE);
664 }
665
666 if(width > 0 && height > 0)
667 {
668 egl::Image *renderTarget = source->getRenderTarget();
669
670 if(!renderTarget)
671 {
672 ERR("Failed to retrieve the render target.");
673 return error(GL_OUT_OF_MEMORY);
674 }
675
676 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
677 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
678
679 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[level]);
680
681 renderTarget->release();
682 }
683 }
684
setSharedImage(egl::Image * sharedImage)685 void Texture2D::setSharedImage(egl::Image *sharedImage)
686 {
687 if(sharedImage == image[0])
688 {
689 return;
690 }
691
692 sharedImage->addRef();
693
694 if(image[0])
695 {
696 image[0]->release();
697 }
698
699 image[0] = sharedImage;
700 }
701
isBaseLevelDefined() const702 bool Texture2D::isBaseLevelDefined() const
703 {
704 if(!image[mBaseLevel])
705 {
706 return false;
707 }
708
709 GLsizei width = image[mBaseLevel]->getWidth();
710 GLsizei height = image[mBaseLevel]->getHeight();
711
712 if(width <= 0 || height <= 0)
713 {
714 return false;
715 }
716
717 return true;
718 }
719
720 // Tests for 2D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isSamplerComplete(Sampler * sampler) const721 bool Texture2D::isSamplerComplete(Sampler *sampler) const
722 {
723 if(mImmutableFormat == GL_TRUE)
724 {
725 return true;
726 }
727
728 if(!isBaseLevelDefined())
729 {
730 return false;
731 }
732
733 if(isMipmapFiltered(sampler))
734 {
735 if(!isMipmapComplete())
736 {
737 return false;
738 }
739 }
740
741 return true;
742 }
743
744 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isMipmapComplete() const745 bool Texture2D::isMipmapComplete() const
746 {
747 if(mBaseLevel > mMaxLevel)
748 {
749 return false;
750 }
751
752 GLsizei width = image[mBaseLevel]->getWidth();
753 GLsizei height = image[mBaseLevel]->getHeight();
754 int maxsize = std::max(width, height);
755 int p = log2(maxsize) + mBaseLevel;
756 int q = std::min(p, mMaxLevel);
757
758 for(int level = mBaseLevel + 1; level <= q; level++)
759 {
760 if(!image[level])
761 {
762 return false;
763 }
764
765 if(image[level]->getFormat() != image[mBaseLevel]->getFormat())
766 {
767 return false;
768 }
769
770 int i = level - mBaseLevel;
771
772 if(image[level]->getWidth() != std::max(1, width >> i))
773 {
774 return false;
775 }
776
777 if(image[level]->getHeight() != std::max(1, height >> i))
778 {
779 return false;
780 }
781 }
782
783 return true;
784 }
785
isCompressed(GLenum target,GLint level) const786 bool Texture2D::isCompressed(GLenum target, GLint level) const
787 {
788 return IsCompressed(getFormat(target, level));
789 }
790
isDepth(GLenum target,GLint level) const791 bool Texture2D::isDepth(GLenum target, GLint level) const
792 {
793 return IsDepthTexture(getFormat(target, level));
794 }
795
generateMipmaps()796 void Texture2D::generateMipmaps()
797 {
798 if(!image[mBaseLevel])
799 {
800 return; // Image unspecified. Not an error.
801 }
802
803 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0)
804 {
805 return; // Zero dimension. Not an error.
806 }
807
808 int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight());
809 int p = log2(maxsize) + mBaseLevel;
810 int q = std::min(p, mMaxLevel);
811
812 for(int i = mBaseLevel + 1; i <= q; i++)
813 {
814 if(image[i])
815 {
816 image[i]->release();
817 }
818
819 image[i] = egl::Image::create(this, std::max(image[mBaseLevel]->getWidth() >> i, 1), std::max(image[mBaseLevel]->getHeight() >> i, 1), image[mBaseLevel]->getFormat());
820
821 if(!image[i])
822 {
823 return error(GL_OUT_OF_MEMORY);
824 }
825
826 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
827 }
828 }
829
getImage(unsigned int level)830 egl::Image *Texture2D::getImage(unsigned int level)
831 {
832 return image[level];
833 }
834
getRenderbuffer(GLenum target,GLint level)835 Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level)
836 {
837 if(target != getTarget())
838 {
839 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
840 }
841
842 if(!mColorbufferProxy)
843 {
844 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));
845 }
846 else
847 {
848 mColorbufferProxy->setLevel(level);
849 }
850
851 return mColorbufferProxy;
852 }
853
getRenderTarget(GLenum target,unsigned int level)854 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
855 {
856 ASSERT(target == getTarget());
857 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
858
859 if(image[level])
860 {
861 image[level]->addRef();
862 }
863
864 return image[level];
865 }
866
isShared(GLenum target,unsigned int level) const867 bool Texture2D::isShared(GLenum target, unsigned int level) const
868 {
869 ASSERT(target == getTarget());
870 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
871
872 if(mSurface) // Bound to an EGLSurface
873 {
874 return true;
875 }
876
877 if(!image[level])
878 {
879 return false;
880 }
881
882 return image[level]->isShared();
883 }
884
Texture2DRect(GLuint name)885 Texture2DRect::Texture2DRect(GLuint name) : Texture2D(name)
886 {
887 mMinFilter = GL_LINEAR;
888 mMagFilter = GL_LINEAR;
889 mWrapS = GL_CLAMP_TO_EDGE;
890 mWrapT = GL_CLAMP_TO_EDGE;
891 mWrapR = GL_CLAMP_TO_EDGE;
892 }
893
getTarget() const894 GLenum Texture2DRect::getTarget() const
895 {
896 return GL_TEXTURE_RECTANGLE_ARB;
897 }
898
getRenderbuffer(GLenum target,GLint level)899 Renderbuffer *Texture2DRect::getRenderbuffer(GLenum target, GLint level)
900 {
901 if((target != getTarget()) || (level != 0))
902 {
903 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
904 }
905
906 if(!mColorbufferProxy)
907 {
908 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2DRect(this));
909 }
910
911 return mColorbufferProxy;
912 }
913
TextureCubeMap(GLuint name)914 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
915 {
916 for(int f = 0; f < 6; f++)
917 {
918 mFaceProxies[f] = nullptr;
919 mFaceProxyRefs[f] = 0;
920 }
921 }
922
~TextureCubeMap()923 TextureCubeMap::~TextureCubeMap()
924 {
925 for(int i = 0; i < 6; i++)
926 {
927 image[i].unbind(this);
928 mFaceProxies[i] = nullptr;
929 }
930 }
931
932 // We need to maintain a count of references to renderbuffers acting as
933 // proxies for this texture, so that the texture is not deleted while
934 // proxy references still exist. If the reference count drops to zero,
935 // we set our proxy pointer null, so that a new attempt at referencing
936 // will cause recreation.
addProxyRef(const Renderbuffer * proxy)937 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
938 {
939 for(int f = 0; f < 6; f++)
940 {
941 if(mFaceProxies[f] == proxy)
942 {
943 mFaceProxyRefs[f]++;
944 }
945 }
946 }
947
releaseProxy(const Renderbuffer * proxy)948 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
949 {
950 for(int f = 0; f < 6; f++)
951 {
952 if(mFaceProxies[f] == proxy)
953 {
954 if(mFaceProxyRefs[f] > 0)
955 {
956 mFaceProxyRefs[f]--;
957 }
958
959 if(mFaceProxyRefs[f] == 0)
960 {
961 mFaceProxies[f] = nullptr;
962 }
963 }
964 }
965 }
966
sweep()967 void TextureCubeMap::sweep()
968 {
969 int imageCount = 0;
970
971 for(int f = 0; f < 6; f++)
972 {
973 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
974 {
975 if(image[f][i] && image[f][i]->isChildOf(this))
976 {
977 if(!image[f][i]->hasSingleReference())
978 {
979 return;
980 }
981
982 imageCount++;
983 }
984 }
985 }
986
987 if(imageCount == referenceCount)
988 {
989 destroy();
990 }
991 }
992
getTarget() const993 GLenum TextureCubeMap::getTarget() const
994 {
995 return GL_TEXTURE_CUBE_MAP;
996 }
997
getWidth(GLenum target,GLint level) const998 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
999 {
1000 int face = CubeFaceIndex(target);
1001 return image[face][level] ? image[face][level]->getWidth() : 0;
1002 }
1003
getHeight(GLenum target,GLint level) const1004 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
1005 {
1006 int face = CubeFaceIndex(target);
1007 return image[face][level] ? image[face][level]->getHeight() : 0;
1008 }
1009
getFormat(GLenum target,GLint level) const1010 GLint TextureCubeMap::getFormat(GLenum target, GLint level) const
1011 {
1012 int face = CubeFaceIndex(target);
1013 return image[face][level] ? image[face][level]->getFormat() : 0;
1014 }
1015
getTopLevel() const1016 int TextureCubeMap::getTopLevel() const
1017 {
1018 int level = mBaseLevel;
1019
1020 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][level])
1021 {
1022 level++;
1023 }
1024
1025 return level - 1;
1026 }
1027
hasNonBaseLevels() const1028 bool TextureCubeMap::hasNonBaseLevels() const
1029 {
1030 for(int level = 1; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1031 {
1032 for(int face = 0; face < 6; face++)
1033 {
1034 if (image[face][level])
1035 {
1036 return true;
1037 }
1038 }
1039 }
1040
1041 return false;
1042 }
1043
requiresSync() const1044 bool TextureCubeMap::requiresSync() const
1045 {
1046 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1047 {
1048 for(int face = 0; face < 6; face++)
1049 {
1050 if(image[face][level] && image[face][level]->requiresSync())
1051 {
1052 return true;
1053 }
1054 }
1055 }
1056
1057 return false;
1058 }
1059
setCompressedImage(GLenum target,GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)1060 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1061 {
1062 int face = CubeFaceIndex(target);
1063
1064 if(image[face][level])
1065 {
1066 image[face][level]->release();
1067 }
1068
1069 image[face][level] = egl::Image::create(this, width, height, 1, 1, format);
1070
1071 if(!image[face][level])
1072 {
1073 return error(GL_OUT_OF_MEMORY);
1074 }
1075
1076 Texture::setCompressedImage(imageSize, pixels, image[face][level]);
1077 }
1078
subImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1079 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1080 {
1081 Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels, image[CubeFaceIndex(target)][level]);
1082 }
1083
subImageCompressed(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)1084 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1085 {
1086 Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);
1087 }
1088
isBaseLevelDefined() const1089 bool TextureCubeMap::isBaseLevelDefined() const
1090 {
1091 for(int face = 0; face < 6; face++)
1092 {
1093 if(!image[face][mBaseLevel])
1094 {
1095 return false;
1096 }
1097 }
1098
1099 int size = image[0][mBaseLevel]->getWidth();
1100
1101 if(size <= 0)
1102 {
1103 return false;
1104 }
1105
1106 return true;
1107 }
1108
1109 // Tests for cube map sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 161.
isSamplerComplete(Sampler * sampler) const1110 bool TextureCubeMap::isSamplerComplete(Sampler *sampler) const
1111 {
1112 if(mImmutableFormat == GL_TRUE)
1113 {
1114 return true;
1115 }
1116
1117 if(!isBaseLevelDefined())
1118 {
1119 return false;
1120 }
1121
1122 if(!isMipmapFiltered(sampler))
1123 {
1124 if(!isCubeComplete())
1125 {
1126 return false;
1127 }
1128 }
1129 else
1130 {
1131 if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1132 {
1133 return false;
1134 }
1135 }
1136
1137 return true;
1138 }
1139
1140 // Tests for cube texture completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isCubeComplete() const1141 bool TextureCubeMap::isCubeComplete() const
1142 {
1143 if(!isBaseLevelDefined())
1144 {
1145 return false;
1146 }
1147
1148 if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth())
1149 {
1150 return false;
1151 }
1152
1153 for(unsigned int face = 1; face < 6; face++)
1154 {
1155 if(image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getWidth() ||
1156 image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getHeight() ||
1157 image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat())
1158 {
1159 return false;
1160 }
1161 }
1162
1163 return true;
1164 }
1165
isMipmapCubeComplete() const1166 bool TextureCubeMap::isMipmapCubeComplete() const
1167 {
1168 if(mBaseLevel > mMaxLevel)
1169 {
1170 return false;
1171 }
1172
1173 if(!isCubeComplete())
1174 {
1175 return false;
1176 }
1177
1178 GLsizei size = image[0][mBaseLevel]->getWidth();
1179 int p = log2(size) + mBaseLevel;
1180 int q = std::min(p, mMaxLevel);
1181
1182 for(int face = 0; face < 6; face++)
1183 {
1184 for(int level = mBaseLevel + 1; level <= q; level++)
1185 {
1186 if(!image[face][level])
1187 {
1188 return false;
1189 }
1190
1191 if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat())
1192 {
1193 return false;
1194 }
1195
1196 int i = level - mBaseLevel;
1197
1198 if(image[face][level]->getWidth() != std::max(1, size >> i))
1199 {
1200 return false;
1201 }
1202 }
1203 }
1204
1205 return true;
1206 }
1207
updateBorders(int level)1208 void TextureCubeMap::updateBorders(int level)
1209 {
1210 egl::Image *posX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_X)][level];
1211 egl::Image *negX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_X)][level];
1212 egl::Image *posY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Y)][level];
1213 egl::Image *negY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y)][level];
1214 egl::Image *posZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Z)][level];
1215 egl::Image *negZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)][level];
1216
1217 if(!posX || !negX || !posY || !negY || !posZ || !negZ)
1218 {
1219 return;
1220 }
1221
1222 if(posX->getBorder() == 0) // Non-seamless cube map.
1223 {
1224 return;
1225 }
1226
1227 if(!posX->hasDirtyContents() || !posY->hasDirtyContents() || !posZ->hasDirtyContents() || !negX->hasDirtyContents() || !negY->hasDirtyContents() || !negZ->hasDirtyContents())
1228 {
1229 return;
1230 }
1231
1232 // Copy top / bottom first.
1233 posX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::RIGHT);
1234 posY->copyCubeEdge(sw::Surface::BOTTOM, posZ, sw::Surface::TOP);
1235 posZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::TOP);
1236 negX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::LEFT);
1237 negY->copyCubeEdge(sw::Surface::BOTTOM, negZ, sw::Surface::BOTTOM);
1238 negZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::BOTTOM);
1239
1240 posX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::RIGHT);
1241 posY->copyCubeEdge(sw::Surface::TOP, negZ, sw::Surface::TOP);
1242 posZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::BOTTOM);
1243 negX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::LEFT);
1244 negY->copyCubeEdge(sw::Surface::TOP, posZ, sw::Surface::BOTTOM);
1245 negZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::TOP);
1246
1247 // Copy left / right after top and bottom are done.
1248 // The corner colors will be computed assuming top / bottom are already set.
1249 posX->copyCubeEdge(sw::Surface::RIGHT, negZ, sw::Surface::LEFT);
1250 posY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::TOP);
1251 posZ->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::LEFT);
1252 negX->copyCubeEdge(sw::Surface::RIGHT, posZ, sw::Surface::LEFT);
1253 negY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::BOTTOM);
1254 negZ->copyCubeEdge(sw::Surface::RIGHT, negX, sw::Surface::LEFT);
1255
1256 posX->copyCubeEdge(sw::Surface::LEFT, posZ, sw::Surface::RIGHT);
1257 posY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::TOP);
1258 posZ->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::RIGHT);
1259 negX->copyCubeEdge(sw::Surface::LEFT, negZ, sw::Surface::RIGHT);
1260 negY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::BOTTOM);
1261 negZ->copyCubeEdge(sw::Surface::LEFT, posX, sw::Surface::RIGHT);
1262
1263 posX->markContentsClean();
1264 posY->markContentsClean();
1265 posZ->markContentsClean();
1266 negX->markContentsClean();
1267 negY->markContentsClean();
1268 negZ->markContentsClean();
1269 }
1270
isCompressed(GLenum target,GLint level) const1271 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1272 {
1273 return IsCompressed(getFormat(target, level));
1274 }
1275
isDepth(GLenum target,GLint level) const1276 bool TextureCubeMap::isDepth(GLenum target, GLint level) const
1277 {
1278 return IsDepthTexture(getFormat(target, level));
1279 }
1280
releaseTexImage()1281 void TextureCubeMap::releaseTexImage()
1282 {
1283 UNREACHABLE(0); // Cube maps cannot have an EGL surface bound as an image
1284 }
1285
setImage(GLenum target,GLint level,GLsizei width,GLsizei height,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1286 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1287 {
1288 int face = CubeFaceIndex(target);
1289
1290 if(image[face][level])
1291 {
1292 image[face][level]->release();
1293 }
1294
1295 image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat);
1296
1297 if(!image[face][level])
1298 {
1299 return error(GL_OUT_OF_MEMORY);
1300 }
1301
1302 Texture::setImage(format, type, unpackParameters, pixels, image[face][level]);
1303 }
1304
copyImage(GLenum target,GLint level,GLenum internalformat,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1305 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1306 {
1307 int face = CubeFaceIndex(target);
1308
1309 if(image[face][level])
1310 {
1311 image[face][level]->release();
1312 }
1313
1314 image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat);
1315
1316 if(!image[face][level])
1317 {
1318 return error(GL_OUT_OF_MEMORY);
1319 }
1320
1321 if(width != 0 && height != 0)
1322 {
1323 egl::Image *renderTarget = source->getRenderTarget();
1324
1325 if(!renderTarget)
1326 {
1327 ERR("Failed to retrieve the render target.");
1328 return error(GL_OUT_OF_MEMORY);
1329 }
1330
1331 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
1332 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1333
1334 copy(renderTarget, sourceRect, 0, 0, 0, image[face][level]);
1335
1336 renderTarget->release();
1337 }
1338 }
1339
getImage(int face,unsigned int level)1340 egl::Image *TextureCubeMap::getImage(int face, unsigned int level)
1341 {
1342 return image[face][level];
1343 }
1344
getImage(GLenum face,unsigned int level)1345 egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
1346 {
1347 return image[CubeFaceIndex(face)][level];
1348 }
1349
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1350 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1351 {
1352 int face = CubeFaceIndex(target);
1353
1354 if(!image[face][level])
1355 {
1356 return error(GL_INVALID_OPERATION);
1357 }
1358
1359 GLsizei size = image[face][level]->getWidth();
1360
1361 if(xoffset + width > size || yoffset + height > size || zoffset != 0)
1362 {
1363 return error(GL_INVALID_VALUE);
1364 }
1365
1366 if(width > 0 && height > 0)
1367 {
1368 egl::Image *renderTarget = source->getRenderTarget();
1369
1370 if(!renderTarget)
1371 {
1372 ERR("Failed to retrieve the render target.");
1373 return error(GL_OUT_OF_MEMORY);
1374 }
1375
1376 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
1377 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1378
1379 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[face][level]);
1380
1381 renderTarget->release();
1382 }
1383 }
1384
generateMipmaps()1385 void TextureCubeMap::generateMipmaps()
1386 {
1387 if(!isCubeComplete())
1388 {
1389 return error(GL_INVALID_OPERATION);
1390 }
1391
1392 int p = log2(image[0][mBaseLevel]->getWidth()) + mBaseLevel;
1393 int q = std::min(p, mMaxLevel);
1394
1395 for(int f = 0; f < 6; f++)
1396 {
1397 ASSERT(image[f][mBaseLevel]);
1398
1399 for(int i = mBaseLevel + 1; i <= q; i++)
1400 {
1401 if(image[f][i])
1402 {
1403 image[f][i]->release();
1404 }
1405
1406 image[f][i] = egl::Image::create(this, std::max(image[f][mBaseLevel]->getWidth() >> i, 1), std::max(image[f][mBaseLevel]->getHeight() >> i, 1), 1, 1, image[f][mBaseLevel]->getFormat());
1407
1408 if(!image[f][i])
1409 {
1410 return error(GL_OUT_OF_MEMORY);
1411 }
1412
1413 getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
1414 }
1415 }
1416 }
1417
getRenderbuffer(GLenum target,GLint level)1418 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level)
1419 {
1420 if(!IsCubemapTextureTarget(target))
1421 {
1422 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
1423 }
1424
1425 int face = CubeFaceIndex(target);
1426
1427 if(!mFaceProxies[face])
1428 {
1429 mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));
1430 }
1431 else
1432 {
1433 mFaceProxies[face]->setLevel(level);
1434 }
1435
1436 return mFaceProxies[face];
1437 }
1438
getRenderTarget(GLenum target,unsigned int level)1439 egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)
1440 {
1441 ASSERT(IsCubemapTextureTarget(target));
1442 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1443
1444 int face = CubeFaceIndex(target);
1445
1446 if(image[face][level])
1447 {
1448 image[face][level]->addRef();
1449 }
1450
1451 return image[face][level];
1452 }
1453
isShared(GLenum target,unsigned int level) const1454 bool TextureCubeMap::isShared(GLenum target, unsigned int level) const
1455 {
1456 ASSERT(IsCubemapTextureTarget(target));
1457 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1458
1459 int face = CubeFaceIndex(target);
1460
1461 if(!image[face][level])
1462 {
1463 return false;
1464 }
1465
1466 return image[face][level]->isShared();
1467 }
1468
Texture3D(GLuint name)1469 Texture3D::Texture3D(GLuint name) : Texture(name)
1470 {
1471 mSurface = nullptr;
1472
1473 mColorbufferProxy = nullptr;
1474 mProxyRefs = 0;
1475 }
1476
~Texture3D()1477 Texture3D::~Texture3D()
1478 {
1479 image.unbind(this);
1480
1481 if(mSurface)
1482 {
1483 mSurface->setBoundTexture(nullptr);
1484 mSurface = nullptr;
1485 }
1486
1487 mColorbufferProxy = nullptr;
1488 }
1489
1490 // We need to maintain a count of references to renderbuffers acting as
1491 // proxies for this texture, so that we do not attempt to use a pointer
1492 // to a renderbuffer proxy which has been deleted.
addProxyRef(const Renderbuffer * proxy)1493 void Texture3D::addProxyRef(const Renderbuffer *proxy)
1494 {
1495 mProxyRefs++;
1496 }
1497
releaseProxy(const Renderbuffer * proxy)1498 void Texture3D::releaseProxy(const Renderbuffer *proxy)
1499 {
1500 if(mProxyRefs > 0)
1501 {
1502 mProxyRefs--;
1503 }
1504
1505 if(mProxyRefs == 0)
1506 {
1507 mColorbufferProxy = nullptr;
1508 }
1509 }
1510
sweep()1511 void Texture3D::sweep()
1512 {
1513 int imageCount = 0;
1514
1515 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1516 {
1517 if(image[i] && image[i]->isChildOf(this))
1518 {
1519 if(!image[i]->hasSingleReference())
1520 {
1521 return;
1522 }
1523
1524 imageCount++;
1525 }
1526 }
1527
1528 if(imageCount == referenceCount)
1529 {
1530 destroy();
1531 }
1532 }
1533
getTarget() const1534 GLenum Texture3D::getTarget() const
1535 {
1536 return GL_TEXTURE_3D_OES;
1537 }
1538
getWidth(GLenum target,GLint level) const1539 GLsizei Texture3D::getWidth(GLenum target, GLint level) const
1540 {
1541 ASSERT(target == getTarget());
1542 return image[level] ? image[level]->getWidth() : 0;
1543 }
1544
getHeight(GLenum target,GLint level) const1545 GLsizei Texture3D::getHeight(GLenum target, GLint level) const
1546 {
1547 ASSERT(target == getTarget());
1548 return image[level] ? image[level]->getHeight() : 0;
1549 }
1550
getDepth(GLenum target,GLint level) const1551 GLsizei Texture3D::getDepth(GLenum target, GLint level) const
1552 {
1553 ASSERT(target == getTarget());
1554 return image[level] ? image[level]->getDepth() : 0;
1555 }
1556
getFormat(GLenum target,GLint level) const1557 GLint Texture3D::getFormat(GLenum target, GLint level) const
1558 {
1559 ASSERT(target == getTarget());
1560 return image[level] ? image[level]->getFormat() : GL_NONE;
1561 }
1562
getTopLevel() const1563 int Texture3D::getTopLevel() const
1564 {
1565 int level = mBaseLevel;
1566
1567 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level])
1568 {
1569 level++;
1570 }
1571
1572 return level - 1;
1573 }
1574
hasNonBaseLevels() const1575 bool Texture3D::hasNonBaseLevels() const
1576 {
1577 for(int level = 1; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1578 {
1579 if (image[level])
1580 {
1581 return true;
1582 }
1583 }
1584
1585 return false;
1586 }
1587
requiresSync() const1588 bool Texture3D::requiresSync() const
1589 {
1590 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1591 {
1592 if(image[level] && image[level]->requiresSync())
1593 {
1594 return true;
1595 }
1596 }
1597
1598 return false;
1599 }
1600
setImage(GLint level,GLsizei width,GLsizei height,GLsizei depth,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1601 void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1602 {
1603 if(image[level])
1604 {
1605 image[level]->release();
1606 }
1607
1608 image[level] = egl::Image::create(this, width, height, depth, 0, internalformat);
1609
1610 if(!image[level])
1611 {
1612 return error(GL_OUT_OF_MEMORY);
1613 }
1614
1615 Texture::setImage(format, type, unpackParameters, pixels, image[level]);
1616 }
1617
releaseTexImage()1618 void Texture3D::releaseTexImage()
1619 {
1620 UNREACHABLE(0); // 3D textures cannot have an EGL surface bound as an image
1621 }
1622
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei depth,GLsizei imageSize,const void * pixels)1623 void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
1624 {
1625 if(image[level])
1626 {
1627 image[level]->release();
1628 }
1629
1630 image[level] = egl::Image::create(this, width, height, depth, 0, format);
1631
1632 if(!image[level])
1633 {
1634 return error(GL_OUT_OF_MEMORY);
1635 }
1636
1637 Texture::setCompressedImage(imageSize, pixels, image[level]);
1638 }
1639
subImage(GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1640 void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1641 {
1642 Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackParameters, pixels, image[level]);
1643 }
1644
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLsizei imageSize,const void * pixels)1645 void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
1646 {
1647 Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);
1648 }
1649
copyImage(GLint level,GLenum internalformat,GLint x,GLint y,GLint z,GLsizei width,GLsizei height,GLsizei depth,Renderbuffer * source)1650 void Texture3D::copyImage(GLint level, GLenum internalformat, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Renderbuffer *source)
1651 {
1652 if(image[level])
1653 {
1654 image[level]->release();
1655 }
1656
1657 image[level] = egl::Image::create(this, width, height, depth, 0, internalformat);
1658
1659 if(!image[level])
1660 {
1661 return error(GL_OUT_OF_MEMORY);
1662 }
1663
1664 if(width != 0 && height != 0 && depth != 0)
1665 {
1666 egl::Image *renderTarget = source->getRenderTarget();
1667
1668 if(!renderTarget)
1669 {
1670 ERR("Failed to retrieve the render target.");
1671 return error(GL_OUT_OF_MEMORY);
1672 }
1673
1674 sw::SliceRect sourceRect(x, y, x + width, y + height, z);
1675 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1676
1677 for(GLint sliceZ = 0; sliceZ < depth; sliceZ++, sourceRect.slice++)
1678 {
1679 copy(renderTarget, sourceRect, 0, 0, sliceZ, image[level]);
1680 }
1681
1682 renderTarget->release();
1683 }
1684 }
1685
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1686 void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1687 {
1688 if(!image[level])
1689 {
1690 return error(GL_INVALID_OPERATION);
1691 }
1692
1693 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())
1694 {
1695 return error(GL_INVALID_VALUE);
1696 }
1697
1698 if(width > 0 && height > 0)
1699 {
1700 egl::Image *renderTarget = source->getRenderTarget();
1701
1702 if(!renderTarget)
1703 {
1704 ERR("Failed to retrieve the render target.");
1705 return error(GL_OUT_OF_MEMORY);
1706 }
1707
1708 sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};
1709 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1710
1711 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[level]);
1712
1713 renderTarget->release();
1714 }
1715 }
1716
setSharedImage(egl::Image * sharedImage)1717 void Texture3D::setSharedImage(egl::Image *sharedImage)
1718 {
1719 sharedImage->addRef();
1720
1721 if(image[0])
1722 {
1723 image[0]->release();
1724 }
1725
1726 image[0] = sharedImage;
1727 }
1728
isBaseLevelDefined() const1729 bool Texture3D::isBaseLevelDefined() const
1730 {
1731 if(!image[mBaseLevel])
1732 {
1733 return false;
1734 }
1735
1736 GLsizei width = image[mBaseLevel]->getWidth();
1737 GLsizei height = image[mBaseLevel]->getHeight();
1738 GLsizei depth = image[mBaseLevel]->getDepth();
1739
1740 if(width <= 0 || height <= 0 || depth <= 0)
1741 {
1742 return false;
1743 }
1744
1745 return true;
1746 }
1747
1748 // Tests for 3D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isSamplerComplete(Sampler * sampler) const1749 bool Texture3D::isSamplerComplete(Sampler *sampler) const
1750 {
1751 if(mImmutableFormat == GL_TRUE)
1752 {
1753 return true;
1754 }
1755
1756 if(!isBaseLevelDefined())
1757 {
1758 return false;
1759 }
1760
1761 if(isMipmapFiltered(sampler))
1762 {
1763 if(!isMipmapComplete())
1764 {
1765 return false;
1766 }
1767 }
1768
1769 return true;
1770 }
1771
1772 // Tests for 3D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isMipmapComplete() const1773 bool Texture3D::isMipmapComplete() const
1774 {
1775 if(mBaseLevel > mMaxLevel)
1776 {
1777 return false;
1778 }
1779
1780 GLsizei width = image[mBaseLevel]->getWidth();
1781 GLsizei height = image[mBaseLevel]->getHeight();
1782 GLsizei depth = image[mBaseLevel]->getDepth();
1783 bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;
1784
1785 int maxsize = isTexture2DArray ? std::max(width, height) : std::max(std::max(width, height), depth);
1786 int p = log2(maxsize) + mBaseLevel;
1787 int q = std::min(p, mMaxLevel);
1788
1789 for(int level = mBaseLevel + 1; level <= q; level++)
1790 {
1791 if(!image[level])
1792 {
1793 return false;
1794 }
1795
1796 if(image[level]->getFormat() != image[mBaseLevel]->getFormat())
1797 {
1798 return false;
1799 }
1800
1801 int i = level - mBaseLevel;
1802
1803 if(image[level]->getWidth() != std::max(1, width >> i))
1804 {
1805 return false;
1806 }
1807
1808 if(image[level]->getHeight() != std::max(1, height >> i))
1809 {
1810 return false;
1811 }
1812
1813 int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> i);
1814 if(image[level]->getDepth() != levelDepth)
1815 {
1816 return false;
1817 }
1818 }
1819
1820 return true;
1821 }
1822
isCompressed(GLenum target,GLint level) const1823 bool Texture3D::isCompressed(GLenum target, GLint level) const
1824 {
1825 return IsCompressed(getFormat(target, level));
1826 }
1827
isDepth(GLenum target,GLint level) const1828 bool Texture3D::isDepth(GLenum target, GLint level) const
1829 {
1830 return IsDepthTexture(getFormat(target, level));
1831 }
1832
generateMipmaps()1833 void Texture3D::generateMipmaps()
1834 {
1835 if(!image[mBaseLevel])
1836 {
1837 return; // Image unspecified. Not an error.
1838 }
1839
1840 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0)
1841 {
1842 return; // Zero dimension. Not an error.
1843 }
1844
1845 int maxsize = std::max(std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight()), image[mBaseLevel]->getDepth());
1846 int p = log2(maxsize) + mBaseLevel;
1847 int q = std::min(p, mMaxLevel);
1848
1849 for(int i = mBaseLevel + 1; i <= q; i++)
1850 {
1851 if(image[i])
1852 {
1853 image[i]->release();
1854 }
1855
1856 image[i] = egl::Image::create(this, std::max(image[mBaseLevel]->getWidth() >> i, 1), std::max(image[mBaseLevel]->getHeight() >> i, 1), std::max(image[mBaseLevel]->getDepth() >> i, 1), 0, image[mBaseLevel]->getFormat());
1857
1858 if(!image[i])
1859 {
1860 return error(GL_OUT_OF_MEMORY);
1861 }
1862
1863 getDevice()->stretchCube(image[i - 1], image[i]);
1864 }
1865 }
1866
getImage(unsigned int level)1867 egl::Image *Texture3D::getImage(unsigned int level)
1868 {
1869 return image[level];
1870 }
1871
getRenderbuffer(GLenum target,GLint level)1872 Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level)
1873 {
1874 if(target != getTarget())
1875 {
1876 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
1877 }
1878
1879 if(!mColorbufferProxy)
1880 {
1881 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level));
1882 }
1883 else
1884 {
1885 mColorbufferProxy->setLevel(level);
1886 }
1887
1888 return mColorbufferProxy;
1889 }
1890
getRenderTarget(GLenum target,unsigned int level)1891 egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)
1892 {
1893 ASSERT(target == getTarget());
1894 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1895
1896 if(image[level])
1897 {
1898 image[level]->addRef();
1899 }
1900
1901 return image[level];
1902 }
1903
isShared(GLenum target,unsigned int level) const1904 bool Texture3D::isShared(GLenum target, unsigned int level) const
1905 {
1906 ASSERT(target == getTarget());
1907 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1908
1909 if(mSurface) // Bound to an EGLSurface
1910 {
1911 return true;
1912 }
1913
1914 if(!image[level])
1915 {
1916 return false;
1917 }
1918
1919 return image[level]->isShared();
1920 }
1921
Texture2DArray(GLuint name)1922 Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)
1923 {
1924 }
1925
~Texture2DArray()1926 Texture2DArray::~Texture2DArray()
1927 {
1928 }
1929
getTarget() const1930 GLenum Texture2DArray::getTarget() const
1931 {
1932 return GL_TEXTURE_2D_ARRAY;
1933 }
1934
generateMipmaps()1935 void Texture2DArray::generateMipmaps()
1936 {
1937 if(!image[mBaseLevel])
1938 {
1939 return; // Image unspecified. Not an error.
1940 }
1941
1942 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0)
1943 {
1944 return; // Zero dimension. Not an error.
1945 }
1946
1947 int depth = image[mBaseLevel]->getDepth();
1948 int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight());
1949 int p = log2(maxsize) + mBaseLevel;
1950 int q = std::min(p, mMaxLevel);
1951
1952 for(int i = mBaseLevel + 1; i <= q; i++)
1953 {
1954 if(image[i])
1955 {
1956 image[i]->release();
1957 }
1958
1959 GLsizei w = std::max(image[mBaseLevel]->getWidth() >> i, 1);
1960 GLsizei h = std::max(image[mBaseLevel]->getHeight() >> i, 1);
1961 image[i] = egl::Image::create(this, w, h, depth, 0, image[mBaseLevel]->getFormat());
1962
1963 if(!image[i])
1964 {
1965 return error(GL_OUT_OF_MEMORY);
1966 }
1967
1968 GLsizei srcw = image[i - 1]->getWidth();
1969 GLsizei srch = image[i - 1]->getHeight();
1970 for(int z = 0; z < depth; ++z)
1971 {
1972 sw::SliceRectF srcRect(0.0f, 0.0f, static_cast<float>(srcw), static_cast<float>(srch), z);
1973 sw::SliceRect dstRect(0, 0, w, h, z);
1974 getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER);
1975 }
1976 }
1977 }
1978
TextureExternal(GLuint name)1979 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
1980 {
1981 mMinFilter = GL_LINEAR;
1982 mMagFilter = GL_LINEAR;
1983 mWrapS = GL_CLAMP_TO_EDGE;
1984 mWrapT = GL_CLAMP_TO_EDGE;
1985 mWrapR = GL_CLAMP_TO_EDGE;
1986 }
1987
~TextureExternal()1988 TextureExternal::~TextureExternal()
1989 {
1990 }
1991
getTarget() const1992 GLenum TextureExternal::getTarget() const
1993 {
1994 return GL_TEXTURE_EXTERNAL_OES;
1995 }
1996
1997 }
1998
createBackBuffer(int width,int height,sw::Format format,int multiSampleDepth)1999 NO_SANITIZE_FUNCTION egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth)
2000 {
2001 if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
2002 {
2003 ERR("Invalid parameters: %dx%d", width, height);
2004 return nullptr;
2005 }
2006
2007 GLenum internalformat = sw2es::ConvertBackBufferFormat(format);
2008
2009 return egl::Image::create(width, height, internalformat, multiSampleDepth, false);
2010 }
2011
createBackBufferFromClientBuffer(const egl::ClientBuffer & clientBuffer)2012 NO_SANITIZE_FUNCTION egl::Image *createBackBufferFromClientBuffer(const egl::ClientBuffer& clientBuffer)
2013 {
2014 if(clientBuffer.getWidth() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE ||
2015 clientBuffer.getHeight() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
2016 {
2017 ERR("Invalid parameters: %dx%d", clientBuffer.getWidth(), clientBuffer.getHeight());
2018 return nullptr;
2019 }
2020
2021 return egl::Image::create(clientBuffer);
2022 }
2023
createDepthStencil(int width,int height,sw::Format format,int multiSampleDepth)2024 NO_SANITIZE_FUNCTION egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth)
2025 {
2026 if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
2027 {
2028 ERR("Invalid parameters: %dx%d", width, height);
2029 return nullptr;
2030 }
2031
2032 bool lockable = true;
2033
2034 switch(format)
2035 {
2036 // case sw::FORMAT_D15S1:
2037 case sw::FORMAT_D24S8:
2038 case sw::FORMAT_D24X8:
2039 // case sw::FORMAT_D24X4S4:
2040 case sw::FORMAT_D24FS8:
2041 case sw::FORMAT_D32:
2042 case sw::FORMAT_D16:
2043 lockable = false;
2044 break;
2045 // case sw::FORMAT_S8_LOCKABLE:
2046 // case sw::FORMAT_D16_LOCKABLE:
2047 case sw::FORMAT_D32F_LOCKABLE:
2048 // case sw::FORMAT_D32_LOCKABLE:
2049 case sw::FORMAT_DF24S8:
2050 case sw::FORMAT_DF16S8:
2051 lockable = true;
2052 break;
2053 default:
2054 UNREACHABLE(format);
2055 }
2056
2057 GLenum internalformat = sw2es::ConvertDepthStencilFormat(format);
2058
2059 egl::Image *surface = egl::Image::create(width, height, internalformat, multiSampleDepth, lockable);
2060
2061 if(!surface)
2062 {
2063 ERR("Out of memory");
2064 return nullptr;
2065 }
2066
2067 return surface;
2068 }
2069