Add ability to resize ArrayHandleStride

This feature enables the ability to anonomously create an array (such as
with `UnknownArrayHandle::NewInstance()`) and then use that as an output
array. Although resizing `ArrayHandleStride` is a little wonky, it
allows worklets to resize them after creation rather than having to know
what size to make and allocating the array.
This commit is contained in:
Kenneth Moreland 2023-01-17 23:10:37 -06:00
parent 146b051436
commit 2061e95efe
3 changed files with 104 additions and 4 deletions

@ -0,0 +1,18 @@
# Added ability to resize strided arrays from ArrayExtractComponent
Previously, it was not possible to resize an `ArrayHandleStride` because
the operation is a bit ambiguous. The actual array is likely to be padded
by some amount, and there could be an unknown amount of space skipped at
the beginning.
However, there is a good reason to want to resize `ArrayHandleStride`. This
is the array used to implement the `ArrayExtractComponent` feature, and
this in turn is used when extracting arrays from an `UnknownArrayHandle`
whether independent or as an `ArrayHandleRecombineVec`.
The problem really happens when you create an array of an unknown type in
an `UnknownArrayHandle` (such as with `NewInstance`) and then use that as
an output to a worklet. Sure, you could use `ArrayHandle::Allocate` to
resize before getting the array, but that is awkward for programers.
Instead, allow the extracted arrays to be resized as normal output arrays
would be.

@ -158,8 +158,6 @@ class VTKM_ALWAYS_EXPORT Storage<T, vtkm::cont::StorageTagStride>
using StrideInfo = vtkm::internal::ArrayStrideInfo;
public:
VTKM_STORAGE_NO_RESIZE;
using ReadPortalType = vtkm::internal::ArrayPortalStrideRead<T>;
using WritePortalType = vtkm::internal::ArrayPortalStrideWrite<T>;
@ -174,6 +172,85 @@ public:
return GetInfo(buffers).NumberOfValues;
}
VTKM_CONT static void ResizeBuffers(vtkm::Id numValues,
const std::vector<vtkm::cont::internal::Buffer>& buffers,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token)
{
StrideInfo& info = GetInfo(buffers);
if (info.NumberOfValues == numValues)
{
// Array resized to current size. Don't need to do anything.
return;
}
// Find the end index after dealing with the divsor and modulo.
auto lengthDivMod = [info](vtkm::Id length) -> vtkm::Id {
vtkm::Id resultLength = ((length - 1) / info.Divisor) + 1;
if ((info.Modulo > 0) && (info.Modulo < resultLength))
{
resultLength = info.Modulo;
}
return resultLength;
};
vtkm::Id lastStridedIndex = lengthDivMod(numValues);
vtkm::Id originalStride;
vtkm::Id originalOffset;
if (info.Stride > 0)
{
originalStride = info.Stride;
originalOffset = info.Offset;
}
else
{
// The stride is negative, which means we are counting backward. Here we have to be careful
// about the offset, which should move to push to the end of the array. We also need to
// be careful about multiplying by the stride.
originalStride = -info.Stride;
vtkm::Id originalSize = lengthDivMod(info.NumberOfValues);
// Because the stride is negative, we expect the offset to be at the end of the array.
// We will call the "real" offset the distance from that end.
originalOffset = originalSize - info.Offset - 1;
}
// If the offset is more than the stride, that means there are values skipped at the
// beginning of the array, and it is impossible to know exactly how many. In this case,
// we cannot know how to resize. (If this is an issue, we will have to change
// `ArrayHandleStride` to take resizing parameters.)
if (originalOffset >= originalStride)
{
if (numValues == 0)
{
// Array resized to zero. This can happen when releasing resources.
// Should we try to clear out the buffers, or avoid that for messing up shared buffers?
return;
}
throw vtkm::cont::ErrorBadAllocation(
"Cannot resize stride array with offset greater than stride (start of stride unknown).");
}
// lastIndex should be the index in the source array after each stride block. Assuming the
// offset is inside the first stride, this should be the end of the array regardless of
// offset.
vtkm::Id lastIndex = lastStridedIndex * originalStride;
buffers[1].SetNumberOfBytes(
vtkm::internal::NumberOfValuesToNumberOfBytes<T>(lastIndex), preserve, token);
info.NumberOfValues = numValues;
if (info.Stride < 0)
{
// As described above, when the stride is negative, we are counting backward. This means
// that the offset is actually relative to the end, so we need to adjust it to the new
// end of the array.
info.Offset = lastIndex - originalOffset - 1;
}
}
VTKM_CONT static void Fill(const std::vector<vtkm::cont::internal::Buffer>&,
const T&,
vtkm::Id,

@ -73,7 +73,6 @@ void CheckOutputArray(const vtkm::cont::ArrayHandle<T, S>& originalArray)
//vtkm::cont::printSummary_ArrayHandle(originalArray, std::cout);
vtkm::cont::ArrayHandle<T, S> outputArray;
outputArray.Allocate(originalArray.GetNumberOfValues());
using FlatVec = vtkm::VecFlat<T>;
using ComponentType = typename FlatVec::ComponentType;
@ -84,6 +83,7 @@ void CheckOutputArray(const vtkm::cont::ArrayHandle<T, S>& originalArray)
vtkm::cont::ArrayExtractComponent(originalArray, numComponents - componentId - 1);
vtkm::cont::ArrayHandleStride<ComponentType> outComponentArray =
vtkm::cont::ArrayExtractComponent(outputArray, componentId, vtkm::CopyFlag::Off);
outComponentArray.Allocate(originalArray.GetNumberOfValues());
auto inPortal = inComponentArray.ReadPortal();
auto outPortal = outComponentArray.WritePortal();
@ -173,7 +173,12 @@ void DoTest()
auto compositeArray = vtkm::cont::make_ArrayHandleCompositeVector(array0, array1);
CheckOutputArray(compositeArray);
CheckOutputArray(vtkm::cont::make_ArrayHandleExtractComponent(compositeArray, 1));
// Note that when the extracted component array gets allocated, it only allocates the
// array it was given. This is a weird case when using `ArrayHandleExtractComponent`
// on something that has multiple arrays as input. It works fine if all components get
// extracted and updated, but can cause issues if only one is resized. In this case
// just test the input.
CheckInputArray(vtkm::cont::make_ArrayHandleExtractComponent(compositeArray, 1));
}
{