Home | History | Annotate | Download | only in libGLES_CM
      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. [OpenGL ES 2.0.24] section 3.7 page 63.
     18 
     19 #include "Texture.h"
     20 
     21 #include "main.h"
     22 #include "mathutil.h"
     23 #include "Framebuffer.h"
     24 #include "Device.hpp"
     25 #include "libEGL/Display.h"
     26 #include "common/Surface.hpp"
     27 #include "common/debug.h"
     28 
     29 #include <algorithm>
     30 
     31 namespace es1
     32 {
     33 
     34 Texture::Texture(GLuint name) : egl::Texture(name)
     35 {
     36 	mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
     37 	mMagFilter = GL_LINEAR;
     38 	mWrapS = GL_REPEAT;
     39 	mWrapT = GL_REPEAT;
     40 	mMaxAnisotropy = 1.0f;
     41 	generateMipmap = GL_FALSE;
     42 	cropRectU = 0;
     43 	cropRectV = 0;
     44 	cropRectW = 0;
     45 	cropRectH = 0;
     46 
     47 	resource = new sw::Resource(0);
     48 }
     49 
     50 Texture::~Texture()
     51 {
     52 	resource->destruct();
     53 }
     54 
     55 sw::Resource *Texture::getResource() const
     56 {
     57 	return resource;
     58 }
     59 
     60 // Returns true on successful filter state update (valid enum parameter)
     61 bool Texture::setMinFilter(GLenum filter)
     62 {
     63 	switch(filter)
     64 	{
     65 	case GL_NEAREST_MIPMAP_NEAREST:
     66 	case GL_LINEAR_MIPMAP_NEAREST:
     67 	case GL_NEAREST_MIPMAP_LINEAR:
     68 	case GL_LINEAR_MIPMAP_LINEAR:
     69 		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
     70 		{
     71 			return false;
     72 		}
     73 		// Fall through
     74 	case GL_NEAREST:
     75 	case GL_LINEAR:
     76 		mMinFilter = filter;
     77 		return true;
     78 	default:
     79 		return false;
     80 	}
     81 }
     82 
     83 // Returns true on successful filter state update (valid enum parameter)
     84 bool Texture::setMagFilter(GLenum filter)
     85 {
     86 	switch(filter)
     87 	{
     88 	case GL_NEAREST:
     89 	case GL_LINEAR:
     90 		mMagFilter = filter;
     91 		return true;
     92 	default:
     93 		return false;
     94 	}
     95 }
     96 
     97 // Returns true on successful wrap state update (valid enum parameter)
     98 bool Texture::setWrapS(GLenum wrap)
     99 {
    100 	switch(wrap)
    101 	{
    102 	case GL_REPEAT:
    103 	case GL_MIRRORED_REPEAT_OES:
    104 		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
    105 		{
    106 			return false;
    107 		}
    108 		// Fall through
    109 	case GL_CLAMP_TO_EDGE:
    110 		mWrapS = wrap;
    111 		return true;
    112 	default:
    113 		return false;
    114 	}
    115 }
    116 
    117 // Returns true on successful wrap state update (valid enum parameter)
    118 bool Texture::setWrapT(GLenum wrap)
    119 {
    120 	switch(wrap)
    121 	{
    122 	case GL_REPEAT:
    123 	case GL_MIRRORED_REPEAT_OES:
    124 		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
    125 		{
    126 			return false;
    127 		}
    128 		// Fall through
    129 	case GL_CLAMP_TO_EDGE:
    130 		 mWrapT = wrap;
    131 		 return true;
    132 	default:
    133 		return false;
    134 	}
    135 }
    136 
    137 // Returns true on successful max anisotropy update (valid anisotropy value)
    138 bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)
    139 {
    140 	textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);
    141 
    142 	if(textureMaxAnisotropy < 1.0f)
    143 	{
    144 		return false;
    145 	}
    146 
    147 	if(mMaxAnisotropy != textureMaxAnisotropy)
    148 	{
    149 		mMaxAnisotropy = textureMaxAnisotropy;
    150 	}
    151 
    152 	return true;
    153 }
    154 
    155 void Texture::setGenerateMipmap(GLboolean enable)
    156 {
    157 	generateMipmap = enable;
    158 }
    159 
    160 void Texture::setCropRect(GLint u, GLint v, GLint w, GLint h)
    161 {
    162 	cropRectU = u;
    163 	cropRectV = v;
    164 	cropRectW = w;
    165 	cropRectH = h;
    166 }
    167 
    168 GLenum Texture::getMinFilter() const
    169 {
    170 	return mMinFilter;
    171 }
    172 
    173 GLenum Texture::getMagFilter() const
    174 {
    175 	return mMagFilter;
    176 }
    177 
    178 GLenum Texture::getWrapS() const
    179 {
    180 	return mWrapS;
    181 }
    182 
    183 GLenum Texture::getWrapT() const
    184 {
    185 	return mWrapT;
    186 }
    187 
    188 GLfloat Texture::getMaxAnisotropy() const
    189 {
    190 	return mMaxAnisotropy;
    191 }
    192 
    193 GLboolean Texture::getGenerateMipmap() const
    194 {
    195 	return generateMipmap;
    196 }
    197 
    198 GLint Texture::getCropRectU() const
    199 {
    200 	return cropRectU;
    201 }
    202 
    203 GLint Texture::getCropRectV() const
    204 {
    205 	return cropRectV;
    206 }
    207 
    208 GLint Texture::getCropRectW() const
    209 {
    210 	return cropRectW;
    211 }
    212 
    213 GLint Texture::getCropRectH() const
    214 {
    215 	return cropRectH;
    216 }
    217 
    218 egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)
    219 {
    220 	egl::Image *image = getRenderTarget(target, level);   // Increments reference count
    221 
    222 	if(image)
    223 	{
    224 		image->markShared();
    225 	}
    226 
    227 	return image;
    228 }
    229 
    230 void Texture::setImage(GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)
    231 {
    232 	if(pixels && image)
    233 	{
    234 		gl::PixelStorageModes unpackParameters;
    235 		unpackParameters.alignment = unpackAlignment;
    236 		image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), 1, format, type, unpackParameters, pixels);
    237 	}
    238 }
    239 
    240 void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
    241 {
    242 	if(pixels && image && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
    243 	{
    244 		image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), 1, imageSize, pixels);
    245 	}
    246 }
    247 
    248 void Texture::subImage(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, egl::Image *image)
    249 {
    250 	if(!image)
    251 	{
    252 		return error(GL_INVALID_OPERATION);
    253 	}
    254 
    255 	if(pixels)
    256 	{
    257 		gl::PixelStorageModes unpackParameters;
    258 		unpackParameters.alignment = unpackAlignment;
    259 		image->loadImageData(xoffset, yoffset, 0, width, height, 1, format, type, unpackParameters, pixels);
    260 	}
    261 }
    262 
    263 void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)
    264 {
    265 	if(!image)
    266 	{
    267 		return error(GL_INVALID_OPERATION);
    268 	}
    269 
    270 	if(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with gl::ComputeCompressedSize() at the API level
    271 	{
    272 		image->loadCompressedData(xoffset, yoffset, 0, width, height, 1, imageSize, pixels);
    273 	}
    274 }
    275 
    276 bool Texture::copy(egl::Image *source, const sw::Rect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, egl::Image *dest)
    277 {
    278 	Device *device = getDevice();
    279 
    280 	sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), 0);
    281 	sw::SliceRect sourceSliceRect(sourceRect);
    282 	bool success = device->stretchRect(source, &sourceSliceRect, dest, &destRect, false);
    283 
    284 	if(!success)
    285 	{
    286 		return error(GL_OUT_OF_MEMORY, false);
    287 	}
    288 
    289 	return true;
    290 }
    291 
    292 bool Texture::isMipmapFiltered() const
    293 {
    294 	switch(mMinFilter)
    295 	{
    296 	case GL_NEAREST:
    297 	case GL_LINEAR:
    298 		return false;
    299 	case GL_NEAREST_MIPMAP_NEAREST:
    300 	case GL_LINEAR_MIPMAP_NEAREST:
    301 	case GL_NEAREST_MIPMAP_LINEAR:
    302 	case GL_LINEAR_MIPMAP_LINEAR:
    303 		return true;
    304 	default: UNREACHABLE(mMinFilter);
    305 	}
    306 
    307 	return false;
    308 }
    309 
    310 Texture2D::Texture2D(GLuint name) : Texture(name)
    311 {
    312 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    313 	{
    314 		image[i] = nullptr;
    315 	}
    316 
    317 	mSurface = nullptr;
    318 
    319 	mColorbufferProxy = nullptr;
    320 	mProxyRefs = 0;
    321 }
    322 
    323 Texture2D::~Texture2D()
    324 {
    325 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    326 	{
    327 		if(image[i])
    328 		{
    329 			image[i]->unbind(this);
    330 			image[i] = nullptr;
    331 		}
    332 	}
    333 
    334 	if(mSurface)
    335 	{
    336 		mSurface->setBoundTexture(nullptr);
    337 		mSurface = nullptr;
    338 	}
    339 
    340 	mColorbufferProxy = nullptr;
    341 }
    342 
    343 // We need to maintain a count of references to renderbuffers acting as
    344 // proxies for this texture, so that we do not attempt to use a pointer
    345 // to a renderbuffer proxy which has been deleted.
    346 void Texture2D::addProxyRef(const Renderbuffer *proxy)
    347 {
    348 	mProxyRefs++;
    349 }
    350 
    351 void Texture2D::releaseProxy(const Renderbuffer *proxy)
    352 {
    353 	if(mProxyRefs > 0)
    354 	{
    355 		mProxyRefs--;
    356 	}
    357 
    358 	if(mProxyRefs == 0)
    359 	{
    360 		mColorbufferProxy = nullptr;
    361 	}
    362 }
    363 
    364 void Texture2D::sweep()
    365 {
    366 	int imageCount = 0;
    367 
    368 	for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
    369 	{
    370 		if(image[i] && image[i]->isChildOf(this))
    371 		{
    372 			if(!image[i]->hasSingleReference())
    373 			{
    374 				return;
    375 			}
    376 
    377 			imageCount++;
    378 		}
    379 	}
    380 
    381 	if(imageCount == referenceCount)
    382 	{
    383 		destroy();
    384 	}
    385 }
    386 
    387 GLenum Texture2D::getTarget() const
    388 {
    389 	return GL_TEXTURE_2D;
    390 }
    391 
    392 GLsizei Texture2D::getWidth(GLenum target, GLint level) const
    393 {
    394 	ASSERT(target == GL_TEXTURE_2D);
    395 	return image[level] ? image[level]->getWidth() : 0;
    396 }
    397 
    398 GLsizei Texture2D::getHeight(GLenum target, GLint level) const
    399 {
    400 	ASSERT(target == GL_TEXTURE_2D);
    401 	return image[level] ? image[level]->getHeight() : 0;
    402 }
    403 
    404 GLint Texture2D::getFormat(GLenum target, GLint level) const
    405 {
    406 	ASSERT(target == GL_TEXTURE_2D);
    407 	return image[level] ? image[level]->getFormat() : GL_NONE;
    408 }
    409 
    410 int Texture2D::getTopLevel() const
    411 {
    412 	ASSERT(isSamplerComplete());
    413 	int level = 0;
    414 
    415 	while(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[level])
    416 	{
    417 		level++;
    418 	}
    419 
    420 	return level - 1;
    421 }
    422 
    423 void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLint internalformat, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    424 {
    425 	if(image[level])
    426 	{
    427 		image[level]->release();
    428 	}
    429 
    430 	image[level] = egl::Image::create(this, width, height, internalformat);
    431 
    432 	if(!image[level])
    433 	{
    434 		return error(GL_OUT_OF_MEMORY);
    435 	}
    436 
    437 	Texture::setImage(format, type, unpackAlignment, pixels, image[level]);
    438 }
    439 
    440 void Texture2D::bindTexImage(gl::Surface *surface)
    441 {
    442 	for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
    443 	{
    444 		if(image[level])
    445 		{
    446 			image[level]->release();
    447 			image[level] = nullptr;
    448 		}
    449 	}
    450 
    451 	image[0] = surface->getRenderTarget();
    452 
    453 	mSurface = surface;
    454 	mSurface->setBoundTexture(this);
    455 }
    456 
    457 void Texture2D::releaseTexImage()
    458 {
    459 	for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
    460 	{
    461 		if(image[level])
    462 		{
    463 			image[level]->release();
    464 			image[level] = nullptr;
    465 		}
    466 	}
    467 }
    468 
    469 void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
    470 {
    471 	if(image[level])
    472 	{
    473 		image[level]->release();
    474 	}
    475 
    476 	image[level] = egl::Image::create(this, width, height, format);
    477 
    478 	if(!image[level])
    479 	{
    480 		return error(GL_OUT_OF_MEMORY);
    481 	}
    482 
    483 	Texture::setCompressedImage(imageSize, pixels, image[level]);
    484 }
    485 
    486 void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
    487 {
    488 	Texture::subImage(xoffset, yoffset, width, height, format, type, unpackAlignment, pixels, image[level]);
    489 }
    490 
    491 void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
    492 {
    493 	Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, image[level]);
    494 }
    495 
    496 void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    497 {
    498 	egl::Image *renderTarget = source->getRenderTarget();
    499 
    500 	if(!renderTarget)
    501 	{
    502 		ERR("Failed to retrieve the render target.");
    503 		return error(GL_OUT_OF_MEMORY);
    504 	}
    505 
    506 	if(image[level])
    507 	{
    508 		image[level]->release();
    509 	}
    510 
    511 	image[level] = egl::Image::create(this, width, height, format);
    512 
    513 	if(!image[level])
    514 	{
    515 		return error(GL_OUT_OF_MEMORY);
    516 	}
    517 
    518 	if(width != 0 && height != 0)
    519 	{
    520 		sw::Rect sourceRect = {x, y, x + width, y + height};
    521 		sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    522 
    523 		copy(renderTarget, sourceRect, format, 0, 0, image[level]);
    524 	}
    525 
    526 	renderTarget->release();
    527 }
    528 
    529 void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
    530 {
    531 	if(!image[level])
    532 	{
    533 		return error(GL_INVALID_OPERATION);
    534 	}
    535 
    536 	if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight())
    537 	{
    538 		return error(GL_INVALID_VALUE);
    539 	}
    540 
    541 	egl::Image *renderTarget = source->getRenderTarget();
    542 
    543 	if(!renderTarget)
    544 	{
    545 		ERR("Failed to retrieve the render target.");
    546 		return error(GL_OUT_OF_MEMORY);
    547 	}
    548 
    549 	sw::Rect sourceRect = {x, y, x + width, y + height};
    550 	sourceRect.clip(0, 0, source->getColorbuffer()->getWidth(), source->getColorbuffer()->getHeight());
    551 
    552 	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, image[level]);
    553 
    554 	renderTarget->release();
    555 }
    556 
    557 void Texture2D::setSharedImage(egl::Image *sharedImage)
    558 {
    559 	sharedImage->addRef();
    560 
    561 	if(image[0])
    562 	{
    563 		image[0]->release();
    564 	}
    565 
    566 	image[0] = sharedImage;
    567 }
    568 
    569 // Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
    570 bool Texture2D::isSamplerComplete() const
    571 {
    572 	if(!image[0])
    573 	{
    574 		return false;
    575 	}
    576 
    577 	GLsizei width = image[0]->getWidth();
    578 	GLsizei height = image[0]->getHeight();
    579 
    580 	if(width <= 0 || height <= 0)
    581 	{
    582 		return false;
    583 	}
    584 
    585 	if(isMipmapFiltered())
    586 	{
    587 		if(!generateMipmap && !isMipmapComplete())
    588 		{
    589 			return false;
    590 		}
    591 	}
    592 
    593 	return true;
    594 }
    595 
    596 // Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
    597 bool Texture2D::isMipmapComplete() const
    598 {
    599 	GLsizei width = image[0]->getWidth();
    600 	GLsizei height = image[0]->getHeight();
    601 
    602 	int q = log2(std::max(width, height));
    603 
    604 	for(int level = 1; level <= q; level++)
    605 	{
    606 		if(!image[level])
    607 		{
    608 			return false;
    609 		}
    610 
    611 		if(image[level]->getFormat() != image[0]->getFormat())
    612 		{
    613 			return false;
    614 		}
    615 
    616 		if(image[level]->getWidth() != std::max(1, width >> level))
    617 		{
    618 			return false;
    619 		}
    620 
    621 		if(image[level]->getHeight() != std::max(1, height >> level))
    622 		{
    623 			return false;
    624 		}
    625 	}
    626 
    627 	return true;
    628 }
    629 
    630 bool Texture2D::isCompressed(GLenum target, GLint level) const
    631 {
    632 	return IsCompressed(getFormat(target, level));
    633 }
    634 
    635 bool Texture2D::isDepth(GLenum target, GLint level) const
    636 {
    637 	return IsDepthTexture(getFormat(target, level));
    638 }
    639 
    640 void Texture2D::generateMipmaps()
    641 {
    642 	if(!image[0])
    643 	{
    644 		return;   // FIXME: error?
    645 	}
    646 
    647 	unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
    648 
    649 	for(unsigned int i = 1; i <= q; i++)
    650 	{
    651 		if(image[i])
    652 		{
    653 			image[i]->release();
    654 		}
    655 
    656 		image[i] = egl::Image::create(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat());
    657 
    658 		if(!image[i])
    659 		{
    660 			return error(GL_OUT_OF_MEMORY);
    661 		}
    662 
    663 		getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);
    664 	}
    665 }
    666 
    667 void Texture2D::autoGenerateMipmaps()
    668 {
    669 	if(generateMipmap && image[0]->hasDirtyContents())
    670 	{
    671 		generateMipmaps();
    672 		image[0]->markContentsClean();
    673 	}
    674 }
    675 
    676 egl::Image *Texture2D::getImage(unsigned int level)
    677 {
    678 	return image[level];
    679 }
    680 
    681 Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
    682 {
    683 	if(target != GL_TEXTURE_2D)
    684 	{
    685 		return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
    686 	}
    687 
    688 	if(!mColorbufferProxy)
    689 	{
    690 		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
    691 	}
    692 
    693 	return mColorbufferProxy;
    694 }
    695 
    696 egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
    697 {
    698 	ASSERT(target == GL_TEXTURE_2D);
    699 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
    700 
    701 	if(image[level])
    702 	{
    703 		image[level]->addRef();
    704 	}
    705 
    706 	return image[level];
    707 }
    708 
    709 bool Texture2D::isShared(GLenum target, unsigned int level) const
    710 {
    711 	ASSERT(target == GL_TEXTURE_2D);
    712 	ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
    713 
    714 	if(mSurface)   // Bound to an EGLSurface
    715 	{
    716 		return true;
    717 	}
    718 
    719 	if(!image[level])
    720 	{
    721 		return false;
    722 	}
    723 
    724 	return image[level]->isShared();
    725 }
    726 
    727 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
    728 {
    729 	mMinFilter = GL_LINEAR;
    730 	mMagFilter = GL_LINEAR;
    731 	mWrapS = GL_CLAMP_TO_EDGE;
    732 	mWrapT = GL_CLAMP_TO_EDGE;
    733 }
    734 
    735 TextureExternal::~TextureExternal()
    736 {
    737 }
    738 
    739 GLenum TextureExternal::getTarget() const
    740 {
    741 	return GL_TEXTURE_EXTERNAL_OES;
    742 }
    743 
    744 }
    745 
    746 egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth)
    747 {
    748 	if(width > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
    749 	{
    750 		ERR("Invalid parameters: %dx%d", width, height);
    751 		return nullptr;
    752 	}
    753 
    754 	GLenum internalformat = sw2es::ConvertBackBufferFormat(format);
    755 
    756 	return egl::Image::create(width, height, internalformat, multiSampleDepth, false);
    757 }
    758 
    759 egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth)
    760 {
    761 	if(width > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es1::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
    762 	{
    763 		ERR("Invalid parameters: %dx%d", width, height);
    764 		return nullptr;
    765 	}
    766 
    767 	bool lockable = true;
    768 
    769 	switch(format)
    770 	{
    771 //	case sw::FORMAT_D15S1:
    772 	case sw::FORMAT_D24S8:
    773 	case sw::FORMAT_D24X8:
    774 //	case sw::FORMAT_D24X4S4:
    775 	case sw::FORMAT_D24FS8:
    776 	case sw::FORMAT_D32:
    777 	case sw::FORMAT_D16:
    778 		lockable = false;
    779 		break;
    780 //	case sw::FORMAT_S8_LOCKABLE:
    781 //	case sw::FORMAT_D16_LOCKABLE:
    782 	case sw::FORMAT_D32F_LOCKABLE:
    783 //	case sw::FORMAT_D32_LOCKABLE:
    784 	case sw::FORMAT_DF24S8:
    785 	case sw::FORMAT_DF16S8:
    786 		lockable = true;
    787 		break;
    788 	default:
    789 		UNREACHABLE(format);
    790 	}
    791 
    792 	GLenum internalformat = sw2es::ConvertDepthStencilFormat(format);
    793 
    794 	egl::Image *surface = egl::Image::create(width, height, internalformat, multiSampleDepth, lockable);
    795 
    796 	if(!surface)
    797 	{
    798 		ERR("Out of memory");
    799 		return nullptr;
    800 	}
    801 
    802 	return surface;
    803 }
    804