Skip to content

Commit

Permalink
DebugTools: special-case CompareImage delta calculation for integer t…
Browse files Browse the repository at this point in the history
…ypes.

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.
  • Loading branch information
mosra committed Apr 9, 2023
1 parent d41ab08 commit 79c42fe
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
31 changes: 30 additions & 1 deletion src/Magnum/DebugTools/CompareImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ namespace Magnum { namespace DebugTools { namespace Implementation {

namespace {

template<std::size_t size, class T> Float calculateImageDelta(const Containers::StridedArrayView2D<const Math::Vector<size, T>>& actual, const Containers::StridedArrayView2D<const Math::Vector<size, T>>& expected, const Containers::StridedArrayView2D<Float>& 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<std::size_t size, class T, typename std::enable_if<Math::IsFloatingPoint<T>::value, int>::type = 0> Float calculateImageDelta(const Containers::StridedArrayView2D<const Math::Vector<size, T>>& actual, const Containers::StridedArrayView2D<const Math::Vector<size, T>>& expected, const Containers::StridedArrayView2D<Float>& output) {
CORRADE_INTERNAL_ASSERT(actual.size() == output.size());
CORRADE_INTERNAL_ASSERT(output.size() == expected.size());

Expand Down Expand Up @@ -93,6 +97,31 @@ template<std::size_t size, class T> Float calculateImageDelta(const Containers::
return max;
}

template<std::size_t size, class T, typename std::enable_if<Math::IsIntegral<T>::value, int>::type = 0> Float calculateImageDelta(const Containers::StridedArrayView2D<const Math::Vector<size, T>>& actual, const Containers::StridedArrayView2D<const Math::Vector<size, T>>& expected, const Containers::StridedArrayView2D<Float>& 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<const Math::Vector<size, T>> actualRow = actual[i];
Containers::StridedArrayView1D<const Math::Vector<size, T>> expectedRow = expected[i];
Containers::StridedArrayView1D<Float> 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<size, Float>(actualRow[j]);
auto expectedPixel = Math::Vector<size, Float>(expectedRow[j]);

Math::Vector<size, Float> diff = Math::abs(actualPixel - expectedPixel);
outputRow[j] = diff.sum()/size;
max = Math::max(max, outputRow[j]);
}
}

return max;
}

}

Containers::Triple<Containers::Array<Float>, Float, Float> calculateImageDelta(const PixelFormat actualFormat, const Containers::StridedArrayView3D<const char>& actualPixels, const ImageView2D& expected) {
Expand Down
34 changes: 34 additions & 0 deletions src/Magnum/DebugTools/Test/CompareImageTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct CompareImageTest: TestSuite::Tester {
void formatImplementationSpecific();

void calculateDelta();
void calculateDeltaInteger();
void calculateDeltaStorage();
void calculateDeltaSpecials();
void calculateDeltaSpecials3();
Expand Down Expand Up @@ -132,6 +133,7 @@ CompareImageTest::CompareImageTest() {
&CompareImageTest::formatImplementationSpecific,

&CompareImageTest::calculateDelta,
&CompareImageTest::calculateDeltaInteger,
&CompareImageTest::calculateDeltaStorage,
&CompareImageTest::calculateDeltaSpecials,
&CompareImageTest::calculateDeltaSpecials3,
Expand Down Expand Up @@ -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<Containers::Array<Float>, 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,
Expand Down

0 comments on commit 79c42fe

Please sign in to comment.