//============================================================================ // 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_ArrayHandleCompositeVector_h #define vtk_m_ArrayHandleCompositeVector_h #include #include #include #include #include #include #include #include namespace vtkm { namespace cont { namespace internal { namespace compvec { // AllAreArrayHandles: --------------------------------------------------------- // Ensures that all types in ArrayHandlesT... are subclasses of ArrayHandleBase template struct AllAreArrayHandlesImpl; template struct AllAreArrayHandlesImpl { private: using Next = AllAreArrayHandlesImpl; constexpr static bool HeadValid = std::is_base_of::value; public: constexpr static bool Value = HeadValid && Next::Value; }; template struct AllAreArrayHandlesImpl { constexpr static bool Value = std::is_base_of::value; }; template struct AllAreArrayHandles { constexpr static bool Value = AllAreArrayHandlesImpl::Value; }; // GetValueType: --------------------------------------------------------------- // Determines the output `ValueType` of the set of `ArrayHandle` objects. For example, if the input // set contains 3 types with `vtkm::Float32` ValueTypes, then the ValueType defined here will be // `vtkm::Vec`. This also validates that all members have the same `ValueType`. template constexpr bool CheckValueType() { VTKM_STATIC_ASSERT_MSG((std::is_same::value), "ArrayHandleCompositeVector must be built from " "ArrayHandles with the same ValueTypes."); return std::is_same::value; } template struct GetValueType { static constexpr vtkm::IdComponent COUNT = static_cast(sizeof...(ArrayTypes)) + 1; using ComponentType = typename ArrayType0::ValueType; static constexpr std::array ValueCheck{ { true, CheckValueType()... } }; using ValueType = vtkm::Vec; }; // Special case for only one component template struct GetValueType { static constexpr vtkm::IdComponent COUNT = 1; using ComponentType = typename ArrayType::ValueType; using ValueType = typename ArrayType::ValueType; }; // ----------------------------------------------------------------------------- // Functors to access Storage methods. This is used with vtkm::Tuple's // ForEach and Transform methods. struct WritePortal { template typename ArrayHandle::WritePortalType operator()(const ArrayHandle& array) const { return array.WritePortal(); } }; struct ReadPortal { template typename ArrayHandle::ReadPortalType operator()(const ArrayHandle& array) const { return array.ReadPortal(); } }; struct Allocate { vtkm::Id NumValues; VTKM_CONT Allocate(vtkm::Id numValues) : NumValues(numValues) { } template VTKM_CONT void operator()(Array& array) { array.Allocate(this->NumValues); } }; struct Shrink { vtkm::Id NumValues; VTKM_CONT Shrink(vtkm::Id numValues) : NumValues(numValues) { } template VTKM_CONT void operator()(Array& array) { array.Shrink(this->NumValues); } }; struct ReleaseResources { template VTKM_CONT void operator()(Array& array) { array.ReleaseResources(); } }; // ----------------------------------------------------------------------------- // Functors to access ArrayTransfer methods. This is used with vtkm::Tuple's // ForEach and Transform methods. template struct PrepareForInput { vtkm::cont::Token& Token; VTKM_CONT PrepareForInput(vtkm::cont::Token& token) : Token(token) { } template VTKM_CONT typename Array::template ExecutionTypes::PortalConst operator()( const Array& array) { return array.PrepareForInput(Device{}, this->Token); } }; template struct PrepareForInPlace { vtkm::cont::Token& Token; VTKM_CONT PrepareForInPlace(vtkm::cont::Token& token) : Token(token) { } template VTKM_CONT typename Array::template ExecutionTypes::Portal operator()(Array& array) { return array.PrepareForInPlace(Device{}, this->Token); } }; template struct PrepareForOutput { vtkm::Id NumValues; vtkm::cont::Token& Token; VTKM_CONT PrepareForOutput(vtkm::Id numValues, vtkm::cont::Token& token) : NumValues(numValues) , Token(token) { } template VTKM_CONT typename Array::template ExecutionTypes::Portal operator()(Array& array) { return array.PrepareForOutput(this->NumValues, Device{}, this->Token); } }; struct ReleaseResourcesExecution { template VTKM_CONT void operator()(Array& array) { array.ReleaseResourcesExecution(); } }; // ArraySizeValidator: --------------------------------------------------------- // Call Exec(ArrayTuple, NumValues) to ensure that all arrays in the tuple have // the specified number of values. template struct ArraySizeValidatorImpl { using Next = ArraySizeValidatorImpl; VTKM_CONT static bool Exec(const TupleType& tuple, vtkm::Id numVals) { return vtkm::Get(tuple).GetNumberOfValues() == numVals && Next::Exec(tuple, numVals); } }; template struct ArraySizeValidatorImpl { VTKM_CONT static bool Exec(const TupleType&, vtkm::Id) { return true; } }; template struct ArraySizeValidator { VTKM_CONT static bool Exec(const TupleType& tuple, vtkm::Id numVals) { return ArraySizeValidatorImpl<0, vtkm::TupleSize::value, TupleType>::Exec(tuple, numVals); } }; template using AllPortalsAreWritable = typename brigand::all, brigand::bind>::type; // GetFromPortals: ------------------------------------------------------------- // Given a set of array portals as arguments, returns a Vec comprising the values // at the provided index. VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT typename GetValueType::ValueType GetFromPortals( vtkm::Id index, const Portals&... portals) { return { portals.Get(index)... }; } // SetToPortals: --------------------------------------------------------------- // Given a Vec-like object, and index, and a set of array portals, sets each of // the portals to the respective component of the Vec. VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void SetToPortalsImpl(vtkm::Id index, const ValueType& value, vtkmstd::integer_sequence, const Portals&... portals) { using Traits = vtkm::VecTraits; (void)std::initializer_list{ (portals.Set(index, Traits::GetComponent(value, I)), false)... }; } VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void SetToPortals(vtkm::Id index, const ValueType& value, const Portals&... portals) { SetToPortalsImpl( index, value, vtkmstd::make_integer_sequence{}, portals...); } } // end namespace compvec template class VTKM_ALWAYS_EXPORT ArrayPortalCompositeVector { using Writable = compvec::AllPortalsAreWritable; using TupleType = vtkm::Tuple; TupleType Portals; public: using ValueType = typename compvec::GetValueType::ValueType; VTKM_EXEC_CONT ArrayPortalCompositeVector() {} VTKM_CONT ArrayPortalCompositeVector(const PortalTypes&... portals) : Portals(portals...) { } VTKM_CONT ArrayPortalCompositeVector(const TupleType& portals) : Portals(portals) { } VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return vtkm::Get<0>(this->Portals).GetNumberOfValues(); } VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const { return this->Portals.Apply(compvec::GetFromPortals, index); } template ::type> VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const { this->Portals.Apply(compvec::SetToPortals, index, value); } }; } // namespace internal template struct VTKM_ALWAYS_EXPORT StorageTagCompositeVec { }; namespace internal { template struct CompositeVectorTraits { // Need to check this here, since this traits struct is used in the // ArrayHandleCompositeVector superclass definition before any other // static_asserts could be used. VTKM_STATIC_ASSERT_MSG(compvec::AllAreArrayHandles::Value, "Template parameters for ArrayHandleCompositeVector " "must be a list of ArrayHandle types."); using ValueType = typename compvec::GetValueType::ValueType; using StorageTag = vtkm::cont::StorageTagCompositeVec; using StorageType = Storage; using Superclass = ArrayHandle; }; template class Storage(sizeof...(StorageTags))>, vtkm::cont::StorageTagCompositeVec> { using ArrayTuple = vtkm::Tuple...>; ArrayTuple Arrays; bool Valid; public: using ValueType = vtkm::Vec(sizeof...(StorageTags))>; using PortalType = ArrayPortalCompositeVector< typename vtkm::cont::ArrayHandle::WritePortalType...>; using PortalConstType = ArrayPortalCompositeVector::ReadPortalType...>; VTKM_CONT Storage() : Valid(false) { } template VTKM_CONT Storage(const ArrayTypes&... arrays) : Arrays(arrays...) , Valid(true) { using SizeValidator = compvec::ArraySizeValidator; if (!SizeValidator::Exec(this->Arrays, this->GetNumberOfValues())) { throw ErrorBadValue("All arrays must have the same number of values."); } } VTKM_CONT PortalType GetPortal() { VTKM_ASSERT(this->Valid); return this->Arrays.Transform(compvec::WritePortal{}); } void TypeCheck(int) const; VTKM_CONT PortalConstType GetPortalConst() const { VTKM_ASSERT(this->Valid); this->Arrays.Transform(compvec::ReadPortal{}); return this->Arrays.Transform(compvec::ReadPortal{}); } VTKM_CONT vtkm::Id GetNumberOfValues() const { VTKM_ASSERT(this->Valid); return vtkm::Get<0>(this->Arrays).GetNumberOfValues(); } VTKM_CONT void Allocate(vtkm::Id numValues) { VTKM_ASSERT(this->Valid); this->Arrays.ForEach(compvec::Allocate{ numValues }); } VTKM_CONT void Shrink(vtkm::Id numValues) { VTKM_ASSERT(this->Valid); this->Arrays.ForEach(compvec::Shrink{ numValues }); } VTKM_CONT void ReleaseResources() { VTKM_ASSERT(this->Valid); this->Arrays.ForEach(compvec::ReleaseResources{}); } VTKM_CONT const ArrayTuple& GetArrayTuple() const { VTKM_ASSERT(this->Valid); return this->Arrays; } VTKM_CONT ArrayTuple& GetArrayTuple() { VTKM_ASSERT(this->Valid); return this->Arrays; } }; // Special case for single component. Just defer to the original storage. template class Storage> : public Storage { using ArrayType = vtkm::cont::ArrayHandle; using TupleType = vtkm::Tuple; public: Storage() = default; Storage(const ArrayType& array) : Storage(array.GetStorage()) { } VTKM_CONT const TupleType GetArrayTuple() const { return TupleType(ArrayType(this->GetStoragea())); } }; template class ArrayTransfer(sizeof...(StorageTags))>, vtkm::cont::StorageTagCompositeVec, DeviceTag> { VTKM_IS_DEVICE_ADAPTER_TAG(DeviceTag); using ArrayTuple = vtkm::Tuple...>; public: using ValueType = vtkm::Vec(sizeof...(StorageTags))>; private: using StorageTag = vtkm::cont::StorageTagCompositeVec; using StorageType = internal::Storage; StorageType* Storage; public: using PortalControl = typename StorageType::PortalType; using PortalConstControl = typename StorageType::PortalConstType; using PortalExecution = ArrayPortalCompositeVector:: template ExecutionTypes::Portal...>; using PortalConstExecution = ArrayPortalCompositeVector:: template ExecutionTypes::PortalConst...>; VTKM_CONT ArrayTransfer(StorageType* storage) : Storage(storage) { } VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->Storage->GetNumberOfValues(); } VTKM_CONT PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData), vtkm::cont::Token& token) const { return this->GetArrayTuple().Transform(compvec::PrepareForInput{ token }); } VTKM_CONT PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData), vtkm::cont::Token& token) { return this->GetArrayTuple().Transform(compvec::PrepareForInPlace{ token }); } VTKM_CONT PortalExecution PrepareForOutput(vtkm::Id numValues, vtkm::cont::Token& token) { return this->GetArrayTuple().Transform( compvec::PrepareForOutput{ numValues, token }); } VTKM_CONT void RetrieveOutputData(StorageType* vtkmNotUsed(storage)) const { // Implementation of this method should be unnecessary. The internal // array handle should automatically retrieve the output data as // necessary. } VTKM_CONT void Shrink(vtkm::Id numValues) { this->GetArrayTuple().ForEach(compvec::Shrink{ numValues }); } VTKM_CONT void ReleaseResources() { this->GetArrayTuple().ForEach(compvec::ReleaseResourcesExecution{}); } VTKM_CONT const ArrayTuple& GetArrayTuple() const { return this->Storage->GetArrayTuple(); } ArrayTuple& GetArrayTuple() { return this->Storage->GetArrayTuple(); } }; } // namespace internal /// \brief An \c ArrayHandle that combines components from other arrays. /// /// \c ArrayHandleCompositeVector is a specialization of \c ArrayHandle that /// derives its content from other arrays. It takes any number of /// single-component \c ArrayHandle objects and mimics an array that contains /// vectors with components that come from these delegate arrays. /// /// The easiest way to create and type an \c ArrayHandleCompositeVector is /// to use the \c make_ArrayHandleCompositeVector functions. /// /// The \c ArrayHandleExtractComponent class may be helpful when a desired /// component is part of an \c ArrayHandle with a \c vtkm::Vec \c ValueType. /// template class ArrayHandleCompositeVector : public ArrayHandle::ValueType, typename internal::CompositeVectorTraits::StorageTag> { private: using Traits = internal::CompositeVectorTraits; using TupleType = vtkm::Tuple; using StorageType = typename Traits::StorageType; public: VTKM_ARRAY_HANDLE_SUBCLASS(ArrayHandleCompositeVector, (ArrayHandleCompositeVector), (typename Traits::Superclass)); VTKM_CONT ArrayHandleCompositeVector(const ArrayTs&... arrays) : Superclass(StorageType(arrays...)) { } }; /// Create a composite vector array from other arrays. /// template VTKM_CONT ArrayHandleCompositeVector make_ArrayHandleCompositeVector( const ArrayTs&... arrays) { VTKM_STATIC_ASSERT_MSG(internal::compvec::AllAreArrayHandles::Value, "Arguments to make_ArrayHandleCompositeVector must be " "of ArrayHandle types."); return ArrayHandleCompositeVector(arrays...); } } } // 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_CompositeVector<" + internal::GetVariadicSerializableTypeString(AHs{}...) + ">"; return name; } }; template struct SerializableTypeString< vtkm::cont::ArrayHandle(sizeof...(STs))>, vtkm::cont::StorageTagCompositeVec>> : SerializableTypeString< vtkm::cont::ArrayHandleCompositeVector...>> { }; } } // vtkm::cont namespace mangled_diy_namespace { template struct Serialization> { private: using Type = typename vtkm::cont::ArrayHandleCompositeVector; using BaseType = vtkm::cont::ArrayHandle; struct SaveFunctor { BinaryBuffer& Buffer; SaveFunctor(BinaryBuffer& bb) : Buffer(bb) { } template void operator()(const AH& ah) const { vtkmdiy::save(this->Buffer, ah); } }; struct LoadFunctor { BinaryBuffer& Buffer; LoadFunctor(BinaryBuffer& bb) : Buffer(bb) { } template void operator()(AH& ah) const { vtkmdiy::load(this->Buffer, ah); } }; static BaseType Create(const AHs&... arrays) { return Type(arrays...); } public: static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj) { obj.GetStorage().GetArrayTuple().ForEach(SaveFunctor{ bb }); } static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) { vtkm::Tuple tuple; tuple.ForEach(LoadFunctor{ bb }); obj = tuple.Apply(Create); } }; template struct Serialization< vtkm::cont::ArrayHandle(sizeof...(STs))>, vtkm::cont::StorageTagCompositeVec>> : Serialization...>> { }; } // diy /// @endcond SERIALIZATION #endif //vtk_m_ArrayHandleCompositeVector_h