From 79c42fee6e9e5b9e951a833cc43d8ac4b6ac1d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 9 Apr 2023 11:33:20 +0200 Subject: [PATCH] DebugTools: special-case CompareImage delta calculation for integer types. Integer / packed formats are the majority of uses for this utility, and for them the additional complexity with NaN and infinity handling isn't needed at all. Running ShadersMeshVisualizerGLTest with this change in a Debug build led to its runtime being reduced by about 35%, the total test time excluding benchmarks went from 14.5 seconds to 11. Not bad. --- src/Magnum/DebugTools/CompareImage.cpp | 31 ++++++++++++++++- .../DebugTools/Test/CompareImageTest.cpp | 34 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/Magnum/DebugTools/CompareImage.cpp b/src/Magnum/DebugTools/CompareImage.cpp index 88ad04395..d4c67ff8f 100644 --- a/src/Magnum/DebugTools/CompareImage.cpp +++ b/src/Magnum/DebugTools/CompareImage.cpp @@ -52,7 +52,11 @@ namespace Magnum { namespace DebugTools { namespace Implementation { namespace { -template Float calculateImageDelta(const Containers::StridedArrayView2D>& actual, const Containers::StridedArrayView2D>& expected, const Containers::StridedArrayView2D& output) { +/* There's a separate implementation for integral types, as those don't need + any additional logic for handling NaN and infinity values, allowing the + comparison to be much simpler & faster. */ + +template::value, int>::type = 0> Float calculateImageDelta(const Containers::StridedArrayView2D>& actual, const Containers::StridedArrayView2D>& expected, const Containers::StridedArrayView2D& output) { CORRADE_INTERNAL_ASSERT(actual.size() == output.size()); CORRADE_INTERNAL_ASSERT(output.size() == expected.size()); @@ -93,6 +97,31 @@ template Float calculateImageDelta(const Containers:: return max; } +template::value, int>::type = 0> Float calculateImageDelta(const Containers::StridedArrayView2D>& actual, const Containers::StridedArrayView2D>& expected, const Containers::StridedArrayView2D& output) { + CORRADE_INTERNAL_ASSERT(actual.size() == output.size()); + CORRADE_INTERNAL_ASSERT(output.size() == expected.size()); + + /* Calculate deltas and maximal value of them */ + Float max{}; + for(std::size_t i = 0, iMax = expected.size()[0]; i != iMax; ++i) { + Containers::StridedArrayView1D> actualRow = actual[i]; + Containers::StridedArrayView1D> expectedRow = expected[i]; + Containers::StridedArrayView1D outputRow = output[i]; + + for(std::size_t j = 0, jMax = expectedRow.size(); j != jMax; ++j) { + /* Explicitly convert from T to Float */ + auto actualPixel = Math::Vector(actualRow[j]); + auto expectedPixel = Math::Vector(expectedRow[j]); + + Math::Vector diff = Math::abs(actualPixel - expectedPixel); + outputRow[j] = diff.sum()/size; + max = Math::max(max, outputRow[j]); + } + } + + return max; +} + } Containers::Triple, Float, Float> calculateImageDelta(const PixelFormat actualFormat, const Containers::StridedArrayView3D& actualPixels, const ImageView2D& expected) { diff --git a/src/Magnum/DebugTools/Test/CompareImageTest.cpp b/src/Magnum/DebugTools/Test/CompareImageTest.cpp index 6cfd8bc12..0dc09988e 100644 --- a/src/Magnum/DebugTools/Test/CompareImageTest.cpp +++ b/src/Magnum/DebugTools/Test/CompareImageTest.cpp @@ -60,6 +60,7 @@ struct CompareImageTest: TestSuite::Tester { void formatImplementationSpecific(); void calculateDelta(); + void calculateDeltaInteger(); void calculateDeltaStorage(); void calculateDeltaSpecials(); void calculateDeltaSpecials3(); @@ -132,6 +133,7 @@ CompareImageTest::CompareImageTest() { &CompareImageTest::formatImplementationSpecific, &CompareImageTest::calculateDelta, + &CompareImageTest::calculateDeltaInteger, &CompareImageTest::calculateDeltaStorage, &CompareImageTest::calculateDeltaSpecials, &CompareImageTest::calculateDeltaSpecials3, @@ -289,6 +291,38 @@ void CompareImageTest::calculateDelta() { CORRADE_COMPARE(deltaMaxMean.third(), 0.208889f); } +void CompareImageTest::calculateDeltaInteger() { + /* Like ActualRedData, ExpectedRedData and DeltaRed, just multiplied 100 + times and saved into a Short */ + const Short actualRedData[]{ + 30, 100, 90, + 90, 60, 20, + -10, 100, 0 + }; + + const Short expectedRedData[]{ + 65, 100, 60, + 91, 60, 10, + 2, 0, 0 + }; + + const Float deltaRed[]{ + 35.0f, 0.0f, 30.0f, + 1.0f, 0.0f, 10.0f, + 12.0f, 100.0f, 0.0f + }; + + const ImageView2D actualRed{PixelStorage{}.setAlignment(2), PixelFormat::R16I, {3, 3}, actualRedData}; + const ImageView2D expectedRed{PixelStorage{}.setAlignment(2), PixelFormat::R16I, {3, 3}, expectedRedData}; + + Containers::Triple, Float, Float> deltaMaxMean = Implementation::calculateImageDelta(actualRed.format(), actualRed.pixels(), expectedRed); + CORRADE_COMPARE_AS(deltaMaxMean.first(), + Containers::arrayView(deltaRed), + TestSuite::Compare::Container); + CORRADE_COMPARE(deltaMaxMean.second(), 100.0f); + CORRADE_COMPARE(deltaMaxMean.third(), 20.8889f); +} + /* Different storage for each */ const UnsignedByte ActualRgbData[] = { 0, 0, 0, 0, 0, 0, 0, 0,