-// SwiftShader Software Renderer\r
-//\r
-// Copyright(c) 2005-2013 TransGaming Inc.\r
-//\r
-// All rights reserved. No part of this software may be copied, distributed, transmitted,\r
-// transcribed, stored in a retrieval system, translated into any human or computer\r
-// language by any means, or disclosed to third parties without the explicit written\r
-// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express\r
-// or implied, including but not limited to any patent rights, are granted to you.\r
-//\r
-\r
-// Texture.cpp: Implements the Texture class and its derived classes\r
-// Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects\r
-// and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.\r
-\r
-#include "Texture.h"\r
-\r
-#include "main.h"\r
-#include "mathutil.h"\r
-#include "Framebuffer.h"\r
-#include "Device.hpp"\r
-#include "libEGL/Display.h"\r
-#include "libEGL/Surface.h"\r
-#include "common/debug.h"\r
-\r
-#include <algorithm>\r
-\r
-namespace es2\r
-{\r
-\r
-Texture::Texture(GLuint name) : egl::Texture(name)\r
-{\r
- mMinFilter = GL_NEAREST_MIPMAP_LINEAR;\r
- mMagFilter = GL_LINEAR;\r
- mWrapS = GL_REPEAT;\r
- mWrapT = GL_REPEAT;\r
- mWrapR = GL_REPEAT;\r
- mMaxAnisotropy = 1.0f;\r
- mBaseLevel = 0;\r
- mCompareFunc = GL_LEQUAL;\r
- mCompareMode = GL_NONE;\r
- mImmutableFormat = GL_FALSE;\r
- mImmutableLevels = 0;\r
- mMaxLevel = 1000;\r
- mMaxLOD = 1000;\r
- mMinLOD = -1000;\r
- mSwizzleR = GL_RED;\r
- mSwizzleG = GL_GREEN;\r
- mSwizzleB = GL_BLUE;\r
- mSwizzleA = GL_ALPHA;\r
-\r
- resource = new sw::Resource(0);\r
-}\r
-\r
-Texture::~Texture()\r
-{\r
- resource->destruct();\r
-}\r
-\r
-sw::Resource *Texture::getResource() const\r
-{\r
- return resource;\r
-}\r
-\r
-// Returns true on successful filter state update (valid enum parameter)\r
-bool Texture::setMinFilter(GLenum filter)\r
-{\r
- switch(filter)\r
- {\r
- case GL_NEAREST_MIPMAP_NEAREST:\r
- case GL_LINEAR_MIPMAP_NEAREST:\r
- case GL_NEAREST_MIPMAP_LINEAR:\r
- case GL_LINEAR_MIPMAP_LINEAR:\r
- if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
- {\r
- return false;\r
- }\r
- // Fall through\r
- case GL_NEAREST:\r
- case GL_LINEAR:\r
- mMinFilter = filter;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-// Returns true on successful filter state update (valid enum parameter)\r
-bool Texture::setMagFilter(GLenum filter)\r
-{\r
- switch(filter)\r
- {\r
- case GL_NEAREST:\r
- case GL_LINEAR:\r
- mMagFilter = filter;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapS(GLenum wrap)\r
-{\r
- switch(wrap)\r
- {\r
- case GL_REPEAT:\r
- case GL_MIRRORED_REPEAT:\r
- if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
- {\r
- return false;\r
- }\r
- // Fall through\r
- case GL_CLAMP_TO_EDGE:\r
- mWrapS = wrap;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapT(GLenum wrap)\r
-{\r
- switch(wrap)\r
- {\r
- case GL_REPEAT:\r
- case GL_MIRRORED_REPEAT:\r
- if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
- {\r
- return false;\r
- }\r
- // Fall through\r
- case GL_CLAMP_TO_EDGE:\r
- mWrapT = wrap;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-// Returns true on successful wrap state update (valid enum parameter)\r
-bool Texture::setWrapR(GLenum wrap)\r
-{\r
- switch(wrap)\r
- {\r
- case GL_REPEAT:\r
- case GL_MIRRORED_REPEAT:\r
- if(getTarget() == GL_TEXTURE_EXTERNAL_OES)\r
- {\r
- return false;\r
- }\r
- // Fall through\r
- case GL_CLAMP_TO_EDGE:\r
- mWrapR = wrap;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-// Returns true on successful max anisotropy update (valid anisotropy value)\r
-bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)\r
-{\r
- textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);\r
-\r
- if(textureMaxAnisotropy < 1.0f)\r
- {\r
- return false;\r
- }\r
-\r
- if(mMaxAnisotropy != textureMaxAnisotropy)\r
- {\r
- mMaxAnisotropy = textureMaxAnisotropy;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool Texture::setBaseLevel(GLint baseLevel)\r
-{\r
- mBaseLevel = baseLevel;\r
- return true;\r
-}\r
-\r
-bool Texture::setCompareFunc(GLenum compareFunc)\r
-{\r
- switch(compareFunc)\r
- {\r
- case GL_LEQUAL:\r
- case GL_GEQUAL:\r
- case GL_LESS:\r
- case GL_GREATER:\r
- case GL_EQUAL:\r
- case GL_NOTEQUAL:\r
- case GL_ALWAYS:\r
- case GL_NEVER:\r
- mCompareFunc = compareFunc;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-bool Texture::setCompareMode(GLenum compareMode)\r
-{\r
- switch(compareMode)\r
- {\r
- case GL_COMPARE_REF_TO_TEXTURE:\r
- case GL_NONE:\r
- mCompareMode = compareMode;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-void Texture::makeImmutable(GLsizei levels)\r
-{\r
- mImmutableFormat = GL_TRUE;\r
- mImmutableLevels = levels;\r
-}\r
-\r
-bool Texture::setMaxLevel(GLint maxLevel)\r
-{\r
- mMaxLevel = maxLevel;\r
- return true;\r
-}\r
-\r
-bool Texture::setMaxLOD(GLfloat maxLOD)\r
-{\r
- mMaxLOD = maxLOD;\r
- return true;\r
-}\r
-\r
-bool Texture::setMinLOD(GLfloat minLOD)\r
-{\r
- mMinLOD = minLOD;\r
- return true;\r
-}\r
-\r
-bool Texture::setSwizzleR(GLenum swizzleR)\r
-{\r
- switch(swizzleR)\r
- {\r
- case GL_RED:\r
- case GL_GREEN:\r
- case GL_BLUE:\r
- case GL_ALPHA:\r
- case GL_ZERO:\r
- case GL_ONE:\r
- mSwizzleR = swizzleR;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-bool Texture::setSwizzleG(GLenum swizzleG)\r
-{\r
- switch(swizzleG)\r
- {\r
- case GL_RED:\r
- case GL_GREEN:\r
- case GL_BLUE:\r
- case GL_ALPHA:\r
- case GL_ZERO:\r
- case GL_ONE:\r
- mSwizzleG = swizzleG;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-bool Texture::setSwizzleB(GLenum swizzleB)\r
-{\r
- switch(swizzleB)\r
- {\r
- case GL_RED:\r
- case GL_GREEN:\r
- case GL_BLUE:\r
- case GL_ALPHA:\r
- case GL_ZERO:\r
- case GL_ONE:\r
- mSwizzleB = swizzleB;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-bool Texture::setSwizzleA(GLenum swizzleA)\r
-{\r
- switch(swizzleA)\r
- {\r
- case GL_RED:\r
- case GL_GREEN:\r
- case GL_BLUE:\r
- case GL_ALPHA:\r
- case GL_ZERO:\r
- case GL_ONE:\r
- mSwizzleA = swizzleA;\r
- return true;\r
- default:\r
- return false;\r
- }\r
-}\r
-\r
-GLenum Texture::getMinFilter() const\r
-{\r
- return mMinFilter;\r
-}\r
-\r
-GLenum Texture::getMagFilter() const\r
-{\r
- return mMagFilter;\r
-}\r
-\r
-GLenum Texture::getWrapS() const\r
-{\r
- return mWrapS;\r
-}\r
-\r
-GLenum Texture::getWrapT() const\r
-{\r
- return mWrapT;\r
-}\r
-\r
-GLenum Texture::getWrapR() const\r
-{\r
- return mWrapR;\r
-}\r
-\r
-GLfloat Texture::getMaxAnisotropy() const\r
-{\r
- return mMaxAnisotropy;\r
-}\r
-\r
-GLint Texture::getBaseLevel() const\r
-{\r
- return mBaseLevel;\r
-}\r
-GLenum Texture::getCompareFunc() const\r
-{\r
- return mCompareFunc;\r
-}\r
-GLenum Texture::getCompareMode() const\r
-{\r
- return mCompareMode;\r
-}\r
-GLboolean Texture::getImmutableFormat() const\r
-{\r
- return mImmutableFormat;\r
-}\r
-GLsizei Texture::getImmutableLevels() const\r
-{\r
- return mImmutableLevels;\r
-}\r
-GLint Texture::getMaxLevel() const\r
-{\r
- return mMaxLevel;\r
-}\r
-GLfloat Texture::getMaxLOD() const\r
-{\r
- return mMaxLOD;\r
-}\r
-GLfloat Texture::getMinLOD() const\r
-{\r
- return mMinLOD;\r
-}\r
-GLenum Texture::getSwizzleR() const\r
-{\r
- return mSwizzleR;\r
-}\r
-GLenum Texture::getSwizzleG() const\r
-{\r
- return mSwizzleG;\r
-}\r
-GLenum Texture::getSwizzleB() const\r
-{\r
- return mSwizzleB;\r
-}\r
-GLenum Texture::getSwizzleA() const\r
-{\r
- return mSwizzleA;\r
-}\r
-\r
-GLsizei Texture::getDepth(GLenum target, GLint level) const\r
-{\r
- return 1;\r
-}\r
-\r
-egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)\r
-{\r
- egl::Image *image = getRenderTarget(target, level); // Increments reference count\r
-\r
- if(image)\r
- {\r
- image->markShared();\r
- }\r
-\r
- return image;\r
-}\r
-\r
-void Texture::setImage(GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)\r
-{\r
- if(pixels && image)\r
- {\r
- GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
- image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackInfo, pixels);\r
- }\r
-}\r
-\r
-void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)\r
-{\r
- if(pixels && image)\r
- {\r
- GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;\r
- image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);\r
- }\r
-}\r
-\r
-void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)\r
-{\r
- if(!image)\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
- {\r
- return error(GL_INVALID_VALUE);\r
- }\r
-\r
- if(IsCompressed(image->getFormat(), egl::getClientVersion()))\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(format != image->getFormat())\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(pixels)\r
- {\r
- image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels);\r
- }\r
-}\r
-\r
-void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)\r
-{\r
- if(!image)\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())\r
- {\r
- return error(GL_INVALID_VALUE);\r
- }\r
-\r
- if(format != image->getFormat())\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(pixels)\r
- {\r
- image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);\r
- }\r
-}\r
-\r
-bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest)\r
-{\r
- Device *device = getDevice();\r
-\r
- sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);\r
- bool success = device->stretchRect(source, &sourceRect, dest, &destRect, false);\r
-\r
- if(!success)\r
- {\r
- return error(GL_OUT_OF_MEMORY, false);\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool Texture::isMipmapFiltered() const\r
-{\r
- switch(mMinFilter)\r
- {\r
- case GL_NEAREST:\r
- case GL_LINEAR:\r
- return false;\r
- case GL_NEAREST_MIPMAP_NEAREST:\r
- case GL_LINEAR_MIPMAP_NEAREST:\r
- case GL_NEAREST_MIPMAP_LINEAR:\r
- case GL_LINEAR_MIPMAP_LINEAR:\r
- return true;\r
- default: UNREACHABLE(mMinFilter);\r
- }\r
-\r
- return false;\r
-}\r
-\r
-Texture2D::Texture2D(GLuint name) : Texture(name)\r
-{\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- image[i] = nullptr;\r
- }\r
-\r
- mSurface = nullptr;\r
-\r
- mColorbufferProxy = nullptr;\r
- mProxyRefs = 0;\r
-}\r
-\r
-Texture2D::~Texture2D()\r
-{\r
- resource->lock(sw::DESTRUCT);\r
-\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[i])\r
- {\r
- image[i]->unbind(this);\r
- image[i] = nullptr;\r
- }\r
- }\r
-\r
- resource->unlock();\r
-\r
- if(mSurface)\r
- {\r
- mSurface->setBoundTexture(nullptr);\r
- mSurface = nullptr;\r
- }\r
-\r
- mColorbufferProxy = nullptr;\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as\r
-// proxies for this texture, so that we do not attempt to use a pointer\r
-// to a renderbuffer proxy which has been deleted.\r
-void Texture2D::addProxyRef(const Renderbuffer *proxy)\r
-{\r
- mProxyRefs++;\r
-}\r
-\r
-void Texture2D::releaseProxy(const Renderbuffer *proxy)\r
-{\r
- if(mProxyRefs > 0)\r
- {\r
- mProxyRefs--;\r
- }\r
-\r
- if(mProxyRefs == 0)\r
- {\r
- mColorbufferProxy = nullptr;\r
- }\r
-}\r
-\r
-void Texture2D::sweep()\r
-{\r
- int imageCount = 0;\r
-\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[i] && image[i]->isChildOf(this))\r
- {\r
- if(!image[i]->hasSingleReference())\r
- {\r
- return;\r
- }\r
-\r
- imageCount++;\r
- }\r
- }\r
-\r
- if(imageCount == referenceCount)\r
- {\r
- destroy();\r
- }\r
-}\r
-\r
-GLenum Texture2D::getTarget() const\r
-{\r
- return GL_TEXTURE_2D;\r
-}\r
-\r
-GLsizei Texture2D::getWidth(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- return image[level] ? image[level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei Texture2D::getHeight(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- return image[level] ? image[level]->getHeight() : 0;\r
-}\r
-\r
-GLenum Texture2D::getFormat(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- return image[level] ? image[level]->getFormat() : GL_NONE;\r
-}\r
-\r
-GLenum Texture2D::getType(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- return image[level] ? image[level]->getType() : GL_NONE;\r
-}\r
-\r
-sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int Texture2D::getLevelCount() const\r
-{\r
- ASSERT(isSamplerComplete());\r
- int levels = 0;\r
-\r
- while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])\r
- {\r
- levels++;\r
- }\r
-\r
- return levels;\r
-}\r
-\r
-void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- image[level] = new egl::Image(this, width, height, format, type);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::bindTexImage(egl::Surface *surface)\r
-{\r
- GLenum format;\r
-\r
- switch(surface->getInternalFormat())\r
- {\r
- case sw::FORMAT_A8R8G8B8:\r
- case sw::FORMAT_SRGB8_A8:\r
- format = GL_BGRA_EXT;\r
- break;\r
- case sw::FORMAT_A8B8G8R8:\r
- format = GL_RGBA;\r
- break;\r
- case sw::FORMAT_X8B8G8R8:\r
- case sw::FORMAT_X8R8G8B8:\r
- case sw::FORMAT_SRGB8_X8:\r
- format = GL_RGB;\r
- break;\r
- default:\r
- UNIMPLEMENTED();\r
- return;\r
- }\r
-\r
- for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)\r
- {\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- image[level] = nullptr;\r
- }\r
- }\r
-\r
- image[0] = surface->getRenderTarget();\r
-\r
- mSurface = surface;\r
- mSurface->setBoundTexture(this);\r
-}\r
-\r
-void Texture2D::releaseTexImage()\r
-{\r
- for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)\r
- {\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- image[level] = nullptr;\r
- }\r
- }\r
-}\r
-\r
-void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
-{\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setCompressedImage(imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
- Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- if(width != 0 && height != 0)\r
- {\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
- copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[level]);\r
- }\r
-\r
- renderTarget->release();\r
-}\r
-\r
-void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
- if(!image[level])\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)\r
- {\r
- return error(GL_INVALID_VALUE);\r
- }\r
-\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
- copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
-\r
- renderTarget->release();\r
-}\r
-\r
-void Texture2D::setImage(egl::Image *sharedImage)\r
-{\r
- if (sharedImage == image[0])\r
- {\r
- return;\r
- }\r
-\r
- sharedImage->addRef();\r
-\r
- if(image[0])\r
- {\r
- image[0]->release();\r
- }\r
-\r
- image[0] = sharedImage;\r
-}\r
-\r
-// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
-bool Texture2D::isSamplerComplete() const\r
-{\r
- if(!image[0])\r
- {\r
- return false;\r
- }\r
-\r
- GLsizei width = image[0]->getWidth();\r
- GLsizei height = image[0]->getHeight();\r
-\r
- if(width <= 0 || height <= 0)\r
- {\r
- return false;\r
- }\r
-\r
- if(isMipmapFiltered())\r
- {\r
- if(!isMipmapComplete())\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool Texture2D::isMipmapComplete() const\r
-{\r
- GLsizei width = image[mBaseLevel]->getWidth();\r
- GLsizei height = image[mBaseLevel]->getHeight();\r
-\r
- int q = std::min(log2(std::max(width, height)), mMaxLevel);\r
-\r
- for(int level = mBaseLevel + 1; level <= q; level++)\r
- {\r
- if(!image[level])\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getFormat() != image[0]->getFormat())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getType() != image[0]->getType())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getWidth() != std::max(1, width >> level))\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getHeight() != std::max(1, height >> level))\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool Texture2D::isCompressed(GLenum target, GLint level) const\r
-{\r
- return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool Texture2D::isDepth(GLenum target, GLint level) const\r
-{\r
- return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void Texture2D::generateMipmaps()\r
-{\r
- if(!image[0])\r
- {\r
- return; // FIXME: error?\r
- }\r
-\r
- unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
-\r
- for(unsigned int i = 1; i <= q; i++)\r
- {\r
- if(image[i])\r
- {\r
- image[i]->release();\r
- }\r
-\r
- image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
-\r
- if(!image[i])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- getDevice()->stretchRect(image[i - 1], 0, image[i], 0, true);\r
- }\r
-}\r
-\r
-egl::Image *Texture2D::getImage(unsigned int level)\r
-{\r
- return image[level];\r
-}\r
-\r
-Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
- if(target != GL_TEXTURE_2D)\r
- {\r
- return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
- }\r
-\r
- if(!mColorbufferProxy)\r
- {\r
- mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));\r
- }\r
- else\r
- {\r
- mColorbufferProxy->setLevel(level);\r
- }\r
-\r
- return mColorbufferProxy;\r
-}\r
-\r
-egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- if(image[level])\r
- {\r
- image[level]->addRef();\r
- }\r
-\r
- return image[level];\r
-}\r
-\r
-bool Texture2D::isShared(GLenum target, unsigned int level) const\r
-{\r
- ASSERT(target == GL_TEXTURE_2D);\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- if(mSurface) // Bound to an EGLSurface\r
- {\r
- return true;\r
- }\r
-\r
- if(!image[level])\r
- {\r
- return false;\r
- }\r
-\r
- return image[level]->isShared();\r
-}\r
-\r
-TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)\r
-{\r
- for(int f = 0; f < 6; f++)\r
- {\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- image[f][i] = nullptr;\r
- }\r
- }\r
-\r
- for(int f = 0; f < 6; f++)\r
- {\r
- mFaceProxies[f] = nullptr;\r
- mFaceProxyRefs[f] = 0;\r
- }\r
-}\r
-\r
-TextureCubeMap::~TextureCubeMap()\r
-{\r
- resource->lock(sw::DESTRUCT);\r
-\r
- for(int f = 0; f < 6; f++)\r
- {\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[f][i])\r
- {\r
- image[f][i]->unbind(this);\r
- image[f][i] = nullptr;\r
- }\r
- }\r
- }\r
-\r
- resource->unlock();\r
-\r
- for(int i = 0; i < 6; i++)\r
- {\r
- mFaceProxies[i] = nullptr;\r
- }\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as\r
-// proxies for this texture, so that the texture is not deleted while\r
-// proxy references still exist. If the reference count drops to zero,\r
-// we set our proxy pointer null, so that a new attempt at referencing\r
-// will cause recreation.\r
-void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)\r
-{\r
- for(int f = 0; f < 6; f++)\r
- {\r
- if(mFaceProxies[f] == proxy)\r
- {\r
- mFaceProxyRefs[f]++;\r
- }\r
- }\r
-}\r
-\r
-void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)\r
-{\r
- for(int f = 0; f < 6; f++)\r
- {\r
- if(mFaceProxies[f] == proxy)\r
- {\r
- if(mFaceProxyRefs[f] > 0)\r
- {\r
- mFaceProxyRefs[f]--;\r
- }\r
-\r
- if(mFaceProxyRefs[f] == 0)\r
- {\r
- mFaceProxies[f] = nullptr;\r
- }\r
- }\r
- }\r
-}\r
-\r
-void TextureCubeMap::sweep()\r
-{\r
- int imageCount = 0;\r
-\r
- for(int f = 0; f < 6; f++)\r
- {\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[f][i] && image[f][i]->isChildOf(this))\r
- {\r
- if(!image[f][i]->hasSingleReference())\r
- {\r
- return;\r
- }\r
-\r
- imageCount++;\r
- }\r
- }\r
- }\r
-\r
- if(imageCount == referenceCount)\r
- {\r
- destroy();\r
- }\r
-}\r
-\r
-GLenum TextureCubeMap::getTarget() const\r
-{\r
- return GL_TEXTURE_CUBE_MAP;\r
-}\r
-\r
-GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const\r
-{\r
- int face = CubeFaceIndex(target);\r
- return image[face][level] ? image[face][level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const\r
-{\r
- int face = CubeFaceIndex(target);\r
- return image[face][level] ? image[face][level]->getHeight() : 0;\r
-}\r
-\r
-GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const\r
-{\r
- int face = CubeFaceIndex(target);\r
- return image[face][level] ? image[face][level]->getFormat() : 0;\r
-}\r
-\r
-GLenum TextureCubeMap::getType(GLenum target, GLint level) const\r
-{\r
- int face = CubeFaceIndex(target);\r
- return image[face][level] ? image[face][level]->getType() : 0;\r
-}\r
-\r
-sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const\r
-{\r
- int face = CubeFaceIndex(target);\r
- return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int TextureCubeMap::getLevelCount() const\r
-{\r
- ASSERT(isSamplerComplete());\r
- int levels = 0;\r
-\r
- while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels])\r
- {\r
- levels++;\r
- }\r
-\r
- return levels;\r
-}\r
-\r
-void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)\r
-{\r
- int face = CubeFaceIndex(target);\r
-\r
- if(image[face][level])\r
- {\r
- image[face][level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[face][level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setCompressedImage(imageSize, pixels, image[face][level]);\r
-}\r
-\r
-void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[CubeFaceIndex(target)][level]);\r
-}\r
-\r
-void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
- Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);\r
-}\r
-\r
-// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.\r
-bool TextureCubeMap::isSamplerComplete() const\r
-{\r
- for(int face = 0; face < 6; face++)\r
- {\r
- if(!image[face][0])\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- int size = image[0][0]->getWidth();\r
-\r
- if(size <= 0)\r
- {\r
- return false;\r
- }\r
-\r
- if(!isMipmapFiltered())\r
- {\r
- if(!isCubeComplete())\r
- {\r
- return false;\r
- }\r
- }\r
- else\r
- {\r
- if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool TextureCubeMap::isCubeComplete() const\r
-{\r
- if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth())\r
- {\r
- return false;\r
- }\r
-\r
- for(unsigned int face = 1; face < 6; face++)\r
- {\r
- if(image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getWidth() ||\r
- image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getHeight() ||\r
- image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat() ||\r
- image[face][mBaseLevel]->getType() != image[0][mBaseLevel]->getType())\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool TextureCubeMap::isMipmapCubeComplete() const\r
-{\r
- if(!isCubeComplete())\r
- {\r
- return false;\r
- }\r
-\r
- GLsizei size = image[0][mBaseLevel]->getWidth();\r
- int q = std::min(log2(size), mMaxLevel);\r
-\r
- for(int face = 0; face < 6; face++)\r
- {\r
- for(int level = mBaseLevel + 1; level <= q; level++)\r
- {\r
- if(!image[face][level])\r
- {\r
- return false;\r
- }\r
-\r
- if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[face][level]->getType() != image[0][mBaseLevel]->getType())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[face][level]->getWidth() != std::max(1, size >> level))\r
- {\r
- return false;\r
- }\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool TextureCubeMap::isCompressed(GLenum target, GLint level) const\r
-{\r
- return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool TextureCubeMap::isDepth(GLenum target, GLint level) const\r
-{\r
- return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void TextureCubeMap::releaseTexImage()\r
-{\r
- UNREACHABLE(0); // Cube maps cannot have an EGL surface bound as an image\r
-}\r
-\r
-void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- int face = CubeFaceIndex(target);\r
-\r
- if(image[face][level])\r
- {\r
- image[face][level]->release();\r
- }\r
-\r
- image[face][level] = new egl::Image(this, width, height, format, type);\r
-\r
- if(!image[face][level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setImage(format, type, unpackInfo, pixels, image[face][level]);\r
-}\r
-\r
-void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- int face = CubeFaceIndex(target);\r
-\r
- if(image[face][level])\r
- {\r
- image[face][level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[face][level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- if(width != 0 && height != 0)\r
- {\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
- copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[face][level]);\r
- }\r
-\r
- renderTarget->release();\r
-}\r
-\r
-egl::Image *TextureCubeMap::getImage(int face, unsigned int level)\r
-{\r
- return image[face][level];\r
-}\r
-\r
-egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)\r
-{\r
- return image[CubeFaceIndex(face)][level];\r
-}\r
-\r
-void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
- int face = CubeFaceIndex(target);\r
-\r
- if(!image[face][level])\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- GLsizei size = image[face][level]->getWidth();\r
-\r
- if(xoffset + width > size || yoffset + height > size || zoffset != 0)\r
- {\r
- return error(GL_INVALID_VALUE);\r
- }\r
-\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect(x, y, x + width, y + height, 0);\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
- copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);\r
-\r
- renderTarget->release();\r
-}\r
-\r
-void TextureCubeMap::generateMipmaps()\r
-{\r
- if(!isCubeComplete())\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- unsigned int q = log2(image[0][0]->getWidth());\r
-\r
- for(unsigned int f = 0; f < 6; f++)\r
- {\r
- for(unsigned int i = 1; i <= q; i++)\r
- {\r
- if(image[f][i])\r
- {\r
- image[f][i]->release();\r
- }\r
-\r
- image[f][i] = new egl::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());\r
-\r
- if(!image[f][i])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, true);\r
- }\r
- }\r
-}\r
-\r
-Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
- if(!IsCubemapTextureTarget(target))\r
- {\r
- return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
- }\r
-\r
- int face = CubeFaceIndex(target);\r
-\r
- if(!mFaceProxies[face])\r
- {\r
- mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));\r
- }\r
- else\r
- {\r
- mFaceProxies[face]->setLevel(level);\r
- }\r
-\r
- return mFaceProxies[face];\r
-}\r
-\r
-egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
- ASSERT(IsCubemapTextureTarget(target));\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- int face = CubeFaceIndex(target);\r
-\r
- if(image[face][level])\r
- {\r
- image[face][level]->addRef();\r
- }\r
-\r
- return image[face][level];\r
-}\r
-\r
-bool TextureCubeMap::isShared(GLenum target, unsigned int level) const\r
-{\r
- ASSERT(IsCubemapTextureTarget(target));\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- int face = CubeFaceIndex(target);\r
-\r
- if(!image[face][level])\r
- {\r
- return false;\r
- }\r
-\r
- return image[face][level]->isShared();\r
-}\r
-\r
-Texture3D::Texture3D(GLuint name) : Texture(name)\r
-{\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- image[i] = nullptr;\r
- }\r
-\r
- mSurface = nullptr;\r
-\r
- mColorbufferProxy = nullptr;\r
- mProxyRefs = 0;\r
-}\r
-\r
-Texture3D::~Texture3D()\r
-{\r
- resource->lock(sw::DESTRUCT);\r
-\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[i])\r
- {\r
- image[i]->unbind(this);\r
- image[i] = nullptr;\r
- }\r
- }\r
-\r
- resource->unlock();\r
-\r
- if(mSurface)\r
- {\r
- mSurface->setBoundTexture(nullptr);\r
- mSurface = nullptr;\r
- }\r
-\r
- mColorbufferProxy = nullptr;\r
-}\r
-\r
-// We need to maintain a count of references to renderbuffers acting as\r
-// proxies for this texture, so that we do not attempt to use a pointer\r
-// to a renderbuffer proxy which has been deleted.\r
-void Texture3D::addProxyRef(const Renderbuffer *proxy)\r
-{\r
- mProxyRefs++;\r
-}\r
-\r
-void Texture3D::releaseProxy(const Renderbuffer *proxy)\r
-{\r
- if(mProxyRefs > 0)\r
- {\r
- mProxyRefs--;\r
- }\r
-\r
- if(mProxyRefs == 0)\r
- {\r
- mColorbufferProxy = nullptr;\r
- }\r
-}\r
-\r
-void Texture3D::sweep()\r
-{\r
- int imageCount = 0;\r
-\r
- for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)\r
- {\r
- if(image[i] && image[i]->isChildOf(this))\r
- {\r
- if(!image[i]->hasSingleReference())\r
- {\r
- return;\r
- }\r
-\r
- imageCount++;\r
- }\r
- }\r
-\r
- if(imageCount == referenceCount)\r
- {\r
- destroy();\r
- }\r
-}\r
-\r
-GLenum Texture3D::getTarget() const\r
-{\r
- return GL_TEXTURE_3D_OES;\r
-}\r
-\r
-GLsizei Texture3D::getWidth(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getWidth() : 0;\r
-}\r
-\r
-GLsizei Texture3D::getHeight(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getHeight() : 0;\r
-}\r
-\r
-GLsizei Texture3D::getDepth(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getDepth() : 0;\r
-}\r
-\r
-GLenum Texture3D::getFormat(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getFormat() : GL_NONE;\r
-}\r
-\r
-GLenum Texture3D::getType(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getType() : GL_NONE;\r
-}\r
-\r
-sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const\r
-{\r
- ASSERT(target == getTarget());\r
- return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;\r
-}\r
-\r
-int Texture3D::getLevelCount() const\r
-{\r
- ASSERT(isSamplerComplete());\r
- int levels = 0;\r
-\r
- while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])\r
- {\r
- levels++;\r
- }\r
-\r
- return levels;\r
-}\r
-\r
-void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- image[level] = new egl::Image(this, width, height, depth, format, type);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setImage(format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::bindTexImage(egl::Surface *surface)\r
-{\r
- GLenum format;\r
-\r
- switch(surface->getInternalFormat())\r
- {\r
- case sw::FORMAT_A8R8G8B8:\r
- format = GL_RGBA;\r
- break;\r
- case sw::FORMAT_X8R8G8B8:\r
- format = GL_RGB;\r
- break;\r
- default:\r
- UNIMPLEMENTED();\r
- return;\r
- }\r
-\r
- for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)\r
- {\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- image[level] = nullptr;\r
- }\r
- }\r
-\r
- image[0] = surface->getRenderTarget();\r
-\r
- mSurface = surface;\r
- mSurface->setBoundTexture(this);\r
-}\r
-\r
-void Texture3D::releaseTexImage()\r
-{\r
- for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)\r
- {\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- image[level] = nullptr;\r
- }\r
- }\r
-}\r
-\r
-void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)\r
-{\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Texture::setCompressedImage(imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)\r
-{\r
- Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)\r
-{\r
- Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);\r
-}\r
-\r
-void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source)\r
-{\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- if(image[level])\r
- {\r
- image[level]->release();\r
- }\r
-\r
- GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);\r
- image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);\r
-\r
- if(!image[level])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- if(width != 0 && height != 0 && depth != 0)\r
- {\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect(x, y, x + width, y + height, z);\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
- for(GLint sliceZ = 0; sliceZ < depth; ++sliceZ, ++sourceRect.slice)\r
- {\r
- copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, sliceZ, image[level]);\r
- }\r
- }\r
-\r
- renderTarget->release();\r
-}\r
-\r
-void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)\r
-{\r
- if(!image[level])\r
- {\r
- return error(GL_INVALID_OPERATION);\r
- }\r
-\r
- if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())\r
- {\r
- return error(GL_INVALID_VALUE);\r
- }\r
-\r
- egl::Image *renderTarget = source->getRenderTarget(0);\r
-\r
- if(!renderTarget)\r
- {\r
- ERR("Failed to retrieve the render target.");\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- Renderbuffer* renderbuffer = source->getReadColorbuffer();\r
-\r
- if(!renderbuffer)\r
- {\r
- ERR("Failed to retrieve the source colorbuffer.");\r
- return;\r
- }\r
-\r
- sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};\r
- sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());\r
-\r
- copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);\r
-\r
- renderTarget->release();\r
-}\r
-\r
-void Texture3D::setImage(egl::Image *sharedImage)\r
-{\r
- sharedImage->addRef();\r
-\r
- if(image[0])\r
- {\r
- image[0]->release();\r
- }\r
-\r
- image[0] = sharedImage;\r
-}\r
-\r
-// Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.\r
-bool Texture3D::isSamplerComplete() const\r
-{\r
- if(!image[0])\r
- {\r
- return false;\r
- }\r
-\r
- GLsizei width = image[0]->getWidth();\r
- GLsizei height = image[0]->getHeight();\r
- GLsizei depth = image[0]->getDepth();\r
-\r
- if(width <= 0 || height <= 0 || depth <= 0)\r
- {\r
- return false;\r
- }\r
-\r
- if(isMipmapFiltered())\r
- {\r
- if(!isMipmapComplete())\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-// Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.\r
-bool Texture3D::isMipmapComplete() const\r
-{\r
- GLsizei width = image[mBaseLevel]->getWidth();\r
- GLsizei height = image[mBaseLevel]->getHeight();\r
- GLsizei depth = image[mBaseLevel]->getDepth();\r
- bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;\r
-\r
- int q = isTexture2DArray ? std::min(log2(std::max(width, height)), mMaxLevel) :\r
- std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel);\r
-\r
- for(int level = mBaseLevel + 1; level <= q; level++)\r
- {\r
- if(!image[level])\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getFormat() != image[0]->getFormat())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getType() != image[0]->getType())\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getWidth() != std::max(1, width >> level))\r
- {\r
- return false;\r
- }\r
-\r
- if(image[level]->getHeight() != std::max(1, height >> level))\r
- {\r
- return false;\r
- }\r
-\r
- int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level);\r
- if(image[level]->getDepth() != levelDepth)\r
- {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
-}\r
-\r
-bool Texture3D::isCompressed(GLenum target, GLint level) const\r
-{\r
- return IsCompressed(getFormat(target, level), egl::getClientVersion());\r
-}\r
-\r
-bool Texture3D::isDepth(GLenum target, GLint level) const\r
-{\r
- return IsDepthTexture(getFormat(target, level));\r
-}\r
-\r
-void Texture3D::generateMipmaps()\r
-{\r
- if(!image[0])\r
- {\r
- return; // FIXME: error?\r
- }\r
-\r
- unsigned int q = log2(std::max(std::max(image[0]->getWidth(), image[0]->getHeight()), image[0]->getDepth()));\r
-\r
- for(unsigned int i = 1; i <= q; i++)\r
- {\r
- if(image[i])\r
- {\r
- image[i]->release();\r
- }\r
-\r
- image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), std::max(image[0]->getDepth() >> i, 1), image[0]->getFormat(), image[0]->getType());\r
-\r
- if(!image[i])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- getDevice()->stretchCube(image[i - 1], image[i]);\r
- }\r
-}\r
-\r
-egl::Image *Texture3D::getImage(unsigned int level)\r
-{\r
- return image[level];\r
-}\r
-\r
-Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level, GLint layer)\r
-{\r
- if(target != getTarget())\r
- {\r
- return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);\r
- }\r
-\r
- if(!mColorbufferProxy)\r
- {\r
- mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level, layer));\r
- }\r
- else\r
- {\r
- mColorbufferProxy->setLevel(level);\r
- mColorbufferProxy->setLayer(layer);\r
- }\r
-\r
- return mColorbufferProxy;\r
-}\r
-\r
-egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)\r
-{\r
- ASSERT(target == getTarget());\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- if(image[level])\r
- {\r
- image[level]->addRef();\r
- }\r
-\r
- return image[level];\r
-}\r
-\r
-bool Texture3D::isShared(GLenum target, unsigned int level) const\r
-{\r
- ASSERT(target == getTarget());\r
- ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);\r
-\r
- if(mSurface) // Bound to an EGLSurface\r
- {\r
- return true;\r
- }\r
-\r
- if(!image[level])\r
- {\r
- return false;\r
- }\r
-\r
- return image[level]->isShared();\r
-}\r
-\r
-Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)\r
-{\r
-}\r
-\r
-Texture2DArray::~Texture2DArray()\r
-{\r
-}\r
-\r
-GLenum Texture2DArray::getTarget() const\r
-{\r
- return GL_TEXTURE_2D_ARRAY;\r
-}\r
-\r
-void Texture2DArray::generateMipmaps()\r
-{\r
- int depth = image[0] ? image[0]->getDepth() : 0;\r
- if(!depth)\r
- {\r
- return; // FIXME: error?\r
- }\r
-\r
- unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));\r
-\r
- for(unsigned int i = 1; i <= q; i++)\r
- {\r
- if(image[i])\r
- {\r
- image[i]->release();\r
- }\r
-\r
- GLsizei w = std::max(image[0]->getWidth() >> i, 1);\r
- GLsizei h = std::max(image[0]->getHeight() >> i, 1);\r
- image[i] = new egl::Image(this, w, h, depth, image[0]->getFormat(), image[0]->getType());\r
-\r
- if(!image[i])\r
- {\r
- return error(GL_OUT_OF_MEMORY);\r
- }\r
-\r
- GLsizei srcw = image[i - 1]->getWidth();\r
- GLsizei srch = image[i - 1]->getHeight();\r
- for(int z = 0; z < depth; ++z)\r
- {\r
- sw::SliceRect srcRect(0, 0, srcw, srch, z);\r
- sw::SliceRect dstRect(0, 0, w, h, z);\r
- getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, true);\r
- }\r
- }\r
-}\r
-\r
-TextureExternal::TextureExternal(GLuint name) : Texture2D(name)\r
-{\r
- mMinFilter = GL_LINEAR;\r
- mMagFilter = GL_LINEAR;\r
- mWrapS = GL_CLAMP_TO_EDGE;\r
- mWrapT = GL_CLAMP_TO_EDGE;\r
- mWrapR = GL_CLAMP_TO_EDGE;\r
-}\r
-\r
-TextureExternal::~TextureExternal()\r
-{\r
-}\r
-\r
-GLenum TextureExternal::getTarget() const\r
-{\r
- return GL_TEXTURE_EXTERNAL_OES;\r
-}\r
-\r
-}\r
-\r
-egl::Image *createBackBuffer(int width, int height, const egl::Config *config)\r
-{\r
- if(config)\r
- {\r
- return new egl::Image(width, height, config->mRenderTargetFormat, config->mSamples, false);\r
- }\r
-\r
- return nullptr;\r
-}\r
-\r
-egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)\r
-{\r
- if(width == 0 || height == 0 || height > sw::OUTLINE_RESOLUTION)\r
- {\r
- ERR("Invalid parameters: %dx%d", width, height);\r
- return nullptr;\r
- }\r
-\r
- bool lockable = true;\r
-\r
- switch(format)\r
- {\r
-// case sw::FORMAT_D15S1:\r
- case sw::FORMAT_D24S8:\r
- case sw::FORMAT_D24X8:\r
-// case sw::FORMAT_D24X4S4:\r
- case sw::FORMAT_D24FS8:\r
- case sw::FORMAT_D32:\r
- case sw::FORMAT_D16:\r
- lockable = false;\r
- break;\r
-// case sw::FORMAT_S8_LOCKABLE:\r
-// case sw::FORMAT_D16_LOCKABLE:\r
- case sw::FORMAT_D32F_LOCKABLE:\r
-// case sw::FORMAT_D32_LOCKABLE:\r
- case sw::FORMAT_DF24S8:\r
- case sw::FORMAT_DF16S8:\r
- lockable = true;\r
- break;\r
- default:\r
- UNREACHABLE(format);\r
- }\r
-\r
- egl::Image *surface = new egl::Image(width, height, format, multiSampleDepth, lockable);\r
-\r
- if(!surface)\r
- {\r
- ERR("Out of memory");\r
- return nullptr;\r
- }\r
-\r
- return surface;\r
-}\r
+// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Texture.cpp: Implements the Texture class and its derived classes
+// Texture2D, TextureCubeMap, Texture3D and Texture2DArray. Implements GL texture objects
+// and related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
+
+#include "Texture.h"
+
+#include "main.h"
+#include "mathutil.h"
+#include "Framebuffer.h"
+#include "Device.hpp"
+#include "libEGL/Display.h"
+#include "libEGL/EGLSurface.h"
+#include "common/debug.h"
+
+#include <algorithm>
+
+namespace es2
+{
+
+Texture::Texture(GLuint name) : egl::Texture(name)
+{
+ mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
+ mMagFilter = GL_LINEAR;
+ mWrapS = GL_REPEAT;
+ mWrapT = GL_REPEAT;
+ mWrapR = GL_REPEAT;
+ mMaxAnisotropy = 1.0f;
+ mBaseLevel = 0;
+ mCompareFunc = GL_LEQUAL;
+ mCompareMode = GL_NONE;
+ mImmutableFormat = GL_FALSE;
+ mImmutableLevels = 0;
+ mMaxLevel = 1000;
+ mMaxLOD = 1000;
+ mMinLOD = -1000;
+ mSwizzleR = GL_RED;
+ mSwizzleG = GL_GREEN;
+ mSwizzleB = GL_BLUE;
+ mSwizzleA = GL_ALPHA;
+
+ resource = new sw::Resource(0);
+}
+
+Texture::~Texture()
+{
+ resource->destruct();
+}
+
+sw::Resource *Texture::getResource() const
+{
+ return resource;
+}
+
+// Returns true on successful filter state update (valid enum parameter)
+bool Texture::setMinFilter(GLenum filter)
+{
+ switch(filter)
+ {
+ case GL_NEAREST_MIPMAP_NEAREST:
+ case GL_LINEAR_MIPMAP_NEAREST:
+ case GL_NEAREST_MIPMAP_LINEAR:
+ case GL_LINEAR_MIPMAP_LINEAR:
+ if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+ {
+ return false;
+ }
+ // Fall through
+ case GL_NEAREST:
+ case GL_LINEAR:
+ mMinFilter = filter;
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true on successful filter state update (valid enum parameter)
+bool Texture::setMagFilter(GLenum filter)
+{
+ switch(filter)
+ {
+ case GL_NEAREST:
+ case GL_LINEAR:
+ mMagFilter = filter;
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true on successful wrap state update (valid enum parameter)
+bool Texture::setWrapS(GLenum wrap)
+{
+ switch(wrap)
+ {
+ case GL_REPEAT:
+ case GL_MIRRORED_REPEAT:
+ if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+ {
+ return false;
+ }
+ // Fall through
+ case GL_CLAMP_TO_EDGE:
+ mWrapS = wrap;
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true on successful wrap state update (valid enum parameter)
+bool Texture::setWrapT(GLenum wrap)
+{
+ switch(wrap)
+ {
+ case GL_REPEAT:
+ case GL_MIRRORED_REPEAT:
+ if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+ {
+ return false;
+ }
+ // Fall through
+ case GL_CLAMP_TO_EDGE:
+ mWrapT = wrap;
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true on successful wrap state update (valid enum parameter)
+bool Texture::setWrapR(GLenum wrap)
+{
+ switch(wrap)
+ {
+ case GL_REPEAT:
+ case GL_MIRRORED_REPEAT:
+ if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+ {
+ return false;
+ }
+ // Fall through
+ case GL_CLAMP_TO_EDGE:
+ mWrapR = wrap;
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true on successful max anisotropy update (valid anisotropy value)
+bool Texture::setMaxAnisotropy(float textureMaxAnisotropy)
+{
+ textureMaxAnisotropy = std::min(textureMaxAnisotropy, MAX_TEXTURE_MAX_ANISOTROPY);
+
+ if(textureMaxAnisotropy < 1.0f)
+ {
+ return false;
+ }
+
+ if(mMaxAnisotropy != textureMaxAnisotropy)
+ {
+ mMaxAnisotropy = textureMaxAnisotropy;
+ }
+
+ return true;
+}
+
+bool Texture::setBaseLevel(GLint baseLevel)
+{
+ mBaseLevel = baseLevel;
+ return true;
+}
+
+bool Texture::setCompareFunc(GLenum compareFunc)
+{
+ switch(compareFunc)
+ {
+ case GL_LEQUAL:
+ case GL_GEQUAL:
+ case GL_LESS:
+ case GL_GREATER:
+ case GL_EQUAL:
+ case GL_NOTEQUAL:
+ case GL_ALWAYS:
+ case GL_NEVER:
+ mCompareFunc = compareFunc;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Texture::setCompareMode(GLenum compareMode)
+{
+ switch(compareMode)
+ {
+ case GL_COMPARE_REF_TO_TEXTURE:
+ case GL_NONE:
+ mCompareMode = compareMode;
+ return true;
+ default:
+ return false;
+ }
+}
+
+void Texture::makeImmutable(GLsizei levels)
+{
+ mImmutableFormat = GL_TRUE;
+ mImmutableLevels = levels;
+}
+
+bool Texture::setMaxLevel(GLint maxLevel)
+{
+ mMaxLevel = maxLevel;
+ return true;
+}
+
+bool Texture::setMaxLOD(GLfloat maxLOD)
+{
+ mMaxLOD = maxLOD;
+ return true;
+}
+
+bool Texture::setMinLOD(GLfloat minLOD)
+{
+ mMinLOD = minLOD;
+ return true;
+}
+
+bool Texture::setSwizzleR(GLenum swizzleR)
+{
+ switch(swizzleR)
+ {
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_ZERO:
+ case GL_ONE:
+ mSwizzleR = swizzleR;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Texture::setSwizzleG(GLenum swizzleG)
+{
+ switch(swizzleG)
+ {
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_ZERO:
+ case GL_ONE:
+ mSwizzleG = swizzleG;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Texture::setSwizzleB(GLenum swizzleB)
+{
+ switch(swizzleB)
+ {
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_ZERO:
+ case GL_ONE:
+ mSwizzleB = swizzleB;
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool Texture::setSwizzleA(GLenum swizzleA)
+{
+ switch(swizzleA)
+ {
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_ZERO:
+ case GL_ONE:
+ mSwizzleA = swizzleA;
+ return true;
+ default:
+ return false;
+ }
+}
+
+GLenum Texture::getMinFilter() const
+{
+ return mMinFilter;
+}
+
+GLenum Texture::getMagFilter() const
+{
+ return mMagFilter;
+}
+
+GLenum Texture::getWrapS() const
+{
+ return mWrapS;
+}
+
+GLenum Texture::getWrapT() const
+{
+ return mWrapT;
+}
+
+GLenum Texture::getWrapR() const
+{
+ return mWrapR;
+}
+
+GLfloat Texture::getMaxAnisotropy() const
+{
+ return mMaxAnisotropy;
+}
+
+GLint Texture::getBaseLevel() const
+{
+ return mBaseLevel;
+}
+GLenum Texture::getCompareFunc() const
+{
+ return mCompareFunc;
+}
+GLenum Texture::getCompareMode() const
+{
+ return mCompareMode;
+}
+GLboolean Texture::getImmutableFormat() const
+{
+ return mImmutableFormat;
+}
+GLsizei Texture::getImmutableLevels() const
+{
+ return mImmutableLevels;
+}
+GLint Texture::getMaxLevel() const
+{
+ return mMaxLevel;
+}
+GLfloat Texture::getMaxLOD() const
+{
+ return mMaxLOD;
+}
+GLfloat Texture::getMinLOD() const
+{
+ return mMinLOD;
+}
+GLenum Texture::getSwizzleR() const
+{
+ return mSwizzleR;
+}
+GLenum Texture::getSwizzleG() const
+{
+ return mSwizzleG;
+}
+GLenum Texture::getSwizzleB() const
+{
+ return mSwizzleB;
+}
+GLenum Texture::getSwizzleA() const
+{
+ return mSwizzleA;
+}
+
+GLsizei Texture::getDepth(GLenum target, GLint level) const
+{
+ return 1;
+}
+
+egl::Image *Texture::createSharedImage(GLenum target, unsigned int level)
+{
+ egl::Image *image = getRenderTarget(target, level); // Increments reference count
+
+ if(image)
+ {
+ image->markShared();
+ }
+
+ return image;
+}
+
+void Texture::setImage(GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)
+{
+ if(pixels && image)
+ {
+ GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;
+ image->loadImageData(0, 0, 0, image->getWidth(), image->getHeight(), depth, format, type, unpackInfo, pixels);
+ }
+}
+
+void Texture::setCompressedImage(GLsizei imageSize, const void *pixels, egl::Image *image)
+{
+ if(pixels && image)
+ {
+ GLsizei depth = (getTarget() == GL_TEXTURE_3D_OES || getTarget() == GL_TEXTURE_2D_ARRAY) ? image->getDepth() : 1;
+ image->loadCompressedData(0, 0, 0, image->getWidth(), image->getHeight(), depth, imageSize, pixels);
+ }
+}
+
+void Texture::subImage(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels, egl::Image *image)
+{
+ if(!image)
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())
+ {
+ return error(GL_INVALID_VALUE);
+ }
+
+ if(IsCompressed(image->getFormat(), egl::getClientVersion()))
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(format != image->getFormat())
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(pixels)
+ {
+ image->loadImageData(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels);
+ }
+}
+
+void Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels, egl::Image *image)
+{
+ if(!image)
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())
+ {
+ return error(GL_INVALID_VALUE);
+ }
+
+ if(format != image->getFormat())
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(pixels)
+ {
+ image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);
+ }
+}
+
+bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLenum destFormat, GLint xoffset, GLint yoffset, GLint zoffset, egl::Image *dest)
+{
+ Device *device = getDevice();
+
+ sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);
+ bool success = device->stretchRect(source, &sourceRect, dest, &destRect, Device::ALL_BUFFERS);
+
+ if(!success)
+ {
+ return error(GL_OUT_OF_MEMORY, false);
+ }
+
+ return true;
+}
+
+bool Texture::isMipmapFiltered() const
+{
+ switch(mMinFilter)
+ {
+ case GL_NEAREST:
+ case GL_LINEAR:
+ return false;
+ case GL_NEAREST_MIPMAP_NEAREST:
+ case GL_LINEAR_MIPMAP_NEAREST:
+ case GL_NEAREST_MIPMAP_LINEAR:
+ case GL_LINEAR_MIPMAP_LINEAR:
+ return true;
+ default: UNREACHABLE(mMinFilter);
+ }
+
+ return false;
+}
+
+Texture2D::Texture2D(GLuint name) : Texture(name)
+{
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ image[i] = nullptr;
+ }
+
+ mSurface = nullptr;
+
+ mColorbufferProxy = nullptr;
+ mProxyRefs = 0;
+}
+
+Texture2D::~Texture2D()
+{
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[i])
+ {
+ image[i]->unbind(this);
+ image[i] = nullptr;
+ }
+ }
+
+ if(mSurface)
+ {
+ mSurface->setBoundTexture(nullptr);
+ mSurface = nullptr;
+ }
+
+ mColorbufferProxy = nullptr;
+}
+
+// We need to maintain a count of references to renderbuffers acting as
+// proxies for this texture, so that we do not attempt to use a pointer
+// to a renderbuffer proxy which has been deleted.
+void Texture2D::addProxyRef(const Renderbuffer *proxy)
+{
+ mProxyRefs++;
+}
+
+void Texture2D::releaseProxy(const Renderbuffer *proxy)
+{
+ if(mProxyRefs > 0)
+ {
+ mProxyRefs--;
+ }
+
+ if(mProxyRefs == 0)
+ {
+ mColorbufferProxy = nullptr;
+ }
+}
+
+void Texture2D::sweep()
+{
+ int imageCount = 0;
+
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[i] && image[i]->isChildOf(this))
+ {
+ if(!image[i]->hasSingleReference())
+ {
+ return;
+ }
+
+ imageCount++;
+ }
+ }
+
+ if(imageCount == referenceCount)
+ {
+ destroy();
+ }
+}
+
+GLenum Texture2D::getTarget() const
+{
+ return GL_TEXTURE_2D;
+}
+
+GLsizei Texture2D::getWidth(GLenum target, GLint level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ return image[level] ? image[level]->getWidth() : 0;
+}
+
+GLsizei Texture2D::getHeight(GLenum target, GLint level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ return image[level] ? image[level]->getHeight() : 0;
+}
+
+GLenum Texture2D::getFormat(GLenum target, GLint level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ return image[level] ? image[level]->getFormat() : GL_NONE;
+}
+
+GLenum Texture2D::getType(GLenum target, GLint level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ return image[level] ? image[level]->getType() : GL_NONE;
+}
+
+sw::Format Texture2D::getInternalFormat(GLenum target, GLint level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
+}
+
+int Texture2D::getLevelCount() const
+{
+ ASSERT(isSamplerComplete());
+ int levels = 0;
+
+ while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])
+ {
+ levels++;
+ }
+
+ return levels;
+}
+
+void Texture2D::setImage(GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ image[level] = new egl::Image(this, width, height, format, type);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setImage(format, type, unpackInfo, pixels, image[level]);
+}
+
+void Texture2D::bindTexImage(egl::Surface *surface)
+{
+ GLenum format;
+
+ switch(surface->getInternalFormat())
+ {
+ case sw::FORMAT_A8R8G8B8:
+ case sw::FORMAT_SRGB8_A8:
+ format = GL_BGRA_EXT;
+ break;
+ case sw::FORMAT_A8B8G8R8:
+ format = GL_RGBA;
+ break;
+ case sw::FORMAT_X8B8G8R8:
+ case sw::FORMAT_X8R8G8B8:
+ case sw::FORMAT_SRGB8_X8:
+ format = GL_RGB;
+ break;
+ default:
+ UNIMPLEMENTED();
+ return;
+ }
+
+ for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
+ {
+ if(image[level])
+ {
+ image[level]->release();
+ image[level] = nullptr;
+ }
+ }
+
+ image[0] = surface->getRenderTarget();
+
+ mSurface = surface;
+ mSurface->setBoundTexture(this);
+}
+
+void Texture2D::releaseTexImage()
+{
+ for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
+ {
+ if(image[level])
+ {
+ image[level]->release();
+ image[level] = nullptr;
+ }
+ }
+}
+
+void Texture2D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
+{
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setCompressedImage(imageSize, pixels, image[level]);
+}
+
+void Texture2D::subImage(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[level]);
+}
+
+void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
+{
+ Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[level]);
+}
+
+void Texture2D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
+{
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if(width != 0 && height != 0)
+ {
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+ copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[level]);
+ }
+
+ renderTarget->release();
+}
+
+void Texture2D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
+{
+ if(!image[level])
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset != 0)
+ {
+ return error(GL_INVALID_VALUE);
+ }
+
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+ copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
+
+ renderTarget->release();
+}
+
+void Texture2D::setSharedImage(egl::Image *sharedImage)
+{
+ if(sharedImage == image[0])
+ {
+ return;
+ }
+
+ sharedImage->addRef();
+
+ if(image[0])
+ {
+ image[0]->release();
+ }
+
+ image[0] = sharedImage;
+}
+
+// Tests for 2D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
+bool Texture2D::isSamplerComplete() const
+{
+ if(!image[0])
+ {
+ return false;
+ }
+
+ GLsizei width = image[0]->getWidth();
+ GLsizei height = image[0]->getHeight();
+
+ if(width <= 0 || height <= 0)
+ {
+ return false;
+ }
+
+ if(isMipmapFiltered())
+ {
+ if(!isMipmapComplete())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Tests for 2D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
+bool Texture2D::isMipmapComplete() const
+{
+ GLsizei width = image[mBaseLevel]->getWidth();
+ GLsizei height = image[mBaseLevel]->getHeight();
+
+ int q = std::min(log2(std::max(width, height)), mMaxLevel);
+
+ for(int level = mBaseLevel + 1; level <= q; level++)
+ {
+ if(!image[level])
+ {
+ return false;
+ }
+
+ if(image[level]->getFormat() != image[0]->getFormat())
+ {
+ return false;
+ }
+
+ if(image[level]->getType() != image[0]->getType())
+ {
+ return false;
+ }
+
+ if(image[level]->getWidth() != std::max(1, width >> level))
+ {
+ return false;
+ }
+
+ if(image[level]->getHeight() != std::max(1, height >> level))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Texture2D::isCompressed(GLenum target, GLint level) const
+{
+ return IsCompressed(getFormat(target, level), egl::getClientVersion());
+}
+
+bool Texture2D::isDepth(GLenum target, GLint level) const
+{
+ return IsDepthTexture(getFormat(target, level));
+}
+
+void Texture2D::generateMipmaps()
+{
+ if(!image[0])
+ {
+ return; // FIXME: error?
+ }
+
+ unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
+
+ for(unsigned int i = 1; i <= q; i++)
+ {
+ if(image[i])
+ {
+ image[i]->release();
+ }
+
+ image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());
+
+ if(!image[i])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ getDevice()->stretchRect(image[i - 1], 0, image[i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
+ }
+}
+
+egl::Image *Texture2D::getImage(unsigned int level)
+{
+ return image[level];
+}
+
+Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level, GLint layer)
+{
+ if(target != GL_TEXTURE_2D)
+ {
+ return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
+ }
+
+ if(!mColorbufferProxy)
+ {
+ mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));
+ }
+ else
+ {
+ mColorbufferProxy->setLevel(level);
+ }
+
+ return mColorbufferProxy;
+}
+
+egl::Image *Texture2D::getRenderTarget(GLenum target, unsigned int level)
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ if(image[level])
+ {
+ image[level]->addRef();
+ }
+
+ return image[level];
+}
+
+bool Texture2D::isShared(GLenum target, unsigned int level) const
+{
+ ASSERT(target == GL_TEXTURE_2D);
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ if(mSurface) // Bound to an EGLSurface
+ {
+ return true;
+ }
+
+ if(!image[level])
+ {
+ return false;
+ }
+
+ return image[level]->isShared();
+}
+
+TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
+{
+ for(int f = 0; f < 6; f++)
+ {
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ image[f][i] = nullptr;
+ }
+ }
+
+ for(int f = 0; f < 6; f++)
+ {
+ mFaceProxies[f] = nullptr;
+ mFaceProxyRefs[f] = 0;
+ }
+}
+
+TextureCubeMap::~TextureCubeMap()
+{
+ for(int f = 0; f < 6; f++)
+ {
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[f][i])
+ {
+ image[f][i]->unbind(this);
+ image[f][i] = nullptr;
+ }
+ }
+ }
+
+ for(int i = 0; i < 6; i++)
+ {
+ mFaceProxies[i] = nullptr;
+ }
+}
+
+// We need to maintain a count of references to renderbuffers acting as
+// proxies for this texture, so that the texture is not deleted while
+// proxy references still exist. If the reference count drops to zero,
+// we set our proxy pointer null, so that a new attempt at referencing
+// will cause recreation.
+void TextureCubeMap::addProxyRef(const Renderbuffer *proxy)
+{
+ for(int f = 0; f < 6; f++)
+ {
+ if(mFaceProxies[f] == proxy)
+ {
+ mFaceProxyRefs[f]++;
+ }
+ }
+}
+
+void TextureCubeMap::releaseProxy(const Renderbuffer *proxy)
+{
+ for(int f = 0; f < 6; f++)
+ {
+ if(mFaceProxies[f] == proxy)
+ {
+ if(mFaceProxyRefs[f] > 0)
+ {
+ mFaceProxyRefs[f]--;
+ }
+
+ if(mFaceProxyRefs[f] == 0)
+ {
+ mFaceProxies[f] = nullptr;
+ }
+ }
+ }
+}
+
+void TextureCubeMap::sweep()
+{
+ int imageCount = 0;
+
+ for(int f = 0; f < 6; f++)
+ {
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[f][i] && image[f][i]->isChildOf(this))
+ {
+ if(!image[f][i]->hasSingleReference())
+ {
+ return;
+ }
+
+ imageCount++;
+ }
+ }
+ }
+
+ if(imageCount == referenceCount)
+ {
+ destroy();
+ }
+}
+
+GLenum TextureCubeMap::getTarget() const
+{
+ return GL_TEXTURE_CUBE_MAP;
+}
+
+GLsizei TextureCubeMap::getWidth(GLenum target, GLint level) const
+{
+ int face = CubeFaceIndex(target);
+ return image[face][level] ? image[face][level]->getWidth() : 0;
+}
+
+GLsizei TextureCubeMap::getHeight(GLenum target, GLint level) const
+{
+ int face = CubeFaceIndex(target);
+ return image[face][level] ? image[face][level]->getHeight() : 0;
+}
+
+GLenum TextureCubeMap::getFormat(GLenum target, GLint level) const
+{
+ int face = CubeFaceIndex(target);
+ return image[face][level] ? image[face][level]->getFormat() : 0;
+}
+
+GLenum TextureCubeMap::getType(GLenum target, GLint level) const
+{
+ int face = CubeFaceIndex(target);
+ return image[face][level] ? image[face][level]->getType() : 0;
+}
+
+sw::Format TextureCubeMap::getInternalFormat(GLenum target, GLint level) const
+{
+ int face = CubeFaceIndex(target);
+ return image[face][level] ? image[face][level]->getInternalFormat() : sw::FORMAT_NULL;
+}
+
+int TextureCubeMap::getLevelCount() const
+{
+ ASSERT(isSamplerComplete());
+ int levels = 0;
+
+ while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[0][levels])
+ {
+ levels++;
+ }
+
+ return levels;
+}
+
+void TextureCubeMap::setCompressedImage(GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
+{
+ int face = CubeFaceIndex(target);
+
+ if(image[face][level])
+ {
+ image[face][level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[face][level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setCompressedImage(imageSize, pixels, image[face][level]);
+}
+
+void TextureCubeMap::subImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ Texture::subImage(xoffset, yoffset, 0, width, height, 1, format, type, unpackInfo, pixels, image[CubeFaceIndex(target)][level]);
+}
+
+void TextureCubeMap::subImageCompressed(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
+{
+ Texture::subImageCompressed(xoffset, yoffset, 0, width, height, 1, format, imageSize, pixels, image[CubeFaceIndex(target)][level]);
+}
+
+// Tests for cube map sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 86.
+bool TextureCubeMap::isSamplerComplete() const
+{
+ for(int face = 0; face < 6; face++)
+ {
+ if(!image[face][0])
+ {
+ return false;
+ }
+ }
+
+ int size = image[0][0]->getWidth();
+
+ if(size <= 0)
+ {
+ return false;
+ }
+
+ if(!isMipmapFiltered())
+ {
+ if(!isCubeComplete())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if(!isMipmapCubeComplete()) // Also tests for isCubeComplete()
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Tests for cube texture completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
+bool TextureCubeMap::isCubeComplete() const
+{
+ if(image[0][mBaseLevel]->getWidth() <= 0 || image[0][mBaseLevel]->getHeight() != image[0][mBaseLevel]->getWidth())
+ {
+ return false;
+ }
+
+ for(unsigned int face = 1; face < 6; face++)
+ {
+ if(image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getWidth() ||
+ image[face][mBaseLevel]->getWidth() != image[0][mBaseLevel]->getHeight() ||
+ image[face][mBaseLevel]->getFormat() != image[0][mBaseLevel]->getFormat() ||
+ image[face][mBaseLevel]->getType() != image[0][mBaseLevel]->getType())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TextureCubeMap::isMipmapCubeComplete() const
+{
+ if(!isCubeComplete())
+ {
+ return false;
+ }
+
+ GLsizei size = image[0][mBaseLevel]->getWidth();
+ int q = std::min(log2(size), mMaxLevel);
+
+ for(int face = 0; face < 6; face++)
+ {
+ for(int level = mBaseLevel + 1; level <= q; level++)
+ {
+ if(!image[face][level])
+ {
+ return false;
+ }
+
+ if(image[face][level]->getFormat() != image[0][mBaseLevel]->getFormat())
+ {
+ return false;
+ }
+
+ if(image[face][level]->getType() != image[0][mBaseLevel]->getType())
+ {
+ return false;
+ }
+
+ if(image[face][level]->getWidth() != std::max(1, size >> level))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool TextureCubeMap::isCompressed(GLenum target, GLint level) const
+{
+ return IsCompressed(getFormat(target, level), egl::getClientVersion());
+}
+
+bool TextureCubeMap::isDepth(GLenum target, GLint level) const
+{
+ return IsDepthTexture(getFormat(target, level));
+}
+
+void TextureCubeMap::releaseTexImage()
+{
+ UNREACHABLE(0); // Cube maps cannot have an EGL surface bound as an image
+}
+
+void TextureCubeMap::setImage(GLenum target, GLint level, GLsizei width, GLsizei height, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ int face = CubeFaceIndex(target);
+
+ if(image[face][level])
+ {
+ image[face][level]->release();
+ }
+
+ image[face][level] = new egl::Image(this, width, height, format, type);
+
+ if(!image[face][level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setImage(format, type, unpackInfo, pixels, image[face][level]);
+}
+
+void TextureCubeMap::copyImage(GLenum target, GLint level, GLenum format, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
+{
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ int face = CubeFaceIndex(target);
+
+ if(image[face][level])
+ {
+ image[face][level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[face][level] = new egl::Image(this, width, height, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[face][level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if(width != 0 && height != 0)
+ {
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+ copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, 0, image[face][level]);
+ }
+
+ renderTarget->release();
+}
+
+egl::Image *TextureCubeMap::getImage(int face, unsigned int level)
+{
+ return image[face][level];
+}
+
+egl::Image *TextureCubeMap::getImage(GLenum face, unsigned int level)
+{
+ return image[CubeFaceIndex(face)][level];
+}
+
+void TextureCubeMap::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
+{
+ int face = CubeFaceIndex(target);
+
+ if(!image[face][level])
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ GLsizei size = image[face][level]->getWidth();
+
+ if(xoffset + width > size || yoffset + height > size || zoffset != 0)
+ {
+ return error(GL_INVALID_VALUE);
+ }
+
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+ copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);
+
+ renderTarget->release();
+}
+
+void TextureCubeMap::generateMipmaps()
+{
+ if(!isCubeComplete())
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ unsigned int q = log2(image[0][0]->getWidth());
+
+ for(unsigned int f = 0; f < 6; f++)
+ {
+ for(unsigned int i = 1; i <= q; i++)
+ {
+ if(image[f][i])
+ {
+ image[f][i]->release();
+ }
+
+ image[f][i] = new egl::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());
+
+ if(!image[f][i])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ getDevice()->stretchRect(image[f][i - 1], 0, image[f][i], 0, Device::ALL_BUFFERS | Device::USE_FILTER);
+ }
+ }
+}
+
+Renderbuffer *TextureCubeMap::getRenderbuffer(GLenum target, GLint level, GLint layer)
+{
+ if(!IsCubemapTextureTarget(target))
+ {
+ return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
+ }
+
+ int face = CubeFaceIndex(target);
+
+ if(!mFaceProxies[face])
+ {
+ mFaceProxies[face] = new Renderbuffer(name, new RenderbufferTextureCubeMap(this, target, level));
+ }
+ else
+ {
+ mFaceProxies[face]->setLevel(level);
+ }
+
+ return mFaceProxies[face];
+}
+
+egl::Image *TextureCubeMap::getRenderTarget(GLenum target, unsigned int level)
+{
+ ASSERT(IsCubemapTextureTarget(target));
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ int face = CubeFaceIndex(target);
+
+ if(image[face][level])
+ {
+ image[face][level]->addRef();
+ }
+
+ return image[face][level];
+}
+
+bool TextureCubeMap::isShared(GLenum target, unsigned int level) const
+{
+ ASSERT(IsCubemapTextureTarget(target));
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ int face = CubeFaceIndex(target);
+
+ if(!image[face][level])
+ {
+ return false;
+ }
+
+ return image[face][level]->isShared();
+}
+
+Texture3D::Texture3D(GLuint name) : Texture(name)
+{
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ image[i] = nullptr;
+ }
+
+ mSurface = nullptr;
+
+ mColorbufferProxy = nullptr;
+ mProxyRefs = 0;
+}
+
+Texture3D::~Texture3D()
+{
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[i])
+ {
+ image[i]->unbind(this);
+ image[i] = nullptr;
+ }
+ }
+
+ if(mSurface)
+ {
+ mSurface->setBoundTexture(nullptr);
+ mSurface = nullptr;
+ }
+
+ mColorbufferProxy = nullptr;
+}
+
+// We need to maintain a count of references to renderbuffers acting as
+// proxies for this texture, so that we do not attempt to use a pointer
+// to a renderbuffer proxy which has been deleted.
+void Texture3D::addProxyRef(const Renderbuffer *proxy)
+{
+ mProxyRefs++;
+}
+
+void Texture3D::releaseProxy(const Renderbuffer *proxy)
+{
+ if(mProxyRefs > 0)
+ {
+ mProxyRefs--;
+ }
+
+ if(mProxyRefs == 0)
+ {
+ mColorbufferProxy = nullptr;
+ }
+}
+
+void Texture3D::sweep()
+{
+ int imageCount = 0;
+
+ for(int i = 0; i < IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++)
+ {
+ if(image[i] && image[i]->isChildOf(this))
+ {
+ if(!image[i]->hasSingleReference())
+ {
+ return;
+ }
+
+ imageCount++;
+ }
+ }
+
+ if(imageCount == referenceCount)
+ {
+ destroy();
+ }
+}
+
+GLenum Texture3D::getTarget() const
+{
+ return GL_TEXTURE_3D_OES;
+}
+
+GLsizei Texture3D::getWidth(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getWidth() : 0;
+}
+
+GLsizei Texture3D::getHeight(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getHeight() : 0;
+}
+
+GLsizei Texture3D::getDepth(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getDepth() : 0;
+}
+
+GLenum Texture3D::getFormat(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getFormat() : GL_NONE;
+}
+
+GLenum Texture3D::getType(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getType() : GL_NONE;
+}
+
+sw::Format Texture3D::getInternalFormat(GLenum target, GLint level) const
+{
+ ASSERT(target == getTarget());
+ return image[level] ? image[level]->getInternalFormat() : sw::FORMAT_NULL;
+}
+
+int Texture3D::getLevelCount() const
+{
+ ASSERT(isSamplerComplete());
+ int levels = 0;
+
+ while(levels < IMPLEMENTATION_MAX_TEXTURE_LEVELS && image[levels])
+ {
+ levels++;
+ }
+
+ return levels;
+}
+
+void Texture3D::setImage(GLint level, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ image[level] = new egl::Image(this, width, height, depth, format, type);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setImage(format, type, unpackInfo, pixels, image[level]);
+}
+
+void Texture3D::bindTexImage(egl::Surface *surface)
+{
+ GLenum format;
+
+ switch(surface->getInternalFormat())
+ {
+ case sw::FORMAT_A8R8G8B8:
+ format = GL_RGBA;
+ break;
+ case sw::FORMAT_X8R8G8B8:
+ format = GL_RGB;
+ break;
+ default:
+ UNIMPLEMENTED();
+ return;
+ }
+
+ for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
+ {
+ if(image[level])
+ {
+ image[level]->release();
+ image[level] = nullptr;
+ }
+ }
+
+ image[0] = surface->getRenderTarget();
+
+ mSurface = surface;
+ mSurface->setBoundTexture(this);
+}
+
+void Texture3D::releaseTexImage()
+{
+ for(int level = 0; level < IMPLEMENTATION_MAX_TEXTURE_LEVELS; level++)
+ {
+ if(image[level])
+ {
+ image[level]->release();
+ image[level] = nullptr;
+ }
+ }
+}
+
+void Texture3D::setCompressedImage(GLint level, GLenum format, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels)
+{
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Texture::setCompressedImage(imageSize, pixels, image[level]);
+}
+
+void Texture3D::subImage(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const egl::Image::UnpackInfo& unpackInfo, const void *pixels)
+{
+ Texture::subImage(xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels, image[level]);
+}
+
+void Texture3D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *pixels)
+{
+ Texture::subImageCompressed(xoffset, yoffset, zoffset, width, height, depth, format, imageSize, pixels, image[level]);
+}
+
+void Texture3D::copyImage(GLint level, GLenum format, GLint x, GLint y, GLint z, GLsizei width, GLsizei height, GLsizei depth, Framebuffer *source)
+{
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if(image[level])
+ {
+ image[level]->release();
+ }
+
+ GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);
+ image[level] = new egl::Image(this, width, height, depth, sizedInternalFormat, GL_UNSIGNED_BYTE);
+
+ if(!image[level])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ if(width != 0 && height != 0 && depth != 0)
+ {
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect(x, y, x + width, y + height, z);
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+ for(GLint sliceZ = 0; sliceZ < depth; ++sliceZ, ++sourceRect.slice)
+ {
+ copy(renderTarget, sourceRect, sizedInternalFormat, 0, 0, sliceZ, image[level]);
+ }
+ }
+
+ renderTarget->release();
+}
+
+void Texture3D::copySubImage(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, Framebuffer *source)
+{
+ if(!image[level])
+ {
+ return error(GL_INVALID_OPERATION);
+ }
+
+ if(xoffset + width > image[level]->getWidth() || yoffset + height > image[level]->getHeight() || zoffset >= image[level]->getDepth())
+ {
+ return error(GL_INVALID_VALUE);
+ }
+
+ egl::Image *renderTarget = source->getRenderTarget(0);
+
+ if(!renderTarget)
+ {
+ ERR("Failed to retrieve the render target.");
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+ if(!renderbuffer)
+ {
+ ERR("Failed to retrieve the source colorbuffer.");
+ return;
+ }
+
+ sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};
+ sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+ copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
+
+ renderTarget->release();
+}
+
+void Texture3D::setSharedImage(egl::Image *sharedImage)
+{
+ sharedImage->addRef();
+
+ if(image[0])
+ {
+ image[0]->release();
+ }
+
+ image[0] = sharedImage;
+}
+
+// Tests for 3D texture sampling completeness. [OpenGL ES 2.0.24] section 3.8.2 page 85.
+bool Texture3D::isSamplerComplete() const
+{
+ if(!image[0])
+ {
+ return false;
+ }
+
+ GLsizei width = image[0]->getWidth();
+ GLsizei height = image[0]->getHeight();
+ GLsizei depth = image[0]->getDepth();
+
+ if(width <= 0 || height <= 0 || depth <= 0)
+ {
+ return false;
+ }
+
+ if(isMipmapFiltered())
+ {
+ if(!isMipmapComplete())
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Tests for 3D texture (mipmap) completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
+bool Texture3D::isMipmapComplete() const
+{
+ GLsizei width = image[mBaseLevel]->getWidth();
+ GLsizei height = image[mBaseLevel]->getHeight();
+ GLsizei depth = image[mBaseLevel]->getDepth();
+ bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;
+
+ int q = isTexture2DArray ? std::min(log2(std::max(width, height)), mMaxLevel) :
+ std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel);
+
+ for(int level = mBaseLevel + 1; level <= q; level++)
+ {
+ if(!image[level])
+ {
+ return false;
+ }
+
+ if(image[level]->getFormat() != image[0]->getFormat())
+ {
+ return false;
+ }
+
+ if(image[level]->getType() != image[0]->getType())
+ {
+ return false;
+ }
+
+ if(image[level]->getWidth() != std::max(1, width >> level))
+ {
+ return false;
+ }
+
+ if(image[level]->getHeight() != std::max(1, height >> level))
+ {
+ return false;
+ }
+
+ int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level);
+ if(image[level]->getDepth() != levelDepth)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Texture3D::isCompressed(GLenum target, GLint level) const
+{
+ return IsCompressed(getFormat(target, level), egl::getClientVersion());
+}
+
+bool Texture3D::isDepth(GLenum target, GLint level) const
+{
+ return IsDepthTexture(getFormat(target, level));
+}
+
+void Texture3D::generateMipmaps()
+{
+ if(!image[0])
+ {
+ return; // FIXME: error?
+ }
+
+ unsigned int q = log2(std::max(std::max(image[0]->getWidth(), image[0]->getHeight()), image[0]->getDepth()));
+
+ for(unsigned int i = 1; i <= q; i++)
+ {
+ if(image[i])
+ {
+ image[i]->release();
+ }
+
+ image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), std::max(image[0]->getDepth() >> i, 1), image[0]->getFormat(), image[0]->getType());
+
+ if(!image[i])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ getDevice()->stretchCube(image[i - 1], image[i]);
+ }
+}
+
+egl::Image *Texture3D::getImage(unsigned int level)
+{
+ return image[level];
+}
+
+Renderbuffer *Texture3D::getRenderbuffer(GLenum target, GLint level, GLint layer)
+{
+ if(target != getTarget())
+ {
+ return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
+ }
+
+ if(!mColorbufferProxy)
+ {
+ mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture3D(this, level, layer));
+ }
+ else
+ {
+ mColorbufferProxy->setLevel(level);
+ mColorbufferProxy->setLayer(layer);
+ }
+
+ return mColorbufferProxy;
+}
+
+egl::Image *Texture3D::getRenderTarget(GLenum target, unsigned int level)
+{
+ ASSERT(target == getTarget());
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ if(image[level])
+ {
+ image[level]->addRef();
+ }
+
+ return image[level];
+}
+
+bool Texture3D::isShared(GLenum target, unsigned int level) const
+{
+ ASSERT(target == getTarget());
+ ASSERT(level < IMPLEMENTATION_MAX_TEXTURE_LEVELS);
+
+ if(mSurface) // Bound to an EGLSurface
+ {
+ return true;
+ }
+
+ if(!image[level])
+ {
+ return false;
+ }
+
+ return image[level]->isShared();
+}
+
+Texture2DArray::Texture2DArray(GLuint name) : Texture3D(name)
+{
+}
+
+Texture2DArray::~Texture2DArray()
+{
+}
+
+GLenum Texture2DArray::getTarget() const
+{
+ return GL_TEXTURE_2D_ARRAY;
+}
+
+void Texture2DArray::generateMipmaps()
+{
+ int depth = image[0] ? image[0]->getDepth() : 0;
+ if(!depth)
+ {
+ return; // FIXME: error?
+ }
+
+ unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));
+
+ for(unsigned int i = 1; i <= q; i++)
+ {
+ if(image[i])
+ {
+ image[i]->release();
+ }
+
+ GLsizei w = std::max(image[0]->getWidth() >> i, 1);
+ GLsizei h = std::max(image[0]->getHeight() >> i, 1);
+ image[i] = new egl::Image(this, w, h, depth, image[0]->getFormat(), image[0]->getType());
+
+ if(!image[i])
+ {
+ return error(GL_OUT_OF_MEMORY);
+ }
+
+ GLsizei srcw = image[i - 1]->getWidth();
+ GLsizei srch = image[i - 1]->getHeight();
+ for(int z = 0; z < depth; ++z)
+ {
+ sw::SliceRect srcRect(0, 0, srcw, srch, z);
+ sw::SliceRect dstRect(0, 0, w, h, z);
+ getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER);
+ }
+ }
+}
+
+TextureExternal::TextureExternal(GLuint name) : Texture2D(name)
+{
+ mMinFilter = GL_LINEAR;
+ mMagFilter = GL_LINEAR;
+ mWrapS = GL_CLAMP_TO_EDGE;
+ mWrapT = GL_CLAMP_TO_EDGE;
+ mWrapR = GL_CLAMP_TO_EDGE;
+}
+
+TextureExternal::~TextureExternal()
+{
+}
+
+GLenum TextureExternal::getTarget() const
+{
+ return GL_TEXTURE_EXTERNAL_OES;
+}
+
+}
+
+egl::Image *createBackBuffer(int width, int height, const egl::Config *config)
+{
+ if(config)
+ {
+ return new egl::Image(width, height, config->mRenderTargetFormat, config->mSamples, false);
+ }
+
+ return nullptr;
+}
+
+egl::Image *createDepthStencil(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)
+{
+ if(width == 0 || height == 0 || height > sw::OUTLINE_RESOLUTION)
+ {
+ ERR("Invalid parameters: %dx%d", width, height);
+ return nullptr;
+ }
+
+ bool lockable = true;
+
+ switch(format)
+ {
+// case sw::FORMAT_D15S1:
+ case sw::FORMAT_D24S8:
+ case sw::FORMAT_D24X8:
+// case sw::FORMAT_D24X4S4:
+ case sw::FORMAT_D24FS8:
+ case sw::FORMAT_D32:
+ case sw::FORMAT_D16:
+ lockable = false;
+ break;
+// case sw::FORMAT_S8_LOCKABLE:
+// case sw::FORMAT_D16_LOCKABLE:
+ case sw::FORMAT_D32F_LOCKABLE:
+// case sw::FORMAT_D32_LOCKABLE:
+ case sw::FORMAT_DF24S8:
+ case sw::FORMAT_DF16S8:
+ lockable = true;
+ break;
+ default:
+ UNREACHABLE(format);
+ }
+
+ egl::Image *surface = new egl::Image(width, height, format, multiSampleDepth, lockable);
+
+ if(!surface)
+ {
+ ERR("Out of memory");
+ return nullptr;
+ }
+
+ return surface;
+}