Add ability to fill values in a Buffer

This is in preparation to add a method to `ArrayHandle` that allows
initializing fill values.
This commit is contained in:
Kenneth Moreland 2021-11-22 14:52:41 -07:00
parent 2eabba3366
commit 0cf996f410
4 changed files with 140 additions and 1 deletions

@ -163,7 +163,6 @@ set(sources
ErrorBadType.cxx
FieldRangeCompute.cxx
FieldRangeGlobalCompute.cxx
internal/Buffer.cxx
internal/DeviceAdapterMemoryManager.cxx
internal/DeviceAdapterMemoryManagerShared.cxx
internal/RuntimeDeviceConfiguration.cxx
@ -193,6 +192,7 @@ set(device_sources
ColorTable.cxx
ConvertNumComponentsToOffsets.cxx
Field.cxx
internal/Buffer.cxx
MergePartitionedDataSet.cxx
PointLocatorSparseGrid.cxx
RuntimeDeviceInformation.cxx

@ -10,14 +10,18 @@
#include <vtkm/internal/Assume.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/ErrorBadDevice.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/RuntimeDeviceInformation.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/cont/internal/Buffer.h>
#include <vtkm/cont/internal/DeviceAdapterMemoryManager.h>
#include <vtkm/exec/FunctorBase.h>
#include <condition_variable>
#include <cstring>
#include <deque>
@ -157,6 +161,75 @@ struct MetaDataManager
}
};
template <typename T>
struct FillFunctor : vtkm::exec::FunctorBase
{
T* TargetArray;
const T* SourceValues;
vtkm::Id NumSourceValues;
VTKM_CONT FillFunctor(void* targetArray,
const void* sourceValues,
vtkm::BufferSizeType sourceValuesSize,
vtkm::BufferSizeType start)
: TargetArray(reinterpret_cast<T*>(targetArray))
, SourceValues(reinterpret_cast<const T*>(sourceValues))
, NumSourceValues(sourceValuesSize / sizeof(T))
{
VTKM_ASSERT((sourceValuesSize % sizeof(T)) == 0);
this->TargetArray += start / sizeof(T);
}
VTKM_EXEC void operator()(vtkm::Id index) const
{
T* target = this->TargetArray + (index * this->NumSourceValues);
for (vtkm::Id sourceIndex = 0; sourceIndex < this->NumSourceValues; ++sourceIndex)
{
*target = this->SourceValues[sourceIndex];
++target;
}
}
};
template <typename Device>
void FillBuffer(const vtkm::cont::internal::Buffer& target,
const vtkm::cont::internal::Buffer& source,
vtkm::BufferSizeType start,
Device device,
vtkm::cont::Token& token)
{
void* targetPointer = target.WritePointerDevice(device, token);
const void* sourcePointer = source.ReadPointerDevice(device, token);
// Get after target locked with token.
vtkm::BufferSizeType targetSize = target.GetNumberOfBytes();
vtkm::BufferSizeType sourceSize = source.GetNumberOfBytes();
if (targetSize <= start)
{
return;
}
VTKM_ASSERT((targetSize % sourceSize) == 0);
VTKM_ASSERT((start % sourceSize) == 0);
vtkm::Id numSources = (targetSize - start) / sourceSize;
if ((sourceSize >= 8) && ((sourceSize % 8) == 0))
{
vtkm::cont::DeviceAdapterAlgorithm<Device>::Schedule(
FillFunctor<vtkm::UInt64>{ targetPointer, sourcePointer, sourceSize, start }, numSources);
}
else if ((sourceSize >= 4) && ((sourceSize % 4) == 0))
{
vtkm::cont::DeviceAdapterAlgorithm<Device>::Schedule(
FillFunctor<vtkm::UInt32>{ targetPointer, sourcePointer, sourceSize, start }, numSources);
}
else
{
vtkm::cont::DeviceAdapterAlgorithm<Device>::Schedule(
FillFunctor<vtkm::UInt8>{ targetPointer, sourcePointer, sourceSize, start }, numSources);
}
}
} // anonymous namespace
namespace vtkm
@ -1017,6 +1090,44 @@ vtkm::cont::internal::BufferInfo Buffer::GetDeviceBufferInfo(
throw vtkm::cont::ErrorBadDevice("Called Buffer::GetDeviceBufferInfo with invalid device");
}
}
void Buffer::Fill(const void* source,
vtkm::BufferSizeType sourceSize,
vtkm::BufferSizeType start,
vtkm::cont::Token& token) const
{
vtkm::cont::internal::Buffer sourceBuffer;
sourceBuffer.Reset(vtkm::cont::internal::BufferInfo(
vtkm::cont::DeviceAdapterTagUndefined{},
const_cast<void*>(source),
const_cast<void*>(source),
sourceSize,
[](void*) {},
[](void*&, void*&, vtkm::BufferSizeType, vtkm::BufferSizeType) {}));
// First, try setting on any device that already has the data.
bool success = vtkm::cont::TryExecute([&](auto device) {
if (this->IsAllocatedOnDevice(device))
{
FillBuffer(*this, sourceBuffer, start, device, token);
return true;
}
else
{
return false;
}
});
if (!success)
{
// Likely the data was not on any device. Fill on any device.
vtkm::cont::TryExecute([&](auto device) {
FillBuffer(*this, sourceBuffer, start, device, token);
return true;
});
}
}
}
}
} // namespace vtkm::cont::internal

@ -310,6 +310,20 @@ public:
VTKM_CONT vtkm::cont::internal::TransferredBuffer TakeDeviceBufferOwnership(
vtkm::cont::DeviceAdapterId device);
/// \brief Fill up the buffer with particular values.
///
/// Given a short `source` C array (defined on the host), sets all values in the buffer
/// to that source. The `sourceSize`, in bytes, is also specified. You also specify an
/// offset to where the fill should `start`. Values before the `start` are not affected.
///
/// Both the size of the buffer (i.e. `GetNumberOfBytes`) and the `start` must be
/// divisible by `sourceSize`.
///
VTKM_CONT void Fill(const void* source,
vtkm::BufferSizeType sourceSize,
vtkm::BufferSizeType start,
vtkm::cont::Token& token) const;
VTKM_CONT bool operator==(const vtkm::cont::internal::Buffer& rhs) const
{
return (this->Internals == rhs.Internals);

@ -186,6 +186,20 @@ void DoTest()
VTKM_TEST_ASSERT(buffer.IsAllocatedOnHost());
VTKM_TEST_ASSERT(!buffer.IsAllocatedOnDevice(device));
std::cout << "Fill end of buffer" << std::endl;
{
vtkm::cont::Token token;
T fillValue = 1.234f;
buffer.Fill(
&fillValue, static_cast<vtkm::BufferSizeType>(sizeof(fillValue)), BUFFER_SIZE / 2, token);
CheckPortal(MakePortal(buffer.ReadPointerHost(token), ARRAY_SIZE / 2));
const T* array = reinterpret_cast<const T*>(buffer.ReadPointerHost(token));
for (vtkm::Id index = (ARRAY_SIZE / 2); index < (ARRAY_SIZE * 2); ++index)
{
VTKM_TEST_ASSERT(array[index] == fillValue);
}
}
std::cout << "Reset with device data" << std::endl;
std::vector<T> v(ARRAY_SIZE);
void* devicePointer = v.data();