From 101e8fd11b26e014b937cad895c39ede64e49491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 25 May 2020 16:52:37 +0200 Subject: [PATCH] MeshTools: isInterleaved() and interleavedData() work with custom formats. The interleavedLayout() etc don't yet (those need to explicitly assert), added a TODO there. --- src/Magnum/MeshTools/Interleave.cpp | 24 ++++- src/Magnum/MeshTools/Test/InterleaveTest.cpp | 98 ++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/src/Magnum/MeshTools/Interleave.cpp b/src/Magnum/MeshTools/Interleave.cpp index 46c32be64..1c140c0f2 100644 --- a/src/Magnum/MeshTools/Interleave.cpp +++ b/src/Magnum/MeshTools/Interleave.cpp @@ -50,14 +50,32 @@ Containers::Optional> interleavedData const UnsignedInt stride = data.attributeStride(0); std::size_t minOffset = ~std::size_t{}; std::size_t maxOffset = 0; + bool hasImplementationSpecificVertexFormat = false; for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { if(data.attributeStride(i) != stride) return Containers::NullOpt; const std::size_t offset = data.attributeOffset(i); minOffset = Math::min(minOffset, offset); - maxOffset = Math::max(maxOffset, offset + attributeSize(data, i)); + + /* If the attribute has implementation-specific format, remember that + for later and optimistically use size of 1 byte for calculations */ + std::size_t size; + if(isVertexFormatImplementationSpecific(data.attributeFormat(i))) { + hasImplementationSpecificVertexFormat = true; + size = 1; + } else size = attributeSize(data, i); + + maxOffset = Math::max(maxOffset, offset + size); } + /* If there's an attribute with implementation-specific format, + conservatively use the whole stride for it. This should work for + majority of cases except when the stride has a padding at the end and + the padding isn't included in the vertexData array for the last vertex, + but that'd probably blow up in many other cases (and drivers) as well. */ + if(hasImplementationSpecificVertexFormat) + maxOffset = Math::max(maxOffset, minOffset + stride); + /* The offsets can't fit into the stride, report failure */ if(maxOffset - minOffset > stride) return Containers::NullOpt; @@ -110,6 +128,9 @@ Containers::Array interleavedLayout(Trade::MeshData&& } else { stride = 0; minOffset = 0; + /** @todo explitily assert on impl-specific vertex formats here -- + however it should work when the original is already interleaved and + nothing in extras is impl-specific */ for(UnsignedInt i = 0, max = data.attributeCount(); i != max; ++i) stride += attributeSize(data, i); } @@ -122,6 +143,7 @@ Containers::Array interleavedLayout(Trade::MeshData&& "MeshTools::interleavedLayout(): negative padding" << extra[i].stride() << "in extra attribute" << i << "too large for stride" << stride, {}); stride += extra[i].stride(); } else { + /** @todo explitily assert on impl-specific vertex formats here */ stride += attributeSize(extra[i]); ++extraAttributeCount; } diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index 45f6bc685..5fc33cb9c 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -60,6 +60,7 @@ struct InterleaveTest: Corrade::TestSuite::Tester { void isInterleavedUnordered(); void isInterleavedAttributeAcrossStride(); void isInterleavedVertexDataWholeMemory(); + void isInterleavedImplementationSpecificVertexFormat(); void interleavedData(); void interleavedDataArrayAttributes(); @@ -68,6 +69,7 @@ struct InterleaveTest: Corrade::TestSuite::Tester { void interleavedDataNotInterleaved(); void interleavedDataVertexDataWholeMemory(); void interleavedMutableDataNotMutable(); + void interleavedDataImplementationSpecificVertexFormat(); void interleavedLayout(); void interleavedLayoutExtra(); @@ -112,6 +114,7 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::isInterleavedUnordered, &InterleaveTest::isInterleavedAttributeAcrossStride, &InterleaveTest::isInterleavedVertexDataWholeMemory, + &InterleaveTest::isInterleavedImplementationSpecificVertexFormat, &InterleaveTest::interleavedData, &InterleaveTest::interleavedDataArrayAttributes, @@ -120,6 +123,7 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::interleavedDataNotInterleaved, &InterleaveTest::interleavedDataVertexDataWholeMemory, &InterleaveTest::interleavedMutableDataNotMutable, + &InterleaveTest::interleavedDataImplementationSpecificVertexFormat, &InterleaveTest::interleavedLayout, &InterleaveTest::interleavedLayoutExtra, @@ -383,6 +387,63 @@ void InterleaveTest::isInterleavedVertexDataWholeMemory() { CORRADE_VERIFY(MeshTools::isInterleaved(data)); } +void InterleaveTest::isInterleavedImplementationSpecificVertexFormat() { + /* Interleaved; fits into one byte at the end of stride */ + { + Containers::Array vertexData{100 + 3*9}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100), 3, 9}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + vertexFormatWrap(0x1234), + Containers::StridedArrayView1D{vertexData, + vertexData.data() + 100 + 8, 3, 9}}; + + /* The result should be independent on the order of calculations */ + Trade::MeshData data{MeshPrimitive::Triangles, {}, vertexData, + {positions, normals}}; + Trade::MeshData dataDifferentOrder{MeshPrimitive::Triangles, {}, vertexData, + {normals, positions}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + CORRADE_VERIFY(MeshTools::isInterleaved(dataDifferentOrder)); + } + + /* Doesn't have even one byte of space in the stride, invalid */ + { + Containers::Array vertexData{100 + 3*8}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100), 3, 8}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + vertexFormatWrap(0x1234), + Containers::StridedArrayView1D{vertexData, + vertexData.data() + 100 + 8, 3, 8}}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data)); + } + + /* A non-interleaved (or not?) attribute with a implementation-specific + format after interleaved ones is also invalid */ + { + Containers::Array vertexData{100 + 3*20 + 3}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100), 3, 20}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100 + 8), 3, 20}}; + Trade::MeshAttributeData extra{Trade::meshAttributeCustom(1234), + vertexFormatWrap(0x1234), + Containers::StridedArrayView1D{vertexData, + vertexData.data() + 100 + 3*20, 3, 1}}; + + Trade::MeshData data{MeshPrimitive::Triangles, {}, vertexData, + {positions, normals, extra}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data)); + } +} + void InterleaveTest::interleavedData() { Containers::Array vertexData{100 + 3*40}; Containers::StridedArrayView1D normals{vertexData, @@ -543,6 +604,43 @@ void InterleaveTest::interleavedMutableDataNotMutable() { CORRADE_COMPARE(out.str(), "MeshTools::interleavedMutableData(): vertex data is not mutable\n"); } +void InterleaveTest::interleavedDataImplementationSpecificVertexFormat() { + Containers::Array vertexData{100 + 3*50}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100), 3, 50}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + vertexFormatWrap(0x1234), + Containers::StridedArrayView1D{vertexData, + vertexData.data() + 100 + 8, 3, 50}}; + + { + Trade::MeshData data{MeshPrimitive::Triangles, {}, vertexData, + {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + Containers::StridedArrayView2D interleaved = MeshTools::interleavedData(data); + CORRADE_COMPARE(interleaved.data(), positions.data().data()); + CORRADE_COMPARE(interleaved.size()[0], 3); + /* The implementation-specific format is conservatively assumed to + occupy the whole stride (even if may be is excessive) */ + CORRADE_COMPARE(interleaved.size()[1], 50); + CORRADE_COMPARE(interleaved.stride()[0], 50); + CORRADE_COMPARE(interleaved.stride()[1], 1); + + /* The result should be the same independent on the order of attributes */ + } { + Trade::MeshData data{MeshPrimitive::Triangles, {}, vertexData, + {normals, positions}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + Containers::StridedArrayView2D interleaved = MeshTools::interleavedData(data); + CORRADE_COMPARE(interleaved.data(), positions.data().data()); + CORRADE_COMPARE(interleaved.size()[0], 3); + CORRADE_COMPARE(interleaved.size()[1], 50); + CORRADE_COMPARE(interleaved.stride()[0], 50); + CORRADE_COMPARE(interleaved.stride()[1], 1); + } +} + void InterleaveTest::interleavedLayout() { Containers::Array indexData{6}; Containers::Array vertexData{3*24};