//============================================================================ // 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_ArrayHandleMultiplexer_h #define vtk_m_cont_ArrayHandleMultiplexer_h #include #include #include #include #include #include #include #include namespace vtkm { namespace internal { namespace detail { struct ArrayPortalMultiplexerGetNumberOfValuesFunctor { template VTKM_EXEC_CONT vtkm::Id operator()(const PortalType& portal) const noexcept { return portal.GetNumberOfValues(); } }; struct ArrayPortalMultiplexerGetFunctor { VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT typename PortalType::ValueType operator()(const PortalType& portal, vtkm::Id index) const noexcept { return portal.Get(index); } }; struct ArrayPortalMultiplexerSetFunctor { template VTKM_EXEC_CONT void operator()(const PortalType& portal, vtkm::Id index, const typename PortalType::ValueType& value) const noexcept { this->DoSet( portal, index, value, typename vtkm::internal::PortalSupportsSets::type{}); } private: VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void DoSet(const PortalType& portal, vtkm::Id index, const typename PortalType::ValueType& value, std::true_type) const noexcept { portal.Set(index, value); } VTKM_SUPPRESS_EXEC_WARNINGS template VTKM_EXEC_CONT void DoSet(const PortalType&, vtkm::Id, const typename PortalType::ValueType&, std::false_type) const noexcept { // This is an error but whatever. VTKM_ASSERT(false && "Calling Set on a portal that does not support it."); } }; } // namespace detail template struct ArrayPortalMultiplexer { using PortalVariantType = vtkm::exec::internal::Variant; PortalVariantType PortalVariant; using ValueType = typename PortalVariantType::template TypeAt<0>::ValueType; ArrayPortalMultiplexer() = default; ~ArrayPortalMultiplexer() = default; ArrayPortalMultiplexer(ArrayPortalMultiplexer&&) = default; ArrayPortalMultiplexer(const ArrayPortalMultiplexer&) = default; ArrayPortalMultiplexer& operator=(ArrayPortalMultiplexer&&) = default; ArrayPortalMultiplexer& operator=(const ArrayPortalMultiplexer&) = default; template VTKM_EXEC_CONT ArrayPortalMultiplexer(const Portal& src) noexcept : PortalVariant(src) { } template VTKM_EXEC_CONT ArrayPortalMultiplexer& operator=(const Portal& src) noexcept { this->PortalVariant = src; return *this; } VTKM_EXEC_CONT vtkm::Id GetNumberOfValues() const noexcept { return this->PortalVariant.CastAndCall( detail::ArrayPortalMultiplexerGetNumberOfValuesFunctor{}); } VTKM_EXEC_CONT ValueType Get(vtkm::Id index) const noexcept { return this->PortalVariant.CastAndCall(detail::ArrayPortalMultiplexerGetFunctor{}, index); } VTKM_EXEC_CONT void Set(vtkm::Id index, const ValueType& value) const noexcept { this->PortalVariant.CastAndCall(detail::ArrayPortalMultiplexerSetFunctor{}, index, value); } }; } // namespace internal namespace cont { template struct StorageTagMultiplexer { }; namespace internal { namespace detail { struct MultiplexerGetNumberOfValuesFunctor { template VTKM_CONT vtkm::Id operator()(ArrayHandleType&& array) const { return array.GetNumberOfValues(); } }; struct MultiplexerAllocateFunctor { template VTKM_CONT void operator()(ArrayHandleType&& array, vtkm::Id numberOfValues) const { array.Allocate(numberOfValues); } }; struct MultiplexerShrinkFunctor { template VTKM_CONT void operator()(ArrayHandleType&& array, vtkm::Id numberOfValues) const { array.Shrink(numberOfValues); } }; struct MultiplexerReleaseResourcesFunctor { template VTKM_CONT void operator()(ArrayHandleType&& array) const { array.ReleaseResources(); } }; struct MultiplexerReleaseResourcesExecutionFunctor { template VTKM_CONT void operator()(ArrayHandleType&& array) const { array.ReleaseResourcesExecution(); } }; } // namespace detail template class Storage> { public: using ValueType = ValueType_; private: template using StorageToArrayHandle = vtkm::cont::ArrayHandle; template using StorageToPortalControl = typename StorageToArrayHandle::WritePortalType; template using StorageToPortalConstControl = typename StorageToArrayHandle::ReadPortalType; using ArrayHandleVariantType = vtkm::cont::internal::Variant...>; ArrayHandleVariantType ArrayHandleVariant; public: using PortalType = vtkm::internal::ArrayPortalMultiplexer...>; using PortalConstType = vtkm::internal::ArrayPortalMultiplexer...>; Storage() = default; Storage(Storage&&) = default; Storage(const Storage&) = default; Storage& operator=(Storage&&) = default; Storage& operator=(const Storage&) = default; template VTKM_CONT Storage(vtkm::cont::ArrayHandle&& rhs) : ArrayHandleVariant(std::move(rhs)) { } template VTKM_CONT Storage(const vtkm::cont::ArrayHandle& src) : ArrayHandleVariant(src) { } VTKM_CONT bool IsValid() const { return this->ArrayHandleVariant.IsValid(); } template VTKM_CONT void SetArray(vtkm::cont::ArrayHandle&& rhs) { this->ArrayHandleVariant = std::move(rhs); } template VTKM_CONT void SetArray(const vtkm::cont::ArrayHandle& src) { this->ArrayHandleVariant = src; } private: struct GetPortalFunctor { template VTKM_CONT PortalType operator()(ArrayHandleType&& array) const { return PortalType(array.WritePortal()); } }; struct GetPortalConstFunctor { template VTKM_CONT PortalConstType operator()(ArrayHandleType&& array) const { return PortalConstType(array.ReadPortal()); } }; public: VTKM_CONT PortalType GetPortal() { return this->ArrayHandleVariant.CastAndCall(GetPortalFunctor{}); } VTKM_CONT PortalConstType GetPortalConst() const { return this->ArrayHandleVariant.CastAndCall(GetPortalConstFunctor{}); } VTKM_CONT vtkm::Id GetNumberOfValues() const { if (this->IsValid()) { return this->ArrayHandleVariant.CastAndCall(detail::MultiplexerGetNumberOfValuesFunctor{}); } else { return 0; } } VTKM_CONT void Allocate(vtkm::Id numberOfValues) { if (this->IsValid()) { this->ArrayHandleVariant.CastAndCall(detail::MultiplexerAllocateFunctor{}, numberOfValues); } else if (numberOfValues > 0) { throw vtkm::cont::ErrorBadValue( "Attempted to allocate an ArrayHandleMultiplexer with no underlying array."); } else { // Special case, OK to perform "0" allocation on invalid array. } } VTKM_CONT void Shrink(vtkm::Id numberOfValues) { if (this->IsValid()) { this->ArrayHandleVariant.CastAndCall(detail::MultiplexerShrinkFunctor{}, numberOfValues); } else if (numberOfValues > 0) { throw vtkm::cont::ErrorBadValue( "Attempted to allocate an ArrayHandleMultiplexer with no underlying array."); } else { // Special case, OK to perform "0" allocation on invalid array. } } VTKM_CONT void ReleaseResources() { if (this->IsValid()) { this->ArrayHandleVariant.CastAndCall(detail::MultiplexerReleaseResourcesFunctor{}); } } VTKM_CONT ArrayHandleVariantType& GetArrayHandleVariant() { return this->ArrayHandleVariant; } VTKM_CONT const ArrayHandleVariantType& GetArrayHandleVariant() const { return this->ArrayHandleVariant; } }; template class ArrayTransfer, Device> { public: using ValueType = ValueType_; private: using StorageTag = StorageTagMultiplexer; using StorageType = vtkm::cont::internal::Storage; template using StorageToArrayHandle = vtkm::cont::ArrayHandle; template using StorageToPortalExecution = typename StorageToArrayHandle::template ExecutionTypes::Portal; template using StorageToPortalConstExecution = typename StorageToArrayHandle::template ExecutionTypes::PortalConst; public: using PortalControl = typename StorageType::PortalType; using PortalConstControl = typename StorageType::PortalConstType; using PortalExecution = vtkm::internal::ArrayPortalMultiplexer...>; using PortalConstExecution = vtkm::internal::ArrayPortalMultiplexer...>; private: StorageType* StoragePointer; public: VTKM_CONT ArrayTransfer(StorageType* storage) : StoragePointer(storage) { } VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->StoragePointer->GetNumberOfValues(); } VTKM_CONT PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData), vtkm::cont::Token& token) { return this->StoragePointer->GetArrayHandleVariant().CastAndCall(PrepareForInputFunctor{}, token); } VTKM_CONT PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData), vtkm::cont::Token& token) { return this->StoragePointer->GetArrayHandleVariant().CastAndCall(PrepareForInPlaceFunctor{}, token); } VTKM_CONT PortalExecution PrepareForOutput(vtkm::Id numberOfValues, vtkm::cont::Token& token) { return this->StoragePointer->GetArrayHandleVariant().CastAndCall( PrepareForOutputFunctor{}, numberOfValues, token); } VTKM_CONT void RetrieveOutputData(StorageType* vtkmNotUsed(storage)) const { // Implementation of this method should be unnecessary. The internal // array handles should automatically retrieve the output data as // necessary. } VTKM_CONT void Shrink(vtkm::Id numberOfValues) { this->StoragePointer->GetArrayHandleVariant().CastAndCall(detail::MultiplexerShrinkFunctor{}, numberOfValues); } VTKM_CONT void ReleaseResources() { this->StoragePointer->GetArrayHandleVariant().CastAndCall( detail::MultiplexerReleaseResourcesExecutionFunctor{}); } private: struct PrepareForInputFunctor { template VTKM_CONT PortalConstExecution operator()(const ArrayHandleType& array, vtkm::cont::Token& token) { return PortalConstExecution(array.PrepareForInput(Device{}, token)); } }; struct PrepareForInPlaceFunctor { template VTKM_CONT PortalExecution operator()(ArrayHandleType& array, vtkm::cont::Token& token) { return PortalExecution(array.PrepareForInPlace(Device{}, token)); } }; struct PrepareForOutputFunctor { template VTKM_CONT PortalExecution operator()(ArrayHandleType& array, vtkm::Id numberOfValues, vtkm::cont::Token& token) { return PortalExecution(array.PrepareForOutput(numberOfValues, Device{}, token)); } }; }; } // namespace internal namespace detail { template struct ArrayHandleMultiplexerTraits { using ArrayHandleType0 = brigand::at, std::integral_constant>; VTKM_IS_ARRAY_HANDLE(ArrayHandleType0); using ValueType = typename ArrayHandleType0::ValueType; // If there is a compile error in this group of lines, then one of the array types given to // ArrayHandleMultiplexer must contain an invalid ArrayHandle. That could mean that it is not an // ArrayHandle type or it could mean that the value type does not match the appropriate value // type. template struct CheckArrayHandleTransform { VTKM_IS_ARRAY_HANDLE(ArrayHandle); VTKM_STATIC_ASSERT((std::is_same::value)); }; using CheckArrayHandle = brigand::list...>; // Note that this group of code could be simplified as the pair of lines: // template // using ArrayHandleToStorageTag = typename ArrayHandle::StorageTag; // However, there are issues with older Visual Studio compilers that is not working // correctly with that form. template struct ArrayHandleToStorageTagImpl { using Type = typename ArrayHandle::StorageTag; }; template using ArrayHandleToStorageTag = typename ArrayHandleToStorageTagImpl::Type; using StorageTag = vtkm::cont::StorageTagMultiplexer...>; using StorageType = vtkm::cont::internal::Storage; }; } /// \brief An ArrayHandle that can behave like several other handles. /// /// An \c ArrayHandleMultiplexer simply redirects its calls to another \c ArrayHandle. However /// the type of that \c ArrayHandle does not need to be (completely) known at runtime. Rather, /// \c ArrayHandleMultiplexer is defined over a set of possible \c ArrayHandle types. Any /// one of these \c ArrayHandles may be assigned to the \c ArrayHandleMultiplexer. /// /// When a value is retreived from the \c ArrayHandleMultiplexer, the multiplexer checks to /// see which type of array is currently stored in it. It then redirects to the \c ArrayHandle /// of the appropriate type. /// /// The \c ArrayHandleMultiplexer template parameters are all the ArrayHandle types it /// should support. /// /// If only one template parameter is given, it is assumed to be the \c ValueType of the /// array. A default list of supported arrays is supported (see /// \c vtkm::cont::internal::ArrayHandleMultiplexerDefaultArrays.) If multiple template /// parameters are given, they are all considered possible \c ArrayHandle types. /// template class ArrayHandleMultiplexer : public vtkm::cont::ArrayHandle< typename detail::ArrayHandleMultiplexerTraits::ValueType, typename detail::ArrayHandleMultiplexerTraits::StorageTag> { using Traits = detail::ArrayHandleMultiplexerTraits; public: VTKM_ARRAY_HANDLE_SUBCLASS( ArrayHandleMultiplexer, (ArrayHandleMultiplexer), (vtkm::cont::ArrayHandle)); private: using StorageType = vtkm::cont::internal::Storage; public: template VTKM_CONT ArrayHandleMultiplexer(const vtkm::cont::ArrayHandle& src) : Superclass(StorageType(src)) { } template VTKM_CONT ArrayHandleMultiplexer(vtkm::cont::ArrayHandle&& rhs) : Superclass(StorageType(std::move(rhs))) { } VTKM_CONT bool IsValid() const { return this->GetStorage().IsValid(); } template VTKM_CONT void SetArray(vtkm::cont::ArrayHandle&& rhs) { this->GetStorage().SetArray(std::move(rhs)); } template VTKM_CONT void SetArray(const vtkm::cont::ArrayHandle& src) { this->GetStorage().SetArray(src); } }; /// \brief Converts a \c vtkm::ListTag to an \c ArrayHandleMultiplexer /// /// The argument of this template must be a vtkm::ListTag and furthermore all the types in /// the list tag must be some type of \c ArrayHandle. The templated type gets aliased to /// an \c ArrayHandleMultiplexer that can store any of these ArrayHandle types. /// /// Deprecated. Use `ArrayHandleMultiplexerFromList` instead. /// template using ArrayHandleMultiplexerFromListTag VTKM_DEPRECATED( 1.6, "vtkm::ListTag is no longer supported. Use vtkm::List instead.") = vtkm::ListApply; /// \brief Converts a`vtkm::List` to an `ArrayHandleMultiplexer` /// /// The argument of this template must be a `vtkm::List` and furthermore all the types in /// the list tag must be some type of \c ArrayHandle. The templated type gets aliased to /// an \c ArrayHandleMultiplexer that can store any of these ArrayHandle types. /// template using ArrayHandleMultiplexerFromList = vtkm::ListApply; } // namespace cont } // namespace vtkm #endif //vtk_m_cont_ArrayHandleMultiplexer_h