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