From 423f9a414078ce8e6cf83f140e4c76d52276e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 6 Oct 2023 16:12:01 +0200 Subject: [PATCH] GL: implement PixelFormat mapping to unsized/luminance ES2 TextureFormat. Originally GL::hasTextureFormat() returned false on ES2 for PixelFormat::R8Unorm, RG8Unorm, RGB8Unorm and RGBA8Unorm because glTexStorage() didn't work with the matching Luminance, LuminanceAlpha, RGB and RGBA formats. But since the only ES2 platform is nowadays basically just WebGL 1, which has neither EXT_texture_rg nor EXT_texture_storage, this implicit failure made no sense and just made the textureFormat() (and the new genericPixelFormat() API) useless there. Now it maps to them, and it's up to the caller to make sure glTexStorage() doesn't get called with those, only glTexImage does. Furthermore, if formats from EXT_texture_rg are used, the genericPixelFormat() now also provides inverse mapping of them back to the generic PixelFormat. Before it was basically *no* ES2 TextureFormat that'd work with either of these, now it's all that have a (vaguely) corresponding PixelFormat. --- .../GL/Implementation/pixelFormatMapping.hpp | 32 +++++++++--- src/Magnum/GL/PixelFormat.cpp | 22 ++++++++ src/Magnum/GL/Test/PixelFormatTest.cpp | 50 +++++++++++++++++-- src/Magnum/PixelFormat.h | 21 +++++--- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/Magnum/GL/Implementation/pixelFormatMapping.hpp b/src/Magnum/GL/Implementation/pixelFormatMapping.hpp index c55504dbf..046ef107e 100644 --- a/src/Magnum/GL/Implementation/pixelFormatMapping.hpp +++ b/src/Magnum/GL/Implementation/pixelFormatMapping.hpp @@ -35,15 +35,26 @@ _c(R8Unorm, Red, UnsignedByte, R8) _c(RG8Unorm, RG, UnsignedByte, RG8) #else -_n(R8Unorm, Luminance, UnsignedByte) -_n(RG8Unorm, LuminanceAlpha, UnsignedByte) +/* Luminance / LuminanceAlpha isn't allowed in glTexStorage(). Usually though + there's either both the EXT_texture_rg and EXT_texture_storage extensions or + neither of them (such as in WebGL), and unconditionally failing for single- + and two-channel formats isn't desirable. */ +/** @todo ideally there would be some variant of this mapping for when + EXT_texture_rg is supported */ +_c(R8Unorm, Luminance, UnsignedByte, Luminance) +_c(RG8Unorm, LuminanceAlpha, UnsignedByte, LuminanceAlpha) #endif #ifndef MAGNUM_TARGET_GLES2 _c(RGB8Unorm, RGB, UnsignedByte, RGB8) _c(RGBA8Unorm, RGBA, UnsignedByte, RGBA8) #else -_n(RGB8Unorm, RGB, UnsignedByte) -_n(RGBA8Unorm, RGBA, UnsignedByte) +/* (Unsized) RGB / RGBA isn't allowed in glTexStorage(), however + unconditionally failing for for these in WebGL isn't really desirable + either. */ +/** @todo ideally there would be some variant of this mapping for when + EXT_texture_rg is supported */ +_c(RGB8Unorm, RGB, UnsignedByte, RGB) +_c(RGBA8Unorm, RGBA, UnsignedByte, RGBA) #endif #ifndef MAGNUM_TARGET_GLES2 _c(R8Snorm, Red, Byte, R8Snorm) @@ -79,8 +90,13 @@ _dn(RG8Srgb, LuminanceAlpha, UnsignedByte) _d(RGB8Srgb, RGB, UnsignedByte, SRGB8) _d(RGBA8Srgb, RGBA, UnsignedByte, SRGB8Alpha8) #else -_dn(RGB8Srgb, RGB, UnsignedByte) -_dn(RGBA8Srgb, RGBA, UnsignedByte) +/* (Unsized) SRGB / SRGBA isn't allowed in glTexStorage(), however + unconditionally failing for for these in WebGL isn't really desirable + either. */ +/** @todo ideally there would be some variant of this mapping for when + EXT_texture_rg is supported */ +_d(RGB8Srgb, RGB, UnsignedByte, SRGB) +_d(RGBA8Srgb, RGBA, UnsignedByte, SRGBAlpha) #endif #ifndef MAGNUM_TARGET_GLES2 _c(R8UI, RedInteger, UnsignedByte, R8UI) @@ -195,6 +211,8 @@ _n(Depth24Unorm, DepthComponent, UnsignedInt) #ifndef MAGNUM_TARGET_GLES2 _c(Depth32F, DepthComponent, Float, DepthComponent32F) #else +/* There isn't any possibility to have a depth / stencil texture on ES2, so not + even any unsized format */ _s(Depth32F) #endif #ifndef MAGNUM_TARGET_WEBGL @@ -211,6 +229,8 @@ _n(Depth24UnormStencil8UI, DepthStencil, UnsignedInt248) #ifndef MAGNUM_TARGET_GLES2 _c(Depth32FStencil8UI, DepthStencil, Float32UnsignedInt248Rev, Depth32FStencil8) #else +/* There isn't any possibility to have a depth / stencil texture on ES2, so not + even any unsized format */ _s(Depth32FStencil8UI) #endif #endif diff --git a/src/Magnum/GL/PixelFormat.cpp b/src/Magnum/GL/PixelFormat.cpp index e7ddc365d..119dde644 100644 --- a/src/Magnum/GL/PixelFormat.cpp +++ b/src/Magnum/GL/PixelFormat.cpp @@ -288,6 +288,17 @@ Containers::Optional genericPixelFormat(const PixelFormat f #undef _n #undef _d #undef _c + + #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* The mapping defaults to Luminance and LuminanceAlpha on ES2. + Recognize also the R and RG formats from EXT_texture_rg. */ + /** @todo ideally there would be some variant of the above mapping for + when EXT_texture_rg is supported, to have it consistent */ + case (UnsignedLong(PixelFormat::Red) << 32)|UnsignedLong(PixelType::UnsignedByte): + return Magnum::PixelFormat::R8Unorm; + case (UnsignedLong(PixelFormat::RG) << 32)|UnsignedLong(PixelType::UnsignedByte): + return Magnum::PixelFormat::RG8Unorm; + #endif } return {}; @@ -308,6 +319,17 @@ Containers::Optional genericPixelFormat(const TextureFormat #undef _d #undef _c + #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) + /* The mapping defaults to Luminance, LuminanceAlpha, RGB and RGBA on + ES2. Recognize also the sized formats from EXT_texture_rg. */ + /** @todo ideally there would be some variant of the above mapping for + when EXT_texture_rg is supported, to have it consistent */ + case TextureFormat::R8: return Magnum::PixelFormat::R8Unorm; + case TextureFormat::RG8: return Magnum::PixelFormat::RG8Unorm; + case TextureFormat::RGB8: return Magnum::PixelFormat::RGB8Unorm; + case TextureFormat::RGBA8: return Magnum::PixelFormat::RGBA8Unorm; + #endif + /* For compressed formats it returns NullOpt too instead of asserting, as -- compared to the generic-to-GL translation, which is O(1) -- the inverse mapping is potentially a linear lookup and forcing the diff --git a/src/Magnum/GL/Test/PixelFormatTest.cpp b/src/Magnum/GL/Test/PixelFormatTest.cpp index d475026af..1ab31990c 100644 --- a/src/Magnum/GL/Test/PixelFormatTest.cpp +++ b/src/Magnum/GL/Test/PixelFormatTest.cpp @@ -109,18 +109,58 @@ void PixelFormatTest::mapFormatTypeTextureFormat() { CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::RGBA8Unorm), PixelFormat::RGBA); CORRADE_COMPARE(pixelType(Magnum::PixelFormat::RGBA8Unorm), PixelType::UnsignedByte); CORRADE_COMPARE(genericPixelFormat(PixelFormat::RGB, PixelType::UnsignedByte), Magnum::PixelFormat::RGB8Unorm); - #ifndef MAGNUM_TARGET_GLES2 CORRADE_VERIFY(hasTextureFormat(Magnum::PixelFormat::RGBA8Unorm)); + #ifndef MAGNUM_TARGET_GLES2 CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::RGBA8Unorm), TextureFormat::RGBA8); CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB8), Magnum::PixelFormat::RGB8Unorm); #else - CORRADE_VERIFY(!hasTextureFormat(Magnum::PixelFormat::RGBA8Unorm)); - CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB), Containers::NullOpt); + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::RGBA8Unorm), TextureFormat::RGBA); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB), Magnum::PixelFormat::RGB8Unorm); + #endif + + /* No mapping for these */ + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB565), Containers::NullOpt); + #ifdef MAGNUM_TARGET_GLES2 + CORRADE_VERIFY(!hasTextureFormat(Magnum::PixelFormat::Depth32F)); #endif + /* sRGB formats have N:1 mapping, conversion back is losing the sRGB bit */ CORRADE_COMPARE(genericPixelFormat(pixelFormat(Magnum::PixelFormat::R8Srgb), pixelType(Magnum::PixelFormat::R8Srgb)), Magnum::PixelFormat::R8Unorm); CORRADE_COMPARE(genericPixelFormat(pixelFormat(Magnum::PixelFormat::RGBA8Srgb), pixelType(Magnum::PixelFormat::RGBA8Srgb)), Magnum::PixelFormat::RGBA8Unorm); + /* On ES2, forward PixelFormat mapping goes to luminance, but backwards + mapping from R and RG works too. For TextureFormat, forward mapping goes + to unsized formats and luminance (which aren't usable in glTexStorage() + then, only glTexImage()), but backwards mapping from sized formats works + too. */ + #ifdef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::R8Unorm), PixelFormat::Luminance); + CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::RG8Unorm), PixelFormat::LuminanceAlpha); + CORRADE_COMPARE(pixelType(Magnum::PixelFormat::R8Unorm), PixelType::UnsignedByte); + CORRADE_COMPARE(pixelType(Magnum::PixelFormat::RG8Unorm), PixelType::UnsignedByte); + CORRADE_COMPARE(genericPixelFormat(PixelFormat::Luminance, PixelType::UnsignedByte), Magnum::PixelFormat::R8Unorm); + CORRADE_COMPARE(genericPixelFormat(PixelFormat::LuminanceAlpha, PixelType::UnsignedByte), Magnum::PixelFormat::RG8Unorm); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_COMPARE(genericPixelFormat(PixelFormat::Red, PixelType::UnsignedByte), Magnum::PixelFormat::R8Unorm); + CORRADE_COMPARE(genericPixelFormat(PixelFormat::RG, PixelType::UnsignedByte), Magnum::PixelFormat::RG8Unorm); + #endif + + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::R8Unorm), TextureFormat::Luminance); + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::RG8Unorm), TextureFormat::LuminanceAlpha); + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::RGB8Unorm), TextureFormat::RGB); + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::RGBA8Unorm), TextureFormat::RGBA); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::Luminance), Magnum::PixelFormat::R8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::LuminanceAlpha), Magnum::PixelFormat::RG8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB), Magnum::PixelFormat::RGB8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGBA), Magnum::PixelFormat::RGBA8Unorm); + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_COMPARE(genericPixelFormat(TextureFormat::R8), Magnum::PixelFormat::R8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RG8), Magnum::PixelFormat::RG8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB8), Magnum::PixelFormat::RGB8Unorm); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGBA8), Magnum::PixelFormat::RGBA8Unorm); + #endif + #endif + /* This goes through the first 16 bits, which should be enough. Going through 32 bits takes 8 seconds, too much. */ UnsignedInt firstUnhandled = 0xffff; @@ -343,8 +383,8 @@ void PixelFormatTest::mapTextureFormatUnsupported() { std::ostringstream out; Error redirectError{&out}; - textureFormat(Magnum::PixelFormat::RGB8Unorm); - CORRADE_COMPARE(out.str(), "GL::textureFormat(): format PixelFormat::RGB8Unorm is not supported on this target\n"); + textureFormat(Magnum::PixelFormat::Depth32F); + CORRADE_COMPARE(out.str(), "GL::textureFormat(): format PixelFormat::Depth32F is not supported on this target\n"); #else std::ostringstream out; Error redirectError{&out}; diff --git a/src/Magnum/PixelFormat.h b/src/Magnum/PixelFormat.h index c1ccb1348..fcab00b5a 100644 --- a/src/Magnum/PixelFormat.h +++ b/src/Magnum/PixelFormat.h @@ -83,8 +83,10 @@ enum class PixelFormat: UnsignedInt { * Red component, normalized unsigned byte. * * Corresponds to @ref GL::PixelFormat::Red and - * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::R8; - * @ref Vk::PixelFormat::R8Unorm; + * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::R8 or + * alternatively @ref GL::PixelFormat::Luminance and + * @ref GL::TextureFormat::Luminance on unextended OpenGL 2.1 and OpenGL ES + * 2.0; @ref Vk::PixelFormat::R8Unorm; * @m_class{m-doc-external} [DXGI_FORMAT_R8_UNORM](https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format) * or @m_class{m-doc-external} [MTLPixelFormatR8Unorm](https://developer.apple.com/documentation/metal/mtlpixelformat/mtlpixelformatr8unorm?language=objc). * @m_keywords{DXGI_FORMAT_R8_UNORM MTLPixelFormatR8Unorm} @@ -95,8 +97,10 @@ enum class PixelFormat: UnsignedInt { * Red and green component, normalized unsigned byte. * * Corresponds to @ref GL::PixelFormat::RG and - * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::RG8; - * @ref Vk::PixelFormat::RG8Unorm; + * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::RG8 or + * alternatively @ref GL::PixelFormat::LuminanceAlpha and + * @ref GL::TextureFormat::LuminanceAlpha on unextended OpenGL 2.1 and + * OpenGL ES 2.0; @ref Vk::PixelFormat::RG8Unorm; * @m_class{m-doc-external} [DXGI_FORMAT_R8G8_UNORM](https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format) * or @m_class{m-doc-external} [MTLPixelFormatRG8Unorm](https://developer.apple.com/documentation/metal/mtlpixelformat/mtlpixelformatrg8unorm?language=objc). * @m_keywords{DXGI_FORMAT_R8G8_UNORM MTLPixelFormatRG8Unorm} @@ -108,7 +112,9 @@ enum class PixelFormat: UnsignedInt { * * Corresponds to @ref GL::PixelFormat::RGB and * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::RGB8 or - * @ref Vk::PixelFormat::RGB8Unorm. No 24-bit D3D or Metal equivalent. + * alternatively @ref GL::TextureFormat::RGB on unextended OpenGL 2.1 and + * OpenGL ES 2.0; @ref Vk::PixelFormat::RGB8Unorm. No 24-bit D3D or Metal + * equivalent. */ RGB8Unorm, @@ -116,8 +122,9 @@ enum class PixelFormat: UnsignedInt { * RGBA, normalized unsigned byte. * * Corresponds to @ref GL::PixelFormat::RGBA and - * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::RGBA8; - * @ref Vk::PixelFormat::RGBA8Unorm; + * @ref GL::PixelType::UnsignedByte, @ref GL::TextureFormat::RGBA8 or + * alternatively @ref GL::TextureFormat::RGBA on unextended OpenGL 2.1 and + * OpenGL ES 2.0; @ref Vk::PixelFormat::RGBA8Unorm; * @m_class{m-doc-external} [DXGI_FORMAT_R8G8B8A8_UNORM](https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format) * or @m_class{m-doc-external} [MTLPixelFormatRGBA8Unorm](https://developer.apple.com/documentation/metal/mtlpixelformat/mtlpixelformatrgba8unorm?language=objc). * @m_keywords{DXGI_FORMAT_R8G8B8A8_UNORM MTLPixelFormatRGBA8Unorm}