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 && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
    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 && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
    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 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    289 	{
    290 		if(image[i])
    291 		{
    292 			image[i]->unbind();
    293 			image[i] = 0;
    294 		}
    295 	}
    296 
    297 	mColorbufferProxy = nullptr;
    298 }
    299 
    300 // We need to maintain a count of references to renderbuffers acting as
    301 // proxies for this texture, so that we do not attempt to use a pointer
    302 // to a renderbuffer proxy which has been deleted.
    303 void Texture2D::addProxyRef(const Renderbuffer *proxy)
    304 {
    305 	mProxyRefs++;
    306 }
    307 
    308 void Texture2D::releaseProxy(const Renderbuffer *proxy)
    309 {
    310 	if(mProxyRefs > 0)
    311 	{
    312 		mProxyRefs--;
    313 	}
    314 
    315 	if(mProxyRefs == 0)
    316 	{
    317 		mColorbufferProxy = nullptr;
    318 	}
    319 }
    320 
    321 GLenum Texture2D::getTarget() const
    322 {
    323 	return GL_TEXTURE_2D;
    324 }
    325 
    326 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
    327 {
    328 	ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
    329 	return image[level] ? image[level]->getWidth() : 0;
    330 }
    331 
    332 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
    333 {
    334 	ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
    335 	return image[level] ? image[level]->getHeight() : 0;
    336 }
    337 
    338 GLenum Texture2D::getFormat(GLenum target, GLint level) const
    339 {
    340 	ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
    341 	return image[level] ? image[level]->getFormat() : GL_NONE;
    342 }
    343 
    344 GLenum Texture2D::getType(GLenum target, GLint level) const
    345 {
    346 	ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
    347 	return image[level] ? image[level]->getType() : GL_NONE;
    348 }
    349 
    350 sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const
    351 {
    352 	ASSERT(target == GL_TEXTURE_2D || target == GL_PROXY_TEXTURE_2D);
    353 	return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
    354 }
    355 
    356 int Texture2D::getTopLevel() const
    357 {
    358 	ASSERT(isSamplerComplete());
    359 	int levels = 0;
    360 
    361 	while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])
    362 	{
    363 		levels++;
    364 	}
    365 
    366 	return levels;
    367 }
    368 
    369 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    370 {
    371 	if(image[level])
    372 	{
    373 		image[level]->unbind();
    374 	}
    375 
    376 	image[level] = new Image(this, width, height, format, type);
    377 
    378 	if(!image[level])
    379 	{
    380 		return error(GL_OUT_OF_MEMORY);
    381 	}
    382 
    383 	Texture::setImage(format, type, unpackAlignment, pixels, image[level]);
    384 }
    385 
    386 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
    387 {
    388 	if(image[level])
    389 	{
    390 		image[level]->unbind();
    391 	}
    392 
    393 	image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
    394 
    395 	if(!image[level])
    396 	{
    397 		return error(GL_OUT_OF_MEMORY);
    398 	}
    399 
    400 	Texture::setCompressedImage(imageSize, pixels, image[level]);
    401 }
    402 
    403 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    404 {
    405 	Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
    406 }
    407 
    408 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
    409 {
    410 	Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
    411 }
    412 
    413 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    414 {
    415 	Image *renderTarget = source->getRenderTarget();
    416 
    417 	if(!renderTarget)
    418 	{
    419 		ERR("Failed to retrieve the render target.");
    420 		return error(GL_OUT_OF_MEMORY);
    421 	}
    422 
    423 	if(image[level])
    424 	{
    425 		image[level]->unbind();
    426 	}
    427 
    428 	image[level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
    429 
    430 	if(!image[level])
    431 	{
    432 		return error(GL_OUT_OF_MEMORY);
    433 	}
    434 
    435 	if(width != 0 && height != 0)
    436 	{
    437 		sw::Rect sourceRect = {x, y, x + width, y + height};
    438 		sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    439 
    440 		copy(renderTarget, sourceRect, format, 0, 0, image[level]);
    441 	}
    442 
    443 	renderTarget->release();
    444 }
    445 
    446 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    447 {
    448 	if(!image[level])
    449 	{
    450 		return error(GL_INVALID_OPERATION);
    451 	}
    452 
    453 	if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
    454 	{
    455 		return error(GL_INVALID_VALUE);
    456 	}
    457 
    458 	Image *renderTarget = source->getRenderTarget();
    459 
    460 	if(!renderTarget)
    461 	{
    462 		ERR("Failed to retrieve the render target.");
    463 		return error(GL_OUT_OF_MEMORY);
    464 	}
    465 
    466 	sw::Rect sourceRect = {x, y, x + width, y + height};
    467 	sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    468 
    469 	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
    470 
    471 	renderTarget->release();
    472 }
    473 
    474 void Texture2D::setImage(Image *sharedImage)
    475 {
    476 	sharedImage->addRef();
    477 
    478 	if(image[0])
    479 	{
    480 		image[0]->unbind();
    481 	}
    482 
    483 	image[0] = sharedImage;
    484 }
    485 
    486 // Tests for 2D texture sampling completeness.
    487 bool Texture2D::isSamplerComplete() const
    488 {
    489 	if(!image[0])
    490 	{
    491 		return false;
    492 	}
    493 
    494 	GLsizei width = image[0]->getWidth();
    495 	GLsizei height = image[0]->getHeight();
    496 
    497 	if(width <= 0 || height <= 0)
    498 	{
    499 		return false;
    500 	}
    501 
    502 	if(isMipmapFiltered())
    503 	{
    504 		if(!isMipmapComplete())
    505 		{
    506 			return false;
    507 		}
    508 	}
    509 
    510 	return true;
    511 }
    512 
    513 // Tests for 2D texture (mipmap) completeness.
    514 bool Texture2D::isMipmapComplete() const
    515 {
    516 	GLsizei width = image[0]->getWidth();
    517 	GLsizei height = image[0]->getHeight();
    518 
    519 	int q = log2(std::max(width, height));
    520 
    521 	for(int level = 1; level <= q && level <= mMaxLevel; level++)
    522 	{
    523 		if(!image[level])
    524 		{
    525 			return false;
    526 		}
    527 
    528 		if(image[level]->getFormat() != image[0]->getFormat())
    529 		{
    530 			return false;
    531 		}
    532 
    533 		if(image[level]->getType() != image[0]->getType())
    534 		{
    535 			return false;
    536 		}
    537 
    538 		if(image[level]->getWidth() != std::max(1, width >> level))
    539 		{
    540 			return false;
    541 		}
    542 
    543 		if(image[level]->getHeight() != std::max(1, height >> level))
    544 		{
    545 			return false;
    546 		}
    547 	}
    548 
    549 	return true;
    550 }
    551 
    552 bool Texture2D::isCompressed(GLenum target, GLint level) const
    553 {
    554 	return IsCompressed(getFormat(target, level));
    555 }
    556 
    557 bool Texture2D::isDepth(GLenum target, GLint level) const
    558 {
    559 	return IsDepthTexture(getFormat(target, level));
    560 }
    561 
    562 void Texture2D::generateMipmaps()
    563 {
    564 	if(!image[0])
    565 	{
    566 		return;   // FIXME: error?
    567 	}
    568 
    569 	unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
    570 
    571 	for(unsigned int i = 1; i <= q; i++)
    572 	{
    573 		if(image[i])
    574 		{
    575 			image[i]->unbind();
    576 		}
    577 
    578 		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());
    579 
    580 		if(!image[i])
    581 		{
    582 			return error(GL_OUT_OF_MEMORY);
    583 		}
    584 
    585 		getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
    586 	}
    587 }
    588 
    589 Image *Texture2D::getImage(unsigned int level)
    590 {
    591 	return image[level];
    592 }
    593 
    594 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
    595 {
    596 	if(target != GL_TEXTURE_2D)
    597 	{
    598 		return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
    599 	}
    600 
    601 	if(!mColorbufferProxy)
    602 	{
    603 		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
    604 	}
    605 
    606 	return mColorbufferProxy;
    607 }
    608 
    609 Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
    610 {
    611 	ASSERT(target == GL_TEXTURE_2D);
    612 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
    613 
    614 	if(image[level])
    615 	{
    616 		image[level]->addRef();
    617 	}
    618 
    619 	return image[level];
    620 }
    621 
    622 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
    623 {
    624 	for(int f = 0; f < 6; f++)
    625 	{
    626 		for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    627 		{
    628 			image[f][i] = 0;
    629 		}
    630 	}
    631 
    632 	for(int f = 0; f < 6; f++)
    633 	{
    634 		mFaceProxies[f] = nullptr;
    635 		mFaceProxyRefs[f] = 0;
    636 	}
    637 }
    638 
    639 TextureCubeMap::~TextureCubeMap()
    640 {
    641 	for(int f = 0; f < 6; f++)
    642 	{
    643 		for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    644 		{
    645 			if(image[f][i])
    646 			{
    647 				image[f][i]->unbind();
    648 				image[f][i] = 0;
    649 			}
    650 		}
    651 	}
    652 
    653 	for(int i = 0; i < 6; i++)
    654 	{
    655 		mFaceProxies[i] = nullptr;
    656 	}
    657 }
    658 
    659 // We need to maintain a count of references to renderbuffers acting as
    660 // proxies for this texture, so that the texture is not deleted while
    661 // proxy references still exist. If the reference count drops to zero,
    662 // we set our proxy pointer null, so that a new attempt at referencing
    663 // will cause recreation.
    664 void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
    665 {
    666 	for(int f = 0; f < 6; f++)
    667 	{
    668 		if(mFaceProxies[f] == proxy)
    669 		{
    670 			mFaceProxyRefs[f]++;
    671 		}
    672 	}
    673 }
    674 
    675 void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
    676 {
    677 	for(int f = 0; f < 6; f++)
    678 	{
    679 		if(mFaceProxies[f] == proxy)
    680 		{
    681 			if(mFaceProxyRefs[f] > 0)
    682 			{
    683 				mFaceProxyRefs[f]--;
    684 			}
    685 
    686 			if(mFaceProxyRefs[f] == 0)
    687 			{
    688 				mFaceProxies[f] = nullptr;
    689 			}
    690 		}
    691 	}
    692 }
    693 
    694 GLenum TextureCubeMap::getTarget() const
    695 {
    696 	return GL_TEXTURE_CUBE_MAP;
    697 }
    698 
    699 GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
    700 {
    701 	int face = CubeFaceIndex(target);
    702 	return image[face][level] ? image[face][level]->getWidth() : 0;
    703 }
    704 
    705 GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
    706 {
    707 	int face = CubeFaceIndex(target);
    708 	return image[face][level] ? image[face][level]->getHeight() : 0;
    709 }
    710 
    711 GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const
    712 {
    713 	int face = CubeFaceIndex(target);
    714 	return image[face][level] ? image[face][level]->getFormat() : GL_NONE;
    715 }
    716 
    717 GLenum TextureCubeMap::getType(GLenum target, GLint level) const
    718 {
    719 	int face = CubeFaceIndex(target);
    720 	return image[face][level] ? image[face][level]->getType() : GL_NONE;
    721 }
    722 
    723 sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
    724 {
    725 	int face = CubeFaceIndex(target);
    726 	return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;
    727 }
    728 
    729 int TextureCubeMap::getTopLevel() const
    730 {
    731 	ASSERT(isSamplerComplete());
    732 	int levels = 0;
    733 
    734 	while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels])
    735 	{
    736 		levels++;
    737 	}
    738 
    739 	return levels;
    740 }
    741 
    742 void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
    743 {
    744 	int face = CubeFaceIndex(target);
    745 
    746 	if(image[face][level])
    747 	{
    748 		image[face][level]->unbind();
    749 	}
    750 
    751 	image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
    752 
    753 	if(!image[face][level])
    754 	{
    755 		return error(GL_OUT_OF_MEMORY);
    756 	}
    757 
    758 	Texture::setCompressedImage(imageSize, pixels, image[face][level]);
    759 }
    760 
    761 void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    762 {
    763 	Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[CubeFaceIndex(target)][level]);
    764 }
    765 
    766 void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
    767 {
    768 	Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);
    769 }
    770 
    771 // Tests for cube map sampling completeness.
    772 bool TextureCubeMap::isSamplerComplete() const
    773 {
    774 	for(int face = 0; face < 6; face++)
    775 	{
    776 		if(!image[face][0])
    777 		{
    778 			return false;
    779 		}
    780 	}
    781 
    782 	int size = image[0][0]->getWidth();
    783 
    784 	if(size <= 0)
    785 	{
    786 		return false;
    787 	}
    788 
    789 	if(!isMipmapFiltered())
    790 	{
    791 		if(!isCubeComplete())
    792 		{
    793 			return false;
    794 		}
    795 	}
    796 	else
    797 	{
    798 		if(!isMipmapCubeComplete())   // Also tests for isCubeComplete()
    799 		{
    800 			return false;
    801 		}
    802 	}
    803 
    804 	return true;
    805 }
    806 
    807 // Tests for cube texture completeness.
    808 bool TextureCubeMap::isCubeComplete() const
    809 {
    810 	if(image[0][0]->getWidth() <= 0 || image[0][0]->getHeight() != image[0][0]->getWidth())
    811 	{
    812 		return false;
    813 	}
    814 
    815 	for(unsigned int face = 1; face < 6; face++)
    816 	{
    817 		if(image[face][0]->getWidth()  != image[0][0]->getWidth() ||
    818 		   image[face][0]->getWidth()  != image[0][0]->getHeight() ||
    819 		   image[face][0]->getFormat() != image[0][0]->getFormat() ||
    820 		   image[face][0]->getType()   != image[0][0]->getType())
    821 		{
    822 			return false;
    823 		}
    824 	}
    825 
    826 	return true;
    827 }
    828 
    829 bool TextureCubeMap::isMipmapCubeComplete() const
    830 {
    831 	if(!isCubeComplete())
    832 	{
    833 		return false;
    834 	}
    835 
    836 	GLsizei size = image[0][0]->getWidth();
    837 	int q = log2(size);
    838 
    839 	for(int face = 0; face < 6; face++)
    840 	{
    841 		for(int level = 1; level <= q; level++)
    842 		{
    843 			if(!image[face][level])
    844 			{
    845 				return false;
    846 			}
    847 
    848 			if(image[face][level]->getFormat() != image[0][0]->getFormat())
    849 			{
    850 				return false;
    851 			}
    852 
    853 			if(image[face][level]->getType() != image[0][0]->getType())
    854 			{
    855 				return false;
    856 			}
    857 
    858 			if(image[face][level]->getWidth() != std::max(1, size >> level))
    859 			{
    860 				return false;
    861 			}
    862 		}
    863 	}
    864 
    865 	return true;
    866 }
    867 
    868 bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
    869 {
    870 	return IsCompressed(getFormat(target, level));
    871 }
    872 
    873 bool TextureCubeMap::isDepth(GLenum target, GLint level) const
    874 {
    875 	return IsDepthTexture(getFormat(target, level));
    876 }
    877 
    878 void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    879 {
    880 	int face = CubeFaceIndex(target);
    881 
    882 	if(image[face][level])
    883 	{
    884 		image[face][level]->unbind();
    885 	}
    886 
    887 	image[face][level] = new Image(this, width, height, format, type);
    888 
    889 	if(!image[face][level])
    890 	{
    891 		return error(GL_OUT_OF_MEMORY);
    892 	}
    893 
    894 	Texture::setImage(format, type, unpackAlignment, pixels, image[face][level]);
    895 }
    896 
    897 void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    898 {
    899 	Image *renderTarget = source->getRenderTarget();
    900 
    901 	if(!renderTarget)
    902 	{
    903 		ERR("Failed to retrieve the render target.");
    904 		return error(GL_OUT_OF_MEMORY);
    905 	}
    906 
    907 	int face = CubeFaceIndex(target);
    908 
    909 	if(image[face][level])
    910 	{
    911 		image[face][level]->unbind();
    912 	}
    913 
    914 	image[face][level] = new Image(this, width, height, format, GL_UNSIGNED_BYTE);
    915 
    916 	if(!image[face][level])
    917 	{
    918 		return error(GL_OUT_OF_MEMORY);
    919 	}
    920 
    921 	if(width != 0 && height != 0)
    922 	{
    923 		sw::Rect sourceRect = {x, y, x + width, y + height};
    924 		sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    925 
    926 		copy(renderTarget, sourceRect, format, 0, 0, image[face][level]);
    927 	}
    928 
    929 	renderTarget->release();
    930 }
    931 
    932 Image *TextureCubeMap::getImage(int face, unsigned int level)
    933 {
    934 	return image[face][level];
    935 }
    936 
    937 Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
    938 {
    939 	return image[CubeFaceIndex(face)][level];
    940 }
    941 
    942 void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    943 {
    944 	int face = CubeFaceIndex(target);
    945 
    946 	if(!image[face][level])
    947 	{
    948 		return error(GL_INVALID_OPERATION);
    949 	}
    950 
    951 	GLsizei size = image[face][level]->getWidth();
    952 
    953 	if(xoffset + width > size || yoffset + height > size)
    954 	{
    955 		return error(GL_INVALID_VALUE);
    956 	}
    957 
    958 	Image *renderTarget = source->getRenderTarget();
    959 
    960 	if(!renderTarget)
    961 	{
    962 		ERR("Failed to retrieve the render target.");
    963 		return error(GL_OUT_OF_MEMORY);
    964 	}
    965 
    966 	sw::Rect sourceRect = {x, y, x + width, y + height};
    967 	sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    968 
    969 	copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, image[face][level]);
    970 
    971 	renderTarget->release();
    972 }
    973 
    974 void TextureCubeMap::generateMipmaps()
    975 {
    976 	if(!isCubeComplete())
    977 	{
    978 		return error(GL_INVALID_OPERATION);
    979 	}
    980 
    981 	unsigned int q = log2(image[0][0]->getWidth());
    982 
    983 	for(unsigned int f = 0; f < 6; f++)
    984 	{
    985 		for(unsigned int i = 1; i <= q; i++)
    986 		{
    987 			if(image[f][i])
    988 			{
    989 				image[f][i]->unbind();
    990 			}
    991 
    992 			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());
    993 
    994 			if(!image[f][i])
    995 			{
    996 				return error(GL_OUT_OF_MEMORY);
    997 			}
    998 
    999 			getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);
   1000 		}
   1001 	}
   1002 }
   1003 
   1004 Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target)
   1005 {
   1006 	if(!IsCubemapTextureTarget(target))
   1007 	{
   1008 		return error(GL_INVALID_OPERATION, (Renderbuffer *)nullptr);
   1009 	}
   1010 
   1011 	int face = CubeFaceIndex(target);
   1012 
   1013 	if(!mFaceProxies[face])
   1014 	{
   1015 		mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target));
   1016 	}
   1017 
   1018 	return mFaceProxies[face];
   1019 }
   1020 
   1021 Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)
   1022 {
   1023 	ASSERT(IsCubemapTextureTarget(target));
   1024 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
   1025 
   1026 	int face = CubeFaceIndex(target);
   1027 
   1028 	if(image[face][level])
   1029 	{
   1030 		image[face][level]->addRef();
   1031 	}
   1032 
   1033 	return image[face][level];
   1034 }
   1035 
   1036 }
   1037