From cdd1dbd7bc0726811407528d246e89065b9487b9 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Mon, 6 Feb 2023 06:50:34 -0700 Subject: [PATCH] Add ArrayHandleRuntimeVec The new `ArrayHandleRuntimeVec` is a fancy `ArrayHandle` allows you to specify a basic array of `Vec`s where the number of components of the `Vec` are not known until runtime. (It can also optionally specify scalars.) The behavior is much like that of `ArrayHandleGroupVecVariable` except that its representation is much more constrained. This constrained representation allows it to be automatically converted to an `ArrayHandleBasic` with the 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. --- docs/changelog/runtime-vec-array.md | 12 + vtkm/VecFromPortal.h | 26 + vtkm/cont/ArrayHandleGroupVecVariable.h | 19 +- vtkm/cont/ArrayHandleRuntimeVec.h | 454 ++++++++++++++++++ vtkm/cont/CMakeLists.txt | 1 + vtkm/cont/testing/CMakeLists.txt | 1 + .../testing/UnitTestArrayExtractComponent.cxx | 86 +++- .../testing/UnitTestArrayHandleRuntimeVec.cxx | 201 ++++++++ .../UnitTestSerializationArrayHandle.cxx | 18 + 9 files changed, 796 insertions(+), 22 deletions(-) create mode 100644 docs/changelog/runtime-vec-array.md create mode 100644 vtkm/cont/ArrayHandleRuntimeVec.h create mode 100644 vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx diff --git a/docs/changelog/runtime-vec-array.md b/docs/changelog/runtime-vec-array.md new file mode 100644 index 000000000..01bbae39e --- /dev/null +++ b/docs/changelog/runtime-vec-array.md @@ -0,0 +1,12 @@ +# Added `ArrayHandleRuntimeVec` to specify vector sizes at runtime. + +The new `ArrayHandleRuntimeVec` is a fancy `ArrayHandle` allows you to +specify a basic array of `Vec`s where the number of components of the `Vec` +are not known until runtime. (It can also optionally specify scalars.) The +behavior is much like that of `ArrayHandleGroupVecVariable` except that its +representation is much more constrained. This constrained representation +allows it to be automatically converted to an `ArrayHandleBasic` with the +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. diff --git a/vtkm/VecFromPortal.h b/vtkm/VecFromPortal.h index 268da892b..b68468307 100644 --- a/vtkm/VecFromPortal.h +++ b/vtkm/VecFromPortal.h @@ -53,6 +53,18 @@ public: } } + template + VTKM_EXEC_CONT operator vtkm::Vec() const + { + vtkm::Vec result; + this->CopyInto(result); + for (vtkm::IdComponent index = this->NumComponents; index < N; ++index) + { + result[index] = vtkm::TypeTraits::ZeroInitialization(); + } + return result; + } + VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT vtkm::internal::ArrayPortalValueReference operator[](vtkm::IdComponent index) const @@ -61,6 +73,20 @@ public: index + this->Offset); } + template + VTKM_EXEC_CONT VecFromPortal& operator=(const vtkm::Vec& src) + { + vtkm::IdComponent numComponents = vtkm::Min(N, this->NumComponents); + for (vtkm::IdComponent index = 0; index < numComponents; ++index) + { + this->Portal.Set(index + this->Offset, src[index]); + } + return *this; + } + + VTKM_EXEC_CONT const PortalType& GetPortal() const { return this->Portal; } + VTKM_EXEC_CONT vtkm::Id GetOffset() const { return this->Offset; } + private: PortalType Portal; vtkm::IdComponent NumComponents; diff --git a/vtkm/cont/ArrayHandleGroupVecVariable.h b/vtkm/cont/ArrayHandleGroupVecVariable.h index 1c28ab194..98f625301 100644 --- a/vtkm/cont/ArrayHandleGroupVecVariable.h +++ b/vtkm/cont/ArrayHandleGroupVecVariable.h @@ -47,7 +47,7 @@ public: { } - /// Copy constructor for any other ArrayPortalConcatenate with a portal type + /// Copy constructor for any other ArrayPortalGroupVecVariable with a portal type /// that can be copied to this portal type. This allows us to do any type /// casting that the portals do (like the non-const to const cast). VTKM_SUPPRESS_EXEC_WARNINGS @@ -77,12 +77,19 @@ public: VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT - void Set(vtkm::Id vtkmNotUsed(index), const ValueType& vtkmNotUsed(value)) const + void Set(vtkm::Id index, const ValueType& value) const { - // The ValueType (VecFromPortal) operates on demand. Thus, if you set - // something in the value, it has already been passed to the array. Perhaps - // we should check to make sure that the value used matches the location - // you are trying to set in the array, but we don't do that. + if ((&value.GetPortal() == &this->ComponentsPortal) && + (value.GetOffset() == this->OffsetsPortal.Get(index))) + { + // The ValueType (VecFromPortal) operates on demand. Thus, if you set + // something in the value, it has already been passed to the array. + } + else + { + // The value comes from somewhere else. Copy data in. + this->Get(index) = value; + } } VTKM_SUPPRESS_EXEC_WARNINGS diff --git a/vtkm/cont/ArrayHandleRuntimeVec.h b/vtkm/cont/ArrayHandleRuntimeVec.h new file mode 100644 index 000000000..af0ae5850 --- /dev/null +++ b/vtkm/cont/ArrayHandleRuntimeVec.h @@ -0,0 +1,454 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_cont_ArrayHandleRuntimeVec_h +#define vtk_m_cont_ArrayHandleRuntimeVec_h + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace vtkm +{ +namespace internal +{ + +template +class VTKM_ALWAYS_EXPORT ArrayPortalRuntimeVec +{ +public: + using ComponentType = typename std::remove_const::type; + using ValueType = vtkm::VecFromPortal; + + ArrayPortalRuntimeVec() = default; + + VTKM_EXEC_CONT ArrayPortalRuntimeVec(const ComponentsPortalType& componentsPortal, + vtkm::IdComponent numComponents) + : ComponentsPortal(componentsPortal) + , NumberOfComponents(numComponents) + { + } + + /// Copy constructor for any other ArrayPortalRuntimeVec with a portal type + /// that can be copied to this portal type. This allows us to do any type + /// casting that the portals do (like the non-const to const cast). + template + VTKM_EXEC_CONT ArrayPortalRuntimeVec(const ArrayPortalRuntimeVec& src) + : ComponentsPortal(src.GetComponentsPortal()) + , NumberOfComponents(src.GetNumberOfComponents()) + { + } + + VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const + { + return this->ComponentsPortal.GetNumberOfValues() / this->NumberOfComponents; + } + + VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const + { + return ValueType( + this->ComponentsPortal, this->NumberOfComponents, index * this->NumberOfComponents); + } + + VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const + { + if ((&value.GetPortal() == &this->ComponentsPortal) && + (value.GetOffset() == (index * this->NumberOfComponents))) + { + // The ValueType (VecFromPortal) operates on demand. Thus, if you set + // something in the value, it has already been passed to the array. + } + else + { + // The value comes from somewhere else. Copy data in. + this->Get(index) = value; + } + } + + VTKM_EXEC_CONT const ComponentsPortalType& GetComponentsPortal() const + { + return this->ComponentsPortal; + } + + VTKM_EXEC_CONT vtkm::IdComponent GetNumberOfComponents() const + { + return this->NumberOfComponents; + } + +private: + ComponentsPortalType ComponentsPortal; + vtkm::IdComponent NumberOfComponents = 0; +}; + +} +} // namespace vtkm::internal + +namespace vtkm +{ +namespace cont +{ + +struct VTKM_ALWAYS_EXPORT StorageTagRuntimeVec +{ +}; + +namespace internal +{ + +template +class Storage, vtkm::cont::StorageTagRuntimeVec> +{ + using ComponentType = typename ComponentsPortal::ValueType; + using ComponentsStorage = + vtkm::cont::internal::Storage; + + using ComponentsArray = vtkm::cont::ArrayHandle; + + VTKM_STATIC_ASSERT_MSG( + (std::is_same::value), + "Used invalid ComponentsPortal type with expected ComponentsStorageTag."); + + struct Info + { + vtkm::IdComponent NumberOfComponents; + }; + + VTKM_CONT static std::vector ComponentsBuffers( + const std::vector& buffers) + { + return std::vector(buffers.begin() + 1, buffers.end()); + } + +public: + using ReadPortalType = + vtkm::internal::ArrayPortalRuntimeVec; + using WritePortalType = + vtkm::internal::ArrayPortalRuntimeVec; + + VTKM_CONT static vtkm::IdComponent GetNumberOfComponents( + const std::vector& buffers) + { + return buffers[0].GetMetaData().NumberOfComponents; + } + + VTKM_CONT static vtkm::Id GetNumberOfValues( + const std::vector& buffers) + { + return ComponentsStorage::GetNumberOfValues(ComponentsBuffers(buffers)) / + GetNumberOfComponents(buffers); + } + + VTKM_CONT static void ResizeBuffers(vtkm::Id numValues, + const std::vector& buffers, + vtkm::CopyFlag preserve, + vtkm::cont::Token& token) + { + ComponentsStorage::ResizeBuffers( + numValues * GetNumberOfComponents(buffers), ComponentsBuffers(buffers), preserve, token); + } + + VTKM_CONT static void Fill(const std::vector&, + const vtkm::VecFromPortal&, + vtkm::Id, + vtkm::Id, + vtkm::cont::Token&) + { + throw vtkm::cont::ErrorBadType("Fill not supported for ArrayHandleRuntimeVec."); + } + + VTKM_CONT static ReadPortalType CreateReadPortal( + const std::vector& buffers, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) + { + return ReadPortalType( + ComponentsStorage::CreateReadPortal(ComponentsBuffers(buffers), device, token), + GetNumberOfComponents(buffers)); + } + + VTKM_CONT static WritePortalType CreateWritePortal( + const std::vector& buffers, + vtkm::cont::DeviceAdapterId device, + vtkm::cont::Token& token) + { + return WritePortalType( + ComponentsStorage::CreateWritePortal(ComponentsBuffers(buffers), device, token), + GetNumberOfComponents(buffers)); + } + + VTKM_CONT static std::vector CreateBuffers( + vtkm::IdComponent numComponents = 1, + const ComponentsArray& componentsArray = ComponentsArray{}) + { + VTKM_LOG_IF_S(vtkm::cont::LogLevel::Warn, + (componentsArray.GetNumberOfValues() % numComponents) != 0, + "Array given to ArrayHandleRuntimeVec has size (" + << componentsArray.GetNumberOfValues() + << ") that is not divisible by the number of components selected (" + << numComponents << ")."); + Info info; + info.NumberOfComponents = numComponents; + return vtkm::cont::internal::CreateBuffers(info, componentsArray); + } + + VTKM_CONT static ComponentsArray GetComponentsArray( + const std::vector& buffers) + { + return ComponentsArray(ComponentsBuffers(buffers)); + } + + VTKM_CONT static void AsArrayHandleBasic( + const std::vector& buffers, + vtkm::cont::ArrayHandle& dest) + { + if (GetNumberOfComponents(buffers) != 1) + { + throw vtkm::cont::ErrorBadType( + "Attempted to pull a scalar array from an ArrayHandleRuntime that does not hold scalars."); + } + dest = GetComponentsArray(buffers); + } + + template + VTKM_CONT static void AsArrayHandleBasic( + const std::vector& buffers, + vtkm::cont::ArrayHandle, vtkm::cont::StorageTagBasic>& dest) + { + if (GetNumberOfComponents(buffers) != N) + { + throw vtkm::cont::ErrorBadType( + "Attempted to pull an array of Vecs of the wrong size from an ArrayHandleRuntime."); + } + dest = vtkm::cont::ArrayHandle, vtkm::cont::StorageTagBasic>( + ComponentsBuffers(buffers)); + } + + template + VTKM_CONT static void AsArrayHandleBasic( + const std::vector& buffers, + vtkm::cont::ArrayHandle, NOuter>, vtkm::cont::StorageTagBasic>& + dest) + { + // Flatten the Vec by one level and attempt to get the array handle for that. + vtkm::cont::ArrayHandleBasic> squashedArray; + AsArrayHandleBasic(buffers, squashedArray); + // Now unsquash the array by stealling the buffers and creating an array of the right type + dest = + vtkm::cont::ArrayHandle, NOuter>, vtkm::cont::StorageTagBasic>( + squashedArray.GetBuffers()); + } +}; + +} // namespace internal + +/// \brief Fancy array handle for a basic array with runtime selected vec size. +/// +/// It is sometimes the case that you need to create an array of `Vec`s where +/// the number of components is not known until runtime. This is problematic +/// for normal `ArrayHandle`s because you have to specify the size of the `Vec`s +/// as a template parameter at compile time. `ArrayHandleRuntimeVec` can be used +/// in this case. +/// +/// Note that this version of `ArrayHandle` breaks some of the assumptions +/// about `ArrayHandle` a little bit. Typically, there is exactly one type for +/// every value in the array, and this value is also the same between the +/// control and execution environment. However, this class uses `VecFromPortal` +/// it implement a `Vec`-like class that has a variable number of +/// values, and this type can change between control and execution +/// environments. +/// +/// It is possible to provide an `ArrayHandleBasic` of the same component +/// type as the underlying storage for this array. In this case, the array +/// will be accessed much in the same manner as `ArrayHandleGroupVec`. +/// +/// `ArrayHandleRuntimeVec` also allows you to convert the array to an +/// `ArrayHandleBasic` of the appropriate `Vec` type (or `component` type). +/// A runtime check will be performed to make sure the number of components +/// matches. +/// +template +class ArrayHandleRuntimeVec + : public vtkm::cont::ArrayHandle< + vtkm::VecFromPortal::WritePortalType>, + vtkm::cont::StorageTagRuntimeVec> +{ +public: + VTKM_ARRAY_HANDLE_SUBCLASS( + ArrayHandleRuntimeVec, + (ArrayHandleRuntimeVec), + (vtkm::cont::ArrayHandle< + vtkm::VecFromPortal::WritePortalType>, + vtkm::cont::StorageTagRuntimeVec>)); + +private: + using StorageType = vtkm::cont::internal::Storage; + using ComponentsArrayType = vtkm::cont::ArrayHandle; + +public: + VTKM_CONT + ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, + const ComponentsArrayType& componentsArray = ComponentsArrayType{}) + : Superclass(StorageType::CreateBuffers(numComponents, componentsArray)) + { + } + + VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const + { + return StorageType::GetNumberOfComponents(this->GetBuffers()); + } + + VTKM_CONT vtkm::cont::ArrayHandleBasic GetComponentsArray() const + { + return StorageType::GetComponentsArray(this->GetBuffers()); + } + + ///@{ + /// \brief Converts the array to that of a basic array handle. + /// + /// This method converts the `ArrayHandleRuntimeVec` to a simple `ArrayHandleBasic`. + /// This is useful if the `ArrayHandleRuntimeVec` is passed to a routine that works + /// on an array of a specific `Vec` size (or scalars). After a runtime check, the + /// array can be converted to a typical array and used as such. + template + void AsArrayHandleBasic(vtkm::cont::ArrayHandle& array) const + { + StorageType::AsArrayHandleBasic(this->GetBuffers(), array); + } + + template + ArrayType AsArrayHandleBasic() const + { + ArrayType array; + this->AsArrayHandleBasic(array); + return array; + } + ///@} +}; + +/// \c make_ArrayHandleRuntimeVec is convenience function to generate an +/// ArrayHandleRuntimeVec. It takes in an ArrayHandle of values and an +/// array handle of offsets and returns an array handle with consecutive +/// entries grouped in a Vec. +/// +template +VTKM_CONT vtkm::cont::ArrayHandleRuntimeVec make_ArrayHandleRuntimeVec( + vtkm::IdComponent numComponents, + const vtkm::cont::ArrayHandle& componentsArray) +{ + return vtkm::cont::ArrayHandleRuntimeVec(numComponents, componentsArray); +} + +namespace internal +{ + +template <> +struct ArrayExtractComponentImpl +{ + template + auto operator()(const vtkm::cont::ArrayHandle& src, + vtkm::IdComponent componentIndex, + vtkm::CopyFlag allowCopy) const + { + using ComponentType = typename T::ComponentType; + vtkm::cont::ArrayHandleRuntimeVec array{ src }; + constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat::NUM_COMPONENTS; + vtkm::cont::ArrayHandleStride::BaseComponentType> dest = + ArrayExtractComponentImpl{}( + array.GetComponentsArray(), componentIndex % NUM_SUB_COMPONENTS, allowCopy); + + // Adjust stride and offset to expectations of grouped values + const vtkm::IdComponent numComponents = array.GetNumberOfComponents(); + return vtkm::cont::ArrayHandleStride::BaseComponentType>( + dest.GetBasicArray(), + dest.GetNumberOfValues() / numComponents, + dest.GetStride() * numComponents, + dest.GetOffset() + (dest.GetStride() * (componentIndex / NUM_SUB_COMPONENTS)), + dest.GetModulo(), + dest.GetDivisor()); + } +}; + +} // namespace internal + +} +} // namespace vtkm::cont + +//============================================================================= +// Specializations of serialization related classes +/// @cond SERIALIZATION +namespace vtkm +{ +namespace cont +{ + +template +struct SerializableTypeString> +{ + static VTKM_CONT const std::string& Get() + { + static std::string name = "AH_RuntimeVec<" + SerializableTypeString::Get() + ">"; + return name; + } +}; + +template +struct SerializableTypeString> + : SerializableTypeString> +{ +}; + +} +} // vtkm::cont + +namespace mangled_diy_namespace +{ + +template +struct Serialization> +{ +private: + using Type = vtkm::cont::ArrayHandleRuntimeVec; + using BaseType = vtkm::cont::ArrayHandle; + +public: + static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj) + { + vtkmdiy::save(bb, Type(obj).GetNumberOfComponents()); + vtkmdiy::save(bb, Type(obj).GetComponentsArray()); + } + + static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) + { + vtkm::IdComponent numComponents; + vtkm::cont::ArrayHandleBasic componentArray; + + vtkmdiy::load(bb, numComponents); + vtkmdiy::load(bb, componentArray); + + obj = vtkm::cont::make_ArrayHandleRuntimeVec(numComponents, componentArray); + } +}; + +template +struct Serialization> + : Serialization> +{ +}; + +} // diy +/// @endcond SERIALIZATION + +#endif //vtk_m_cont_ArrayHandleRuntimeVec_h diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 761652c22..53b5308d5 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -38,6 +38,7 @@ set(headers ArrayHandleRandomStandardNormal.h ArrayHandleRandomUniformBits.h ArrayHandleRandomUniformReal.h + ArrayHandleRuntimeVec.h ArrayHandleSOA.h ArrayHandleStride.h ArrayHandleSwizzle.h diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index ef4c4e566..be6595f0c 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -85,6 +85,7 @@ set(unit_tests_device UnitTestArrayHandleRandomStandardNormal.cxx UnitTestArrayHandleRandomUniformReal.cxx UnitTestArrayHandleRecombineVec.cxx + UnitTestArrayHandleRuntimeVec.cxx UnitTestArrayHandleSOA.cxx UnitTestArrayHandleSwizzle.cxx UnitTestArrayHandleTransform.cxx diff --git a/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx b/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx index 9a9ccade4..07da16d8b 100644 --- a/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx +++ b/vtkm/cont/testing/UnitTestArrayExtractComponent.cxx @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -34,39 +35,79 @@ namespace constexpr vtkm::Id ARRAY_SIZE = 10; +template +vtkm::IdComponent GetTotalNumComponents(const T& vec) +{ + using VTraits = vtkm::VecTraits; + if (std::is_same::value) + { + return VTraits::GetNumberOfComponents(vec); + } + else + { + return VTraits::GetNumberOfComponents(vec) * + GetTotalNumComponents(VTraits::GetComponent(vec, 0)); + } +} + +// VecFlat.h has something similar, but it only works with static Vec sizes. It might make sense +// to move this somewhere else later +template +struct GetVecFlatIndexImpl +{ + template + VTKM_CONT BaseComponentType operator()(const VecType& vec, vtkm::IdComponent index) const + { + const vtkm::IdComponent subSize = GetTotalNumComponents(vec[0]); + return (*this)(vec[index / subSize], index % subSize); + } + + VTKM_CONT BaseComponentType operator()(const BaseComponentType& component, + vtkm::IdComponent index) const + { + VTKM_ASSERT(index == 0); + return component; + } +}; + +template +auto GetVecFlatIndex(const T& vec, vtkm::IdComponent index) +{ + return GetVecFlatIndexImpl::BaseComponentType>{}(vec, index); +} + template void CheckInputArray(const vtkm::cont::ArrayHandle& originalArray, vtkm::CopyFlag allowCopy = vtkm::CopyFlag::Off) { - using FlatVec = vtkm::VecFlat; - using ComponentType = typename FlatVec::ComponentType; - for (vtkm::IdComponent componentId = 0; componentId < FlatVec::NUM_COMPONENTS; ++componentId) + auto originalPortal = originalArray.ReadPortal(); + using ComponentType = typename vtkm::VecTraits::BaseComponentType; + const vtkm::IdComponent numComponents = GetTotalNumComponents(originalPortal.Get(0)); + for (vtkm::IdComponent componentId = 0; componentId < numComponents; ++componentId) { vtkm::cont::ArrayHandleStride componentArray = vtkm::cont::ArrayExtractComponent(originalArray, componentId, allowCopy); - auto originalPortal = originalArray.ReadPortal(); auto componentPortal = componentArray.ReadPortal(); VTKM_TEST_ASSERT(originalPortal.GetNumberOfValues() == componentPortal.GetNumberOfValues()); for (vtkm::Id arrayIndex = 0; arrayIndex < originalArray.GetNumberOfValues(); ++arrayIndex) { - auto originalValue = vtkm::make_VecFlat(originalPortal.Get(arrayIndex)); + auto originalValue = GetVecFlatIndex(originalPortal.Get(arrayIndex), componentId); ComponentType componentValue = componentPortal.Get(arrayIndex); - VTKM_TEST_ASSERT(test_equal(originalValue[componentId], componentValue)); + VTKM_TEST_ASSERT(test_equal(originalValue, componentValue)); } } } template -void CheckOutputArray(const vtkm::cont::ArrayHandle& originalArray) +void CheckOutputArray( + const vtkm::cont::ArrayHandle& originalArray, + const vtkm::cont::ArrayHandle& outputArray = vtkm::cont::ArrayHandle{}) { CheckInputArray(originalArray); - vtkm::cont::ArrayHandle outputArray; - - using FlatVec = vtkm::VecFlat; - using ComponentType = typename FlatVec::ComponentType; - constexpr vtkm::IdComponent numComponents = FlatVec::NUM_COMPONENTS; + using ComponentType = typename vtkm::VecTraits::BaseComponentType; + const vtkm::IdComponent numComponents = GetTotalNumComponents(originalArray.ReadPortal().Get(0)); // Extract all the stride arrays first, and then allocate them later. This tests to // to make sure that the independent allocation of all the extracted arrays are consistent @@ -105,11 +146,12 @@ void CheckOutputArray(const vtkm::cont::ArrayHandle& originalArray) auto outPortal = outputArray.ReadPortal(); for (vtkm::Id arrayIndex = 0; arrayIndex < originalArray.GetNumberOfValues(); ++arrayIndex) { - FlatVec inValue = vtkm::make_VecFlat(inPortal.Get(arrayIndex)); - FlatVec outValue = vtkm::make_VecFlat(outPortal.Get(arrayIndex)); + auto inValue = inPortal.Get(arrayIndex); + auto outValue = outPortal.Get(arrayIndex); for (vtkm::IdComponent componentId = 0; componentId < numComponents; ++componentId) { - VTKM_TEST_ASSERT(test_equal(inValue[componentId], outValue[numComponents - componentId - 1])); + VTKM_TEST_ASSERT(test_equal(GetVecFlatIndex(inValue, componentId), + GetVecFlatIndex(outValue, numComponents - componentId - 1))); } } } @@ -161,9 +203,10 @@ void DoTest() { std::cout << "ArrayHandleGroupVec" << std::endl; vtkm::cont::ArrayHandle array; - array.Allocate(ARRAY_SIZE * 2); + array.Allocate(ARRAY_SIZE * 4); SetPortal(array.WritePortal()); CheckOutputArray(vtkm::cont::make_ArrayHandleGroupVec<2>(array)); + CheckOutputArray(vtkm::cont::make_ArrayHandleGroupVec<4>(array)); } { @@ -185,6 +228,17 @@ void DoTest() CheckInputArray(vtkm::cont::make_ArrayHandleExtractComponent(compositeArray, 1)); } + { + std::cout << "ArrayHandleRuntimeVec" << std::endl; + vtkm::cont::ArrayHandle array; + array.Allocate(ARRAY_SIZE * 4); + SetPortal(array.WritePortal()); + CheckOutputArray(vtkm::cont::make_ArrayHandleRuntimeVec(2, array), + vtkm::cont::ArrayHandleRuntimeVec(2)); + CheckOutputArray(vtkm::cont::make_ArrayHandleRuntimeVec(4, array), + vtkm::cont::ArrayHandleRuntimeVec(4)); + } + { std::cout << "ArrayHandleCartesianProduct" << std::endl; vtkm::cont::ArrayHandle array0; diff --git a/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx b/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx new file mode 100644 index 000000000..a56a9d27a --- /dev/null +++ b/vtkm/cont/testing/UnitTestArrayHandleRuntimeVec.cxx @@ -0,0 +1,201 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include + +#include + +#include + +namespace +{ + +constexpr vtkm::Id ARRAY_SIZE = 10; + +struct UnusualType +{ + vtkm::Id X; +}; + +} // anonymous namespace + +namespace detail +{ + +template <> +struct TestValueImpl +{ + VTKM_EXEC_CONT UnusualType operator()(vtkm::Id index) const + { + return { TestValue(index, decltype(UnusualType::X){}) }; + } +}; + +template <> +struct TestEqualImpl +{ + VTKM_EXEC_CONT bool operator()(UnusualType value1, + UnusualType value2, + vtkm::Float64 tolerance) const + { + return test_equal(value1.X, value2.X, tolerance); + } +}; + +} // namespace detail + +namespace +{ + +struct PassThrough : vtkm::worklet::WorkletMapField +{ + using ControlSignature = void(FieldIn, FieldOut); + using ExecutionSignature = void(_1, _2); + + template + VTKM_EXEC void operator()(const InValue& inValue, OutValue& outValue) const + { + outValue = inValue; + } +}; + +template +struct TestRuntimeVecAsInput +{ + template + VTKM_CONT void operator()(ComponentType) const + { + using ValueType = vtkm::Vec; + + vtkm::cont::ArrayHandle baseArray; + baseArray.Allocate(ARRAY_SIZE * NUM_COMPONENTS); + SetPortal(baseArray.WritePortal()); + + vtkm::cont::ArrayHandleRuntimeVec runtimeVecArray(NUM_COMPONENTS, baseArray); + VTKM_TEST_ASSERT(runtimeVecArray.GetNumberOfValues() == ARRAY_SIZE, + "Group array reporting wrong array size."); + + vtkm::cont::ArrayHandle resultArray; + + vtkm::cont::Invoker{}(PassThrough{}, runtimeVecArray, resultArray); + + VTKM_TEST_ASSERT(resultArray.GetNumberOfValues() == ARRAY_SIZE, "Got bad result array size."); + + //verify that the control portal works + vtkm::Id totalIndex = 0; + auto resultPortal = resultArray.ReadPortal(); + for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index) + { + const ValueType result = resultPortal.Get(index); + for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; componentIndex++) + { + const ComponentType expectedValue = TestValue(totalIndex, ComponentType()); + VTKM_TEST_ASSERT(test_equal(result[componentIndex], expectedValue), + "Result array got wrong value."); + totalIndex++; + } + } + + //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::cont::ArrayHandle, NUM_COMPONENTS>> + nestedComponents; + runtimeVecArray.AsArrayHandleBasic(nestedComponents); + auto flatPortal = flatComponents.ReadPortal(); + auto nestedPortal = nestedComponents.ReadPortal(); + for (vtkm::Id index = 0; index < flatPortal.GetNumberOfValues(); ++index) + { + VTKM_TEST_ASSERT(test_equal(vtkm::make_VecFlat(flatPortal.Get(index)), + vtkm::make_VecFlat(nestedPortal.Get(index)))); + } + + runtimeVecArray.ReleaseResources(); + } +}; + +template +struct TestRuntimeVecAsOutput +{ + template + VTKM_CONT void operator()(ComponentType) const + { + using ValueType = vtkm::Vec; + + vtkm::cont::ArrayHandle baseArray; + baseArray.Allocate(ARRAY_SIZE); + SetPortal(baseArray.WritePortal()); + + vtkm::cont::ArrayHandle resultArray; + + vtkm::cont::ArrayHandleRuntimeVec runtimeVecArray(NUM_COMPONENTS, resultArray); + + vtkm::cont::Invoker{}(PassThrough{}, baseArray, runtimeVecArray); + + VTKM_TEST_ASSERT(runtimeVecArray.GetNumberOfValues() == ARRAY_SIZE, + "Group array reporting wrong array size."); + + VTKM_TEST_ASSERT(resultArray.GetNumberOfValues() == ARRAY_SIZE * NUM_COMPONENTS, + "Got bad result array size."); + + //verify that the control portal works + vtkm::Id totalIndex = 0; + auto resultPortal = resultArray.ReadPortal(); + for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index) + { + const ValueType expectedValue = TestValue(index, ValueType()); + for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; componentIndex++) + { + const ComponentType result = resultPortal.Get(totalIndex); + VTKM_TEST_ASSERT(test_equal(result, expectedValue[componentIndex]), + "Result array got wrong value."); + totalIndex++; + } + } + } +}; + +void Run() +{ + using HandleTypesToTest = + vtkm::List; + using ScalarTypesToTest = vtkm::List; + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayHandleRuntimeVec(3) as Input" << std::endl; + vtkm::testing::Testing::TryTypes(TestRuntimeVecAsInput<3>(), HandleTypesToTest()); + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayHandleRuntimeVec(4) as Input" << std::endl; + vtkm::testing::Testing::TryTypes(TestRuntimeVecAsInput<4>(), HandleTypesToTest()); + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayHandleRuntimeVec(2) as Output" << std::endl; + vtkm::testing::Testing::TryTypes(TestRuntimeVecAsOutput<2>(), ScalarTypesToTest()); + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayHandleRuntimeVec(3) as Output" << std::endl; + vtkm::testing::Testing::TryTypes(TestRuntimeVecAsOutput<3>(), ScalarTypesToTest()); + + std::cout << "-------------------------------------------" << std::endl; + std::cout << "Testing ArrayHandleRuntimeVec(3) as Input with unusual type" << std::endl; + TestRuntimeVecAsInput<3>{}(UnusualType{}); +} + +} // anonymous namespace + +int UnitTestArrayHandleRuntimeVec(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(Run, argc, argv); +} diff --git a/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx b/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx index 93afbf170..30bdcb04e 100644 --- a/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx +++ b/vtkm/cont/testing/UnitTestSerializationArrayHandle.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -303,6 +304,20 @@ struct TestArrayHandleGroupVecVariable } }; +struct TestArrayHandleRuntimeVec +{ + template + void operator()(T) const + { + auto numComps = RandomValue::Make(1, 5); + 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)); + } +}; + void TestArrayHandleIndex() { auto size = RandomValue::Make(2, 10); @@ -413,6 +428,9 @@ void TestArrayHandleSerialization() std::cout << "Testing ArrayHandleGroupVecVariable\n"; vtkm::testing::Testing::TryTypes(TestArrayHandleGroupVecVariable(), TestTypesList()); + std::cout << "Testing ArrayHandleRuntimeVec\n"; + vtkm::testing::Testing::TryTypes(TestArrayHandleRuntimeVec(), TestTypesList()); + std::cout << "Testing ArrayHandleIndex\n"; TestArrayHandleIndex();