Skip to content

Commit

Permalink
Trade: allow omitting implicit SceneFieldData object mapping.
Browse files Browse the repository at this point in the history
  • Loading branch information
mosra committed Dec 7, 2021
1 parent 4418bec commit 6b6e59c
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 9 deletions.
11 changes: 11 additions & 0 deletions doc/snippets/MagnumTrade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,17 @@ Trade::SceneData{Trade::SceneMappingType::UnsignedInt, 120, std::move(data),
/* [SceneFieldData-usage-offset-only] */
}

{
/* [SceneFieldData-usage-implicit-mapping] */
Containers::ArrayView<Matrix4> transformations = DOXYGEN_ELLIPSIS({});

Trade::SceneFieldData field{Trade::SceneField::Transformation,
Containers::ArrayView<UnsignedInt>{nullptr, transformations.size()},
transformations,
Trade::SceneFieldFlag::ImplicitMapping};
/* [SceneFieldData-usage-implicit-mapping] */
}

{
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D> Object3D;
Expand Down
43 changes: 36 additions & 7 deletions src/Magnum/Trade/SceneData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,19 +594,30 @@ SceneData::SceneData(const SceneMappingType mappingType, const UnsignedLong mapp
const UnsignedInt fieldTypeSize = sceneFieldTypeSize(field._fieldType)*
(field._fieldArraySize ? field._fieldArraySize : 1);
if(field._flags & SceneFieldFlag::OffsetOnly) {
const std::size_t mappingSize = field._mappingData.offset + (field._size - 1)*field._mappingStride + mappingTypeSize;
/* If an offset-only field has an implicit mapping, we ignore
the offset / size completely */
if(!(field._flags >= SceneFieldFlag::ImplicitMapping)) {
const std::size_t mappingSize = field._mappingData.offset + (field._size - 1)*field._mappingStride + mappingTypeSize;
CORRADE_ASSERT(mappingSize <= _data.size(),
"Trade::SceneData: offset-only mapping data of field" << i << "span" << mappingSize << "bytes but passed data array has only" << _data.size(), );
}

const std::size_t fieldSize = field._fieldData.offset + (field._size - 1)*field._fieldStride + fieldTypeSize;
CORRADE_ASSERT(mappingSize <= _data.size(),
"Trade::SceneData: offset-only mapping data of field" << i << "span" << mappingSize << "bytes but passed data array has only" << _data.size(), );
CORRADE_ASSERT(fieldSize <= _data.size(),
"Trade::SceneData: offset-only field data of field" << i << "span" << fieldSize << "bytes but passed data array has only" << _data.size(), );

} else {
const void* const mappingBegin = field._mappingData.pointer;
/* If a field has an implicit mapping, we allow it to be
nullptr */
if(!(field._flags >= SceneFieldFlag::ImplicitMapping && !field._mappingData.pointer)) {
const void* const mappingBegin = field._mappingData.pointer;
const void* const mappingEnd = static_cast<const char*>(field._mappingData.pointer) + (field._size - 1)*field._mappingStride + mappingTypeSize;
CORRADE_ASSERT(mappingBegin >= _data.begin() && mappingEnd <= _data.end(),
"Trade::SceneData: mapping data [" << Debug::nospace << mappingBegin << Debug::nospace << ":" << Debug::nospace << mappingEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
}

const void* const fieldBegin = field._fieldData.pointer;
const void* const mappingEnd = static_cast<const char*>(field._mappingData.pointer) + (field._size - 1)*field._mappingStride + mappingTypeSize;
const void* const fieldEnd = static_cast<const char*>(field._fieldData.pointer) + (field._size - 1)*field._fieldStride + fieldTypeSize;
CORRADE_ASSERT(mappingBegin >= _data.begin() && mappingEnd <= _data.end(),
"Trade::SceneData: mapping data [" << Debug::nospace << mappingBegin << Debug::nospace << ":" << Debug::nospace << mappingEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
CORRADE_ASSERT(fieldBegin >= _data.begin() && fieldEnd <= _data.end(),
"Trade::SceneData: field data [" << Debug::nospace << fieldBegin << Debug::nospace << ":" << Debug::nospace << fieldEnd << Debug::nospace << "] of field" << i << "are not contained in passed data array [" << Debug::nospace << static_cast<const void*>(_data.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_data.end()) << Debug::nospace << "]", );
}
Expand Down Expand Up @@ -806,6 +817,12 @@ Containers::ArrayView<char> SceneData::mutableData() & {

Containers::StridedArrayView1D<const void> SceneData::fieldDataMappingViewInternal(const SceneFieldData& field, const std::size_t offset, const std::size_t size) const {
CORRADE_INTERNAL_ASSERT(offset + size <= field._size);

/* If this is a offset-only field with implicit mapping, ignore the
offset/stride and always assume it's not present */
if(field._flags >= (SceneFieldFlag::OffsetOnly|SceneFieldFlag::ImplicitMapping))
return {{nullptr, ~std::size_t{}}, size, field._mappingStride};

return Containers::StridedArrayView1D<const void>{
/* We're *sure* the view is correct, so faking the view size */
{static_cast<const char*>(field._flags & SceneFieldFlag::OffsetOnly ?
Expand Down Expand Up @@ -1140,6 +1157,18 @@ void SceneData::mappingIntoInternal(const UnsignedInt fieldId, const std::size_t
checked by the callers */

const SceneFieldData& field = _fields[fieldId];

/* If we don't have any data for an implicit mapping or the implicit
mapping is offset-only (where we always assume there's no data),
generate the sequence */
if((field._flags >= SceneFieldFlag::ImplicitMapping && !field._mappingData.pointer) ||
(field._flags >= (SceneFieldFlag::ImplicitMapping|SceneFieldFlag::OffsetOnly)))
{
for(std::size_t i = 0; i != destination.size(); ++i)
destination[i] = offset + i;
return;
}

const Containers::StridedArrayView1D<const void> mappingData = fieldDataMappingViewInternal(field, offset, destination.size());
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);

Expand Down
62 changes: 60 additions & 2 deletions src/Magnum/Trade/SceneData.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,9 @@ enum class SceneFieldFlag: UnsignedByte {
* from 0 up to size of the field. A superset of
* @ref SceneFieldFlag::OrderedMapping. Object IDs in fields marked with
* this flag can be looked up with an @f$ \mathcal{O}(1) @f$ complexity,
* but the field is restricted to exactly one value for each object.
* but the field is restricted to exactly one value for each object. If
* this flag is set, the object mapping view is allowed to be
* @cpp nullptr @ce.
*
* Note that validity of the object mapping data isn't checked in any way
* and if the data doesn't correspond to rules of the flag, queries such
Expand Down Expand Up @@ -647,7 +649,18 @@ In some cases the object mapping is even implicit, i.e. the first entry of the
field specifying data for object @cpp 0 @ce, second entry for object
@cpp 1 @ce, third for object @cpp 2 @ce and so on. You can annotate such fields
with @ref SceneFieldFlag::ImplicitMapping, which is a superset of
@relativeref{SceneFieldFlag,OrderedMapping}.
@relativeref{SceneFieldFlag,OrderedMapping}. Furthermore, to avoid having to
generate such mapping data, the mapping view can be @cpp nullptr @ce if this
flag is present. The view however still needs to have a size matching the field
data size and the same @ref SceneMappingType as other fields passed to the
@link SceneData @endlink:
@snippet MagnumTrade.cpp SceneFieldData-usage-implicit-mapping
Fields that are both @ref SceneFieldFlag::OffsetOnly and
@ref SceneFieldFlag::ImplicitMapping have their object mapping data always
ignored as it's not possible to know whether the offset points to actual data
or not.
*/
class MAGNUM_TRADE_EXPORT SceneFieldData {
public:
Expand Down Expand Up @@ -675,6 +688,14 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* Expects that @p mappingData and @p fieldData have the same size,
* @p fieldType corresponds to @p name and @p fieldArraySize is zero
* for builtin fields.
*
* If @p flags contain @ref SceneFieldFlag::ImplicitMapping, the
* @p mappingData can be a @cpp nullptr @ce view (although it still has
* to follow other constraints regarding size and type). While
* @ref SceneData::mapping() will return it as-is,
* @relativeref{SceneData,mappingAsArray()} and
* @relativeref{SceneData,mappingInto()} functions will generate its
* contents on-the-fly.
*/
constexpr explicit SceneFieldData(SceneField name, SceneMappingType mappingType, const Containers::StridedArrayView1D<const void>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView1D<const void>& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;

Expand All @@ -699,6 +720,14 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @p fieldData is contiguous and its size matches @p fieldType and
* @p fieldArraySize and that @p fieldType corresponds to @p name and
* @p fieldArraySize is zero for builtin attributes.
*
* If @p flags contain @ref SceneFieldFlag::ImplicitMapping, the
* @p mappingData can be a @cpp nullptr @ce view (although it still has
* to follow other constraints regarding size and type). While
* @ref SceneData::mapping() will return it as-is,
* @relativeref{SceneData,mappingAsArray()} and
* @relativeref{SceneData,mappingInto()} functions will generate its
* contents on-the-fly.
*/
explicit SceneFieldData(SceneField name, const Containers::StridedArrayView2D<const char>& mappingData, SceneFieldType fieldType, const Containers::StridedArrayView2D<const char>& fieldData, UnsignedShort fieldArraySize = 0, SceneFieldFlags flags = {}) noexcept;

Expand Down Expand Up @@ -779,6 +808,15 @@ class MAGNUM_TRADE_EXPORT SceneFieldData {
* @p fieldType / @p fieldArraySize checks against @p fieldStride can
* be done. You're encouraged to use the @ref SceneFieldData(SceneField, SceneMappingType, const Containers::StridedArrayView1D<const void>&, SceneFieldType, const Containers::StridedArrayView1D<const void>&, UnsignedShort, SceneFieldFlags)
* constructor if you want additional safeguards.
*
* If @p flags contain @ref SceneFieldFlag::ImplicitMapping, the
* @p mappingOffset and @p mappingStride fields are ignored and the
* object mapping is assumed to not be present (however you still have
* to follow constraints regarding its type). The
* @ref SceneData::mapping() will then return a @cpp nullptr @ce view,
* and the @relativeref{SceneData,mappingAsArray()} and
* @relativeref{SceneData,mappingInto()} functions will generate its
* contents on-the-fly.
* @see @ref flags(), @ref fieldArraySize(),
* @ref mappingData(Containers::ArrayView<const void>) const,
* @ref fieldData(Containers::ArrayView<const void>) const
Expand Down Expand Up @@ -1621,6 +1659,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* to @ref SceneMappingType size) and is guaranteed to be contiguous.
* Use the templated overload below to get the mapping in a concrete
* type.
*
* If the field has @ref SceneFieldFlag::ImplicitMapping set and no
* data was supplied for it or it's @ref SceneFieldFlag::OffsetOnly,
* the returned view will be correctly sized but @cpp nullptr @ce.
* @see @ref mutableMapping(UnsignedInt),
* @ref Corrade::Containers::StridedArrayView::isContiguous(),
* @ref sceneMappingTypeSize()
Expand All @@ -1644,6 +1686,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* The @p fieldId is expected to be smaller than @ref fieldCount() and
* @p T is expected to correspond to @ref mappingType().
*
* If the field has @ref SceneFieldFlag::ImplicitMapping set and either
* no data was supplied for it or it's @ref SceneFieldFlag::OffsetOnly,
* the returned view will be correctly sized but @cpp nullptr @ce.
*
* You can also use the non-templated @ref mappingAsArray() accessor
* (or the combined @ref parentsAsArray(),
* @ref transformations2DAsArray(), @ref transformations3DAsArray(),
Expand Down Expand Up @@ -1677,6 +1723,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* @ref SceneMappingType size) and is guaranteed to be contiguous. Use
* the templated overload below to get the object mapping in a concrete
* type.
*
* If the field has @ref SceneFieldFlag::ImplicitMapping set and either
* no data was supplied for it or it's @ref SceneFieldFlag::OffsetOnly,
* the returned view will be correctly sized but @cpp nullptr @ce.
* @see @ref hasField(), @ref mapping(UnsignedInt) const,
* @ref mutableMapping(SceneField),
* @ref Corrade::Containers::StridedArrayView::isContiguous()
Expand All @@ -1700,6 +1750,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* The @p fieldName is expected to exist and @p T is expected to
* correspond to @ref mappingType().
*
* If the field has @ref SceneFieldFlag::ImplicitMapping set and either
* no data was supplied for it or it's @ref SceneFieldFlag::OffsetOnly,
* the returned view will be correctly sized but @cpp nullptr @ce.
*
* You can also use the non-templated @ref mappingAsArray() accessor
* (or the combined @ref parentsAsArray(),
* @ref transformations2DAsArray(), @ref transformations3DAsArray(),
Expand Down Expand Up @@ -1896,6 +1950,10 @@ class MAGNUM_TRADE_EXPORT SceneData {
* arbitrary underlying type and returns it in a newly-allocated array.
* The @p fieldId is expected to be smaller than @ref fieldCount().
*
* If the field has @ref SceneFieldFlag::ImplicitMapping set and either
* no data was supplied for it or it's @ref SceneFieldFlag::OffsetOnly,
* the data will be generated on-the-fly.
*
* Note that, for common fields, you can also use the
* @ref parentsAsArray(), @ref transformations2DAsArray(),
* @ref transformations3DAsArray(),
Expand Down
Loading

0 comments on commit 6b6e59c

Please sign in to comment.