diff --git a/xbmc/guilib/TextureGL.cpp b/xbmc/guilib/TextureGL.cpp index 78dd891b1fcc6..28da1cc000d56 100644 --- a/xbmc/guilib/TextureGL.cpp +++ b/xbmc/guilib/TextureGL.cpp @@ -9,6 +9,7 @@ #include "TextureGL.h" #include "ServiceBroker.h" +#include "guilib/TextureFormats.h" #include "guilib/TextureManager.h" #include "rendering/RenderSystem.h" #include "settings/AdvancedSettings.h" @@ -19,6 +20,118 @@ #include +namespace +{ +// clang-format off +static const std::map TextureMapping +{ +#if defined(GL_EXT_texture_sRGB_R8) && (GL_EXT_texture_sRGB_RG8) + {KD_TEX_FMT_SDR_R8, {GL_R8, GL_SR8_EXT, GL_RED}}, + {KD_TEX_FMT_SDR_RG8, {GL_RG8, GL_SRG8_EXT, GL_RG}}, +#else + {KD_TEX_FMT_SDR_R8, {GL_R8, GL_FALSE, GL_RED}}, + {KD_TEX_FMT_SDR_RG8, {GL_RG8, GL_FALSE, GL_RG}}, +#endif + {KD_TEX_FMT_SDR_R5G6B5, {GL_RGB565, GL_FALSE, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}}, + {KD_TEX_FMT_SDR_RGB5_A1, {GL_RGB5_A1, GL_FALSE, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}}, + {KD_TEX_FMT_SDR_RGBA4, {GL_RGBA4, GL_FALSE, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}}, + {KD_TEX_FMT_SDR_RGB8, {GL_RGB8, GL_SRGB8, GL_RGB}}, + {KD_TEX_FMT_SDR_RGBA8, {GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA}}, + {KD_TEX_FMT_SDR_BGRA8, {GL_RGBA8, GL_SRGB8_ALPHA8, GL_BGRA}}, + +#if defined(GL_VERSION_3_0) + {KD_TEX_FMT_HDR_R16f, {GL_R16F, GL_FALSE, GL_RED, GL_HALF_FLOAT}}, + {KD_TEX_FMT_HDR_RG16f, {GL_RG16F, GL_FALSE, GL_RG, GL_HALF_FLOAT}}, + {KD_TEX_FMT_HDR_R11F_G11F_B10F, {GL_R11F_G11F_B10F, GL_FALSE, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}}, + {KD_TEX_FMT_HDR_RGB9_E5, {GL_RGB9_E5, GL_FALSE, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}}, + {KD_TEX_FMT_HDR_RGB10_A2, {GL_RGB10_A2, GL_FALSE, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}}, + {KD_TEX_FMT_HDR_RGBA16f, {GL_RGBA16F, GL_FALSE, GL_RGBA, GL_HALF_FLOAT}}, +#endif + +#if defined(GL_EXT_texture_compression_s3tc) && (GL_EXT_texture_sRGB) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A4, {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}}, + {KD_TEX_FMT_S3TC_RGBA8, {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}}, +#elif defined(GL_EXT_texture_compression_s3tc) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A4, {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}}, + {KD_TEX_FMT_S3TC_RGBA8, {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}}, +#elif defined(GL_EXT_texture_compression_dxt1) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}}, +#endif + +#if defined(GL_EXT_texture_compression_rgtc) + {KD_TEX_FMT_RGTC_R11, {GL_COMPRESSED_RED_RGTC1_EXT}}, + {KD_TEX_FMT_RGTC_RG11, {GL_COMPRESSED_RED_GREEN_RGTC2_EXT}}, +#endif + +#if defined(GL_ARB_texture_compression_bptc) + {KD_TEX_FMT_BPTC_RGB16F, {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB}}, + {KD_TEX_FMT_BPTC_RGBA8, {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB}}, +#endif + +#if defined(GL_VERSION_4_3) + {KD_TEX_FMT_ETC1_RGB8, {GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2}}, + + {KD_TEX_FMT_ETC2_R11, {GL_COMPRESSED_R11_EAC}}, + {KD_TEX_FMT_ETC2_RG11, {GL_COMPRESSED_RG11_EAC}}, + {KD_TEX_FMT_ETC2_RGB8, {GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2}}, + {KD_TEX_FMT_ETC2_RGB8_A1, {GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2}}, + {KD_TEX_FMT_ETC2_RGBA8, {GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC}}, +#endif + +#if defined(GL_KHR_texture_compression_astc_ldr) || (GL_KHR_texture_compression_astc_hdr) + {KD_TEX_FMT_ASTC_LDR_4x4, {GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}}, + {KD_TEX_FMT_ASTC_LDR_5x4, {GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}}, + {KD_TEX_FMT_ASTC_LDR_5x5, {GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_6x5, {GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_6x6, {GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x5, {GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x6, {GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x8, {GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x5, {GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x6, {GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x8, {GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x10, {GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}}, + {KD_TEX_FMT_ASTC_LDR_12x10, {GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}}, + {KD_TEX_FMT_ASTC_LDR_12x12, {GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}}, + + {KD_TEX_FMT_ASTC_HDR_4x4, {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}}, + {KD_TEX_FMT_ASTC_HDR_5x4, {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}}, + {KD_TEX_FMT_ASTC_HDR_5x5, {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_6x5, {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_6x6, {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x5, {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x6, {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x8, {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x5, {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x6, {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x8, {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x10, {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}}, + {KD_TEX_FMT_ASTC_HDR_12x10, {GL_COMPRESSED_RGBA_ASTC_12x10_KHR}}, + {KD_TEX_FMT_ASTC_HDR_12x12, {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}}, +#endif +}; + +static const std::map SwizzleMap +{ + {KD_TEX_SWIZ_RGBA, {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}}, + {KD_TEX_SWIZ_RGB1, {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}}, + {KD_TEX_SWIZ_RRR1, {GL_RED, GL_RED, GL_RED, GL_ONE}}, + {KD_TEX_SWIZ_111R, {GL_ONE, GL_ONE, GL_ONE, GL_RED}}, + {KD_TEX_SWIZ_RRRG, {GL_RED, GL_RED, GL_RED, GL_GREEN}}, + {KD_TEX_SWIZ_RRRR, {GL_RED, GL_RED, GL_RED, GL_RED}}, + {KD_TEX_SWIZ_GGG1, {GL_GREEN, GL_GREEN, GL_GREEN, GL_ONE}}, + {KD_TEX_SWIZ_111G, {GL_ONE, GL_ONE, GL_ONE, GL_GREEN}}, + {KD_TEX_SWIZ_GGGA, {GL_GREEN, GL_GREEN, GL_GREEN, GL_ALPHA}}, + {KD_TEX_SWIZ_GGGG, {GL_GREEN, GL_GREEN, GL_GREEN, GL_GREEN}}, +}; +// clang-format on +} // namespace + std::unique_ptr CTexture::CreateTexture(unsigned int width, unsigned int height, XB_FMT format) @@ -33,6 +146,8 @@ CGLTexture::CGLTexture(unsigned int width, unsigned int height, XB_FMT format) CServiceBroker::GetRenderSystem()->GetRenderVersion(major, minor); if (major >= 3) m_isOglVersion3orNewer = true; + if (major > 3 || (major == 3 && minor >= 3)) + m_isOglVersion33orNewer = true; } CGLTexture::~CGLTexture() @@ -119,49 +234,30 @@ void CGLTexture::LoadToGPU() m_textureWidth = maxSize; } - GLenum format = GL_BGRA; - GLint numcomponents = GL_RGBA; + SetSwizzle(); + + TextureFormat glFormat = GetFormatGL(m_textureFormat); - switch (m_format) + if (glFormat.format == GL_FALSE) { - case XB_FMT_DXT1: - format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - break; - case XB_FMT_DXT3: - format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - break; - case XB_FMT_DXT5: - case XB_FMT_DXT5_YCoCg: - format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - case XB_FMT_RGB8: - format = GL_RGB; - numcomponents = GL_RGB; - break; - case XB_FMT_A8R8G8B8: - default: - break; + CLog::LogF(LOGDEBUG, "Failed to load texture. Unsupported format {}", m_textureFormat); + m_loadedToGPU = true; + return; } - if ((m_format & XB_FMT_DXT_MASK) == 0) + if ((m_textureFormat & KD_TEX_FMT_SDR) || (m_textureFormat & KD_TEX_FMT_HDR)) { - glTexImage2D(GL_TEXTURE_2D, 0, numcomponents, - m_textureWidth, m_textureHeight, 0, - format, GL_UNSIGNED_BYTE, m_pixels); + glTexImage2D(GL_TEXTURE_2D, 0, glFormat.internalFormat, m_textureWidth, m_textureHeight, 0, + glFormat.format, glFormat.type, m_pixels); } else { - glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, - m_textureWidth, m_textureHeight, 0, - GetPitch() * GetRows(), m_pixels); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, glFormat.internalFormat, m_textureWidth, + m_textureHeight, 0, GetPitch() * GetRows(), m_pixels); } if (IsMipmapped() && m_isOglVersion3orNewer) - { glGenerateMipmap(GL_TEXTURE_2D); - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); VerifyGLState(); @@ -185,3 +281,41 @@ void CGLTexture::BindToUnit(unsigned int unit) glBindTexture(GL_TEXTURE_2D, m_texture); } +void CGLTexture::SetSwizzle() +{ + if (!SwizzleMap.contains(m_textureSwizzle)) + return; + + Textureswizzle swiz = SwizzleMap.at(m_textureSwizzle); + + // GL_TEXTURE_SWIZZLE_RGBA and GL_TEXTURE_SWIZZLE_RGBA_EXT should be the same + // token, but just to be sure... +#if defined(GL_VERSION_3_3) || (GL_ARB_texture_swizzle) + if (m_isOglVersion33orNewer || + CServiceBroker::GetRenderSystem()->IsExtSupported("GL_ARB_texture_swizzle")) + { + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, &swiz.r); + return; + } +#endif +#if defined(GL_EXT_texture_swizzle) + if (CServiceBroker::GetRenderSystem()->IsExtSupported("GL_EXT_texture_swizzle")) + { + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA_EXT, &swiz.r); + return; + } +#endif +} + +TextureFormat CGLTexture::GetFormatGL(KD_TEX_FMT textureFormat) +{ + TextureFormat glFormat; + + const auto it = TextureMapping.find(textureFormat); + if (it != TextureMapping.cend()) + { + glFormat = it->second; + } + + return glFormat; +} diff --git a/xbmc/guilib/TextureGL.h b/xbmc/guilib/TextureGL.h index 0a979065e1be0..e3f68bc421ec2 100644 --- a/xbmc/guilib/TextureGL.h +++ b/xbmc/guilib/TextureGL.h @@ -12,6 +12,22 @@ #include "system_gl.h" +struct TextureFormat +{ + GLenum internalFormat{GL_FALSE}; + GLenum internalFormatSRGB{GL_FALSE}; + GLint format{GL_FALSE}; + GLenum type{GL_UNSIGNED_BYTE}; +}; + +struct Textureswizzle +{ + GLint r{GL_RED}; + GLint g{GL_GREEN}; + GLint b{GL_BLUE}; + GLint a{GL_ALPHA}; +}; + /************************************************************************/ /* CGLTexture */ /************************************************************************/ @@ -28,7 +44,11 @@ class CGLTexture : public CTexture void BindToUnit(unsigned int unit) override; protected: - GLuint m_texture = 0; - bool m_isOglVersion3orNewer = false; + void SetSwizzle(); + TextureFormat GetFormatGL(KD_TEX_FMT textureFormat); + + GLuint m_texture{0}; + bool m_isOglVersion3orNewer{false}; + bool m_isOglVersion33orNewer{false}; }; diff --git a/xbmc/guilib/TextureGLES.cpp b/xbmc/guilib/TextureGLES.cpp index 828d005edb73b..bf0785cc77f5e 100644 --- a/xbmc/guilib/TextureGLES.cpp +++ b/xbmc/guilib/TextureGLES.cpp @@ -9,6 +9,7 @@ #include "TextureGLES.h" #include "ServiceBroker.h" +#include "guilib/TextureFormats.h" #include "guilib/TextureManager.h" #include "rendering/RenderSystem.h" #include "settings/AdvancedSettings.h" @@ -19,6 +20,156 @@ #include +namespace +{ +// clang-format off +// GLES 2.0 texture formats. +// Any extension used here is in the core 3.0 profile (except BGRA) +// format = (unsized) internalFormat (with core 2.0) +static const std::map TextureMappingGLES20 +{ +#if defined(GL_EXT_texture_rg) + {KD_TEX_FMT_SDR_R8, {GL_RED_EXT}}, + {KD_TEX_FMT_SDR_RG8, {GL_RG_EXT}}, +#endif + {KD_TEX_FMT_SDR_R5G6B5, {GL_RGB, GL_FALSE, GL_FALSE, GL_UNSIGNED_SHORT_5_6_5}}, + {KD_TEX_FMT_SDR_RGB5_A1, {GL_RGBA, GL_FALSE, GL_FALSE, GL_UNSIGNED_SHORT_5_5_5_1}}, + {KD_TEX_FMT_SDR_RGBA4, {GL_RGBA, GL_FALSE, GL_FALSE, GL_UNSIGNED_SHORT_4_4_4_4}}, +#if defined(GL_EXT_sRGB) + {KD_TEX_FMT_SDR_RGB8, {GL_RGB, GL_SRGB_EXT}}, + {KD_TEX_FMT_SDR_RGBA8, {GL_RGBA, GL_SRGB_ALPHA_EXT}}, +#else + {KD_TEX_FMT_SDR_RGB8, {GL_RGB}}, + {KD_TEX_FMT_SDR_RGBA8, {GL_RGBA}}, +#endif + +#if defined(GL_EXT_texture_format_BGRA8888) || (GL_IMG_texture_format_BGRA8888) + {KD_TEX_FMT_SDR_BGRA8, {GL_BGRA_EXT}}, +#endif + +#if defined(GL_EXT_texture_type_2_10_10_10_REV) + {KD_TEX_FMT_HDR_RGB10_A2, {GL_RGBA, GL_FALSE, GL_FALSE, GL_UNSIGNED_INT_2_10_10_10_REV_EXT}}, +#endif +#if defined(GL_OES_texture_half_float_linear) + {KD_TEX_FMT_HDR_RGBA16f, {GL_RGBA, GL_FALSE, GL_FALSE, GL_HALF_FLOAT_OES}}, +#endif + +#if defined(GL_OES_compressed_ETC1_RGB8_texture) + {KD_TEX_FMT_ETC1_RGB8, {GL_ETC1_RGB8_OES}}, +#endif +}; + +// GLES 3.0 texture formats. +#if defined(GL_ES_VERSION_3_0) +std::map TextureMappingGLES30 +{ +#if defined(GL_EXT_texture_sRGB_R8) && (GL_EXT_texture_sRGB_RG8) // in gl2ext.h, but spec says >= 3.0 + {KD_TEX_FMT_SDR_R8, {GL_R8, GL_SR8_EXT, GL_RED}}, + {KD_TEX_FMT_SDR_RG8, {GL_RG8, GL_SRG8_EXT, GL_RG}}, +#else + {KD_TEX_FMT_SDR_R8, {GL_R8, GL_FALSE, GL_RED}}, + {KD_TEX_FMT_SDR_RG8, {GL_RG8, GL_FALSE, GL_RG}}, +#endif + {KD_TEX_FMT_SDR_R5G6B5, {GL_RGB565, GL_FALSE, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}}, + {KD_TEX_FMT_SDR_RGB5_A1, {GL_RGB5_A1, GL_FALSE, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}}, + {KD_TEX_FMT_SDR_RGBA4, {GL_RGBA4, GL_FALSE, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}}, + {KD_TEX_FMT_SDR_RGB8, {GL_RGB8, GL_SRGB8, GL_RGB}}, + {KD_TEX_FMT_SDR_RGBA8, {GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA}}, + + {KD_TEX_FMT_HDR_R16f, {GL_R16F, GL_FALSE, GL_RED, GL_HALF_FLOAT}}, + {KD_TEX_FMT_HDR_RG16f, {GL_RG16F, GL_FALSE, GL_RG, GL_HALF_FLOAT}}, + {KD_TEX_FMT_HDR_R11F_G11F_B10F, {GL_R11F_G11F_B10F, GL_FALSE, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}}, + {KD_TEX_FMT_HDR_RGB9_E5, {GL_RGB9_E5, GL_FALSE, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}}, + {KD_TEX_FMT_HDR_RGB10_A2, {GL_RGB10_A2, GL_FALSE, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}}, + {KD_TEX_FMT_HDR_RGBA16f, {GL_RGBA16F, GL_FALSE, GL_RGBA, GL_HALF_FLOAT}}, + + {KD_TEX_FMT_ETC1_RGB8, {GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2}}, + + {KD_TEX_FMT_ETC2_R11, {GL_COMPRESSED_R11_EAC}}, + {KD_TEX_FMT_ETC2_RG11, {GL_COMPRESSED_RG11_EAC}}, + {KD_TEX_FMT_ETC2_RGB8, {GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2}}, + {KD_TEX_FMT_ETC2_RGB8_A1, {GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2}}, + {KD_TEX_FMT_ETC2_RGBA8, {GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC}}, +}; +#endif // GL_ES_VERSION_3_0 + +// Common GLES extensions (texture compression) +static const std::map TextureMappingGLESExtensions +{ +#if defined(GL_EXT_texture_compression_s3tc) && (GL_EXT_texture_compression_s3tc_srgb) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A4, {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}}, + {KD_TEX_FMT_S3TC_RGBA8, {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}}, +#elif defined(GL_EXT_texture_compression_s3tc) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A4, {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}}, + {KD_TEX_FMT_S3TC_RGBA8, {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}}, +#elif defined(GL_EXT_texture_compression_dxt1) + {KD_TEX_FMT_S3TC_RGB8, {GL_COMPRESSED_RGB_S3TC_DXT1_EXT}}, + {KD_TEX_FMT_S3TC_RGB8_A1, {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}}, +#endif + +#if defined(GL_EXT_texture_compression_rgtc) + {KD_TEX_FMT_RGTC_R11, {GL_COMPRESSED_RED_RGTC1_EXT}}, + {KD_TEX_FMT_RGTC_RG11, {GL_COMPRESSED_RED_GREEN_RGTC2_EXT}}, +#endif + +#if defined(GL_EXT_texture_compression_bptc) + {KD_TEX_FMT_BPTC_RGB16F, {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}}, + {KD_TEX_FMT_BPTC_RGBA8, {GL_COMPRESSED_RGBA_BPTC_UNORM_EXT, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT}}, +#endif + +#if defined(GL_KHR_texture_compression_astc_ldr) || (GL_KHR_texture_compression_astc_hdr) + {KD_TEX_FMT_ASTC_LDR_4x4, {GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}}, + {KD_TEX_FMT_ASTC_LDR_5x4, {GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}}, + {KD_TEX_FMT_ASTC_LDR_5x5, {GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_6x5, {GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_6x6, {GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x5, {GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x6, {GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_8x8, {GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x5, {GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x6, {GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x8, {GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}}, + {KD_TEX_FMT_ASTC_LDR_10x10, {GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}}, + {KD_TEX_FMT_ASTC_LDR_12x10, {GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR}}, + {KD_TEX_FMT_ASTC_LDR_12x12, {GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}}, + + {KD_TEX_FMT_ASTC_HDR_4x4, {GL_COMPRESSED_RGBA_ASTC_4x4_KHR}}, + {KD_TEX_FMT_ASTC_HDR_5x4, {GL_COMPRESSED_RGBA_ASTC_5x4_KHR}}, + {KD_TEX_FMT_ASTC_HDR_5x5, {GL_COMPRESSED_RGBA_ASTC_5x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_6x5, {GL_COMPRESSED_RGBA_ASTC_6x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_6x6, {GL_COMPRESSED_RGBA_ASTC_6x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x5, {GL_COMPRESSED_RGBA_ASTC_8x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x6, {GL_COMPRESSED_RGBA_ASTC_8x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_8x8, {GL_COMPRESSED_RGBA_ASTC_8x8_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x5, {GL_COMPRESSED_RGBA_ASTC_10x5_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x6, {GL_COMPRESSED_RGBA_ASTC_10x6_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x8, {GL_COMPRESSED_RGBA_ASTC_10x8_KHR}}, + {KD_TEX_FMT_ASTC_HDR_10x10, {GL_COMPRESSED_RGBA_ASTC_10x10_KHR}}, + {KD_TEX_FMT_ASTC_HDR_12x10, {GL_COMPRESSED_RGBA_ASTC_12x10_KHR}}, + {KD_TEX_FMT_ASTC_HDR_12x12, {GL_COMPRESSED_RGBA_ASTC_12x12_KHR}}, +#endif +}; + +static const std::map SwizzleMapGLES +{ + {KD_TEX_SWIZ_RGBA, {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}}, + {KD_TEX_SWIZ_RGB1, {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}}, + {KD_TEX_SWIZ_RRR1, {GL_RED, GL_RED, GL_RED, GL_ONE}}, + {KD_TEX_SWIZ_111R, {GL_ONE, GL_ONE, GL_ONE, GL_RED}}, + {KD_TEX_SWIZ_RRRG, {GL_RED, GL_RED, GL_RED, GL_GREEN}}, + {KD_TEX_SWIZ_RRRR, {GL_RED, GL_RED, GL_RED, GL_RED}}, + {KD_TEX_SWIZ_GGG1, {GL_GREEN, GL_GREEN, GL_GREEN, GL_ONE}}, + {KD_TEX_SWIZ_111G, {GL_ONE, GL_ONE, GL_ONE, GL_GREEN}}, + {KD_TEX_SWIZ_GGGA, {GL_GREEN, GL_GREEN, GL_GREEN, GL_ALPHA}}, + {KD_TEX_SWIZ_GGGG, {GL_GREEN, GL_GREEN, GL_GREEN, GL_GREEN}}, +}; +// clang-format on +} // namespace + std::unique_ptr CTexture::CreateTexture(unsigned int width, unsigned int height, XB_FMT format) @@ -125,52 +276,42 @@ void CGLESTexture::LoadToGPU() } } - // All incoming textures are BGRA, which GLES does not necessarily support. - // Some (most?) hardware supports BGRA textures via an extension. - // If not, we convert to RGBA first to avoid having to swizzle in shaders. - // Explicitly define GL_BGRA_EXT here in the case that it's not defined by - // system headers, and trust the extension list instead. -#ifndef GL_BGRA_EXT -#define GL_BGRA_EXT 0x80E1 -#endif - - GLint internalformat; - GLenum pixelformat; - - switch (m_format) - { - default: - case XB_FMT_RGBA8: - internalformat = pixelformat = GL_RGBA; - break; - case XB_FMT_RGB8: - internalformat = pixelformat = GL_RGB; - break; - case XB_FMT_A8R8G8B8: - if (CServiceBroker::GetRenderSystem()->IsExtSupported("GL_EXT_texture_format_BGRA8888") || - CServiceBroker::GetRenderSystem()->IsExtSupported("GL_IMG_texture_format_BGRA8888")) - { - internalformat = pixelformat = GL_BGRA_EXT; - } - else if (CServiceBroker::GetRenderSystem()->IsExtSupported( - "GL_APPLE_texture_format_BGRA8888")) - { - // Apple's implementation does not conform to spec. Instead, they require - // differing format/internalformat, more like GL. - internalformat = GL_RGBA; - pixelformat = GL_BGRA_EXT; - } - else - { - SwapBlueRed(m_pixels, m_textureHeight, GetPitch()); - internalformat = pixelformat = GL_RGBA; - } - break; - } - - glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_textureWidth, m_textureHeight, 0, pixelformat, - GL_UNSIGNED_BYTE, m_pixels); + TextureFormat glesFormat; + if (m_isGLESVersion30orNewer) + { + KD_TEX_FMT textureFormat = m_textureFormat; + bool swapRB = false; + // Support for BGRA is hit and miss, swizzle instead + if (textureFormat == KD_TEX_FMT_SDR_BGRA8) + { + textureFormat = KD_TEX_FMT_SDR_RGBA8; + swapRB = true; + } + SetSwizzle(swapRB); + glesFormat = GetFormatGLES30(textureFormat); + } + else + { + glesFormat = GetFormatGLES20(m_textureFormat); + } + + if (glesFormat.internalFormat == GL_FALSE) + { + CLog::LogF(LOGDEBUG, "Failed to load texture. Unsupported format {}", m_textureFormat); + m_loadedToGPU = true; + return; + } + if ((m_textureFormat & KD_TEX_FMT_SDR) || (m_textureFormat & KD_TEX_FMT_HDR)) + { + glTexImage2D(GL_TEXTURE_2D, 0, glesFormat.internalFormat, m_textureWidth, m_textureHeight, 0, + glesFormat.format, glesFormat.type, m_pixels); + } + else + { + glCompressedTexImage2D(GL_TEXTURE_2D, 0, glesFormat.internalFormat, m_textureWidth, + m_textureHeight, 0, GetPitch() * GetRows(), m_pixels); + } if (IsMipmapped()) { glGenerateMipmap(GL_TEXTURE_2D); @@ -196,3 +337,113 @@ void CGLESTexture::BindToUnit(unsigned int unit) glActiveTexture(GL_TEXTURE0 + unit); glBindTexture(GL_TEXTURE_2D, m_texture); } + +void CGLESTexture::SetSwizzle(bool swapRB) +{ +#if defined(GL_ES_VERSION_3_0) + TextureSwizzle swiz; + + const auto it = SwizzleMapGLES.find(m_textureSwizzle); + if (it != SwizzleMapGLES.cend()) + swiz = it->second; + + if (swapRB) + { + SwapBlueRedSwizzle(swiz.r); + SwapBlueRedSwizzle(swiz.g); + SwapBlueRedSwizzle(swiz.b); + SwapBlueRedSwizzle(swiz.a); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swiz.r); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swiz.g); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swiz.b); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, swiz.a); +#endif +} + +void CGLESTexture::SwapBlueRedSwizzle(GLint& component) +{ + if (component == GL_RED) + component = GL_BLUE; + else if (component == GL_BLUE) + component = GL_RED; +} + +TextureFormat CGLESTexture::GetFormatGLES20(KD_TEX_FMT textureFormat) +{ + TextureFormat glFormat; + + // GLES 2.0 does not support swizzling. But for some Kodi formats+swizzles, + // we can map GLES formats (Luminance, Luminance-Alpha, BGRA). Other swizzles + // would have to be supported in the shader, or converted before upload. + + if (m_textureFormat == KD_TEX_FMT_SDR_R8 && m_textureSwizzle == KD_TEX_SWIZ_RRR1) + { + glFormat.format = glFormat.internalFormat = GL_LUMINANCE; + } + else if (m_textureFormat == KD_TEX_FMT_SDR_RG8 && m_textureSwizzle == KD_TEX_SWIZ_RRRG) + { + glFormat.format = glFormat.internalFormat = GL_LUMINANCE_ALPHA; + } + else if (m_textureFormat == KD_TEX_FMT_SDR_BGRA8 && m_textureSwizzle == KD_TEX_SWIZ_RGBA && + !CServiceBroker::GetRenderSystem()->IsExtSupported("GL_EXT_texture_format_BGRA8888") && + !CServiceBroker::GetRenderSystem()->IsExtSupported("GL_IMG_texture_format_BGRA8888")) + { +#if defined(GL_APPLE_texture_format_BGRA8888) + if (CServiceBroker::GetRenderSystem()->IsExtSupported("GL_APPLE_texture_format_BGRA8888")) + { + glFormat.internalFormat = GL_RGBA; + glFormat.format = GL_BGRA_EXT; + } + else +#endif + { + SwapBlueRed(m_pixels, m_textureHeight, GetPitch()); + glFormat.format = glFormat.internalFormat = GL_RGBA; + } + } + else if (textureFormat & KD_TEX_FMT_SDR || textureFormat & KD_TEX_FMT_HDR || + textureFormat & KD_TEX_FMT_ETC1) + { + const auto it = TextureMappingGLES20.find(textureFormat); + if (it != TextureMappingGLES20.cend()) + glFormat = it->second; + glFormat.format = glFormat.internalFormat; + } + else + { + const auto it = TextureMappingGLESExtensions.find(textureFormat); + if (it != TextureMappingGLESExtensions.cend()) + glFormat = it->second; + } + + return glFormat; +} + +TextureFormat CGLESTexture::GetFormatGLES30(KD_TEX_FMT textureFormat) +{ + TextureFormat glFormat; + + if (textureFormat & KD_TEX_FMT_SDR || textureFormat & KD_TEX_FMT_HDR) + { +#if defined(GL_ES_VERSION_3_0) + const auto it = TextureMappingGLES30.find(KD_TEX_FMT_SDR_RGBA8); + if (it != TextureMappingGLES30.cend()) + glFormat = it->second; +#else + const auto it = TextureMappingGLES20.find(textureFormat); + if (it != TextureMappingGLES20.cend()) + glFormat = it->second; + glFormat.format = glFormat.internalFormat; +#endif + } + else + { + const auto it = TextureMappingGLESExtensions.find(textureFormat); + if (it != TextureMappingGLESExtensions.cend()) + glFormat = it->second; + } + + return glFormat; +} diff --git a/xbmc/guilib/TextureGLES.h b/xbmc/guilib/TextureGLES.h index 4a9cb0502f882..92089506c50e0 100644 --- a/xbmc/guilib/TextureGLES.h +++ b/xbmc/guilib/TextureGLES.h @@ -12,6 +12,22 @@ #include "system_gl.h" +struct TextureFormat +{ + GLenum internalFormat{GL_FALSE}; + GLenum internalFormatSRGB{GL_FALSE}; + GLint format{GL_FALSE}; + GLenum type{GL_UNSIGNED_BYTE}; +}; + +struct TextureSwizzle +{ + GLint r{GL_RED}; + GLint g{GL_GREEN}; + GLint b{GL_BLUE}; + GLint a{GL_ALPHA}; +}; + class CGLESTexture : public CTexture { public: @@ -24,6 +40,11 @@ class CGLESTexture : public CTexture void BindToUnit(unsigned int unit) override; protected: + void SetSwizzle(bool swapRB); + void SwapBlueRedSwizzle(GLint& component); + TextureFormat GetFormatGLES20(KD_TEX_FMT textureFormat); + TextureFormat GetFormatGLES30(KD_TEX_FMT textureFormat); + GLuint m_texture = 0; bool m_isGLESVersion30orNewer{false}; };