From c59fa42e7b6b9e65c20d351e103c9c1e762c08b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 Jul 2020 18:05:49 +0200 Subject: [PATCH] Trade: allow storing 64bit ints and pointers in MaterialData. Suited mainly for custom app-specific material properties (e.g., actual texture pointers and handles), not really planning on using this in Magnum itself. --- src/Magnum/Trade/MaterialData.cpp | 10 ++ src/Magnum/Trade/MaterialData.h | 60 +++++++++- src/Magnum/Trade/Test/MaterialDataTest.cpp | 122 +++++++++++++++++++++ 3 files changed, 186 insertions(+), 6 deletions(-) diff --git a/src/Magnum/Trade/MaterialData.cpp b/src/Magnum/Trade/MaterialData.cpp index 47c65d15a..947e8613e 100644 --- a/src/Magnum/Trade/MaterialData.cpp +++ b/src/Magnum/Trade/MaterialData.cpp @@ -68,6 +68,8 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Int: return 4; + case MaterialAttributeType::UnsignedLong: + case MaterialAttributeType::Long: case MaterialAttributeType::Vector2: case MaterialAttributeType::Vector2ui: case MaterialAttributeType::Vector2i: @@ -98,6 +100,10 @@ std::size_t materialAttributeTypeSize(const MaterialAttributeType type) { case MaterialAttributeType::Matrix3x4: case MaterialAttributeType::Matrix4x3: return 48; + + case MaterialAttributeType::Pointer: + case MaterialAttributeType::MutablePointer: + return sizeof(void*); } CORRADE_ASSERT_UNREACHABLE("Trade::materialAttributeTypeSize(): invalid type" << type, {}); @@ -328,6 +334,8 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Rad) _c(UnsignedInt) _c(Int) + _c(UnsignedLong) + _c(Long) _c(Vector2) _c(Vector2ui) _c(Vector2i) @@ -345,6 +353,8 @@ Debug& operator<<(Debug& debug, const MaterialAttributeType value) { _c(Matrix3x4) _c(Matrix4x2) _c(Matrix4x3) + _c(Pointer) + _c(MutablePointer) #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/Trade/MaterialData.h b/src/Magnum/Trade/MaterialData.h index 9ca17cc58..3d172ee80 100644 --- a/src/Magnum/Trade/MaterialData.h +++ b/src/Magnum/Trade/MaterialData.h @@ -291,6 +291,8 @@ enum class MaterialAttributeType: UnsignedByte { Rad, /**< @ref Magnum::Rad "Rad" */ UnsignedInt, /**< @ref Magnum::UnsignedInt "UnsignedInt" */ Int, /**< @ref Magnum::Int "Int" */ + UnsignedLong, /**< @ref Magnum::UnsignedLong "UnsignedLong" */ + Long, /**< @ref Magnum::Long "Long" */ Vector2, /**< @ref Magnum::Vector2 "Vector2" */ Vector2ui, /**< @ref Magnum::Vector2ui "Vector2ui" */ @@ -316,6 +318,20 @@ enum class MaterialAttributeType: UnsignedByte { Matrix4x3, /**< @ref Magnum::Matrix4x3 "Matrix4x3" */ /* Matrix4x4 not present */ + + /** + * @cpp const void* @ce, type is not preserved. For convenience it's + * possible to retrieve the value by calling @cpp attribute() @ce + * with an arbitrary `T` but the user has to ensure the type is correct. + */ + Pointer, + + /** + * @cpp void* @ce, type is not preserved. For convenience it's possible to + * retrieve the value by calling @cpp attribute() @ce with an arbitrary + * `T` but the user has to ensure the type is correct. + */ + MutablePointer, }; /** @@ -427,7 +443,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { /** * @brief Type-erased attribute value * - * Cast the pointer to a concrete type based on @ref type(). + * Cast the pointer to a concrete type based on @ref type(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. */ const void* value() const; @@ -476,6 +495,13 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { UnsignedInt u; Int i; }; + union ErasedLongScalar { + constexpr explicit ErasedLongScalar(UnsignedLong value): u{value} {} + constexpr explicit ErasedLongScalar(Long value): i{value} {} + + UnsignedLong u; + Long i; + }; template union ErasedVector { constexpr explicit ErasedVector(const Math::Vector& value): f{value} {} constexpr explicit ErasedVector(const Math::Vector& value): u{value} {} @@ -504,8 +530,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { constexpr explicit Storage() noexcept: data{} {} template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {} - template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} - template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value && !std::is_pointer::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {} + template constexpr explicit Storage(typename std::enable_if::value && !std::is_pointer::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8v{type, name, value} {} + constexpr explicit Storage(MaterialAttributeType type, Containers::StringView name, const void* value) noexcept: p{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::type type, Containers::StringView name, const T& value) noexcept: _12{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16{type, name, value} {} template constexpr explicit Storage(typename std::enable_if::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16m{type, name, value} {} @@ -517,8 +545,10 @@ class MAGNUM_TRADE_EXPORT MaterialAttributeData { MaterialAttributeType type; char data[Implementation::MaterialAttributeDataSize]; Data _1; + Data p; Data _4; - Data> _8; + Data _8; + Data> _8v; Data> _12; Data> _16; Data> _16m; @@ -776,7 +806,10 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @brief Type-erased attribute value * * The @p id is expected to be smaller than @ref attributeCount() const. - * Cast the pointer to a concrete type based on @ref type(). + * Cast the pointer to a concrete type based on @ref type(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. */ const void* attribute(UnsignedInt id) const; @@ -784,7 +817,10 @@ class MAGNUM_TRADE_EXPORT MaterialData { * @brief Type-erased value of a named attribute * * The @p name is expected to exist. Cast the pointer to a concrete - * type based on @ref attributeType(). + * type based on @ref attributeType(). Note that + * in case of a @ref MaterialAttributeType::Pointer or a + * @ref MaterialAttributeType::MutablePointer, returns a + * *pointer to a pointer*, not the pointer value itself. * @see @ref hasAttribute(), @ref tryAttribute(), @ref attributeOr() */ const void* attribute(Containers::StringView name) const; @@ -950,6 +986,16 @@ namespace Implementation { return MaterialAttributeType::Bool; } }; + template struct MaterialAttributeTypeFor { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::Pointer; + } + }; + template struct MaterialAttributeTypeFor { + constexpr static MaterialAttributeType type() { + return MaterialAttributeType::MutablePointer; + } + }; #ifndef DOXYGEN_GENERATING_OUTPUT #define _c(type_) template<> struct MaterialAttributeTypeFor { \ constexpr static MaterialAttributeType type() { \ @@ -961,6 +1007,8 @@ namespace Implementation { _c(Rad) _c(UnsignedInt) _c(Int) + _c(UnsignedLong) + _c(Long) _c(Vector2) _c(Vector2ui) _c(Vector2i) diff --git a/src/Magnum/Trade/Test/MaterialDataTest.cpp b/src/Magnum/Trade/Test/MaterialDataTest.cpp index 6ad01acc0..8cfd7c9bf 100644 --- a/src/Magnum/Trade/Test/MaterialDataTest.cpp +++ b/src/Magnum/Trade/Test/MaterialDataTest.cpp @@ -55,11 +55,15 @@ class MaterialDataTest: public TestSuite::Tester { template void constructAttributeStringConstexpr(); + void constructAttributePointer(); + void constructAttributeMutablePointer(); + void constructAttributeInvalidName(); void constructAttributeWrongTypeForName(); void constructAttributeInvalidType(); void constructAttributeTooLarge(); void constructAttributeWrongAccessType(); + void constructAttributeWrongAccessPointerType(); void construct(); void constructEmptyAttribute(); @@ -75,11 +79,13 @@ class MaterialDataTest: public TestSuite::Tester { void constructMove(); void access(); + void accessPointer(); void accessOptional(); void accessOutOfBounds(); void accessInvalidAttributeName(); void accessNotFound(); void accessWrongType(); + void accessWrongPointerType(); void release(); @@ -134,6 +140,8 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, @@ -152,11 +160,15 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructAttributeStringConstexpr, &MaterialDataTest::constructAttributeStringConstexpr, + &MaterialDataTest::constructAttributePointer, + &MaterialDataTest::constructAttributeMutablePointer, + &MaterialDataTest::constructAttributeInvalidName, &MaterialDataTest::constructAttributeWrongTypeForName, &MaterialDataTest::constructAttributeInvalidType, &MaterialDataTest::constructAttributeTooLarge, &MaterialDataTest::constructAttributeWrongAccessType, + &MaterialDataTest::constructAttributeWrongAccessPointerType, &MaterialDataTest::construct, &MaterialDataTest::constructEmptyAttribute}); @@ -175,11 +187,13 @@ MaterialDataTest::MaterialDataTest() { &MaterialDataTest::constructMove, &MaterialDataTest::access, + &MaterialDataTest::accessPointer, &MaterialDataTest::accessOptional, &MaterialDataTest::accessOutOfBounds, &MaterialDataTest::accessInvalidAttributeName, &MaterialDataTest::accessNotFound, &MaterialDataTest::accessWrongType, + &MaterialDataTest::accessWrongPointerType, &MaterialDataTest::release, @@ -377,6 +391,54 @@ template void MaterialDataTest::constructAttributeStringConstexpr() { CORRADE_COMPARE(attribute.value(), T(15)); } +constexpr Int SomeData = 3; + +void MaterialDataTest::constructAttributePointer() { + MaterialAttributeData attribute{"pointer!", &SomeData}; + CORRADE_COMPARE(attribute.name(), "pointer!"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(*static_cast(attribute.value()), &SomeData); + CORRADE_COMPARE(attribute.value(), &SomeData); + /* Any type works */ + CORRADE_COMPARE(attribute.value(), &SomeData); + + constexpr MaterialAttributeData cattribute{"pointer!"_s, &SomeData}; + CORRADE_COMPARE(cattribute.name(), "pointer!"); + CORRADE_COMPARE(cattribute.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(*static_cast(cattribute.value()), &SomeData); + CORRADE_COMPARE(cattribute.value(), &SomeData); + + /* Type-erased variant */ + const Int* pointer = &SomeData; + MaterialAttributeData typeErased{"pointer!", MaterialAttributeType::Pointer, &pointer}; + CORRADE_COMPARE(typeErased.name(), "pointer!"); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::Pointer); + CORRADE_COMPARE(typeErased.value(), &SomeData); + /* Any type works */ + CORRADE_COMPARE(typeErased.value(), &SomeData); +} + +void MaterialDataTest::constructAttributeMutablePointer() { + Float data = 85.1f; + + MaterialAttributeData attribute{"pointer!", &data}; + CORRADE_COMPARE(attribute.name(), "pointer!"); + CORRADE_COMPARE(attribute.type(), MaterialAttributeType::MutablePointer); + CORRADE_COMPARE(*static_cast(attribute.value()), &data); + CORRADE_COMPARE(attribute.value(), &data); + /* Any type works */ + CORRADE_COMPARE(attribute.value(), &data); + + /* Type-erased variant */ + Float* pointer = &data; + MaterialAttributeData typeErased{"pointer!", MaterialAttributeType::MutablePointer, &pointer}; + CORRADE_COMPARE(typeErased.name(), "pointer!"); + CORRADE_COMPARE(typeErased.type(), MaterialAttributeType::MutablePointer); + CORRADE_COMPARE(typeErased.value(), &data); + /* Any type works */ + CORRADE_COMPARE(typeErased.value(), &data); +} + void MaterialDataTest::constructAttributeInvalidName() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); @@ -444,6 +506,23 @@ void MaterialDataTest::constructAttributeWrongAccessType() { CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::Matrix4x3\n"); } +void MaterialDataTest::constructAttributeWrongAccessPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Int a = 3; + const Float b = 57.0f; + + std::ostringstream out; + Error redirectError{&out}; + MaterialAttributeData{"thing3", &a}.value(); + MaterialAttributeData{"boom", &b}.value(); + CORRADE_COMPARE(out.str(), + "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::MutablePointer\n" + "Trade::MaterialAttributeData::value(): improper type requested for boom of Trade::MaterialAttributeType::Pointer\n"); +} + void MaterialDataTest::construct() { int state; MaterialData data{MaterialType::Phong, { @@ -722,6 +801,23 @@ void MaterialDataTest::access() { CORRADE_COMPARE(c.alphaMask(), 0.5f); } +void MaterialDataTest::accessPointer() { + const Float a = 3.0f; + Long b = -4; + + MaterialData data{{}, { + {"pointer", &a}, + {"mutable", &b} + }}; + CORRADE_COMPARE(data.attributeType("pointer"), MaterialAttributeType::Pointer); + CORRADE_COMPARE(data.attributeType("mutable"), MaterialAttributeType::MutablePointer); + + CORRADE_COMPARE(*static_cast(data.attribute("pointer")), &a); + CORRADE_COMPARE(*static_cast(data.attribute("mutable")), &b); + CORRADE_COMPARE(data.attribute("pointer"), &a); + CORRADE_COMPARE(data.attribute("mutable"), &b); +} + void MaterialDataTest::accessOptional() { MaterialData data{{}, { {MaterialAttribute::AlphaMask, 0.5f}, @@ -868,6 +964,32 @@ void MaterialDataTest::accessWrongType() { "Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"); } +void MaterialDataTest::accessWrongPointerType() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Int a = 3; + const Double b = 57.0; + + MaterialData data{{}, { + {"mutablePointer", &a}, + {"pointer", &b} + }}; + + /* These are fine (type is not checked) */ + data.attribute("mutablePointer"); + data.attribute("pointer"); + + std::ostringstream out; + Error redirectError{&out}; + data.attribute("mutablePointer"); + data.attribute("pointer"); + CORRADE_COMPARE(out.str(), + "Trade::MaterialData::attribute(): improper type requested for mutablePointer of Trade::MaterialAttributeType::MutablePointer\n" + "Trade::MaterialData::attribute(): improper type requested for pointer of Trade::MaterialAttributeType::Pointer\n"); +} + void MaterialDataTest::release() { MaterialData data{{}, { {"DiffuseColor", 0xff3366aa_rgbaf},