From 06b350871993e08cc31124faca76baaf86e158bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 6 Mar 2014 12:44:17 +0100 Subject: [PATCH] MeshTools: refactored implementation of interleave(). Everything are now pure functions, no shared state, no useless functor class wrapping. Still needed to wrap attribute count and stride computation functions in struct to make proper overload resolution working without the need to deinline the functions (they have cyclic dependencies). --- src/Magnum/MeshTools/Interleave.h | 181 +++++++++---------- src/Magnum/MeshTools/Test/InterleaveTest.cpp | 16 +- 2 files changed, 93 insertions(+), 104 deletions(-) diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index 8b572c7eb..43fccbb4b 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -41,94 +41,58 @@ namespace Magnum { namespace MeshTools { namespace Implementation { -class Interleave { - public: - Interleave(): _attributeCount(0), _stride(0) {} - - template std::tuple> operator()(const T&... attributes) { - /* Compute buffer size and stride */ - _attributeCount = attributeCount(attributes...); - Containers::Array data; - if(_attributeCount && _attributeCount != ~std::size_t(0)) { - _stride = stride(attributes...); - - /* Create output buffer */ - data = Containers::Array(_attributeCount*_stride); - - /* Save the data */ - write(data.begin(), attributes...); - } - - return std::make_tuple(_attributeCount, _stride, std::move(data)); - } - - template void operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - Containers::Array data; - std::tie(std::ignore, std::ignore, data) = operator()(attributes...); - - mesh.setVertexCount(_attributeCount); - buffer.setData(data, usage); - } - - /* Specialization for only one attribute array */ - template typename std::enable_if::value, void>::type operator()(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { - mesh.setVertexCount(attribute.size()); - buffer.setData(attribute, usage); - } - - template static typename std::enable_if::value, std::size_t>::type attributeCount(const T& first, const U&... next) { - CORRADE_ASSERT(sizeof...(next) == 0 || attributeCount(next...) == first.size() || attributeCount(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, expected" << first.size() << "but got" << attributeCount(next...), 0); - - return first.size(); - } - - template static std::size_t attributeCount(std::size_t, const T&... next) { - return attributeCount(next...); - } - - template static std::size_t attributeCount(std::size_t) { - return ~std::size_t(0); - } - - template static typename std::enable_if::value, std::size_t>::type stride(const T&, const U&... next) { - return sizeof(typename T::value_type) + stride(next...); - } - - template static std::size_t stride(std::size_t gap, const T&... next) { - return gap + stride(next...); - } - - private: - template void write(char* startingOffset, const T& first, const U&... next) { - write(startingOffset+writeOne(startingOffset, first), next...); - } - - /* Copy data to the buffer */ - template typename std::enable_if::value, std::size_t>::type writeOne(char* startingOffset, const T& attributeList) { - auto it = attributeList.begin(); - for(std::size_t i = 0; i != _attributeCount; ++i, ++it) - std::memcpy(startingOffset+i*_stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); - - return sizeof(typename T::value_type); - } - - /* Fill gap with zeros */ - std::size_t writeOne(char* startingOffset, std::size_t gap) { - for(std::size_t i = 0; i != _attributeCount; ++i) - std::memset(startingOffset+i*_stride, 0, gap); - - return gap; - } - - /* Terminator functions for recursive calls */ - static std::size_t attributeCount() { return 0; } - static std::size_t stride() { return 0; } - void write(char*) {} - - std::size_t _attributeCount; - std::size_t _stride; +/* Attribute count, skipping gaps. If the attributes are just gaps, returns + ~std::size_t{0}. It must be in the structure to have proper overload + resolution (the functions would otherwise need to be de-inlined to break + cyclic dependencies) */ +struct AttributeCount { + template typename std::enable_if::value, std::size_t>::type operator()(const T& first, const U&... next) { + CORRADE_ASSERT(sizeof...(next) == 0 || AttributeCount{}(next...) == first.size() || AttributeCount{}(next...) == ~std::size_t(0), "MeshTools::interleave(): attribute arrays don't have the same length, expected" << first.size() << "but got" << AttributeCount{}(next...), 0); + + return first.size(); + } + template std::size_t operator()(std::size_t, const T& first, const U&... next) { + return AttributeCount{}(first, next...); + } + constexpr std::size_t operator()(std::size_t) { return ~std::size_t(0); } + constexpr std::size_t operator()() { return 0; } }; +/* Stride, taking gaps into account. It must be in the structure, same reason + as above */ +struct Stride { + template typename std::enable_if::value, std::size_t>::type operator()(const T&, const U&... next) { + return sizeof(typename T::value_type) + Stride{}(next...); + } + template std::size_t operator()(std::size_t gap, const T&... next) { + return gap + Stride{}(next...); + } + constexpr std::size_t operator()() { return 0; } +}; + +/* Copy data to the buffer */ +template typename std::enable_if::value, std::size_t>::type writeOneInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, const T& attributeList) { + auto it = attributeList.begin(); + for(std::size_t i = 0; i != attributeCount; ++i, ++it) + std::memcpy(startingOffset + i*stride, reinterpret_cast(&*it), sizeof(typename T::value_type)); + + return sizeof(typename T::value_type); +} + +/* Fill gap with zeros */ +inline std::size_t writeOneInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, std::size_t gap) { + for(std::size_t i = 0; i != attributeCount; ++i) + std::memset(startingOffset + i*stride, 0, gap); + + return gap; +} + +/* Write interleaved data */ +inline void writeInterleaved(std::size_t, std::size_t, char*) {} +template void writeInterleaved(std::size_t attributeCount, std::size_t stride, char* startingOffset, const T& first, const U&... next) { + writeInterleaved(attributeCount, stride, startingOffset + writeOneInterleaved(attributeCount, stride, startingOffset, first), next...); +} + } /** @@ -176,12 +140,24 @@ See also @ref interleave(Mesh&, Buffer&, BufferUsage, const T&...), which writes the interleaved array directly into buffer of given mesh. */ /* enable_if to avoid clash with overloaded function below */ -template inline typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { - return Implementation::Interleave()(first, next...); +template typename std::enable_if::value, std::tuple>>::type interleave(const T& first, const U&... next) { + /* Compute buffer size and stride */ + const std::size_t attributeCount = Implementation::AttributeCount{}(first, next...); + const std::size_t stride = Implementation::Stride{}(first, next...); + if(attributeCount && attributeCount != ~std::size_t(0)) { + + /* Create output buffer */ + Containers::Array data = Containers::Array(attributeCount*stride); + + /* Save the data */ + Implementation::writeInterleaved(attributeCount, stride, data.begin(), first, next...); + return std::make_tuple(attributeCount, stride, std::move(data)); + + } else return std::make_tuple(0, stride, nullptr); } /** -@brief %Interleave vertex attributes and write them to array buffer +@brief %Interleave vertex attributes, write them to array buffer and configure the mesh @param mesh Output mesh @param buffer Output vertex buffer @param usage Vertex buffer usage @@ -194,18 +170,31 @@ so you don't have to call @ref Mesh::setVertexCount() on your own. @attention You still must call @ref Mesh::setPrimitive() and @ref Mesh::addVertexBuffer() on the mesh afterwards. -For only one attribute array this function is convenient equivalent to the -following, without any performance loss: +@see @ref compressIndices(), @ref compile() +@todo rework so Mesh & Buffer doesn't need to be included in header +*/ +template void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { + Containers::Array data; + std::size_t attributeCount; + std::tie(attributeCount, std::ignore, data) = interleave(attributes...); + + mesh.setVertexCount(attributeCount); + buffer.setData(data, usage); +} + +/** +@brief Write vertex attribute to array buffer and configure the mesh + +Simplified specialization of the above function for only one attribute array, +equivalent to the following: @code buffer.setData(attribute, usage); mesh.setVertexCount(attribute.size()); @endcode - -@see @ref MeshTools::compressIndices() -@todo rework so Mesh & Buffer doesn't need to be included in header */ -template inline void interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T&... attributes) { - return Implementation::Interleave()(mesh, buffer, usage, attributes...); +template typename std::enable_if::value, void>::type interleave(Mesh& mesh, Buffer& buffer, BufferUsage usage, const T& attribute) { + mesh.setVertexCount(attribute.size()); + buffer.setData(attribute, usage); } }} diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index 00ec10389..55ad00b4b 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -56,30 +56,30 @@ InterleaveTest::InterleaveTest() { void InterleaveTest::attributeCount() { std::stringstream ss; Error::setOutput(&ss); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{0, 1, 2, 3, 4, 5})), std::size_t(0)); CORRADE_COMPARE(ss.str(), "MeshTools::interleave(): attribute arrays don't have the same length, expected 3 but got 6\n"); - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, std::vector{3, 4, 5})), std::size_t(3)); } void InterleaveTest::attributeCountGaps() { - CORRADE_COMPARE((Implementation::Interleave::attributeCount(std::vector{0, 1, 2}, 3, + CORRADE_COMPARE((Implementation::AttributeCount{}(std::vector{0, 1, 2}, 3, std::vector{3, 4, 5}, 5)), std::size_t(3)); /* No arrays from which to get size */ - CORRADE_COMPARE(Implementation::Interleave::attributeCount(3, 5), ~std::size_t(0)); + CORRADE_COMPARE(Implementation::AttributeCount{}(3, 5), ~std::size_t(0)); } void InterleaveTest::stride() { - CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(1)); - CORRADE_COMPARE(Implementation::Interleave::stride(std::vector()), std::size_t(4)); - CORRADE_COMPARE((Implementation::Interleave::stride(std::vector(), std::vector())), std::size_t(5)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(1)); + CORRADE_COMPARE(Implementation::Stride{}(std::vector()), std::size_t(4)); + CORRADE_COMPARE((Implementation::Stride{}(std::vector(), std::vector())), std::size_t(5)); } void InterleaveTest::strideGaps() { - CORRADE_COMPARE((Implementation::Interleave::stride(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); + CORRADE_COMPARE((Implementation::Stride{}(2, std::vector(), 1, std::vector(), 12)), std::size_t(20)); } void InterleaveTest::write() {