//============================================================================ // 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_ArrayHandleRecombineVec_h #define vtk_m_cont_ArrayHandleRecombineVec_h #include #include #include #include #include #include namespace vtkm { namespace internal { // Forward declaration template class ArrayPortalRecombineVec; template class RecombineVec { vtkm::VecCConst Portals; vtkm::Id Index; friend vtkm::internal::ArrayPortalRecombineVec; public: using ComponentType = typename std::remove_const::type; RecombineVec(const RecombineVec&) = default; VTKM_EXEC_CONT RecombineVec(const vtkm::VecCConst& portals, vtkm::Id index) : Portals(portals) , Index(index) { } VTKM_EXEC_CONT vtkm::IdComponent GetNumberOfComponents() const { return this->Portals.GetNumberOfComponents(); } VTKM_EXEC_CONT vtkm::internal::ArrayPortalValueReference operator[](vtkm::IdComponent cIndex) const { return vtkm::internal::ArrayPortalValueReference(this->Portals[cIndex], this->Index); } template VTKM_EXEC_CONT void CopyInto(vtkm::Vec& dest) const { vtkm::IdComponent numComponents = vtkm::Min(DestSize, this->GetNumberOfComponents()); for (vtkm::IdComponent cIndex = 0; cIndex < numComponents; ++cIndex) { dest[cIndex] = this->Portals[cIndex].Get(this->Index); } // Clear out any components not held by this dynamic Vec-like for (vtkm::IdComponent cIndex = numComponents; cIndex < DestSize; ++cIndex) { dest[cIndex] = vtkm::TypeTraits::ZeroInitialization(); } } VTKM_EXEC_CONT vtkm::Id GetIndex() const { return this->Index; } VTKM_EXEC_CONT RecombineVec& operator=(const RecombineVec& src) { if ((&this->Portals[0] != &src.Portals[0]) || (this->Index != src.Index)) { this->DoCopy(src); } else { // Copying to myself. Do not need to do anything. } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator=(const T& src) { this->DoCopy(src); return *this; } VTKM_EXEC_CONT operator ComponentType() const { return this->Portals[0].Get(this->Index); } template VTKM_EXEC_CONT operator vtkm::Vec() const { vtkm::Vec result; this->CopyInto(result); return result; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator+=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] += VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator-=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] -= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator*=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] *= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator/=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] /= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator%=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] %= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator&=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] &= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator|=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] |= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator^=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] ^= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator>>=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] >>= VTraits::GetComponent(src, cIndex); } return *this; } template ::value>::type> VTKM_EXEC_CONT RecombineVec& operator<<=(const T& src) { using VTraits = vtkm::VecTraits; VTKM_ASSERT(this->GetNumberOfComponents() == VTraits::GetNumberOfComponents(src)); for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { (*this)[cIndex] <<= VTraits::GetComponent(src, cIndex); } return *this; } private: template VTKM_EXEC_CONT void DoCopy(const T& src) { using VTraits = vtkm::VecTraits; vtkm::IdComponent numComponents = VTraits::GetNumberOfComponents(src); if (numComponents > 1) { if (numComponents > this->GetNumberOfComponents()) { numComponents = this->GetNumberOfComponents(); } for (vtkm::IdComponent cIndex = 0; cIndex < numComponents; ++cIndex) { this->Portals[cIndex].Set(this->Index, static_cast(VTraits::GetComponent(src, cIndex))); } } else { // Special case when copying from a scalar for (vtkm::IdComponent cIndex = 0; cIndex < this->GetNumberOfComponents(); ++cIndex) { this->Portals[cIndex].Set(this->Index, static_cast(VTraits::GetComponent(src, 0))); } } } }; } // namespace internal template struct TypeTraits> { private: using VecType = vtkm::internal::RecombineVec; using ComponentType = typename VecType::ComponentType; public: using NumericTag = typename vtkm::TypeTraits::NumericTag; using DimensionalityTag = vtkm::TypeTraitsVectorTag; VTKM_EXEC_CONT static vtkm::internal::RecombineVec ZeroInitialization() { // Return a vec-like of size 0. return vtkm::internal::RecombineVec{}; } }; template struct VecTraits> { using VecType = vtkm::internal::RecombineVec; using ComponentType = typename VecType::ComponentType; using BaseComponentType = typename vtkm::VecTraits::BaseComponentType; using HasMultipleComponents = vtkm::VecTraitsTagMultipleComponents; using IsSizeStatic = vtkm::VecTraitsTagSizeVariable; VTKM_EXEC_CONT static vtkm::IdComponent GetNumberOfComponents(const VecType& vector) { return vector.GetNumberOfComponents(); } VTKM_EXEC_CONT static ComponentType GetComponent(const VecType& vector, vtkm::IdComponent componentIndex) { return vector[componentIndex]; } VTKM_EXEC_CONT static void SetComponent(const VecType& vector, vtkm::IdComponent componentIndex, const ComponentType& component) { vector[componentIndex] = component; } template VTKM_EXEC_CONT static void CopyInto(const VecType& src, vtkm::Vec& dest) { src.CopyInto(dest); } }; namespace internal { template class ArrayPortalRecombineVec { // Note that this ArrayPortal has a pointer to a C array of other portals. We need to // make sure that the pointer is valid on the device we are using it on. See the // CreateReadPortal and CreateWritePortal in the Storage below to see how that is // managed. const SourcePortalType* Portals; vtkm::IdComponent NumberOfComponents; public: using ValueType = vtkm::internal::RecombineVec; ArrayPortalRecombineVec() = default; ArrayPortalRecombineVec(const SourcePortalType* portals, vtkm::IdComponent numComponents) : Portals(portals) , NumberOfComponents(numComponents) { } VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const { return this->Portals[0].GetNumberOfValues(); } VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const { return ValueType({ this->Portals, this->NumberOfComponents }, index); } VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const { if ((value.GetIndex() == index) && (value.Portals.GetPointer() == this->Portals)) { // The ValueType is actually a reference back to the portals. If this reference is // actually pointing back to the same index, we don't need to do anything. } else { this->DoCopy(index, value); } } template VTKM_EXEC_CONT void Set(vtkm::Id index, const T& value) const { this->DoCopy(index, value); } private: template VTKM_EXEC_CONT void DoCopy(vtkm::Id index, const T& value) const { using Traits = vtkm::VecTraits; VTKM_ASSERT(Traits::GetNumberOfComponents(value) == this->NumberOfComponents); for (vtkm::IdComponent cIndex = 0; cIndex < this->NumberOfComponents; ++cIndex) { this->Portals[cIndex].Set(index, Traits::GetComponent(value, cIndex)); } } }; } } // namespace vtkm::internal namespace vtkm { namespace cont { namespace internal { struct StorageTagRecombineVec { }; namespace detail { struct RecombineVecMetaData { mutable std::vector PortalBuffers; std::vector ArrayBufferOffsets; RecombineVecMetaData() = default; RecombineVecMetaData(const RecombineVecMetaData& src) { *this = src; } RecombineVecMetaData& operator=(const RecombineVecMetaData& src) { this->ArrayBufferOffsets = src.ArrayBufferOffsets; this->PortalBuffers.clear(); // Intentionally not copying portals. Portals will be recreated from proper array when requsted. return *this; } }; template using RecombinedPortalType = vtkm::internal::ArrayPortalMultiplexer< typename vtkm::cont::internal::Storage::ReadPortalType, typename vtkm::cont::internal::Storage::WritePortalType>; template using RecombinedValueType = vtkm::internal::RecombineVec>; } // namespace detail template class Storage, vtkm::cont::internal::StorageTagRecombineVec> { using ComponentType = typename ReadWritePortal::ValueType; using SourceStorage = vtkm::cont::internal::Storage; using ArrayType = vtkm::cont::ArrayHandle; VTKM_STATIC_ASSERT( (std::is_same>::value)); VTKM_CONT static std::vector BuffersForComponent( const std::vector& buffers, vtkm::IdComponent componentIndex) { auto& metaData = buffers[0].GetMetaData(); std::size_t index = static_cast(componentIndex); return std::vector( buffers.begin() + metaData.ArrayBufferOffsets[index], buffers.begin() + metaData.ArrayBufferOffsets[index + 1]); } public: using ReadPortalType = vtkm::internal::ArrayPortalRecombineVec; using WritePortalType = vtkm::internal::ArrayPortalRecombineVec; VTKM_CONT static vtkm::IdComponent NumberOfComponents( const std::vector& buffers) { return static_cast( buffers[0].GetMetaData().ArrayBufferOffsets.size() - 1); } VTKM_CONT static vtkm::Id GetNumberOfValues( const std::vector& buffers) { return SourceStorage::GetNumberOfValues(BuffersForComponent(buffers, 0)); } VTKM_CONT static void ResizeBuffers(vtkm::Id numValues, const std::vector& buffers, vtkm::CopyFlag preserve, vtkm::cont::Token& token) { vtkm::IdComponent numComponents = NumberOfComponents(buffers); for (vtkm::IdComponent component = 0; component < numComponents; ++component) { SourceStorage::ResizeBuffers( numValues, BuffersForComponent(buffers, component), preserve, token); } } VTKM_CONT static void Fill(const std::vector&, const vtkm::internal::RecombineVec&, vtkm::Id, vtkm::Id, vtkm::cont::Token&) { throw vtkm::cont::ErrorBadType("Fill not supported for ArrayHandleRecombineVec."); } VTKM_CONT static ReadPortalType CreateReadPortal( const std::vector& buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { vtkm::IdComponent numComponents = NumberOfComponents(buffers); // The array portal needs a runtime-allocated array of portals for each component. // We use the vtkm::cont::internal::Buffer object to allow us to allocate memory on the // device and copy data there. vtkm::cont::internal::Buffer portalBuffer; portalBuffer.SetNumberOfBytes(static_cast(sizeof(ReadWritePortal)) * numComponents, vtkm::CopyFlag::Off, token); // Save a reference of the portal in our metadata. // Note that the buffer we create is going to hang around until the ArrayHandle gets // destroyed. The buffers are small and should not be a problem unless you create a // lot of portals. buffers[0].GetMetaData().PortalBuffers.push_back(portalBuffer); // Get the control-side memory and fill it with the execution-side portals ReadWritePortal* portals = reinterpret_cast(portalBuffer.WritePointerHost(token)); for (vtkm::IdComponent cIndex = 0; cIndex < numComponents; ++cIndex) { portals[cIndex] = ReadWritePortal( SourceStorage::CreateReadPortal(BuffersForComponent(buffers, cIndex), device, token)); } // Now get the execution-side memory (portals will be copied as necessary) and create // the portal for the appropriate device return ReadPortalType( reinterpret_cast(portalBuffer.ReadPointerDevice(device, token)), numComponents); } VTKM_CONT static WritePortalType CreateWritePortal( const std::vector& buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) { vtkm::IdComponent numComponents = NumberOfComponents(buffers); // The array portal needs a runtime-allocated array of portals for each component. // We use the vtkm::cont::internal::Buffer object to allow us to allocate memory on the // device and copy data there. vtkm::cont::internal::Buffer portalBuffer; portalBuffer.SetNumberOfBytes(static_cast(sizeof(ReadWritePortal)) * numComponents, vtkm::CopyFlag::Off, token); // Save a reference of the portal in our metadata. // Note that the buffer we create is going to hang around until the ArrayHandle gets // destroyed. The buffers are small and should not be a problem unless you create a // lot of portals. buffers[0].GetMetaData().PortalBuffers.push_back(portalBuffer); // Get the control-side memory and fill it with the execution-side portals ReadWritePortal* portals = reinterpret_cast(portalBuffer.WritePointerHost(token)); for (vtkm::IdComponent cIndex = 0; cIndex < numComponents; ++cIndex) { portals[cIndex] = ReadWritePortal( SourceStorage::CreateWritePortal(BuffersForComponent(buffers, cIndex), device, token)); } // Now get the execution-side memory (portals will be copied as necessary) and create // the portal for the appropriate device return WritePortalType( reinterpret_cast(portalBuffer.ReadPointerDevice(device, token)), numComponents); } VTKM_CONT static ArrayType ArrayForComponent( const std::vector& buffers, vtkm::IdComponent componentIndex) { return ArrayType(BuffersForComponent(buffers, componentIndex)); } VTKM_CONT static std::vector CreateBuffers() { detail::RecombineVecMetaData metaData; metaData.ArrayBufferOffsets.push_back(1); return vtkm::cont::internal::CreateBuffers(metaData); } VTKM_CONT static void AppendComponent(std::vector& buffers, const ArrayType& array) { // Add buffers of new array to our list of buffers. buffers.insert(buffers.end(), array.GetBuffers().begin(), array.GetBuffers().end()); // Update metadata for new offset to end. buffers[0].GetMetaData().ArrayBufferOffsets.push_back( buffers.size()); } }; } // namespace internal /// \brief A grouping of `ArrayHandleStride`s into an `ArrayHandle` of `Vec`s. /// /// The main intention of `ArrayHandleStride` is to pull out a component of an /// `ArrayHandle` without knowing there `ArrayHandle`'s storage or `Vec` shape. /// However, usually you want to do an operation on all the components together. /// `ArrayHandleRecombineVec` implements the functionality to easily take a /// group of extracted components and treat them as a single `ArrayHandle` of /// `Vec` values. /// /// 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. /// template class ArrayHandleRecombineVec : public vtkm::cont::ArrayHandle, vtkm::cont::internal::StorageTagRecombineVec> { public: VTKM_ARRAY_HANDLE_SUBCLASS( ArrayHandleRecombineVec, (ArrayHandleRecombineVec), (vtkm::cont::ArrayHandle, vtkm::cont::internal::StorageTagRecombineVec>)); private: using StorageType = vtkm::cont::internal::Storage; public: vtkm::IdComponent GetNumberOfComponents() const { return StorageType::NumberOfComponents(this->GetBuffers()); } vtkm::cont::ArrayHandleStride GetComponentArray( vtkm::IdComponent componentIndex) const { return StorageType::ArrayForComponent(this->GetBuffers(), componentIndex); } void AppendComponentArray( const vtkm::cont::ArrayHandle& array) { std::vector buffers = this->GetBuffers(); StorageType::AppendComponent(buffers, array); this->SetBuffers(std::move(buffers)); } }; namespace internal { template <> struct ArrayExtractComponentImpl { template vtkm::cont::ArrayHandleStride< typename vtkm::VecFlat::ComponentType> operator()( const vtkm::cont::ArrayHandle& src, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy) const { using ComponentType = typename RecombineVec::ComponentType; vtkm::cont::ArrayHandleRecombineVec array(src); constexpr vtkm::IdComponent subComponents = vtkm::VecFlat::NUM_COMPONENTS; return vtkm::cont::ArrayExtractComponent( array.GetComponentArray(componentIndex / subComponents), componentIndex % subComponents, allowCopy); } }; } // namespace internal } } // namespace vtkm::cont #endif //vtk_m_cont_ArrayHandleRecombineVec_h