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
requiresSync() const528 bool Texture2D::requiresSync() const
529 {
530 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
531 {
532 if(image[level] && image[level]->requiresSync())
533 {
534 return true;
535 }
536 }
537
538 return false;
539 }
540
setImage(GLint level,GLsizei width,GLsizei height,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)541 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
542 {
543 if(image[level])
544 {
545 image[level]->release();
546 }
547
548 image[level] = egl::Image::create(this, width, height, internalformat);
549
550 if(!image[level])
551 {
552 return error(GL_OUT_OF_MEMORY);
553 }
554
555 Texture::setImage(format, type, unpackParameters, pixels, image[level]);
556 }
557
bindTexImage(gl::Surface * surface)558 void Texture2D::bindTexImage(gl::Surface *surface)
559 {
560 image.release();
561
562 image[0] = surface->getRenderTarget();
563
564 mSurface = surface;
565 mSurface->setBoundTexture(this);
566 }
567
releaseTexImage()568 void Texture2D::releaseTexImage()
569 {
570 image.release();
571
572 if(mSurface)
573 {
574 mSurface->setBoundTexture(nullptr);
575 mSurface = nullptr;
576 }
577 }
578
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)579 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
580 {
581 if(image[level])
582 {
583 image[level]->release();
584 }
585
586 image[level] = egl::Image::create(this, width, height, format);
587
588 if(!image[level])
589 {
590 return error(GL_OUT_OF_MEMORY);
591 }
592
593 Texture::setCompressedImage(imageSize, pixels, image[level]);
594 }
595
subImage(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)596 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
597 {
598 Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels, image[level]);
599 }
600
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)601 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
602 {
603 Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);
604 }
605
copyImage(GLint level,GLenum internalformat,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)606 void Texture2D::copyImage(GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
607 {
608 if(image[level])
609 {
610 image[level]->release();
611 }
612
613 image[level] = egl::Image::create(this, width, height, internalformat);
614
615 if(!image[level])
616 {
617 return error(GL_OUT_OF_MEMORY);
618 }
619
620 if(width != 0 && height != 0)
621 {
622 egl::Image *renderTarget = source->getRenderTarget();
623
624 if(!renderTarget)
625 {
626 ERR("Failed to retrieve the render target.");
627 return error(GL_OUT_OF_MEMORY);
628 }
629
630 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
631 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
632
633 copy(renderTarget, sourceRect, 0, 0, 0, image[level]);
634
635 renderTarget->release();
636 }
637 }
638
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)639 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
640 {
641 if(!image[level])
642 {
643 return error(GL_INVALID_OPERATION);
644 }
645
646 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)
647 {
648 return error(GL_INVALID_VALUE);
649 }
650
651 if(width > 0 && height > 0)
652 {
653 egl::Image *renderTarget = source->getRenderTarget();
654
655 if(!renderTarget)
656 {
657 ERR("Failed to retrieve the render target.");
658 return error(GL_OUT_OF_MEMORY);
659 }
660
661 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
662 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
663
664 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[level]);
665
666 renderTarget->release();
667 }
668 }
669
setSharedImage(egl::Image * sharedImage)670 void Texture2D::setSharedImage(egl::Image *sharedImage)
671 {
672 if(sharedImage == image[0])
673 {
674 return;
675 }
676
677 sharedImage->addRef();
678
679 if(image[0])
680 {
681 image[0]->release();
682 }
683
684 image[0] = sharedImage;
685 }
686
687 // Tests for 2D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isSamplerComplete(Sampler * sampler) const688 bool Texture2D::isSamplerComplete(Sampler *sampler) const
689 {
690 if(!image[mBaseLevel])
691 {
692 return false;
693 }
694
695 GLsizei width = image[mBaseLevel]->getWidth();
696 GLsizei height = image[mBaseLevel]->getHeight();
697
698 if(width <= 0 || height <= 0)
699 {
700 return false;
701 }
702
703 if(isMipmapFiltered(sampler))
704 {
705 if(!isMipmapComplete())
706 {
707 return false;
708 }
709 }
710
711 return true;
712 }
713
714 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isMipmapComplete() const715 bool Texture2D::isMipmapComplete() const
716 {
717 if(mBaseLevel > mMaxLevel)
718 {
719 return false;
720 }
721
722 GLsizei width = image[mBaseLevel]->getWidth();
723 GLsizei height = image[mBaseLevel]->getHeight();
724 int maxsize = std::max(width, height);
725 int p = log2(maxsize) + mBaseLevel;
726 int q = std::min(p, mMaxLevel);
727
728 for(int level = mBaseLevel + 1; level <= q; level++)
729 {
730 if(!image[level])
731 {
732 return false;
733 }
734
735 if(image[level]->getFormat() != image[mBaseLevel]->getFormat())
736 {
737 return false;
738 }
739
740 int i = level - mBaseLevel;
741
742 if(image[level]->getWidth() != std::max(1, width >> i))
743 {
744 return false;
745 }
746
747 if(image[level]->getHeight() != std::max(1, height >> i))
748 {
749 return false;
750 }
751 }
752
753 return true;
754 }
755
isCompressed(GLenum target,GLint level) const756 bool Texture2D::isCompressed(GLenum target, GLint level) const
757 {
758 return IsCompressed(getFormat(target, level));
759 }
760
isDepth(GLenum target,GLint level) const761 bool Texture2D::isDepth(GLenum target, GLint level) const
762 {
763 return IsDepthTexture(getFormat(target, level));
764 }
765
generateMipmaps()766 void Texture2D::generateMipmaps()
767 {
768 if(!image[mBaseLevel])
769 {
770 return; // Image unspecified. Not an error.
771 }
772
773 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0)
774 {
775 return; // Zero dimension. Not an error.
776 }
777
778 int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight());
779 int p = log2(maxsize) + mBaseLevel;
780 int q = std::min(p, mMaxLevel);
781
782 for(int i = mBaseLevel + 1; i <= q; i++)
783 {
784 if(image[i])
785 {
786 image[i]->release();
787 }
788
789 image[i] = egl::Image::create(this, std::max(image[mBaseLevel]->getWidth() >> i, 1), std::max(image[mBaseLevel]->getHeight() >> i, 1), image[mBaseLevel]->getFormat());
790
791 if(!image[i])
792 {
793 return error(GL_OUT_OF_MEMORY);
794 }
795
796 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
797 }
798 }
799
getImage(unsigned int level)800 egl::Image *Texture2D::getImage(unsigned int level)
801 {
802 return image[level];
803 }
804
getRenderbuffer(GLenum target,GLint level)805 Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level)
806 {
807 if(target != getTarget())
808 {
809 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
810 }
811
812 if(!mColorbufferProxy)
813 {
814 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));
815 }
816 else
817 {
818 mColorbufferProxy->setLevel(level);
819 }
820
821 return mColorbufferProxy;
822 }
823
getRenderTarget(GLenum target,unsigned int level)824 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
825 {
826 ASSERT(target == getTarget());
827 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
828
829 if(image[level])
830 {
831 image[level]->addRef();
832 }
833
834 return image[level];
835 }
836
isShared(GLenum target,unsigned int level) const837 bool Texture2D::isShared(GLenum target, unsigned int level) const
838 {
839 ASSERT(target == getTarget());
840 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
841
842 if(mSurface) // Bound to an EGLSurface
843 {
844 return true;
845 }
846
847 if(!image[level])
848 {
849 return false;
850 }
851
852 return image[level]->isShared();
853 }
854
Texture2DRect(GLuint name)855 Texture2DRect::Texture2DRect(GLuint name) : Texture2D(name)
856 {
857 mMinFilter = GL_LINEAR;
858 mMagFilter = GL_LINEAR;
859 mWrapS = GL_CLAMP_TO_EDGE;
860 mWrapT = GL_CLAMP_TO_EDGE;
861 mWrapR = GL_CLAMP_TO_EDGE;
862 }
863
getTarget() const864 GLenum Texture2DRect::getTarget() const
865 {
866 return GL_TEXTURE_RECTANGLE_ARB;
867 }
868
getRenderbuffer(GLenum target,GLint level)869 Renderbuffer *Texture2DRect::getRenderbuffer(GLenum target, GLint level)
870 {
871 if((target != getTarget()) || (level != 0))
872 {
873 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
874 }
875
876 if(!mColorbufferProxy)
877 {
878 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2DRect(this));
879 }
880
881 return mColorbufferProxy;
882 }
883
TextureCubeMap(GLuint name)884 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
885 {
886 for(int f = 0; f < 6; f++)
887 {
888 mFaceProxies[f] = nullptr;
889 mFaceProxyRefs[f] = 0;
890 }
891 }
892
~TextureCubeMap()893 TextureCubeMap::~TextureCubeMap()
894 {
895 for(int i = 0; i < 6; i++)
896 {
897 image[i].unbind(this);
898 mFaceProxies[i] = nullptr;
899 }
900 }
901
902 // We need to maintain a count of references to renderbuffers acting as
903 // proxies for this texture, so that the texture is not deleted while
904 // proxy references still exist. If the reference count drops to zero,
905 // we set our proxy pointer null, so that a new attempt at referencing
906 // will cause recreation.
addProxyRef(const Renderbuffer * proxy)907 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
908 {
909 for(int f = 0; f < 6; f++)
910 {
911 if(mFaceProxies[f] == proxy)
912 {
913 mFaceProxyRefs[f]++;
914 }
915 }
916 }
917
releaseProxy(const Renderbuffer * proxy)918 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
919 {
920 for(int f = 0; f < 6; f++)
921 {
922 if(mFaceProxies[f] == proxy)
923 {
924 if(mFaceProxyRefs[f] > 0)
925 {
926 mFaceProxyRefs[f]--;
927 }
928
929 if(mFaceProxyRefs[f] == 0)
930 {
931 mFaceProxies[f] = nullptr;
932 }
933 }
934 }
935 }
936
sweep()937 void TextureCubeMap::sweep()
938 {
939 int imageCount = 0;
940
941 for(int f = 0; f < 6; f++)
942 {
943 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
944 {
945 if(image[f][i] && image[f][i]->isChildOf(this))
946 {
947 if(!image[f][i]->hasSingleReference())
948 {
949 return;
950 }
951
952 imageCount++;
953 }
954 }
955 }
956
957 if(imageCount == referenceCount)
958 {
959 destroy();
960 }
961 }
962
getTarget() const963 GLenum TextureCubeMap::getTarget() const
964 {
965 return GL_TEXTURE_CUBE_MAP;
966 }
967
getWidth(GLenum target,GLint level) const968 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
969 {
970 int face = CubeFaceIndex(target);
971 return image[face][level] ? image[face][level]->getWidth() : 0;
972 }
973
getHeight(GLenum target,GLint level) const974 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
975 {
976 int face = CubeFaceIndex(target);
977 return image[face][level] ? image[face][level]->getHeight() : 0;
978 }
979
getFormat(GLenum target,GLint level) const980 GLint TextureCubeMap::getFormat(GLenum target, GLint level) const
981 {
982 int face = CubeFaceIndex(target);
983 return image[face][level] ? image[face][level]->getFormat() : 0;
984 }
985
getTopLevel() const986 int TextureCubeMap::getTopLevel() const
987 {
988 int level = mBaseLevel;
989
990 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][level])
991 {
992 level++;
993 }
994
995 return level - 1;
996 }
997
requiresSync() const998 bool TextureCubeMap::requiresSync() const
999 {
1000 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1001 {
1002 for(int face = 0; face < 6; face++)
1003 {
1004 if(image[face][level] && image[face][level]->requiresSync())
1005 {
1006 return true;
1007 }
1008 }
1009 }
1010
1011 return false;
1012 }
1013
setCompressedImage(GLenum target,GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)1014 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
1015 {
1016 int face = CubeFaceIndex(target);
1017
1018 if(image[face][level])
1019 {
1020 image[face][level]->release();
1021 }
1022
1023 image[face][level] = egl::Image::create(this, width, height, 1, 1, format);
1024
1025 if(!image[face][level])
1026 {
1027 return error(GL_OUT_OF_MEMORY);
1028 }
1029
1030 Texture::setCompressedImage(imageSize, pixels, image[face][level]);
1031 }
1032
subImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1033 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)
1034 {
1035 Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels, image[CubeFaceIndex(target)][level]);
1036 }
1037
subImageCompressed(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)1038 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
1039 {
1040 Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);
1041 }
1042
1043 // Tests for cube map sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 161.
isSamplerComplete(Sampler * sampler) const1044 bool TextureCubeMap::isSamplerComplete(Sampler *sampler) const
1045 {
1046 for(int face = 0; face < 6; face++)
1047 {
1048 if(!image[face][mBaseLevel])
1049 {
1050 return false;
1051 }
1052 }
1053
1054 int size = image[0][mBaseLevel]->getWidth();
1055
1056 if(size <= 0)
1057 {
1058 return false;
1059 }
1060
1061 if(!isMipmapFiltered(sampler))
1062 {
1063 if(!isCubeComplete())
1064 {
1065 return false;
1066 }
1067 }
1068 else
1069 {
1070 if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()
1071 {
1072 return false;
1073 }
1074 }
1075
1076 return true;
1077 }
1078
1079 // Tests for cube texture completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isCubeComplete() const1080 bool TextureCubeMap::isCubeComplete() const
1081 {
1082 if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth())
1083 {
1084 return false;
1085 }
1086
1087 for(unsigned int face = 1; face < 6; face++)
1088 {
1089 if(image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getWidth() ||
1090 image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getHeight() ||
1091 image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat())
1092 {
1093 return false;
1094 }
1095 }
1096
1097 return true;
1098 }
1099
isMipmapCubeComplete() const1100 bool TextureCubeMap::isMipmapCubeComplete() const
1101 {
1102 if(mBaseLevel > mMaxLevel)
1103 {
1104 return false;
1105 }
1106
1107 if(!isCubeComplete())
1108 {
1109 return false;
1110 }
1111
1112 GLsizei size = image[0][mBaseLevel]->getWidth();
1113 int p = log2(size) + mBaseLevel;
1114 int q = std::min(p, mMaxLevel);
1115
1116 for(int face = 0; face < 6; face++)
1117 {
1118 for(int level = mBaseLevel + 1; level <= q; level++)
1119 {
1120 if(!image[face][level])
1121 {
1122 return false;
1123 }
1124
1125 if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat())
1126 {
1127 return false;
1128 }
1129
1130 int i = level - mBaseLevel;
1131
1132 if(image[face][level]->getWidth() != std::max(1, size >> i))
1133 {
1134 return false;
1135 }
1136 }
1137 }
1138
1139 return true;
1140 }
1141
updateBorders(int level)1142 void TextureCubeMap::updateBorders(int level)
1143 {
1144 egl::Image *posX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_X)][level];
1145 egl::Image *negX = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_X)][level];
1146 egl::Image *posY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Y)][level];
1147 egl::Image *negY = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y)][level];
1148 egl::Image *posZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_POSITIVE_Z)][level];
1149 egl::Image *negZ = image[CubeFaceIndex(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)][level];
1150
1151 if(!posX || !negX || !posY || !negY || !posZ || !negZ)
1152 {
1153 return;
1154 }
1155
1156 if(posX->getBorder() == 0) // Non-seamless cube map.
1157 {
1158 return;
1159 }
1160
1161 if(!posX->hasDirtyContents() || !posY->hasDirtyContents() || !posZ->hasDirtyContents() || !negX->hasDirtyContents() || !negY->hasDirtyContents() || !negZ->hasDirtyContents())
1162 {
1163 return;
1164 }
1165
1166 // Copy top / bottom first.
1167 posX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::RIGHT);
1168 posY->copyCubeEdge(sw::Surface::BOTTOM, posZ, sw::Surface::TOP);
1169 posZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::TOP);
1170 negX->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::LEFT);
1171 negY->copyCubeEdge(sw::Surface::BOTTOM, negZ, sw::Surface::BOTTOM);
1172 negZ->copyCubeEdge(sw::Surface::BOTTOM, negY, sw::Surface::BOTTOM);
1173
1174 posX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::RIGHT);
1175 posY->copyCubeEdge(sw::Surface::TOP, negZ, sw::Surface::TOP);
1176 posZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::BOTTOM);
1177 negX->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::LEFT);
1178 negY->copyCubeEdge(sw::Surface::TOP, posZ, sw::Surface::BOTTOM);
1179 negZ->copyCubeEdge(sw::Surface::TOP, posY, sw::Surface::TOP);
1180
1181 // Copy left / right after top and bottom are done.
1182 // The corner colors will be computed assuming top / bottom are already set.
1183 posX->copyCubeEdge(sw::Surface::RIGHT, negZ, sw::Surface::LEFT);
1184 posY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::TOP);
1185 posZ->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::LEFT);
1186 negX->copyCubeEdge(sw::Surface::RIGHT, posZ, sw::Surface::LEFT);
1187 negY->copyCubeEdge(sw::Surface::RIGHT, posX, sw::Surface::BOTTOM);
1188 negZ->copyCubeEdge(sw::Surface::RIGHT, negX, sw::Surface::LEFT);
1189
1190 posX->copyCubeEdge(sw::Surface::LEFT, posZ, sw::Surface::RIGHT);
1191 posY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::TOP);
1192 posZ->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::RIGHT);
1193 negX->copyCubeEdge(sw::Surface::LEFT, negZ, sw::Surface::RIGHT);
1194 negY->copyCubeEdge(sw::Surface::LEFT, negX, sw::Surface::BOTTOM);
1195 negZ->copyCubeEdge(sw::Surface::LEFT, posX, sw::Surface::RIGHT);
1196
1197 posX->markContentsClean();
1198 posY->markContentsClean();
1199 posZ->markContentsClean();
1200 negX->markContentsClean();
1201 negY->markContentsClean();
1202 negZ->markContentsClean();
1203 }
1204
isCompressed(GLenum target,GLint level) const1205 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
1206 {
1207 return IsCompressed(getFormat(target, level));
1208 }
1209
isDepth(GLenum target,GLint level) const1210 bool TextureCubeMap::isDepth(GLenum target, GLint level) const
1211 {
1212 return IsDepthTexture(getFormat(target, level));
1213 }
1214
releaseTexImage()1215 void TextureCubeMap::releaseTexImage()
1216 {
1217 UNREACHABLE(0); // Cube maps cannot have an EGL surface bound as an image
1218 }
1219
setImage(GLenum target,GLint level,GLsizei width,GLsizei height,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1220 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1221 {
1222 int face = CubeFaceIndex(target);
1223
1224 if(image[face][level])
1225 {
1226 image[face][level]->release();
1227 }
1228
1229 image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat);
1230
1231 if(!image[face][level])
1232 {
1233 return error(GL_OUT_OF_MEMORY);
1234 }
1235
1236 Texture::setImage(format, type, unpackParameters, pixels, image[face][level]);
1237 }
1238
copyImage(GLenum target,GLint level,GLenum internalformat,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1239 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1240 {
1241 int face = CubeFaceIndex(target);
1242
1243 if(image[face][level])
1244 {
1245 image[face][level]->release();
1246 }
1247
1248 image[face][level] = egl::Image::create(this, width, height, 1, 1, internalformat);
1249
1250 if(!image[face][level])
1251 {
1252 return error(GL_OUT_OF_MEMORY);
1253 }
1254
1255 if(width != 0 && height != 0)
1256 {
1257 egl::Image *renderTarget = source->getRenderTarget();
1258
1259 if(!renderTarget)
1260 {
1261 ERR("Failed to retrieve the render target.");
1262 return error(GL_OUT_OF_MEMORY);
1263 }
1264
1265 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
1266 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1267
1268 copy(renderTarget, sourceRect, 0, 0, 0, image[face][level]);
1269
1270 renderTarget->release();
1271 }
1272 }
1273
getImage(int face,unsigned int level)1274 egl::Image *TextureCubeMap::getImage(int face, unsigned int level)
1275 {
1276 return image[face][level];
1277 }
1278
getImage(GLenum face,unsigned int level)1279 egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
1280 {
1281 return image[CubeFaceIndex(face)][level];
1282 }
1283
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1284 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1285 {
1286 int face = CubeFaceIndex(target);
1287
1288 if(!image[face][level])
1289 {
1290 return error(GL_INVALID_OPERATION);
1291 }
1292
1293 GLsizei size = image[face][level]->getWidth();
1294
1295 if(xoffset + width > size || yoffset + height > size || zoffset != 0)
1296 {
1297 return error(GL_INVALID_VALUE);
1298 }
1299
1300 if(width > 0 && height > 0)
1301 {
1302 egl::Image *renderTarget = source->getRenderTarget();
1303
1304 if(!renderTarget)
1305 {
1306 ERR("Failed to retrieve the render target.");
1307 return error(GL_OUT_OF_MEMORY);
1308 }
1309
1310 sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
1311 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1312
1313 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[face][level]);
1314
1315 renderTarget->release();
1316 }
1317 }
1318
generateMipmaps()1319 void TextureCubeMap::generateMipmaps()
1320 {
1321 if(!isCubeComplete())
1322 {
1323 return error(GL_INVALID_OPERATION);
1324 }
1325
1326 int p = log2(image[0][mBaseLevel]->getWidth()) + mBaseLevel;
1327 int q = std::min(p, mMaxLevel);
1328
1329 for(int f = 0; f < 6; f++)
1330 {
1331 ASSERT(image[f][mBaseLevel]);
1332
1333 for(int i = mBaseLevel + 1; i <= q; i++)
1334 {
1335 if(image[f][i])
1336 {
1337 image[f][i]->release();
1338 }
1339
1340 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());
1341
1342 if(!image[f][i])
1343 {
1344 return error(GL_OUT_OF_MEMORY);
1345 }
1346
1347 getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
1348 }
1349 }
1350 }
1351
getRenderbuffer(GLenum target,GLint level)1352 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level)
1353 {
1354 if(!IsCubemapTextureTarget(target))
1355 {
1356 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
1357 }
1358
1359 int face = CubeFaceIndex(target);
1360
1361 if(!mFaceProxies[face])
1362 {
1363 mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));
1364 }
1365 else
1366 {
1367 mFaceProxies[face]->setLevel(level);
1368 }
1369
1370 return mFaceProxies[face];
1371 }
1372
getRenderTarget(GLenum target,unsigned int level)1373 egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)
1374 {
1375 ASSERT(IsCubemapTextureTarget(target));
1376 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1377
1378 int face = CubeFaceIndex(target);
1379
1380 if(image[face][level])
1381 {
1382 image[face][level]->addRef();
1383 }
1384
1385 return image[face][level];
1386 }
1387
isShared(GLenum target,unsigned int level) const1388 bool TextureCubeMap::isShared(GLenum target, unsigned int level) const
1389 {
1390 ASSERT(IsCubemapTextureTarget(target));
1391 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1392
1393 int face = CubeFaceIndex(target);
1394
1395 if(!image[face][level])
1396 {
1397 return false;
1398 }
1399
1400 return image[face][level]->isShared();
1401 }
1402
Texture3D(GLuint name)1403 Texture3D::Texture3D(GLuint name) : Texture(name)
1404 {
1405 mSurface = nullptr;
1406
1407 mColorbufferProxy = nullptr;
1408 mProxyRefs = 0;
1409 }
1410
~Texture3D()1411 Texture3D::~Texture3D()
1412 {
1413 image.unbind(this);
1414
1415 if(mSurface)
1416 {
1417 mSurface->setBoundTexture(nullptr);
1418 mSurface = nullptr;
1419 }
1420
1421 mColorbufferProxy = nullptr;
1422 }
1423
1424 // We need to maintain a count of references to renderbuffers acting as
1425 // proxies for this texture, so that we do not attempt to use a pointer
1426 // to a renderbuffer proxy which has been deleted.
addProxyRef(const Renderbuffer * proxy)1427 void Texture3D::addProxyRef(const Renderbuffer *proxy)
1428 {
1429 mProxyRefs++;
1430 }
1431
releaseProxy(const Renderbuffer * proxy)1432 void Texture3D::releaseProxy(const Renderbuffer *proxy)
1433 {
1434 if(mProxyRefs > 0)
1435 {
1436 mProxyRefs--;
1437 }
1438
1439 if(mProxyRefs == 0)
1440 {
1441 mColorbufferProxy = nullptr;
1442 }
1443 }
1444
sweep()1445 void Texture3D::sweep()
1446 {
1447 int imageCount = 0;
1448
1449 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
1450 {
1451 if(image[i] && image[i]->isChildOf(this))
1452 {
1453 if(!image[i]->hasSingleReference())
1454 {
1455 return;
1456 }
1457
1458 imageCount++;
1459 }
1460 }
1461
1462 if(imageCount == referenceCount)
1463 {
1464 destroy();
1465 }
1466 }
1467
getTarget() const1468 GLenum Texture3D::getTarget() const
1469 {
1470 return GL_TEXTURE_3D_OES;
1471 }
1472
getWidth(GLenum target,GLint level) const1473 GLsizei Texture3D::getWidth(GLenum target, GLint level) const
1474 {
1475 ASSERT(target == getTarget());
1476 return image[level] ? image[level]->getWidth() : 0;
1477 }
1478
getHeight(GLenum target,GLint level) const1479 GLsizei Texture3D::getHeight(GLenum target, GLint level) const
1480 {
1481 ASSERT(target == getTarget());
1482 return image[level] ? image[level]->getHeight() : 0;
1483 }
1484
getDepth(GLenum target,GLint level) const1485 GLsizei Texture3D::getDepth(GLenum target, GLint level) const
1486 {
1487 ASSERT(target == getTarget());
1488 return image[level] ? image[level]->getDepth() : 0;
1489 }
1490
getFormat(GLenum target,GLint level) const1491 GLint Texture3D::getFormat(GLenum target, GLint level) const
1492 {
1493 ASSERT(target == getTarget());
1494 return image[level] ? image[level]->getFormat() : GL_NONE;
1495 }
1496
getTopLevel() const1497 int Texture3D::getTopLevel() const
1498 {
1499 int level = mBaseLevel;
1500
1501 while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level])
1502 {
1503 level++;
1504 }
1505
1506 return level - 1;
1507 }
1508
requiresSync() const1509 bool Texture3D::requiresSync() const
1510 {
1511 for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
1512 {
1513 if(image[level] && image[level]->requiresSync())
1514 {
1515 return true;
1516 }
1517 }
1518
1519 return false;
1520 }
1521
setImage(GLint level,GLsizei width,GLsizei height,GLsizei depth,GLint internalformat,GLenum format,GLenum type,const gl::PixelStorageModes & unpackParameters,const void * pixels)1522 void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLint internalformat, GLenum format, GLenum type, const gl::PixelStorageModes &unpackParameters, const void *pixels)
1523 {
1524 if(image[level])
1525 {
1526 image[level]->release();
1527 }
1528
1529 image[level] = egl::Image::create(this, width, height, depth, 0, internalformat);
1530
1531 if(!image[level])
1532 {
1533 return error(GL_OUT_OF_MEMORY);
1534 }
1535
1536 Texture::setImage(format, type, unpackParameters, pixels, image[level]);
1537 }
1538
releaseTexImage()1539 void Texture3D::releaseTexImage()
1540 {
1541 UNREACHABLE(0); // 3D textures cannot have an EGL surface bound as an image
1542 }
1543
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei depth,GLsizei imageSize,const void * pixels)1544 void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
1545 {
1546 if(image[level])
1547 {
1548 image[level]->release();
1549 }
1550
1551 image[level] = egl::Image::create(this, width, height, depth, 0, format);
1552
1553 if(!image[level])
1554 {
1555 return error(GL_OUT_OF_MEMORY);
1556 }
1557
1558 Texture::setCompressedImage(imageSize, pixels, image[level]);
1559 }
1560
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)1561 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)
1562 {
1563 Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackParameters, pixels, image[level]);
1564 }
1565
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLsizei imageSize,const void * pixels)1566 void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
1567 {
1568 Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);
1569 }
1570
copyImage(GLint level,GLenum internalformat,GLint x,GLint y,GLint z,GLsizei width,GLsizei height,GLsizei depth,Renderbuffer * source)1571 void Texture3D::copyImage(GLint level, GLenum internalformat, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Renderbuffer *source)
1572 {
1573 if(image[level])
1574 {
1575 image[level]->release();
1576 }
1577
1578 image[level] = egl::Image::create(this, width, height, depth, 0, internalformat);
1579
1580 if(!image[level])
1581 {
1582 return error(GL_OUT_OF_MEMORY);
1583 }
1584
1585 if(width != 0 && height != 0 && depth != 0)
1586 {
1587 egl::Image *renderTarget = source->getRenderTarget();
1588
1589 if(!renderTarget)
1590 {
1591 ERR("Failed to retrieve the render target.");
1592 return error(GL_OUT_OF_MEMORY);
1593 }
1594
1595 sw::SliceRect sourceRect(x, y, x + width, y + height, z);
1596 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1597
1598 for(GLint sliceZ = 0; sliceZ < depth; sliceZ++, sourceRect.slice++)
1599 {
1600 copy(renderTarget, sourceRect, 0, 0, sliceZ, image[level]);
1601 }
1602
1603 renderTarget->release();
1604 }
1605 }
1606
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLint x,GLint y,GLsizei width,GLsizei height,Renderbuffer * source)1607 void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Renderbuffer *source)
1608 {
1609 if(!image[level])
1610 {
1611 return error(GL_INVALID_OPERATION);
1612 }
1613
1614 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())
1615 {
1616 return error(GL_INVALID_VALUE);
1617 }
1618
1619 if(width > 0 && height > 0)
1620 {
1621 egl::Image *renderTarget = source->getRenderTarget();
1622
1623 if(!renderTarget)
1624 {
1625 ERR("Failed to retrieve the render target.");
1626 return error(GL_OUT_OF_MEMORY);
1627 }
1628
1629 sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};
1630 sourceRect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
1631
1632 copy(renderTarget, sourceRect, xoffset, yoffset, zoffset, image[level]);
1633
1634 renderTarget->release();
1635 }
1636 }
1637
setSharedImage(egl::Image * sharedImage)1638 void Texture3D::setSharedImage(egl::Image *sharedImage)
1639 {
1640 sharedImage->addRef();
1641
1642 if(image[0])
1643 {
1644 image[0]->release();
1645 }
1646
1647 image[0] = sharedImage;
1648 }
1649
1650 // Tests for 3D texture sampling completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isSamplerComplete(Sampler * sampler) const1651 bool Texture3D::isSamplerComplete(Sampler *sampler) const
1652 {
1653 if(!image[mBaseLevel])
1654 {
1655 return false;
1656 }
1657
1658 GLsizei width = image[mBaseLevel]->getWidth();
1659 GLsizei height = image[mBaseLevel]->getHeight();
1660 GLsizei depth = image[mBaseLevel]->getDepth();
1661
1662 if(width <= 0 || height <= 0 || depth <= 0)
1663 {
1664 return false;
1665 }
1666
1667 if(isMipmapFiltered(sampler))
1668 {
1669 if(!isMipmapComplete())
1670 {
1671 return false;
1672 }
1673 }
1674
1675 return true;
1676 }
1677
1678 // Tests for 3D texture (mipmap) completeness. [OpenGL ES 3.0.5] section 3.8.13 page 160.
isMipmapComplete() const1679 bool Texture3D::isMipmapComplete() const
1680 {
1681 if(mBaseLevel > mMaxLevel)
1682 {
1683 return false;
1684 }
1685
1686 GLsizei width = image[mBaseLevel]->getWidth();
1687 GLsizei height = image[mBaseLevel]->getHeight();
1688 GLsizei depth = image[mBaseLevel]->getDepth();
1689 bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;
1690
1691 int maxsize = isTexture2DArray ? std::max(width, height) : std::max(std::max(width, height), depth);
1692 int p = log2(maxsize) + mBaseLevel;
1693 int q = std::min(p, mMaxLevel);
1694
1695 for(int level = mBaseLevel + 1; level <= q; level++)
1696 {
1697 if(!image[level])
1698 {
1699 return false;
1700 }
1701
1702 if(image[level]->getFormat() != image[mBaseLevel]->getFormat())
1703 {
1704 return false;
1705 }
1706
1707 int i = level - mBaseLevel;
1708
1709 if(image[level]->getWidth() != std::max(1, width >> i))
1710 {
1711 return false;
1712 }
1713
1714 if(image[level]->getHeight() != std::max(1, height >> i))
1715 {
1716 return false;
1717 }
1718
1719 int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> i);
1720 if(image[level]->getDepth() != levelDepth)
1721 {
1722 return false;
1723 }
1724 }
1725
1726 return true;
1727 }
1728
isCompressed(GLenum target,GLint level) const1729 bool Texture3D::isCompressed(GLenum target, GLint level) const
1730 {
1731 return IsCompressed(getFormat(target, level));
1732 }
1733
isDepth(GLenum target,GLint level) const1734 bool Texture3D::isDepth(GLenum target, GLint level) const
1735 {
1736 return IsDepthTexture(getFormat(target, level));
1737 }
1738
generateMipmaps()1739 void Texture3D::generateMipmaps()
1740 {
1741 if(!image[mBaseLevel])
1742 {
1743 return; // Image unspecified. Not an error.
1744 }
1745
1746 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0)
1747 {
1748 return; // Zero dimension. Not an error.
1749 }
1750
1751 int maxsize = std::max(std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight()), image[mBaseLevel]->getDepth());
1752 int p = log2(maxsize) + mBaseLevel;
1753 int q = std::min(p, mMaxLevel);
1754
1755 for(int i = mBaseLevel + 1; i <= q; i++)
1756 {
1757 if(image[i])
1758 {
1759 image[i]->release();
1760 }
1761
1762 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());
1763
1764 if(!image[i])
1765 {
1766 return error(GL_OUT_OF_MEMORY);
1767 }
1768
1769 getDevice()->stretchCube(image[i - 1], image[i]);
1770 }
1771 }
1772
getImage(unsigned int level)1773 egl::Image *Texture3D::getImage(unsigned int level)
1774 {
1775 return image[level];
1776 }
1777
getRenderbuffer(GLenum target,GLint level)1778 Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level)
1779 {
1780 if(target != getTarget())
1781 {
1782 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
1783 }
1784
1785 if(!mColorbufferProxy)
1786 {
1787 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level));
1788 }
1789 else
1790 {
1791 mColorbufferProxy->setLevel(level);
1792 }
1793
1794 return mColorbufferProxy;
1795 }
1796
getRenderTarget(GLenum target,unsigned int level)1797 egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)
1798 {
1799 ASSERT(target == getTarget());
1800 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1801
1802 if(image[level])
1803 {
1804 image[level]->addRef();
1805 }
1806
1807 return image[level];
1808 }
1809
isShared(GLenum target,unsigned int level) const1810 bool Texture3D::isShared(GLenum target, unsigned int level) const
1811 {
1812 ASSERT(target == getTarget());
1813 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1814
1815 if(mSurface) // Bound to an EGLSurface
1816 {
1817 return true;
1818 }
1819
1820 if(!image[level])
1821 {
1822 return false;
1823 }
1824
1825 return image[level]->isShared();
1826 }
1827
Texture2DArray(GLuint name)1828 Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)
1829 {
1830 }
1831
~Texture2DArray()1832 Texture2DArray::~Texture2DArray()
1833 {
1834 }
1835
getTarget() const1836 GLenum Texture2DArray::getTarget() const
1837 {
1838 return GL_TEXTURE_2D_ARRAY;
1839 }
1840
generateMipmaps()1841 void Texture2DArray::generateMipmaps()
1842 {
1843 if(!image[mBaseLevel])
1844 {
1845 return; // Image unspecified. Not an error.
1846 }
1847
1848 if(image[mBaseLevel]->getWidth() == 0 || image[mBaseLevel]->getHeight() == 0 || image[mBaseLevel]->getDepth() == 0)
1849 {
1850 return; // Zero dimension. Not an error.
1851 }
1852
1853 int depth = image[mBaseLevel]->getDepth();
1854 int maxsize = std::max(image[mBaseLevel]->getWidth(), image[mBaseLevel]->getHeight());
1855 int p = log2(maxsize) + mBaseLevel;
1856 int q = std::min(p, mMaxLevel);
1857
1858 for(int i = mBaseLevel + 1; i <= q; i++)
1859 {
1860 if(image[i])
1861 {
1862 image[i]->release();
1863 }
1864
1865 GLsizei w = std::max(image[mBaseLevel]->getWidth() >> i, 1);
1866 GLsizei h = std::max(image[mBaseLevel]->getHeight() >> i, 1);
1867 image[i] = egl::Image::create(this, w, h, depth, 0, image[mBaseLevel]->getFormat());
1868
1869 if(!image[i])
1870 {
1871 return error(GL_OUT_OF_MEMORY);
1872 }
1873
1874 GLsizei srcw = image[i - 1]->getWidth();
1875 GLsizei srch = image[i - 1]->getHeight();
1876 for(int z = 0; z < depth; ++z)
1877 {
1878 sw::SliceRectF srcRect(0.0f, 0.0f, static_cast<float>(srcw), static_cast<float>(srch), z);
1879 sw::SliceRect dstRect(0, 0, w, h, z);
1880 getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER);
1881 }
1882 }
1883 }
1884
TextureExternal(GLuint name)1885 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
1886 {
1887 mMinFilter = GL_LINEAR;
1888 mMagFilter = GL_LINEAR;
1889 mWrapS = GL_CLAMP_TO_EDGE;
1890 mWrapT = GL_CLAMP_TO_EDGE;
1891 mWrapR = GL_CLAMP_TO_EDGE;
1892 }
1893
~TextureExternal()1894 TextureExternal::~TextureExternal()
1895 {
1896 }
1897
getTarget() const1898 GLenum TextureExternal::getTarget() const
1899 {
1900 return GL_TEXTURE_EXTERNAL_OES;
1901 }
1902
1903 }
1904
createBackBuffer(int width,int height,sw::Format format,int multiSampleDepth)1905 NO_SANITIZE_FUNCTION egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth)
1906 {
1907 if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
1908 {
1909 ERR("Invalid parameters: %dx%d", width, height);
1910 return nullptr;
1911 }
1912
1913 GLenum internalformat = sw2es::ConvertBackBufferFormat(format);
1914
1915 return egl::Image::create(width, height, internalformat, multiSampleDepth, false);
1916 }
1917
createBackBufferFromClientBuffer(const egl::ClientBuffer & clientBuffer)1918 NO_SANITIZE_FUNCTION egl::Image *createBackBufferFromClientBuffer(const egl::ClientBuffer& clientBuffer)
1919 {
1920 if(clientBuffer.getWidth() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE ||
1921 clientBuffer.getHeight() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
1922 {
1923 ERR("Invalid parameters: %dx%d", clientBuffer.getWidth(), clientBuffer.getHeight());
1924 return nullptr;
1925 }
1926
1927 return egl::Image::create(clientBuffer);
1928 }
1929
createDepthStencil(int width,int height,sw::Format format,int multiSampleDepth)1930 NO_SANITIZE_FUNCTION egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth)
1931 {
1932 if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
1933 {
1934 ERR("Invalid parameters: %dx%d", width, height);
1935 return nullptr;
1936 }
1937
1938 bool lockable = true;
1939
1940 switch(format)
1941 {
1942 // case sw::FORMAT_D15S1:
1943 case sw::FORMAT_D24S8:
1944 case sw::FORMAT_D24X8:
1945 // case sw::FORMAT_D24X4S4:
1946 case sw::FORMAT_D24FS8:
1947 case sw::FORMAT_D32:
1948 case sw::FORMAT_D16:
1949 lockable = false;
1950 break;
1951 // case sw::FORMAT_S8_LOCKABLE:
1952 // case sw::FORMAT_D16_LOCKABLE:
1953 case sw::FORMAT_D32F_LOCKABLE:
1954 // case sw::FORMAT_D32_LOCKABLE:
1955 case sw::FORMAT_DF24S8:
1956 case sw::FORMAT_DF16S8:
1957 lockable = true;
1958 break;
1959 default:
1960 UNREACHABLE(format);
1961 }
1962
1963 GLenum internalformat = sw2es::ConvertDepthStencilFormat(format);
1964
1965 egl::Image *surface = egl::Image::create(width, height, internalformat, multiSampleDepth, lockable);
1966
1967 if(!surface)
1968 {
1969 ERR("Out of memory");
1970 return nullptr;
1971 }
1972
1973 return surface;
1974 }
1975