#include "TextureGL.h" #include "base/ccMacros.h" #include "base/CCEventListenerCustom.h" #include "base/CCEventDispatcher.h" #include "base/CCEventType.h" #include "base/CCDirector.h" #include "platform/CCPlatformConfig.h" #include "renderer/backend/opengl/UtilsGL.h" CC_BACKEND_BEGIN #define ISPOW2(n) (((n) & (n-1)) == 0) void TextureInfoGL::applySamplerDescriptor(const SamplerDescriptor& descriptor, bool isPow2) { if (descriptor.magFilter != SamplerFilter::DONT_CARE) { magFilterGL = UtilsGL::toGLMagFilter(descriptor.magFilter); } if (descriptor.minFilter != SamplerFilter::DONT_CARE) { minFilterGL = UtilsGL::toGLMinFilter(descriptor.minFilter, descriptor.mipmapFilter, descriptor.mipmapEnabled, isPow2); } if (descriptor.sAddressMode != SamplerAddressMode::DONT_CARE) { sAddressModeGL = UtilsGL::toGLAddressMode(descriptor.sAddressMode, isPow2); } if (descriptor.tAddressMode != SamplerAddressMode::DONT_CARE) { tAddressModeGL = UtilsGL::toGLAddressMode(descriptor.tAddressMode, isPow2); } } Texture2DGL::Texture2DGL(const TextureDescriptor& descriptor) : Texture2D(descriptor) { glGenTextures(1, &_textureInfo.texture); UtilsGL::toGLTypes(_textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed); bool isPow2 = ISPOW2(_width) && ISPOW2(_height); _textureInfo.magFilterGL = UtilsGL::toGLMagFilter(descriptor.samplerDescriptor.magFilter); _textureInfo.minFilterGL = UtilsGL::toGLMinFilter(descriptor.samplerDescriptor.minFilter, descriptor.samplerDescriptor.mipmapFilter, _isMipmapEnabled, isPow2); _textureInfo.sAddressModeGL = UtilsGL::toGLAddressMode(descriptor.samplerDescriptor.sAddressMode, isPow2); _textureInfo.tAddressModeGL = UtilsGL::toGLAddressMode(descriptor.samplerDescriptor.tAddressMode, isPow2); // Update data here because `updateData()` may not be invoked later. // For example, a texture used as depth buffer will not invoke updateData(). initWithZeros(); #if CC_ENABLE_CACHE_TEXTURE_DATA // Listen this event to restored texture id after coming to foreground on Android. _backToForegroundListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom*){ glGenTextures(1, &(this->_textureInfo.texture)); this->initWithZeros(); }); Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1); #endif } void Texture2DGL::initWithZeros() { auto size = _width * _height * _bitsPerElement / 8; uint8_t* data = (uint8_t*)malloc(size); memset(data, 0, size); updateData(data); free(data); } void Texture2DGL::updateTextureDescriptor(const cocos2d::backend::TextureDescriptor &descriptor) { Texture::updateTextureDescriptor(descriptor); UtilsGL::toGLTypes(descriptor.textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed); _width = descriptor.width; _height = descriptor.height; updateSamplerDescriptor(descriptor.samplerDescriptor); initWithZeros(); } Texture2DGL::~Texture2DGL() { if (_textureInfo.texture) glDeleteTextures(1, &_textureInfo.texture); _textureInfo.texture = 0; #if CC_ENABLE_CACHE_TEXTURE_DATA Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener); #endif } void Texture2DGL::updateSamplerDescriptor(const SamplerDescriptor &sampler) { bool isPow2 = ISPOW2(_width) && ISPOW2(_height); _textureInfo.applySamplerDescriptor(sampler, isPow2); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _textureInfo.texture); if (sampler.magFilter != SamplerFilter::DONT_CARE) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL); } if (sampler.minFilter != SamplerFilter::DONT_CARE) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL); } if (sampler.sAddressMode != SamplerAddressMode::DONT_CARE) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL); } if (sampler.tAddressMode != SamplerAddressMode::DONT_CARE) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL); } } void Texture2DGL::updateData(uint8_t* data) { // TODO: support texture cube, and compressed data. //Set the row align only when mipmapsNum == 1 and the data is uncompressed if(!_isMipmapEnabled && !_isCompressed) { unsigned int bytesPerRow = _width * _bitsPerElement / 8; if(bytesPerRow % 8 == 0) { glPixelStorei(GL_UNPACK_ALIGNMENT, 8); } else if(bytesPerRow % 4 == 0) { glPixelStorei(GL_UNPACK_ALIGNMENT, 4); } else if(bytesPerRow % 2 == 0) { glPixelStorei(GL_UNPACK_ALIGNMENT, 2); } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _textureInfo.texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL); if(_isCompressed) { auto datalen = _width * _height * _bitsPerElement / 8; glCompressedTexImage2D(GL_TEXTURE_2D, 0, _textureInfo.internalFormat, (GLsizei)_width, (GLsizei)_height, 0, datalen, data); } else { glTexImage2D(GL_TEXTURE_2D, 0, _textureInfo.internalFormat, _width, _height, 0, _textureInfo.format, _textureInfo.type, data); } CHECK_GL_ERROR_DEBUG(); if(_isMipmapEnabled) { _isMipmapGenerated = false; generateMipmaps(); } CHECK_GL_ERROR_DEBUG(); } void Texture2DGL::updateSubData(unsigned int xoffset, unsigned int yoffset, unsigned int width, unsigned int height, uint8_t* data) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, _textureInfo.texture); glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, width, height, _textureInfo.format, _textureInfo.type, data); CHECK_GL_ERROR_DEBUG(); if(_isMipmapEnabled) { _isMipmapGenerated = false; generateMipmaps(); } CHECK_GL_ERROR_DEBUG(); } void Texture2DGL::apply(int index) const { glActiveTexture(GL_TEXTURE0 + index); glBindTexture(GL_TEXTURE_2D, _textureInfo.texture); } void Texture2DGL::generateMipmaps() { if (TextureUsage::RENDER_TARGET == _textureUsage) return; if(!_isMipmapGenerated) { _isMipmapGenerated = true; glGenerateMipmap(GL_TEXTURE_2D); } } void Texture2DGL::getBytes(int x, int y, int width, int height, bool flipImage, std::function callback) { GLint defaultFBO = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); GLuint frameBuffer = 0; glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _textureInfo.texture, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); auto bytePerRow = width * _bitsPerElement / 8; unsigned char* image = new unsigned char[bytePerRow * height]; glReadPixels(x,y,width, height,GL_RGBA,GL_UNSIGNED_BYTE, image); if(flipImage) { unsigned char* flippedImage = new unsigned char[bytePerRow * height]; for (int i = 0; i < height; ++i) { memcpy(&flippedImage[i * bytePerRow], &image[(height - i - 1) * bytePerRow], bytePerRow); } callback(flippedImage, width, height); CC_SAFE_DELETE_ARRAY(flippedImage); } else { callback(image, width, height); CC_SAFE_DELETE_ARRAY(image); } glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glDeleteFramebuffers(1, &frameBuffer); } TextureCubeGL::TextureCubeGL(const TextureDescriptor& descriptor) :TextureCubemap(descriptor) { assert(descriptor.width == descriptor.height); _size = descriptor.width; _textureType = TextureType::TEXTURE_CUBE; UtilsGL::toGLTypes(_textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed); glGenTextures(1, &_textureInfo.texture); updateSamplerDescriptor(descriptor.samplerDescriptor); #if CC_ENABLE_CACHE_TEXTURE_DATA // Listen this event to restored texture id after coming to foreground on Android. _backToForegroundListener = EventListenerCustom::create(EVENT_COME_TO_FOREGROUND, [this](EventCustom*){ glGenTextures(1, &(this->_textureInfo.texture)); this->setTexParameters(); }); Director::getInstance()->getEventDispatcher()->addEventListenerWithFixedPriority(_backToForegroundListener, -1); #endif CHECK_GL_ERROR_DEBUG(); } void TextureCubeGL::setTexParameters() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, _textureInfo.minFilterGL); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, _textureInfo.magFilterGL); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, _textureInfo.sAddressModeGL); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, _textureInfo.tAddressModeGL); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } void TextureCubeGL::updateTextureDescriptor(const cocos2d::backend::TextureDescriptor &descriptor) { UtilsGL::toGLTypes(descriptor.textureFormat, _textureInfo.internalFormat, _textureInfo.format, _textureInfo.type, _isCompressed); _textureFormat = descriptor.textureFormat; _size = descriptor.width; updateSamplerDescriptor(descriptor.samplerDescriptor); } TextureCubeGL::~TextureCubeGL() { if(_textureInfo.texture) glDeleteTextures(1, &_textureInfo.texture); _textureInfo.texture = 0; #if CC_ENABLE_CACHE_TEXTURE_DATA Director::getInstance()->getEventDispatcher()->removeEventListener(_backToForegroundListener); #endif } void TextureCubeGL::updateSamplerDescriptor(const SamplerDescriptor &sampler) { _textureInfo.applySamplerDescriptor(sampler, true); setTexParameters(); } void TextureCubeGL::apply(int index) const { glActiveTexture(GL_TEXTURE0+ index); glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture); CHECK_GL_ERROR_DEBUG(); } void TextureCubeGL::updateFaceData(TextureCubeFace side, void *data) { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, _textureInfo.texture); CHECK_GL_ERROR_DEBUG(); int i = static_cast(side); glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, // level GL_RGBA, // internal format _size, // width _size, // height 0, // border _textureInfo.internalFormat, // format _textureInfo.type, // type data); // pixel data if(_isMipmapEnabled) { _isMipmapGenerated = false; generateMipmaps(); } CHECK_GL_ERROR_DEBUG(); glBindTexture(GL_TEXTURE_CUBE_MAP, 0); } void TextureCubeGL::getBytes(int x, int y, int width, int height, bool flipImage, std::function callback) { GLint defaultFBO = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); GLuint frameBuffer = 0; glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP, _textureInfo.texture, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); auto bytePerRow = width * _bitsPerElement / 8; unsigned char* image = new unsigned char[bytePerRow * height]; glReadPixels(x,y,width, height,GL_RGBA,GL_UNSIGNED_BYTE, image); if(flipImage) { unsigned char* flippedImage = new unsigned char[bytePerRow * height]; for (int i = 0; i < height; ++i) { memcpy(&flippedImage[i * bytePerRow], &image[(height - i - 1) * bytePerRow], bytePerRow); } callback(flippedImage, width, height); CC_SAFE_DELETE_ARRAY(flippedImage); } else { callback(image, width, height); CC_SAFE_DELETE_ARRAY(image); } glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); glDeleteFramebuffers(1, &frameBuffer); } void TextureCubeGL::generateMipmaps() { if (TextureUsage::RENDER_TARGET == _textureUsage) return; if(!_isMipmapGenerated) { _isMipmapGenerated = true; glGenerateMipmap(GL_TEXTURE_CUBE_MAP); } } CC_BACKEND_END