//============================================================================ // 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 #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 ComponentPortalType::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; ComponentPortalType 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 ComponentPortalType& 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: VTKM_SUPPRESS_EXEC_WARNINGS 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)... }; } VTKM_SUPPRESS_EXEC_WARNINGS 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 { template class VTKM_ALWAYS_EXPORT Storage, vtkm::cont::StorageTagSOA> { using ValueType = vtkm::Vec; public: using ReadPortalType = vtkm::internal::ArrayPortalSOA>; using WritePortalType = vtkm::internal::ArrayPortalSOA>; VTKM_CONT constexpr static vtkm::IdComponent GetNumberOfBuffers() { return NUM_COMPONENTS; } VTKM_CONT static void ResizeBuffers(vtkm::Id numValues, vtkm::cont::internal::Buffer* buffers, vtkm::CopyFlag preserve, vtkm::cont::Token& token) { vtkm::BufferSizeType numBytes = vtkm::internal::NumberOfValuesToNumberOfBytes(numValues); for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { buffers[componentIndex].SetNumberOfBytes(numBytes, preserve, token); } } VTKM_CONT static vtkm::Id GetNumberOfValues(const vtkm::cont::internal::Buffer* buffers) { // Assume all buffers are the same size. return static_cast(buffers[0].GetNumberOfBytes()) / static_cast(sizeof(ComponentType)); } VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { vtkm::Id numValues = GetNumberOfValues(buffers); ReadPortalType portal(numValues); for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { VTKM_ASSERT(buffers[0].GetNumberOfBytes() == buffers[componentIndex].GetNumberOfBytes()); portal.SetPortal(componentIndex, vtkm::internal::ArrayPortalBasicRead( reinterpret_cast( buffers[componentIndex].ReadPointerDevice(device, token)), numValues)); } return portal; } VTKM_CONT static WritePortalType CreateWritePortal(vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { vtkm::Id numValues = GetNumberOfValues(buffers); WritePortalType portal(numValues); for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { VTKM_ASSERT(buffers[0].GetNumberOfBytes() == buffers[componentIndex].GetNumberOfBytes()); portal.SetPortal(componentIndex, vtkm::internal::ArrayPortalBasicWrite( reinterpret_cast( buffers[componentIndex].WritePointerDevice(device, token)), numValues)); } return portal; } }; } // namespace internal template VTKM_ARRAY_HANDLE_NEW_STYLE(T, vtkm::cont::StorageTagSOA); /// \brief An `ArrayHandle` that for Vecs stores each component in a separate physical array. /// /// `ArrayHandleSOA` behaves like a regular `ArrayHandle` (with a basic storage) except that /// if you specify a `ValueType` of a `Vec` or a `Vec-like`, it will actually store each /// component in a separate physical array. When data are retrieved from the array, they are /// reconstructed into `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 `Vec`) or a "structure of arrays" where /// you have an array of a basic type (like `float`) for each component of the data being /// represented. The `ArrayHandleSOA` makes it easy to cover this second case without creating /// special types. /// /// `ArrayHandleSOA` can be constructed from a collection of `ArrayHandle` with basic storage. /// This allows you to construct `Vec` arrays from components without deep copies. /// template class ArrayHandleSOA : public ArrayHandle { using ComponentType = typename vtkm::VecTraits::ComponentType; static constexpr vtkm::IdComponent NUM_COMPONENTS = vtkm::VecTraits::NUM_COMPONENTS; using StorageType = vtkm::cont::internal::Storage; using ComponentArrayType = vtkm::cont::ArrayHandle; public: VTKM_ARRAY_HANDLE_SUBCLASS(ArrayHandleSOA, (ArrayHandleSOA), (ArrayHandle)); ArrayHandleSOA(std::initializer_list&& componentBuffers) : Superclass(std::move(componentBuffers)) { } ArrayHandleSOA(const std::array& componentArrays) { for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->SetArray(componentIndex, componentArrays[componentIndex]); } } ArrayHandleSOA(const std::vector& componentArrays) { VTKM_ASSERT(componentArrays.size() == NUM_COMPONENTS); for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { this->SetArray(componentIndex, componentArrays[componentIndex]); } } ArrayHandleSOA(std::initializer_list&& componentArrays) { VTKM_ASSERT(componentArrays.size() == NUM_COMPONENTS); vtkm::IdComponent componentIndex = 0; for (auto&& array : componentArrays) { this->SetArray(componentIndex, array); ++componentIndex; } } ArrayHandleSOA(std::initializer_list>&& componentVectors) { VTKM_ASSERT(componentVectors.size() == NUM_COMPONENTS); vtkm::IdComponent componentIndex = 0; for (auto&& vector : componentVectors) { // 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(vector, 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(std::vector{ vtkm::cont::make_ArrayHandle(vector0, copy).GetBuffers()[0], vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy) .GetBuffers()[0]... }) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == 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(std::vector{ vtkm::cont::make_ArrayHandle(std::move(vector0), copy), vtkm::cont::make_ArrayHandle(std::forward(componentVectors), copy) .GetBuffers()[0]... }) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == 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(std::vector{ vtkm::cont::make_ArrayHandle(vector0, vtkm::CopyFlag::Off).GetBuffers()[0], vtkm::cont::make_ArrayHandle(std::forward(componentVectors), vtkm::CopyFlag::Off) .GetBuffers()[0]... }) { VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == NUM_COMPONENTS); } ArrayHandleSOA(std::initializer_list componentArrays, vtkm::Id length, vtkm::CopyFlag copy) { VTKM_ASSERT(componentArrays.size() == 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() == 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(std::vector{ vtkm::cont::make_ArrayHandle(array0, length, copy).GetBuffers()[0], vtkm::cont::make_ArrayHandle(componentArrays, length, copy).GetBuffers()[0]... }) { VTKM_STATIC_ASSERT(sizeof...(RemainingArrays) + 1 == 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(std::vector{ vtkm::cont::make_ArrayHandle(array0, length, vtkm::CopyFlag::Off).GetBuffers()[0], vtkm::cont::make_ArrayHandle(componentArrays, length, vtkm::CopyFlag::Off) .GetBuffers()[0]... }) { VTKM_STATIC_ASSERT(sizeof...(RemainingArrays) + 1 == NUM_COMPONENTS); } VTKM_CONT vtkm::cont::ArrayHandleBasic GetArray(vtkm::IdComponent index) const { return ComponentArrayType(&this->GetBuffers()[index]); } VTKM_CONT void SetArray(vtkm::IdComponent index, const ComponentArrayType& array) { this->SetBuffer(index, array.GetBuffers()[0]); } }; 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 internal { template <> struct ArrayExtractComponentImpl { template auto operator()(const vtkm::cont::ArrayHandle& src, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy) const -> decltype( ArrayExtractComponentImpl{}(vtkm::cont::ArrayHandleBasic{}, componentIndex, allowCopy)) { using FirstLevelComponentType = typename vtkm::VecTraits::ComponentType; vtkm::cont::ArrayHandleSOA array(src); constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat::NUM_COMPONENTS; return ArrayExtractComponentImpl{}( array.GetArray(componentIndex / NUM_SUB_COMPONENTS), componentIndex % NUM_SUB_COMPONENTS, allowCopy); } }; } // 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_SOA<" + SerializableTypeString::Get() + ">"; return name; } }; template struct SerializableTypeString> : SerializableTypeString> { }; } } // namespace vtkm::cont namespace mangled_diy_namespace { template struct Serialization> { using BaseType = vtkm::cont::ArrayHandle; static constexpr vtkm::IdComponent NUM_COMPONENTS = vtkm::VecTraits::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.GetBuffers()[componentIndex]); } } static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) { std::vector buffers(NUM_COMPONENTS); for (std::size_t componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) { vtkmdiy::load(bb, buffers[componentIndex]); } obj = BaseType(buffers); } }; 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, 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