//============================================================================ // 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_ArrayHandleSOA_h #define vtk_m_cont_ArrayHandleSOA_h #include #include #include #include #include #include #include #include namespace vtkm { namespace internal { /// \brief An array portal that combines indices from multiple sources. /// /// This will only work if \c VecTraits is defined for the type. /// template class ArrayPortalSOA { public: using ValueType = ValueType_; private: using ComponentType = typename SourcePortalType::ValueType; VTKM_STATIC_ASSERT(vtkm::HasVecTraits::value); using VTraits = vtkm::VecTraits; VTKM_STATIC_ASSERT((std::is_same::value)); static constexpr vtkm::IdComponent NUM_COMPONENTS = VTraits::NUM_COMPONENTS; SourcePortalType Portals[NUM_COMPONENTS]; vtkm::Id NumberOfValues; public: VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT explicit ArrayPortalSOA(vtkm::Id numValues = 0) : NumberOfValues(numValues) { } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT void SetPortal(vtkm::IdComponent index, const SourcePortalType& portal) { this->Portals[index] = portal; } VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return this->NumberOfValues; } template ::type, typename = typename std::enable_if::type> VTKM_EXEC_CONT ValueType Get(vtkm::Id valueIndex) const { return this->Get(valueIndex, vtkmstd::make_index_sequence()); } template ::type, typename = typename std::enable_if::type> VTKM_EXEC_CONT void Set(vtkm::Id valueIndex, const ValueType& value) const { this->Set(valueIndex, value, vtkmstd::make_index_sequence()); } private: template VTKM_EXEC_CONT ComponentType GetComponent(vtkm::Id valueIndex) const { return this->Portals[I].Get(valueIndex); } template VTKM_EXEC_CONT ValueType Get(vtkm::Id valueIndex, vtkmstd::index_sequence) const { return ValueType{ this->GetComponent(valueIndex)... }; } template VTKM_EXEC_CONT bool SetComponent(vtkm::Id valueIndex, const ValueType& value) const { this->Portals[I].Set(valueIndex, VTraits::GetComponent(value, static_cast(I))); return true; } template VTKM_EXEC_CONT void Set(vtkm::Id valueIndex, const ValueType& value, vtkmstd::index_sequence) const { // Is there a better way to unpack an expression and execute them with no other side effects? (void)std::initializer_list{ this->SetComponent(valueIndex, value)... }; } }; } // namespace internal namespace cont { struct VTKM_ALWAYS_EXPORT StorageTagSOA { }; namespace internal { namespace detail { template struct SOAPortalChooser; template struct SOAPortalChooser { using Type = vtkm::internal::ArrayPortalSOA; }; template struct SOAPortalChooser { using Type = PortalType; }; template ReturnType MakeSOAPortal(std::array, NUM_COMPONENTS> arrays, vtkm::Id numValues, const PortalMaker& portalMaker) { ReturnType portal(numValues); for (std::size_t componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { portal.SetPortal(static_cast(componentIndex), portalMaker(arrays[componentIndex])); VTKM_ASSERT(arrays[componentIndex].GetNumberOfValues() == numValues); } return portal; } template ReturnType MakeSOAPortal( std::array, 1> arrays, vtkm::Id vtkmNotUsed(numValues), const PortalMaker& portalMaker) { return portalMaker(arrays[0]); } } // namespace detail template struct ArrayHandleSOATraits { using VTraits = vtkm::VecTraits; using ComponentType = typename VTraits::ComponentType; using BaseArrayType = vtkm::cont::ArrayHandle; static constexpr vtkm::IdComponent NUM_COMPONENTS = VTraits::NUM_COMPONENTS; VTKM_STATIC_ASSERT_MSG(NUM_COMPONENTS > 0, "ArrayHandleSOA requires a type with at least 1 component."); using IsTrueVec = std::integral_constant 1)>; using PortalControl = typename detail::SOAPortalChooser::Type; using PortalConstControl = typename detail::SOAPortalChooser::Type; template using PortalExecution = typename detail::SOAPortalChooser< ValueType, typename BaseArrayType::template ExecutionTypes::Portal, IsTrueVec>::Type; template using PortalConstExecution = typename detail::SOAPortalChooser< ValueType, typename BaseArrayType::template ExecutionTypes::PortalConst, IsTrueVec>::Type; }; template class Storage { using Traits = ArrayHandleSOATraits; static constexpr vtkm::IdComponent NUM_COMPONENTS = Traits::NUM_COMPONENTS; using BaseArrayType = typename Traits::BaseArrayType; std::array Arrays; VTKM_CONT bool IsValidImpl(std::true_type) const { vtkm::Id size = this->Arrays[0].GetNumberOfValues(); for (vtkm::IdComponent componentIndex = 1; componentIndex < NUM_COMPONENTS; ++componentIndex) { if (this->GetArray(componentIndex).GetNumberOfValues() != size) { return false; } } return true; } VTKM_CONT constexpr bool IsValidImpl(std::false_type) const { return true; } public: using ValueType = ValueType_; using PortalType = typename Traits::PortalControl; using PortalConstType = typename Traits::PortalConstControl; VTKM_CONT bool IsValid() const { return this->IsValidImpl(typename Traits::IsTrueVec{}); } Storage() = default; VTKM_CONT Storage(std::array&& arrays) : Arrays(std::move(arrays)) { VTKM_ASSERT(IsValid()); } // For this constructor to work, all types have to be // vtkm::cont::ArrayHandle template VTKM_CONT Storage(const BaseArrayType& array0, const ArrayTypes&... arrays) : Arrays{ { array0, arrays... } } { VTKM_ASSERT(IsValid()); } VTKM_CONT BaseArrayType& GetArray(vtkm::IdComponent index) { return this->Arrays[static_cast(index)]; } VTKM_CONT const BaseArrayType& GetArray(vtkm::IdComponent index) const { return this->Arrays[static_cast(index)]; } VTKM_CONT std::array& GetArrays() { return this->Arrays; } VTKM_CONT const std::array& GetArrays() const { return this->Arrays; } VTKM_CONT void SetArray(vtkm::IdComponent index, const BaseArrayType& array) { this->Arrays[static_cast(index)] = array; } VTKM_CONT vtkm::Id GetNumberOfValues() const { VTKM_ASSERT(IsValid()); return this->GetArray(0).GetNumberOfValues(); } VTKM_CONT PortalType GetPortal() { VTKM_ASSERT(this->IsValid()); return detail::MakeSOAPortal( this->Arrays, this->GetNumberOfValues(), [](BaseArrayType& array) { return array.WritePortal(); }); } VTKM_CONT PortalConstType GetPortalConst() const { VTKM_ASSERT(this->IsValid()); return detail::MakeSOAPortal( this->Arrays, this->GetNumberOfValues(), [](const BaseArrayType& array) { return array.ReadPortal(); }); } VTKM_CONT void Allocate(vtkm::Id numValues) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->GetArray(componentIndex).Allocate(numValues); } } VTKM_CONT void Shrink(vtkm::Id numValues) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->GetArray(componentIndex).Shrink(numValues); } } VTKM_CONT void ReleaseResources() { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->GetArray(componentIndex).ReleaseResources(); } } }; template class ArrayTransfer { VTKM_IS_DEVICE_ADAPTER_TAG(Device); using StorageType = vtkm::cont::internal::Storage; using Traits = ArrayHandleSOATraits; using BaseArrayType = typename Traits::BaseArrayType; static constexpr vtkm::IdComponent NUM_COMPONENTS = Traits::NUM_COMPONENTS; StorageType* Storage; public: using ValueType = ValueType_; using PortalControl = typename StorageType::PortalType; using PortalConstControl = typename StorageType::PortalConstType; using PortalExecution = typename Traits::template PortalExecution; using PortalConstExecution = typename Traits::template PortalConstExecution; 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 detail::MakeSOAPortal( this->Storage->GetArrays(), this->GetNumberOfValues(), [&token](const BaseArrayType& array) { return array.PrepareForInput(Device{}, token); }); } VTKM_CONT PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData), vtkm::cont::Token& token) const { return detail::MakeSOAPortal( this->Storage->GetArrays(), this->GetNumberOfValues(), [&token](BaseArrayType& array) { return array.PrepareForInPlace(Device{}, token); }); } VTKM_CONT PortalExecution PrepareForOutput(vtkm::Id numValues, vtkm::cont::Token& token) const { return detail::MakeSOAPortal( this->Storage->GetArrays(), numValues, [numValues, &token](BaseArrayType& array) { return array.PrepareForOutput(numValues, Device{}, 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) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->Storage->GetArray(componentIndex).Shrink(numValues); } } VTKM_CONT void ReleaseResources() { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->Storage->GetArray(componentIndex).ReleaseResourcesExecution(); } } }; } // namespace internal /// \brief An \c ArrayHandle that for Vecs stores each component in a separate physical array. /// /// \c ArrayHandleSOA behaves like a regular \c ArrayHandle (with a basic storage) except that /// if you specify a \c ValueType of a \c Vec or a \c Vec-like, it will actually store each /// component in a separate physical array. When data are retrieved from the array, they are /// reconstructed into \c Vec objects as expected. /// /// The intention of this array type is to help cover the most common ways data is lain out in /// memory. Typically, arrays of data are either an "array of structures" like the basic storage /// where you have a single array of structures (like \c Vec) or a "structure of arrays" where /// you have an array of a basic type (like \c float) for each component of the data being /// represented. The\c ArrayHandleSOA makes it easy to cover this second case without creating /// special types. /// /// \c ArrayHandleSOA can be constructed from a collection of \c ArrayHandle with basic storage. /// This allows you to construct \c Vec arrays from components without deep copies. /// template class ArrayHandleSOA : public ArrayHandle { using Traits = vtkm::cont::internal::ArrayHandleSOATraits; using ComponentType = typename Traits::ComponentType; using BaseArrayType = typename Traits::BaseArrayType; using StorageType = vtkm::cont::internal::Storage; public: VTKM_ARRAY_HANDLE_SUBCLASS(ArrayHandleSOA, (ArrayHandleSOA), (ArrayHandle)); ArrayHandleSOA(std::array&& componentArrays) : Superclass(StorageType(std::move(componentArrays))) { } ArrayHandleSOA(std::initializer_list&& componentArrays) { VTKM_ASSERT(componentArrays.size() == Traits::NUM_COMPONENTS); std::copy( componentArrays.begin(), componentArrays.end(), this->GetStorage().GetArrays().begin()); } ArrayHandleSOA(std::initializer_list>&& componentVectors) { VTKM_ASSERT(componentVectors.size() == Traits::NUM_COMPONENTS); vtkm::IdComponent componentIndex = 0; for (auto&& vectorIter = componentVectors.begin(); vectorIter != componentVectors.end(); ++vectorIter) { // Note, std::vectors that come from std::initializer_list must be copied because the scope // of the objects in the initializer list disappears. this->SetArray(componentIndex, vtkm::cont::make_ArrayHandle(*vectorIter, vtkm::CopyFlag::On)); ++componentIndex; } } // This only works if all the templated arguments are of type std::vector. template ArrayHandleSOA(vtkm::CopyFlag copy, const std::vector& vector0, RemainingVectors&&... componentVectors) : Superclass(StorageType( vtkm::cont::make_ArrayHandle(vector0, copy), vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy)...)) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == Traits::NUM_COMPONENTS); } // This only works if all the templated arguments are of type std::vector. template ArrayHandleSOA(vtkm::CopyFlag copy, std::vector&& vector0, RemainingVectors&&... componentVectors) : Superclass(StorageType( vtkm::cont::make_ArrayHandle(std::move(vector0), copy), vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy)...)) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == Traits::NUM_COMPONENTS); } // This only works if all the templated arguments are of type std::vector. template #ifndef VTKM_MSVC // For some reason, having VTKM_DEPRECATED here is causing a syntax error in some MSVC // compilers. VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") #endif ArrayHandleSOA(const std::vector& vector0, const RemainingVectors&... componentVectors) : Superclass( StorageType(vtkm::cont::make_ArrayHandle(vector0, vtkm::CopyFlag::Off), vtkm::cont::make_ArrayHandle(componentVectors, vtkm::CopyFlag::Off)...)) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == Traits::NUM_COMPONENTS); } ArrayHandleSOA(std::initializer_list componentArrays, vtkm::Id length, vtkm::CopyFlag copy) { VTKM_ASSERT(componentArrays.size() == Traits::NUM_COMPONENTS); vtkm::IdComponent componentIndex = 0; for (auto&& vectorIter = componentArrays.begin(); vectorIter != componentArrays.end(); ++vectorIter) { this->SetArray(componentIndex, vtkm::cont::make_ArrayHandle(*vectorIter, length, copy)); ++componentIndex; } } VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") ArrayHandleSOA(std::initializer_list componentArrays, vtkm::Id length) { VTKM_ASSERT(componentArrays.size() == Traits::NUM_COMPONENTS); vtkm::IdComponent componentIndex = 0; for (auto&& vectorIter = componentArrays.begin(); vectorIter != componentArrays.end(); ++vectorIter) { this->SetArray(componentIndex, vtkm::cont::make_ArrayHandle(*vectorIter, length, vtkm::CopyFlag::Off)); ++componentIndex; } } // This only works if all the templated arguments are of type std::vector. template ArrayHandleSOA(vtkm::Id length, vtkm::CopyFlag copy, const ComponentType* array0, const RemainingArrays&... componentArrays) : Superclass(StorageType(vtkm::cont::make_ArrayHandle(array0, length, copy), vtkm::cont::make_ArrayHandle(componentArrays, length, copy)...)) { VTKM_STATIC_ASSERT(sizeof...(RemainingArrays) + 1 == Traits::NUM_COMPONENTS); } // This only works if all the templated arguments are of type std::vector. template #ifndef VTKM_MSVC // For some reason, having VTKM_DEPRECATED here is causing a syntax error in some MSVC // compilers. VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") #endif ArrayHandleSOA(vtkm::Id length, const ComponentType* array0, const RemainingArrays&... componentArrays) : Superclass( StorageType(vtkm::cont::make_ArrayHandle(array0, length, vtkm::CopyFlag::Off), vtkm::cont::make_ArrayHandle(componentArrays, length, vtkm::CopyFlag::Off)...)) { VTKM_STATIC_ASSERT(sizeof...(RemainingArrays) + 1 == Traits::NUM_COMPONENTS); } VTKM_CONT BaseArrayType& GetArray(vtkm::IdComponent index) { return this->GetStorage().GetArray(index); } VTKM_CONT const BaseArrayType& GetArray(vtkm::IdComponent index) const { return this->GetStorage().GetArray(index); } VTKM_CONT void SetArray(vtkm::IdComponent index, const BaseArrayType& array) { this->GetStorage().SetArray(index, array); } }; template VTKM_CONT ArrayHandleSOA make_ArrayHandleSOA( std::initializer_list::ComponentType, vtkm::cont::StorageTagBasic>>&& componentArrays) { return ArrayHandleSOA(std::move(componentArrays)); } template VTKM_CONT ArrayHandleSOA> make_ArrayHandleSOA( const vtkm::cont::ArrayHandle& componentArray0, const RemainingArrays&... componentArrays) { return { componentArray0, componentArrays... }; } template VTKM_CONT ArrayHandleSOA make_ArrayHandleSOA( std::initializer_list::ComponentType>>&& componentVectors) { return ArrayHandleSOA(std::move(componentVectors)); } // This only works if all the templated arguments are of type std::vector. template VTKM_CONT ArrayHandleSOA> make_ArrayHandleSOA(vtkm::CopyFlag copy, const std::vector& vector0, RemainingVectors&&... componentVectors) { // Convert std::vector to ArrayHandle first so that it correctly handles a mix of rvalue args. return { vtkm::cont::make_ArrayHandle(vector0, copy), vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy)... }; } // This only works if all the templated arguments are of type std::vector. template VTKM_CONT ArrayHandleSOA> make_ArrayHandleSOA(vtkm::CopyFlag copy, std::vector&& vector0, RemainingVectors&&... componentVectors) { // Convert std::vector to ArrayHandle first so that it correctly handles a mix of rvalue args. return ArrayHandleSOA< vtkm::Vec>( vtkm::cont::make_ArrayHandle(std::move(vector0), copy), vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy)...); } template VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") VTKM_CONT ArrayHandleSOA< vtkm::Vec> make_ArrayHandleSOA(const std::vector& vector0, const RemainingVectors&... componentVectors) { return ArrayHandleSOA< vtkm::Vec>( vector0, componentVectors...); } // This only works if all the templated arguments are rvalues of std::vector. template VTKM_CONT ArrayHandleSOA> make_ArrayHandleSOAMove(std::vector&& vector0, RemainingVectors&&... componentVectors) { return { vtkm::cont::make_ArrayHandleMove(std::move(vector0)), vtkm::cont::make_ArrayHandleMove(std::forward(componentVectors))... }; } template VTKM_CONT ArrayHandleSOA make_ArrayHandleSOA( std::initializer_list::ComponentType*>&& componentVectors, vtkm::Id length, vtkm::CopyFlag copy) { return ArrayHandleSOA(std::move(componentVectors), length, copy); } template VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") VTKM_CONT ArrayHandleSOA make_ArrayHandleSOA( std::initializer_list::ComponentType*>&& componentVectors, vtkm::Id length) { return vtkm::cont::make_ArrayHandleSOA(std::move(componentVectors), length, vtkm::CopyFlag::Off); } // This only works if all the templated arguments are of type std::vector. template VTKM_CONT ArrayHandleSOA> make_ArrayHandleSOA(vtkm::Id length, vtkm::CopyFlag copy, const ComponentType* array0, const RemainingArrays*... componentArrays) { return ArrayHandleSOA< vtkm::Vec>( length, copy, array0, componentArrays...); } template VTKM_DEPRECATED(1.6, "Specify a vtkm::CopyFlag or use a move version of make_ArrayHandle.") VTKM_CONT ArrayHandleSOA< vtkm::Vec> make_ArrayHandleSOA(vtkm::Id length, const ComponentType* array0, const RemainingArrays*... componentArrays) { return ArrayHandleSOA< vtkm::Vec>( length, array0, componentArrays...); } } } // 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_SOA<" + SerializableTypeString::Get() + ">"; return name; } }; template struct SerializableTypeString> : SerializableTypeString> { }; } } // namespace vtkm::cont namespace mangled_diy_namespace { template struct Serialization> { using BaseType = vtkm::cont::ArrayHandle; using Traits = vtkm::cont::internal::ArrayHandleSOATraits; static constexpr vtkm::IdComponent NUM_COMPONENTS = Traits::NUM_COMPONENTS; static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { vtkmdiy::save(bb, obj.GetStorage().GetArray(componentIndex)); } } static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { typename Traits::BaseArrayType componentArray; vtkmdiy::load(bb, componentArray); obj.GetStorage().SetArray(componentIndex, componentArray); } } }; template struct Serialization> : Serialization> { }; } // namespace mangled_diy_namespace // @endcond SERIALIZATION //============================================================================= // Precompiled instances #ifndef vtkm_cont_ArrayHandleSOA_cxx namespace vtkm { namespace cont { #define VTKM_ARRAYHANDLE_SOA_EXPORT(Type) \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle; \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle, StorageTagSOA>; \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle, StorageTagSOA>; \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle, StorageTagSOA>; VTKM_ARRAYHANDLE_SOA_EXPORT(char) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Int8) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::UInt8) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Int16) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::UInt16) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Int32) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::UInt32) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Int64) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::UInt64) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Float32) VTKM_ARRAYHANDLE_SOA_EXPORT(vtkm::Float64) #undef VTKM_ARRAYHANDLE_SOA_EXPORT } } // namespace vtkm::cont #endif // !vtkm_cont_ArrayHandleSOA_cxx #endif //vtk_m_cont_ArrayHandleSOA_h