diff --git a/docs/changelog/allocate-and-fill.md b/docs/changelog/allocate-and-fill.md new file mode 100644 index 000000000..ebf5226c6 --- /dev/null +++ b/docs/changelog/allocate-and-fill.md @@ -0,0 +1,16 @@ +# ArrayHandle::Fill + +`ArrayHandle` has a new method named `Fill`. As the name would suggest, the +`Fill` method initializes all elements in the array to a specified value. +In addition to being more convenient than calling `Algorithm::Fill` or +`ArrayCopy` with a constant array, the `ArrayHandle::Fill` can be used +without using a device adapter. + +Calling `Fill` directly requires the `ArrayHandle` to first be allocated to +the appropriate size. The `ArrayHandle` now also has a new member named +`AllocateAndFill`. As the name would suggest, this method resizes the array +and then fills it with the specified value. Another feature this method has +over calling `Allocate` and `Fill` separately is if you call +`AllocateAndFill` with `vtkm::CopyFlag::On`, it will fill only the extended +portion of the array. + diff --git a/vtkm/cont/ArrayHandle.h b/vtkm/cont/ArrayHandle.h index c4fdb3165..dd4e5dfa7 100644 --- a/vtkm/cont/ArrayHandle.h +++ b/vtkm/cont/ArrayHandle.h @@ -528,12 +528,75 @@ public: } ///@} + ///@{ + /// \brief Allocates an array and fills it with an initial value. + /// + /// `AllocateAndFill` behaves similar to `Allocate` except that after allocation it fills + /// the array with a given `fillValue`. This method is convenient when you wish to initialize + /// the array. + /// + /// If the `preserve` flag is `vtkm::CopyFlag::On`, then any data that existed before the + /// call to `AllocateAndFill` will remain after the call (assuming the new array size is + /// large enough). If the array size is expanded, then the new values at the end will be + /// filled. + /// + /// If the `preserve` flag is `vtkm::CopyFlag::Off` (the default), the entire array is + /// filled with the given `fillValue`. + /// + VTKM_CONT void AllocateAndFill(vtkm::Id numberOfValues, + const ValueType& fillValue, + vtkm::CopyFlag preserve, + vtkm::cont::Token& token) const + { + // Note that there is a slight potential for a race condition here. It is possible for someone + // else to resize the array in between getting the startIndex and locking the array in the + // Allocate call. If there really are 2 threads trying to allocate this array at the same time, + // you probably have bigger problems than filling at the wrong index. + vtkm::Id startIndex = (preserve == vtkm::CopyFlag::On) ? this->GetNumberOfValues() : 0; + + this->Allocate(numberOfValues, preserve, token); + + if (startIndex < numberOfValues) + { + this->Fill(fillValue, startIndex, token); + } + } + + VTKM_CONT void AllocateAndFill(vtkm::Id numberOfValues, + const ValueType& fillValue, + vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const + { + vtkm::cont::Token token; + this->AllocateAndFill(numberOfValues, fillValue, preserve, token); + } + ///@} + VTKM_DEPRECATED(1.6, "Use Allocate(n, vtkm::CopyFlag::On) instead of Shrink(n).") VTKM_CONT void Shrink(vtkm::Id numberOfValues) { this->Allocate(numberOfValues, vtkm::CopyFlag::On); } + /// @{ + /// \brief Fills the array with a given value. + /// + /// After calling this method, every entry in the array from `startIndex` to the + /// end of the array is set to `fillValue`. If `startIndex` is not specified, then + /// every entry is set to the fill value. + /// + VTKM_CONT void Fill(const ValueType& fillValue, + vtkm::Id startIndex, + vtkm::cont::Token& token) const + { + StorageType::Fill(this->GetBuffers(), fillValue, startIndex, token); + } + VTKM_CONT void Fill(const ValueType& fillValue, vtkm::Id startIndex = 0) const + { + vtkm::cont::Token token; + this->Fill(fillValue, startIndex, token); + } + /// @} + /// Releases any resources being used in the execution environment (that are /// not being shared by the control environment). /// diff --git a/vtkm/cont/ArrayHandleBasic.h b/vtkm/cont/ArrayHandleBasic.h index 7ce67c771..a7d31d838 100644 --- a/vtkm/cont/ArrayHandleBasic.h +++ b/vtkm/cont/ArrayHandleBasic.h @@ -52,6 +52,16 @@ public: static_cast(sizeof(T))); } + VTKM_CONT static void Fill(vtkm::cont::internal::Buffer* buffers, + const T& fillValue, + vtkm::Id startIndex, + vtkm::cont::Token& token) + { + constexpr vtkm::BufferSizeType fillValueSize = + static_cast(sizeof(fillValue)); + buffers[0].Fill(&fillValue, fillValueSize, startIndex * fillValueSize, token); + } + VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) diff --git a/vtkm/cont/ArrayHandleSOA.h b/vtkm/cont/ArrayHandleSOA.h index 66beabceb..22cee8d2b 100644 --- a/vtkm/cont/ArrayHandleSOA.h +++ b/vtkm/cont/ArrayHandleSOA.h @@ -162,6 +162,21 @@ public: static_cast(sizeof(ComponentType)); } + VTKM_CONT static void Fill(vtkm::cont::internal::Buffer* buffers, + const ValueType& fillValue, + vtkm::Id startIndex, + vtkm::cont::Token& token) + { + constexpr vtkm::BufferSizeType sourceSize = + static_cast(sizeof(ComponentType)); + vtkm::BufferSizeType startByte = startIndex * sourceSize; + for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex) + { + ComponentType source = fillValue[componentIndex]; + buffers[componentIndex].Fill(&source, sourceSize, startByte, token); + } + } + VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) diff --git a/vtkm/cont/ArrayHandleStride.h b/vtkm/cont/ArrayHandleStride.h index 73b3d612d..68c65407e 100644 --- a/vtkm/cont/ArrayHandleStride.h +++ b/vtkm/cont/ArrayHandleStride.h @@ -11,6 +11,7 @@ #define vtk_m_cont_ArrayHandleStride_h #include +#include #include @@ -174,6 +175,11 @@ public: return GetInfo(buffers).NumberOfValues; } + VTKM_CONT static void Fill(vtkm::cont::internal::Buffer*, const T&, vtkm::Id, vtkm::cont::Token&) + { + throw vtkm::cont::ErrorBadType("Fill not supported for ArrayHandleStride."); + } + VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) diff --git a/vtkm/cont/Storage.h b/vtkm/cont/Storage.h index a91e8055f..6135d1f18 100644 --- a/vtkm/cont/Storage.h +++ b/vtkm/cont/Storage.h @@ -134,6 +134,13 @@ public: /// \brief Returns the number of entries allocated in the array. VTKM_CONT static vtkm::Id GetNumberOfValues(const vtkm::cont::internal::Buffer* buffers); + /// \brief Fills the array with the given value starting at the given index. + /// + VTKM_CONT static void Fill(vtkm::cont::internal::Buffer* buffers, + const ValueType& fillValue, + vtkm::Id startIndex, + vtkm::cont::Token& token); + /// \brief Create a read-only portal on the specified device. /// VTKM_CONT static ReadPortalType CreateReadPortal(const vtkm::cont::internal::Buffer* buffers, @@ -181,6 +188,16 @@ struct StorageTraits> #define VTKM_STORAGE_NO_WRITE_PORTAL \ using WritePortalType = vtkm::internal::ArrayPortalDummy< \ typename vtkm::cont::internal::StorageTraits::ValueType>; \ + VTKM_CONT static void Fill( \ + vtkm::cont::internal::Buffer*, \ + const typename vtkm::cont::internal::StorageTraits::ValueType&, \ + vtkm::Id, \ + vtkm::cont::Token&) \ + { \ + throw vtkm::cont::ErrorBadAllocation( \ + "Cannot write to arrays with storage type of " + \ + vtkm::cont::TypeToString::Tag>()); \ + } \ VTKM_CONT static WritePortalType CreateWritePortal( \ vtkm::cont::internal::Buffer*, vtkm::cont::DeviceAdapterId, vtkm::cont::Token&) \ { \