Home | History | Annotate | Download | only in libGL
      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 
     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 
     45 Texture::~Texture()
     46 {
     47 	resource->destruct();
     48 }
     49 
     50 sw::Resource *Texture::getResource() const
     51 {
     52 	return resource;
     53 }
     54 
     55 // Returns true on successful filter state update (valid enum parameter)
     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)
     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)
     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)
    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)
    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 
    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 
    149 GLenum Texture::getMinFilter() const
    150 {
    151 	return mMinFilter;
    152 }
    153 
    154 GLenum Texture::getMagFilter() const
    155 {
    156 	return mMagFilter;
    157 }
    158 
    159 GLenum Texture::getWrapS() const
    160 {
    161 	return mWrapS;
    162 }
    163 
    164 GLenum Texture::getWrapT() const
    165 {
    166 	return mWrapT;
    167 }
    168 
    169 GLfloat Texture::getMaxAnisotropy() const
    170 {
    171 	return mMaxAnisotropy;
    172 }
    173 
    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 
    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 
    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 
    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 
    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 
    257 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 
    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 
    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.
    307 void Texture2D::addProxyRef(const Renderbuffer *proxy)
    308 {
    309 	mProxyRefs++;
    310 }
    311 
    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 
    325 GLenum Texture2D::getTarget() const
    326 {
    327 	return GL_TEXTURE_2D;
    328 }
    329 
    330 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 
    336 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 
    342 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 
    348 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 
    354 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 
    360 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    491 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.
    518 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 
    556 bool Texture2D::isCompressed(GLenum target, GLint level) const
    557 {
    558 	return IsCompressed(getFormat(target, level));
    559 }
    560 
    561 bool Texture2D::isDepth(GLenum target, GLint level) const
    562 {
    563 	return IsDepthTexture(getFormat(target, level));
    564 }
    565 
    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 
    593 Image *Texture2D::getImage(unsigned int level)
    594 {
    595 	return image[level];
    596 }
    597 
    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 
    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 
    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 
    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.
    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 
    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 
    702 GLenum TextureCubeMap::getTarget() const
    703 {
    704 	return GL_TEXTURE_CUBE_MAP;
    705 }
    706 
    707 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 
    713 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 
    719 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 
    725 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 
    731 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 
    737 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 
    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 
    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 
    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.
    780 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.
    816 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 
    837 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 
    876 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
    877 {
    878 	return IsCompressed(getFormat(target, level));
    879 }
    880 
    881 bool TextureCubeMap::isDepth(GLenum target, GLint level) const
    882 {
    883 	return IsDepthTexture(getFormat(target, level));
    884 }
    885 
    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 
    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 
    940 Image *TextureCubeMap::getImage(int face, unsigned int level)
    941 {
    942 	return image[face][level];
    943 }
    944 
    945 Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
    946 {
    947 	return image[CubeFaceIndex(face)][level];
    948 }
    949 
    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 
    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 
   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 
   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