//============================================================================ // 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_ArrayHandleCartesianProduct_h #define vtk_m_cont_ArrayHandleCartesianProduct_h #include #include #include #include #include #include #include #include namespace vtkm { namespace internal { /// \brief An array portal that acts as a 3D cartesian product of 3 arrays. /// template class VTKM_ALWAYS_EXPORT ArrayPortalCartesianProduct { public: using ValueType = ValueType_; using IteratorType = ValueType_; using PortalTypeFirst = PortalTypeFirst_; using PortalTypeSecond = PortalTypeSecond_; using PortalTypeThird = PortalTypeThird_; using set_supported_p1 = vtkm::internal::PortalSupportsSets; using set_supported_p2 = vtkm::internal::PortalSupportsSets; using set_supported_p3 = vtkm::internal::PortalSupportsSets; using Writable = std::integral_constant; VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT ArrayPortalCartesianProduct() : PortalFirst() , PortalSecond() , PortalThird() { } //needs to be host and device so that cuda can create lvalue of these VTKM_CONT ArrayPortalCartesianProduct(const PortalTypeFirst& portalfirst, const PortalTypeSecond& portalsecond, const PortalTypeThird& portalthird) : PortalFirst(portalfirst) , PortalSecond(portalsecond) , PortalThird(portalthird) { } /// Copy constructor for any other ArrayPortalCartesianProduct with an iterator /// type that can be copied to this iterator type. This allows us to do any /// type casting that the iterators do (like the non-const to const cast). /// template VTKM_CONT ArrayPortalCartesianProduct( const ArrayPortalCartesianProduct& src) : PortalFirst(src.GetPortalFirst()) , PortalSecond(src.GetPortalSecond()) , PortalThird(src.GetPortalThird()) { } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return this->PortalFirst.GetNumberOfValues() * this->PortalSecond.GetNumberOfValues() * this->PortalThird.GetNumberOfValues(); } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const { VTKM_ASSERT(index >= 0); VTKM_ASSERT(index < this->GetNumberOfValues()); vtkm::Id dim1 = this->PortalFirst.GetNumberOfValues(); vtkm::Id dim2 = this->PortalSecond.GetNumberOfValues(); vtkm::Id dim12 = dim1 * dim2; vtkm::Id idx12 = index % dim12; vtkm::Id i1 = idx12 % dim1; vtkm::Id i2 = idx12 / dim1; vtkm::Id i3 = index / dim12; return vtkm::make_Vec( this->PortalFirst.Get(i1), this->PortalSecond.Get(i2), this->PortalThird.Get(i3)); } VTKM_SUPPRESS_EXEC_WARNINGS template ::type> VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const { VTKM_ASSERT(index >= 0); VTKM_ASSERT(index < this->GetNumberOfValues()); vtkm::Id dim1 = this->PortalFirst.GetNumberOfValues(); vtkm::Id dim2 = this->PortalSecond.GetNumberOfValues(); vtkm::Id dim12 = dim1 * dim2; vtkm::Id idx12 = index % dim12; vtkm::Id i1 = idx12 % dim1; vtkm::Id i2 = idx12 / dim1; vtkm::Id i3 = index / dim12; this->PortalFirst.Set(i1, value[0]); this->PortalSecond.Set(i2, value[1]); this->PortalThird.Set(i3, value[2]); } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT const PortalTypeFirst& GetFirstPortal() const { return this->PortalFirst; } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT const PortalTypeSecond& GetSecondPortal() const { return this->PortalSecond; } VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT const PortalTypeThird& GetThirdPortal() const { return this->PortalThird; } private: PortalTypeFirst PortalFirst; PortalTypeSecond PortalSecond; PortalTypeThird PortalThird; }; } } // namespace vtkm::internal namespace vtkm { namespace cont { template struct VTKM_ALWAYS_EXPORT StorageTagCartesianProduct { }; namespace internal { /// This helper struct defines the value type for a zip container containing /// the given two array handles. /// template struct ArrayHandleCartesianProductTraits { VTKM_IS_ARRAY_HANDLE(AH1); VTKM_IS_ARRAY_HANDLE(AH2); VTKM_IS_ARRAY_HANDLE(AH3); using ComponentType = typename AH1::ValueType; VTKM_STATIC_ASSERT_MSG( (std::is_same::value), "All arrays for ArrayHandleCartesianProduct must have the same value type. " "Use ArrayHandleCast as necessary to make types match."); VTKM_STATIC_ASSERT_MSG( (std::is_same::value), "All arrays for ArrayHandleCartesianProduct must have the same value type. " "Use ArrayHandleCast as necessary to make types match."); /// The ValueType (a pair containing the value types of the two arrays). /// using ValueType = vtkm::Vec; /// The appropriately templated tag. /// using Tag = vtkm::cont::StorageTagCartesianProduct; /// The superclass for ArrayHandleCartesianProduct. /// using Superclass = vtkm::cont::ArrayHandle; }; template class Storage, vtkm::cont::StorageTagCartesianProduct> { struct Info { std::array BufferOffset; }; using Storage1 = vtkm::cont::internal::Storage; using Storage2 = vtkm::cont::internal::Storage; using Storage3 = vtkm::cont::internal::Storage; using Array1 = vtkm::cont::ArrayHandle; using Array2 = vtkm::cont::ArrayHandle; using Array3 = vtkm::cont::ArrayHandle; VTKM_CONT static std::vector GetBuffers( const std::vector& buffers, std::size_t subArray) { Info info = buffers[0].GetMetaData(); return std::vector(buffers.begin() + info.BufferOffset[subArray - 1], buffers.begin() + info.BufferOffset[subArray]); } public: VTKM_STORAGE_NO_RESIZE; using ReadPortalType = vtkm::internal::ArrayPortalCartesianProduct, typename Storage1::ReadPortalType, typename Storage2::ReadPortalType, typename Storage3::ReadPortalType>; using WritePortalType = vtkm::internal::ArrayPortalCartesianProduct, typename Storage1::WritePortalType, typename Storage2::WritePortalType, typename Storage3::WritePortalType>; VTKM_CONT static vtkm::IdComponent GetNumberOfComponentsFlat( const std::vector&) { return vtkm::VecFlat::NUM_COMPONENTS * 3; } VTKM_CONT static vtkm::Id GetNumberOfValues( const std::vector& buffers) { return (Storage1::GetNumberOfValues(GetBuffers(buffers, 1)) * Storage2::GetNumberOfValues(GetBuffers(buffers, 2)) * Storage3::GetNumberOfValues(GetBuffers(buffers, 3))); } VTKM_CONT static void Fill(const std::vector& buffers, const vtkm::Vec& fillValue, vtkm::Id startIndex, vtkm::Id endIndex, vtkm::cont::Token& token) { if ((startIndex != 0) || (endIndex != GetNumberOfValues(buffers))) { throw vtkm::cont::ErrorBadValue( "Fill for ArrayHandleCartesianProduct can only be used to fill entire array."); } auto subBuffers = GetBuffers(buffers, 1); Storage1::Fill(subBuffers, fillValue[0], 0, Storage1::GetNumberOfValues(subBuffers), token); subBuffers = GetBuffers(buffers, 2); Storage2::Fill(subBuffers, fillValue[1], 0, Storage2::GetNumberOfValues(subBuffers), token); subBuffers = GetBuffers(buffers, 3); Storage3::Fill(subBuffers, fillValue[2], 0, Storage3::GetNumberOfValues(subBuffers), token); } VTKM_CONT static ReadPortalType CreateReadPortal( const std::vector& buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { return ReadPortalType(Storage1::CreateReadPortal(GetBuffers(buffers, 1), device, token), Storage2::CreateReadPortal(GetBuffers(buffers, 2), device, token), Storage3::CreateReadPortal(GetBuffers(buffers, 3), device, token)); } VTKM_CONT static WritePortalType CreateWritePortal( const std::vector& buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { return WritePortalType(Storage1::CreateWritePortal(GetBuffers(buffers, 1), device, token), Storage2::CreateWritePortal(GetBuffers(buffers, 2), device, token), Storage3::CreateWritePortal(GetBuffers(buffers, 3), device, token)); } VTKM_CONT static Array1 GetArrayHandle1(const std::vector& buffers) { return Array1(GetBuffers(buffers, 1)); } VTKM_CONT static Array2 GetArrayHandle2(const std::vector& buffers) { return Array2(GetBuffers(buffers, 2)); } VTKM_CONT static Array3 GetArrayHandle3(const std::vector& buffers) { return Array3(GetBuffers(buffers, 3)); } VTKM_CONT static std::vector CreateBuffers( const Array1& array1 = Array1{}, const Array2& array2 = Array2{}, const Array3& array3 = Array3{}) { const std::vector& buffers1 = array1.GetBuffers(); const std::vector& buffers2 = array2.GetBuffers(); const std::vector& buffers3 = array3.GetBuffers(); Info info; info.BufferOffset[0] = 1; info.BufferOffset[1] = info.BufferOffset[0] + buffers1.size(); info.BufferOffset[2] = info.BufferOffset[1] + buffers2.size(); info.BufferOffset[3] = info.BufferOffset[2] + buffers3.size(); return vtkm::cont::internal::CreateBuffers(info, buffers1, buffers2, buffers3); } }; } // namespace internal /// ArrayHandleCartesianProduct is a specialization of ArrayHandle. It takes two delegate /// array handle and makes a new handle that access the corresponding entries /// in these arrays as a pair. /// template class ArrayHandleCartesianProduct : public internal::ArrayHandleCartesianProductTraits::Superclass { // If the following line gives a compile error, then the FirstHandleType // template argument is not a valid ArrayHandle type. VTKM_IS_ARRAY_HANDLE(FirstHandleType); VTKM_IS_ARRAY_HANDLE(SecondHandleType); VTKM_IS_ARRAY_HANDLE(ThirdHandleType); public: VTKM_ARRAY_HANDLE_SUBCLASS( ArrayHandleCartesianProduct, (ArrayHandleCartesianProduct), (typename internal::ArrayHandleCartesianProductTraits::Superclass)); VTKM_CONT ArrayHandleCartesianProduct(const FirstHandleType& firstArray, const SecondHandleType& secondArray, const ThirdHandleType& thirdArray) : Superclass(StorageType::CreateBuffers(firstArray, secondArray, thirdArray)) { } /// Implemented so that it is defined exclusively in the control environment. /// If there is a separate device for the execution environment (for example, /// with CUDA), then the automatically generated destructor could be /// created for all devices, and it would not be valid for all devices. /// ~ArrayHandleCartesianProduct() {} VTKM_CONT FirstHandleType GetFirstArray() const { return StorageType::GetArrayHandle1(this->GetBuffers()); } VTKM_CONT SecondHandleType GetSecondArray() const { return StorageType::GetArrayHandle2(this->GetBuffers()); } VTKM_CONT ThirdHandleType GetThirdArray() const { return StorageType::GetArrayHandle3(this->GetBuffers()); } }; /// A convenience function for creating an ArrayHandleCartesianProduct. It takes the two /// arrays to be zipped together. /// template VTKM_CONT vtkm::cont::ArrayHandleCartesianProduct make_ArrayHandleCartesianProduct(const FirstHandleType& first, const SecondHandleType& second, const ThirdHandleType& third) { return ArrayHandleCartesianProduct( first, second, third); } //-------------------------------------------------------------------------------- // Specialization of ArrayExtractComponent namespace internal { // Superclass will inherit the ArrayExtractComponentImplInefficient property if any // of the sub-storage are inefficient (thus making everything inefficient). template struct ArrayExtractComponentImpl> : vtkm::cont::internal::ArrayExtractComponentImplInherit { template vtkm::cont::ArrayHandleStride AdjustStrideForComponent( const vtkm::cont::ArrayHandleStride& componentArray, const vtkm::Id3& dims, vtkm::IdComponent component, vtkm::Id totalNumValues) const { VTKM_ASSERT(componentArray.GetModulo() == 0); VTKM_ASSERT(componentArray.GetDivisor() == 1); vtkm::Id modulo = 0; if (component < 2) { modulo = dims[component]; } vtkm::Id divisor = 1; for (vtkm::IdComponent c = 0; c < component; ++c) { divisor *= dims[c]; } return vtkm::cont::ArrayHandleStride(componentArray.GetBasicArray(), totalNumValues, componentArray.GetStride(), componentArray.GetOffset(), modulo, divisor); } template vtkm::cont::ArrayHandleStride::BaseComponentType> GetStrideForComponentArray(const vtkm::cont::ArrayHandle& componentArray, const CartesianArrayType& cartesianArray, vtkm::IdComponent subIndex, vtkm::IdComponent productIndex, vtkm::CopyFlag allowCopy) const { vtkm::cont::ArrayHandleStride::BaseComponentType> strideArray = ArrayExtractComponentImpl{}(componentArray, subIndex, allowCopy); if ((strideArray.GetModulo() != 0) || (strideArray.GetDivisor() != 1)) { // If the sub array has its own modulo and/or divisor, that will likely interfere // with this math. Give up and fall back to simple copy. constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat::NUM_COMPONENTS; return vtkm::cont::internal::ArrayExtractComponentFallback( cartesianArray, (productIndex * NUM_SUB_COMPONENTS) + subIndex, allowCopy); } vtkm::Id3 dims = { cartesianArray.GetFirstArray().GetNumberOfValues(), cartesianArray.GetSecondArray().GetNumberOfValues(), cartesianArray.GetThirdArray().GetNumberOfValues() }; return this->AdjustStrideForComponent( strideArray, dims, productIndex, cartesianArray.GetNumberOfValues()); } template vtkm::cont::ArrayHandleStride::BaseComponentType> operator()( const vtkm::cont::ArrayHandle, vtkm::cont::StorageTagCartesianProduct>& src, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy) const { vtkm::cont::ArrayHandleCartesianProduct...> array(src); constexpr vtkm::IdComponent NUM_SUB_COMPONENTS = vtkm::VecFlat::NUM_COMPONENTS; vtkm::IdComponent subIndex = componentIndex % NUM_SUB_COMPONENTS; vtkm::IdComponent productIndex = componentIndex / NUM_SUB_COMPONENTS; switch (productIndex) { case 0: return this->GetStrideForComponentArray( array.GetFirstArray(), array, subIndex, productIndex, allowCopy); case 1: return this->GetStrideForComponentArray( array.GetSecondArray(), array, subIndex, productIndex, allowCopy); case 2: return this->GetStrideForComponentArray( array.GetThirdArray(), array, subIndex, productIndex, allowCopy); default: throw vtkm::cont::ErrorBadValue("Invalid component index to ArrayExtractComponent."); } } }; template vtkm::cont::ArrayHandle ArrayRangeComputeGeneric( const vtkm::cont::ArrayHandle& input, const vtkm::cont::ArrayHandle& maskArray, bool computeFiniteRange, vtkm::cont::DeviceAdapterId device); template struct ArrayRangeComputeImpl; template struct VTKM_CONT_EXPORT ArrayRangeComputeImpl> { template VTKM_CONT vtkm::cont::ArrayHandle operator()( const vtkm::cont::ArrayHandle, vtkm::cont::StorageTagCartesianProduct>& input_, const vtkm::cont::ArrayHandle& maskArray, bool computeFiniteRange, vtkm::cont::DeviceAdapterId device) const { if (maskArray.GetNumberOfValues() != 0) { return vtkm::cont::internal::ArrayRangeComputeGeneric( input_, maskArray, computeFiniteRange, device); } const auto& input = static_cast, vtkm::cont::ArrayHandle, vtkm::cont::ArrayHandle>&>( input_); vtkm::cont::ArrayHandle ranges[3]; ranges[0] = vtkm::cont::internal::ArrayRangeComputeImpl{}( input.GetFirstArray(), maskArray, computeFiniteRange, device); ranges[1] = vtkm::cont::internal::ArrayRangeComputeImpl{}( input.GetSecondArray(), maskArray, computeFiniteRange, device); ranges[2] = vtkm::cont::internal::ArrayRangeComputeImpl{}( input.GetThirdArray(), maskArray, computeFiniteRange, device); auto numComponents = ranges[0].GetNumberOfValues() + ranges[1].GetNumberOfValues() + ranges[2].GetNumberOfValues(); vtkm::cont::ArrayHandle result; result.Allocate(numComponents); auto resultPortal = result.WritePortal(); for (vtkm::Id i = 0, index = 0; i < 3; ++i) { auto rangePortal = ranges[i].ReadPortal(); for (vtkm::Id j = 0; j < rangePortal.GetNumberOfValues(); ++j, ++index) { resultPortal.Set(index, rangePortal.Get(j)); } } return result; } }; } // 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_CartesianProduct<" + SerializableTypeString::Get() + "," + SerializableTypeString::Get() + "," + SerializableTypeString::Get() + ">"; return name; } }; template struct SerializableTypeString< vtkm::cont::ArrayHandle, vtkm::cont::StorageTagCartesianProduct>> : SerializableTypeString, vtkm::cont::ArrayHandle, vtkm::cont::ArrayHandle>> { }; } } // vtkm::cont namespace mangled_diy_namespace { template struct Serialization> { private: using Type = typename vtkm::cont::ArrayHandleCartesianProduct; using BaseType = vtkm::cont::ArrayHandle; public: static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj) { Type array = obj; vtkmdiy::save(bb, array.GetFirstArray()); vtkmdiy::save(bb, array.GetSecondArray()); vtkmdiy::save(bb, array.GetThirdArray()); } static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) { AH1 array1; AH2 array2; AH3 array3; vtkmdiy::load(bb, array1); vtkmdiy::load(bb, array2); vtkmdiy::load(bb, array3); obj = vtkm::cont::make_ArrayHandleCartesianProduct(array1, array2, array3); } }; template struct Serialization< vtkm::cont::ArrayHandle, vtkm::cont::StorageTagCartesianProduct>> : Serialization, vtkm::cont::ArrayHandle, vtkm::cont::ArrayHandle>> { }; } // diy /// @endcond SERIALIZATION #endif //vtk_m_cont_ArrayHandleCartesianProduct_h