Browse Source

Trade: implement SceneField::Bit.

Uses StridedBitArrayView underneath. Waited for this container to exist,
because implementing this using bools and wasting 8x more memory wasn't
a good option. Plus, being able to address single bits opens a
possibility to describe individual bits in enum flags, whereas the only
other option would be to take the whole flag as an opaque type
containing "some bit values".
pull/601/head
Vladimír Vondruš 3 years ago
parent
commit
9f662d793e
  1. 228
      src/Magnum/Trade/SceneData.cpp
  2. 430
      src/Magnum/Trade/SceneData.h
  3. 1107
      src/Magnum/Trade/Test/SceneDataTest.cpp

228
src/Magnum/Trade/SceneData.cpp

@ -30,6 +30,7 @@
#include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h> #include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StridedBitArrayView.h>
#include <Corrade/Containers/StridedArrayViewStl.h> #include <Corrade/Containers/StridedArrayViewStl.h>
#include <Corrade/Containers/StringIterable.h> #include <Corrade/Containers/StringIterable.h>
#include <Corrade/Containers/StringView.h> #include <Corrade/Containers/StringView.h>
@ -147,6 +148,7 @@ Debug& operator<<(Debug& debug, const SceneFieldType value) {
switch(value) { switch(value) {
/* LCOV_EXCL_START */ /* LCOV_EXCL_START */
#define _c(value) case SceneFieldType::value: return debug << (debug.immediateFlags() & Debug::Flag::Packed ? "" : "::") << Debug::nospace << #value; #define _c(value) case SceneFieldType::value: return debug << (debug.immediateFlags() & Debug::Flag::Packed ? "" : "::") << Debug::nospace << #value;
_c(Bit)
_c(Float) _c(Float)
_c(Half) _c(Half)
_c(Double) _c(Double)
@ -265,6 +267,8 @@ UnsignedInt sceneFieldTypeSize(const SceneFieldType type) {
#pragma GCC diagnostic error "-Wswitch" #pragma GCC diagnostic error "-Wswitch"
#endif #endif
switch(type) { switch(type) {
case SceneFieldType::Bit:
CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeSize(): can't use with" << SceneFieldType::Bit, {});
case SceneFieldType::UnsignedByte: case SceneFieldType::UnsignedByte:
case SceneFieldType::Byte: case SceneFieldType::Byte:
case SceneFieldType::StringOffset8: case SceneFieldType::StringOffset8:
@ -402,6 +406,8 @@ UnsignedInt sceneFieldTypeAlignment(const SceneFieldType type) {
#pragma GCC diagnostic error "-Wswitch" #pragma GCC diagnostic error "-Wswitch"
#endif #endif
switch(type) { switch(type) {
case SceneFieldType::Bit:
CORRADE_ASSERT_UNREACHABLE("Trade::sceneFieldTypeAlignment(): can't use with" << SceneFieldType::Bit, {});
case SceneFieldType::UnsignedByte: case SceneFieldType::UnsignedByte:
case SceneFieldType::Vector2ub: case SceneFieldType::Vector2ub:
case SceneFieldType::Vector3ub: case SceneFieldType::Vector3ub:
@ -551,6 +557,11 @@ Debug& operator<<(Debug& debug, const SceneFieldFlags value) {
} }
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize, flags} { SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldType, Containers::StridedArrayView1D<const void>{{fieldData.data(), ~std::size_t{}}, fieldData.size()[0], fieldData.stride()[0]}, fieldArraySize, flags} {
#ifdef CORRADE_GRACEFUL_ASSERT
/* This caused an assertion in the delegated-to constructor, bail instead
of cascading the asserts further */
if(fieldType == SceneFieldType::Bit) return;
#endif
/* Yes, this calls into a constexpr function defined in the header -- /* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert because I feel that makes more sense than duplicating the full assert
logic */ logic */
@ -575,6 +586,38 @@ SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedA
CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", ); CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", );
} }
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const void* fieldData, UnsignedByte fieldBitOffset, std::size_t fieldSize, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize, SceneFieldFlags flags) noexcept: SceneFieldData{name, {}, Containers::StridedArrayView1D<const void>{{mappingData.data(), ~std::size_t{}}, mappingData.size()[0], mappingData.stride()[0]}, fieldData, fieldBitOffset, fieldSize, fieldStride, fieldArraySize, flags} {
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
if(mappingData.size()[1] == 8)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedLong);
else if(mappingData.size()[1] == 4)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedInt);
else if(mappingData.size()[1] == 2)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedShort);
else if(mappingData.size()[1] == 1)
_mappingTypeStringType = UnsignedByte(SceneMappingType::UnsignedByte);
else CORRADE_ASSERT_UNREACHABLE("Trade::SceneFieldData: expected second mapping view dimension size 1, 2, 4 or 8 but got" << mappingData.size()[1], );
CORRADE_ASSERT(mappingData.isContiguous<1>(), "Trade::SceneFieldData: second mapping view dimension is not contiguous", );
}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const Containers::StridedBitArrayView1D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingData, fieldData.data(), UnsignedByte(fieldData.offset()), fieldData.size(), fieldData.stride(), 0, flags} {}
SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const Containers::StridedBitArrayView2D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingData, fieldData.data(), UnsignedByte(fieldData.offset()), fieldData.size()[0], fieldData.stride()[0],
/* There's no need to check that the array size fits into 16 bits, as the
stronger requirement is that the signed stride fits into 16 bits */
UnsignedShort(fieldData.size()[1]),
flags}
{
/* Yes, this calls into a constexpr function defined in the header --
because I feel that makes more sense than duplicating the full assert
logic */
CORRADE_ASSERT(fieldData.isContiguous<1>(), "Trade::SceneFieldData: second field view dimension is not contiguous", );
}
SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const char* const stringData, const SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const char* const stringData, const SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const SceneFieldFlags flags) noexcept:
_size{mappingData.size()}, _size{mappingData.size()},
_name{name}, _name{name},
@ -647,6 +690,8 @@ Containers::StridedArrayView1D<const void> SceneFieldData::mappingData(const Con
Containers::StridedArrayView1D<const void> SceneFieldData::fieldData() const { Containers::StridedArrayView1D<const void> SceneFieldData::fieldData() const {
CORRADE_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly), CORRADE_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly),
"Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array", {}); "Trade::SceneFieldData::fieldData(): the field is offset-only, supply a data array", {});
CORRADE_ASSERT(fieldType() != SceneFieldType::Bit,
"Trade::SceneFieldData::fieldData(): the field is" << SceneFieldType::Bit << Debug::nospace << ", use fieldBitData() instead", {});
return Containers::StridedArrayView1D<const void>{ return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */ /* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */ /** @todo better ideas for the StridedArrayView API? */
@ -654,12 +699,37 @@ Containers::StridedArrayView1D<const void> SceneFieldData::fieldData() const {
} }
Containers::StridedArrayView1D<const void> SceneFieldData::fieldData(const Containers::ArrayView<const void> data) const { Containers::StridedArrayView1D<const void> SceneFieldData::fieldData(const Containers::ArrayView<const void> data) const {
CORRADE_ASSERT(fieldType() != SceneFieldType::Bit,
"Trade::SceneFieldData::fieldData(): the field is" << SceneFieldType::Bit << Debug::nospace << ", use fieldBitData() instead", {});
return Containers::StridedArrayView1D<const void>{ return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */ /* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedArrayView API? */ /** @todo better ideas for the StridedArrayView API? */
data, _flags & SceneFieldFlag::OffsetOnly ? static_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _field.data.stride}; data, _flags & SceneFieldFlag::OffsetOnly ? static_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _size, _field.data.stride};
} }
Containers::StridedBitArrayView2D SceneFieldData::fieldBitData() const {
CORRADE_ASSERT(!(_flags & SceneFieldFlag::OffsetOnly),
"Trade::SceneFieldData::fieldBitData(): the field is offset-only, supply a data array", {});
CORRADE_ASSERT(fieldType() == SceneFieldType::Bit,
"Trade::SceneFieldData::fieldBitData(): the field is" << fieldType() << Debug::nospace << ", not a bit", {});
return Containers::StridedBitArrayView2D{
/* We're *sure* the view is correct, so faking the view size */
/** @todo better ideas for the StridedBitArrayView API? */
{_fieldData.pointer, _field.data.bitOffset, ~std::size_t{} >> 3},
{std::size_t(_size), _field.data.arraySize ? _field.data.arraySize : 1},
{_field.data.stride, 1}};
}
Containers::StridedBitArrayView2D SceneFieldData::fieldBitData(const Containers::ArrayView<const void> data) const {
CORRADE_ASSERT(fieldType() == SceneFieldType::Bit,
"Trade::SceneFieldData::fieldBitData(): the field is" << fieldType() << Debug::nospace << ", not a bit", {});
return Containers::StridedBitArrayView2D{
{data, 0, data.size()*8},
_flags & SceneFieldFlag::OffsetOnly ? static_cast<const char*>(data.data()) + _fieldData.offset : _fieldData.pointer, _field.data.bitOffset,
{std::size_t(_size), _field.data.arraySize ? _field.data.arraySize : 1},
{_field.data.stride, 1}};
}
namespace { namespace {
/* https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend ; /* https://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend ;
@ -770,28 +840,50 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
accessing the memory would be invalid anyway and enforcing this accessing the memory would be invalid anyway and enforcing this
would lead to unnecessary friction with optional fields. */ would lead to unnecessary friction with optional fields. */
if(field._size) { if(field._size) {
/* Sizes, strides and array sizes are in bits for
SceneFieldType::Bit, additionally bit fields contain also bit
offset in the first byte */
const bool isBitField = field.fieldType() == SceneFieldType::Bit;
const UnsignedShort fieldArraySize = field.fieldArraySize(); const UnsignedShort fieldArraySize = field.fieldArraySize();
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field.fieldType())*(fieldArraySize ? fieldArraySize : 1); const UnsignedInt fieldTypeSize = (isBitField ? 1 : sceneFieldTypeSize(field.fieldType()))*(fieldArraySize ? fieldArraySize : 1);
/* For negative strides the size is negative */
/* Both pointer and offset-only rely on basically same calculation, const std::ptrdiff_t signedMappingSize = (field._size - 1)*field._mappingStride;
do it with offsets in a single place and only interpret as const std::ptrdiff_t signedFieldSize = (isBitField ? field._field.data.bitOffset : 0) + (field._size - 1)*field._field.data.stride;
pointers in the non-offset-only check. Yes, yes, this may read
the `pointer` union member through `offset`. */ /* Calculate begin and end offsets. Both pointer and offset-only
std::size_t mappingBegin = field._mappingData.offset; rely on basically same calculation, do it with offsets in a
std::size_t fieldBegin = field._fieldData.offset; single place and only interpret as pointers in the
std::size_t mappingEnd = mappingBegin + (field._size - 1)*field._mappingStride; non-offset-only check. */
std::size_t fieldEnd = fieldBegin + (field._size - 1)*field._field.data.stride; std::ptrdiff_t mappingBegin = 0;
/* Flip for negative strides */ std::ptrdiff_t mappingEnd = mappingTypeSize;
if(mappingBegin > mappingEnd) std::swap(mappingBegin, mappingEnd); std::ptrdiff_t fieldBegin = 0;
if(fieldBegin > fieldEnd) std::swap(fieldBegin, fieldEnd); std::ptrdiff_t fieldEnd = fieldTypeSize;
/* Add the last element size to the higher address */ /* For negative strides the begin pointer is moved backwards */
mappingEnd += mappingTypeSize; if(field._mappingStride < 0)
fieldEnd += fieldTypeSize; mappingBegin += signedMappingSize;
else
mappingEnd += signedMappingSize;
if(field._field.data.stride < 0)
fieldBegin += signedFieldSize;
else
fieldEnd += signedFieldSize;
/* For bit fields round the offset to whole bytes -- begin down,
end up */
if(isBitField) {
fieldBegin = fieldBegin/8;
fieldEnd = (fieldEnd + 7)/8;
}
/* Add the base data offset to both begin and end. Yes, yes, this
may read the `pointer` union member through `offset`. */
mappingBegin += field._mappingData.offset;
mappingEnd += field._mappingData.offset;
fieldBegin += field._fieldData.offset;
fieldEnd += field._fieldData.offset;
if(field._flags & SceneFieldFlag::OffsetOnly) { if(field._flags & SceneFieldFlag::OffsetOnly) {
CORRADE_ASSERT(mappingEnd <= _data.size(), CORRADE_ASSERT(std::size_t(mappingEnd) <= _data.size(),
"Trade::SceneData: offset-only mapping data of field" << i << "span" << mappingEnd << "bytes but passed data array has only" << _data.size(), ); "Trade::SceneData: offset-only mapping data of field" << i << "span" << mappingEnd << "bytes but passed data array has only" << _data.size(), );
CORRADE_ASSERT(fieldEnd <= _data.size(), CORRADE_ASSERT(std::size_t(fieldEnd) <= _data.size(),
"Trade::SceneData: offset-only field data of field" << i << "span" << fieldEnd << "bytes but passed data array has only" << _data.size(), ); "Trade::SceneData: offset-only field data of field" << i << "span" << fieldEnd << "bytes but passed data array has only" << _data.size(), );
} else { } else {
CORRADE_ASSERT(reinterpret_cast<const void*>(mappingBegin) >= _data.begin() && reinterpret_cast<const void*>(mappingEnd) <= _data.end(), CORRADE_ASSERT(reinterpret_cast<const void*>(mappingBegin) >= _data.begin() && reinterpret_cast<const void*>(mappingEnd) <= _data.end(),
@ -1045,6 +1137,8 @@ SceneFieldData SceneData::fieldData(const UnsignedInt id) const {
const SceneFieldFlags flags = field._flags & ~SceneFieldFlag::OffsetOnly; const SceneFieldFlags flags = field._flags & ~SceneFieldFlag::OffsetOnly;
if(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask) if(field._mappingTypeStringType & Implementation::SceneMappingStringTypeMask)
return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field.stringData(_data), field.fieldType(), fieldDataFieldViewInternal(field), flags}; return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field.stringData(_data), field.fieldType(), fieldDataFieldViewInternal(field), flags};
else if(field._field.data.type == SceneFieldType::Bit)
return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : field._fieldData.pointer, field._field.data.bitOffset, std::size_t(field._size), field._field.data.stride, field._field.data.arraySize, flags};
else else
return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field._field.data.type, fieldDataFieldViewInternal(field), field._field.data.arraySize, flags}; return SceneFieldData{field._name, field.mappingType(), fieldDataMappingViewInternal(field), field._field.data.type, fieldDataFieldViewInternal(field), field._field.data.arraySize, flags};
} }
@ -1303,6 +1397,8 @@ Containers::StridedArrayView2D<const char> SceneData::field(const UnsignedInt id
CORRADE_ASSERT(id < _fields.size(), CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {}); "Trade::SceneData::field(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id]; const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() != SceneFieldType::Bit,
"Trade::SceneData::field():" << field._name << "is" << field.fieldType() << Debug::nospace << ", use fieldBits() or fieldBitArrays() to access it", {});
const UnsignedShort fieldArraySize = field.fieldArraySize(); const UnsignedShort fieldArraySize = field.fieldArraySize();
/* Build a 2D view using information about mapping type size */ /* Build a 2D view using information about mapping type size */
return Containers::arrayCast<2, const char>( return Containers::arrayCast<2, const char>(
@ -1316,6 +1412,8 @@ Containers::StridedArrayView2D<char> SceneData::mutableField(const UnsignedInt i
CORRADE_ASSERT(id < _fields.size(), CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::mutableField(): index" << id << "out of range for" << _fields.size() << "fields", {}); "Trade::SceneData::mutableField(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id]; const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() != SceneFieldType::Bit,
"Trade::SceneData::mutableField():" << field._name << "is" << field.fieldType() << Debug::nospace << ", use mutableFieldBits() or mutableFieldBitArrays() to access it", {});
const UnsignedShort fieldArraySize = field.fieldArraySize(); const UnsignedShort fieldArraySize = field.fieldArraySize();
/* Build a 2D view using information about attribute type size */ /* Build a 2D view using information about attribute type size */
const auto out = Containers::arrayCast<2, const char>( const auto out = Containers::arrayCast<2, const char>(
@ -1343,6 +1441,100 @@ Containers::StridedArrayView2D<char> SceneData::mutableField(const SceneField na
return mutableField(fieldId); return mutableField(fieldId);
} }
Containers::StridedBitArrayView1D SceneData::fieldBits(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldBits(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() == SceneFieldType::Bit,
"Trade::SceneData::fieldBits():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a bit", {});
CORRADE_ASSERT(!field.fieldArraySize(),
"Trade::SceneData::fieldBits():" << field._name << "is an array field, use fieldBitArrays() to access it", {});
/** @todo this is duplicated four times with a few variants in the
fieldBits() overloads below plus once more in fieldData(), any way to
deduplicate this without writing an equally nasty helper or using
expensive transposes to get a 1D array from 2D? */
return Containers::StridedBitArrayView1D{
{_data.data(), 0, _data.size()*8},
field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : field._fieldData.pointer,
field._field.data.bitOffset,
field._size, field._field.data.stride};
}
Containers::StridedBitArrayView2D SceneData::fieldBitArrays(const UnsignedInt id) const {
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::fieldBitArrays(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() == SceneFieldType::Bit,
"Trade::SceneData::fieldBitArrays():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a bit", {});
return Containers::StridedBitArrayView2D{
{_data.data(), 0, _data.size()*8},
field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : field._fieldData.pointer,
field._field.data.bitOffset,
{std::size_t(field._size), field._field.data.arraySize ? field._field.data.arraySize : 1},
{field._field.data.stride, 1}};
}
Containers::MutableStridedBitArrayView1D SceneData::mutableFieldBits(const UnsignedInt id) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableFieldBits(): data not mutable", {});
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::mutableFieldBits(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() == SceneFieldType::Bit,
"Trade::SceneData::mutableFieldBits():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a bit", {});
CORRADE_ASSERT(!field.fieldArraySize(),
"Trade::SceneData::mutableFieldBits():" << field._name << "is an array field, use fieldBitArrays() to access it", {});
return Containers::MutableStridedBitArrayView1D{
{_data.data(), 0, _data.size()*8},
field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : static_cast<char*>(const_cast<void*>(field._fieldData.pointer)),
field._field.data.bitOffset,
field._size, field._field.data.stride};
}
Containers::MutableStridedBitArrayView2D SceneData::mutableFieldBitArrays(const UnsignedInt id) {
CORRADE_ASSERT(_dataFlags & DataFlag::Mutable,
"Trade::SceneData::mutableFieldBitArrays(): data not mutable", {});
CORRADE_ASSERT(id < _fields.size(),
"Trade::SceneData::mutableFieldBitArrays(): index" << id << "out of range for" << _fields.size() << "fields", {});
const SceneFieldData& field = _fields[id];
CORRADE_ASSERT(field.fieldType() == SceneFieldType::Bit,
"Trade::SceneData::mutableFieldBitArrays():" << field._name << "is" << field._field.data.type << Debug::nospace << ", not a bit", {});
return Containers::MutableStridedBitArrayView2D{
{_data.data(), 0, _data.size()*8},
field._flags & SceneFieldFlag::OffsetOnly ? _data.data() + field._fieldData.offset : static_cast<char*>(const_cast<void*>(field._fieldData.pointer)),
field._field.data.bitOffset,
{std::size_t(field._size), field._field.data.arraySize ? field._field.data.arraySize : 1},
{field._field.data.stride, 1}};
}
Containers::StridedBitArrayView1D SceneData::fieldBits(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::fieldBits(): field" << name << "not found", {});
return fieldBits(fieldId);
}
Containers::StridedBitArrayView2D SceneData::fieldBitArrays(const SceneField name) const {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::fieldBitArrays(): field" << name << "not found", {});
return fieldBitArrays(fieldId);
}
Containers::MutableStridedBitArrayView1D SceneData::mutableFieldBits(const SceneField name) {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::mutableFieldBits(): field" << name << "not found", {});
return mutableFieldBits(fieldId);
}
Containers::MutableStridedBitArrayView2D SceneData::mutableFieldBitArrays(const SceneField name) {
const UnsignedInt fieldId = findFieldIdInternal(name);
CORRADE_ASSERT(fieldId != ~UnsignedInt{},
"Trade::SceneData::mutableFieldBitArrays(): field" << name << "not found", {});
return mutableFieldBitArrays(fieldId);
}
const char* SceneData::fieldDataStringDataInternal(const SceneFieldData& field) const { const char* SceneData::fieldDataStringDataInternal(const SceneFieldData& field) const {
return static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ? _data.data() : field._fieldData.pointer) + extractStringFieldOffset(field._field.strideOffset); return static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ? _data.data() : field._fieldData.pointer) + extractStringFieldOffset(field._field.strideOffset);
} }

430
src/Magnum/Trade/SceneData.h

@ -397,9 +397,13 @@ enum class SceneFieldType: UnsignedShort {
/* 1-12 used by String* types defined below as they need to fit into 4 bits /* 1-12 used by String* types defined below as they need to fit into 4 bits
to be stored in a single byte together with SceneMappingType */ to be stored in a single byte together with SceneMappingType */
/* 13 reserved for Bit, which needs StridedBitArrayView first */ /**
* A single bit or an array of bits. Use @ref SceneData::fieldBits() or
* @relativeref{SceneData,fieldBitArrays()} for convenient access.
*/
Bit = 13,
Float = 14, /**< @relativeref{Magnum,Float} */ Float, /**< @relativeref{Magnum,Float} */
Half, /**< @relativeref{Magnum,Half} */ Half, /**< @relativeref{Magnum,Half} */
Double, /**< @relativeref{Magnum,Double} */ Double, /**< @relativeref{Magnum,Double} */
UnsignedByte, /**< @relativeref{Magnum,UnsignedByte} */ UnsignedByte, /**< @relativeref{Magnum,UnsignedByte} */
@ -685,6 +689,8 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, SceneFieldType value);
@brief Size of given scene field type @brief Size of given scene field type
@m_since_latest @m_since_latest
Expects that @p type isn't @ref SceneFieldType::Bit, for which the size isn't
representable in bytes.
@see @ref sceneFieldTypeAlignment() @see @ref sceneFieldTypeAlignment()
*/ */
MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type); MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type);
@ -693,6 +699,8 @@ MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeSize(SceneFieldType type);
@brief Alignment of given scene field type @brief Alignment of given scene field type
@m_since_latest @m_since_latest
Expects that @p type isn't @ref SceneFieldType::Bit, for which the alignment
isn't representable in bytes.
@see @ref sceneFieldTypeSize() @see @ref sceneFieldTypeSize()
*/ */
MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type); MAGNUM_TRADE_EXPORT UnsignedInt sceneFieldTypeAlignment(SceneFieldType type);
@ -835,6 +843,16 @@ field specifying data for object @cpp 0 @ce, second entry for object
with @ref SceneFieldFlag::ImplicitMapping, which is a superset of with @ref SceneFieldFlag::ImplicitMapping, which is a superset of
@relativeref{SceneFieldFlag,OrderedMapping}. @relativeref{SceneFieldFlag,OrderedMapping}.
@subsection Trade-SceneFieldData-usage-bits Bit fields
Bit fields have dedicated constructors taking a
@ref Containers::StridedBitArrayView1D or
@ref Containers::StridedBitArrayView2D, and because the type is always
@ref SceneFieldType::Bit, it's omitted. For offset-only bit fields there's a
@ref SceneFieldData(SceneField, std::size_t, SceneMappingType, std::size_t, std::ptrdiff_t, std::size_t, std::size_t, std::ptrdiff_t, UnsignedShort, SceneFieldFlags)
constructor that omits @ref SceneFieldType as well, but contains an additional
bit offset parameter.
@subsection Trade-SceneFieldData-usage-strings String fields @subsection Trade-SceneFieldData-usage-strings String fields
String fields have to be constructed using dedicated constructors that String fields have to be constructed using dedicated constructors that
@ -865,8 +883,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param name Field name * @param name Field name
* @param mappingType Object mapping type * @param mappingType Object mapping type
* @param mappingData Object mapping data * @param mappingData Object mapping data
* @param fieldType Field type. `SceneFieldType::String*` * @param fieldType Field type. @ref SceneFieldType::Bit and
* values are not allowed here. * `SceneFieldType::String*` values are not allowed here.
* @param fieldData Field data * @param fieldData Field data
* @param fieldArraySize Field array size. Use @cpp 0 @ce for * @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields. * non-array fields.
@ -887,8 +905,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @brief Construct from 2D type-erased views * @brief Construct from 2D type-erased views
* @param name Field name * @param name Field name
* @param mappingData Object mapping data * @param mappingData Object mapping data
* @param fieldType Field type. `SceneFieldType::String*` * @param fieldType Field type. @ref SceneFieldType::Bit and
* values are not allowed here. * `SceneFieldType::String*` values are not allowed here.
* @param fieldData Field data * @param fieldData Field data
* @param fieldArraySize Field array size. Use @cpp 0 @ce for * @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields. * non-array fields.
@ -959,6 +977,126 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/** @overload */ /** @overload */
template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {} template<class T, class U> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedArrayView2D<U>& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, Containers::stridedArrayView(mappingData), fieldData, flags} {}
/**
* @brief Construct a bit field with a type-erased mapping view
* @param name Field name
* @param mappingType Object mapping type
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Field type is implicitly @ref SceneFieldType::Bit and array size is
* @cpp 0 @ce. Expects that @p mappingData and @p fieldData have the
* same size. At the moment only custom fields can be bits, which means
* this constructor can't be used with a builtin @p name.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::StridedBitArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept;
#else
/* Has to be a template to avoid having to include StridedBitArrayView
here -- compared to StridedArrayView it's used rather rarely */
template<class T> constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::BasicStridedBitArrayView<1, T>& fieldData, SceneFieldFlags flags = {}) noexcept;
/* Uhh, and because of the template we need this overload as well */
template<class T> constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, Containers::BasicBitArrayView<T> fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, mappingType, mappingData, Containers::BasicStridedBitArrayView<1, T>{fieldData}, flags} {}
#endif
/**
* @brief Construct a bit field with a 2D type-erased mapping view
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Field type is implicitly @ref SceneFieldType::Bit and array size is
* @cpp 0 @ce. Expects that @p mappingData and @p fieldData have the
* same size in the first dimension and that the second dimension of
* @p mappingData is contiguous and its size is either 1, 2, 4 or 8,
* corresponding to one of the @ref SceneMappingType values. At the
* moment only custom fields can be bits, which means this constructor
* can't be used with a builtin @p name.
*/
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const Containers::StridedBitArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept;
/**
* @brief Construct a bit field
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Detects @ref SceneMappingType based on @p T and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, const Containers::StridedBitArrayView1D&, SceneFieldFlags).
*/
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedBitArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept;
/** @overload */
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedBitArrayView1D& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, stridedArrayView(mappingData), fieldData, flags} {}
/**
* @brief Construct an array bit field with a type-erased mapping view
* @param name Field name
* @param mappingType Object mapping type
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Field type is implicitly @ref SceneFieldType::Bit. Expects that
* @p mappingData and @p fieldData have the same size in the first
* dimension and that the second dimension of @p fieldData is
* contiguous. At the moment only custom fields can be bits, which
* means this constructor can't be used with a builtin @p name.
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::StridedBitArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept;
#else
/* Has to be a template to avoid having to include StridedBitArrayView
here -- compared to StridedArrayView it's used rather rarely */
template<class T> constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::BasicStridedBitArrayView<2, T>& fieldData, SceneFieldFlags flags = {}) noexcept;
#endif
/**
* @brief Construct an array bit field with a 2D type-erased mapping view
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Field type is implicitly @ref SceneFieldType::Bit and array size is
* @cpp 0 @ce. Expects that @p mappingData and @p fieldData have the
* same size in the first dimension, that the second dimension of
* @p mappingData is contiguous and its size is either 1, 2, 4 or 8,
* corresponding to one of the @ref SceneMappingType values, and that
* the second dimension of @p fieldData is contiguous. At the moment
* only custom fields can be bits, which means this constructor
* can't be used with a builtin @p name.
*/
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const Containers::StridedBitArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept;
/**
* @brief Construct an array bit field
* @param name Field name
* @param mappingData Object mapping data
* @param fieldData Field data
* @param flags Field flags. @ref SceneFieldFlag::OffsetOnly
* and @ref SceneFieldFlag::NullTerminatedString is not allowed
* here.
*
* Detects @ref SceneMappingType based on @p T and calls @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, const Containers::StridedBitArrayView2D&, SceneFieldFlags).
*/
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedBitArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept;
/** @overload */
template<class T> constexpr explicit SceneFieldData(SceneField name, const Containers::ArrayView<T>& mappingData, const Containers::StridedBitArrayView2D& fieldData, SceneFieldFlags flags = {}) noexcept: SceneFieldData{name, stridedArrayView(mappingData), fieldData, flags} {}
/** /**
* @brief Construct a string field from type-erased views * @brief Construct a string field from type-erased views
* @param name Field name * @param name Field name
@ -1053,8 +1191,8 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @param mappingType Object mapping type * @param mappingType Object mapping type
* @param mappingOffset Object mapping data offset * @param mappingOffset Object mapping data offset
* @param mappingStride Object mapping data stride * @param mappingStride Object mapping data stride
* @param fieldType Field type. `SceneFieldType::String*` * @param fieldType Field type. @ref SceneFieldType::Bit and
* values are not allowed here. * `SceneFieldType::String*` values are not allowed here.
* @param fieldOffset Field data offset * @param fieldOffset Field data offset
* @param fieldStride Field data stride * @param fieldStride Field data stride
* @param fieldArraySize Field array size. Use @cpp 0 @ce for * @param fieldArraySize Field array size. Use @cpp 0 @ce for
@ -1083,6 +1221,46 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/** @overload */ /** @overload */
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags) noexcept: SceneFieldData{name, size, mappingType, mappingOffset, mappingStride, fieldType, fieldOffset, fieldStride, 0, flags} {} explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, SceneFieldType fieldType, std::size_t fieldOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags) noexcept: SceneFieldData{name, size, mappingType, mappingOffset, mappingStride, fieldType, fieldOffset, fieldStride, 0, flags} {}
/**
* @brief Construct an offset-only bit field
* @param name Field name
* @param size Number of entries
* @param mappingType Object mapping type
* @param mappingOffset Object mapping data offset
* @param mappingStride Object mapping data stride
* @param fieldOffset Field data offset in bytes
* @param fieldBitOffset Field data bit offset
* @param fieldStride Field data stride *in bits*
* @param fieldArraySize Field array size. Use @cpp 0 @ce for
* non-array fields.
* @param flags Field flags.
* @ref SceneFieldFlag::OffsetOnly is set implicitly.
* @ref SceneFieldFlag::NullTerminatedString is not allowed here.
*
* Instances created this way refer to offsets in unspecified
* external scene data instead of containing the data views directly.
* Useful when the location of the scene data array is not known at
* field construction time. At the moment only custom fields can be
* bits, which means this constructor can't be used with a builtin
* @p name. Expects that @p fieldBitOffset is less than @cpp 8 @ce, for
* consistency with @ref Corrade::Containers::BasicStridedBitArrayView "Containers::StridedBitArrayView"
* also expects that @p size fits into 29 bits on 32-bit platforms and
* into 61 bits on 64-bit platforms.
*
* Note that due to the @cpp constexpr @ce nature of this constructor,
* no @p mappingType checks against @p mappingStride can be done.
* You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, const Containers::StridedBitArrayView1D&, SceneFieldFlags)
* or @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, const Containers::StridedBitArrayView2D&, SceneFieldFlags)
* constructors if you want additional safeguards.
* @see @ref flags(),
* @ref mappingData(Containers::ArrayView<const void>) const,
* @ref fieldData(Containers::ArrayView<const void>) const
*/
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, std::size_t fieldOffset, std::size_t fieldBitOffset, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;
/** @overload */
explicit constexpr SceneFieldData(SceneField name, std::size_t size, SceneMappingType mappingType, std::size_t mappingOffset, std::ptrdiff_t mappingStride, std::size_t fieldOffset, std::size_t fieldBitOffset, std::ptrdiff_t fieldStride, SceneFieldFlags flags) noexcept: SceneFieldData{name, size, mappingType, mappingOffset, mappingStride, fieldOffset, fieldBitOffset, fieldStride, 0, flags} {}
/** /**
* @brief Construct an offset-only string field * @brief Construct an offset-only string field
* @param name Field name * @param name Field name
@ -1170,22 +1348,53 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
/** /**
* @brief Type-erased field data * @brief Type-erased field data
* *
* Expects that the field does not have @ref SceneFieldFlag::OffsetOnly * Expects that the field is not @ref SceneFieldType::Bit and does not
* set, in that case use the @ref fieldData(Containers::ArrayView<const void>) const * have @ref SceneFieldFlag::OffsetOnly set. For bit fields use
* overload instead. * @ref fieldBitData() instead, for offset-only fields use the
* @see @ref flags() * @ref fieldData(Containers::ArrayView<const void>) const overload
* instead.
* @see @ref fieldType(), @ref flags()
*/ */
Containers::StridedArrayView1D<const void> fieldData() const; Containers::StridedArrayView1D<const void> fieldData() const;
/** /**
* @brief Type-erased field data for an offset-only field * @brief Type-erased field data for an offset-only field
* *
* If the field does not have @ref SceneFieldFlag::OffsetOnly set, the * Expects that the field is not @ref SceneFieldType::Bit, in that case
* @p data parameter is ignored. * use @ref fieldBitData(Containers::ArrayView<const void>) const
* @see @ref flags(), @ref fieldData() const * instead. If the field does not have @ref SceneFieldFlag::OffsetOnly
* set, the @p data parameter is ignored.
* @see @ref fieldType(), @ref flags(), @ref fieldData() const
*/ */
Containers::StridedArrayView1D<const void> fieldData(Containers::ArrayView<const void> data) const; Containers::StridedArrayView1D<const void> fieldData(Containers::ArrayView<const void> data) const;
/**
* @brief Bit field data
*
* Expects that the field is @ref SceneFieldType::Bit and does not have
* @ref SceneFieldFlag::OffsetOnly set. For non-bit fields use
* @ref fieldData() instead, for offset-only bit fields use the
* @ref fieldBitData(Containers::ArrayView<const void>) const overload
* instead. The returned view is 2D with the second dimension being
* always @cpp 1 @ce for non-array fields. The second dimension is
* always contiguous.
* @see @ref fieldType(), @ref flags()
*/
Containers::StridedBitArrayView2D fieldBitData() const;
/**
* @brief Bit field data for an offset-only field
*
* Expects that the field is @ref SceneFieldType::Bit, otherwise use
* @ref fieldData(Containers::ArrayView<const void>) const instead. If
* the field does not have @ref SceneFieldFlag::OffsetOnly set, the
* @p data parameter is ignored. The returned view is 2D with the
* second dimension being always @cpp 1 @ce for non-array fields. The
* second dimension is always contiguous.
* @see @ref fieldType(), @ref flags(), @ref fieldBitData() const
*/
Containers::StridedBitArrayView2D fieldBitData(Containers::ArrayView<const void> data) const;
/** /**
* @brief Base data pointer for a string field * @brief Base data pointer for a string field
* *
@ -1222,6 +1431,13 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
private: private:
friend SceneData; friend SceneData;
/* Delegated to from all StridedBitArrayView constructors, contains
assertions common for all variants */
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const void* fieldData, UnsignedByte fieldBitOffset, std::size_t fieldSize, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize, SceneFieldFlags flags) noexcept;
/* Delegated to from all StridedBitArrayView constructors with a 2D
mapping view, contains common SceneMappingType handling */
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, const void* fieldData, UnsignedByte fieldBitOffset, std::size_t fieldSize, std::ptrdiff_t fieldStride, UnsignedShort fieldArraySize, SceneFieldFlags flags) noexcept;
union Data { union Data {
/* FFS C++ why this doesn't JUST WORK goddamit?! It's already past /* FFS C++ why this doesn't JUST WORK goddamit?! It's already past
the End Of Times AND YET this piece of complex shit can't do the the End Of Times AND YET this piece of complex shit can't do the
@ -1256,10 +1472,10 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
plenty, making it relative to _fieldData.pointer accounts for cases plenty, making it relative to _fieldData.pointer accounts for cases
where an absolute pointer itself wouldn't fit into 48 bits. where an absolute pointer itself wouldn't fit into 48 bits.
0 2 4 6 8 LE 0 2 4 6 7 8 LE
+------------+------------+------------+------------+ +------------+------------+------------+------+-----+
| | type | array size | (unused) | | | type | array size | bOff | |
+ stride +------------+------------+------------+ + stride +------------+------------+------+-----+
| | string data offset | | | string data offset |
+------------+--------------------------------------+ +------------+--------------------------------------+
8 6 4 2 0 BE */ 8 6 4 2 0 BE */
@ -1268,14 +1484,15 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
needed. I just want to initialize one or the other union needed. I just want to initialize one or the other union
field! */ field! */
constexpr explicit Field(): data{} {} constexpr explicit Field(): data{} {}
constexpr explicit Field(Short stride, SceneFieldType type, UnsignedShort arraySize): data{stride, type, arraySize} {} constexpr explicit Field(Short stride, SceneFieldType type, UnsignedShort arraySize, UnsignedByte bitOffset = 0): data{stride, type, arraySize, bitOffset} {}
constexpr explicit Field(Short stride, Long offset): strideOffset{(UnsignedLong(stride) & 0xffff)|((UnsignedLong(offset) & 0xffffffffffffull) << 16)} {} constexpr explicit Field(Short stride, Long offset): strideOffset{(UnsignedLong(stride) & 0xffff)|((UnsignedLong(offset) & 0xffffffffffffull) << 16)} {}
struct { struct {
Short stride; Short stride;
SceneFieldType type; SceneFieldType type;
UnsignedShort arraySize; UnsignedShort arraySize;
/* 2 bytes unused */ UnsignedByte bitOffset; /* for FieldType::Bit, 5 bits unused */
/* 1 byte unused */
} data; } data;
UnsignedLong strideOffset; /* Upper 48 bits on LE, lower 48 on BE */ UnsignedLong strideOffset; /* Upper 48 bits on LE, lower 48 on BE */
} _field; } _field;
@ -1448,7 +1665,9 @@ scale, it may prove problematic with huge scenes. Or maybe the internal
representation is already optimized for best processing efficiency and the representation is already optimized for best processing efficiency and the
convenience functions would ruin that. The @ref SceneData class thus provides convenience functions would ruin that. The @ref SceneData class thus provides
access directly to the stored object mapping and field data using the access directly to the stored object mapping and field data using the
@ref mapping() and @ref field() accessors. @ref mapping() and @ref field() accessors, together with specialized
@ref fieldBits() for accessing bit fields and @ref fieldStrings() for accessing
string fields.
However, since each @ref SceneField can be in a variety of types, you're However, since each @ref SceneField can be in a variety of types, you're
expected to either check that the type is indeed what you expect using expected to either check that the type is indeed what you expect using
@ -2201,6 +2420,9 @@ class MAGNUM_TRADE_EXPORT SceneData {
* get the field in a concrete type. You can also use * get the field in a concrete type. You can also use
* @ref field(SceneField) const to directly get data for given named * @ref field(SceneField) const to directly get data for given named
* field. * field.
*
* For @ref SceneFieldType::Bit use @ref fieldBits() or
* @ref fieldBitArrays() instead.
* @see @ref Corrade::Containers::StridedArrayView::isContiguous(), * @see @ref Corrade::Containers::StridedArrayView::isContiguous(),
* @ref sceneFieldTypeSize(), @ref mutableField(UnsignedInt) * @ref sceneFieldTypeSize(), @ref mutableField(UnsignedInt)
*/ */
@ -2224,6 +2446,8 @@ class MAGNUM_TRADE_EXPORT SceneData {
* is expected to correspond to @ref fieldType(UnsignedInt) const. The * is expected to correspond to @ref fieldType(UnsignedInt) const. The
* field is also expected to not be an array, in that case you need to * field is also expected to not be an array, in that case you need to
* use the overload below by using @cpp T[] @ce instead of @cpp T @ce. * use the overload below by using @cpp T[] @ce instead of @cpp T @ce.
* For @ref SceneFieldType::Bit use @ref fieldBits() or
* @ref fieldBitArrays() instead.
* *
* You can also use the non-templated @ref parentsAsArray(), * You can also use the non-templated @ref parentsAsArray(),
* @ref transformations2DAsArray(), @ref transformations3DAsArray(), * @ref transformations2DAsArray(), @ref transformations3DAsArray(),
@ -2355,6 +2579,100 @@ class MAGNUM_TRADE_EXPORT SceneData {
*/ */
template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableField(SceneField name); template<class T, class = typename std::enable_if<std::is_array<T>::value>::type> Containers::StridedArrayView2D<typename std::remove_extent<T>::type> mutableField(SceneField name);
/**
* @brief Contents of given bit field
* @m_since_latest
*
* Expects that @p id is smaller than @ref fieldCount() const and that
* the field is @ref SceneFieldType::Bit. The field is also expected to
* not be an array, in that case you need to use
* @ref fieldBitArrays(UnsignedInt) const instead.
* @see @ref fieldType(UnsignedInt) const,
* @ref fieldArraySize(UnsignedInt) const
*/
Containers::StridedBitArrayView1D fieldBits(UnsignedInt id) const;
/**
* @brief Contents of given array bit field
* @m_since_latest
*
* Same as above, except that it works with array fields as well. The
* second dimension is guaranteed to be contiguous and have the same
* size as reported by @ref fieldArraySize(UnsignedInt) const for given
* field. For non-array fields the second dimension has a size of
* @cpp 1 @ce.
*/
Containers::StridedBitArrayView2D fieldBitArrays(UnsignedInt id) const;
/**
* @brief Mutable contents of given bit field
* @m_since_latest
*
* Like @ref fieldBits(UnsignedInt) const, but returns a mutable view.
* Expects that the scene is mutable.
* @see @ref dataFlags()
*/
Containers::MutableStridedBitArrayView1D mutableFieldBits(UnsignedInt id);
/**
* @brief Mutable contents of given array bit field
* @m_since_latest
*
* Same as above, except that it works with array fields as well. The
* second dimension is guaranteed to be contiguous and have the same
* size as reported by @ref fieldArraySize(UnsignedInt) const for given
* field. For non-array fields the second dimension has a size of
* @cpp 1 @ce.
*/
Containers::MutableStridedBitArrayView2D mutableFieldBitArrays(UnsignedInt id);
/**
* @brief Contents of given named bit field
* @m_since_latest
*
* Expects that @p name exists and that the field is
* @ref SceneFieldType::Bit. The field is also expected to not be an
* array, in that case you need to use
* @ref fieldBitArrays(SceneField) const instead.
* @see @ref hasField(), @ref fieldType(SceneField) const,
* @ref fieldArraySize(SceneField) const
*/
Containers::StridedBitArrayView1D fieldBits(SceneField name) const;
/**
* @brief Contents of given array bit field
* @m_since_latest
*
* Same as above, except that it works with array fields as well. The
* second dimension is guaranteed to be contiguous and have the same
* size as reported by @ref fieldArraySize(SceneField) const for given
* field. For non-array fields the second dimension has a size of
* @cpp 1 @ce.
*/
Containers::StridedBitArrayView2D fieldBitArrays(SceneField name) const;
/**
* @brief Mutable contents of given bit field
* @m_since_latest
*
* Like @ref fieldBits(SceneField) const, but returns a mutable view.
* Expects that the scene is mutable.
* @see @ref dataFlags()
*/
Containers::MutableStridedBitArrayView1D mutableFieldBits(SceneField name);
/**
* @brief Mutable contents of given array bit field
* @m_since_latest
*
* Same as above, except that it works with array fields as well. The
* second dimension is guaranteed to be contiguous and have the same
* size as reported by @ref fieldArraySize(SceneField) const for given
* field. For non-array fields the second dimension has a size of
* @cpp 1 @ce.
*/
Containers::MutableStridedBitArrayView2D mutableFieldBitArrays(SceneField name);
/** /**
* @brief Base data pointer for given string field * @brief Base data pointer for given string field
* @m_since_latest * @m_since_latest
@ -3676,7 +3994,9 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappi
(CORRADE_CONSTEXPR_ASSERT(fieldData.stride() >= -32768 && fieldData.stride() <= 32767, (CORRADE_CONSTEXPR_ASSERT(fieldData.stride() >= -32768 && fieldData.stride() <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldData.stride()), Short(fieldData.stride())), "Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldData.stride()), Short(fieldData.stride())),
(CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType), (CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: use a string constructor for" << fieldType), fieldType), "Trade::SceneFieldData: use a string constructor for" << fieldType),
CORRADE_CONSTEXPR_ASSERT(fieldType != SceneFieldType::Bit,
"Trade::SceneFieldData: use a bit constructor for" << fieldType), fieldType),
(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name), (CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_fieldData{fieldData.data()} {} _fieldData{fieldData.data()} {}
@ -3694,6 +4014,45 @@ template<class T, class U> constexpr SceneFieldData::SceneFieldData(const SceneF
flags flags
} {} } {}
/* Assumes that fieldDataBitOffset and fieldDataSize is already bounds-checked,
either by the StridedBitArrayView itself or by the offset-only constructor;
and fieldArraySize as well by the constructor taking the 2D bit view */
constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const void* const fieldData, const UnsignedByte fieldBitOffset, const std::size_t fieldSize, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept:
_size{(CORRADE_CONSTEXPR_ASSERT(mappingData.size() == fieldSize,
"Trade::SceneFieldData: expected" << name << "mapping and field view to have the same size but got" << mappingData.size() << "and" << fieldSize), mappingData.size())},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, SceneFieldType::Bit),
"Trade::SceneFieldData:" << SceneFieldType::Bit << "is not a valid type for" << name), name)},
_flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)),
"Trade::SceneFieldData: can't pass" << (flags & (SceneFieldFlag::OffsetOnly|SceneFieldFlag::NullTerminatedString)) << "for a view of" << SceneFieldType::Bit), flags)},
/* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType'
set but not used" */
_mappingTypeStringType(UnsignedByte(mappingType)),
_mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingData.stride() >= -32768 && mappingData.stride() <= 32767,
"Trade::SceneFieldData: expected mapping view stride to fit into 16 bits but got" << mappingData.stride()), Short(mappingData.stride()))},
_mappingData{mappingData.data()},
_field{
(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)),
SceneFieldType::Bit, fieldArraySize, fieldBitOffset},
_fieldData{fieldData} {}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T> constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::BasicStridedBitArrayView<1, T>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingType, mappingData, fieldData.data(), UnsignedByte(fieldData.offset()), fieldData.size(), fieldData.stride(), 0, flags} {}
#endif
template<class T> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedBitArrayView1D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, fieldData, flags} {}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T> constexpr SceneFieldData::SceneFieldData(const SceneField name, const SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, const Containers::BasicStridedBitArrayView<2, T>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, mappingType, mappingData, fieldData.data(), UnsignedByte(fieldData.offset()), fieldData.size()[0],
(CORRADE_CONSTEXPR_ASSERT(fieldData.stride()[1] == 1,
"Trade::SceneFieldData: second field view dimension is not contiguous"), fieldData.stride()[0]),
/* There's no need to check that the array size fits into 16 bits, as the
stronger requirement is that the signed stride fits into 16 bits */
UnsignedShort(fieldData.size()[1]), flags} {}
#endif
template<class T> constexpr SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const Containers::StridedBitArrayView2D& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, fieldData, flags} {}
template<class T> inline SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, stringData, fieldType, fieldData, flags} {} template<class T> inline SceneFieldData::SceneFieldData(const SceneField name, const Containers::StridedArrayView1D<T>& mappingData, const char* stringData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, const SceneFieldFlags flags) noexcept: SceneFieldData{name, Implementation::sceneMappingTypeFor<typename std::remove_const<T>::type>(), mappingData, stringData, fieldType, fieldData, flags} {}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept: constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept:
@ -3712,11 +4071,34 @@ constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_
(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767, (CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)), "Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)),
(CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType), (CORRADE_CONSTEXPR_ASSERT(!Implementation::isSceneFieldTypeString(fieldType),
"Trade::SceneFieldData: use a string constructor for" << fieldType), fieldType), "Trade::SceneFieldData: use a string constructor for" << fieldType),
CORRADE_CONSTEXPR_ASSERT(fieldType != SceneFieldType::Bit,
"Trade::SceneFieldData: use a bit constructor for" << fieldType), fieldType),
(CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name), (CORRADE_CONSTEXPR_ASSERT(!fieldArraySize || Implementation::isSceneFieldArrayAllowed(name),
"Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)}, "Trade::SceneFieldData:" << name << "can't be an array field"), fieldArraySize)},
_fieldData{fieldOffset} {} _fieldData{fieldOffset} {}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const std::size_t fieldOffset, const std::size_t fieldBitOffset, const std::ptrdiff_t fieldStride, const UnsignedShort fieldArraySize, const SceneFieldFlags flags) noexcept:
_size{(CORRADE_CONSTEXPR_ASSERT(size < std::size_t{1} << (sizeof(std::size_t)*8 - 3),
"Trade::SceneFieldData: size expected to be smaller than 2^" << Debug::nospace << (sizeof(std::size_t)*8 - 3) << "bits, got" << size), size)},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, SceneFieldType::Bit),
"Trade::SceneFieldData:" << SceneFieldType::Bit << "is not a valid type for" << name), name)},
_flags{(CORRADE_CONSTEXPR_ASSERT(!(flags & SceneFieldFlag::NullTerminatedString),
"Trade::SceneFieldData: can't pass" << (flags & SceneFieldFlag::NullTerminatedString) << "for" << SceneFieldType::Bit), flags|SceneFieldFlag::OffsetOnly)},
/* Can't use {} because GCC 4.8 then says "warning: parameter 'mappingType'
set but not used" */
_mappingTypeStringType(UnsignedByte(mappingType)),
_mappingStride{(CORRADE_CONSTEXPR_ASSERT(mappingStride >= -32768 && mappingStride <= 32767,
"Trade::SceneFieldData: expected mapping view stride to fit into 16 bits but got" << mappingStride), Short(mappingStride))},
_mappingData{mappingOffset},
_field{
(CORRADE_CONSTEXPR_ASSERT(fieldStride >= -32768 && fieldStride <= 32767,
"Trade::SceneFieldData: expected field view stride to fit into 16 bits but got" << fieldStride), Short(fieldStride)),
SceneFieldType::Bit, fieldArraySize,
(CORRADE_CONSTEXPR_ASSERT(fieldBitOffset < 8,
"Trade::SceneFieldData: bit offset expected to be smaller than 8, got" << fieldBitOffset), UnsignedByte(fieldBitOffset))},
_fieldData{fieldOffset} {}
constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const std::size_t stringOffset, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const SceneFieldFlags flags) noexcept: constexpr SceneFieldData::SceneFieldData(const SceneField name, const std::size_t size, const SceneMappingType mappingType, const std::size_t mappingOffset, const std::ptrdiff_t mappingStride, const std::size_t stringOffset, const SceneFieldType fieldType, const std::size_t fieldOffset, const std::ptrdiff_t fieldStride, const SceneFieldFlags flags) noexcept:
_size{size}, _size{size},
_name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType), _name{(CORRADE_CONSTEXPR_ASSERT(Implementation::isSceneFieldTypeCompatibleWithField(name, fieldType),

1107
src/Magnum/Trade/Test/SceneDataTest.cpp

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save