From 0c552dd8fb03aa5acb6e8a346464440e01d20e97 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Fri, 7 Jan 2022 18:54:52 +0100 Subject: [PATCH] Added a GLSL shader to convert the color model BGRA to RGBA. Also added support for specifying a separate format as compared to internalFormat when creating textures, although the shader should be used primarily as this is not really supported by the OpenGL standard. --- es-core/src/renderers/Renderer.cpp | 1 + es-core/src/renderers/Renderer.h | 3 +++ es-core/src/renderers/Renderer_GL21.cpp | 26 ++++++++++++++---- es-core/src/renderers/Renderer_GLES10.cpp | 17 ++++++++---- es-core/src/resources/Font.cpp | 4 +-- es-core/src/resources/TextureData.cpp | 4 +-- es-core/src/resources/TextureData.h | 4 +++ es-core/src/resources/TextureResource.h | 2 ++ resources/shaders/glsl/bgra_to_rgba.glsl | 33 +++++++++++++++++++++++ 9 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 resources/shaders/glsl/bgra_to_rgba.glsl diff --git a/es-core/src/renderers/Renderer.cpp b/es-core/src/renderers/Renderer.cpp index 721045408..793644938 100644 --- a/es-core/src/renderers/Renderer.cpp +++ b/es-core/src/renderers/Renderer.cpp @@ -271,6 +271,7 @@ namespace Renderer shaderFiles.push_back(":/shaders/glsl/blur_horizontal.glsl"); shaderFiles.push_back(":/shaders/glsl/blur_vertical.glsl"); shaderFiles.push_back(":/shaders/glsl/scanlines.glsl"); + shaderFiles.push_back(":/shaders/glsl/bgra_to_rgba.glsl"); for (auto it = shaderFiles.cbegin(); it != shaderFiles.cend(); ++it) { Shader* loadShader = new Shader(); diff --git a/es-core/src/renderers/Renderer.h b/es-core/src/renderers/Renderer.h index a932449ed..ff4b61c53 100644 --- a/es-core/src/renderers/Renderer.h +++ b/es-core/src/renderers/Renderer.h @@ -26,6 +26,7 @@ namespace Renderer const unsigned int SHADER_BLUR_HORIZONTAL{8}; const unsigned int SHADER_BLUR_VERTICAL{16}; const unsigned int SHADER_SCANLINES{32}; + const unsigned int SHADER_BGRA_TO_RGBA{64}; struct shaderParameters { std::array textureSize; @@ -94,6 +95,7 @@ namespace Renderer { enum Type { RGBA, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0). + BGRA, ALPHA }; } @@ -170,6 +172,7 @@ namespace Renderer bool createContext(); void destroyContext(); unsigned int createTexture(const Texture::Type type, + const Texture::Type format, const bool linearMinify, const bool linearMagnify, const bool repeat, diff --git a/es-core/src/renderers/Renderer_GL21.cpp b/es-core/src/renderers/Renderer_GL21.cpp index 25cb6e4ac..ca6da6496 100644 --- a/es-core/src/renderers/Renderer_GL21.cpp +++ b/es-core/src/renderers/Renderer_GL21.cpp @@ -45,6 +45,7 @@ namespace Renderer // clang-format off switch (_type) { case Texture::RGBA: { return GL_RGBA; } break; + case Texture::BGRA: { return GL_BGRA; } break; case Texture::ALPHA: { return GL_ALPHA; } break; default: { return GL_ZERO; } } @@ -142,7 +143,7 @@ namespace Renderer } uint8_t data[4] = {255, 255, 255, 255}; - whiteTexture = createTexture(Texture::RGBA, false, false, true, 1, 1, data); + whiteTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, true, 1, 1, data); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D)); @@ -167,6 +168,7 @@ namespace Renderer } unsigned int createTexture(const Texture::Type type, + const Texture::Type format, const bool linearMinify, const bool linearMagnify, const bool repeat, @@ -175,6 +177,7 @@ namespace Renderer void* data) { const GLenum textureType = convertTextureType(type); + const GLenum textureFormat = convertTextureType(format); unsigned int texture; GL_CHECK_ERROR(glGenTextures(1, &texture)); @@ -192,8 +195,11 @@ namespace Renderer GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linearMagnify ? static_cast(GL_LINEAR) : static_cast(GL_NEAREST))); - - GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, + // Setting different values for internalFormat and format is not really supported by the + // OpenGL standard so hopefully it works with all drivers and on all operating systems. + // This is only intended as a last resort anyway, normally the BGRA_TO_RGBA shader should + // be used for color model conversion. + GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureFormat, GL_UNSIGNED_BYTE, data)); return texture; @@ -355,6 +361,16 @@ namespace Renderer runShader->deactivateShaders(); } } + + if (vertices->shaders & SHADER_BGRA_TO_RGBA) { + Shader* runShader = getShaderProgram(SHADER_BGRA_TO_RGBA); + if (runShader) { + runShader->activateShaders(); + runShader->setModelViewProjectionMatrix(getProjectionMatrix() * trans); + GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, numVertices)); + runShader->deactivateShaders(); + } + } } #endif } @@ -470,8 +486,8 @@ namespace Renderer vertices[0].saturation = parameters.fragmentSaturation; setMatrix(getIdentity()); - GLuint screenTexture = - createTexture(Texture::RGBA, false, false, false, width, height, nullptr); + GLuint screenTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, false, + width, height, nullptr); GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)); diff --git a/es-core/src/renderers/Renderer_GLES10.cpp b/es-core/src/renderers/Renderer_GLES10.cpp index 21cd8cfd1..596a966ab 100644 --- a/es-core/src/renderers/Renderer_GLES10.cpp +++ b/es-core/src/renderers/Renderer_GLES10.cpp @@ -43,9 +43,10 @@ namespace Renderer { // clang-format off switch (_type) { - case Texture::RGBA: { return GL_RGBA; } break; - case Texture::ALPHA: { return GL_ALPHA; } break; - default: { return GL_ZERO; } + case Texture::RGBA: { return GL_RGBA; } break; + case Texture::BGRA: { return GL_BGRA_EXT; } break; + case Texture::ALPHA: { return GL_ALPHA; } break; + default: { return GL_ZERO; } } // clang-format on } @@ -91,7 +92,7 @@ namespace Renderer "MISSING"); uint8_t data[4] = {255, 255, 255, 255}; - whiteTexture = createTexture(Texture::RGBA, false, false, true, 1, 1, data); + whiteTexture = createTexture(Texture::RGBA, Texture::RGBA, false, false, true, 1, 1, data); GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); GL_CHECK_ERROR(glEnable(GL_TEXTURE_2D)); @@ -112,6 +113,7 @@ namespace Renderer } unsigned int createTexture(const Texture::Type type, + const Texture::Type format, const bool linearMinify, const bool linearMagnify, const bool repeat, @@ -120,6 +122,7 @@ namespace Renderer void* data) { const GLenum textureType = convertTextureType(type); + const GLenum textureFormat = convertTextureType(format); unsigned int texture; GL_CHECK_ERROR(glGenTextures(1, &texture)); @@ -134,7 +137,11 @@ namespace Renderer GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linearMagnify ? GL_LINEAR : GL_NEAREST)); - GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureType, + // Setting different values for internalFormat and format is not really supported by the + // OpenGL standard so hopefully it works with all drivers and on all operating systems. + // This is only intended as a last resort anyway, normally the BGRA_TO_RGBA shader should + // be used for color model conversion. + GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, textureType, width, height, 0, textureFormat, GL_UNSIGNED_BYTE, data)); return texture; diff --git a/es-core/src/resources/Font.cpp b/es-core/src/resources/Font.cpp index 1469aa711..cf06f3204 100644 --- a/es-core/src/resources/Font.cpp +++ b/es-core/src/resources/Font.cpp @@ -199,8 +199,8 @@ bool Font::FontTexture::findEmpty(const glm::ivec2& size, glm::ivec2& cursor_out void Font::FontTexture::initTexture() { assert(textureId == 0); - textureId = Renderer::createTexture(Renderer::Texture::ALPHA, false, false, false, - textureSize.x, textureSize.y, nullptr); + textureId = Renderer::createTexture(Renderer::Texture::ALPHA, Renderer::Texture::ALPHA, false, + false, false, textureSize.x, textureSize.y, nullptr); } void Font::FontTexture::deinitTexture() diff --git a/es-core/src/resources/TextureData.cpp b/es-core/src/resources/TextureData.cpp index 3cbe001cc..29de4858e 100644 --- a/es-core/src/resources/TextureData.cpp +++ b/es-core/src/resources/TextureData.cpp @@ -13,7 +13,6 @@ #include "ImageIO.h" #include "Log.h" -#include "renderers/Renderer.h" #include "resources/ResourceManager.h" #include "utils/StringUtil.h" @@ -28,6 +27,7 @@ TextureData::TextureData(bool tile) : mTile{tile} , mTextureID{0} , mDataRGBA({}) + , mFormat{Renderer::Texture::RGBA} , mWidth{0} , mHeight{0} , mSourceWidth{0.0f} @@ -219,7 +219,7 @@ bool TextureData::uploadAndBind() // Upload texture. mTextureID = - Renderer::createTexture(Renderer::Texture::RGBA, true, mLinearMagnify, mTile, + Renderer::createTexture(Renderer::Texture::RGBA, mFormat, true, mLinearMagnify, mTile, static_cast(mWidth), static_cast(mHeight), mDataRGBA.data()); } diff --git a/es-core/src/resources/TextureData.h b/es-core/src/resources/TextureData.h index aee2eac4c..5946db389 100644 --- a/es-core/src/resources/TextureData.h +++ b/es-core/src/resources/TextureData.h @@ -9,6 +9,7 @@ #ifndef ES_CORE_RESOURCES_TEXTURE_DATA_H #define ES_CORE_RESOURCES_TEXTURE_DATA_H +#include "renderers/Renderer.h" #include "utils/MathUtil.h" #include @@ -63,6 +64,8 @@ public: // Whether to rasterize the image even if a size has not been set yet. void setForceRasterization(bool setting) { mForceRasterization = setting; } + void setFormat(Renderer::Texture::Type format) { mFormat = format; } + // Has the image been loaded but not yet been rasterized as the size was not known? bool getPendingRasterization() { return mPendingRasterization; } @@ -77,6 +80,7 @@ private: std::string mPath; std::atomic mTextureID; std::vector mDataRGBA; + Renderer::Texture::Type mFormat; std::atomic mWidth; std::atomic mHeight; std::atomic mSourceWidth; diff --git a/es-core/src/resources/TextureResource.h b/es-core/src/resources/TextureResource.h index 1e2c39432..1777117b3 100644 --- a/es-core/src/resources/TextureResource.h +++ b/es-core/src/resources/TextureResource.h @@ -45,6 +45,8 @@ public: return (mTextureData != nullptr ? mTextureData->getPendingRasterization() : false); } + void setFormat(Renderer::Texture::Type format) { mTextureData->setFormat(format); } + std::string getTextureFilePath(); void rasterizeAt(float width, float height); diff --git a/resources/shaders/glsl/bgra_to_rgba.glsl b/resources/shaders/glsl/bgra_to_rgba.glsl new file mode 100644 index 000000000..aae41ab9a --- /dev/null +++ b/resources/shaders/glsl/bgra_to_rgba.glsl @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// +// EmulationStation Desktop Edition +// bgra_to_rgba.glsl +// +// Convert from color model BGRA to RGBA. +// + +#if defined(VERTEX) +// Vertex section of code: + +uniform mat4 MVPMatrix; +varying vec2 vTexCoord; + +void main(void) +{ + vTexCoord = gl_MultiTexCoord0.xy; + gl_Position = MVPMatrix * gl_Vertex; +} + +#elif defined(FRAGMENT) +// Fragment section of code: + +uniform sampler2D myTexture; +varying vec2 vTexCoord; + +void main() +{ + vec4 color = texture2D(myTexture, vTexCoord); + gl_FragColor = vec4(color.bgra); +} + +#endif