//============================================================================ // 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. // // Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_cont_ArrayHandle_h #define vtk_m_cont_ArrayHandle_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace cont { namespace internal { /// \brief Base class of all ArrayHandle classes. /// /// This is an empty class that is used to check if something is an \c /// ArrayHandle class (or at least something that behaves exactly like one). /// The \c ArrayHandle template class inherits from this. /// class VTKM_CONT_EXPORT ArrayHandleBase { }; /// Checks to see if the given type and storage forms a valid array handle /// (some storage objects cannot support all types). This check is compatible /// with C++11 type_traits. /// template struct IsValidArrayHandle : std::integral_constant>::value)> { }; /// Checks to see if the given type and storage forms a invalid array handle /// (some storage objects cannot support all types). This check is compatible /// with C++11 type_traits. /// template struct IsInValidArrayHandle : std::integral_constant>::value)> { }; /// Checks to see if the ArrayHandle allows /// writing, as some ArrayHandles (Implicit) don't support writing. /// This check is compatible with the C++11 type_traits. /// It contains a typedef named type that is either /// std::true_type or std::false_type. /// Both of these have a typedef named value with the respective boolean value. /// template struct IsWriteableArrayHandle { private: using ValueType = typename ArrayHandle::PortalControl::ValueType; //All ArrayHandles that use ImplicitStorage as the final writable location //will have a value type of void*, which is what we are trying to detect using RawValueType = typename std::remove_pointer::type; using IsVoidType = std::is_void; public: using type = std::integral_constant; static constexpr bool value = !IsVoidType::value; }; /// Checks to see if the given object is an array handle. This check is /// compatible with C++11 type_traits. It a typedef named \c type that is /// either std::true_type or std::false_type. Both of these have a typedef /// named value with the respective boolean value. /// /// Unlike \c IsValidArrayHandle, if an \c ArrayHandle is used with this /// class, then it must be created by the compiler and therefore must already /// be valid. Where \c IsValidArrayHandle is used when you know something is /// an \c ArrayHandle but you are not sure if the \c StorageTag is valid, this /// class is used to ensure that a given type is an \c ArrayHandle. It is /// used internally in the VTKM_IS_ARRAY_HANDLE macro. /// template struct ArrayHandleCheck { using U = typename std::remove_pointer::type; using type = typename std::is_base_of<::vtkm::cont::internal::ArrayHandleBase, U>::type; }; #define VTKM_IS_ARRAY_HANDLE(T) \ VTKM_STATIC_ASSERT(::vtkm::cont::internal::ArrayHandleCheck::type::value) } // namespace internal namespace detail { template struct GetTypeInParentheses; template struct GetTypeInParentheses { using type = T; }; } // namespace detail // Implementation for VTKM_ARRAY_HANDLE_SUBCLASS macros #define VTK_M_ARRAY_HANDLE_SUBCLASS_IMPL(classname, fullclasstype, superclass, typename__) \ using Thisclass = typename__ vtkm::cont::detail::GetTypeInParentheses::type; \ using Superclass = typename__ vtkm::cont::detail::GetTypeInParentheses::type; \ \ VTKM_IS_ARRAY_HANDLE(Superclass); \ \ VTKM_CONT \ classname() \ : Superclass() \ { \ } \ \ VTKM_CONT \ classname(const Thisclass& src) \ : Superclass(src) \ { \ } \ \ VTKM_CONT \ classname(const vtkm::cont::ArrayHandle& src) \ : Superclass(src) \ { \ } \ \ VTKM_CONT \ Thisclass& operator=(const Thisclass& src) \ { \ this->Superclass::operator=(src); \ return *this; \ } \ \ using ValueType = typename__ Superclass::ValueType; \ using StorageTag = typename__ Superclass::StorageTag /// \brief Macro to make default methods in ArrayHandle subclasses. /// /// This macro defines the default constructors, destructors and assignment /// operators for ArrayHandle subclasses that are templates. The ArrayHandle /// subclasses are assumed to be empty convenience classes. The macro should be /// defined after a \c public: declaration. /// /// This macro takes three arguments. The first argument is the classname. /// The second argument is the full class type. The third argument is the /// superclass type (either \c ArrayHandle or another sublcass). Because /// C macros do not handle template parameters very well (the preprocessor /// thinks the template commas are macro argument commas), the second and /// third arguments must be wrapped in parentheses. /// /// This macro also defines a Superclass typedef as well as ValueType and /// StorageTag. /// /// Note that this macro only works on ArrayHandle subclasses that are /// templated. For ArrayHandle sublcasses that are not templates, use /// VTKM_ARRAY_HANDLE_SUBCLASS_NT. /// #define VTKM_ARRAY_HANDLE_SUBCLASS(classname, fullclasstype, superclass) \ VTK_M_ARRAY_HANDLE_SUBCLASS_IMPL(classname, fullclasstype, superclass, typename) /// \brief Macro to make default methods in ArrayHandle subclasses. /// /// This macro defines the default constructors, destructors and assignment /// operators for ArrayHandle subclasses that are not templates. The /// ArrayHandle subclasses are assumed to be empty convenience classes. The /// macro should be defined after a \c public: declaration. /// /// This macro takes two arguments. The first argument is the classname. The /// second argument is the superclass type (either \c ArrayHandle or another /// sublcass). Because C macros do not handle template parameters very well /// (the preprocessor thinks the template commas are macro argument commas), /// the second argument must be wrapped in parentheses. /// /// This macro also defines a Superclass typedef as well as ValueType and /// StorageTag. /// /// Note that this macro only works on ArrayHandle subclasses that are not /// templated. For ArrayHandle sublcasses that are templates, use /// VTKM_ARRAY_HANDLE_SUBCLASS. /// #define VTKM_ARRAY_HANDLE_SUBCLASS_NT(classname, superclass) \ VTK_M_ARRAY_HANDLE_SUBCLASS_IMPL(classname, (classname), superclass, ) /// \brief Manages an array-worth of data. /// /// \c ArrayHandle manages as array of data that can be manipulated by VTKm /// algorithms. The \c ArrayHandle may have up to two copies of the array, one /// for the control environment and one for the execution environment, although /// depending on the device and how the array is being used, the \c ArrayHandle /// will only have one copy when possible. /// /// An ArrayHandle can be constructed one of two ways. Its default construction /// creates an empty, unallocated array that can later be allocated and filled /// either by the user or a VTKm algorithm. The \c ArrayHandle can also be /// constructed with iterators to a user's array. In this case the \c /// ArrayHandle will keep a reference to this array but will throw an exception /// if asked to re-allocate to a larger size. /// /// \c ArrayHandle behaves like a shared smart pointer in that when it is copied /// each copy holds a reference to the same array. These copies are reference /// counted so that when all copies of the \c ArrayHandle are destroyed, any /// allocated memory is released. /// /// template class VTKM_ALWAYS_EXPORT ArrayHandle : public internal::ArrayHandleBase { private: // Basic storage is specialized; this template should not be instantiated // for it. Specialization is in ArrayHandleBasicImpl.h static_assert(!std::is_same::value, "StorageTagBasic should not use this implementation."); using ExecutionManagerType = vtkm::cont::internal::ArrayHandleExecutionManagerBase; public: using StorageType = vtkm::cont::internal::Storage; using ValueType = T; using StorageTag = StorageTag_; using PortalControl = typename StorageType::PortalType; using PortalConstControl = typename StorageType::PortalConstType; template struct ExecutionTypes { using Portal = typename ExecutionManagerType::template ExecutionTypes::Portal; using PortalConst = typename ExecutionManagerType::template ExecutionTypes::PortalConst; }; /// Constructs an empty ArrayHandle. Typically used for output or /// intermediate arrays that will be filled by a VTKm algorithm. /// VTKM_CONT ArrayHandle(); /// Copy constructor. /// /// 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 copy constructor could be /// created for all devices, and it would not be valid for all devices. /// ArrayHandle(const vtkm::cont::ArrayHandle& src); /// Move constructor. /// /// 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 move constructor could be /// created for all devices, and it would not be valid for all devices. /// ArrayHandle(vtkm::cont::ArrayHandle&& src); /// Special constructor for subclass specializations that need to set the /// initial state of the control array. When this constructor is used, it /// is assumed that the control array is valid. /// ArrayHandle(const StorageType& storage); /// Special constructor for subclass specializations that need to set the /// initial state of the control array. When this constructor is used, it /// is assumed that the control array is valid. /// ArrayHandle(StorageType&& storage); /// Destructs an empty ArrayHandle. /// /// 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. /// ~ArrayHandle(); /// \brief Copies an ArrayHandle /// VTKM_CONT vtkm::cont::ArrayHandle& operator=( const vtkm::cont::ArrayHandle& src); /// \brief Move and Assignment of an ArrayHandle /// VTKM_CONT vtkm::cont::ArrayHandle& operator=( vtkm::cont::ArrayHandle&& src); /// Like a pointer, two \c ArrayHandles are considered equal if they point /// to the same location in memory. /// VTKM_CONT bool operator==(const ArrayHandle& rhs) const { return (this->Internals == rhs.Internals); } VTKM_CONT bool operator!=(const ArrayHandle& rhs) const { return (this->Internals != rhs.Internals); } template VTKM_CONT bool operator==(const ArrayHandle&) const { return false; // different valuetype and/or storage } template VTKM_CONT bool operator!=(const ArrayHandle&) const { return true; // different valuetype and/or storage } /// Get the storage. /// VTKM_CONT StorageType& GetStorage(); /// Get the storage. /// VTKM_CONT const StorageType& GetStorage() const; /// Get the array portal of the control array. /// Since worklet invocations are asynchronous and this routine is a synchronization point, /// exceptions maybe thrown for errors from previously executed worklets. /// VTKM_CONT PortalControl GetPortalControl(); /// Get the array portal of the control array. /// Since worklet invocations are asynchronous and this routine is a synchronization point, /// exceptions maybe thrown for errors from previously executed worklets. /// VTKM_CONT PortalConstControl GetPortalConstControl() const; /// Returns the number of entries in the array. /// VTKM_CONT vtkm::Id GetNumberOfValues() const; /// \brief Allocates an array large enough to hold the given number of values. /// /// The allocation may be done on an already existing array, but can wipe out /// any data already in the array. This method can throw /// ErrorBadAllocation if the array cannot be allocated or /// ErrorBadValue if the allocation is not feasible (for example, the /// array storage is read-only). /// VTKM_CONT void Allocate(vtkm::Id numberOfValues) { this->ReleaseResourcesExecutionInternal(); this->Internals->ControlArray.Allocate(numberOfValues); this->Internals->ControlArrayValid = true; } /// \brief Reduces the size of the array without changing its values. /// /// This method allows you to resize the array without reallocating it. The /// number of entries in the array is changed to \c numberOfValues. The data /// in the array (from indices 0 to \c numberOfValues - 1) are the same, but /// \c numberOfValues must be equal or less than the preexisting size /// (returned from GetNumberOfValues). That is, this method can only be used /// to shorten the array, not lengthen. void Shrink(vtkm::Id numberOfValues); /// Releases any resources being used in the execution environment (that are /// not being shared by the control environment). /// VTKM_CONT void ReleaseResourcesExecution() { // Save any data in the execution environment by making sure it is synced // with the control environment. this->SyncControlArray(); this->ReleaseResourcesExecutionInternal(); } /// Releases all resources in both the control and execution environments. /// VTKM_CONT void ReleaseResources() { this->ReleaseResourcesExecutionInternal(); if (this->Internals->ControlArrayValid) { this->Internals->ControlArray.ReleaseResources(); this->Internals->ControlArrayValid = false; } } // clang-format off /// Prepares this array to be used as an input to an operation in the /// execution environment. If necessary, copies data to the execution /// environment. Can throw an exception if this array does not yet contain /// any data. Returns a portal that can be used in code running in the /// execution environment. /// template VTKM_CONT typename ExecutionTypes::PortalConst PrepareForInput(DeviceAdapterTag) const; // clang-format on /// Prepares (allocates) this array to be used as an output from an operation /// in the execution environment. The internal state of this class is set to /// have valid data in the execution array with the assumption that the array /// will be filled soon (i.e. before any other methods of this object are /// called). Returns a portal that can be used in code running in the /// execution environment. /// template VTKM_CONT typename ExecutionTypes::Portal PrepareForOutput( vtkm::Id numberOfValues, DeviceAdapterTag); /// Prepares this array to be used in an in-place operation (both as input /// and output) in the execution environment. If necessary, copies data to /// the execution environment. Can throw an exception if this array does not /// yet contain any data. Returns a portal that can be used in code running /// in the execution environment. /// template VTKM_CONT typename ExecutionTypes::Portal PrepareForInPlace(DeviceAdapterTag); /// Gets this array handle ready to interact with the given device. If the /// array handle has already interacted with this device, then this method /// does nothing. Although the internal state of this class can change, the /// method is declared const because logically the data does not. /// template VTKM_CONT void PrepareForDevice(DeviceAdapterTag) const; /// Synchronizes the control array with the execution array. If either the /// user array or control array is already valid, this method does nothing /// (because the data is already available in the control environment). /// Although the internal state of this class can change, the method is /// declared const because logically the data does not. /// VTKM_CONT void SyncControlArray() const; VTKM_CONT void ReleaseResourcesExecutionInternal() { if (this->Internals->ExecutionArrayValid) { this->Internals->ExecutionArray->ReleaseResources(); this->Internals->ExecutionArrayValid = false; } } /// Returns the DeviceAdapterId for the current device. If there is no device /// with an up-to-date copy of the data, VTKM_DEVICE_ADAPTER_UNDEFINED is /// returned. VTKM_CONT DeviceAdapterId GetDeviceAdapterId() const { return this->Internals->ExecutionArrayValid ? this->Internals->ExecutionArray->GetDeviceAdapterId() : DeviceAdapterIdUndefined{}; } struct VTKM_ALWAYS_EXPORT InternalStruct { mutable StorageType ControlArray; mutable bool ControlArrayValid; mutable std::unique_ptr< vtkm::cont::internal::ArrayHandleExecutionManagerBase> ExecutionArray; mutable bool ExecutionArrayValid; }; VTKM_CONT ArrayHandle(const std::shared_ptr& i) : Internals(i) { } std::shared_ptr Internals; }; /// A convenience function for creating an ArrayHandle from a standard C array. /// template VTKM_CONT vtkm::cont::ArrayHandle make_ArrayHandle(const T* array, vtkm::Id length, vtkm::CopyFlag copy = vtkm::CopyFlag::Off) { using ArrayHandleType = vtkm::cont::ArrayHandle; if (copy == vtkm::CopyFlag::On) { ArrayHandleType handle; handle.Allocate(length); std::copy( array, array + length, vtkm::cont::ArrayPortalToIteratorBegin(handle.GetPortalControl())); return handle; } else { using StorageType = vtkm::cont::internal::Storage; return ArrayHandleType(StorageType(array, length)); } } /// A convenience function for creating an ArrayHandle from an std::vector. /// template VTKM_CONT vtkm::cont::ArrayHandle make_ArrayHandle( const std::vector& array, vtkm::CopyFlag copy = vtkm::CopyFlag::Off) { if (!array.empty()) { return make_ArrayHandle(&array.front(), static_cast(array.size()), copy); } else { // Vector empty. Just return an empty array handle. return vtkm::cont::ArrayHandle(); } } namespace detail { template VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle_Value(const T& value, std::ostream& out, vtkm::VecTraitsTagSingleComponent) { out << value; } VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle_Value(vtkm::UInt8 value, std::ostream& out, vtkm::VecTraitsTagSingleComponent) { out << static_cast(value); } VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle_Value(vtkm::Int8 value, std::ostream& out, vtkm::VecTraitsTagSingleComponent) { out << static_cast(value); } template VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle_Value( const T& value, std::ostream& out, vtkm::VecTraitsTagMultipleComponents) { using Traits = vtkm::VecTraits; using ComponentType = typename Traits::ComponentType; using IsVecOfVec = typename vtkm::VecTraits::HasMultipleComponents; vtkm::IdComponent numComponents = Traits::GetNumberOfComponents(value); out << "("; printSummary_ArrayHandle_Value(Traits::GetComponent(value, 0), out, IsVecOfVec()); for (vtkm::IdComponent index = 1; index < numComponents; ++index) { out << ","; printSummary_ArrayHandle_Value(Traits::GetComponent(value, index), out, IsVecOfVec()); } out << ")"; } template VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle_Value( const vtkm::Pair& value, std::ostream& out, vtkm::VecTraitsTagSingleComponent) { out << "{"; printSummary_ArrayHandle_Value( value.first, out, typename vtkm::VecTraits::HasMultipleComponents()); out << ","; printSummary_ArrayHandle_Value( value.second, out, typename vtkm::VecTraits::HasMultipleComponents()); out << "}"; } } // namespace detail template VTKM_NEVER_EXPORT VTKM_CONT inline void printSummary_ArrayHandle( const vtkm::cont::ArrayHandle& array, std::ostream& out, bool full = false) { using ArrayType = vtkm::cont::ArrayHandle; using PortalType = typename ArrayType::PortalConstControl; using IsVec = typename vtkm::VecTraits::HasMultipleComponents; vtkm::Id sz = array.GetNumberOfValues(); out << "valueType=" << typeid(T).name() << " storageType=" << typeid(StorageT).name() << " numValues=" << sz << " ["; PortalType portal = array.GetPortalConstControl(); if (full || sz <= 7) { for (vtkm::Id i = 0; i < sz; i++) { detail::printSummary_ArrayHandle_Value(portal.Get(i), out, IsVec()); if (i != (sz - 1)) { out << " "; } } } else { detail::printSummary_ArrayHandle_Value(portal.Get(0), out, IsVec()); out << " "; detail::printSummary_ArrayHandle_Value(portal.Get(1), out, IsVec()); out << " "; detail::printSummary_ArrayHandle_Value(portal.Get(2), out, IsVec()); out << " ... "; detail::printSummary_ArrayHandle_Value(portal.Get(sz - 3), out, IsVec()); out << " "; detail::printSummary_ArrayHandle_Value(portal.Get(sz - 2), out, IsVec()); out << " "; detail::printSummary_ArrayHandle_Value(portal.Get(sz - 1), out, IsVec()); } out << "]\n"; } } } //namespace vtkm::cont #include #include #include #ifndef vtkm_cont_ArrayHandle_cxx namespace vtkm { namespace cont { #define _VTKM_ARRAYHANDLE_EXPORT(Type) \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle; \ extern template class VTKM_CONT_TEMPLATE_EXPORT \ ArrayHandle, StorageTagBasic>; \ extern template class VTKM_CONT_TEMPLATE_EXPORT \ ArrayHandle, StorageTagBasic>; \ extern template class VTKM_CONT_TEMPLATE_EXPORT ArrayHandle, StorageTagBasic>; _VTKM_ARRAYHANDLE_EXPORT(char) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Int8) _VTKM_ARRAYHANDLE_EXPORT(vtkm::UInt8) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Int16) _VTKM_ARRAYHANDLE_EXPORT(vtkm::UInt16) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Int32) _VTKM_ARRAYHANDLE_EXPORT(vtkm::UInt32) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Int64) _VTKM_ARRAYHANDLE_EXPORT(vtkm::UInt64) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Float32) _VTKM_ARRAYHANDLE_EXPORT(vtkm::Float64) #undef _VTKM_ARRAYHANDLE_EXPORT } } // end vtkm::cont #endif // !vtkm_cont_ArrayHandle_cxx #endif //vtk_m_cont_ArrayHandle_h