diff --git a/doc/changelog.dox b/doc/changelog.dox index 8413fedb1..0a216a322 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -606,6 +606,11 @@ See also: in @ref GL::Extensions for compatibility with older browsers. - Re-enabled @fn_gl{DrawRangeElements} for WebGL 2, this time hopefully for the last time (see [mosra/magnum#97](https://github.com/mosra/magnum/issues/97)) +- Added @ref GL::genericPixelFormat() and + @ref GL::genericCompressedPixelFormat() helpers for convenient mapping of + @ref GL::PixelFormat, @ref GL::PixelType, @ref GL::CompressedPixelFormat + and @ref GL::TextureFormat values back to the generic @ref PixelFormat and + @ref CompressedPixelFormat @subsubsection changelog-latest-changes-math Math library diff --git a/src/Magnum/DebugTools/Screenshot.cpp b/src/Magnum/DebugTools/Screenshot.cpp index c74fa9327..d6d034788 100644 --- a/src/Magnum/DebugTools/Screenshot.cpp +++ b/src/Magnum/DebugTools/Screenshot.cpp @@ -47,20 +47,9 @@ bool screenshot(PluginManager::Manager& manager, /* Get the implementation-specific color read format for given framebuffer */ const GL::PixelFormat format = framebuffer.implementationColorReadFormat(); const GL::PixelType type = framebuffer.implementationColorReadType(); - auto genericFormat = [](GL::PixelFormat format, GL::PixelType type) -> Containers::Optional { - #ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */ - #define _c(generic, glFormat, glType, glTextureFormat) if(format == GL::PixelFormat::glFormat && type == GL::PixelType::glType) return PixelFormat::generic; - #define _n(generic, glFormat, glType) if(format == GL::PixelFormat::glFormat && type == GL::PixelType::glType) return PixelFormat::generic; - #define _s(generic) return {}; - #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" - #undef _c - #undef _n - #undef _s - #endif - return {}; - }(format, type); + const Containers::Optional genericFormat = GL::genericPixelFormat(format, type); if(!genericFormat) { - Error{} << "DebugTools::screenshot(): can't map (" << Debug::nospace << format << Debug::nospace << "," << type << Debug::nospace << ") to a generic pixel format"; + Error{} << "DebugTools::screenshot(): can't map {" << Debug::nospace << format << Debug::nospace << "," << type << Debug::nospace << "} to a generic pixel format"; return false; } diff --git a/src/Magnum/DebugTools/Test/ScreenshotGLTest.cpp b/src/Magnum/DebugTools/Test/ScreenshotGLTest.cpp index 479b82798..4b4d590a7 100644 --- a/src/Magnum/DebugTools/Test/ScreenshotGLTest.cpp +++ b/src/Magnum/DebugTools/Test/ScreenshotGLTest.cpp @@ -244,9 +244,9 @@ void ScreenshotGLTest::unknownFormat() { CORRADE_VERIFY(!succeeded); if(framebuffer.implementationColorReadFormat() == GL::PixelFormat::RGBA) - CORRADE_COMPARE(out.str(), "DebugTools::screenshot(): can't map (GL::PixelFormat::RGBA, GL::PixelType::UnsignedShort565) to a generic pixel format\n"); + CORRADE_COMPARE(out.str(), "DebugTools::screenshot(): can't map {GL::PixelFormat::RGBA, GL::PixelType::UnsignedShort565} to a generic pixel format\n"); else - CORRADE_COMPARE(out.str(), "DebugTools::screenshot(): can't map (GL::PixelFormat::RGB, GL::PixelType::UnsignedShort565) to a generic pixel format\n"); + CORRADE_COMPARE(out.str(), "DebugTools::screenshot(): can't map {GL::PixelFormat::RGB, GL::PixelType::UnsignedShort565} to a generic pixel format\n"); } void ScreenshotGLTest::pluginLoadFailed() { diff --git a/src/Magnum/GL/Implementation/compressedPixelFormatMapping.hpp b/src/Magnum/GL/Implementation/compressedPixelFormatMapping.hpp index 043da4799..2d5f5931b 100644 --- a/src/Magnum/GL/Implementation/compressedPixelFormatMapping.hpp +++ b/src/Magnum/GL/Implementation/compressedPixelFormatMapping.hpp @@ -23,7 +23,11 @@ DEALINGS IN THE SOFTWARE. */ -/* See Magnum/GL/PixelFormat.cpp and Magnum/GL/Test/PixelFormatTest.cpp */ +/* See Magnum/GL/PixelFormat.cpp and Magnum/GL/Test/PixelFormatTest.cpp. _c() + is a mapping with the second element being both a CompressedPixelFormat and + a (compressed) TextureFormat as they share the same values. _s() denotes a + skipped value (so the enum numbering is preserved), _d() denotes a duplicate + _c() value, i.e. a N:1 mapping from a generic format. */ #ifdef _c _c(Bc1RGBUnorm, RGBS3tcDxt1) _c(Bc1RGBSrgb, SRGBS3tcDxt1) @@ -62,77 +66,77 @@ _c(Etc2RGB8A1Unorm, RGB8PunchthroughAlpha1Etc2) _c(Etc2RGB8A1Srgb, SRGB8PunchthroughAlpha1Etc2) _c(Etc2RGBA8Unorm, RGBA8Etc2Eac) _c(Etc2RGBA8Srgb, SRGB8Alpha8Etc2Eac) -_c(Astc4x4RGBAUnorm, RGBAAstc4x4) +_d(Astc4x4RGBAUnorm, RGBAAstc4x4) _c(Astc4x4RGBASrgb, SRGB8Alpha8Astc4x4) _c(Astc4x4RGBAF, RGBAAstc4x4) -_c(Astc5x4RGBAUnorm, RGBAAstc5x4) +_d(Astc5x4RGBAUnorm, RGBAAstc5x4) _c(Astc5x4RGBASrgb, SRGB8Alpha8Astc5x4) _c(Astc5x4RGBAF, RGBAAstc5x4) -_c(Astc5x5RGBAUnorm, RGBAAstc5x5) +_d(Astc5x5RGBAUnorm, RGBAAstc5x5) _c(Astc5x5RGBASrgb, SRGB8Alpha8Astc5x5) _c(Astc5x5RGBAF, RGBAAstc5x5) -_c(Astc6x5RGBAUnorm, RGBAAstc6x5) +_d(Astc6x5RGBAUnorm, RGBAAstc6x5) _c(Astc6x5RGBASrgb, SRGB8Alpha8Astc6x5) _c(Astc6x5RGBAF, RGBAAstc6x5) -_c(Astc6x6RGBAUnorm, RGBAAstc6x6) +_d(Astc6x6RGBAUnorm, RGBAAstc6x6) _c(Astc6x6RGBASrgb, SRGB8Alpha8Astc6x6) _c(Astc6x6RGBAF, RGBAAstc6x6) -_c(Astc8x5RGBAUnorm, RGBAAstc8x5) +_d(Astc8x5RGBAUnorm, RGBAAstc8x5) _c(Astc8x5RGBASrgb, SRGB8Alpha8Astc8x5) _c(Astc8x5RGBAF, RGBAAstc8x5) -_c(Astc8x6RGBAUnorm, RGBAAstc8x6) +_d(Astc8x6RGBAUnorm, RGBAAstc8x6) _c(Astc8x6RGBASrgb, SRGB8Alpha8Astc8x6) _c(Astc8x6RGBAF, RGBAAstc8x6) -_c(Astc8x8RGBAUnorm, RGBAAstc8x8) +_d(Astc8x8RGBAUnorm, RGBAAstc8x8) _c(Astc8x8RGBASrgb, SRGB8Alpha8Astc8x8) _c(Astc8x8RGBAF, RGBAAstc8x8) -_c(Astc10x5RGBAUnorm, RGBAAstc10x5) +_d(Astc10x5RGBAUnorm, RGBAAstc10x5) _c(Astc10x5RGBASrgb, SRGB8Alpha8Astc10x5) _c(Astc10x5RGBAF, RGBAAstc10x5) -_c(Astc10x6RGBAUnorm, RGBAAstc10x6) +_d(Astc10x6RGBAUnorm, RGBAAstc10x6) _c(Astc10x6RGBASrgb, SRGB8Alpha8Astc10x6) _c(Astc10x6RGBAF, RGBAAstc10x6) -_c(Astc10x8RGBAUnorm, RGBAAstc10x8) +_d(Astc10x8RGBAUnorm, RGBAAstc10x8) _c(Astc10x8RGBASrgb, SRGB8Alpha8Astc10x8) _c(Astc10x8RGBAF, RGBAAstc10x8) -_c(Astc10x10RGBAUnorm, RGBAAstc10x10) +_d(Astc10x10RGBAUnorm, RGBAAstc10x10) _c(Astc10x10RGBASrgb, SRGB8Alpha8Astc10x10) _c(Astc10x10RGBAF, RGBAAstc10x10) -_c(Astc12x10RGBAUnorm, RGBAAstc12x10) +_d(Astc12x10RGBAUnorm, RGBAAstc12x10) _c(Astc12x10RGBASrgb, SRGB8Alpha8Astc12x10) _c(Astc12x10RGBAF, RGBAAstc12x10) -_c(Astc12x12RGBAUnorm, RGBAAstc12x12) +_d(Astc12x12RGBAUnorm, RGBAAstc12x12) _c(Astc12x12RGBASrgb, SRGB8Alpha8Astc12x12) _c(Astc12x12RGBAF, RGBAAstc12x12) #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL) -_c(Astc3x3x3RGBAUnorm, RGBAAstc3x3x3) +_d(Astc3x3x3RGBAUnorm, RGBAAstc3x3x3) _c(Astc3x3x3RGBASrgb, SRGB8Alpha8Astc3x3x3) _c(Astc3x3x3RGBAF, RGBAAstc3x3x3) -_c(Astc4x3x3RGBAUnorm, RGBAAstc4x3x3) +_d(Astc4x3x3RGBAUnorm, RGBAAstc4x3x3) _c(Astc4x3x3RGBASrgb, SRGB8Alpha8Astc4x3x3) _c(Astc4x3x3RGBAF, RGBAAstc4x3x3) -_c(Astc4x4x3RGBAUnorm, RGBAAstc4x4x3) +_d(Astc4x4x3RGBAUnorm, RGBAAstc4x4x3) _c(Astc4x4x3RGBASrgb, SRGB8Alpha8Astc4x4x3) _c(Astc4x4x3RGBAF, RGBAAstc4x4x3) -_c(Astc4x4x4RGBAUnorm, RGBAAstc4x4x4) +_d(Astc4x4x4RGBAUnorm, RGBAAstc4x4x4) _c(Astc4x4x4RGBASrgb, SRGB8Alpha8Astc4x4x4) _c(Astc4x4x4RGBAF, RGBAAstc4x4x4) -_c(Astc5x4x4RGBAUnorm, RGBAAstc5x4x4) +_d(Astc5x4x4RGBAUnorm, RGBAAstc5x4x4) _c(Astc5x4x4RGBASrgb, SRGB8Alpha8Astc5x4x4) _c(Astc5x4x4RGBAF, RGBAAstc5x4x4) -_c(Astc5x5x4RGBAUnorm, RGBAAstc5x5x4) +_d(Astc5x5x4RGBAUnorm, RGBAAstc5x5x4) _c(Astc5x5x4RGBASrgb, SRGB8Alpha8Astc5x5x4) _c(Astc5x5x4RGBAF, RGBAAstc5x5x4) -_c(Astc5x5x5RGBAUnorm, RGBAAstc5x5x5) +_d(Astc5x5x5RGBAUnorm, RGBAAstc5x5x5) _c(Astc5x5x5RGBASrgb, SRGB8Alpha8Astc5x5x5) _c(Astc5x5x5RGBAF, RGBAAstc5x5x5) -_c(Astc6x5x5RGBAUnorm, RGBAAstc6x5x5) +_d(Astc6x5x5RGBAUnorm, RGBAAstc6x5x5) _c(Astc6x5x5RGBASrgb, SRGB8Alpha8Astc6x5x5) _c(Astc6x5x5RGBAF, RGBAAstc6x5x5) -_c(Astc6x6x5RGBAUnorm, RGBAAstc6x6x5) +_d(Astc6x6x5RGBAUnorm, RGBAAstc6x6x5) _c(Astc6x6x5RGBASrgb, SRGB8Alpha8Astc6x6x5) _c(Astc6x6x5RGBAF, RGBAAstc6x6x5) -_c(Astc6x6x6RGBAUnorm, RGBAAstc6x6x6) +_d(Astc6x6x6RGBAUnorm, RGBAAstc6x6x6) _c(Astc6x6x6RGBASrgb, SRGB8Alpha8Astc6x6x6) _c(Astc6x6x6RGBAF, RGBAAstc6x6x6) #else diff --git a/src/Magnum/GL/Implementation/pixelFormatMapping.hpp b/src/Magnum/GL/Implementation/pixelFormatMapping.hpp index 1fea0520a..c55504dbf 100644 --- a/src/Magnum/GL/Implementation/pixelFormatMapping.hpp +++ b/src/Magnum/GL/Implementation/pixelFormatMapping.hpp @@ -26,7 +26,10 @@ /* See Magnum/GL/PixelFormat.cpp, Magnum/GL/Test/PixelFormatTest.cpp and DebugTools/Screenshot.cpp. _c() is a mapping, _s() denotes a skipped value (so the enum numbering is preserved), _n() denotes a value where pixel - format mapping is defined, but texture format is not */ + format mapping is defined, but texture format is not; _d() denotes a + _c() value with duplicated format/type combination, i.e. a N:1 mapping from + a generic format, _dn() then a _n() value with duplicated format/type + combination. */ #ifdef _c #ifndef MAGNUM_TARGET_GLES2 _c(R8Unorm, Red, UnsignedByte, R8) @@ -58,23 +61,26 @@ _s(RGBA8Snorm) texture format tho. */ #ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_WEBGL -_c(R8Srgb, Red, UnsignedByte, SR8) -_c(RG8Srgb, RG, UnsignedByte, SRG8) +_d(R8Srgb, Red, UnsignedByte, SR8) +_d(RG8Srgb, RG, UnsignedByte, SRG8) #else -_n(R8Srgb, Red, UnsignedByte) -_n(RG8Srgb, RG, UnsignedByte) +_dn(R8Srgb, Red, UnsignedByte) +_dn(RG8Srgb, RG, UnsignedByte) #endif #else /* SLUMINANCE / SLUMINANCE_ALPHA texture formats not exposed */ -_n(R8Srgb, Luminance, UnsignedByte) -_n(RG8Srgb, LuminanceAlpha, UnsignedByte) +_dn(R8Srgb, Luminance, UnsignedByte) +_dn(RG8Srgb, LuminanceAlpha, UnsignedByte) #endif +/* Again, GL's pixel format doesn't distinguish between linear and sRGB, so + mapping is the same as in case of the Unorm types. It's encoded in the + texture format tho. */ #ifndef MAGNUM_TARGET_GLES2 -_c(RGB8Srgb, RGB, UnsignedByte, SRGB8) -_c(RGBA8Srgb, RGBA, UnsignedByte, SRGB8Alpha8) +_d(RGB8Srgb, RGB, UnsignedByte, SRGB8) +_d(RGBA8Srgb, RGBA, UnsignedByte, SRGB8Alpha8) #else -_n(RGB8Srgb, RGB, UnsignedByte) -_n(RGBA8Srgb, RGBA, UnsignedByte) +_dn(RGB8Srgb, RGB, UnsignedByte) +_dn(RGBA8Srgb, RGBA, UnsignedByte) #endif #ifndef MAGNUM_TARGET_GLES2 _c(R8UI, RedInteger, UnsignedByte, R8UI) diff --git a/src/Magnum/GL/PixelFormat.cpp b/src/Magnum/GL/PixelFormat.cpp index b21a215cd..e7ddc365d 100644 --- a/src/Magnum/GL/PixelFormat.cpp +++ b/src/Magnum/GL/PixelFormat.cpp @@ -25,8 +25,9 @@ #include "PixelFormat.h" -#include #include +#include +#include #include #include "Magnum/PixelFormat.h" @@ -41,24 +42,32 @@ constexpr struct { PixelFormat format; PixelType type; } FormatMapping[] { - #define _c(input, format, type, textureFormat) {PixelFormat::format, PixelType::type}, - /* GCC 4.8 doesn't like just a {} for default enum values */ #define _n(input, format, type) {PixelFormat::format, PixelType::type}, + #define _dn _n + #define _c(input, format, type, textureFormat) _n(input, format, type) + #define _d _c + /* GCC 4.8 doesn't like just a {} for default enum values */ #define _s(input) {PixelFormat{}, PixelType{}}, #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" #undef _s - #undef _n + #undef _d #undef _c + #undef _nd + #undef _n }; constexpr TextureFormat TextureFormatMapping[] { #define _c(input, format, type, textureFormat) TextureFormat::textureFormat, + #define _d _c /* GCC 4.8 doesn't like just a {} for default enum values */ - #define _n(input, format, type) TextureFormat{}, #define _s(input) TextureFormat{}, + #define _n(input, format, type) _s(input) + #define _dn _n #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" - #undef _s + #undef _dn #undef _n + #undef _s + #undef _d #undef _c }; #endif @@ -261,6 +270,57 @@ UnsignedInt pixelFormatSize(const PixelFormat format, const PixelType type) { CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ } +Containers::Optional genericPixelFormat(const PixelFormat format, PixelType type) { + /* Assuming a switch will get better optimized to some LUT than an if + cascade */ + switch((UnsignedLong(format) << 32)|UnsignedLong(type)) { + #define _n(input, format, type) \ + case (UnsignedLong(PixelFormat::format) << 32)| \ + UnsignedLong(PixelType::type): \ + return Magnum::PixelFormat::input; + #define _c(input, format, type, textureFormat) _n(input, format, type) + #define _d(input, format, type, textureFormat) + #define _dn(input, format, type) + #define _s(input) + #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" + #undef _s + #undef _dn + #undef _n + #undef _d + #undef _c + } + + return {}; +} + +Containers::Optional genericPixelFormat(const TextureFormat format) { + switch(format) { + #define _c(input, format, type, textureFormat) \ + case TextureFormat::textureFormat: return Magnum::PixelFormat::input; + #define _d _c + #define _n(input, format, type) + #define _dn(input, format, type) + #define _s(input) + #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" + #undef _s + #undef _dn + #undef _n + #undef _d + #undef _c + + /* 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 + user to check some isTextureFormatCompressed() first (which would do + another linear lookup) makes no sense from a perf PoV. Plus for + unknown formats it's unknown whether it's a compressed format or + not, and the function suddenly starting to assert when a format + becomes known isn't good for backwards compatibility. */ + default: + return {}; + } +} + #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, const PixelFormat value) { debug << "GL::PixelFormat" << Debug::nospace; @@ -402,9 +462,11 @@ namespace { having just a single table for both */ constexpr CompressedPixelFormat CompressedFormatMapping[] { #define _c(input, format) CompressedPixelFormat::format, + #define _d _c #define _s(input) CompressedPixelFormat{}, #include "Magnum/GL/Implementation/compressedPixelFormatMapping.hpp" #undef _s + #undef _d #undef _c }; #endif @@ -455,6 +517,38 @@ TextureFormat textureFormat(const Magnum::CompressedPixelFormat format) { return out; } +Containers::Optional genericCompressedPixelFormat(const CompressedPixelFormat format) { + switch(format) { + #define _c(input, format) \ + case CompressedPixelFormat::format: \ + return Magnum::CompressedPixelFormat::input; + #define _d(input, format) + #define _s(input) + #include "Magnum/GL/Implementation/compressedPixelFormatMapping.hpp" + #undef _s + #undef _d + #undef _c + + default: + return {}; + } +} + +Containers::Optional genericCompressedPixelFormat(const TextureFormat format) { + /* Enum values are the same between CompressedPixelFormat and + TextureFormat, so just casting and delegating. + + For uncompressed 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 user to + check some isTextureFormatCompressed() first (which would do another + linear lookup) makes no sense from a perf PoV. Plus for unknown formats + it's unknown whether it's a compressed format or not, and the function + suddenly starting to assert when a format becomes known isn't good for + backwards compatibility. */ + return genericCompressedPixelFormat(CompressedPixelFormat(UnsignedInt(format))); +} + Debug& operator<<(Debug& debug, const CompressedPixelFormat value) { debug << "GL::CompressedPixelFormat" << Debug::nospace; diff --git a/src/Magnum/GL/PixelFormat.h b/src/Magnum/GL/PixelFormat.h index 487f9c8ad..e782a1ea4 100644 --- a/src/Magnum/GL/PixelFormat.h +++ b/src/Magnum/GL/PixelFormat.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Enum @ref Magnum::GL::PixelFormat, @ref Magnum::GL::PixelType, @ref Magnum::GL::CompressedPixelFormat, function @ref Magnum::GL::hasPixelFormat(), @ref Magnum::GL::pixelFormat(), @ref Magnum::GL::pixelType(), @ref Magnum::GL::pixelFormatSize(), @ref Magnum::GL::hasCompressedPixelFormat(), @ref Magnum::GL::compressedPixelFormat() + * @brief Enum @ref Magnum::GL::PixelFormat, @ref Magnum::GL::PixelType, @ref Magnum::GL::CompressedPixelFormat, function @ref Magnum::GL::hasPixelFormat(), @ref Magnum::GL::pixelFormat(), @ref Magnum::GL::pixelType(), @ref Magnum::GL::pixelFormatSize(), @ref Magnum::GL::genericPixelFormat(), @ref Magnum::GL::hasCompressedPixelFormat(), @ref Magnum::GL::compressedPixelFormat(), @ref Magnum::GL::genericCompressedPixelFormat() */ #include @@ -648,6 +648,7 @@ value is expected to be valid. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref pixelFormat(), @ref pixelType(), @ref hasCompressedPixelFormat() */ MAGNUM_GL_EXPORT bool hasPixelFormat(Magnum::PixelFormat format); @@ -671,7 +672,9 @@ to query availability of given format. @gl_extension{EXT,texture_rg} being present or not. If you wish to use @ref PixelFormat::Red and @ref PixelFormat::RG instead, specify the GL-specific pixel format directly instead of using the generic enum. -@see @ref pixelType(), @ref compressedPixelFormat() +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref pixelType(), @ref compressedPixelFormat(), + @ref genericPixelFormat(PixelFormat, PixelType) */ MAGNUM_GL_EXPORT PixelFormat pixelFormat(Magnum::PixelFormat format); @@ -687,7 +690,10 @@ In case @ref isPixelFormatImplementationSpecific() returns @cpp false @ce for Not all generic pixel formats may be available on all targets and this function expects that given format is available on the target. Use @ref hasPixelFormat() to query availability of given format. -@see @ref pixelFormat(), @ref compressedPixelFormat() + +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref pixelFormat(), @ref compressedPixelFormat(), + @ref genericPixelFormat(PixelFormat, PixelType) */ MAGNUM_GL_EXPORT PixelType pixelType(Magnum::PixelFormat format, UnsignedInt extra = 0); @@ -708,6 +714,27 @@ CORRADE_DEPRECATED("use pixelFormatSize() instead") inline UnsignedInt pixelSize } #endif +/** +@brief Convert OpenGL pixel format and type combination to a generic pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given combination doesn't match any generic +pixel format. Otherwise the returned value will result in the same @p format +and @p type when passed back to @ref pixelFormat(Magnum::PixelFormat) and +@ref pixelType(Magnum::PixelFormat, UnsignedInt). + +An exception is sRGB formats --- those map to the same OpenGL format + type +combination, e.g. @ref Magnum::PixelFormat::RGBA8Unorm and +@relativeref{Magnum::PixelFormat,RGBA8Srgb} both result in +@ref GL::PixelFormat::RGBA + @ref GL::PixelType::UnsignedByte. This function +always maps the OpenGL format + type combination to the linear type, not sRGB. + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericCompressedPixelFormat(CompressedPixelFormat) +*/ +MAGNUM_GL_EXPORT Containers::Optional genericPixelFormat(PixelFormat format, PixelType type); + /** @debugoperatorenum{PixelFormat} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, PixelFormat value); @@ -2103,6 +2130,7 @@ expected to be valid. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref compressedPixelFormat(), @ref hasPixelFormat(), @ref hasTextureFormat() */ @@ -2120,10 +2148,34 @@ returns @ref compressedPixelFormatUnwrap() cast to @ref GL::CompressedPixelForma Not all generic pixel formats may be available on all targets and this function expects that given format is available on the target. Use @ref hasCompressedPixelFormat() to query availability of given format. -@see @ref pixelFormat(), @ref textureFormat() + +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref pixelFormat(), @ref textureFormat(), + @ref genericCompressedPixelFormat(CompressedPixelFormat) */ MAGNUM_GL_EXPORT CompressedPixelFormat compressedPixelFormat(Magnum::CompressedPixelFormat format); +/** +@brief Convert OpenGL compressed pixel format to a generic compressed pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given format doesn't match any generic +pixel format. Otherwise the returned value will result in the same @p format +when passed back to @ref compressedPixelFormat(Magnum::CompressedPixelFormat). + +An exception is ASTC float and normalized formats --- those map to the same +OpenGL format, e.g. @ref Magnum::CompressedPixelFormat::Astc4x4RGBAUnorm and +@relativeref{Magnum::CompressedPixelFormat,Astc4x4RGBAF} both result in +@ref GL::CompressedPixelFormat::RGBAAstc4x4. To avoid potential information +loss, this function always maps the OpenGL ASTC format back to the float +variant. + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericPixelFormat(PixelFormat, PixelType) +*/ +MAGNUM_GL_EXPORT Containers::Optional genericCompressedPixelFormat(CompressedPixelFormat format); + /** @debugoperatorenum{CompressedPixelFormat} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, CompressedPixelFormat value); diff --git a/src/Magnum/GL/Test/PixelFormatTest.cpp b/src/Magnum/GL/Test/PixelFormatTest.cpp index 2e4227346..d475026af 100644 --- a/src/Magnum/GL/Test/PixelFormatTest.cpp +++ b/src/Magnum/GL/Test/PixelFormatTest.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -50,6 +51,7 @@ struct PixelFormatTest: TestSuite::Tester { void mapTextureFormatImplementationSpecific(); void mapTextureFormatUnsupported(); void mapTextureFormatInvalid(); + void mapGenericFormatUnsupported(); void size(); void sizeInvalid(); @@ -61,6 +63,7 @@ struct PixelFormatTest: TestSuite::Tester { void mapCompressedTextureFormatImplementationSpecific(); void mapCompressedTextureFormatUnsupported(); void mapCompressedTextureFormatInvalid(); + void mapGenericCompressedFormatUnsupported(); void debugPixelFormat(); void debugPixelType(); @@ -80,6 +83,7 @@ PixelFormatTest::PixelFormatTest() { &PixelFormatTest::mapTextureFormatImplementationSpecific, &PixelFormatTest::mapTextureFormatUnsupported, &PixelFormatTest::mapTextureFormatInvalid, + &PixelFormatTest::mapGenericFormatUnsupported, &PixelFormatTest::size, &PixelFormatTest::sizeInvalid, @@ -91,6 +95,7 @@ PixelFormatTest::PixelFormatTest() { &PixelFormatTest::mapCompressedTextureFormatImplementationSpecific, &PixelFormatTest::mapCompressedTextureFormatUnsupported, &PixelFormatTest::mapCompressedTextureFormatInvalid, + &PixelFormatTest::mapGenericCompressedFormatUnsupported, &PixelFormatTest::debugPixelFormat, &PixelFormatTest::debugPixelType, @@ -103,12 +108,18 @@ void PixelFormatTest::mapFormatTypeTextureFormat() { CORRADE_VERIFY(hasPixelFormat(Magnum::PixelFormat::RGBA8Unorm)); 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)); 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); #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); /* This goes through the first 16 bits, which should be enough. Going through 32 bits takes 8 seconds, too much. */ @@ -134,11 +145,51 @@ void PixelFormatTest::mapFormatTypeTextureFormat() { CORRADE_VERIFY(hasPixelFormat(Magnum::PixelFormat::format)); \ CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::format), Magnum::GL::PixelFormat::expectedFormat); \ CORRADE_COMPARE(pixelType(Magnum::PixelFormat::format), Magnum::GL::PixelType::expectedType); \ + CORRADE_COMPARE(genericPixelFormat(Magnum::GL::PixelFormat::expectedFormat, Magnum::GL::PixelType::expectedType), Magnum::PixelFormat::format); \ CORRADE_VERIFY(hasTextureFormat(Magnum::PixelFormat::format)); \ CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::format), Magnum::GL::TextureFormat::expectedTextureFormat); \ + CORRADE_COMPARE(genericPixelFormat(Magnum::GL::TextureFormat::expectedTextureFormat), Magnum::PixelFormat::format); \ + ++nextHandled; \ + continue; + /* For duplicate format/type mappings compared to _c() it only + checks the forward mapping and genericPixelFormat() from a + TextureFormat. The duplicate mapping is tested in the touchstone + verification above. */ + #define _d(format, expectedFormat, expectedType, expectedTextureFormat) \ + case Magnum::PixelFormat::format: \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xffff); \ + CORRADE_VERIFY(hasPixelFormat(Magnum::PixelFormat::format)); \ + CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::format), Magnum::GL::PixelFormat::expectedFormat); \ + CORRADE_COMPARE(pixelType(Magnum::PixelFormat::format), Magnum::GL::PixelType::expectedType); \ + CORRADE_VERIFY(hasTextureFormat(Magnum::PixelFormat::format)); \ + CORRADE_COMPARE(textureFormat(Magnum::PixelFormat::format), Magnum::GL::TextureFormat::expectedTextureFormat); \ + CORRADE_COMPARE(genericPixelFormat(Magnum::GL::TextureFormat::expectedTextureFormat), Magnum::PixelFormat::format); \ ++nextHandled; \ continue; #define _n(format, expectedFormat, expectedType) \ + case Magnum::PixelFormat::format: { \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xffff); \ + CORRADE_VERIFY(hasPixelFormat(Magnum::PixelFormat::format)); \ + CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::format), Magnum::GL::PixelFormat::expectedFormat); \ + CORRADE_COMPARE(pixelType(Magnum::PixelFormat::format), Magnum::GL::PixelType::expectedType); \ + CORRADE_COMPARE(genericPixelFormat(Magnum::GL::PixelFormat::expectedFormat, Magnum::GL::PixelType::expectedType), Magnum::PixelFormat::format); \ + CORRADE_VERIFY(!hasTextureFormat(Magnum::PixelFormat::format)); \ + std::ostringstream out; \ + { /* Redirected otherwise graceful assert would abort */ \ + Error redirectError{&out}; \ + textureFormat(Magnum::PixelFormat::format); \ + } \ + Debug{Debug::Flag::NoNewlineAtTheEnd} << out.str(); \ + ++nextHandled; \ + continue; \ + } + /* For duplicate format/type mappings compared to _n() it only + checks the forward mapping and genericPixelFormat() from a + TextureFormat. The duplicate mapping is tested in the touchstone + verification above. */ + #define _dn(format, expectedFormat, expectedType) \ case Magnum::PixelFormat::format: { \ CORRADE_COMPARE(nextHandled, i); \ CORRADE_COMPARE(firstUnhandled, 0xffff); \ @@ -175,6 +226,7 @@ void PixelFormatTest::mapFormatTypeTextureFormat() { #include "Magnum/GL/Implementation/pixelFormatMapping.hpp" #undef _s #undef _n + #undef _d #undef _c } #ifdef CORRADE_TARGET_GCC @@ -319,6 +371,18 @@ void PixelFormatTest::mapTextureFormatInvalid() { "GL::textureFormat(): invalid format PixelFormat(0x123)\n"); } +void PixelFormatTest::mapGenericFormatUnsupported() { + /* These don't have any generic equivalent yet */ + #ifndef MAGNUM_TARGET_WEBGL + CORRADE_COMPARE(genericPixelFormat(PixelFormat::BGRA, PixelType::UnsignedByte), Containers::NullOpt); + #endif + CORRADE_COMPARE(genericPixelFormat(PixelFormat::RGBA, PixelType::UnsignedShort565), Containers::NullOpt); + CORRADE_COMPARE(genericPixelFormat(TextureFormat::RGB565), Containers::NullOpt); + /* For compressed texture formats it returns NullOpt too, instead of + asserting. See comment in the source for reasons. */ + CORRADE_COMPARE(genericPixelFormat(TextureFormat::CompressedR11Eac), Containers::NullOpt); +} + void PixelFormatTest::size() { #ifndef MAGNUM_TARGET_GLES CORRADE_COMPARE(pixelFormatSize(PixelFormat::RGB, PixelType::UnsignedByte332), 1); @@ -350,6 +414,9 @@ void PixelFormatTest::mapCompressedFormatTextureFormat() { CORRADE_COMPARE(compressedPixelFormat(Magnum::CompressedPixelFormat::Bc1RGBAUnorm), CompressedPixelFormat::RGBAS3tcDxt1); CORRADE_VERIFY(hasTextureFormat(Magnum::CompressedPixelFormat::Astc8x8RGBASrgb)); CORRADE_COMPARE(textureFormat(Magnum::CompressedPixelFormat::Astc8x8RGBASrgb), TextureFormat::CompressedSRGB8Alpha8Astc8x8); + /* ASTC Unorm formats have N:1 mapping, conversion back is losing the + Unorm/F distinction */ + CORRADE_COMPARE(genericCompressedPixelFormat(compressedPixelFormat(Magnum::CompressedPixelFormat::Astc4x4RGBAUnorm)), Magnum::CompressedPixelFormat::Astc4x4RGBAF); /* This goes through the first 16 bits, which should be enough. Going through 32 bits takes 8 seconds, too much. */ @@ -369,6 +436,21 @@ void PixelFormatTest::mapCompressedFormatTextureFormat() { #endif switch(format) { #define _c(format, expectedFormat) \ + case Magnum::CompressedPixelFormat::format: \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xffff); \ + CORRADE_VERIFY(hasCompressedPixelFormat(Magnum::CompressedPixelFormat::format)); \ + CORRADE_COMPARE(genericCompressedPixelFormat(Magnum::GL::CompressedPixelFormat::expectedFormat), Magnum::CompressedPixelFormat::format); \ + CORRADE_VERIFY(hasTextureFormat(Magnum::CompressedPixelFormat::format)); \ + CORRADE_COMPARE(compressedPixelFormat(Magnum::CompressedPixelFormat::format), Magnum::GL::CompressedPixelFormat::expectedFormat); \ + CORRADE_COMPARE(textureFormat(Magnum::CompressedPixelFormat::format), Magnum::GL::TextureFormat::Compressed ## expectedFormat); \ + CORRADE_COMPARE(genericCompressedPixelFormat(Magnum::GL::TextureFormat::Compressed ## expectedFormat), Magnum::CompressedPixelFormat::format); \ + ++nextHandled; \ + continue; + /* For duplicate mappings compared to _c() it only checks the + forward mapping. The duplicate mapping is tested in the + touchstone verification above */ + #define _d(format, expectedFormat) \ case Magnum::CompressedPixelFormat::format: \ CORRADE_COMPARE(nextHandled, i); \ CORRADE_COMPARE(firstUnhandled, 0xffff); \ @@ -396,6 +478,7 @@ void PixelFormatTest::mapCompressedFormatTextureFormat() { } #include "Magnum/GL/Implementation/compressedPixelFormatMapping.hpp" #undef _s + #undef _d #undef _c } #ifdef CORRADE_TARGET_GCC @@ -501,6 +584,17 @@ void PixelFormatTest::mapCompressedTextureFormatInvalid() { "GL::textureFormat(): invalid format CompressedPixelFormat(0x123)\n"); } +void PixelFormatTest::mapGenericCompressedFormatUnsupported() { + /* Generic formats don't have any generic equivalent yet (heh) */ + #ifndef MAGNUM_TARGET_GLES + CORRADE_COMPARE(genericCompressedPixelFormat(CompressedPixelFormat::Red), Containers::NullOpt); + CORRADE_COMPARE(genericCompressedPixelFormat(TextureFormat::CompressedRed), Containers::NullOpt); + #endif + /* For uncompressed texture formats it returns NullOpt too, instead of + asserting. See comment in the source for reasons. */ + CORRADE_COMPARE(genericCompressedPixelFormat(TextureFormat::RGB), Containers::NullOpt); +} + void PixelFormatTest::debugPixelFormat() { std::ostringstream out; diff --git a/src/Magnum/GL/TextureFormat.cpp b/src/Magnum/GL/TextureFormat.cpp index 744f7c50f..c436070bf 100644 --- a/src/Magnum/GL/TextureFormat.cpp +++ b/src/Magnum/GL/TextureFormat.cpp @@ -34,9 +34,11 @@ namespace Magnum { namespace GL { -/* the textureFormat() and hasTextureFormat() utilities share mapping tables - with pixelFormat() / hasPixelFormat() so are defined in PixelFormat.cpp - instead (and tested there too) */ +/* compressed textureFormat(), hasTextureFormat() and + genericCompressedPixelFormat(TextureFormat) utilities share mapping tables + with compressedPixelFormat(), hasCompressedPixelFormat() and + genericCompressedPixelFormat(CompressedPixelFormat) so are defined in + PixelFormat.cpp instead (and tested there too) */ #ifndef DOXYGEN_GENERATING_OUTPUT Debug& operator<<(Debug& debug, const TextureFormat value) { diff --git a/src/Magnum/GL/TextureFormat.h b/src/Magnum/GL/TextureFormat.h index 533bd8a59..424245c17 100644 --- a/src/Magnum/GL/TextureFormat.h +++ b/src/Magnum/GL/TextureFormat.h @@ -2625,6 +2625,7 @@ and other format-specific extensions are supported. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref textureFormat(), @ref hasPixelFormat(), @ref hasCompressedPixelFormat() */ @@ -2647,7 +2648,9 @@ manually based on whether @ref Texture::setImage() or @ref Texture::setStorage() is used and whether @gl_extension{EXT,texture_storage} and other format-specific extensions are supported. -@see @ref pixelType(), @ref compressedPixelFormat() +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref pixelType(), @ref compressedPixelFormat(), + @ref genericPixelFormat(TextureFormat) */ MAGNUM_GL_EXPORT TextureFormat textureFormat(Magnum::PixelFormat format); @@ -2669,6 +2672,7 @@ performed. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref textureFormat(), @ref hasCompressedPixelFormat(), @ref hasPixelFormat() */ @@ -2688,12 +2692,51 @@ an implementation-specific pixel format to an OpenGL texture format can't be performed. Not all generic pixel formats may be available on all targets and this function -expects that given format is available on the target. Use @ref hasTextureFormat() -to query availability of given format. -@see @ref compressedPixelFormat(), @ref pixelFormat() +expects that given format is available on the target. Use +@ref hasTextureFormat() to query availability of given format. + +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref compressedPixelFormat(), @ref pixelFormat(), + @ref genericCompressedPixelFormat(TextureFormat) */ MAGNUM_GL_EXPORT TextureFormat textureFormat(Magnum::CompressedPixelFormat format); +/** +@brief Convert OpenGL texture format to a generic pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given format is compressed or if it doesn't +match any generic pixel format. Otherwise the returned value will result in the +same @p format when passed back to @ref textureFormat(Magnum::PixelFormat). + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericCompressedPixelFormat(TextureFormat) +*/ +MAGNUM_GL_EXPORT Containers::Optional genericPixelFormat(TextureFormat format); + +/** +@brief Convert OpenGL compressed texture format to a generic compressed pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given format is not compressed or if it +doesn't match any generic compressed pixel format. Otherwise the returned value +will result in the same @p format when passed back to +@ref textureFormat(Magnum::CompressedPixelFormat). + +An exception is ASTC float and normalized formats --- those map to the same +OpenGL format, e.g. @ref Magnum::CompressedPixelFormat::Astc4x4RGBAUnorm and +@relativeref{Magnum::CompressedPixelFormat,Astc4x4RGBAF} both result in +@ref GL::TextureFormat::CompressedRGBAAstc4x4. To avoid potential information +loss, this function always maps the OpenGL ASTC format back to the float +variant. + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericPixelFormat(TextureFormat) +*/ +MAGNUM_GL_EXPORT Containers::Optional genericCompressedPixelFormat(TextureFormat format); + /** @debugoperatorenum{TextureFormat} @m_since{2019,10} diff --git a/src/Magnum/PixelFormat.h b/src/Magnum/PixelFormat.h index 759bf66cc..c1ccb1348 100644 --- a/src/Magnum/PixelFormat.h +++ b/src/Magnum/PixelFormat.h @@ -48,17 +48,20 @@ Can act also as a wrapper for implementation-specific pixel format values using generic and implementation-specific formats can be done using @ref isPixelFormatImplementationSpecific(). -In case of OpenGL, corresponds to @ref GL::PixelFormat and @ref GL::PixelType -and is convertible to them using @ref GL::pixelFormat() and -@ref GL::pixelType(). See documentation of each value for more information -about the mapping. Note that not every format is available on all targets, use -@ref GL::hasPixelFormat() to check for its presence. - -In case of Vulkan, corresponds to @ref Vk::PixelFormat and is convertible -to it using @ref Vk::pixelFormat(Magnum::PixelFormat). See documentation of -each value for more information about the mapping. Note that not every format -may be available, use @ref Vk::hasPixelFormat(Magnum::PixelFormat) to check for -its presence. +In case of OpenGL, corresponds to @ref GL::PixelFormat, @ref GL::PixelType +and uncompressed @ref GL::TextureFormat values, is convertible to them using +@ref GL::pixelFormat(), @ref GL::pixelType() and @ref GL::textureFormat(), and +an inverse mapping can be done via @ref GL::genericPixelFormat(PixelFormat, PixelType) +and @ref GL::genericPixelFormat(TextureFormat). See documentation of each value +for more information about the mapping. Note that not every format is available +on all targets, use @ref GL::hasPixelFormat() to check for its presence. + +In case of Vulkan, corresponds to uncompressed @ref Vk::PixelFormat values, is +convertible to it using @ref Vk::pixelFormat(Magnum::PixelFormat), and an +inverse mapping can be done via @ref Vk::genericPixelFormat(). See +documentation of each value for more information about the mapping. Note that +not every format may be available, use +@ref Vk::hasPixelFormat(Magnum::PixelFormat) to check for its presence. For D3D, corresponds to @m_class{m-doc-external} [DXGI_FORMAT](https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format) and import is provided by the @ref Trade::DdsImporter "DdsImporter" plugin; for @@ -982,17 +985,22 @@ Can act also as a wrapper for implementation-specific pixel format values using Distinction between generic and implementation-specific formats can be done using @ref isCompressedPixelFormatImplementationSpecific(). -In case of OpenGL, corresponds to @ref GL::CompressedPixelFormat and is -convertible to it using @ref GL::compressedPixelFormat(). See documentation of +In case of OpenGL, corresponds to @ref GL::CompressedPixelFormat and compressed +@ref GL::TextureFormat values, is convertible to them using +@ref GL::compressedPixelFormat() and @ref GL::textureFormat(), and an inverse +mapping can be done via @ref GL::genericCompressedPixelFormat(CompressedPixelFormat) +and @ref GL::genericCompressedPixelFormat(TextureFormat). See documentation of each value for more information about the mapping. Note that not every format is available on all targets, use @ref GL::hasCompressedPixelFormat() to check for its presence. -In case of Vulkan, corresponds to @ref Vk::PixelFormat and is convertible to it -using @ref Vk::pixelFormat(Magnum::CompressedPixelFormat). See documentation -of each value for more information about the mapping. Note that not every -format may be available, use @ref Vk::hasPixelFormat(Magnum::CompressedPixelFormat) -to check for its presence. +In case of Vulkan, corresponds to compressed @ref Vk::PixelFormat values, is +convertible to it using @ref Vk::pixelFormat(Magnum::CompressedPixelFormat), +and an inverse mapping can be done via @ref Vk::genericCompressedPixelFormat(). +See documentation of each value for more information about the mapping. Note +that not every format may be available, use +@ref Vk::hasPixelFormat(Magnum::CompressedPixelFormat) to check for its +presence. For D3D, corresponds to @m_class{m-doc-external} [DXGI_FORMAT](https://docs.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format) and import is provided by the @ref Trade::DdsImporter "DdsImporter" plugin; for diff --git a/src/Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp b/src/Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp index 2ce7830e6..e7dfc4f7f 100644 --- a/src/Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp +++ b/src/Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp @@ -23,7 +23,11 @@ DEALINGS IN THE SOFTWARE. */ -/* See Magnum/Vk/Enums.cpp and Magnum/Vk/Test/EnumsTest.cpp */ +/* See Magnum/Vk/Enums.cpp and Magnum/Vk/Test/EnumsTest.cpp. _c() is a mapping, + _s() denotes a skipped value (so the enum numbering is preserved), _d() + denotes a _c() value with duplicated result, i.e. a N:1 mapping from a + generic format. The _c2() is just a shortand for the input and output name + being the same. */ #ifdef _c #define _c2(input) _c(input, input) _c2(Bc1RGBUnorm) @@ -128,12 +132,12 @@ _s(Astc6x6x6RGBASrgb) //, ASTC_6x6x6_SRGB_BLOCK) _s(Astc6x6x6RGBAF) //, ASTC_6x6x6_SFLOAT_BLOCK_EXT) /* https://github.com/KhronosGroup/Vulkan-Docs/issues/512 */ -_c(PvrtcRGB2bppUnorm, PvrtcRGBA2bppUnorm) -_c(PvrtcRGB2bppSrgb, PvrtcRGBA2bppSrgb) +_d(PvrtcRGB2bppUnorm, PvrtcRGBA2bppUnorm) +_d(PvrtcRGB2bppSrgb, PvrtcRGBA2bppSrgb) _c2(PvrtcRGBA2bppUnorm) _c2(PvrtcRGBA2bppSrgb) -_c(PvrtcRGB4bppUnorm, PvrtcRGBA4bppUnorm) -_c(PvrtcRGB4bppSrgb, PvrtcRGBA4bppSrgb) +_d(PvrtcRGB4bppUnorm, PvrtcRGBA4bppUnorm) +_d(PvrtcRGB4bppSrgb, PvrtcRGBA4bppSrgb) _c2(PvrtcRGBA4bppUnorm) _c2(PvrtcRGBA4bppSrgb) #undef _c2 diff --git a/src/Magnum/Vk/PixelFormat.cpp b/src/Magnum/Vk/PixelFormat.cpp index b50d83413..9d01b0a8a 100644 --- a/src/Magnum/Vk/PixelFormat.cpp +++ b/src/Magnum/Vk/PixelFormat.cpp @@ -26,6 +26,7 @@ #include "PixelFormat.h" #include +#include #include #include "Magnum/PixelFormat.h" @@ -46,9 +47,11 @@ constexpr PixelFormat PixelFormatMapping[] { constexpr PixelFormat CompressedPixelFormatMapping[] { /* GCC 4.8 doesn't like just a {} for default enum values */ #define _c(input, format) PixelFormat::Compressed ## format, + #define _d _c #define _s(input) PixelFormat{}, #include "Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp" #undef _s + #undef _d #undef _c }; @@ -245,4 +248,50 @@ PixelFormat pixelFormat(const Magnum::CompressedPixelFormat format) { return out; } +Containers::Optional genericPixelFormat(const PixelFormat format) { + switch(format) { + #define _c(format) \ + case PixelFormat::format: \ + return Magnum::PixelFormat::format; + #include "Magnum/Vk/Implementation/pixelFormatMapping.hpp" + #undef _c + + /* For compressed formats it returns NullOpt too instead of asserting, + as -- compared to the generic-to-Vk translation, which is O(1) -- + the inverse mapping is potentially a linear lookup and forcing the + user to check some isPixelFormatCompressed() first (which would do + another linear lookup) makes no sense from a perf PoV. Plus for + unknown formats it's unknown whether it's a compressed format or + not, and the function suddenly starting to assert when a format + becomes known isn't good for backwards compatibility. */ + default: + return {}; + } +} + +Containers::Optional genericCompressedPixelFormat(const PixelFormat format) { + switch(format) { + #define _c(input, format) \ + case PixelFormat::Compressed ## format: \ + return Magnum::CompressedPixelFormat::input; + #define _d(input, format) + #define _s(input) + #include "Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp" + #undef _s + #undef _d + #undef _c + + /* For uncompressed 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 user + to check some isTextureFormatCompressed() first (which would do + another linear lookup) makes no sense from a perf PoV. Plus for + unknown formats it's unknown whether it's a compressed format or not, + and the function suddenly starting to assert when a format becomes + known isn't good for backwards compatibility. */ + default: + return {}; + } +} + }} diff --git a/src/Magnum/Vk/PixelFormat.h b/src/Magnum/Vk/PixelFormat.h index 971061c05..4912e5b10 100644 --- a/src/Magnum/Vk/PixelFormat.h +++ b/src/Magnum/Vk/PixelFormat.h @@ -30,7 +30,7 @@ #include "Magnum/Vk/visibility.h" /** @file - * @brief Enum @ref Magnum::Vk::PixelFormat, function @ref Magnum::Vk::hasPixelFormat(), @ref Magnum::Vk::pixelFormat() + * @brief Enum @ref Magnum::Vk::PixelFormat, function @ref Magnum::Vk::hasPixelFormat(), @ref Magnum::Vk::pixelFormat(), @ref Magnum::Vk::genericPixelFormat(), @ref Magnum::Vk::genericCompressedPixelFormat() * @m_since_latest */ @@ -1413,6 +1413,7 @@ be valid. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref pixelFormat(Magnum::PixelFormat) */ MAGNUM_VK_EXPORT bool hasPixelFormat(Magnum::PixelFormat format); @@ -1430,6 +1431,7 @@ expected to be valid. extension. Such check is outside of the scope of this function and you are expected to verify extension availability before using such format. +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. @see @ref pixelFormat(Magnum::CompressedPixelFormat) */ MAGNUM_VK_EXPORT bool hasPixelFormat(Magnum::CompressedPixelFormat format); @@ -1447,6 +1449,9 @@ cast to @ref PixelFormat. Not all generic pixel formats have a Vulkan equivalent and this function expects that given format is available. Use @ref hasPixelFormat(Magnum::PixelFormat) to query availability of given format. + +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref genericPixelFormat(PixelFormat) */ MAGNUM_VK_EXPORT PixelFormat pixelFormat(Magnum::PixelFormat format); @@ -1462,11 +1467,50 @@ assumes @p format stores a Vulkan-specific format and returns Not all generic pixel formats have a Vulkan equivalent and this function expects that given format is available. Use -@ref hasPixelFormat(Magnum::CompressedPixelFormat) to query availability of given -format. +@ref hasPixelFormat(Magnum::CompressedPixelFormat) to query availability of +given format. + +The mapping operation is done with an @f$ \mathcal{O}(1) @f$ complexity. +@see @ref genericCompressedPixelFormat(PixelFormat) */ MAGNUM_VK_EXPORT PixelFormat pixelFormat(Magnum::CompressedPixelFormat format); +/** +@brief Convert Vulkan pixel format to a generic pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given format is compressed or if it doesn't +match any generic pixel format. Otherwise the returned value will result in the +same @p format when passed back to @ref pixelFormat(Magnum::PixelFormat). + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericCompressedPixelFormat(PixelFormat) +*/ +MAGNUM_VK_EXPORT Containers::Optional genericPixelFormat(PixelFormat format); + +/** +@brief Convert Vulkan compressed pixel format to a generic compressed pixel format +@m_since_latest + +Returns @ref Containers::NullOpt if given format is not compressed or if it +doesn't match any generic compressed pixel format. Otherwise the returned value +will result in the same @p format when passed back to +@ref pixelFormat(Magnum::CompressedPixelFormat). + +An exception is PVRTC formats --- the RGB and RGBA variants map to the same +Vulkan format, e.g. @ref Magnum::CompressedPixelFormat::PvrtcRGB2bppSrgb and +@relativeref{Magnum::CompressedPixelFormat,PvrtcRGBA2bppSrgb} both result in +@ref Vk::PixelFormat::CompressedPvrtcRGBA2bppSrgb. To avoid potential +information loss, this function always maps the Vulkan PVRTC formats back to +the RGBA variants. + +Unlike mapping *from* a generic pixel format, the inverse operation is done +with an @f$ \mathcal{O}(n) @f$ complexity. +@see @ref genericPixelFormat(PixelFormat) +*/ +MAGNUM_VK_EXPORT Containers::Optional genericCompressedPixelFormat(PixelFormat format); + }} #endif diff --git a/src/Magnum/Vk/Test/PixelFormatTest.cpp b/src/Magnum/Vk/Test/PixelFormatTest.cpp index 0c9e5b798..280d62718 100644 --- a/src/Magnum/Vk/Test/PixelFormatTest.cpp +++ b/src/Magnum/Vk/Test/PixelFormatTest.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -39,11 +40,13 @@ struct PixelFormatTest: TestSuite::Tester { void mapImplementationSpecific(); void mapUnsupported(); void mapInvalid(); + void mapGenericUnsupported(); void mapCompressed(); void mapCompressedImplementationSpecific(); void mapCompressedUnsupported(); void mapCompressedInvalid(); + void mapGenericCompressedUnsupported(); void debug(); }; @@ -53,11 +56,13 @@ PixelFormatTest::PixelFormatTest() { &PixelFormatTest::mapImplementationSpecific, &PixelFormatTest::mapUnsupported, &PixelFormatTest::mapInvalid, + &PixelFormatTest::mapGenericUnsupported, &PixelFormatTest::mapCompressed, &PixelFormatTest::mapCompressedImplementationSpecific, &PixelFormatTest::mapCompressedUnsupported, &PixelFormatTest::mapCompressedInvalid, + &PixelFormatTest::mapGenericCompressedUnsupported, &PixelFormatTest::debug}); } @@ -90,6 +95,7 @@ void PixelFormatTest::map() { CORRADE_COMPARE(nextHandled, i); \ CORRADE_COMPARE(firstUnhandled, 0xffff); \ CORRADE_VERIFY(hasPixelFormat(Magnum::PixelFormat::format)); \ + CORRADE_COMPARE(genericPixelFormat(PixelFormat::format), Magnum::PixelFormat::format); \ CORRADE_COMPARE(pixelFormat(Magnum::PixelFormat::format), PixelFormat::format); \ { \ std::ostringstream out; \ @@ -166,11 +172,23 @@ void PixelFormatTest::mapInvalid() { "Vk::pixelFormat(): invalid format PixelFormat(0x123)\n"); } +void PixelFormatTest::mapGenericUnsupported() { + /* This one doesn't have any generic equivalent yet, and isn't in the + enum either */ + CORRADE_COMPARE(genericPixelFormat(PixelFormat(VK_FORMAT_R5G6B5_UNORM_PACK16)), Containers::NullOpt); + /* For compressed texture formats it returns NullOpt too, instead of + asserting. See comment in the source for reasons. */ + CORRADE_COMPARE(genericPixelFormat(PixelFormat::CompressedAstc4x4RGBAF), Containers::NullOpt); +} + void PixelFormatTest::mapCompressed() { /* Touchstone verification. Using Vulkan enums directly to sidestep potential problems in enum mapping as well. */ CORRADE_VERIFY(hasPixelFormat(Magnum::CompressedPixelFormat::Bc1RGBAUnorm)); CORRADE_COMPARE(pixelFormat(Magnum::CompressedPixelFormat::Bc1RGBAUnorm), PixelFormat(VK_FORMAT_BC1_RGBA_UNORM_BLOCK)); + /* PVRTC RGB and RGBA formats have N:1 mapping, conversion back makes them + always RGBA */ + CORRADE_COMPARE(genericCompressedPixelFormat(pixelFormat(Magnum::CompressedPixelFormat::PvrtcRGB4bppSrgb)), Magnum::CompressedPixelFormat::PvrtcRGBA4bppSrgb); /* This goes through the first 16 bits, which should be enough. Going through 32 bits takes 8 seconds, too much. */ @@ -190,6 +208,23 @@ void PixelFormatTest::mapCompressed() { #endif switch(format) { #define _c(format, expectedFormat) \ + case Magnum::CompressedPixelFormat::format: \ + CORRADE_COMPARE(nextHandled, i); \ + CORRADE_COMPARE(firstUnhandled, 0xffff); \ + CORRADE_VERIFY(hasPixelFormat(Magnum::CompressedPixelFormat::format)); \ + CORRADE_COMPARE(genericCompressedPixelFormat(PixelFormat::Compressed ## expectedFormat), Magnum::CompressedPixelFormat::format); \ + CORRADE_COMPARE(pixelFormat(Magnum::CompressedPixelFormat::format), PixelFormat::Compressed ## expectedFormat); \ + { \ + std::ostringstream out; \ + Debug{&out} << pixelFormat(Magnum::CompressedPixelFormat::format); \ + CORRADE_COMPARE(out.str(), "Vk::PixelFormat::Compressed" #expectedFormat "\n"); \ + } \ + ++nextHandled; \ + continue; + /* For duplicate mappings compared to _c() it only checks the + forward mapping. The duplicate mapping is tested in the + touchstone verification above */ + #define _d(format, expectedFormat) \ case Magnum::CompressedPixelFormat::format: \ CORRADE_COMPARE(nextHandled, i); \ CORRADE_COMPARE(firstUnhandled, 0xffff); \ @@ -267,6 +302,14 @@ void PixelFormatTest::mapCompressedInvalid() { "Vk::pixelFormat(): invalid format CompressedPixelFormat(0x123)\n"); } +void PixelFormatTest::mapGenericCompressedUnsupported() { + /* PVRTC2 doesn't have any generic equivalent yet */ + CORRADE_COMPARE(genericPixelFormat(PixelFormat::CompressedPvrtc2RGBA2bppUnorm), Containers::NullOpt); + /* For uncompressed texture formats it returns NullOpt too, instead of + asserting. See comment in the source for reasons. */ + CORRADE_COMPARE(genericCompressedPixelFormat(PixelFormat::RGB8Unorm), Containers::NullOpt); +} + void PixelFormatTest::debug() { std::ostringstream out; Debug{&out} << PixelFormat::RGB16UI << PixelFormat(-10007655);