Browse Source

Trade: pack MeshAttributeData internals better.

Before it was a 32-byte structure with 3 bytes free (or a 20-byte
structure with 3 bytes free), now it's a 24-byte structure with 5 bytes
free. Exploiting the fact that strides can't be too high for a GPU
anyway (so 2 bytes is enough instead of 8), and vertex count is capped
to 32bit by MeshData anyway (so no need for 8 also), saving 10 bytes on
a 64-bit build.
pull/371/head
Vladimír Vondruš 6 years ago
parent
commit
5e81d10e13
  1. 155
      src/Magnum/Trade/MeshData.cpp
  2. 49
      src/Magnum/Trade/MeshData.h
  3. 30
      src/Magnum/Trade/Test/MeshDataTest.cpp

155
src/Magnum/Trade/MeshData.cpp

@ -57,7 +57,7 @@ MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexForma
logic */ logic */
/** @todo support zero / negative stride? would be hard to transfer to GL */ /** @todo support zero / negative stride? would be hard to transfer to GL */
CORRADE_ASSERT(data.empty() || std::ptrdiff_t(vertexFormatSize(format)) <= data.stride(), CORRADE_ASSERT(data.empty() || std::ptrdiff_t(vertexFormatSize(format)) <= data.stride(),
"Trade::MeshAttributeData: view stride" << data.stride() << "is not large enough to contain" << format, ); "Trade::MeshAttributeData: expected stride to be positive and enough to fit" << format << Debug::nospace << ", got" << data.stride(), );
} }
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, nullptr} { MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView2D<const char>& data) noexcept: MeshAttributeData{name, format, Containers::StridedArrayView1D<const void>{{data.data(), ~std::size_t{}}, data.size()[0], data.stride()[0]}, nullptr} {
@ -83,7 +83,7 @@ MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& inde
"Trade::MeshData: indices are expected to be valid if there are no attributes and vertex count isn't passed explicitly", ); "Trade::MeshData: indices are expected to be valid if there are no attributes and vertex count isn't passed explicitly", );
/** @todo some better value? attributeless indexed with defined vertex count? */ /** @todo some better value? attributeless indexed with defined vertex count? */
_vertexCount = 0; _vertexCount = 0;
} else _vertexCount = _attributes[0]._data.size(); } else _vertexCount = _attributes[0]._vertexCount;
CORRADE_ASSERT(!_indices.empty() || _indexData.empty(), CORRADE_ASSERT(!_indices.empty() || _indexData.empty(),
"Trade::MeshData: indexData passed for a non-indexed mesh", ); "Trade::MeshData: indexData passed for a non-indexed mesh", );
@ -97,12 +97,12 @@ MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& inde
const MeshAttributeData& attribute = _attributes[i]; const MeshAttributeData& attribute = _attributes[i];
CORRADE_ASSERT(attribute._format != VertexFormat{}, CORRADE_ASSERT(attribute._format != VertexFormat{},
"Trade::MeshData: attribute" << i << "doesn't specify anything", ); "Trade::MeshData: attribute" << i << "doesn't specify anything", );
CORRADE_ASSERT(attribute._vertexCount == _vertexCount,
const Containers::StridedArrayView1D<const char> data = Containers::arrayCast<const char>(attribute._data); "Trade::MeshData: attribute" << i << "has" << attribute._vertexCount << "vertices but" << _vertexCount << "expected", );
CORRADE_ASSERT(data.size() == _vertexCount, const void* const begin = static_cast<const char*>(attribute._data);
"Trade::MeshData: attribute" << i << "has" << data.size() << "vertices but" << _vertexCount << "expected", ); const void* const end = static_cast<const char*>(attribute._data) + (_vertexCount - 1)*attribute._stride + vertexFormatSize(attribute._format);
CORRADE_ASSERT(data.empty() || (&data.front() >= _vertexData.begin() && &data.back() + vertexFormatSize(attribute._format) <= _vertexData.end()), CORRADE_ASSERT(!_vertexCount || (begin >= _vertexData.begin() && end <= _vertexData.end()),
"Trade::MeshData: attribute" << i << "[" << Debug::nospace << static_cast<const void*>(&data.front()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(&data.back() + vertexFormatSize(attribute._format)) << Debug::nospace << "] is not contained in passed vertexData array [" << Debug::nospace << static_cast<const void*>(_vertexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_vertexData.end()) << Debug::nospace << "]", ); "Trade::MeshData: attribute" << i << "[" << Debug::nospace << begin << Debug::nospace << ":" << Debug::nospace << end << Debug::nospace << "] is not contained in passed vertexData array [" << Debug::nospace << static_cast<const void*>(_vertexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_vertexData.end()) << Debug::nospace << "]", );
} }
#endif #endif
} }
@ -233,13 +233,13 @@ VertexFormat MeshData::attributeFormat(UnsignedInt id) const {
std::size_t MeshData::attributeOffset(UnsignedInt id) const { std::size_t MeshData::attributeOffset(UnsignedInt id) const {
CORRADE_ASSERT(id < _attributes.size(), CORRADE_ASSERT(id < _attributes.size(),
"Trade::MeshData::attributeOffset(): index" << id << "out of range for" << _attributes.size() << "attributes", {}); "Trade::MeshData::attributeOffset(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
return static_cast<const char*>(_attributes[id]._data.data()) - _vertexData.data(); return static_cast<const char*>(_attributes[id]._data) - _vertexData.data();
} }
UnsignedInt MeshData::attributeStride(UnsignedInt id) const { UnsignedInt MeshData::attributeStride(UnsignedInt id) const {
CORRADE_ASSERT(id < _attributes.size(), CORRADE_ASSERT(id < _attributes.size(),
"Trade::MeshData::attributeStride(): index" << id << "out of range for" << _attributes.size() << "attributes", {}); "Trade::MeshData::attributeStride(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
return _attributes[id]._data.stride(); return _attributes[id]._stride;
} }
UnsignedInt MeshData::attributeCount(const MeshAttribute name) const { UnsignedInt MeshData::attributeCount(const MeshAttribute name) const {
@ -292,8 +292,9 @@ Containers::StridedArrayView2D<const char> MeshData::attribute(UnsignedInt id) c
/* Build a 2D view using information about attribute type size, return only /* Build a 2D view using information about attribute type size, return only
a prefix of the actual vertex count (which is zero in case vertex data a prefix of the actual vertex count (which is zero in case vertex data
is released) */ is released) */
return Containers::arrayCast<2, const char>(_attributes[id]._data, return Containers::arrayCast<2, const char>(
vertexFormatSize(_attributes[id]._format)).prefix(_vertexCount); attributeDataViewInternal(_attributes[id]),
vertexFormatSize(_attributes[id]._format));
} }
Containers::StridedArrayView2D<char> MeshData::mutableAttribute(UnsignedInt id) { Containers::StridedArrayView2D<char> MeshData::mutableAttribute(UnsignedInt id) {
@ -304,8 +305,9 @@ Containers::StridedArrayView2D<char> MeshData::mutableAttribute(UnsignedInt id)
/* Build a 2D view using information about attribute type size, return only /* Build a 2D view using information about attribute type size, return only
a prefix of the actual vertex count (which is zero in case vertex data a prefix of the actual vertex count (which is zero in case vertex data
is released) */ is released) */
auto out = Containers::arrayCast<2, const char>(_attributes[id]._data, auto out = Containers::arrayCast<2, const char>(
vertexFormatSize(_attributes[id]._format)).prefix(_vertexCount); attributeDataViewInternal(_attributes[id]),
vertexFormatSize(_attributes[id]._format));
/** @todo some arrayConstCast? UGH */ /** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{ return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the /* The view size is there only for a size assert, we're pretty sure the
@ -360,45 +362,56 @@ Containers::Array<UnsignedInt> MeshData::indicesAsArray() const {
return output; return output;
} }
Containers::StridedArrayView1D<const void> MeshData::attributeDataViewInternal(const MeshAttributeData& attribute) const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
{attribute._data, ~std::size_t{}},
/* Not using attribute._vertexCount because that gets stale after
releaseVertexData() gets called, and then we would need to slice the
result inside attribute() and elsewhere */
_vertexCount, attribute._stride};
}
void MeshData::positions2DInto(const Containers::StridedArrayView1D<Vector2> destination, const UnsignedInt id) const { void MeshData::positions2DInto(const Containers::StridedArrayView1D<Vector2> destination, const UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(MeshAttribute::Position, id); const UnsignedInt attributeId = attributeFor(MeshAttribute::Position, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId]; const MeshAttributeData& attribute = _attributes[attributeId];
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
const auto destination2f = Containers::arrayCast<2, Float>(destination); const auto destination2f = Containers::arrayCast<2, Float>(destination);
/* Copy 2D positions as-is, for 3D positions ignore Z */ /* Copy 2D positions as-is, for 3D positions ignore Z */
if(attribute._format == VertexFormat::Vector2 || if(attribute._format == VertexFormat::Vector2 ||
attribute._format == VertexFormat::Vector3) attribute._format == VertexFormat::Vector3)
Utility::copy(Containers::arrayCast<const Vector2>(attribute._data), destination); Utility::copy(Containers::arrayCast<const Vector2>(attributeData), destination);
else if(attribute._format == VertexFormat::Vector2h || else if(attribute._format == VertexFormat::Vector2h ||
attribute._format == VertexFormat::Vector3h) attribute._format == VertexFormat::Vector3h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ub || else if(attribute._format == VertexFormat::Vector2ub ||
attribute._format == VertexFormat::Vector3ub) attribute._format == VertexFormat::Vector3ub)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2b || else if(attribute._format == VertexFormat::Vector2b ||
attribute._format == VertexFormat::Vector3b) attribute._format == VertexFormat::Vector3b)
Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2us || else if(attribute._format == VertexFormat::Vector2us ||
attribute._format == VertexFormat::Vector3us) attribute._format == VertexFormat::Vector3us)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2s || else if(attribute._format == VertexFormat::Vector2s ||
attribute._format == VertexFormat::Vector3s) attribute._format == VertexFormat::Vector3s)
Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ubNormalized || else if(attribute._format == VertexFormat::Vector2ubNormalized ||
attribute._format == VertexFormat::Vector3ubNormalized) attribute._format == VertexFormat::Vector3ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2bNormalized || else if(attribute._format == VertexFormat::Vector2bNormalized ||
attribute._format == VertexFormat::Vector3bNormalized) attribute._format == VertexFormat::Vector3bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2usNormalized || else if(attribute._format == VertexFormat::Vector2usNormalized ||
attribute._format == VertexFormat::Vector3usNormalized) attribute._format == VertexFormat::Vector3usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2sNormalized || else if(attribute._format == VertexFormat::Vector2sNormalized ||
attribute._format == VertexFormat::Vector3sNormalized) attribute._format == VertexFormat::Vector3sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
} }
@ -413,54 +426,54 @@ void MeshData::positions3DInto(const Containers::StridedArrayView1D<Vector3> des
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions3DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions3DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions3DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions3DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId]; const MeshAttributeData& attribute = _attributes[attributeId];
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
const Containers::StridedArrayView2D<Float> destination2f = Containers::arrayCast<2, Float>(Containers::arrayCast<Vector2>(destination)); const Containers::StridedArrayView2D<Float> destination2f = Containers::arrayCast<2, Float>(Containers::arrayCast<Vector2>(destination));
const Containers::StridedArrayView2D<Float> destination3f = Containers::arrayCast<2, Float>(destination); const Containers::StridedArrayView2D<Float> destination3f = Containers::arrayCast<2, Float>(destination);
/* For 2D positions copy the XY part to the first two components */ /* For 2D positions copy the XY part to the first two components */
if(attribute._format == VertexFormat::Vector2) if(attribute._format == VertexFormat::Vector2)
Utility::copy(Containers::arrayCast<const Vector2>(attribute._data), Utility::copy(Containers::arrayCast<const Vector2>(attributeData),
Containers::arrayCast<Vector2>(destination)); Containers::arrayCast<Vector2>(destination));
else if(attribute._format == VertexFormat::Vector2h) else if(attribute._format == VertexFormat::Vector2h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ub) else if(attribute._format == VertexFormat::Vector2ub)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2b) else if(attribute._format == VertexFormat::Vector2b)
Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2us) else if(attribute._format == VertexFormat::Vector2us)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2s) else if(attribute._format == VertexFormat::Vector2s)
Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ubNormalized) else if(attribute._format == VertexFormat::Vector2ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2bNormalized) else if(attribute._format == VertexFormat::Vector2bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2usNormalized) else if(attribute._format == VertexFormat::Vector2usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2sNormalized) else if(attribute._format == VertexFormat::Vector2sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
/* Copy 3D positions as-is */ /* Copy 3D positions as-is */
else if(attribute._format == VertexFormat::Vector3) else if(attribute._format == VertexFormat::Vector3)
Utility::copy(Containers::arrayCast<const Vector3>(attribute._data), destination); Utility::copy(Containers::arrayCast<const Vector3>(attributeData), destination);
else if(attribute._format == VertexFormat::Vector3h) else if(attribute._format == VertexFormat::Vector3h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3ub) else if(attribute._format == VertexFormat::Vector3ub)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3b) else if(attribute._format == VertexFormat::Vector3b)
Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); Math::castInto(Containers::arrayCast<2, const Byte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3us) else if(attribute._format == VertexFormat::Vector3us)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3s) else if(attribute._format == VertexFormat::Vector3s)
Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); Math::castInto(Containers::arrayCast<2, const Short>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3ubNormalized) else if(attribute._format == VertexFormat::Vector3ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3bNormalized) else if(attribute._format == VertexFormat::Vector3bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3usNormalized) else if(attribute._format == VertexFormat::Vector3usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3sNormalized) else if(attribute._format == VertexFormat::Vector3sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 3), destination3f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* For 2D positions finally fill the Z with a single value */ /* For 2D positions finally fill the Z with a single value */
@ -492,17 +505,17 @@ void MeshData::normalsInto(const Containers::StridedArrayView1D<Vector3> destina
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::normalsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Normal) << "normal attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::normalsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Normal) << "normal attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId]; const MeshAttributeData& attribute = _attributes[attributeId];
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
const auto destination3f = Containers::arrayCast<2, Float>(destination); const auto destination3f = Containers::arrayCast<2, Float>(destination);
if(attribute._format == VertexFormat::Vector3) if(attribute._format == VertexFormat::Vector3)
Utility::copy(Containers::arrayCast<const Vector3>(attribute._data), destination); Utility::copy(Containers::arrayCast<const Vector3>(attributeData), destination);
else if(attribute._format == VertexFormat::Vector3h) else if(attribute._format == VertexFormat::Vector3h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3bNormalized) else if(attribute._format == VertexFormat::Vector3bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3sNormalized) else if(attribute._format == VertexFormat::Vector3sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 3), destination3f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
} }
@ -517,29 +530,29 @@ void MeshData::textureCoordinates2DInto(const Containers::StridedArrayView1D<Vec
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::textureCoordinates2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::TextureCoordinates) << "texture coordinate attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::textureCoordinates2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::TextureCoordinates) << "texture coordinate attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::textureCoordinates2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::textureCoordinates2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId]; const MeshAttributeData& attribute = _attributes[attributeId];
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
const auto destination2f = Containers::arrayCast<2, Float>(destination); const auto destination2f = Containers::arrayCast<2, Float>(destination);
if(attribute._format == VertexFormat::Vector2) if(attribute._format == VertexFormat::Vector2)
Utility::copy(Containers::arrayCast<const Vector2>(attribute._data), destination); Utility::copy(Containers::arrayCast<const Vector2>(attributeData), destination);
else if(attribute._format == VertexFormat::Vector2h) else if(attribute._format == VertexFormat::Vector2h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ub) else if(attribute._format == VertexFormat::Vector2ub)
Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2b) else if(attribute._format == VertexFormat::Vector2b)
Math::castInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2us) else if(attribute._format == VertexFormat::Vector2us)
Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2s) else if(attribute._format == VertexFormat::Vector2s)
Math::castInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::castInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2ubNormalized) else if(attribute._format == VertexFormat::Vector2ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2bNormalized) else if(attribute._format == VertexFormat::Vector2bNormalized)
Math::unpackInto(Containers::arrayCast<2, const Byte>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Byte>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2usNormalized) else if(attribute._format == VertexFormat::Vector2usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 2), destination2f);
else if(attribute._format == VertexFormat::Vector2sNormalized) else if(attribute._format == VertexFormat::Vector2sNormalized)
Math::unpackInto(Containers::arrayCast<2, const Short>(attribute._data, 2), destination2f); Math::unpackInto(Containers::arrayCast<2, const Short>(attributeData, 2), destination2f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
} }
@ -554,32 +567,32 @@ void MeshData::colorsInto(const Containers::StridedArrayView1D<Color4> destinati
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::colorsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Color) << "color attributes", ); CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::colorsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Color) << "color attributes", );
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::colorsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), ); CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::colorsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
const MeshAttributeData& attribute = _attributes[attributeId]; const MeshAttributeData& attribute = _attributes[attributeId];
const Containers::StridedArrayView1D<const void> attributeData = attributeDataViewInternal(attribute);
const Containers::StridedArrayView2D<Float> destination3f = Containers::arrayCast<2, Float>(Containers::arrayCast<Vector3>(destination)); const Containers::StridedArrayView2D<Float> destination3f = Containers::arrayCast<2, Float>(Containers::arrayCast<Vector3>(destination));
const Containers::StridedArrayView2D<Float> destination4f = Containers::arrayCast<2, Float>(destination); const Containers::StridedArrayView2D<Float> destination4f = Containers::arrayCast<2, Float>(destination);
/* For three-component colors copy the RGB part to the first three /* For three-component colors copy the RGB part to the first three
components */ components */
if(attribute._format == VertexFormat::Vector3) if(attribute._format == VertexFormat::Vector3)
Utility::copy(Containers::arrayCast<const Vector3>(attribute._data), Utility::copy(Containers::arrayCast<const Vector3>(attributeData),
Containers::arrayCast<Vector3>(destination)); Containers::arrayCast<Vector3>(destination));
else if(attribute._format == VertexFormat::Vector3h) else if(attribute._format == VertexFormat::Vector3h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3ubNormalized) else if(attribute._format == VertexFormat::Vector3ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 3), destination3f);
else if(attribute._format == VertexFormat::Vector3usNormalized) else if(attribute._format == VertexFormat::Vector3usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 3), destination3f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 3), destination3f);
/* Copy four-component colors as-is */ /* Copy four-component colors as-is */
else if(attribute._format == VertexFormat::Vector4) else if(attribute._format == VertexFormat::Vector4)
Utility::copy(Containers::arrayCast<const Vector4>(attribute._data), Utility::copy(Containers::arrayCast<const Vector4>(attributeData),
Containers::arrayCast<Vector4>(destination)); Containers::arrayCast<Vector4>(destination));
else if(attribute._format == VertexFormat::Vector4h) else if(attribute._format == VertexFormat::Vector4h)
Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 4), destination4f); Math::unpackHalfInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 4), destination4f);
else if(attribute._format == VertexFormat::Vector4ubNormalized) else if(attribute._format == VertexFormat::Vector4ubNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attribute._data, 4), destination4f); Math::unpackInto(Containers::arrayCast<2, const UnsignedByte>(attributeData, 4), destination4f);
else if(attribute._format == VertexFormat::Vector4usNormalized) else if(attribute._format == VertexFormat::Vector4usNormalized)
Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attribute._data, 4), destination4f); Math::unpackInto(Containers::arrayCast<2, const UnsignedShort>(attributeData, 4), destination4f);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* For three-component colors finally fill the alpha with a single value */ /* For three-component colors finally fill the alpha with a single value */

49
src/Magnum/Trade/MeshData.h

@ -244,7 +244,7 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* initialization of the attribute array for @ref MeshData, expected to * initialization of the attribute array for @ref MeshData, expected to
* be replaced with concrete values later. * be replaced with concrete values later.
*/ */
constexpr explicit MeshAttributeData() noexcept: _name{}, _format{}, _data{} {} constexpr explicit MeshAttributeData() noexcept: _data{}, _vertexCount{}, _format{}, _stride{}, _name{} {}
/** /**
* @brief Type-erased constructor * @brief Type-erased constructor
@ -313,7 +313,10 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
* multiple different attributes onto each other. Not meant to be * multiple different attributes onto each other. Not meant to be
* passed to @ref MeshData. * passed to @ref MeshData.
*/ */
constexpr explicit MeshAttributeData(Int padding): _name{}, _format{}, _data{nullptr, 0, padding} {} constexpr explicit MeshAttributeData(Int padding): _data{nullptr}, _vertexCount{0}, _format{}, _stride{
(CORRADE_CONSTEXPR_ASSERT(padding >= -32768 && padding <= 32767,
"Trade::MeshAttributeData: at most 32k padding supported, got" << padding), Short(padding))
}, _name{} {}
/** @brief Attribute name */ /** @brief Attribute name */
constexpr MeshAttribute name() const { return _name; } constexpr MeshAttribute name() const { return _name; }
@ -322,16 +325,29 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
constexpr VertexFormat format() const { return _format; } constexpr VertexFormat format() const { return _format; }
/** @brief Type-erased attribute data */ /** @brief Type-erased attribute data */
constexpr Containers::StridedArrayView1D<const void> data() const { return _data; } constexpr Containers::StridedArrayView1D<const void> data() const {
return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */
{_data, ~std::size_t{}},
_vertexCount, _stride};
}
private: private:
constexpr explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept; constexpr explicit MeshAttributeData(MeshAttribute name, VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept;
friend MeshData; friend MeshData;
MeshAttribute _name; const void* _data;
/* Here's some room for flags */ /* Vertex count in MeshData is currently 32-bit, so this doesn't need
to be 64-bit either */
UnsignedInt _vertexCount;
VertexFormat _format; VertexFormat _format;
Containers::StridedArrayView1D<const void> _data; /* According to https://opengl.gpuinfo.org/displaycapability.php?name=GL_MAX_VERTEX_ATTRIB_STRIDE,
current largest reported stride is 4k so 32k should be enough */
Short _stride;
MeshAttribute _name;
/* 4 bytes free for more stuff on 64b (20, aligned to 24); nothing on
32b */
}; };
/** @relatesalso MeshAttributeData /** @relatesalso MeshAttributeData
@ -1183,6 +1199,12 @@ class MAGNUM_TRADE_EXPORT MeshData {
/* Internal helper that doesn't assert, unlike attributeId() */ /* Internal helper that doesn't assert, unlike attributeId() */
UnsignedInt attributeFor(MeshAttribute name, UnsignedInt id) const; UnsignedInt attributeFor(MeshAttribute name, UnsignedInt id) const;
/* Like attribute(), but returning just a 1D view */
Containers::StridedArrayView1D<const void> attributeDataViewInternal(const MeshAttributeData& attribute) const;
/* GPUs don't currently support more than 32-bit index types / vertex
counts so this should be enough. Sanity check:
https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkIndexType.html */
UnsignedInt _vertexCount; UnsignedInt _vertexCount;
MeshIndexType _indexType; MeshIndexType _indexType;
MeshPrimitive _primitive; MeshPrimitive _primitive;
@ -1298,9 +1320,14 @@ namespace Implementation {
#endif #endif
constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept: constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, std::nullptr_t) noexcept:
_name{name}, _format{format}, _data{(CORRADE_CONSTEXPR_ASSERT( _data{data.data()}, _vertexCount{UnsignedInt(data.size())}, _format{format},
/* Double formats intentionally not supported for any builtin attributes /** @todo support zero / negative stride? would be hard to transfer to GL */
right now -- only for custom formats */ _stride{(CORRADE_CONSTEXPR_ASSERT(!(UnsignedInt(data.stride()) & 0xffff8000),
"Trade::MeshAttributeData: expected stride to be positive and at most 32k, got" << data.stride()),
Short(data.stride()))
}, _name{(CORRADE_CONSTEXPR_ASSERT(
/* Double types intentionally not supported for any builtin attributes
right now -- only for custom types */
(name == MeshAttribute::Position && (name == MeshAttribute::Position &&
(format == VertexFormat::Vector2 || (format == VertexFormat::Vector2 ||
format == VertexFormat::Vector2h || format == VertexFormat::Vector2h ||
@ -1348,8 +1375,8 @@ constexpr MeshAttributeData::MeshAttributeData(const MeshAttribute name, const V
format == VertexFormat::Vector2s || format == VertexFormat::Vector2s ||
format == VertexFormat::Vector2sNormalized)) || format == VertexFormat::Vector2sNormalized)) ||
isMeshAttributeCustom(name) /* can be any format */, isMeshAttributeCustom(name) /* can be any format */,
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name), data)} "Trade::MeshAttributeData:" << format << "is not a valid format for" << name), name)
{} } {}
template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), data, nullptr} {} template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute name, const Containers::StridedArrayView1D<T>& data) noexcept: MeshAttributeData{name, Implementation::vertexFormatFor<typename std::remove_const<T>::type>(), data, nullptr} {}

30
src/Magnum/Trade/Test/MeshDataTest.cpp

@ -57,10 +57,10 @@ struct MeshDataTest: TestSuite::Tester {
void constructAttribute2DWrongSize(); void constructAttribute2DWrongSize();
void constructAttribute2DNonContiguous(); void constructAttribute2DNonContiguous();
void constructAttributeTypeErased(); void constructAttributeTypeErased();
void constructAttributeTypeErasedWrongStride();
void constructAttributeNullptr(); void constructAttributeNullptr();
void constructAttributePadding(); void constructAttributePadding();
void constructAttributeNonOwningArray(); void constructAttributeNonOwningArray();
void constructAttributeWrongStride();
void construct(); void construct();
void constructZeroIndices(); void constructZeroIndices();
@ -173,10 +173,10 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructAttribute2DWrongSize, &MeshDataTest::constructAttribute2DWrongSize,
&MeshDataTest::constructAttribute2DNonContiguous, &MeshDataTest::constructAttribute2DNonContiguous,
&MeshDataTest::constructAttributeTypeErased, &MeshDataTest::constructAttributeTypeErased,
&MeshDataTest::constructAttributeTypeErasedWrongStride,
&MeshDataTest::constructAttributeNullptr, &MeshDataTest::constructAttributeNullptr,
&MeshDataTest::constructAttributePadding, &MeshDataTest::constructAttributePadding,
&MeshDataTest::constructAttributeNonOwningArray, &MeshDataTest::constructAttributeNonOwningArray,
&MeshDataTest::constructAttributeWrongStride,
&MeshDataTest::construct, &MeshDataTest::construct,
&MeshDataTest::constructZeroIndices, &MeshDataTest::constructZeroIndices,
@ -521,15 +521,6 @@ void MeshDataTest::constructAttributeTypeErased() {
CORRADE_VERIFY(positions.data().data() == positionData); CORRADE_VERIFY(positions.data().data() == positionData);
} }
void MeshDataTest::constructAttributeTypeErasedWrongStride() {
char positionData[3*sizeof(Vector3)]{};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, Containers::arrayCast<const char>(positionData)};
CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: view stride 1 is not large enough to contain VertexFormat::Vector3\n");
}
void MeshDataTest::constructAttributeNullptr() { void MeshDataTest::constructAttributeNullptr() {
MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr}; MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr};
CORRADE_COMPARE(positions.name(), MeshAttribute::Position); CORRADE_COMPARE(positions.name(), MeshAttribute::Position);
@ -553,6 +544,23 @@ void MeshDataTest::constructAttributeNonOwningArray() {
CORRADE_COMPARE(static_cast<const void*>(array.data()), data); CORRADE_COMPARE(static_cast<const void*>(array.data()), data);
} }
void MeshDataTest::constructAttributeWrongStride() {
char positionData[3*sizeof(Vector3)]{};
std::ostringstream out;
Error redirectError{&out};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, Containers::arrayCast<const char>(positionData)};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, Containers::StridedArrayView1D<const void>{positionData, 0, -16}};
MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, Containers::StridedArrayView1D<const void>{positionData, 0, 65000}};
MeshAttributeData{65000};
CORRADE_COMPARE(out.str(),
"Trade::MeshAttributeData: expected stride to be positive and enough to fit VertexFormat::Vector3, got 1\n"
"Trade::MeshAttributeData: expected stride to be positive and at most 32k, got -16\n"
"Trade::MeshAttributeData: expected stride to be positive and at most 32k, got 65000\n"
"Trade::MeshAttributeData: at most 32k padding supported, got 65000\n"
);
}
void MeshDataTest::construct() { void MeshDataTest::construct() {
struct Vertex { struct Vertex {
Vector3 position; Vector3 position;

Loading…
Cancel
Save