From 331a277fe3481aa54a388a67ebf282fb1145b8a2 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Wed, 8 Feb 2023 15:16:49 -0700 Subject: [PATCH] Automatically convert between ArrayHandleBasic and ArrayHandleRuntimeVec The `UnknownArrayHandle` has been updated to allow `ArrayHandleRuntimeVec` to work interchangeably with basic `ArrayHandle`. If an `ArrayHandleRuntimeVec` is put into an `UnknownArrayHandle`, it can be later retrieved as an `ArrayHandleBasic` as long as the base component type matches and it has the correct amount of components. This means that an array can be created as an `ArrayHandleRuntimeVec` and be used with any filters or most other features designed to operate on basic `ArrayHandle`s. Likewise, an array added as a basic `ArrayHandle` can be retrieved in an `ArrayHandleRuntimeVec`. This makes it easier to pull arrays from VTK-m and place them in external structures (such as `vtkDataArray`). --- docs/changelog/runtime-vec-array.md | 11 ++ vtkm/VecFromPortal.h | 9 ++ vtkm/cont/ArrayHandleRuntimeVec.h | 52 ++++++- vtkm/cont/UnknownArrayHandle.cxx | 141 ++++++++++++++--- vtkm/cont/UnknownArrayHandle.h | 144 +++++++++++++++++- .../testing/UnitTestArrayExtractComponent.cxx | 6 +- .../testing/UnitTestArrayHandleRuntimeVec.cxx | 53 ++++++- .../UnitTestSerializationArrayHandle.cxx | 3 +- .../testing/UnitTestUnknownArrayHandle.cxx | 55 +++++++ 9 files changed, 440 insertions(+), 34 deletions(-) diff --git a/docs/changelog/runtime-vec-array.md b/docs/changelog/runtime-vec-array.md index 01bbae39e..a6dd859ee 100644 --- a/docs/changelog/runtime-vec-array.md +++ b/docs/changelog/runtime-vec-array.md @@ -10,3 +10,14 @@ proper `Vec` value type. This allows one part of code (such as a file reader) to create an array with any `Vec` size, and then that array can be fed to an algorithm that expects an `ArrayHandleBasic` of a certain value type. + +The `UnknownArrayHandle` has also been updated to allow +`ArrayHandleRuntimeVec` to work interchangeably with basic `ArrayHandle`. +If an `ArrayHandleRuntimeVec` is put into an `UnknownArrayHandle`, it can +be later retrieved as an `ArrayHandleBasic` as long as the base component +type matches and it has the correct amount of components. This means that +an array can be created as an `ArrayHandleRuntimeVec` and be used with any +filters or most other features designed to operate on basic `ArrayHandle`s. +Likewise, an array added as a basic `ArrayHandle` can be retrieved in an +`ArrayHandleRuntimeVec`. This makes it easier to pull arrays from VTK-m and +place them in external structures (such as `vtkDataArray`). diff --git a/vtkm/VecFromPortal.h b/vtkm/VecFromPortal.h index b68468307..a5b5caca2 100644 --- a/vtkm/VecFromPortal.h +++ b/vtkm/VecFromPortal.h @@ -136,6 +136,15 @@ struct VecTraits> return vector[componentIndex]; } + VTKM_SUPPRESS_EXEC_WARNINGS + VTKM_EXEC_CONT + static void SetComponent(const VecType& vector, + vtkm::IdComponent componentIndex, + const ComponentType& value) + { + vector[componentIndex] = value; + } + VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT static void CopyInto(const VecType& src, vtkm::Vec& dest) diff --git a/vtkm/cont/ArrayHandleRuntimeVec.h b/vtkm/cont/ArrayHandleRuntimeVec.h index 67012d79d..6006452a9 100644 --- a/vtkm/cont/ArrayHandleRuntimeVec.h +++ b/vtkm/cont/ArrayHandleRuntimeVec.h @@ -26,6 +26,31 @@ namespace vtkm namespace internal { +namespace detail +{ + +template +struct UnrollVecImpl +{ + using type = vtkm::Vec; +}; + +template +struct UnrollVecImpl> +{ + using subtype = typename UnrollVecImpl::type; + using type = vtkm::Vec; +}; + +} // namespace detail + +// A helper class that unrolls a nested `Vec` to a single layer `Vec`. This is similar +// to `vtkm::VecFlat`, except that this only flattens `vtkm::Vec` objects, and not +// any other `Vec`-like objects. The reason is that a `vtkm::Vec` is the same as N +// consecutive `T` objects whereas the same may not be said about other `Vec`-like objects. +template +using UnrollVec = typename detail::UnrollVecImpl::type; + template class VTKM_ALWAYS_EXPORT ArrayPortalRuntimeVec { @@ -115,6 +140,12 @@ class Storage, vtkm::cont::StorageTagRunti using ComponentsStorage = vtkm::cont::internal::Storage; + VTKM_STATIC_ASSERT_MSG( + vtkm::internal::SafeVecTraits::NUM_COMPONENTS == 1, + "ArrayHandleRuntimeVec only supports scalars grouped into a single Vec. Nested Vecs can " + "still be used with ArrayHandleRuntimeVec. The values are treated as flattened (like " + "with VecFlat)."); + using ComponentsArray = vtkm::cont::ArrayHandle; VTKM_STATIC_ASSERT_MSG( @@ -346,11 +377,28 @@ public: /// entries grouped in a Vec. /// template -VTKM_CONT vtkm::cont::ArrayHandleRuntimeVec make_ArrayHandleRuntimeVec( +VTKM_CONT auto make_ArrayHandleRuntimeVec( vtkm::IdComponent numComponents, + const vtkm::cont::ArrayHandle& componentsArray = + vtkm::cont::ArrayHandle{}) +{ + using UnrolledVec = vtkm::internal::UnrollVec; + using ComponentType = typename UnrolledVec::ComponentType; + + // Use some dangerous magic to convert the basic array to its base component and create + // an ArrayHandleRuntimeVec from that. + vtkm::cont::ArrayHandle flatComponents( + componentsArray.GetBuffers()); + + return vtkm::cont::ArrayHandleRuntimeVec( + numComponents * UnrolledVec::NUM_COMPONENTS, flatComponents); +} + +template +VTKM_CONT auto make_ArrayHandleRuntimeVec( const vtkm::cont::ArrayHandle& componentsArray) { - return vtkm::cont::ArrayHandleRuntimeVec(numComponents, componentsArray); + return make_ArrayHandleRuntimeVec(1, componentsArray); } namespace internal diff --git a/vtkm/cont/UnknownArrayHandle.cxx b/vtkm/cont/UnknownArrayHandle.cxx index dde409c17..1c5f1f4ab 100644 --- a/vtkm/cont/UnknownArrayHandle.cxx +++ b/vtkm/cont/UnknownArrayHandle.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -45,12 +46,16 @@ struct AllVecImpl> template using AllVec = typename AllVecImpl::type; +template +using IsBasicStorage = std::is_same; +template +using RemoveBasicStorage = vtkm::ListRemoveIf; + using UnknownSerializationTypes = vtkm::ListAppend, AllVec<3>, AllVec<4>>; -using UnknownSerializationStorage = - vtkm::ListAppend, + vtkm::List, vtkm::cont::StorageTagConstant, @@ -432,30 +437,82 @@ std::string SerializableTypeString::Get() } } // namespace vtkm::cont -namespace mangled_diy_namespace +namespace { -void Serialization::save(BinaryBuffer& bb, - const vtkm::cont::UnknownArrayHandle& obj) +enum struct SerializedArrayType : vtkm::UInt8 +{ + BasicArray = 0, + SpecializedStorage +}; + +struct SaveBasicArray +{ + template + VTKM_CONT void operator()(ComponentType, + mangled_diy_namespace::BinaryBuffer& bb, + const vtkm::cont::UnknownArrayHandle& obj, + bool& saved) + { + // Basic arrays and arrays with compatible layouts can be loaed/saved as an + // ArrayHandleRuntimeVec. Thus, we can load/save them all with one routine. + using ArrayType = vtkm::cont::ArrayHandleRuntimeVec; + if (!saved && obj.CanConvert()) + { + ArrayType array = obj.AsArrayHandle(); + vtkmdiy::save(bb, SerializedArrayType::BasicArray); + vtkmdiy::save(bb, vtkm::cont::TypeToString()); + vtkmdiy::save(bb, array); + saved = true; + } + } +}; + +struct LoadBasicArray +{ + template + VTKM_CONT void operator()(ComponentType, + mangled_diy_namespace::BinaryBuffer& bb, + vtkm::cont::UnknownArrayHandle& obj, + const std::string& componentTypeString, + bool& loaded) + { + if (!loaded && (componentTypeString == vtkm::cont::TypeToString())) + { + vtkm::cont::ArrayHandleRuntimeVec array; + vtkmdiy::load(bb, array); + obj = array; + loaded = true; + } + } +}; + +VTKM_CONT void SaveSpecializedArray(mangled_diy_namespace::BinaryBuffer& bb, + const vtkm::cont::UnknownArrayHandle& obj) { vtkm::IdComponent numComponents = obj.GetNumberOfComponents(); switch (numComponents) { case 1: + vtkmdiy::save(bb, SerializedArrayType::SpecializedStorage); vtkmdiy::save(bb, numComponents); - vtkmdiy::save(bb, obj.ResetTypes()); + vtkmdiy::save(bb, + obj.ResetTypes()); break; case 2: + vtkmdiy::save(bb, SerializedArrayType::SpecializedStorage); vtkmdiy::save(bb, numComponents); - vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationStorage>()); + vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationSpecializedStorage>()); break; case 3: + vtkmdiy::save(bb, SerializedArrayType::SpecializedStorage); vtkmdiy::save(bb, numComponents); - vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationStorage>()); + vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationSpecializedStorage>()); break; case 4: + vtkmdiy::save(bb, SerializedArrayType::SpecializedStorage); vtkmdiy::save(bb, numComponents); - vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationStorage>()); + vtkmdiy::save(bb, obj.ResetTypes, UnknownSerializationSpecializedStorage>()); break; default: throw vtkm::cont::ErrorBadType( @@ -465,16 +522,17 @@ void Serialization::save(BinaryBuffer& bb, } } -void Serialization::load(BinaryBuffer& bb, - vtkm::cont::UnknownArrayHandle& obj) +VTKM_CONT void LoadSpecializedArray(mangled_diy_namespace::BinaryBuffer& bb, + vtkm::cont::UnknownArrayHandle& obj) { vtkm::IdComponent numComponents; vtkmdiy::load(bb, numComponents); - vtkm::cont::UncertainArrayHandle array1; - vtkm::cont::UncertainArrayHandle, UnknownSerializationStorage> array2; - vtkm::cont::UncertainArrayHandle, UnknownSerializationStorage> array3; - vtkm::cont::UncertainArrayHandle, UnknownSerializationStorage> array4; + vtkm::cont::UncertainArrayHandle + array1; + vtkm::cont::UncertainArrayHandle, UnknownSerializationSpecializedStorage> array2; + vtkm::cont::UncertainArrayHandle, UnknownSerializationSpecializedStorage> array3; + vtkm::cont::UncertainArrayHandle, UnknownSerializationSpecializedStorage> array4; switch (numComponents) { @@ -499,4 +557,53 @@ void Serialization::load(BinaryBuffer& bb, } } +} // anonymous namespace + +namespace mangled_diy_namespace +{ + +void Serialization::save(BinaryBuffer& bb, + const vtkm::cont::UnknownArrayHandle& obj) +{ + bool saved = false; + + // First, try serializing basic arrays (which we can do for any Vec size). + vtkm::ListForEach(SaveBasicArray{}, vtkm::TypeListBaseC{}, bb, obj, saved); + + // If that did not work, try one of the specialized arrays. + if (!saved) + { + SaveSpecializedArray(bb, obj); + } +} + +void Serialization::load(BinaryBuffer& bb, + vtkm::cont::UnknownArrayHandle& obj) +{ + SerializedArrayType arrayType; + vtkmdiy::load(bb, arrayType); + + switch (arrayType) + { + case SerializedArrayType::BasicArray: + { + std::string componentTypeString; + vtkmdiy::load(bb, componentTypeString); + bool loaded = false; + vtkm::ListForEach( + LoadBasicArray{}, vtkm::TypeListBaseC{}, bb, obj, componentTypeString, loaded); + if (!loaded) + { + throw vtkm::cont::ErrorInternal("Failed to load basic array. Unexpected buffer values."); + } + break; + } + case SerializedArrayType::SpecializedStorage: + LoadSpecializedArray(bb, obj); + break; + default: + throw vtkm::cont::ErrorInternal("Got inappropriate enumeration value for loading array."); + } +} + } // namespace mangled_diy_namespace diff --git a/vtkm/cont/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h index 1f8c2b0e6..88b40590f 100644 --- a/vtkm/cont/UnknownArrayHandle.h +++ b/vtkm/cont/UnknownArrayHandle.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,14 @@ void UnknownAHDelete(void* mem) delete arrayHandle; } +template +const std::vector& UnknownAHBuffers(void* mem) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + return arrayHandle->GetBuffers(); +} + template void* UnknownAHNewInstance() { @@ -259,6 +268,9 @@ struct VTKM_CONT_EXPORT UnknownAHContainer using DeleteType = void(void*); DeleteType* DeleteFunction; + using BuffersType = const std::vector&(void*); + BuffersType* Buffers; + using NewInstanceType = void*(); NewInstanceType* NewInstance; @@ -383,6 +395,7 @@ inline UnknownAHContainer::UnknownAHContainer(const vtkm::cont::ArrayHandle::BaseComponentType>()) , DeleteFunction(detail::UnknownAHDelete) + , Buffers(detail::UnknownAHBuffers) , NewInstance(detail::UnknownAHNewInstance) , NewInstanceBasic(detail::UnknownAHNewInstanceBasic) , NewInstanceFloatBasic(detail::UnknownAHNewInstanceFloatBasic) @@ -646,13 +659,10 @@ public: #ifdef VTKM_MSVC VTKM_DEPRECATED_SUPPRESS_BEGIN #endif - ///@{ - /// Returns this array cast appropriately and stored in the given `ArrayHandle` type. - /// Throws an `ErrorBadType` if the stored array cannot be stored in the given array type. - /// Use the `CanConvert` method to determine if the array can be returned with the given type. - /// + +private: template - VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle& array) const + VTKM_CONT void BaseAsArrayHandle(vtkm::cont::ArrayHandle& array) const { using ArrayType = vtkm::cont::ArrayHandle; if (!this->IsType()) @@ -664,6 +674,21 @@ public: array = *reinterpret_cast(this->Container->ArrayHandlePointer); } +public: + ///@{ + /// Returns this array cast appropriately and stored in the given `ArrayHandle` type. + /// Throws an `ErrorBadType` if the stored array cannot be stored in the given array type. + /// Use the `CanConvert` method to determine if the array can be returned with the given type. + /// + template + VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle& array) const + { + this->BaseAsArrayHandle(array); + } + + template + VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle& array) const; + template VTKM_CONT void AsArrayHandle( vtkm::cont::ArrayHandle>& array) const; @@ -677,6 +702,26 @@ public: this->AsArrayHandle()); } + template + VTKM_CONT void AsArrayHandle( + vtkm::cont::ArrayHandle& array) const + { + using BaseT = typename T::ComponentType; + if (this->IsStorageType() && this->IsBaseComponentType()) + { + // Reinterpret the basic array as components, and then wrap that in a runtime vec + // with the correct amount of components. + vtkm::cont::ArrayHandle basicArray( + this->Container->Buffers(this->Container->ArrayHandlePointer)); + array = + vtkm::cont::ArrayHandleRuntimeVec(this->GetNumberOfComponentsFlat(), basicArray); + } + else + { + this->BaseAsArrayHandle(array); + } + } + template VTKM_CONT ArrayType AsArrayHandle() const { @@ -916,6 +961,19 @@ struct UnknownArrayHandleCanConvert } }; +template +struct UnknownArrayHandleCanConvert +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const + { + using UnrolledVec = vtkm::internal::UnrollVec; + return (array.IsType>() || + (array.IsStorageType() && + array.IsBaseComponentType() && + UnrolledVec::NUM_COMPONENTS == array.GetNumberOfComponentsFlat())); + } +}; + template struct UnknownArrayHandleCanConvert> { @@ -946,10 +1004,22 @@ struct UnknownArrayHandleCanConvert> } }; +template +struct UnknownArrayHandleCanConvert +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const + { + using BaseComponentType = typename T::ComponentType; + return (array.IsType>() || + (array.IsStorageType() && + array.IsBaseComponentType())); + } +}; + } // namespace detail template -VTKM_CONT bool UnknownArrayHandle::CanConvert() const +VTKM_CONT inline bool UnknownArrayHandle::CanConvert() const { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); @@ -960,6 +1030,66 @@ VTKM_CONT bool UnknownArrayHandle::CanConvert() const namespace detail { +template ::ComponentType>::NUM_COMPONENTS> +struct UnknownArrayHandleRuntimeVecAsBasic +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle*, + const detail::UnknownAHContainer*, + vtkm::cont::ArrayHandle&) const + { + // This version only gets called if T contains a `Vec`-like object that is not a strict `Vec`. + // This is rare but could happen. In this case, the type cannot be stored in an + // `ArrayHandleRuntimeVec` and therefore the load can never happen, so just ignore. + return false; + } +}; + +template +struct UnknownArrayHandleRuntimeVecAsBasic +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle* self, + const detail::UnknownAHContainer* container, + vtkm::cont::ArrayHandle& array) const + { + using UnrolledVec = vtkm::internal::UnrollVec; + using ComponentType = typename UnrolledVec::ComponentType; + if (self->IsStorageType() && + self->IsBaseComponentType() && + UnrolledVec::NUM_COMPONENTS == self->GetNumberOfComponentsFlat()) + { + // Pull out the components array out of the buffers. The array might not match exactly + // the array put in, but the buffer should still be consistent with the array (which works + // because the size of a basic array is based on the number of bytes in the buffer). + using RuntimeVecType = typename vtkm::cont::ArrayHandleRuntimeVec::ValueType; + using StorageRuntimeVec = + vtkm::cont::internal::Storage; + StorageRuntimeVec::AsArrayHandleBasic(container->Buffers(container->ArrayHandlePointer), + array); + return true; + } + else + { + return false; + } + } +}; + +} // namespace detail + +template +VTKM_CONT inline void UnknownArrayHandle::AsArrayHandle(vtkm::cont::ArrayHandle& array) const +{ + if (!detail::UnknownArrayHandleRuntimeVecAsBasic{}(this, this->Container.get(), array)) + { + this->BaseAsArrayHandle(array); + } +} + +namespace detail +{ + struct UnknownArrayHandleMultiplexerCastTry { template diff --git a/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx b/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx index 07da16d8b..600408474 100644 --- a/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx +++ b/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx @@ -230,13 +230,13 @@ void DoTest() { std::cout << "ArrayHandleRuntimeVec" << std::endl; - vtkm::cont::ArrayHandle array; + vtkm::cont::ArrayHandle array; array.Allocate(ARRAY_SIZE * 4); SetPortal(array.WritePortal()); CheckOutputArray(vtkm::cont::make_ArrayHandleRuntimeVec(2, array), - vtkm::cont::ArrayHandleRuntimeVec(2)); + vtkm::cont::ArrayHandleRuntimeVec(2)); CheckOutputArray(vtkm::cont::make_ArrayHandleRuntimeVec(4, array), - vtkm::cont::ArrayHandleRuntimeVec(4)); + vtkm::cont::ArrayHandleRuntimeVec(4)); } { diff --git a/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx b/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx index a56a9d27a..a2da0ad5a 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx @@ -10,6 +10,7 @@ #include +#include #include #include @@ -64,7 +65,52 @@ struct PassThrough : vtkm::worklet::WorkletMapField template VTKM_EXEC void operator()(const InValue& inValue, OutValue& outValue) const { - outValue = inValue; + vtkm::IdComponent inIndex = 0; + vtkm::IdComponent outIndex = 0; + this->FlatCopy(inValue, inIndex, outValue, outIndex); + } + + template + VTKM_EXEC void FlatCopy(const InValue& inValue, + vtkm::IdComponent& inIndex, + OutValue& outValue, + vtkm::IdComponent& outIndex) const + { + using VTraitsIn = vtkm::internal::SafeVecTraits; + using VTraitsOut = vtkm::internal::SafeVecTraits; + VTraitsOut::SetComponent(outValue, outIndex, VTraitsIn::GetComponent(inValue, inIndex)); + inIndex++; + outIndex++; + } + + template + VTKM_EXEC void FlatCopy(const vtkm::Vec& inValue, + vtkm::IdComponent& inIndex, + OutValue& outValue, + vtkm::IdComponent& outIndex) const + { + VTKM_ASSERT(inIndex == 0); + for (vtkm::IdComponent i = 0; i < InN; ++i) + { + FlatCopy(inValue[i], inIndex, outValue, outIndex); + inIndex = 0; + } + } + + template + VTKM_EXEC void FlatCopy(const InValue& inValue, + vtkm::IdComponent& inIndex, + vtkm::Vec& outValue, + vtkm::IdComponent& outIndex) const + { + VTKM_ASSERT(outIndex == 0); + for (vtkm::IdComponent i = 0; i < OutN; ++i) + { + OutComponent outComponent; + FlatCopy(inValue, inIndex, outComponent, outIndex); + outValue[i] = outComponent; + outIndex = 0; + } } }; @@ -80,7 +126,7 @@ struct TestRuntimeVecAsInput baseArray.Allocate(ARRAY_SIZE * NUM_COMPONENTS); SetPortal(baseArray.WritePortal()); - vtkm::cont::ArrayHandleRuntimeVec runtimeVecArray(NUM_COMPONENTS, baseArray); + auto runtimeVecArray = vtkm::cont::make_ArrayHandleRuntimeVec(NUM_COMPONENTS, baseArray); VTKM_TEST_ASSERT(runtimeVecArray.GetNumberOfValues() == ARRAY_SIZE, "Group array reporting wrong array size."); @@ -108,7 +154,8 @@ struct TestRuntimeVecAsInput //verify that you can get the data as a basic array vtkm::cont::ArrayHandle> flatComponents; runtimeVecArray.AsArrayHandleBasic(flatComponents); - VTKM_TEST_ASSERT(test_equal_ArrayHandles(flatComponents, runtimeVecArray)); + VTKM_TEST_ASSERT(test_equal_ArrayHandles( + flatComponents, vtkm::cont::make_ArrayHandleGroupVec(baseArray))); vtkm::cont::ArrayHandle, NUM_COMPONENTS>> nestedComponents; diff --git a/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx b/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx index 30bdcb04e..7b93ea522 100644 --- a/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx +++ b/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx @@ -313,8 +313,7 @@ struct TestArrayHandleRuntimeVec auto flat = RandomArrayHandle::Make(ArraySize * numComps); auto array = vtkm::cont::make_ArrayHandleRuntimeVec(numComps, flat); RunTest(array); - // TODO: Add this back once UnknownArrayHandle supports ArrayHandleRuntimeVec more fully. - //RunTest(MakeTestUnknownArrayHandle(array)); + RunTest(MakeTestUnknownArrayHandle(array)); } }; diff --git a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx index 327da4d4e..036487fd5 100644 --- a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx +++ b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -542,6 +543,57 @@ void TrySetMultiplexerArray() CheckUnknownArray, vtkm::List>(unknownArray, 1); } +template +void TryConvertRuntimeVec() +{ + using BasicArrayType = vtkm::cont::ArrayHandle; + using BasicComponentType = typename vtkm::VecFlat::ComponentType; + constexpr vtkm::IdComponent numFlatComponents = vtkm::VecFlat::NUM_COMPONENTS; + using RuntimeArrayType = vtkm::cont::ArrayHandleRuntimeVec; + + std::cout << " Get basic array as ArrayHandleRuntimeVec" << std::endl; + BasicArrayType inputArray; + inputArray.Allocate(ARRAY_SIZE); + SetPortal(inputArray.WritePortal()); + + vtkm::cont::UnknownArrayHandle unknownWithBasic{ inputArray }; + VTKM_TEST_ASSERT(unknownWithBasic.GetNumberOfComponentsFlat() == numFlatComponents); + + VTKM_TEST_ASSERT(unknownWithBasic.CanConvert()); + RuntimeArrayType runtimeArray = unknownWithBasic.AsArrayHandle(); + + // Hack to convert the array handle to a flat array to make it easy to check the runtime array + vtkm::cont::ArrayHandle> flatInput{ inputArray.GetBuffers() }; + VTKM_TEST_ASSERT(test_equal_ArrayHandles(flatInput, runtimeArray)); + + std::cout << " Get ArrayHandleRuntimeVec as basic array" << std::endl; + vtkm::cont::UnknownArrayHandle unknownWithRuntimeVec{ runtimeArray }; + VTKM_TEST_ASSERT(unknownWithRuntimeVec.GetNumberOfComponentsFlat() == numFlatComponents); + + VTKM_TEST_ASSERT(unknownWithRuntimeVec.CanConvert()); + VTKM_TEST_ASSERT(unknownWithRuntimeVec.CanConvert()); + BasicArrayType outputArray = unknownWithRuntimeVec.AsArrayHandle(); + VTKM_TEST_ASSERT(test_equal_ArrayHandles(inputArray, outputArray)); +} + +void TryConvertRuntimeVec() +{ + std::cout << " Scalar array." << std::endl; + TryConvertRuntimeVec(); + + std::cout << " Equivalent scalar." << std::endl; + TryConvertRuntimeVec(); + + std::cout << " Basic Vec." << std::endl; + TryConvertRuntimeVec(); + + std::cout << " Vec of Vecs." << std::endl; + TryConvertRuntimeVec>(); + + std::cout << " Vec of Vecs of Vecs." << std::endl; + TryConvertRuntimeVec, 2>>(); +} + struct DefaultTypeFunctor { template @@ -573,6 +625,9 @@ void TestUnknownArrayHandle() std::cout << "Try setting ArrayHandleMultiplexer" << std::endl; TrySetMultiplexerArray(); + + std::cout << "Try converting between ArrayHandleRuntimeVec and basic array" << std::endl; + TryConvertRuntimeVec(); } } // anonymous namespace