diff --git a/src/Magnum/SceneTools/CMakeLists.txt b/src/Magnum/SceneTools/CMakeLists.txt index 73680d5dc..03597b040 100644 --- a/src/Magnum/SceneTools/CMakeLists.txt +++ b/src/Magnum/SceneTools/CMakeLists.txt @@ -39,7 +39,8 @@ set(CMAKE_FOLDER "Magnum/SceneTools") find_package(Corrade REQUIRED PluginManager) # Files shared between main library and unit test library -set(MagnumSceneTools_SRCS ) +set(MagnumSceneTools_SRCS + Copy.cpp) # Files compiled with different flags for main library and unit test library set(MagnumSceneTools_GracefulAssert_SRCS @@ -67,22 +68,22 @@ if(MAGNUM_BUILD_DEPRECATED) OrderClusterParents.h) endif() -## Objects shared between main and test library -#add_library(MagnumSceneToolsObjects OBJECT - #${MagnumSceneTools_SRCS} - #${MagnumSceneTools_HEADERS} - #${MagnumSceneTools_PRIVATE_HEADERS}) -#target_include_directories(MagnumSceneToolsObjects PUBLIC $) -#if(NOT MAGNUM_BUILD_STATIC) - #target_compile_definitions(MagnumSceneToolsObjects PRIVATE "MagnumSceneToolsObjects_EXPORTS") -#endif() -#if(NOT MAGNUM_BUILD_STATIC OR MAGNUM_BUILD_STATIC_PIC) - #set_target_properties(MagnumSceneToolsObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) -#endif() +# Objects shared between main and test library +add_library(MagnumSceneToolsObjects OBJECT + ${MagnumSceneTools_SRCS} + ${MagnumSceneTools_HEADERS} + ${MagnumSceneTools_PRIVATE_HEADERS}) +target_include_directories(MagnumSceneToolsObjects PUBLIC $) +if(NOT MAGNUM_BUILD_STATIC) + target_compile_definitions(MagnumSceneToolsObjects PRIVATE "MagnumSceneToolsObjects_EXPORTS") +endif() +if(NOT MAGNUM_BUILD_STATIC OR MAGNUM_BUILD_STATIC_PIC) + set_target_properties(MagnumSceneToolsObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() # Main SceneTools library add_library(MagnumSceneTools ${SHARED_OR_STATIC} - #$ + $ ${MagnumSceneTools_GracefulAssert_SRCS} ${MagnumSceneTools_HEADERS} ${MagnumSceneTools_PRIVATE_HEADERS}) @@ -124,7 +125,7 @@ endif() if(MAGNUM_BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumSceneToolsTestLib ${SHARED_OR_STATIC} ${EXCLUDE_FROM_ALL_IF_TEST_TARGET} - #$ + $ ${MagnumSceneTools_GracefulAssert_SRCS}) set_target_properties(MagnumSceneToolsTestLib PROPERTIES DEBUG_POSTFIX "-d") target_compile_definitions(MagnumSceneToolsTestLib PRIVATE diff --git a/src/Magnum/SceneTools/Combine.h b/src/Magnum/SceneTools/Combine.h index 7261ee65d..b05f457a6 100644 --- a/src/Magnum/SceneTools/Combine.h +++ b/src/Magnum/SceneTools/Combine.h @@ -78,7 +78,8 @@ Calls @ref combineFields(Trade::SceneMappingType, UnsignedLong, Containers::Arra with mapping type, bound and fields coming from @p scene. Useful for conveniently repacking an existing scene and throwing away data not referenced by any field. -@see @ref filterFields(), @ref filterOnlyFields(), @ref filterExceptFields() +@see @ref filterFields(), @ref filterOnlyFields(), @ref filterExceptFields(), + @ref copy(const Trade::SceneData&) */ MAGNUM_SCENETOOLS_EXPORT Trade::SceneData combineFields(const Trade::SceneData& scene); diff --git a/src/Magnum/SceneTools/Copy.cpp b/src/Magnum/SceneTools/Copy.cpp new file mode 100644 index 000000000..5de76a02a --- /dev/null +++ b/src/Magnum/SceneTools/Copy.cpp @@ -0,0 +1,141 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Copy.h" + +#include +#include + +#include "Magnum/Trade/SceneData.h" + +namespace Magnum { namespace SceneTools { + +Trade::SceneData copy(const Trade::SceneData& scene) { + return copy(Trade::SceneData{scene.mappingType(), scene.mappingBound(), + {}, scene.data(), + Trade::sceneFieldDataNonOwningArray(scene.fieldData()), + scene.importerState()}); +} + +Trade::SceneData copy(Trade::SceneData&& scene) { + /* Transfer data if they're owned and mutable, allocate a copy otherwise. + Save also the original data view for new field pointer calculation. */ + const Containers::ArrayView originalData = scene.data(); + Containers::Array data; + if(scene.dataFlags() >= (Trade::DataFlag::Mutable|Trade::DataFlag::Owned)) + data = scene.releaseData(); + else { + data = Containers::Array{NoInit, originalData.size()}; + Utility::copy(originalData, data); + } + + /* There's no way to know if field data are owned until we release them and + check the deleter, but releasing them makes it impossible to use the + convenience SceneData APIs, so we have to do it the hard way. */ + Containers::Array originalFieldData = scene.releaseFieldData(); + + /* If the field data are owned *and* the data weren't copied, we can reuse + the original array in its entirety */ + Containers::Array fieldData; + if(!originalFieldData.deleter() && (scene.dataFlags() & Trade::DataFlag::Owned)) { + fieldData = std::move(originalFieldData); + + /* Otherwise we have to allocate a new one and re-route the fields to a + potentially different data array */ + /** @todo could theoretically also just modify the array in-place if it has + a default deleter, but would need to pay attention to not copy items + to themselves and such */ + } else { + /* Using DefaultInit so the array has a default deleter and isn't + problematic to use in plugins */ + fieldData = Containers::Array{DefaultInit, originalFieldData.size()}; + for(std::size_t i = 0; i != originalFieldData.size(); ++i) { + const Trade::SceneFieldData& originalField = originalFieldData[i]; + + /* If the field is offset-only, copy it directly, yay! */ + if(originalField.flags() & Trade::SceneFieldFlag::OffsetOnly) + fieldData[i] = originalField; + + /* Otherwise there's a bunch of special cases based on its type */ + else { + const Trade::SceneMappingType mappingType = originalField.mappingType(); + const Containers::StridedArrayView1D mappingView{ + data, data.data() + (static_cast(originalField.mappingData().data()) - originalData.data()), + originalField.size(), + originalField.mappingData().stride()}; + + const Trade::SceneFieldType fieldType = originalField.fieldType(); + if(fieldType == Trade::SceneFieldType::Bit) { + if(originalField.fieldArraySize() == 0) { + const Containers::StridedBitArrayView1D fieldView{ + /** @todo explicit construction from an ArrayView?! */ + Containers::BitArrayView{data.data(), 0, data.size()*8}, + data.data() + (static_cast(originalField.fieldBitData().data()) - originalData.data()), + originalField.fieldBitData().offset(), + originalField.size(), + originalField.fieldBitData().stride()[0]}; + fieldData[i] = Trade::SceneFieldData{originalField.name(), + mappingType, mappingView, + fieldView, originalField.flags()}; + } else { + const Containers::StridedBitArrayView2D fieldView{ + /** @todo explicit construction from an ArrayView?! */ + Containers::BitArrayView{data.data(), 0, data.size()*8}, + data.data() + (static_cast(originalField.fieldBitData().data()) - originalData.data()), + originalField.fieldBitData().offset(), + {originalField.size(), originalField.fieldArraySize()}, + originalField.fieldBitData().stride()}; + fieldData[i] = Trade::SceneFieldData{originalField.name(), + mappingType, mappingView, + fieldView, originalField.flags()}; + } + } else { + const Containers::StridedArrayView1D fieldView{ + data, data.data() + (static_cast(originalField.fieldData().data()) - originalData.data()), + originalField.size(), + originalField.fieldData().stride()}; + + if(Trade::Implementation::isSceneFieldTypeString(fieldType)) { + fieldData[i] = Trade::SceneFieldData{originalField.name(), + mappingType, mappingView, + data.data() + (originalField.stringData() - originalData.data()), + fieldType, fieldView, + originalField.flags()}; + } else { + fieldData[i] = Trade::SceneFieldData{originalField.name(), + mappingType, mappingView, + fieldType, fieldView, + originalField.fieldArraySize(), originalField.flags()}; + } + } + } + } + } + + return Trade::SceneData{scene.mappingType(), scene.mappingBound(), + std::move(data), std::move(fieldData), scene.importerState()}; +} + +}} diff --git a/src/Magnum/SceneTools/Copy.h b/src/Magnum/SceneTools/Copy.h new file mode 100644 index 000000000..7ada356fb --- /dev/null +++ b/src/Magnum/SceneTools/Copy.h @@ -0,0 +1,67 @@ +#ifndef Magnum_SceneTools_Copy_h +#define Magnum_SceneTools_Copy_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Function @ref Magnum::SceneTools::copy() + * @m_since_latest + */ + +#include "Magnum/SceneTools/visibility.h" +#include "Magnum/Trade/Trade.h" + +namespace Magnum { namespace SceneTools { + +/** +@brief Make an owned copy of the scene +@m_since_latest + +Allocates a copy of @ref Trade::SceneData::data() and +@relativeref{Trade::SceneData,fieldData()} and returns a new scene with them. +All other properties such as the mapping bound or importer state are passed +through unchanged, the data layout isn't changed in any way. The resulting +@ref Trade::SceneData::dataFlags() are always @ref Trade::DataFlag::Owned and +@ref Trade::DataFlag::Mutable. +@see @ref copy(Trade::SceneData&&) +*/ +MAGNUM_SCENETOOLS_EXPORT Trade::SceneData copy(const Trade::SceneData& material); + +/** +@brief Make a scene with owned data +@m_since_latest + +If @ref Trade::SceneData::dataFlags() are not @ref Trade::DataFlag::Owned and +@ref Trade::DataFlag::Mutable or the field data don't have the default deleter, +allocates a copy of @ref Trade::SceneData::data() or +@relativeref{Trade::SceneData,fieldData()}, otherwise transfers their +ownership. The resulting data are always owned and mutable, the data layout +isn't changed in any way. +*/ +MAGNUM_SCENETOOLS_EXPORT Trade::SceneData copy(Trade::SceneData&& material); + +}} + +#endif diff --git a/src/Magnum/SceneTools/Test/CMakeLists.txt b/src/Magnum/SceneTools/Test/CMakeLists.txt index 654a12940..c5cbc5318 100644 --- a/src/Magnum/SceneTools/Test/CMakeLists.txt +++ b/src/Magnum/SceneTools/Test/CMakeLists.txt @@ -51,6 +51,7 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/$/configure.h INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) corrade_add_test(SceneToolsCombineTest CombineTest.cpp LIBRARIES MagnumSceneToolsTestLib) +corrade_add_test(SceneToolsCopyTest CopyTest.cpp LIBRARIES MagnumSceneTools) corrade_add_test(SceneToolsConvertToSingleFunc___Test ConvertToSingleFunctionObjectsTest.cpp LIBRARIES MagnumSceneToolsTestLib) corrade_add_test(SceneToolsFilterTest FilterTest.cpp LIBRARIES MagnumSceneToolsTestLib) corrade_add_test(SceneToolsHierarchyTest HierarchyTest.cpp LIBRARIES MagnumSceneToolsTestLib) diff --git a/src/Magnum/SceneTools/Test/CopyTest.cpp b/src/Magnum/SceneTools/Test/CopyTest.cpp new file mode 100644 index 000000000..6026ae12d --- /dev/null +++ b/src/Magnum/SceneTools/Test/CopyTest.cpp @@ -0,0 +1,331 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020, 2021, 2022 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/SceneTools/Copy.h" +#include "Magnum/Trade/SceneData.h" + +namespace Magnum { namespace SceneTools { namespace Test { namespace { + +struct CopyTest: TestSuite::Tester { + explicit CopyTest(); + + void test(); + + void rvalueNotOwned(); + void rvalueDataFieldsOwned(); + void rvalueDataOwned(); + void rvalueFieldsOwned(); +}; + +CopyTest::CopyTest() { + addTests({&CopyTest::test, + + &CopyTest::rvalueNotOwned, + &CopyTest::rvalueDataFieldsOwned, + &CopyTest::rvalueDataOwned, + &CopyTest::rvalueFieldsOwned}); +} + +void CopyTest::test() { + const struct Data { + UnsignedShort parentMeshMapping[4]; + Long parent[4]; + UnsignedInt mesh[4]; + UnsignedShort bitsMapping[3]; + bool bit[3]; + UnsignedInt bits[3]; + UnsignedShort stringMapping[2]; + char stringData[7]; + UnsignedByte strings[2]; + } data[]{{ + {1, 3, 5, 2}, + {-1, 1, 3, -1}, + {6667, 29862, 12393, 31222}, + {16, 12, 10}, + {false, true, false}, + {0xa, 0x4, 0xe}, + {100, 200}, + {'N', 'O', '\0', 'y', 'e', 's', '\0'}, + {3, 7} + }}; + + Trade::SceneData scene{Trade::SceneMappingType::UnsignedShort, 201, {}, data, { + Trade::SceneFieldData{Trade::SceneField::Parent, + Containers::arrayView(data->parentMeshMapping), + Containers::arrayView(data->parent)}, + /* Offset-only field */ + Trade::SceneFieldData{Trade::SceneField::Mesh, 4, + Trade::SceneMappingType::UnsignedShort, offsetof(Data, parentMeshMapping), sizeof(UnsignedShort), + Trade::SceneFieldType::UnsignedInt, offsetof(Data, mesh), sizeof(UnsignedInt)}, + /* Bit field */ + Trade::SceneFieldData{Trade::sceneFieldCustom(37), + Containers::arrayView(data->bitsMapping), + Containers::stridedArrayView(data->bit).sliceBit(0)}, + /* Bit array field */ + Trade::SceneFieldData{Trade::sceneFieldCustom(38), + Containers::arrayView(data->bitsMapping), + Containers::StridedBitArrayView2D{Containers::BitArrayView{data->bits}, data->bits, 1, {3, 3}, {sizeof(UnsignedInt)*8, 1}}}, + /* String field */ + Trade::SceneFieldData{Trade::sceneFieldCustom(664), + Containers::arrayView(data->stringMapping), + data->stringData, Trade::SceneFieldType::StringOffset8, + Containers::arrayView(data->strings), Trade::SceneFieldFlag::NullTerminatedString} + }}; + + Trade::SceneData copy = SceneTools::copy(scene); + CORRADE_COMPARE(copy.mappingType(), Trade::SceneMappingType::UnsignedShort); + CORRADE_COMPARE(copy.mappingBound(), 201); + + CORRADE_COMPARE(copy.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable); + CORRADE_COMPARE(copy.fieldCount(), 5); + + for(std::size_t i = 0; i != copy.fieldCount(); ++i) { + CORRADE_ITERATION(i); + + CORRADE_COMPARE(copy.fieldName(i), scene.fieldName(i)); + CORRADE_COMPARE(copy.fieldSize(i), scene.fieldSize(i)); + CORRADE_COMPARE(copy.fieldArraySize(i), scene.fieldArraySize(i)); + CORRADE_COMPARE(copy.fieldType(i), scene.fieldType(i)); + /* Checks also for offset-only flags */ + CORRADE_COMPARE(copy.fieldFlags(i), scene.fieldFlags(i)); + } + + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Parent), + Containers::arrayView(data->parentMeshMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Parent), + Containers::arrayView(data->parent), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Mesh), + Containers::arrayView(data->parentMeshMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Mesh), + Containers::arrayView(data->mesh), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(copy.mapping(Trade::sceneFieldCustom(37)), + Containers::arrayView(data->bitsMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.fieldBits(Trade::sceneFieldCustom(37)), + Containers::stridedArrayView(data->bit).sliceBit(0), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(copy.mapping(Trade::sceneFieldCustom(38)), + Containers::arrayView(data->bitsMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.fieldBitArrays(Trade::sceneFieldCustom(38))[0], + Containers::stridedArrayView({true, false, true}).sliceBit(0), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.fieldBitArrays(Trade::sceneFieldCustom(38))[1], + Containers::stridedArrayView({false, true, false}).sliceBit(0), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.fieldBitArrays(Trade::sceneFieldCustom(38))[2], + Containers::stridedArrayView({true, true, true}).sliceBit(0), + TestSuite::Compare::Container); + + CORRADE_COMPARE_AS(copy.mapping(Trade::sceneFieldCustom(664)), + Containers::arrayView(data->stringMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.fieldStrings(Trade::sceneFieldCustom(664)), + (Containers::StringIterable{"NO", "yes"}), + TestSuite::Compare::Container); + + /* The data layout should be the same and thus the raw data should match + as well */ + CORRADE_COMPARE_AS(copy.data(), + scene.data(), + TestSuite::Compare::Container); + + /* The data should have a default deleter to make this usable in plugins */ + Containers::Array sceneData = copy.releaseData(); + Containers::Array fieldData = copy.releaseFieldData(); + CORRADE_VERIFY(!sceneData.deleter()); + CORRADE_VERIFY(!fieldData.deleter()); +} + +void CopyTest::rvalueNotOwned() { + struct Data { + UnsignedShort parentMapping[2]; + Int parent[2]; + } data[]{{ + {1, 3}, + {-1, 1} + }}; + const Trade::SceneFieldData fields[]{ + Trade::SceneFieldData{Trade::SceneField::Parent, + Containers::arrayView(data->parentMapping), + Containers::arrayView(data->parent)} + }; + + Trade::SceneData copy = SceneTools::copy(Trade::SceneData{Trade::SceneMappingType::UnsignedShort, 12, + Trade::DataFlag::Mutable|Trade::DataFlag::ExternallyOwned, data, + Trade::sceneFieldDataNonOwningArray(fields)}); + + CORRADE_COMPARE(copy.mappingType(), Trade::SceneMappingType::UnsignedShort); + CORRADE_COMPARE(copy.mappingBound(), 12); + CORRADE_COMPARE(copy.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable); + + CORRADE_COMPARE(copy.fieldCount(), 1); + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Parent), + Containers::arrayView(data->parentMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Parent), + Containers::arrayView(data->parent), + TestSuite::Compare::Container); + + /* Nothing should be copied in this case */ + CORRADE_VERIFY(copy.data().data() != static_cast(data)); + CORRADE_VERIFY(copy.fieldData().data() != fields); +} + +void CopyTest::rvalueDataFieldsOwned() { + struct Data { + UnsignedShort parentMapping[2]; + Int parent[2]; + }; + Containers::Array data{NoInit, sizeof(Data)}; + Containers::StridedArrayView1D view = Containers::arrayCast(data); + Utility::copy({{ + {1, 3}, + {-1, 1} + }}, view); + + /* InPlaceInit causes a non-default deleter to be used, which would cause + a copy to be made internally */ + Containers::Array fields{ValueInit, 1}; + fields[0] = Trade::SceneFieldData{Trade::SceneField::Parent, + Containers::arrayView(view[0].parentMapping), + Containers::arrayView(view[0].parent)}; + const Trade::SceneFieldData* originalFields = fields; + + Trade::SceneData copy = SceneTools::copy(Trade::SceneData{Trade::SceneMappingType::UnsignedShort, 12, + std::move(data), std::move(fields)}); + + CORRADE_COMPARE(copy.mappingType(), Trade::SceneMappingType::UnsignedShort); + CORRADE_COMPARE(copy.mappingBound(), 12); + CORRADE_COMPARE(copy.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable); + + CORRADE_COMPARE(copy.fieldCount(), 1); + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Parent), + Containers::arrayView(view[0].parentMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Parent), + Containers::arrayView(view[0].parent), + TestSuite::Compare::Container); + + /* Both data should be transferred without any copy */ + CORRADE_COMPARE(copy.data().data(), view.data()); + CORRADE_COMPARE(copy.fieldData().data(), originalFields); +} + +void CopyTest::rvalueDataOwned() { + struct Data { + UnsignedShort parentMapping[2]; + Int parent[2]; + }; + Containers::Array data{NoInit, sizeof(Data)}; + Containers::StridedArrayView1D view = Containers::arrayCast(data); + Utility::copy({{ + {1, 3}, + {-1, 1} + }}, view); + + Trade::SceneFieldData fields[]{ + Trade::SceneFieldData{Trade::SceneField::Parent, + Containers::arrayView(view[0].parentMapping), + Containers::arrayView(view[0].parent)} + }; + + Trade::SceneData copy = SceneTools::copy(Trade::SceneData{Trade::SceneMappingType::UnsignedShort, 12, + std::move(data), Trade::sceneFieldDataNonOwningArray(fields)}); + + CORRADE_COMPARE(copy.mappingType(), Trade::SceneMappingType::UnsignedShort); + CORRADE_COMPARE(copy.mappingBound(), 12); + CORRADE_COMPARE(copy.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable); + + CORRADE_COMPARE(copy.fieldCount(), 1); + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Parent), + Containers::arrayView(view[0].parentMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Parent), + Containers::arrayView(view[0].parent), + TestSuite::Compare::Container); + + /* Only data should be transferred, fields copied */ + CORRADE_COMPARE(copy.data().data(), view.data()); + CORRADE_VERIFY(copy.fieldData().data() != fields); +} + +void CopyTest::rvalueFieldsOwned() { + struct Data { + UnsignedShort parentMapping[2]; + Int parent[2]; + } data[]{{ + {1, 3}, + {-1, 1} + }}; + + /* InPlaceInit causes a non-default deleter to be used, which would cause + a copy to be made internally */ + Containers::Array fields{ValueInit, 1}; + fields[0] = Trade::SceneFieldData{Trade::SceneField::Parent, + Containers::arrayView(data->parentMapping), + Containers::arrayView(data->parent)}; + const Trade::SceneFieldData* originalFields = fields; + + Trade::SceneData copy = SceneTools::copy(Trade::SceneData{Trade::SceneMappingType::UnsignedShort, 12, + Trade::DataFlag::ExternallyOwned, data, std::move(fields)}); + + CORRADE_COMPARE(copy.mappingType(), Trade::SceneMappingType::UnsignedShort); + CORRADE_COMPARE(copy.mappingBound(), 12); + CORRADE_COMPARE(copy.dataFlags(), Trade::DataFlag::Owned|Trade::DataFlag::Mutable); + + CORRADE_COMPARE(copy.fieldCount(), 1); + CORRADE_COMPARE_AS(copy.mapping(Trade::SceneField::Parent), + Containers::arrayView(data->parentMapping), + TestSuite::Compare::Container); + CORRADE_COMPARE_AS(copy.field(Trade::SceneField::Parent), + Containers::arrayView(data->parent), + TestSuite::Compare::Container); + + /* Data should be copied */ + CORRADE_VERIFY(copy.data().data() != static_cast(data)); + { + CORRADE_EXPECT_FAIL("Field data currently get copied always when they need to be modified."); + CORRADE_COMPARE(copy.fieldData().data(), originalFields); + } +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::SceneTools::Test::CopyTest)