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/VecFlat.h b/vtkm/VecFlat.h index 01d2b5028..476241881 100644 --- a/vtkm/VecFlat.h +++ b/vtkm/VecFlat.h @@ -22,18 +22,20 @@ namespace internal { template ::HasMultipleComponents> + typename MultipleComponents = + typename vtkm::internal::SafeVecTraits::HasMultipleComponents> struct TotalNumComponents; template struct TotalNumComponents { VTKM_STATIC_ASSERT_MSG( - (std::is_same::IsSizeStatic, vtkm::VecTraitsTagSizeStatic>::value), + (std::is_same::IsSizeStatic, + vtkm::VecTraitsTagSizeStatic>::value), "vtkm::VecFlat can only be used with Vec types with a static number of components."); - using ComponentType = typename vtkm::VecTraits::ComponentType; + using ComponentType = typename vtkm::internal::SafeVecTraits::ComponentType; static constexpr vtkm::IdComponent value = - vtkm::VecTraits::NUM_COMPONENTS * TotalNumComponents::value; + vtkm::internal::SafeVecTraits::NUM_COMPONENTS * TotalNumComponents::value; }; template @@ -43,7 +45,7 @@ struct TotalNumComponents }; template -using FlattenVec = vtkm::Vec::BaseComponentType, +using FlattenVec = vtkm::Vec::BaseComponentType, vtkm::internal::TotalNumComponents::value>; template @@ -62,10 +64,10 @@ VTKM_EXEC_CONT T GetFlatVecComponentImpl(const T& component, } template -VTKM_EXEC_CONT typename vtkm::VecTraits::BaseComponentType +VTKM_EXEC_CONT typename vtkm::internal::SafeVecTraits::BaseComponentType GetFlatVecComponentImpl(const T& vec, vtkm::IdComponent index, std::false_type vtkmNotUsed(isBase)) { - using Traits = vtkm::VecTraits; + using Traits = vtkm::internal::SafeVecTraits; using ComponentType = typename Traits::ComponentType; using BaseComponentType = typename Traits::BaseComponentType; @@ -78,7 +80,7 @@ GetFlatVecComponentImpl(const T& vec, vtkm::IdComponent index, std::false_type v } // namespace detail template -VTKM_EXEC_CONT typename vtkm::VecTraits::BaseComponentType GetFlatVecComponent( +VTKM_EXEC_CONT typename vtkm::internal::SafeVecTraits::BaseComponentType GetFlatVecComponent( const T& vec, vtkm::IdComponent index) { @@ -112,7 +114,7 @@ VTKM_EXEC_CONT void CopyVecNestedToFlatImpl(const NestedVecType& nestedVec, vtkm::Vec& flatVec, vtkm::IdComponent flatOffset) { - using Traits = vtkm::VecTraits; + using Traits = vtkm::internal::SafeVecTraits; using ComponentType = typename Traits::ComponentType; constexpr vtkm::IdComponent subSize = TotalNumComponents::value; @@ -174,7 +176,7 @@ VTKM_EXEC_CONT void CopyVecFlatToNestedImpl(const vtkm::Vec& flatVec, vtkm::IdComponent flatOffset, NestedVecType& nestedVec) { - using Traits = vtkm::VecTraits; + using Traits = vtkm::internal::SafeVecTraits; using ComponentType = typename Traits::ComponentType; constexpr vtkm::IdComponent subSize = TotalNumComponents::value; diff --git a/vtkm/VecFromPortal.h b/vtkm/VecFromPortal.h index 9906c0e61..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; @@ -91,7 +117,8 @@ struct VecTraits> using VecType = vtkm::VecFromPortal; using ComponentType = typename VecType::ComponentType; - using BaseComponentType = typename vtkm::VecTraits::BaseComponentType; + using BaseComponentType = + typename vtkm::internal::SafeVecTraits::BaseComponentType; using HasMultipleComponents = vtkm::VecTraitsTagMultipleComponents; using IsSizeStatic = vtkm::VecTraitsTagSizeVariable; diff --git a/vtkm/VecTraits.h b/vtkm/VecTraits.h index 1d6cb1c45..440a4ced2 100644 --- a/vtkm/VecTraits.h +++ b/vtkm/VecTraits.h @@ -45,16 +45,20 @@ struct VecTraitsTagSizeVariable namespace internal { -template +// Forward declaration +template +struct SafeVecTraits; + +template struct VecTraitsMultipleComponentChooser { using Type = vtkm::VecTraitsTagMultipleComponents; }; -template <> -struct VecTraitsMultipleComponentChooser<1> +template +struct VecTraitsMultipleComponentChooser<1, ComponentType> { - using Type = vtkm::VecTraitsTagSingleComponent; + using Type = typename vtkm::internal::SafeVecTraits::HasMultipleComponents; }; } // namespace internal @@ -95,7 +99,7 @@ struct VTKM_NEVER_EXPORT VecTraits /// is really just a scalar. /// using HasMultipleComponents = - typename internal::VecTraitsMultipleComponentChooser::Type; + typename internal::VecTraitsMultipleComponentChooser::Type; /// \brief A tag specifying whether the size of this vector is known at compile time. /// @@ -197,7 +201,8 @@ template struct VecReplaceBaseComponentTypeGCC4or5 { using type = - vtkm::Vec::template ReplaceBaseComponentType, Size>; + vtkm::Vec::template ReplaceBaseComponentType, + Size>; }; } // namespace detail @@ -219,7 +224,8 @@ struct VTKM_NEVER_EXPORT VecTraits> /// Similar to ComponentType except that for nested vectors (e.g. Vec, N>), it /// returns the base scalar type at the end of the composition (T in this example). /// - using BaseComponentType = typename vtkm::VecTraits::BaseComponentType; + using BaseComponentType = + typename vtkm::internal::SafeVecTraits::BaseComponentType; /// Number of components in the vector. /// @@ -235,7 +241,7 @@ struct VTKM_NEVER_EXPORT VecTraits> /// when a vector is really just a scalar. /// using HasMultipleComponents = - typename internal::VecTraitsMultipleComponentChooser::Type; + typename internal::VecTraitsMultipleComponentChooser::Type; /// A tag specifying whether the size of this vector is known at compile /// time. If set to \c VecTraitsTagSizeStatic, then \c NUM_COMPONENTS is set. @@ -298,9 +304,10 @@ struct VTKM_NEVER_EXPORT VecTraits> typename detail::VecReplaceBaseComponentTypeGCC4or5::type; #else // !GCC <= 5 template - using ReplaceBaseComponentType = vtkm::Vec< - typename vtkm::VecTraits::template ReplaceBaseComponentType, - Size>; + using ReplaceBaseComponentType = + vtkm::Vec::template ReplaceBaseComponentType, + Size>; #endif ///@} diff --git a/vtkm/cont/ArrayHandleGroupVecVariable.h b/vtkm/cont/ArrayHandleGroupVecVariable.h index 1c28ab194..d7d512b09 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 @@ -237,13 +244,15 @@ public: /// it contains three values of Vec-like objects with the data [0,1,2,3], /// [4,5], and [6,7,8]. /// -/// Note that this version of \c ArrayHandle breaks some of the assumptions -/// about \c 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 \c -/// VecFromPortal it implement a Vec-like class that has a variable number of -/// values, and this type can change between control and execution -/// environments. +/// Note that caution should be used with `ArrayHandleRuntimeVec` because the +/// size of the `Vec` values is not known at compile time. Thus, the value +/// type of this array is forced to a special `VecFromPortal` class that can cause +/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS` +/// expression does not exist. Furthermore, new variables of type `VecFromPortal` +/// cannot be created. This means that simple operators like `+` will not work +/// because they require an intermediate object to be created. (Equal operators +/// like `+=` do work because they are given an existing variable to place the +/// output.) /// /// The offsets array is often derived from a list of sizes for each of the /// entries. You can use the convenience function \c diff --git a/vtkm/cont/ArrayHandleRecombineVec.h b/vtkm/cont/ArrayHandleRecombineVec.h index cb5f6bf80..f99303a95 100644 --- a/vtkm/cont/ArrayHandleRecombineVec.h +++ b/vtkm/cont/ArrayHandleRecombineVec.h @@ -15,8 +15,6 @@ #include #include -#include - #include namespace vtkm @@ -586,9 +584,13 @@ public: /// /// Note that caution should be used with `ArrayHandleRecombineVec` because the /// size of the `Vec` values is not known at compile time. Thus, the value -/// type of this array is forced to a `VecVariable`, which can cause surprises -/// if treated as a `Vec`. In particular, the static `NUM_COMPONENTS` expression -/// does not exist. +/// type of this array is forced to a special `RecombineVec` class that can cause +/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS` +/// expression does not exist. Furthermore, new variables of type `RecombineVec` +/// cannot be created. This means that simple operators like `+` will not work +/// because they require an intermediate object to be created. (Equal operators +/// like `+=` do work because they are given an existing variable to place the +/// output.) /// template class ArrayHandleRecombineVec diff --git a/vtkm/cont/ArrayHandleRuntimeVec.h b/vtkm/cont/ArrayHandleRuntimeVec.h new file mode 100644 index 000000000..67012d79d --- /dev/null +++ b/vtkm/cont/ArrayHandleRuntimeVec.h @@ -0,0 +1,456 @@ +//============================================================================ +// 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 caution should be used with `ArrayHandleRuntimeVec` because the +/// size of the `Vec` values is not known at compile time. Thus, the value +/// type of this array is forced to a special `VecFromPortal` class that can cause +/// surprises if treated as a `Vec`. In particular, the static `NUM_COMPONENTS` +/// expression does not exist. Furthermore, new variables of type `VecFromPortal` +/// cannot be created. This means that simple operators like `+` will not work +/// because they require an intermediate object to be created. (Equal operators +/// like `+=` do work because they are given an existing variable to place the +/// output.) +/// +/// 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 dd0bc072b..98ff167a1 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 8e770364d..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,53 +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) { - //std::cout << " Checking input array type " - // << vtkm::cont::TypeToString>() << std::endl; - - //std::cout << " Original array: "; - //vtkm::cont::printSummary_ArrayHandle(originalArray, std::cout); - - 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); - //std::cout << " Component " << componentId << ": "; - //vtkm::cont::printSummary_ArrayHandle(componentArray, std::cout); - 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); - //std::cout << " Checking output array type " - // << vtkm::cont::TypeToString>() << std::endl; - - //std::cout << " Original array: "; - //vtkm::cont::printSummary_ArrayHandle(originalArray, std::cout); - - 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 @@ -115,18 +142,16 @@ void CheckOutputArray(const vtkm::cont::ArrayHandle& originalArray) } } - //std::cout << " Output array: "; - //vtkm::cont::printSummary_ArrayHandle(outputArray, std::cout); - auto inPortal = originalArray.ReadPortal(); 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))); } } } @@ -178,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)); } { @@ -202,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();