• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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