Browse Source

DebugTools: new CompareMaterial class.

Now that there will be a whole new MaterialTools library, I really need
this helper.
pull/605/head
Vladimír Vondruš 4 years ago
parent
commit
cdf71fa8bf
  1. 2
      doc/changelog.dox
  2. 14
      doc/snippets/CMakeLists.txt
  3. 16
      doc/snippets/debugtools-comparematerial.ansi
  4. 69
      doc/snippets/debugtools-comparematerial.cpp
  5. 6
      src/Magnum/DebugTools/CMakeLists.txt
  6. 2
      src/Magnum/DebugTools/CompareImage.h
  7. 395
      src/Magnum/DebugTools/CompareMaterial.cpp
  8. 109
      src/Magnum/DebugTools/CompareMaterial.h
  9. 2
      src/Magnum/DebugTools/Test/CMakeLists.txt
  10. 323
      src/Magnum/DebugTools/Test/CompareMaterialTest.cpp

2
doc/changelog.dox

@ -68,6 +68,8 @@ See also:
- Added @ref DebugTools::ColorMap::coolWarmSmooth() and - Added @ref DebugTools::ColorMap::coolWarmSmooth() and
@ref DebugTools::ColorMap::coolWarmBent() (see [mosra/magnum#473](https://github.com/mosra/magnum/pull/473)) @ref DebugTools::ColorMap::coolWarmBent() (see [mosra/magnum#473](https://github.com/mosra/magnum/pull/473))
- New @ref DebugTools::CompareMaterial comparator for convenient comparison
of @ref Trade::MaterialData instances
@subsubsection changelog-latest-new-gl GL library @subsubsection changelog-latest-new-gl GL library

14
doc/snippets/CMakeLists.txt

@ -132,14 +132,14 @@ if(MAGNUM_WITH_DEBUGTOOLS)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h) ${CMAKE_CURRENT_BINARY_DIR}/configure.h)
# CompareImage documentation snippet. I need it executable so I can # CompareImage / CompareMaterial documentation snippets. I need them
# copy&paste the output to the documentation. Also not using # executable so I can include the colored output in the documentation.
# corrade_add_test() because it shouldn't be run as part of CTest as it # Also not using corrade_add_test() because it shouldn't be run as part
# purposely fails. # of CTest as it purposely fails.
add_executable(debugtools-compareimage debugtools-compareimage.cpp) add_executable(debugtools-compareimage debugtools-compareimage.cpp)
target_link_libraries(debugtools-compareimage PRIVATE add_executable(debugtools-comparematerial debugtools-comparematerial.cpp)
MagnumDebugTools target_link_libraries(debugtools-compareimage PRIVATE MagnumDebugTools)
MagnumTrade) target_link_libraries(debugtools-comparematerial PRIVATE MagnumDebugTools)
target_include_directories(debugtools-compareimage PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(debugtools-compareimage PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
endif() endif()

16
doc/snippets/debugtools-comparematerial.ansi

@ -0,0 +1,16 @@
Starting MaterialTest with 1 test cases...
 FAIL [1] conversion() at …/debugtools-comparematerial.cpp:65
Materials actual and expected have different layers. Actual (+) vs expected (-):
 -Types: PbrMetallicRoughness
 +Types: PbrMetallicRoughness|PbrClearCoat
Base layer:
BaseColor @ Vector4: {0.851206, 0.687386, 0.416013, 1}
 - DoubleSided @ Bool: false
 + DoubleSided @ Bool: true
Metalness @ Float: 0.603401
Roughness @ Float: 0.105112
 +Layer 1:
 + LayerName @ String: ClearCoat
 + LayerFactor @ Float: 0.02
 + Roughness @ Float: 0.320856
Finished MaterialTest with 1 errors out of 1 checks.

69
doc/snippets/debugtools-comparematerial.cpp

@ -0,0 +1,69 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <Corrade/TestSuite/Tester.h>
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MaterialData.h"
#include "Magnum/DebugTools/CompareMaterial.h"
using namespace Magnum;
#define DOXYGEN_ELLIPSIS(...) __VA_ARGS__
struct MaterialTest: TestSuite::Tester {
explicit MaterialTest();
void conversion();
};
MaterialTest::MaterialTest() {
addTests({&MaterialTest::conversion});
}
void MaterialTest::conversion() {
Trade::MaterialData actual{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, {
{Trade::MaterialAttribute::BaseColor, Color4{0.851206f, 0.687386f, 0.416013f}},
{Trade::MaterialAttribute::Metalness, 0.603401f},
{Trade::MaterialAttribute::Roughness, 0.105112f},
{Trade::MaterialAttribute::DoubleSided, true},
{Trade::MaterialLayer::ClearCoat},
{Trade::MaterialAttribute::LayerFactor, 0.02f},
{Trade::MaterialAttribute::Roughness, 0.320856f},
}, {4, 7}};
Trade::MaterialData expected{Trade::MaterialType::PbrMetallicRoughness, {
{Trade::MaterialAttribute::BaseColor, Color4{0.851206f, 0.687386f, 0.416013f}},
{Trade::MaterialAttribute::Metalness, 0.603401f},
{Trade::MaterialAttribute::Roughness, 0.105112f},
{Trade::MaterialAttribute::DoubleSided, false}
}};
/* [usage] */
CORRADE_COMPARE_AS(actual, expected, DebugTools::CompareMaterial);
/* [usage] */
}
CORRADE_TEST_MAIN(MaterialTest)

6
src/Magnum/DebugTools/CMakeLists.txt

@ -97,11 +97,15 @@ endif()
# Build the TestSuite-related functionality only if it is present # Build the TestSuite-related functionality only if it is present
find_package(Corrade COMPONENTS TestSuite) find_package(Corrade COMPONENTS TestSuite)
if(Corrade_TestSuite_FOUND AND MAGNUM_WITH_TRADE) if(Corrade_TestSuite_FOUND AND MAGNUM_WITH_TRADE)
list(APPEND MagnumDebugTools_SRCS
CompareMaterial.cpp)
list(APPEND MagnumDebugTools_GracefulAssert_SRCS list(APPEND MagnumDebugTools_GracefulAssert_SRCS
CompareImage.cpp) CompareImage.cpp)
list(APPEND MagnumDebugTools_HEADERS list(APPEND MagnumDebugTools_HEADERS
CompareImage.h) CompareImage.h
CompareMaterial.h)
endif() endif()
# Objects shared between main and test library # Objects shared between main and test library

2
src/Magnum/DebugTools/CompareImage.h

@ -329,6 +329,8 @@ then autodetected from the passed type, with normalized formats preferred. In
practice this means e.g. @ref Math::Vector2 "Math::Vector2<UnsignedByte>" will practice this means e.g. @ref Math::Vector2 "Math::Vector2<UnsignedByte>" will
be understood as @ref PixelFormat::RG8Unorm and there's currently no way to be understood as @ref PixelFormat::RG8Unorm and there's currently no way to
interpret it as @ref PixelFormat::RG8UI, for example. interpret it as @ref PixelFormat::RG8UI, for example.
@see @ref CompareMaterial
*/ */
class CompareImage { class CompareImage {
public: public:

395
src/Magnum/DebugTools/CompareMaterial.cpp

@ -0,0 +1,395 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "CompareMaterial.h"
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Triple.h>
#include <Corrade/Utility/Math.h> /* max() that works with enums */
#include <Corrade/TestSuite/Comparator.h>
#include "Magnum/Math/Matrix.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Trade/MaterialData.h"
namespace Corrade { namespace TestSuite {
using namespace Magnum;
namespace {
/* Higher values mean bigger change */
enum class MaterialState {
Same,
DifferentTypes,
DifferentAttributeValues,
DifferentAttributeTypes,
DifferentAttributes,
DifferentLayers
};
enum class AttributeState {
Same,
DifferentValue,
DifferentType,
OnlyInExpected,
OnlyInActual
};
}
struct Comparator<DebugTools::CompareMaterial>::State {
MaterialState materialState = MaterialState::Same;
/* Second is attribute ID in the actual material (unless AttributeState is
OnlyInExpected), third is attribute ID in the expected material (unless
AttributeState is OnlyInActual). */
Containers::Array<Containers::Triple<AttributeState, UnsignedInt, UnsignedInt>> attributes;
/* Offsets into the attributes array for each layer. I.e., layer i is
stored in `attributes[layerOffsets[i]]` to
`attributes[layerOffsets[i + 1]]`. */
Containers::Array<UnsignedInt> layerOffsets;
const Trade::MaterialData* actual{};
const Trade::MaterialData* expected{};
};
Comparator<DebugTools::CompareMaterial>::Comparator(): _state{InPlaceInit} {}
Comparator<DebugTools::CompareMaterial>::~Comparator() = default;
namespace {
bool attributesEqual(const Trade::MaterialAttributeData& a, const Trade::MaterialAttributeData& b) {
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif
CORRADE_INTERNAL_ASSERT(a.type() == b.type());
switch(a.type()) {
#define _c(type) case Trade::MaterialAttributeType::type: \
return a.value<type>() == b.value<type>();
#define _ct(name, type) case Trade::MaterialAttributeType::name: \
return a.value<type>() == b.value<type>();
_ct(Bool, bool)
/* LCOV_EXCL_START */
_c(Float)
_c(Deg)
_c(Rad)
_c(UnsignedInt)
_c(Int)
_c(UnsignedLong)
_c(Long)
_c(Vector2)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
/* LCOV_EXCL_STOP */
_ct(Pointer, const void*)
_ct(MutablePointer, void*)
_ct(String, Containers::StringView)
_ct(TextureSwizzle, Trade::MaterialTextureSwizzle)
#undef _c
#undef _ct
case Trade::MaterialAttributeType::Buffer:
return Containers::StringView{Containers::arrayCast<const char>(a.value<Containers::ArrayView<const void>>())} == Containers::StringView{Containers::arrayCast<const char>(b.value<Containers::ArrayView<const void>>())};
}
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic pop
#endif
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
TestSuite::ComparisonStatusFlags Comparator<DebugTools::CompareMaterial>::operator()(const Trade::MaterialData& actual, const Trade::MaterialData& expected) {
_state->actual = &actual;
_state->expected = &expected;
if(actual.types() != expected.types())
_state->materialState = MaterialState::DifferentTypes;
/* The layer offset array has one extra item for the last layer count */
const std::size_t layerMax = Math::max(actual.layerCount(), expected.layerCount());
_state->layerOffsets = Containers::Array<UnsignedInt>{NoInit, layerMax + 1};
/* Go over all layers that are in both materials */
std::size_t layer = 0;
for(const std::size_t layerMin = Math::min(actual.layerCount(), expected.layerCount()); layer != layerMin; ++layer) {
_state->layerOffsets[layer] = _state->attributes.size();
UnsignedInt inActual = 0;
UnsignedInt inExpected = 0;
/* Take the earliest-sorted attribute from either material */
while(inActual != actual.attributeCount(layer) && inExpected != expected.attributeCount(layer)) {
if(actual.attributeName(layer, inActual) == expected.attributeName(layer, inExpected)) {
AttributeState attributeState;
if(actual.attributeType(layer, inActual) != expected.attributeType(layer, inExpected)) {
attributeState = AttributeState::DifferentType;
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributeTypes);
} else if(!attributesEqual(actual.attributeData(layer, inActual), expected.attributeData(layer, inExpected))) {
attributeState = AttributeState::DifferentValue;
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributeValues);
} else {
attributeState = AttributeState::Same;
}
arrayAppend(_state->attributes, InPlaceInit, attributeState, inActual, inExpected);
++inActual;
++inExpected;
} else if(actual.attributeName(inActual) < expected.attributeName(inExpected)) {
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{});
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes);
++inActual;
} else if(actual.attributeName(inActual) > expected.attributeName(inExpected)) {
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected);
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes);
++inExpected;
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Consume remaining leftover attributes in either. Only one of these
loops get entered. */
while(inActual < actual.attributeCount(layer)) {
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{});
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes);
++inActual;
}
while(inExpected < expected.attributeCount(layer)) {
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected);
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentAttributes);
++inExpected;
}
}
/* Go over remaining actual/expected layers which weren't in
expected/actual. Only one of these loops get entered. */
for(; layer < actual.layerCount(); ++layer) {
_state->layerOffsets[layer] = _state->attributes.size();
for(UnsignedInt inActual = 0, inActualMax = actual.attributeCount(layer); inActual != inActualMax; ++inActual)
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInActual, inActual, ~UnsignedInt{});
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentLayers);
}
for(; layer < expected.layerCount(); ++layer) {
_state->layerOffsets[layer] = _state->attributes.size();
for(UnsignedInt inExpected = 0, inExpectedMax = expected.attributeCount(layer); inExpected != inExpectedMax; ++inExpected)
arrayAppend(_state->attributes, InPlaceInit, AttributeState::OnlyInExpected, ~UnsignedInt{}, inExpected);
_state->materialState = Utility::max(_state->materialState, MaterialState::DifferentLayers);
}
CORRADE_INTERNAL_ASSERT(layer == _state->layerOffsets.size() - 1);
_state->layerOffsets[layer] = _state->attributes.size();
/** @todo If there's a large sequence of Same attributes, elide the middle
(mark it with SameEllipsis, e.g., and leave at most 3 before and 3
after each difference), and skip them when printing unless Verbose is
set. Would become practically useful only once there's really a lot
material attributes in a single layer, currently not really. */
return _state->materialState == MaterialState::Same ? TestSuite::ComparisonStatusFlags{} : TestSuite::ComparisonStatusFlag::Failed;
}
namespace {
void printAttribute(Debug& out, const Trade::MaterialAttributeData& attribute, const AttributeState state, const bool isActual) {
out << Debug::newline << " ";
if(state == AttributeState::Same)
out << " ";
else if(isActual)
out << Debug::color(Debug::Color::Green) << "+";
else
out << Debug::color(Debug::Color::Red) << "-";
if(state == AttributeState::DifferentType ||
state == AttributeState::DifferentValue)
out << Debug::resetColor;
out << "" << attribute.name() << "@";
if(state == AttributeState::DifferentType)
out << Debug::color(isActual ? Debug::Color::Green : Debug::Color::Red);
out << Debug::packed << attribute.type();
if(state == AttributeState::DifferentType)
out << Debug::resetColor;
out << Debug::nospace << ":";
if(state == AttributeState::DifferentType ||
state == AttributeState::DifferentValue)
out << Debug::color(isActual ? Debug::Color::Green : Debug::Color::Red);
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif
switch(attribute.type()) {
#define _c(type) case Trade::MaterialAttributeType::type: \
out << Debug::packed << attribute.value<type>(); \
break;
#define _ct(name, type) case Trade::MaterialAttributeType::name: \
out << Debug::packed << attribute.value<type>(); \
break;
_ct(Bool, bool)
/* LCOV_EXCL_START */
_c(Float)
_c(Deg)
_c(Rad)
_c(UnsignedInt)
_c(Int)
_c(UnsignedLong)
_c(Long)
_c(Vector2)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
/* LCOV_EXCL_STOP */
_ct(Pointer, const void*)
_ct(MutablePointer, void*)
_ct(String, Containers::StringView)
_ct(TextureSwizzle, Trade::MaterialTextureSwizzle)
#undef _c
#undef _ct
case Trade::MaterialAttributeType::Buffer:
out << Containers::arrayCast<const char>(attribute.value<Containers::ArrayView<const void>>());
}
#ifdef CORRADE_TARGET_GCC
#pragma GCC diagnostic pop
#endif
if(state != AttributeState::Same)
out << Debug::resetColor;
}
}
void Comparator<DebugTools::CompareMaterial>::printMessage(const TestSuite::ComparisonStatusFlags, Debug& out, const Containers::StringView actual, const Containers::StringView expected) const {
out << "Materials" << actual << "and" << expected;
if(_state->materialState == MaterialState::DifferentLayers) {
out << "have different layers.";
} else if(_state->materialState == MaterialState::DifferentAttributes) {
out << "have different attributes.";
} else if(_state->materialState == MaterialState::DifferentAttributeTypes) {
out << "have different attribute types.";
} else if(_state->materialState == MaterialState::DifferentAttributeValues) {
out << "have different attribute values.";
} else if(_state->materialState == MaterialState::DifferentTypes) {
out << "have different types.";
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
out << Debug::color(Debug::Color::Green) << "Actual (+)"
<< Debug::resetColor << "vs" << Debug::color(Debug::Color::Red)
<< "expected (-)" << Debug::resetColor << Debug::nospace << ":";
/* Print the type, or both if they differ */
/** @todo Or maybe if it's MaterialState::DifferentType print just this and
skip the attributes? Because everything else that's printed is the
same... */
if(_state->expected->types()) {
out << Debug::newline << " ";
if(_state->expected->types() != _state->actual->types())
out << Debug::color(Debug::Color::Red) << "-" << Debug::nospace;
else
out << "";
if(_state->actual->types() && _state->expected->types() != _state->actual->types())
out << Debug::resetColor;
out << "Types:";
if(_state->actual->types() && _state->expected->types() != _state->actual->types())
out << Debug::color(Debug::Color::Red);
out << Debug::packed << _state->expected->types() << Debug::resetColor;
}
if(_state->actual->types() && _state->actual->types() != _state->expected->types()) {
out << Debug::newline << " " << Debug::color(Debug::Color::Green) << "+" << Debug::nospace;
if(_state->expected->types()) out << Debug::resetColor;
out << "Types:";
if(_state->expected->types()) out << Debug::color(Debug::Color::Green);
out << Debug::packed << _state->actual->types() << Debug::resetColor;
}
/* Print content of both materials, interleaved, with layers and attributes
that differ marked with +/- */
for(UnsignedInt layer = 0, layerMax = _state->layerOffsets.size() - 1; layer != layerMax; ++layer) {
/* Show layer header only if there's more than one and the base
layer isn't empty */
if(_state->layerOffsets.size() != 2 || _state->layerOffsets[1] != 0) {
out << Debug::newline << " ";
if(layer >= _state->actual->layerCount())
out << Debug::color(Debug::Color::Red) << "-" << Debug::nospace;
else if(layer >= _state->expected->layerCount())
out << Debug::color(Debug::Color::Green) << "+" << Debug::nospace;
else
out << "";
if(layer == 0)
out << "Base layer:";
else
out << "Layer" << layer << Debug::nospace << ":";
if(layer >= _state->actual->layerCount() || layer >= _state->expected->layerCount())
out << Debug::resetColor;
}
/* Print attribute values indented only if there's more than one
layer */
for(UnsignedInt id = _state->layerOffsets[layer], idMax = _state->layerOffsets[layer + 1]; id != idMax; ++id) {
const Containers::Triple<AttributeState, UnsignedInt, UnsignedInt> attribute = _state->attributes[id];
const AttributeState state =attribute.first();
if(state != AttributeState::OnlyInActual)
printAttribute(out, _state->expected->attributeData(layer, attribute.third()), state, false);
if(state != AttributeState::Same && state != AttributeState::OnlyInExpected) {
printAttribute(out, _state->actual->attributeData(layer, attribute.second()), state, true);
}
}
}
}
}}

109
src/Magnum/DebugTools/CompareMaterial.h

@ -0,0 +1,109 @@
#ifndef Magnum_DebugTools_CompareMaterial_h
#define Magnum_DebugTools_CompareMaterial_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Class @ref Magnum::DebugTools::CompareMaterial
* @m_since_latest
*/
#include <Corrade/Containers/Pointer.h>
#include <Corrade/TestSuite/TestSuite.h>
#include "Magnum/Magnum.h"
#include "Magnum/DebugTools/visibility.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace DebugTools {
class CompareMaterial;
}}
#ifndef DOXYGEN_GENERATING_OUTPUT
/* If Doxygen sees this, all @ref Corrade::TestSuite links break (prolly
because the namespace is undocumented in this project) */
namespace Corrade { namespace TestSuite {
template<> class MAGNUM_DEBUGTOOLS_EXPORT Comparator<Magnum::DebugTools::CompareMaterial> {
public:
explicit Comparator();
~Comparator();
ComparisonStatusFlags operator()(const Magnum::Trade::MaterialData& actual, const Magnum::Trade::MaterialData& expected);
void printMessage(ComparisonStatusFlags flags, Utility::Debug& out, Containers::StringView actual, Containers::StringView expected) const;
private:
struct State;
Containers::Pointer<State> _state;
};
}}
#endif
namespace Magnum { namespace DebugTools {
/**
@brief Material comparator for @ref Corrade::TestSuite
@m_since_latest
Compares @ref Trade::MaterialData instances, printing the differences in the
two if they have a different type, different layer count, different attributes,
and different type or different value of the same attribute. Pass the
comparator to @ref CORRADE_COMPARE_AS() along with an actual and expected
material:
@snippet debugtools-comparematerial.cpp usage
Based on actual materials used, in case of a comparison failure the comparator
can give for example the following output:
@m_class{m-console-wrap}
@include debugtools-comparematerial.ansi
All @ref Trade::MaterialAttributeType are supported.
@see @ref CompareImage
*/
class MAGNUM_DEBUGTOOLS_EXPORT CompareMaterial {
public:
explicit CompareMaterial();
#ifndef DOXYGEN_GENERATING_OUTPUT
TestSuite::Comparator<CompareMaterial>& comparator() {
return _c;
}
#endif
private:
TestSuite::Comparator<CompareMaterial> _c;
};
}}
#endif

2
src/Magnum/DebugTools/Test/CMakeLists.txt

@ -100,6 +100,8 @@ if(MAGNUM_WITH_TRADE)
add_dependencies(DebugToolsCompareImageTest TgaImporter) add_dependencies(DebugToolsCompareImageTest TgaImporter)
endif() endif()
endif() endif()
corrade_add_test(DebugToolsCompareMaterialTest CompareMaterialTest.cpp LIBRARIES MagnumDebugToolsTestLib)
endif() endif()
if(MAGNUM_TARGET_GL) if(MAGNUM_TARGET_GL)

323
src/Magnum/DebugTools/Test/CompareMaterialTest.cpp

@ -0,0 +1,323 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/DebugTools/CompareMaterial.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MaterialData.h"
namespace Magnum { namespace DebugTools { namespace Test { namespace {
struct CompareMaterialTest: TestSuite::Tester {
explicit CompareMaterialTest();
void same();
void different();
void differentReverse();
};
using namespace Containers::Literals;
using namespace Math::Literals;
const struct {
const char* name;
Trade::MaterialData material;
} SameData[]{
{"empty", Trade::MaterialData{{}, {}}},
{"empty with types", Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, {}}},
{"base attributes", Trade::MaterialData{Trade::MaterialType::Phong, {
{Trade::MaterialAttribute::DiffuseColor, 0x556699aa_rgbaf},
{Trade::MaterialAttribute::NormalTexture, 5u},
{"name", "hello"_s},
}}},
{"layers", Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness|Trade::MaterialType::PbrClearCoat, {
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialLayer::ClearCoat},
{Trade::MaterialAttribute::LayerFactor, 0.76f},
{"name", "hello"_s},
}, {1, 3, 4}}},
};
const struct {
const char* name;
Trade::MaterialData actual;
Trade::MaterialData expected;
const char* message;
const char* messageReverse;
} DifferentData[]{
{"empty, different types",
Trade::MaterialData{Trade::MaterialType::Flat, {}},
Trade::MaterialData{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, {}},
"Materials a and b have different types. Actual (+) vs expected (-):\n"
" -Types: PbrMetallicRoughness|PbrClearCoat\n"
" +Types: Flat\n",
"Materials b and a have different types. Actual (+) vs expected (-):\n"
" -Types: Flat\n"
" +Types: PbrMetallicRoughness|PbrClearCoat\n"},
{"different types",
Trade::MaterialData{Trade::MaterialType::Flat, {
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf},
}},
Trade::MaterialData{Trade::MaterialType::PbrClearCoat|Trade::MaterialType::PbrMetallicRoughness, {
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf},
}},
"Materials a and b have different types. Actual (+) vs expected (-):\n"
" -Types: PbrMetallicRoughness|PbrClearCoat\n"
" +Types: Flat\n"
" Base layer:\n"
" BaseColor @ Vector4: {1, 0, 1, 1}\n",
"Materials b and a have different types. Actual (+) vs expected (-):\n"
" -Types: Flat\n"
" +Types: PbrMetallicRoughness|PbrClearCoat\n"
" Base layer:\n"
" BaseColor @ Vector4: {1, 0, 1, 1}\n"},
{"different types, one empty",
Trade::MaterialData{Trade::MaterialType::Flat, {
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf}
}},
Trade::MaterialData{{}, {
{Trade::MaterialAttribute::BaseColor, 0xff00ffff_rgbaf}
}},
"Materials a and b have different types. Actual (+) vs expected (-):\n"
" +Types: Flat\n"
" Base layer:\n"
" BaseColor @ Vector4: {1, 0, 1, 1}\n",
"Materials b and a have different types. Actual (+) vs expected (-):\n"
" -Types: Flat\n"
" Base layer:\n"
" BaseColor @ Vector4: {1, 0, 1, 1}\n"},
{"different attributes",
Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness, {
{Trade::MaterialAttribute::DoubleSided, true},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::NormalTextureScale, 0.5f},
{Trade::MaterialAttribute::OcclusionTexture, 3u},
}},
Trade::MaterialData{Trade::MaterialType::PbrMetallicRoughness, {
{Trade::MaterialAttribute::Metalness, 5.5f},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::NormalTextureLayer, 2u},
}},
"Materials a and b have different attributes. Actual (+) vs expected (-):\n"
" Types: PbrMetallicRoughness\n"
" Base layer:\n"
" + DoubleSided @ Bool: true\n"
" - Metalness @ Float: 5.5\n"
" NormalTexture @ UnsignedInt: 5\n"
" - NormalTextureLayer @ UnsignedInt: 2\n"
" + NormalTextureScale @ Float: 0.5\n"
" + OcclusionTexture @ UnsignedInt: 3\n",
"Materials b and a have different attributes. Actual (+) vs expected (-):\n"
" Types: PbrMetallicRoughness\n"
" Base layer:\n"
" - DoubleSided @ Bool: true\n"
" + Metalness @ Float: 5.5\n"
" NormalTexture @ UnsignedInt: 5\n"
" + NormalTextureLayer @ UnsignedInt: 2\n"
" - NormalTextureScale @ Float: 0.5\n"
" - OcclusionTexture @ UnsignedInt: 3\n"},
{"different attribute types",
Trade::MaterialData{{}, {
{"pointer", reinterpret_cast<void*>(0xdead)},
{"integer", 5u},
{"scale", 0.5f},
}},
Trade::MaterialData{{}, {
{"pointer", reinterpret_cast<const void*>(0xdead)},
{"integer", 5},
{"scale", "small"},
}},
"Materials a and b have different attribute types. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - integer @ Int: 5\n"
" + integer @ UnsignedInt: 5\n"
" - pointer @ Pointer: 0xdead\n"
" + pointer @ MutablePointer: 0xdead\n"
" - scale @ String: small\n"
" + scale @ Float: 0.5\n",
"Materials b and a have different attribute types. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - integer @ UnsignedInt: 5\n"
" + integer @ Int: 5\n"
" - pointer @ MutablePointer: 0xdead\n"
" + pointer @ Pointer: 0xdead\n"
" - scale @ Float: 0.5\n"
" + scale @ String: small\n"},
{"different attribute values",
Trade::MaterialData{{}, {
{Trade::MaterialAttribute::Metalness, 0.3f},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::NormalTextureSwizzle, Trade::MaterialTextureSwizzle::RGB},
{"buffer", Containers::ArrayView<const void>{"\x56\x78\x22"}},
{"pointer", reinterpret_cast<const void*>(0xbeef)},
{"pointerMutable", reinterpret_cast<void*>(0xdead)},
}},
Trade::MaterialData{{}, {
{Trade::MaterialAttribute::Metalness, 5.5f},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::NormalTextureSwizzle, Trade::MaterialTextureSwizzle::RG},
{"buffer", Containers::ArrayView<const void>{"\x56\x78\x22"}},
{"pointer", reinterpret_cast<const void*>(0xbeef)},
{"pointerMutable", reinterpret_cast<void*>(0xdead)},
}},
"Materials a and b have different attribute values. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - Metalness @ Float: 5.5\n"
" + Metalness @ Float: 0.3\n"
" NormalTexture @ UnsignedInt: 5\n"
" - NormalTextureSwizzle @ TextureSwizzle: RG\n"
" + NormalTextureSwizzle @ TextureSwizzle: RGB\n"
" buffer @ Buffer: {86, 120, 34, 0}\n"
" pointer @ Pointer: 0xbeef\n"
" pointerMutable @ MutablePointer: 0xdead\n",
"Materials b and a have different attribute values. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - Metalness @ Float: 0.3\n"
" + Metalness @ Float: 5.5\n"
" NormalTexture @ UnsignedInt: 5\n"
" - NormalTextureSwizzle @ TextureSwizzle: RGB\n"
" + NormalTextureSwizzle @ TextureSwizzle: RG\n"
" buffer @ Buffer: {86, 120, 34, 0}\n"
" pointer @ Pointer: 0xbeef\n"
" pointerMutable @ MutablePointer: 0xdead\n"},
{"different attributes in layers",
Trade::MaterialData{{}, {
{Trade::MaterialAttribute::DoubleSided, true},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::NormalTextureScale, 0.5f},
{Trade::MaterialAttribute::OcclusionTexture, 3u},
{"texturePointer", reinterpret_cast<void*>(0xdead)},
}, {3, 5}},
Trade::MaterialData{{}, {
{Trade::MaterialAttribute::DoubleSided, false},
{Trade::MaterialAttribute::NormalTexture, 5u},
{Trade::MaterialAttribute::OcclusionTexture, 3u},
{"texturePointer", reinterpret_cast<const void*>(0xdead)},
{Trade::MaterialAttribute::NormalTextureLayer, 2u},
}, {2, 4, 5}},
"Materials a and b have different layers. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - DoubleSided @ Bool: false\n"
" + DoubleSided @ Bool: true\n"
" NormalTexture @ UnsignedInt: 5\n"
" + NormalTextureScale @ Float: 0.5\n"
" Layer 1:\n"
" OcclusionTexture @ UnsignedInt: 3\n"
" - texturePointer @ Pointer: 0xdead\n"
" + texturePointer @ MutablePointer: 0xdead\n"
" -Layer 2:\n"
" - NormalTextureLayer @ UnsignedInt: 2\n",
"Materials b and a have different layers. Actual (+) vs expected (-):\n"
" Base layer:\n"
" - DoubleSided @ Bool: true\n"
" + DoubleSided @ Bool: false\n"
" NormalTexture @ UnsignedInt: 5\n"
" - NormalTextureScale @ Float: 0.5\n"
" Layer 1:\n"
" OcclusionTexture @ UnsignedInt: 3\n"
" - texturePointer @ MutablePointer: 0xdead\n"
" + texturePointer @ Pointer: 0xdead\n"
" +Layer 2:\n"
" + NormalTextureLayer @ UnsignedInt: 2\n"},
{"different (empty) layer count",
Trade::MaterialData{{}, {}},
Trade::MaterialData{{}, {}, {0, 0, 0}},
"Materials a and b have different layers. Actual (+) vs expected (-):\n"
" Base layer:\n"
" -Layer 1:\n"
" -Layer 2:\n",
"Materials b and a have different layers. Actual (+) vs expected (-):\n"
" Base layer:\n"
" +Layer 1:\n"
" +Layer 2:\n"},
};
CompareMaterialTest::CompareMaterialTest() {
addInstancedTests({&CompareMaterialTest::same},
Containers::arraySize(SameData));
addInstancedTests({&CompareMaterialTest::different,
&CompareMaterialTest::differentReverse},
Containers::arraySize(DifferentData));
}
void CompareMaterialTest::same() {
auto&& data = SameData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_COMPARE_AS(data.material, data.material, CompareMaterial);
}
void CompareMaterialTest::different() {
auto&& data = DifferentData[testCaseInstanceId()];
setTestCaseDescription(data.name);
TestSuite::Comparator<CompareMaterial> compare;
TestSuite::ComparisonStatusFlags flags = compare(data.actual, data.expected);
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed);
CORRADE_INFO("Visual color verification:");
{
Debug out;
compare.printMessage(flags, out, "a", "b");
}
std::ostringstream out;
{
Debug dc{&out, Debug::Flag::DisableColors};
compare.printMessage(flags, dc, "a", "b");
}
CORRADE_COMPARE(out.str(), data.message);
}
void CompareMaterialTest::differentReverse() {
auto&& data = DifferentData[testCaseInstanceId()];
setTestCaseDescription(data.name);
TestSuite::Comparator<CompareMaterial> compare;
TestSuite::ComparisonStatusFlags flags = compare(data.expected, data.actual);
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed);
CORRADE_INFO("Visual color verification:");
{
Debug out;
compare.printMessage(flags, out, "b", "a");
}
std::ostringstream out;
{
Debug dc{&out, Debug::Flag::DisableColors};
compare.printMessage(flags, dc, "b", "a");
}
CORRADE_COMPARE(out.str(), data.messageReverse);
}
}}}}
CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CompareMaterialTest)
Loading…
Cancel
Save