Browse Source

Math: add batch integer packing/unpacking and casting functions.

To be used for conversion of the upcoming mesh attribute types.
pull/275/head
Vladimír Vondruš 6 years ago
parent
commit
daf471381e
  1. 9
      src/Magnum/CMakeLists.txt
  2. 4
      src/Magnum/Math/Packing.h
  3. 242
      src/Magnum/Math/PackingBatch.cpp
  4. 234
      src/Magnum/Math/PackingBatch.h
  5. 3
      src/Magnum/Math/Test/CMakeLists.txt
  6. 469
      src/Magnum/Math/Test/PackingBatchTest.cpp

9
src/Magnum/CMakeLists.txt

@ -85,6 +85,9 @@ set(MagnumMath_SRCS
Math/Packing.cpp
Math/instantiation.cpp)
set(MagnumMath_GracefulAssert_SRCS
Math/PackingBatch.cpp)
# Objects shared between main and math test library
add_library(MagnumMathObjects OBJECT ${MagnumMath_SRCS})
target_include_directories(MagnumMathObjects PUBLIC
@ -120,6 +123,7 @@ set_target_properties(MagnumObjects PROPERTIES FOLDER "Magnum")
add_library(Magnum ${SHARED_OR_STATIC}
$<TARGET_OBJECTS:MagnumMathObjects>
$<TARGET_OBJECTS:MagnumObjects>
${MagnumMath_GracefulAssert_SRCS}
${Magnum_GracefulAssert_SRCS})
set_target_properties(Magnum PROPERTIES
DEBUG_POSTFIX "-d"
@ -194,11 +198,12 @@ if(BUILD_TESTS)
# Math library with graceful assert for testing
add_library(MagnumMathTestLib ${SHARED_OR_STATIC}
$<TARGET_OBJECTS:MagnumMathObjects>
${PROJECT_SOURCE_DIR}/src/dummy.cpp) # XCode workaround, see file comment for details
${MagnumMath_GracefulAssert_SRCS})
target_include_directories(MagnumMathTestLib PUBLIC
${PROJECT_SOURCE_DIR}/src
${PROJECT_BINARY_DIR}/src)
target_compile_definitions(MagnumMathTestLib PRIVATE "CORRADE_GRACEFUL_ASSERT")
target_compile_definitions(MagnumMathTestLib PRIVATE
"CORRADE_GRACEFUL_ASSERT" "Magnum_EXPORTS")
set_target_properties(MagnumMathTestLib PROPERTIES
DEBUG_POSTFIX "-d"
FOLDER "Magnum/Math")

4
src/Magnum/Math/Packing.h

@ -67,7 +67,7 @@ value in range @f$ [0, 1] @f$ or from *signed* integral to range @f$ [-1, 1] @f$
@attention
@snippet MagnumMath.cpp unpack-template-explicit
@see @ref pack()
@see @ref pack(), @ref unpackInto()
*/
template<class FloatingPoint, class Integral> inline FloatingPoint unpack(const Integral& value);
@ -133,7 +133,7 @@ given *signed* integral type.
@attention Return value for floating point numbers outside the normalized
range is undefined.
@see @ref unpack()
@see @ref unpack(), @ref packInto()
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<class Integral, class FloatingPoint> inline Integral pack(const FloatingPoint& value);

242
src/Magnum/Math/PackingBatch.cpp

@ -0,0 +1,242 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
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 "PackingBatch.h"
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Assert.h>
#include "Magnum/Math/Packing.h"
namespace Magnum { namespace Math {
namespace {
template<class T> inline void unpackUnsignedIntoImplementation(const Corrade::Containers::StridedArrayView2D<const T>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
CORRADE_ASSERT(src.size() == dst.size(),
"Math::unpackInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), );
CORRADE_ASSERT(src.template isContiguous<1>() && dst.isContiguous<1>(),
"Math::unpackInto(): second view dimension is not contiguous", );
/** @todo SSE/NEON implementations exploiting second dimension size/stride,
contiguity etc */
/* Caching values to avoid inline function calls in ebug builds */
constexpr Float bitMax = Implementation::bitMax<T>();
const char* srcPtr = reinterpret_cast<const char*>(src.data());
char* dstPtr = reinterpret_cast<char*>(dst.data());
const std::ptrdiff_t srcStride = src.stride()[0];
const std::ptrdiff_t dstStride = dst.stride()[0];
const std::size_t maxJ = src.size()[1];
for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) {
const T* srcPtrI = reinterpret_cast<const T*>(srcPtr);
Float* dstPtrI = reinterpret_cast<Float*>(dstPtr);
for(std::size_t j = 0; j != maxJ; ++j)
*dstPtrI++ = *srcPtrI++/bitMax;
srcPtr += srcStride;
dstPtr += dstStride;
}
}
}
void unpackInto(const Corrade::Containers::StridedArrayView2D<const UnsignedByte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
unpackUnsignedIntoImplementation(src, dst);
}
void unpackInto(const Corrade::Containers::StridedArrayView2D<const UnsignedShort>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
unpackUnsignedIntoImplementation(src, dst);
}
namespace {
template<class T> inline void unpackSignedIntoImplementation(const Corrade::Containers::StridedArrayView2D<const T>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
CORRADE_ASSERT(src.size() == dst.size(),
"Math::unpackInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), );
CORRADE_ASSERT(src.template isContiguous<1>() && dst.isContiguous<1>(),
"Math::unpackInto(): second view dimension is not contiguous", );
/** @todo SSE/NEON implementations exploiting second dimension size/stride,
contiguity etc */
/* Caching values to avoid inline function calls in debug builds */
constexpr Float bitMax = Implementation::bitMax<T>();
const char* srcPtr = reinterpret_cast<const char*>(src.data());
char* dstPtr = reinterpret_cast<char*>(dst.data());
const std::ptrdiff_t srcStride = src.stride()[0];
const std::ptrdiff_t dstStride = dst.stride()[0];
const std::size_t maxJ = src.size()[1];
for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) {
const T* srcPtrI = reinterpret_cast<const T*>(srcPtr);
Float* dstPtrI = reinterpret_cast<Float*>(dstPtr);
for(std::size_t j = 0; j != maxJ; ++j) {
const Float value = *srcPtrI++/bitMax;
/* Avoiding a max() call in Debug */
*dstPtrI++ = value < -1.0f ? -1.0f : value;
}
srcPtr += srcStride;
dstPtr += dstStride;
}
}
}
void unpackInto(const Corrade::Containers::StridedArrayView2D<const Byte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
unpackSignedIntoImplementation(src, dst);
}
void unpackInto(const Corrade::Containers::StridedArrayView2D<const Short>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
unpackSignedIntoImplementation(src, dst);
}
namespace {
template<class T> inline void packIntoImplementation(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<T>& dst) {
CORRADE_ASSERT(src.size() == dst.size(),
"Math::packInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), );
CORRADE_ASSERT(src.isContiguous<1>() && dst.template isContiguous<1>(),
"Math::packInto(): second view dimension is not contiguous", );
/** @todo SSE/NEON implementations exploiting second dimension size/stride,
contiguity etc */
/* Caching values to avoid inline function calls in debug builds */
constexpr Float bitMax = Implementation::bitMax<T>();
const char* srcPtr = reinterpret_cast<const char*>(src.data());
char* dstPtr = reinterpret_cast<char*>(dst.data());
const std::ptrdiff_t srcStride = src.stride()[0];
const std::ptrdiff_t dstStride = dst.stride()[0];
const std::size_t maxJ = src.size()[1];
for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) {
const Float* srcPtrI = reinterpret_cast<const Float*>(srcPtr);
T* dstPtrI = reinterpret_cast<T*>(dstPtr);
for(std::size_t j = 0; j != maxJ; ++j)
/** @todo provide a version that doesn't do rounding */
*dstPtrI++ = std::round(*srcPtrI++*bitMax);
srcPtr += srcStride;
dstPtr += dstStride;
}
}
}
void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedByte>& dst) {
packIntoImplementation(src, dst);
}
void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedShort>& dst) {
packIntoImplementation(src, dst);
}
void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Byte>& dst) {
packIntoImplementation(src, dst);
}
void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Short>& dst) {
packIntoImplementation(src, dst);
}
namespace {
template<class T, class U> inline void castIntoImplementation(const Corrade::Containers::StridedArrayView2D<const T>& src, const Corrade::Containers::StridedArrayView2D<U>& dst) {
CORRADE_ASSERT(src.size() == dst.size(),
"Math::castInto(): wrong destination size, got" << dst.size() << "but expected" << src.size(), );
CORRADE_ASSERT(src.template isContiguous<1>() && dst.template isContiguous<1>(),
"Math::castInto(): second view dimension is not contiguous", );
/** @todo SSE/NEON implementations exploiting second dimension size/stride,
contiguity etc */
/* Caching values to avoid inline function calls in debug buílds */
const char* srcPtr = reinterpret_cast<const char*>(src.data());
char* dstPtr = reinterpret_cast<char*>(dst.data());
const std::ptrdiff_t srcStride = src.stride()[0];
const std::ptrdiff_t dstStride = dst.stride()[0];
const std::size_t maxJ = src.size()[1];
for(std::size_t i = 0, maxI = src.size()[0]; i != maxI; ++i) {
const T* srcPtrI = reinterpret_cast<const T*>(srcPtr);
U* dstPtrI = reinterpret_cast<U*>(dstPtr);
for(std::size_t j = 0; j != maxJ; ++j)
*dstPtrI++ = U(*srcPtrI++);
srcPtr += srcStride;
dstPtr += dstStride;
}
}
}
void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedByte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Byte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedShort>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Short>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedInt>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Int>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedByte>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Byte>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedShort>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Short>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedInt>& dst) {
castIntoImplementation(src, dst);
}
void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Int>& dst) {
castIntoImplementation(src, dst);
}
}}

234
src/Magnum/Math/PackingBatch.h

@ -0,0 +1,234 @@
#ifndef Magnum_Math_PackingBatch_h
#define Magnum_Math_PackingBatch_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
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 Functions @ref Magnum::Math::packInto(), @ref Magnum::Math::unpackInto(), @ref Magnum::Math::castInto()
* @m_since_latest
*/
#include <Corrade/Containers/Containers.h>
#include "Magnum/Types.h"
#include "Magnum/visibility.h"
namespace Magnum { namespace Math {
/**
@{ @name Batch packing functions
These functions process an ubounded range of values, as opposed to single
vectors or scalars.
*/
/**
@brief Unpack unsigned integral values into a floating-point representation
@param[in] src Source integral values
@param[out] dst Destination floating-point values
@m_since_latest
Converts integral values from full range of given *unsigned* integral type to
floating-point values in range @f$ [0, 1] @f$. Second dimension is meant to
contain vector/matrix components, or have a size of 1 for scalars. Expects that
@p src and @p dst have the same size and that the second dimension in both is
contiguous.
@see @ref packInto(), @ref castInto(),
@ref Corrade::Containers::StridedArrayView::isContiguous()
*/
MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D<const UnsignedByte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D<const UnsignedShort>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
@brief Unpack signed integral values into a floating-point representation
@param[in] src Source integral values
@param[out] dst Destination floating-point values
@m_since_latest
Converts integral values from full range of given *signed* integral type to
floating-point values in range @f$ [-1, 1] @f$. Second dimension is meant to
contain vector/matrix components, or have a size of 1 for scalars. Expects that
@p src and @p dst have the same size and that the second dimension in both is
contiguous.
@see @ref packInto(), @ref castInto(),
@ref Corrade::Containers::StridedArrayView::isContiguous()
*/
MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D<const Byte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void unpackInto(const Corrade::Containers::StridedArrayView2D<const Short>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
@brief Pack floating-point values into an integer representation
@param[in] src Source floating-point values
@param[out] dst Destination integral values
@m_since_latest
Converts floating-point value in range @f$ [0, 1] @f$ to full range of
given *unsigned* integral type or range @f$ [-1, 1] @f$ to full range of
given *signed* integral type. Second dimension is meant to contain
vector/matrix components, or have a size of 1 for scalars. Expects that @p src
and @p dst have the same size and that the second dimension in both is
contiguous.
@attention Conversion result for floating-point numbers outside the normalized
range is undefined.
@see @ref unpackInto(), @ref castInto(),
@ref Corrade::Containers::StridedArrayView::isContiguous()
*/
MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedByte>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Byte>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedShort>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void packInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Short>& dst);
/**
@brief Cast integer values into a floating-point representation
@param[in] src Source integral values
@param[out] dst Destination floating-point values
@m_since_latest
Unlike @ref packInto(), this function performs only an equivalent of
@cpp Float(a) @ce over the range, so e.g. @cpp 135 @ce becomes @cpp 135.0f @ce.
Second dimension is meant to contain vector/matrix components, or have a size
of 1 for scalars. Expects that @p src and @p dst have the same size and that
the second dimension in both is contiguous.
@attention Numbers with more than 23 bits of precision will not be represented
accurately when cast into a @ref Magnum::Float "Float".
@see @ref castInto(const Corrade::Containers::StridedArrayView2D<const Float>&, const Corrade::Containers::StridedArrayView2D<UnsignedByte>&),
@ref Corrade::Containers::StridedArrayView::isContiguous()
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedByte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Byte>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedShort>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Short>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const UnsignedInt>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Int>& src, const Corrade::Containers::StridedArrayView2D<Float>& dst);
/**
@brief Cast floating-point values into an integer representation
@param[in] src Source floating-point values
@param[out] dst Destination integral values
@m_since_latest
Unlike @ref packInto(), this function performs only an equivalent of
@cpp Float(a) @ce over the range, so e.g. @cpp 135 @ce becomes @cpp 135.0f @ce.
Second dimension is meant to contain vector/matrix components, or have a size
of 1 for scalars. Expects that @p src and @p dst have the same size and that
the second dimension in both is contiguous.
@attention Numbers with more than 23 bits of precision will not be represented
accurately when cast into a @ref Magnum::Float "Float".
@see @ref castInto(const Corrade::Containers::StridedArrayView2D<const Float>&, const Corrade::Containers::StridedArrayView2D<UnsignedByte>&),
@ref Corrade::Containers::StridedArrayView::isContiguous()
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedByte>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Byte>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedShort>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Short>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<UnsignedInt>& dst);
/**
* @overload
* @m_since_latest
*/
MAGNUM_EXPORT void castInto(const Corrade::Containers::StridedArrayView2D<const Float>& src, const Corrade::Containers::StridedArrayView2D<Int>& dst);
/*@}*/
}}
#endif

3
src/Magnum/Math/Test/CMakeLists.txt

@ -29,6 +29,7 @@ corrade_add_test(MathFunctionsTest FunctionsTest.cpp LIBRARIES MagnumMathTestLib
corrade_add_test(MathFunctionsBatchTest FunctionsBatchTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathHalfTest HalfTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathPackingTest PackingTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathPackingBatchTest PackingBatchTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathTagsTest TagsTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathTypeTraitsTest TypeTraitsTest.cpp LIBRARIES MagnumMathTestLib)
@ -90,8 +91,10 @@ set_target_properties(
MathBoolVectorTest
MathConstantsTest
MathFunctionsTest
MathFunctionsBatchTest
MathHalfTest
MathPackingTest
MathPackingBatchTest
MathTagsTest
MathTypeTraitsTest

469
src/Magnum/Math/Test/PackingBatchTest.cpp

@ -0,0 +1,469 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
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/StridedArrayView.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Packing.h"
#include "Magnum/Math/PackingBatch.h"
#include "Magnum/Math/Vector4.h"
namespace Magnum { namespace Math { namespace Test { namespace {
struct PackingBatchTest: Corrade::TestSuite::Tester {
explicit PackingBatchTest();
void unpackUnsignedByte();
void unpackUnsignedShort();
void unpackSignedByte();
void unpackSignedShort();
void packUnsignedByte();
void packUnsignedShort();
void packSignedByte();
void packSignedShort();
template<class T> void castUnsigned();
template<class T> void castSigned();
template<class T> void assertionsPackUnpack();
template<class T> void assertionsCast();
};
PackingBatchTest::PackingBatchTest() {
addTests({&PackingBatchTest::unpackUnsignedByte,
&PackingBatchTest::unpackUnsignedShort,
&PackingBatchTest::unpackSignedByte,
&PackingBatchTest::unpackSignedShort,
&PackingBatchTest::packUnsignedByte,
&PackingBatchTest::packUnsignedShort,
&PackingBatchTest::packSignedByte,
&PackingBatchTest::packSignedShort,
&PackingBatchTest::castUnsigned<UnsignedByte>,
&PackingBatchTest::castUnsigned<UnsignedShort>,
&PackingBatchTest::castUnsigned<UnsignedInt>,
&PackingBatchTest::castSigned<Byte>,
&PackingBatchTest::castSigned<Short>,
&PackingBatchTest::castSigned<Int>,
&PackingBatchTest::assertionsPackUnpack<UnsignedByte>,
&PackingBatchTest::assertionsPackUnpack<Byte>,
&PackingBatchTest::assertionsPackUnpack<UnsignedShort>,
&PackingBatchTest::assertionsPackUnpack<Short>,
&PackingBatchTest::assertionsCast<UnsignedByte>,
&PackingBatchTest::assertionsCast<Byte>,
&PackingBatchTest::assertionsCast<UnsignedShort>,
&PackingBatchTest::assertionsCast<Short>,
&PackingBatchTest::assertionsCast<UnsignedInt>,
&PackingBatchTest::assertionsCast<Int>});
}
typedef Math::Vector2<UnsignedByte> Vector2ub;
typedef Math::Vector2<UnsignedShort> Vector2us;
typedef Math::Vector2<Byte> Vector2b;
typedef Math::Vector2<Short> Vector2s;
typedef Math::Vector2<Float> Vector2;
typedef Math::Vector3<Float> Vector3;
typedef Math::Vector4<Float> Vector4;
void PackingBatchTest::unpackUnsignedByte() {
/* Test data adapted from PackingTest */
struct Data {
Vector2ub src;
Vector2 dst;
} data[]{
{{0, 89}, {}},
{{149, 255}, {}},
{{0, 255}, {}}
};
constexpr Vector2 expected[] {
{0.0f, 0.34902f},
{0.584314f, 1.0f},
{0.0f, 1.0f}
};
Corrade::Containers::StridedArrayView1D<Vector2ub> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
unpackInto(Corrade::Containers::arrayCast<2, UnsignedByte>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::unpack<Vector2>(data[i].src), data[i].dst);
}
void PackingBatchTest::unpackUnsignedShort() {
/* Test data adapted from PackingTest */
struct Data {
Vector2us src;
Vector2 dst;
} data[]{
{{0, 8192}, {}},
{{49152, 65535}, {}},
{{0, 65535}, {}}
};
constexpr Vector2 expected[] {
{0.0f, 0.125002f},
{0.750011f, 1.0f},
{0.0f, 1.0f}
};
Corrade::Containers::StridedArrayView1D<Vector2us> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
unpackInto(Corrade::Containers::arrayCast<2, UnsignedShort>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::unpack<Vector2>(data[i].src), data[i].dst);
}
void PackingBatchTest::unpackSignedByte() {
/* Test data adapted from PackingTest */
struct Data {
Vector2b src;
Vector2 dst;
} data[]{
{{0, 127}, {}},
{{37, -72}, {}},
{{-127, -128}, {}}
};
constexpr Vector2 expected[] {
{0.0f, 1.0f},
{0.291339f, -0.566929f},
{-1.0f, -1.0f}
};
Corrade::Containers::StridedArrayView1D<Vector2b> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
unpackInto(Corrade::Containers::arrayCast<2, Byte>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::unpack<Vector2>(data[i].src), data[i].dst);
}
void PackingBatchTest::unpackSignedShort() {
/* Test data adapted from PackingTest */
struct Data {
Vector2s src;
Vector2 dst;
} data[]{
{{0, 16384}, {}},
{{-16384, 32767}, {}},
{{-32767, -32768}, {}}
};
constexpr Vector2 expected[] {
{0.0f, 0.500015f},
{-0.500015f, 1.0f},
{-1.0f, -1.0f}
};
Corrade::Containers::StridedArrayView1D<Vector2s> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
unpackInto(Corrade::Containers::arrayCast<2, Short>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::unpack<Vector2>(data[i].src), data[i].dst);
}
void PackingBatchTest::packUnsignedByte() {
/* Test data adapted from PackingTest */
struct Data {
Vector2 src;
Vector2ub dst;
} data[]{
{{0.0f, 0.0000001f}, {}},
{{0.4357f, 0.5f}, {}},
{{1.0f, 0.9999999f}, {}}
};
constexpr Vector2ub expected[] {
{0, 0},
{111, 128},
{255, 255}
};
Corrade::Containers::StridedArrayView1D<Vector2> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2ub> dst{data, &data[0].dst, 3, sizeof(Data)};
packInto(Corrade::Containers::arrayCast<2, Float>(src),
Corrade::Containers::arrayCast<2, UnsignedByte>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::pack<Vector2ub>(data[i].src), data[i].dst);
}
void PackingBatchTest::packUnsignedShort() {
/* Test data adapted from PackingTest */
struct Data {
Vector2 src;
Vector2us dst;
} data[]{
{{0.0f, 0.0000001f}, {}},
{{0.4357f, 0.5f}, {}},
{{1.0f, 0.9999999f}, {}}
};
constexpr Vector2us expected[] {
{0, 0},
{28554, 32768},
{65535, 65535}
};
Corrade::Containers::StridedArrayView1D<Vector2> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2us> dst{data, &data[0].dst, 3, sizeof(Data)};
packInto(Corrade::Containers::arrayCast<2, Float>(src),
Corrade::Containers::arrayCast<2, UnsignedShort>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::pack<Vector2us>(data[i].src), data[i].dst);
}
void PackingBatchTest::packSignedByte() {
/* Test data adapted from PackingTest */
struct Data {
Vector2 src;
Vector2b dst;
} data[]{
{{-1.0f, -0.732f}, {}},
{{0.0f, 0.1357f}, {}},
{{1.0f, 0.9999999f}, {}}
};
constexpr Vector2b expected[] {
{-127, -93},
{0, 17},
{127, 127}
};
Corrade::Containers::StridedArrayView1D<Vector2> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2b> dst{data, &data[0].dst, 3, sizeof(Data)};
packInto(Corrade::Containers::arrayCast<2, Float>(src),
Corrade::Containers::arrayCast<2, Byte>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::pack<Vector2b>(data[i].src), data[i].dst);
}
void PackingBatchTest::packSignedShort() {
/* Test data adapted from PackingTest */
struct Data {
Vector2 src;
Vector2s dst;
} data[]{
{{-1.0f, -0.33f}, {}},
{{0.0f, 0.66f}, {}},
{{1.0f, 0.9999999f}, {}}
};
constexpr Vector2s expected[] {
{-32767, -10813},
{0, 21626},
{32767, 32767}
};
Corrade::Containers::StridedArrayView1D<Vector2> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2s> dst{data, &data[0].dst, 3, sizeof(Data)};
packInto(Corrade::Containers::arrayCast<2, Float>(src),
Corrade::Containers::arrayCast<2, Short>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expected),
Corrade::TestSuite::Compare::Container);
/* Ensure the results are consistent with non-batch APIs */
for(std::size_t i = 0; i != Corrade::Containers::arraySize(data); ++i)
CORRADE_COMPARE(Math::pack<Vector2s>(data[i].src), data[i].dst);
}
template<class T> void PackingBatchTest::castUnsigned() {
setTestCaseTemplateName(TypeTraits<T>::name());
struct Data {
Math::Vector2<T> src;
Vector2 dst;
} data[]{
{{0, 89}, {}},
{{149, 22}, {}},
{{13, 255}, {}}
};
constexpr Vector2 expectedFloat[] {
{0.0f, 89.0f},
{149.0f, 22.0f},
{13.0f, 255.0f}
};
constexpr Math::Vector2<T> expectedInteger[] {
{0, 89},
{149, 22},
{13, 255}
};
Corrade::Containers::StridedArrayView1D<Math::Vector2<T>> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
castInto(Corrade::Containers::arrayCast<2, T>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expectedFloat),
Corrade::TestSuite::Compare::Container);
/* Test the other way around as well */
castInto(Corrade::Containers::arrayCast<2, Float>(dst),
Corrade::Containers::arrayCast<2, T>(src));
CORRADE_COMPARE_AS(src, Corrade::Containers::stridedArrayView(expectedInteger),
Corrade::TestSuite::Compare::Container);
}
template<class T> void PackingBatchTest::castSigned() {
setTestCaseTemplateName(TypeTraits<T>::name());
struct Data {
Math::Vector2<T> src;
Vector2 dst;
} data[]{
{{0, -89}, {}},
{{-119, 22}, {}},
{{13, 127}, {}}
};
constexpr Vector2 expectedFloat[] {
{0.0f, -89.0f},
{-119.0f, 22.0f},
{13.0f, 127.0f}
};
constexpr Math::Vector2<T> expectedInteger[] {
{0, -89},
{-119, 22},
{13, 127}
};
Corrade::Containers::StridedArrayView1D<Math::Vector2<T>> src{data, &data[0].src, 3, sizeof(Data)};
Corrade::Containers::StridedArrayView1D<Vector2> dst{data, &data[0].dst, 3, sizeof(Data)};
castInto(Corrade::Containers::arrayCast<2, T>(src),
Corrade::Containers::arrayCast<2, Float>(dst));
CORRADE_COMPARE_AS(dst, Corrade::Containers::stridedArrayView(expectedFloat),
Corrade::TestSuite::Compare::Container);
/* Test the other way around as well */
castInto(Corrade::Containers::arrayCast<2, Float>(dst),
Corrade::Containers::arrayCast<2, T>(src));
CORRADE_COMPARE_AS(src, Corrade::Containers::stridedArrayView(expectedInteger),
Corrade::TestSuite::Compare::Container);
}
template<class T> void PackingBatchTest::assertionsPackUnpack() {
Math::Vector2<T> data[2]{};
Vector2 resultWrongCount[1]{};
Vector3 resultWrongVectorSize[2]{};
Vector4 resultNonContiguous[2]{};
auto src = Corrade::Containers::arrayCast<2, T>(
Corrade::Containers::arrayView(data));
auto dstWrongCount = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultWrongCount));
auto dstWrongVectorSize = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultWrongVectorSize));
auto dstNotContiguous = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultNonContiguous)).every({1, 2});
std::ostringstream out;
Error redirectError{&out};
unpackInto(src, dstWrongCount);
unpackInto(src, dstWrongVectorSize);
unpackInto(src, dstNotContiguous);
packInto(dstWrongCount, src);
packInto(dstWrongVectorSize, src);
packInto(dstNotContiguous, src);
CORRADE_COMPARE(out.str(),
"Math::unpackInto(): wrong destination size, got {1, 2} but expected {2, 2}\n"
"Math::unpackInto(): wrong destination size, got {2, 3} but expected {2, 2}\n"
"Math::unpackInto(): second view dimension is not contiguous\n"
"Math::packInto(): wrong destination size, got {2, 2} but expected {1, 2}\n"
"Math::packInto(): wrong destination size, got {2, 2} but expected {2, 3}\n"
"Math::packInto(): second view dimension is not contiguous\n");
}
template<class T> void PackingBatchTest::assertionsCast() {
Math::Vector2<T> data[2]{};
Vector2 resultWrongCount[1]{};
Vector3 resultWrongVectorSize[2]{};
Vector4 resultNonContiguous[2]{};
auto src = Corrade::Containers::arrayCast<2, T>(
Corrade::Containers::arrayView(data));
auto dstWrongCount = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultWrongCount));
auto dstWrongVectorSize = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultWrongVectorSize));
auto dstNotContiguous = Corrade::Containers::arrayCast<2, Float>(
Corrade::Containers::arrayView(resultNonContiguous)).every({1, 2});
std::ostringstream out;
Error redirectError{&out};
castInto(src, dstWrongCount);
castInto(src, dstWrongVectorSize);
castInto(src, dstNotContiguous);
castInto(dstWrongCount, src);
castInto(dstWrongVectorSize, src);
castInto(dstNotContiguous, src);
CORRADE_COMPARE(out.str(),
"Math::castInto(): wrong destination size, got {1, 2} but expected {2, 2}\n"
"Math::castInto(): wrong destination size, got {2, 3} but expected {2, 2}\n"
"Math::castInto(): second view dimension is not contiguous\n"
"Math::castInto(): wrong destination size, got {2, 2} but expected {1, 2}\n"
"Math::castInto(): wrong destination size, got {2, 2} but expected {2, 3}\n"
"Math::castInto(): second view dimension is not contiguous\n");
}
}}}}
CORRADE_TEST_MAIN(Magnum::Math::Test::PackingBatchTest)
Loading…
Cancel
Save