Add Fill and AllocateAndFill to ArrayHandle

These allow you to create an `ArrayHandle` filled with an inital value
without having to compile code for the device.
This commit is contained in:
Kenneth Moreland 2021-12-06 09:25:03 -07:00
parent 0cf996f410
commit 926164049f
6 changed files with 127 additions and 0 deletions

@ -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.

@ -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).
///

@ -52,6 +52,16 @@ public:
static_cast<vtkm::BufferSizeType>(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<vtkm::BufferSizeType>(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)

@ -162,6 +162,21 @@ public:
static_cast<vtkm::Id>(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<vtkm::BufferSizeType>(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)

@ -11,6 +11,7 @@
#define vtk_m_cont_ArrayHandleStride_h
#include <vtkm/cont/ArrayHandleBasic.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/internal/ArrayPortalBasic.h>
@ -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)

@ -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<vtkm::cont::internal::Storage<T, S>>
#define VTKM_STORAGE_NO_WRITE_PORTAL \
using WritePortalType = vtkm::internal::ArrayPortalDummy< \
typename vtkm::cont::internal::StorageTraits<Storage>::ValueType>; \
VTKM_CONT static void Fill( \
vtkm::cont::internal::Buffer*, \
const typename vtkm::cont::internal::StorageTraits<Storage>::ValueType&, \
vtkm::Id, \
vtkm::cont::Token&) \
{ \
throw vtkm::cont::ErrorBadAllocation( \
"Cannot write to arrays with storage type of " + \
vtkm::cont::TypeToString<typename vtkm::cont::internal::StorageTraits<Storage>::Tag>()); \
} \
VTKM_CONT static WritePortalType CreateWritePortal( \
vtkm::cont::internal::Buffer*, vtkm::cont::DeviceAdapterId, vtkm::cont::Token&) \
{ \