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 and TextureCubeMap. Implements GL texture objects and related
17 // functionality.
18
19 #include "Texture.h"
20
21 #include "main.h"
22 #include "mathutil.h"
23 #include "Framebuffer.h"
24 #include "Device.hpp"
25 #include "Display.h"
26 #include "common/debug.h"
27
28 #include <algorithm>
29
30 namespace gl
31 {
32
Texture(GLuint name)33 Texture::Texture(GLuint name) : NamedObject(name)
34 {
35 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
36 mMagFilter = GL_LINEAR;
37 mWrapS = GL_REPEAT;
38 mWrapT = GL_REPEAT;
39 mMaxAnisotropy = 1.0f;
40 mMaxLevel = 1000;
41
42 resource = new sw::Resource(0);
43 }
44
~Texture()45 Texture::~Texture()
46 {
47 resource->destruct();
48 }
49
getResource() const50 sw::Resource *Texture::getResource() const
51 {
52 return resource;
53 }
54
55 // Returns true on successful filter state update (valid enum parameter)
setMinFilter(GLenum filter)56 bool Texture::setMinFilter(GLenum filter)
57 {
58 switch(filter)
59 {
60 case GL_NEAREST:
61 case GL_LINEAR:
62 case GL_NEAREST_MIPMAP_NEAREST:
63 case GL_LINEAR_MIPMAP_NEAREST:
64 case GL_NEAREST_MIPMAP_LINEAR:
65 case GL_LINEAR_MIPMAP_LINEAR:
66 mMinFilter = filter;
67 return true;
68 default:
69 return false;
70 }
71 }
72
73 // Returns true on successful filter state update (valid enum parameter)
setMagFilter(GLenum filter)74 bool Texture::setMagFilter(GLenum filter)
75 {
76 switch(filter)
77 {
78 case GL_NEAREST:
79 case GL_LINEAR:
80 mMagFilter = filter;
81 return true;
82 default:
83 return false;
84 }
85 }
86
87 // Returns true on successful wrap state update (valid enum parameter)
setWrapS(GLenum wrap)88 bool Texture::setWrapS(GLenum wrap)
89 {
90 switch(wrap)
91 {
92 case GL_CLAMP:
93 case GL_REPEAT:
94 case GL_CLAMP_TO_EDGE:
95 case GL_MIRRORED_REPEAT:
96 mWrapS = wrap;
97 return true;
98 default:
99 return false;
100 }
101 }
102
103 // Returns true on successful wrap state update (valid enum parameter)
setWrapT(GLenum wrap)104 bool Texture::setWrapT(GLenum wrap)
105 {
106 switch(wrap)
107 {
108 case GL_CLAMP:
109 case GL_REPEAT:
110 case GL_CLAMP_TO_EDGE:
111 case GL_MIRRORED_REPEAT:
112 mWrapT = wrap;
113 return true;
114 default:
115 return false;
116 }
117 }
118
119 // Returns true on successful max anisotropy update (valid anisotropy value)
setMaxAnisotropy(float textureMaxAnisotropy)120 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)
121 {
122 textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);
123
124 if(textureMaxAnisotropy < 1.0f)
125 {
126 return false;
127 }
128
129 if(mMaxAnisotropy != textureMaxAnisotropy)
130 {
131 mMaxAnisotropy = textureMaxAnisotropy;
132 }
133
134 return true;
135 }
136
setMaxLevel(int level)137 bool Texture::setMaxLevel(int level)
138 {
139 if(level < 0)
140 {
141 return false;
142 }
143
144 mMaxLevel = level;
145
146 return true;
147 }
148
getMinFilter() const149 GLenum Texture::getMinFilter() const
150 {
151 return mMinFilter;
152 }
153
getMagFilter() const154 GLenum Texture::getMagFilter() const
155 {
156 return mMagFilter;
157 }
158
getWrapS() const159 GLenum Texture::getWrapS() const
160 {
161 return mWrapS;
162 }
163
getWrapT() const164 GLenum Texture::getWrapT() const
165 {
166 return mWrapT;
167 }
168
getMaxAnisotropy() const169 GLfloat Texture::getMaxAnisotropy() const
170 {
171 return mMaxAnisotropy;
172 }
173
setImage(GLenum format,GLenum type,GLint unpackAlignment,const void * pixels,Image * image)174 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
175 {
176 if(pixels && image)
177 {
178 image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackAlignment, pixels);
179 }
180 }
181
setCompressedImage(GLsizei imageSize,const void * pixels,Image * image)182 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, Image *image)
183 {
184 if(pixels && image)
185 {
186 image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels);
187 }
188 }
189
subImage(GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels,Image * image)190 void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *image)
191 {
192 if(!image)
193 {
194 return error(GL_INVALID_OPERATION);
195 }
196
197 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
198 {
199 return error(GL_INVALID_VALUE);
200 }
201
202 if(IsCompressed(image->getFormat()))
203 {
204 return error(GL_INVALID_OPERATION);
205 }
206
207 if(format != image->getFormat())
208 {
209 return error(GL_INVALID_OPERATION);
210 }
211
212 if(pixels)
213 {
214 image->loadImageData(xoffset, yoffset, 0, width, height, 1, format, type, unpackAlignment, pixels);
215 }
216 }
217
subImageCompressed(GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels,Image * image)218 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *image)
219 {
220 if(!image)
221 {
222 return error(GL_INVALID_OPERATION);
223 }
224
225 if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight())
226 {
227 return error(GL_INVALID_VALUE);
228 }
229
230 if(format != image->getFormat())
231 {
232 return error(GL_INVALID_OPERATION);
233 }
234
235 if(pixels)
236 {
237 image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels);
238 }
239 }
240
copy(Image * source,const sw::Rect & sourceRect,GLenum destFormat,GLint xoffset,GLint yoffset,Image * dest)241 bool Texture::copy(Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, Image *dest)
242 {
243 Device *device = getDevice();
244
245 sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0);
246 sw::SliceRect sourceSliceRect(sourceRect);
247 bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false);
248
249 if(!success)
250 {
251 return error(GL_OUT_OF_MEMORY, false);
252 }
253
254 return true;
255 }
256
isMipmapFiltered() const257 bool Texture::isMipmapFiltered() const
258 {
259 switch(mMinFilter)
260 {
261 case GL_NEAREST:
262 case GL_LINEAR:
263 return false;
264 case GL_NEAREST_MIPMAP_NEAREST:
265 case GL_LINEAR_MIPMAP_NEAREST:
266 case GL_NEAREST_MIPMAP_LINEAR:
267 case GL_LINEAR_MIPMAP_LINEAR:
268 return true;
269 default: UNREACHABLE(mMinFilter);
270 }
271
272 return false;
273 }
274
Texture2D(GLuint name)275 Texture2D::Texture2D(GLuint name) : Texture(name)
276 {
277 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
278 {
279 image[i] = 0;
280 }
281
282 mColorbufferProxy = nullptr;
283 mProxyRefs = 0;
284 }
285
~Texture2D()286 Texture2D::~Texture2D()
287 {
288 resource->lock(sw::DESTRUCT);
289
290 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
291 {
292 if(image[i])
293 {
294 image[i]->unbind();
295 image[i] = 0;
296 }
297 }
298
299 resource->unlock();
300
301 mColorbufferProxy = nullptr;
302 }
303
304 // We need to maintain a count of references to renderbuffers acting as
305 // proxies for this texture, so that we do not attempt to use a pointer
306 // to a renderbuffer proxy which has been deleted.
addProxyRef(const Renderbuffer * proxy)307 void Texture2D::addProxyRef(const Renderbuffer *proxy)
308 {
309 mProxyRefs++;
310 }
311
releaseProxy(const Renderbuffer * proxy)312 void Texture2D::releaseProxy(const Renderbuffer *proxy)
313 {
314 if(mProxyRefs > 0)
315 {
316 mProxyRefs--;
317 }
318
319 if(mProxyRefs == 0)
320 {
321 mColorbufferProxy = nullptr;
322 }
323 }
324
getTarget() const325 GLenum Texture2D::getTarget() const
326 {
327 return GL_TEXTURE_2D;
328 }
329
getWidth(GLenum target,GLint level) const330 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
331 {
332 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
333 return image[level] ? image[level]->getWidth() : 0;
334 }
335
getHeight(GLenum target,GLint level) const336 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
337 {
338 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
339 return image[level] ? image[level]->getHeight() : 0;
340 }
341
getFormat(GLenum target,GLint level) const342 GLenum Texture2D::getFormat(GLenum target, GLint level) const
343 {
344 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
345 return image[level] ? image[level]->getFormat() : GL_NONE;
346 }
347
getType(GLenum target,GLint level) const348 GLenum Texture2D::getType(GLenum target, GLint level) const
349 {
350 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
351 return image[level] ? image[level]->getType() : GL_NONE;
352 }
353
getInternalFormat(GLenum target,GLint level) const354 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const
355 {
356 ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
357 return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
358 }
359
getLevelCount() const360 int Texture2D::getLevelCount() const
361 {
362 ASSERT(isSamplerComplete());
363 int levels = 0;
364
365 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])
366 {
367 levels++;
368 }
369
370 return levels;
371 }
372
setImage(GLint level,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)373 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
374 {
375 if(image[level])
376 {
377 image[level]->unbind();
378 }
379
380 image[level] = new Image(this, width, height, format, type);
381
382 if(!image[level])
383 {
384 return error(GL_OUT_OF_MEMORY);
385 }
386
387 Texture::setImage(format, type, unpackAlignment, pixels, image[level]);
388 }
389
setCompressedImage(GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)390 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
391 {
392 if(image[level])
393 {
394 image[level]->unbind();
395 }
396
397 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
398
399 if(!image[level])
400 {
401 return error(GL_OUT_OF_MEMORY);
402 }
403
404 Texture::setCompressedImage(imageSize, pixels, image[level]);
405 }
406
subImage(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)407 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
408 {
409 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
410 }
411
subImageCompressed(GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)412 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
413 {
414 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
415 }
416
copyImage(GLint level,GLenum format,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)417 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
418 {
419 Image *renderTarget = source->getRenderTarget();
420
421 if(!renderTarget)
422 {
423 ERR("Failed to retrieve the render target.");
424 return error(GL_OUT_OF_MEMORY);
425 }
426
427 if(image[level])
428 {
429 image[level]->unbind();
430 }
431
432 image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
433
434 if(!image[level])
435 {
436 return error(GL_OUT_OF_MEMORY);
437 }
438
439 if(width != 0 && height != 0)
440 {
441 sw::Rect sourceRect = {x, y, x + width, y + height};
442 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
443
444 copy(renderTarget, sourceRect, format, 0, 0, image[level]);
445 }
446
447 renderTarget->release();
448 }
449
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)450 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
451 {
452 if(!image[level])
453 {
454 return error(GL_INVALID_OPERATION);
455 }
456
457 if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
458 {
459 return error(GL_INVALID_VALUE);
460 }
461
462 Image *renderTarget = source->getRenderTarget();
463
464 if(!renderTarget)
465 {
466 ERR("Failed to retrieve the render target.");
467 return error(GL_OUT_OF_MEMORY);
468 }
469
470 sw::Rect sourceRect = {x, y, x + width, y + height};
471 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
472
473 copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
474
475 renderTarget->release();
476 }
477
setImage(Image * sharedImage)478 void Texture2D::setImage(Image *sharedImage)
479 {
480 sharedImage->addRef();
481
482 if(image[0])
483 {
484 image[0]->unbind();
485 }
486
487 image[0] = sharedImage;
488 }
489
490 // Tests for 2D texture sampling completeness.
isSamplerComplete() const491 bool Texture2D::isSamplerComplete() const
492 {
493 if(!image[0])
494 {
495 return false;
496 }
497
498 GLsizei width = image[0]->getWidth();
499 GLsizei height = image[0]->getHeight();
500
501 if(width <= 0 || height <= 0)
502 {
503 return false;
504 }
505
506 if(isMipmapFiltered())
507 {
508 if(!isMipmapComplete())
509 {
510 return false;
511 }
512 }
513
514 return true;
515 }
516
517 // Tests for 2D texture (mipmap) completeness.
isMipmapComplete() const518 bool Texture2D::isMipmapComplete() const
519 {
520 GLsizei width = image[0]->getWidth();
521 GLsizei height = image[0]->getHeight();
522
523 int q = log2(std::max(width, height));
524
525 for(int level = 1; level <= q && level <= mMaxLevel; level++)
526 {
527 if(!image[level])
528 {
529 return false;
530 }
531
532 if(image[level]->getFormat() != image[0]->getFormat())
533 {
534 return false;
535 }
536
537 if(image[level]->getType() != image[0]->getType())
538 {
539 return false;
540 }
541
542 if(image[level]->getWidth() != std::max(1, width >> level))
543 {
544 return false;
545 }
546
547 if(image[level]->getHeight() != std::max(1, height >> level))
548 {
549 return false;
550 }
551 }
552
553 return true;
554 }
555
isCompressed(GLenum target,GLint level) const556 bool Texture2D::isCompressed(GLenum target, GLint level) const
557 {
558 return IsCompressed(getFormat(target, level));
559 }
560
isDepth(GLenum target,GLint level) const561 bool Texture2D::isDepth(GLenum target, GLint level) const
562 {
563 return IsDepthTexture(getFormat(target, level));
564 }
565
generateMipmaps()566 void Texture2D::generateMipmaps()
567 {
568 if(!image[0])
569 {
570 return; // FIXME: error?
571 }
572
573 unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
574
575 for(unsigned int i = 1; i <= q; i++)
576 {
577 if(image[i])
578 {
579 image[i]->unbind();
580 }
581
582 image[i] = new Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());
583
584 if(!image[i])
585 {
586 return error(GL_OUT_OF_MEMORY);
587 }
588
589 getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
590 }
591 }
592
getImage(unsigned int level)593 Image *Texture2D::getImage(unsigned int level)
594 {
595 return image[level];
596 }
597
getRenderbuffer(GLenum target)598 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
599 {
600 if(target != GL_TEXTURE_2D)
601 {
602 return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
603 }
604
605 if(!mColorbufferProxy)
606 {
607 mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
608 }
609
610 return mColorbufferProxy;
611 }
612
getRenderTarget(GLenum target,unsigned int level)613 Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
614 {
615 ASSERT(target == GL_TEXTURE_2D);
616 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
617
618 if(image[level])
619 {
620 image[level]->addRef();
621 }
622
623 return image[level];
624 }
625
TextureCubeMap(GLuint name)626 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
627 {
628 for(int f = 0; f < 6; f++)
629 {
630 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
631 {
632 image[f][i] = 0;
633 }
634 }
635
636 for(int f = 0; f < 6; f++)
637 {
638 mFaceProxies[f] = nullptr;
639 mFaceProxyRefs[f] = 0;
640 }
641 }
642
~TextureCubeMap()643 TextureCubeMap::~TextureCubeMap()
644 {
645 resource->lock(sw::DESTRUCT);
646
647 for(int f = 0; f < 6; f++)
648 {
649 for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
650 {
651 if(image[f][i])
652 {
653 image[f][i]->unbind();
654 image[f][i] = 0;
655 }
656 }
657 }
658
659 resource->unlock();
660
661 for(int i = 0; i < 6; i++)
662 {
663 mFaceProxies[i] = nullptr;
664 }
665 }
666
667 // We need to maintain a count of references to renderbuffers acting as
668 // proxies for this texture, so that the texture is not deleted while
669 // proxy references still exist. If the reference count drops to zero,
670 // we set our proxy pointer null, so that a new attempt at referencing
671 // will cause recreation.
addProxyRef(const Renderbuffer * proxy)672 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
673 {
674 for(int f = 0; f < 6; f++)
675 {
676 if(mFaceProxies[f] == proxy)
677 {
678 mFaceProxyRefs[f]++;
679 }
680 }
681 }
682
releaseProxy(const Renderbuffer * proxy)683 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
684 {
685 for(int f = 0; f < 6; f++)
686 {
687 if(mFaceProxies[f] == proxy)
688 {
689 if(mFaceProxyRefs[f] > 0)
690 {
691 mFaceProxyRefs[f]--;
692 }
693
694 if(mFaceProxyRefs[f] == 0)
695 {
696 mFaceProxies[f] = nullptr;
697 }
698 }
699 }
700 }
701
getTarget() const702 GLenum TextureCubeMap::getTarget() const
703 {
704 return GL_TEXTURE_CUBE_MAP;
705 }
706
getWidth(GLenum target,GLint level) const707 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
708 {
709 int face = CubeFaceIndex(target);
710 return image[face][level] ? image[face][level]->getWidth() : 0;
711 }
712
getHeight(GLenum target,GLint level) const713 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
714 {
715 int face = CubeFaceIndex(target);
716 return image[face][level] ? image[face][level]->getHeight() : 0;
717 }
718
getFormat(GLenum target,GLint level) const719 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const
720 {
721 int face = CubeFaceIndex(target);
722 return image[face][level] ? image[face][level]->getFormat() : GL_NONE;
723 }
724
getType(GLenum target,GLint level) const725 GLenum TextureCubeMap::getType(GLenum target, GLint level) const
726 {
727 int face = CubeFaceIndex(target);
728 return image[face][level] ? image[face][level]->getType() : GL_NONE;
729 }
730
getInternalFormat(GLenum target,GLint level) const731 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
732 {
733 int face = CubeFaceIndex(target);
734 return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;
735 }
736
getLevelCount() const737 int TextureCubeMap::getLevelCount() const
738 {
739 ASSERT(isSamplerComplete());
740 int levels = 0;
741
742 while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels])
743 {
744 levels++;
745 }
746
747 return levels;
748 }
749
setCompressedImage(GLenum target,GLint level,GLenum format,GLsizei width,GLsizei height,GLsizei imageSize,const void * pixels)750 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
751 {
752 int face = CubeFaceIndex(target);
753
754 if(image[face][level])
755 {
756 image[face][level]->unbind();
757 }
758
759 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
760
761 if(!image[face][level])
762 {
763 return error(GL_OUT_OF_MEMORY);
764 }
765
766 Texture::setCompressedImage(imageSize, pixels, image[face][level]);
767 }
768
subImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)769 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
770 {
771 Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);
772 }
773
subImageCompressed(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLsizei imageSize,const void * pixels)774 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
775 {
776 Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);
777 }
778
779 // Tests for cube map sampling completeness.
isSamplerComplete() const780 bool TextureCubeMap::isSamplerComplete() const
781 {
782 for(int face = 0; face < 6; face++)
783 {
784 if(!image[face][0])
785 {
786 return false;
787 }
788 }
789
790 int size = image[0][0]->getWidth();
791
792 if(size <= 0)
793 {
794 return false;
795 }
796
797 if(!isMipmapFiltered())
798 {
799 if(!isCubeComplete())
800 {
801 return false;
802 }
803 }
804 else
805 {
806 if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()
807 {
808 return false;
809 }
810 }
811
812 return true;
813 }
814
815 // Tests for cube texture completeness.
isCubeComplete() const816 bool TextureCubeMap::isCubeComplete() const
817 {
818 if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())
819 {
820 return false;
821 }
822
823 for(unsigned int face = 1; face < 6; face++)
824 {
825 if(image[face][0]->getWidth() != image[0][0]->getWidth() ||
826 image[face][0]->getWidth() != image[0][0]->getHeight() ||
827 image[face][0]->getFormat() != image[0][0]->getFormat() ||
828 image[face][0]->getType() != image[0][0]->getType())
829 {
830 return false;
831 }
832 }
833
834 return true;
835 }
836
isMipmapCubeComplete() const837 bool TextureCubeMap::isMipmapCubeComplete() const
838 {
839 if(!isCubeComplete())
840 {
841 return false;
842 }
843
844 GLsizei size = image[0][0]->getWidth();
845 int q = log2(size);
846
847 for(int face = 0; face < 6; face++)
848 {
849 for(int level = 1; level <= q; level++)
850 {
851 if(!image[face][level])
852 {
853 return false;
854 }
855
856 if(image[face][level]->getFormat() != image[0][0]->getFormat())
857 {
858 return false;
859 }
860
861 if(image[face][level]->getType() != image[0][0]->getType())
862 {
863 return false;
864 }
865
866 if(image[face][level]->getWidth() != std::max(1, size >> level))
867 {
868 return false;
869 }
870 }
871 }
872
873 return true;
874 }
875
isCompressed(GLenum target,GLint level) const876 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
877 {
878 return IsCompressed(getFormat(target, level));
879 }
880
isDepth(GLenum target,GLint level) const881 bool TextureCubeMap::isDepth(GLenum target, GLint level) const
882 {
883 return IsDepthTexture(getFormat(target, level));
884 }
885
setImage(GLenum target,GLint level,GLsizei width,GLsizei height,GLenum format,GLenum type,GLint unpackAlignment,const void * pixels)886 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
887 {
888 int face = CubeFaceIndex(target);
889
890 if(image[face][level])
891 {
892 image[face][level]->unbind();
893 }
894
895 image[face][level] = new Image(this, width, height, format, type);
896
897 if(!image[face][level])
898 {
899 return error(GL_OUT_OF_MEMORY);
900 }
901
902 Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]);
903 }
904
copyImage(GLenum target,GLint level,GLenum format,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)905 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
906 {
907 Image *renderTarget = source->getRenderTarget();
908
909 if(!renderTarget)
910 {
911 ERR("Failed to retrieve the render target.");
912 return error(GL_OUT_OF_MEMORY);
913 }
914
915 int face = CubeFaceIndex(target);
916
917 if(image[face][level])
918 {
919 image[face][level]->unbind();
920 }
921
922 image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
923
924 if(!image[face][level])
925 {
926 return error(GL_OUT_OF_MEMORY);
927 }
928
929 if(width != 0 && height != 0)
930 {
931 sw::Rect sourceRect = {x, y, x + width, y + height};
932 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
933
934 copy(renderTarget, sourceRect, format, 0, 0, image[face][level]);
935 }
936
937 renderTarget->release();
938 }
939
getImage(int face,unsigned int level)940 Image *TextureCubeMap::getImage(int face, unsigned int level)
941 {
942 return image[face][level];
943 }
944
getImage(GLenum face,unsigned int level)945 Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
946 {
947 return image[CubeFaceIndex(face)][level];
948 }
949
copySubImage(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint x,GLint y,GLsizei width,GLsizei height,Framebuffer * source)950 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
951 {
952 int face = CubeFaceIndex(target);
953
954 if(!image[face][level])
955 {
956 return error(GL_INVALID_OPERATION);
957 }
958
959 GLsizei size = image[face][level]->getWidth();
960
961 if(xoffset + width > size || yoffset + height > size)
962 {
963 return error(GL_INVALID_VALUE);
964 }
965
966 Image *renderTarget = source->getRenderTarget();
967
968 if(!renderTarget)
969 {
970 ERR("Failed to retrieve the render target.");
971 return error(GL_OUT_OF_MEMORY);
972 }
973
974 sw::Rect sourceRect = {x, y, x + width, y + height};
975 sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
976
977 copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]);
978
979 renderTarget->release();
980 }
981
generateMipmaps()982 void TextureCubeMap::generateMipmaps()
983 {
984 if(!isCubeComplete())
985 {
986 return error(GL_INVALID_OPERATION);
987 }
988
989 unsigned int q = log2(image[0][0]->getWidth());
990
991 for(unsigned int f = 0; f < 6; f++)
992 {
993 for(unsigned int i = 1; i <= q; i++)
994 {
995 if(image[f][i])
996 {
997 image[f][i]->unbind();
998 }
999
1000 image[f][i] = new Image(this, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType());
1001
1002 if(!image[f][i])
1003 {
1004 return error(GL_OUT_OF_MEMORY);
1005 }
1006
1007 getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);
1008 }
1009 }
1010 }
1011
getRenderbuffer(GLenum target)1012 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
1013 {
1014 if(!IsCubemapTextureTarget(target))
1015 {
1016 return error(GL_INVALID_OPERATION, (Renderbuffer *)nullptr);
1017 }
1018
1019 int face = CubeFaceIndex(target);
1020
1021 if(!mFaceProxies[face])
1022 {
1023 mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target));
1024 }
1025
1026 return mFaceProxies[face];
1027 }
1028
getRenderTarget(GLenum target,unsigned int level)1029 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)
1030 {
1031 ASSERT(IsCubemapTextureTarget(target));
1032 ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
1033
1034 int face = CubeFaceIndex(target);
1035
1036 if(image[face][level])
1037 {
1038 image[face][level]->addRef();
1039 }
1040
1041 return image[face][level];
1042 }
1043
1044 }
1045