Add user guide chapter on memory layout in ArrayHandles

This commit is contained in:
Kenneth Moreland 2024-05-11 11:30:24 -05:00
parent e70f0cb61d
commit 8e570c8b64
11 changed files with 1256 additions and 57 deletions

@ -2,11 +2,11 @@
Basic Array Handles
==============================
.. index:: array handle
.. index:: array handle; basic
:chapref:`dataset:Data Sets` describes the basic data sets used by |VTKm|.
This chapter dives deeper into how |VTKm| represents data.
Ultimately, data structures like \vtkmcont{DataSet} can be broken down into arrays of numbers.
Ultimately, data structures like :class:`vtkm::cont::DataSet` can be broken down into arrays of numbers.
Arrays in |VTKm| are managed by a unit called an *array handle*.
An array handle, which is implemented with the :class:`vtkm::cont::ArrayHandle` class, manages an array of data that can be accessed or manipulated by |VTKm| algorithms.
@ -43,6 +43,8 @@ This is convenient for creating arrays used as the output for algorithms.
Chapter \ref{chap:AccessingAllocatingArrays} describes in detail how to allocate memory and access data in an :class:`vtkm::cont::ArrayHandle`.
However, you can use the :func:`vtkm::cont::make_ArrayHandle` function for a simplified way to create an :class:`vtkm::cont::ArrayHandle` with data.
.. todo:: Update chapter reference above. Also consider moving the access/allocation chapter earlier.
:func:`vtkm::cont::make_ArrayHandle` has many forms.
An easy form to use takes an initializer list and creates a basic :class:`vtkm::cont::ArrayHandle` with it.
This allows you to create a short :class:`vtkm::cont::ArrayHandle` from literals.
@ -156,7 +158,7 @@ The Hidden Second Template Parameter
double: array handle; storage
We have already seen that :class:`vtkm::cont::ArrayHandle` is a templated class with the template parameter indicating the type of values stored in the array.
However, :class:`vtkm::cont::ArrayHandle` has a second hidden parameter that indicates the \keyterm{storage} of the array.
However, :class:`vtkm::cont::ArrayHandle` has a second hidden parameter that indicates the _storage_ of the array.
We have so far been able to ignore this second template parameter because |VTKm| will assign a default storage for us that will store the data in a basic array.
Changing the storage of an :class:`vtkm::cont::ArrayHandle` lets us do many weird and wonderful things.

@ -10,6 +10,7 @@
set(examples
GuideExampleArrayHandle.cxx
GuideExampleArrayHandleRuntimeVec.cxx
GuideExampleCellShapes.cxx
GuideExampleColorTables.cxx
GuideExampleCoreDataTypes.cxx
@ -35,6 +36,7 @@ set(examples_device
GuideExampleFilterDataSetWithField.cxx
GuideExampleGenerateMeshConstantShape.cxx
GuideExampleSimpleAlgorithm.cxx
GuideExampleUnknownArrayHandle.cxx
GuideExampleUseWorkletMapField.cxx
GuideExampleUseWorkletPointNeighborhood.cxx
GuideExampleUseWorkletReduceByKey.cxx

@ -12,6 +12,7 @@
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleSOA.h>
#include <vtkm/cont/ArrayPortalToIterators.h>
#include <vtkm/cont/ArrayRangeCompute.h>
#include <vtkm/cont/DeviceAdapter.h>
@ -517,6 +518,64 @@ void TestExecutionPortalsExample()
CheckArrayValues(outputArray, 2);
}
////
//// BEGIN-EXAMPLE GetArrayPointer
////
void LegacyFunction(const int* data);
void UseArrayWithLegacy(const vtkm::cont::ArrayHandle<vtkm::Int32> array)
{
vtkm::cont::ArrayHandleBasic<vtkm::Int32> basicArray = array;
vtkm::cont::Token token; // Token prevents array from changing while in scope.
const int* cArray = basicArray.GetReadPointer(token);
LegacyFunction(cArray);
// When function returns, token goes out of scope and array can be modified.
}
////
//// END-EXAMPLE GetArrayPointer
////
void LegacyFunction(const int* data)
{
std::cout << "Got data: " << data[0] << std::endl;
}
void TryUseArrayWithLegacy()
{
vtkm::cont::ArrayHandle<vtkm::Int32> array;
array.Allocate(50);
SetPortal(array.WritePortal());
UseArrayWithLegacy(array);
}
void ArrayHandleFromComponents()
{
////
//// BEGIN-EXAMPLE ArrayHandleSOAFromComponentArrays
////
vtkm::cont::ArrayHandle<vtkm::FloatDefault> component1;
vtkm::cont::ArrayHandle<vtkm::FloatDefault> component2;
vtkm::cont::ArrayHandle<vtkm::FloatDefault> component3;
// Fill component arrays...
//// PAUSE-EXAMPLE
component1.AllocateAndFill(50, 0);
component2.AllocateAndFill(50, 1);
component3.AllocateAndFill(50, 2);
//// RESUME-EXAMPLE
vtkm::cont::ArrayHandleSOA<vtkm::Vec3f> soaArray =
vtkm::cont::make_ArrayHandleSOA(component1, component2, component3);
////
//// END-EXAMPLE ArrayHandleSOAFromComponentArrays
////
auto portal = soaArray.ReadPortal();
for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); ++index)
{
VTKM_TEST_ASSERT(portal.Get(index) == vtkm::Vec3f{ 0, 1, 2 });
}
}
void Test()
{
BasicConstruction();
@ -528,6 +587,8 @@ void Test()
TestArrayPortalVectors();
TestControlPortalsExample();
TestExecutionPortalsExample();
TryUseArrayWithLegacy();
ArrayHandleFromComponents();
}
} // anonymous namespace

@ -0,0 +1,139 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/ArrayHandleRuntimeVec.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/UnknownArrayHandle.h>
#include <vtkm/TypeList.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
constexpr vtkm::Id ARRAY_SIZE = 10;
void ReadArray(std::vector<float>& data, int& numComponents)
{
numComponents = 3;
data.resize(static_cast<std::size_t>(ARRAY_SIZE * numComponents));
std::fill(data.begin(), data.end(), 1.23f);
}
////
//// BEGIN-EXAMPLE GroupWithRuntimeVec
////
void ReadArray(std::vector<float>& data, int& numComponents);
vtkm::cont::UnknownArrayHandle LoadData()
{
// Read data from some external source where the vector size is determined at runtime.
std::vector<vtkm::Float32> data;
int numComponents;
ReadArray(data, numComponents);
// Resulting ArrayHandleRuntimeVec gets wrapped in an UnknownArrayHandle
return vtkm::cont::make_ArrayHandleRuntimeVecMove(
static_cast<vtkm::IdComponent>(numComponents), std::move(data));
}
void UseVecArray(const vtkm::cont::UnknownArrayHandle& array)
{
using ExpectedArrayType = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
if (!array.CanConvert<ExpectedArrayType>())
{
throw vtkm::cont::ErrorBadType("Array unexpected type.");
}
ExpectedArrayType concreteArray = array.AsArrayHandle<ExpectedArrayType>();
// Do something with concreteArray...
//// PAUSE-EXAMPLE
VTKM_TEST_ASSERT(concreteArray.GetNumberOfValues() == ARRAY_SIZE);
//// RESUME-EXAMPLE
}
void LoadAndRun()
{
// Load data in a routine that does not know component size until runtime.
vtkm::cont::UnknownArrayHandle array = LoadData();
// Use the data in a method that requires an array of static size.
// This will work as long as the `Vec` size matches correctly (3 in this case).
UseVecArray(array);
}
////
//// END-EXAMPLE GroupWithRuntimeVec
////
template<typename T>
void WriteData(const T*, std::size_t, int)
{
// Dummy function for GetRuntimeVec.
}
////
//// BEGIN-EXAMPLE GetRuntimeVec
////
template<typename T>
void WriteData(const T* data, std::size_t size, int numComponents);
void WriteVTKmArray(const vtkm::cont::UnknownArrayHandle& array)
{
bool writeSuccess = false;
auto doWrite = [&](auto componentType) {
using ComponentType = decltype(componentType);
using VecArrayType = vtkm::cont::ArrayHandleRuntimeVec<ComponentType>;
if (array.CanConvert<VecArrayType>())
{
// Get the array as a runtime Vec.
VecArrayType runtimeVecArray = array.AsArrayHandle<VecArrayType>();
// Get the component array.
vtkm::cont::ArrayHandleBasic<ComponentType> componentArray =
runtimeVecArray.GetComponentsArray();
// Use the general function to write the data.
WriteData(componentArray.GetReadPointer(),
componentArray.GetNumberOfValues(),
runtimeVecArray.GetNumberOfComponentsFlat());
writeSuccess = true;
}
};
// Figure out the base component type, retrieve the data (regardless
// of vec size), and write out the data.
vtkm::ListForEach(doWrite, vtkm::TypeListBaseC{});
}
////
//// END-EXAMPLE GetRuntimeVec
////
void DoWriteTest()
{
vtkm::cont::ArrayHandle<vtkm::Vec3f> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
WriteVTKmArray(array);
}
void Test()
{
LoadAndRun();
DoWriteTest();
}
} // anonymous namespace
int GuideExampleArrayHandleRuntimeVec(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(Test, argc, argv);
}

@ -0,0 +1,515 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleGroupVec.h>
#include <vtkm/cont/ArrayHandleIndex.h>
#include <vtkm/cont/ArrayHandleMultiplexer.h>
#include <vtkm/cont/ArrayRangeCompute.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/Invoker.h>
#include <vtkm/cont/UncertainArrayHandle.h>
#include <vtkm/cont/UnknownArrayHandle.h>
#include <vtkm/cont/internal/StorageError.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/VecTraits.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
constexpr vtkm::Id ARRAY_SIZE = 10;
////
//// BEGIN-EXAMPLE CreateUnknownArrayHandle
////
VTKM_CONT
vtkm::cont::UnknownArrayHandle LoadUnknownArray(const void* buffer,
vtkm::Id length,
std::string type)
{
vtkm::cont::UnknownArrayHandle handle;
if (type == "float")
{
vtkm::cont::ArrayHandle<vtkm::Float32> concreteArray = vtkm::cont::make_ArrayHandle(
reinterpret_cast<const vtkm::Float32*>(buffer), length, vtkm::CopyFlag::On);
handle = concreteArray;
}
else if (type == "int")
{
vtkm::cont::ArrayHandle<vtkm::Int32> concreteArray = vtkm::cont::make_ArrayHandle(
reinterpret_cast<const vtkm::Int32*>(buffer), length, vtkm::CopyFlag::On);
handle = concreteArray;
}
return handle;
}
////
//// END-EXAMPLE CreateUnknownArrayHandle
////
void TryLoadUnknownArray()
{
vtkm::Float32 scalarBuffer[ARRAY_SIZE];
vtkm::cont::UnknownArrayHandle handle =
LoadUnknownArray(scalarBuffer, ARRAY_SIZE, "float");
VTKM_TEST_ASSERT((handle.IsValueType<vtkm::Float32>()), "Type not right.");
VTKM_TEST_ASSERT(!(handle.IsValueType<vtkm::Int32>()), "Type not right.");
vtkm::Int32 idBuffer[ARRAY_SIZE];
handle = LoadUnknownArray(idBuffer, ARRAY_SIZE, "int");
VTKM_TEST_ASSERT((handle.IsValueType<vtkm::Int32>()), "Type not right.");
VTKM_TEST_ASSERT(!(handle.IsValueType<vtkm::Float32>()), "Type not right.");
}
void NonTypeUnknownArrayHandleAllocate()
{
vtkm::cont::ArrayHandle<vtkm::Id> concreteArray;
concreteArray.Allocate(ARRAY_SIZE);
////
//// BEGIN-EXAMPLE NonTypeUnknownArrayHandleNewInstance
//// BEGIN-EXAMPLE UnknownArrayHandleResize
////
vtkm::cont::UnknownArrayHandle unknownHandle = // ... some valid array
//// PAUSE-EXAMPLE
concreteArray;
//// RESUME-EXAMPLE
// Double the size of the array while preserving all the initial values.
vtkm::Id originalArraySize = unknownHandle.GetNumberOfValues();
unknownHandle.Allocate(originalArraySize * 2, vtkm::CopyFlag::On);
////
//// END-EXAMPLE UnknownArrayHandleResize
////
// Create a new array of the same type as the original.
vtkm::cont::UnknownArrayHandle newArray = unknownHandle.NewInstance();
newArray.Allocate(originalArraySize);
////
//// END-EXAMPLE NonTypeUnknownArrayHandleNewInstance
////
VTKM_TEST_ASSERT(originalArraySize == ARRAY_SIZE);
VTKM_TEST_ASSERT(unknownHandle.GetNumberOfValues() == (2 * ARRAY_SIZE));
VTKM_TEST_ASSERT(concreteArray.GetNumberOfValues() == (2 * ARRAY_SIZE));
VTKM_TEST_ASSERT(newArray.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(newArray.IsType<decltype(concreteArray)>());
////
//// BEGIN-EXAMPLE UnknownArrayHandleBasicInstance
////
vtkm::cont::UnknownArrayHandle indexArray = vtkm::cont::ArrayHandleIndex();
// Returns an array of type ArrayHandleBasic<vtkm::Id>
vtkm::cont::UnknownArrayHandle basicArray = indexArray.NewInstanceBasic();
////
//// END-EXAMPLE UnknownArrayHandleBasicInstance
////
VTKM_TEST_ASSERT(basicArray.IsType<vtkm::cont::ArrayHandleBasic<vtkm::Id>>());
////
//// BEGIN-EXAMPLE UnknownArrayHandleFloatInstance
////
vtkm::cont::UnknownArrayHandle intArray = vtkm::cont::ArrayHandleIndex();
// Returns an array of type ArrayHandleBasic<vtkm::FloatDefault>
vtkm::cont::UnknownArrayHandle floatArray = intArray.NewInstanceFloatBasic();
vtkm::cont::UnknownArrayHandle id3Array = vtkm::cont::ArrayHandle<vtkm::Id3>();
// Returns an array of type ArrayHandleBasic<vtkm::Vec3f>
vtkm::cont::UnknownArrayHandle float3Array = id3Array.NewInstanceFloatBasic();
////
//// END-EXAMPLE UnknownArrayHandleFloatInstance
////
VTKM_TEST_ASSERT(
floatArray.IsType<vtkm::cont::ArrayHandleBasic<vtkm::FloatDefault>>());
VTKM_TEST_ASSERT(float3Array.IsType<vtkm::cont::ArrayHandleBasic<vtkm::Vec3f>>());
}
////
//// BEGIN-EXAMPLE UnknownArrayHandleCanConvert
////
VTKM_CONT vtkm::FloatDefault GetMiddleValue(
const vtkm::cont::UnknownArrayHandle& unknownArray)
{
if (unknownArray.CanConvert<vtkm::cont::ArrayHandleConstant<vtkm::FloatDefault>>())
{
// Fast path for known array
vtkm::cont::ArrayHandleConstant<vtkm::FloatDefault> constantArray;
unknownArray.AsArrayHandle(constantArray);
return constantArray.GetValue();
}
else
{
// General path
auto ranges = vtkm::cont::ArrayRangeCompute(unknownArray);
vtkm::Range range = ranges.ReadPortal().Get(0);
return static_cast<vtkm::FloatDefault>((range.Min + range.Max) / 2);
}
}
////
//// END-EXAMPLE UnknownArrayHandleCanConvert
////
////
//// BEGIN-EXAMPLE UnknownArrayHandleDeepCopy
////
VTKM_CONT vtkm::cont::ArrayHandle<vtkm::FloatDefault> CopyToDefaultArray(
const vtkm::cont::UnknownArrayHandle& unknownArray)
{
// Initialize the output UnknownArrayHandle with the array type we want to copy to.
vtkm::cont::UnknownArrayHandle output = vtkm::cont::ArrayHandle<vtkm::FloatDefault>{};
output.DeepCopyFrom(unknownArray);
return output.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::FloatDefault>>();
}
////
//// END-EXAMPLE UnknownArrayHandleDeepCopy
////
////
//// BEGIN-EXAMPLE UnknownArrayHandleShallowCopy
////
VTKM_CONT vtkm::cont::ArrayHandle<vtkm::FloatDefault> GetAsDefaultArray(
const vtkm::cont::UnknownArrayHandle& unknownArray)
{
// Initialize the output UnknownArrayHandle with the array type we want to copy to.
vtkm::cont::UnknownArrayHandle output = vtkm::cont::ArrayHandle<vtkm::FloatDefault>{};
output.CopyShallowIfPossible(unknownArray);
return output.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::FloatDefault>>();
}
////
//// END-EXAMPLE UnknownArrayHandleShallowCopy
////
void CastUnknownArrayHandle()
{
////
//// BEGIN-EXAMPLE UnknownArrayHandleAsCastArray
////
vtkm::cont::ArrayHandle<vtkm::Float32> originalArray;
vtkm::cont::UnknownArrayHandle unknownArray = originalArray;
vtkm::cont::ArrayHandleCast<vtkm::Float64, decltype(originalArray)> castArray;
unknownArray.AsArrayHandle(castArray);
////
//// END-EXAMPLE UnknownArrayHandleAsCastArray
////
////
//// BEGIN-EXAMPLE UnknownArrayHandleAsArrayHandle1
////
vtkm::cont::ArrayHandle<vtkm::Float32> knownArray =
unknownArray.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Float32>>();
////
//// END-EXAMPLE UnknownArrayHandleAsArrayHandle1
////
////
//// BEGIN-EXAMPLE UnknownArrayHandleAsArrayHandle2
////
unknownArray.AsArrayHandle(knownArray);
////
//// END-EXAMPLE UnknownArrayHandleAsArrayHandle2
////
originalArray.Allocate(ARRAY_SIZE);
SetPortal(originalArray.WritePortal());
GetMiddleValue(unknownArray);
CopyToDefaultArray(unknownArray);
GetAsDefaultArray(unknownArray);
}
////
//// BEGIN-EXAMPLE UsingCastAndCallForTypes
////
struct PrintArrayContentsFunctor
{
template<typename T, typename S>
VTKM_CONT void operator()(const vtkm::cont::ArrayHandle<T, S>& array) const
{
this->PrintArrayPortal(array.ReadPortal());
}
private:
template<typename PortalType>
VTKM_CONT void PrintArrayPortal(const PortalType& portal) const
{
for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++)
{
// All ArrayPortal objects have ValueType for the type of each value.
using ValueType = typename PortalType::ValueType;
using VTraits = vtkm::VecTraits<ValueType>;
ValueType value = portal.Get(index);
vtkm::IdComponent numComponents = VTraits::GetNumberOfComponents(value);
for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents;
componentIndex++)
{
std::cout << " " << VTraits::GetComponent(value, componentIndex);
}
std::cout << std::endl;
}
}
};
void PrintArrayContents(const vtkm::cont::UnknownArrayHandle& array)
{
array.CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
PrintArrayContentsFunctor{});
}
////
//// END-EXAMPLE UsingCastAndCallForTypes
////
struct MyWorklet : vtkm::worklet::WorkletMapField
{
using ControlSignature = void(FieldIn, FieldOut);
template<typename T1, typename T2>
VTKM_EXEC void operator()(const T1& in, T2& out) const
{
using VTraitsIn = vtkm::VecTraits<T1>;
using VTraitsOut = vtkm::VecTraits<T2>;
const vtkm::IdComponent numComponents = VTraitsIn::GetNumberOfComponents(in);
VTKM_ASSERT(numComponents == VTraitsOut::GetNumberOfComponents(out));
for (vtkm::IdComponent index = 0; index < numComponents; ++index)
{
VTraitsOut::SetComponent(out,
index,
static_cast<typename VTraitsOut::ComponentType>(
VTraitsIn::GetComponent(in, index)));
}
}
};
void TryPrintArrayContents()
{
vtkm::cont::ArrayHandleIndex implicitArray(ARRAY_SIZE);
vtkm::cont::ArrayHandle<vtkm::FloatDefault> concreteArray;
vtkm::cont::Algorithm::Copy(implicitArray, concreteArray);
vtkm::cont::UnknownArrayHandle unknownArray = concreteArray;
PrintArrayContents(unknownArray);
////
//// BEGIN-EXAMPLE UncertainArrayHandle
////
vtkm::cont::UncertainArrayHandle<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>
uncertainArray(unknownArray);
uncertainArray.CastAndCall(PrintArrayContentsFunctor{});
////
//// END-EXAMPLE UncertainArrayHandle
////
vtkm::cont::ArrayHandle<vtkm::FloatDefault> outArray;
////
//// BEGIN-EXAMPLE UnknownArrayResetTypes
////
vtkm::cont::Invoker invoke;
invoke(
MyWorklet{},
unknownArray.ResetTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(),
outArray);
////
//// END-EXAMPLE UnknownArrayResetTypes
////
////
//// BEGIN-EXAMPLE CastAndCallForTypesWithFloatFallback
////
unknownArray.CastAndCallForTypesWithFloatFallback<vtkm::TypeListField,
VTKM_DEFAULT_STORAGE_LIST>(
PrintArrayContentsFunctor{});
////
//// END-EXAMPLE CastAndCallForTypesWithFloatFallback
////
////
//// BEGIN-EXAMPLE CastAndCallWithFloatFallback
////
uncertainArray.CastAndCall(PrintArrayContentsFunctor{});
////
//// END-EXAMPLE CastAndCallWithFloatFallback
////
}
void ExtractUnknownComponent()
{
////
//// BEGIN-EXAMPLE UnknownArrayExtractComponent
////
vtkm::cont::ArrayHandleBasic<vtkm::Vec3f> concreteArray =
vtkm::cont::make_ArrayHandle<vtkm::Vec3f>({ { 0, 1, 2 },
{ 3, 4, 5 },
{ 6, 7, 8 },
{ 9, 10, 11 },
{ 12, 13, 14 },
{ 15, 16, 17 } });
vtkm::cont::UnknownArrayHandle unknownArray(concreteArray);
//// LABEL Call
auto componentArray = unknownArray.ExtractComponent<vtkm::FloatDefault>(0);
// componentArray contains [ 0, 3, 6, 9, 12, 15 ].
////
//// END-EXAMPLE UnknownArrayExtractComponent
////
VTKM_TEST_ASSERT(componentArray.GetNumberOfValues() ==
concreteArray.GetNumberOfValues());
{
auto portal = componentArray.ReadPortal();
auto expectedPortal = concreteArray.ReadPortal();
for (vtkm::IdComponent i = 0; i < componentArray.GetNumberOfValues(); ++i)
{
VTKM_TEST_ASSERT(test_equal(portal.Get(i), expectedPortal.Get(i)[0]));
}
}
VTKM_TEST_ASSERT(
////
//// BEGIN-EXAMPLE UnknownArrayBaseComponentType
////
unknownArray.IsBaseComponentType<vtkm::FloatDefault>()
////
//// END-EXAMPLE UnknownArrayBaseComponentType
////
);
auto deepTypeArray = vtkm::cont::make_ArrayHandleGroupVec<2>(concreteArray);
unknownArray = deepTypeArray;
VTKM_TEST_ASSERT(unknownArray.GetNumberOfComponentsFlat() == 6);
vtkm::cont::ArrayHandle<vtkm::FloatDefault> outputArray;
vtkm::cont::Invoker invoke;
////
//// BEGIN-EXAMPLE UnknownArrayExtractComponentsMultiple
////
std::vector<vtkm::cont::ArrayHandle<vtkm::FloatDefault>> outputArrays(
static_cast<std::size_t>(unknownArray.GetNumberOfComponentsFlat()));
for (vtkm::IdComponent componentIndex = 0;
componentIndex < unknownArray.GetNumberOfComponentsFlat();
++componentIndex)
{
invoke(MyWorklet{},
unknownArray.ExtractComponent<vtkm::FloatDefault>(componentIndex),
outputArrays[static_cast<std::size_t>(componentIndex)]);
}
////
//// END-EXAMPLE UnknownArrayExtractComponentsMultiple
////
for (std::size_t outIndex = 0; outIndex < outputArrays.size(); ++outIndex)
{
vtkm::IdComponent vecIndex = static_cast<vtkm::IdComponent>(outIndex % 3);
vtkm::IdComponent groupIndex = static_cast<vtkm::IdComponent>(outIndex / 3);
auto portal = outputArrays[outIndex].ReadPortal();
auto expectedPortal = deepTypeArray.ReadPortal();
VTKM_TEST_ASSERT(portal.GetNumberOfValues() ==
(concreteArray.GetNumberOfValues() / 2));
for (vtkm::IdComponent i = 0; i < portal.GetNumberOfValues(); ++i)
{
VTKM_TEST_ASSERT(
test_equal(portal.Get(i), expectedPortal.Get(i)[groupIndex][vecIndex]));
}
}
unknownArray = concreteArray;
vtkm::cont::ArrayHandle<vtkm::Vec3f> outArray;
////
//// BEGIN-EXAMPLE UnknownArrayExtractArrayFromComponents
////
invoke(MyWorklet{},
unknownArray.ExtractArrayFromComponents<vtkm::FloatDefault>(),
outArray);
////
//// END-EXAMPLE UnknownArrayExtractArrayFromComponents
////
VTKM_TEST_ASSERT(test_equal_ArrayHandles(outArray, concreteArray));
////
//// BEGIN-EXAMPLE UnknownArrayCallWithExtractedArray
////
unknownArray.CastAndCallWithExtractedArray(PrintArrayContentsFunctor{});
////
//// END-EXAMPLE UnknownArrayCallWithExtractedArray
////
}
////
//// BEGIN-EXAMPLE UnknownArrayConstOutput
////
void IndexInitialize(vtkm::Id size, const vtkm::cont::UnknownArrayHandle& output)
{
vtkm::cont::ArrayHandleIndex input(size);
output.DeepCopyFrom(input);
}
////
//// END-EXAMPLE UnknownArrayConstOutput
////
////
//// BEGIN-EXAMPLE UseUnknownArrayConstOutput
////
template<typename T>
void Foo(const vtkm::cont::ArrayHandle<T>& input, vtkm::cont::ArrayHandle<T>& output)
{
IndexInitialize(input.GetNumberOfValues(), output);
// ...
////
//// END-EXAMPLE UseUnknownArrayConstOutput
////
VTKM_TEST_ASSERT(output.GetNumberOfValues() == input.GetNumberOfValues());
auto portal = output.ReadPortal();
for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); ++index)
{
VTKM_TEST_ASSERT(portal.Get(index) == index);
}
}
void TryConstOutput()
{
vtkm::cont::ArrayHandle<vtkm::Id> input =
vtkm::cont::make_ArrayHandle<vtkm::Id>({ 3, 6, 1, 4 });
vtkm::cont::ArrayHandle<vtkm::Id> output;
Foo(input, output);
}
void Test()
{
TryLoadUnknownArray();
NonTypeUnknownArrayHandleAllocate();
CastUnknownArrayHandle();
TryPrintArrayContents();
ExtractUnknownComponent();
TryConstOutput();
}
} // anonymous namespace
int GuideExampleUnknownArrayHandle(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(Test, argc, argv);
}

@ -0,0 +1,165 @@
==============================
Memory Layout of Array Handles
==============================
.. index:: array handle; memory layout
:chapref:`basic-array-handles:Basic Array Handles` describes the basics of the :class:`vtkm::cont::ArrayHandle` class, which is the interface to the arrays of data that |VTKm| operates on.
Recall that :class:`vtkm::cont::ArrayHandle` is a templated class with two template parameters.
The first template argument is the type of each item in the array.
The second parameter, which is optional, determines how the array is stored in memory.
This can be used in a variety of different ways, but its primary purpose is to provide a strategy for laying the data out in memory.
This chapter documents the ways in which |VTKm| can store and access arrays of data in different layouts.
------------------------------
Basic Memory Layout
------------------------------
.. index::
single: array handle; basic
single: basic array handle
If the second storage template parameter of :class:`vtkm::cont::ArrayHandle` is not specified, it defaults to the basic memory layout.
This is roughly synonymous with a wrapper around a standard C array, much like ``std::vector``.
In fact, :secref:`basic-array-handles:Creating Array Handles` provides examples of wrapping a default :class:`vtkm::cont::ArrayHandle` around either a basic C array or a ``std::vector``.
|VTKm| provides :class:`vtkm::cont::ArrayHandleBasic` as a convenience class for working with basic array handles.
:class:`vtkm::cont::ArrayHandleBasic` is a simple subclass of :class:`vtkm::cont::ArrayHandle` with the default storage in the second template argument (which is :class:`vtkm::cont::StorageTagBasic`).
:class:`vtkm::cont::ArrayHandleBasic` and its superclass can be used more or less interchangeably.
.. doxygenclass:: vtkm::cont::ArrayHandleBasic
:members:
Because a :class:`vtkm::cont::ArrayHandleBasic` represents arrays as a standard C array, it is possible to get a pointer to this array using either :func:`vtkm::cont::ArrayHandleBasic::GetReadPointer` or :func:`vtkm::cont::ArrayHandleBasic::GetWritePointer`.
.. load-example:: GetArrayPointer
:file: GuideExampleArrayHandle.cxx
:caption: Getting a standard C array from a basic array handle.
.. didyouknow::
When you get an array pointer this way, the :class:`vtkm::cont::ArrayHandle` still has a reference to it.
If using multiple threads, you can use a :class:`vtkm::cont::Token` object to lock the array.
When the token is used to get a pointer, it will lock the array as long as the token exists.
:numref:`ex:GetArrayPointer` demonstrates using a :class:`vtkm::cont::Token`.
--------------------
Structure of Arrays
--------------------
.. index::
single: AOS
single: SOA
The basic :class:`vtkm::cont::ArrayHandle` stores :class:`vtkm::Vec` objects in sequence.
In this sense, a basic array is an *Array of Structures* (AOS).
Another approach is to store each component of the structure (i.e., the :class:`vtkm::Vec`) in a separate array.
This is known as a *Structure of Arrays* (SOA).
There are advantages to this approach including potentially better cache performance and the ability to combine arrays already represented as separate components without copying them.
Arrays of this nature are represented with a :class:`vtkm::cont::ArrayHandleSOA`, which is a subclass of :class:`vtkm::cont::StorageTagSOA`.
.. doxygenclass:: vtkm::cont::ArrayHandleSOA
:members:
:class:`vtkm::cont::ArrayHandleSOA` can be constructed and allocated just as a basic array handle.
Additionally, you can use its constructors or the :func:`vtkm::cont::make_ArrayHandleSOA` functions to build a :class:`vtkm::cont::ArrayHandleSOA` from basic :class:`vtkm::cont::ArrayHandle`'s that hold the components.
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(std::initializer_list<vtkm::cont::ArrayHandle<typename vtkm::VecTraits<ValueType>::ComponentType, vtkm::cont::StorageTagBasic>> &&)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(const vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic>&, const RemainingArrays&...)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(std::initializer_list<std::vector<typename vtkm::VecTraits<ValueType>::ComponentType>>&&)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag, const std::vector<ComponentType>&, RemainingVectors&&...)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag, std::vector<ComponentType>&&, RemainingVectors&&...)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOAMove(std::vector<ComponentType>&&, RemainingVectors&&...)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(std::initializer_list<const typename vtkm::VecTraits<ValueType>::ComponentType*>&&, vtkm::Id, vtkm::CopyFlag)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleSOA(vtkm::Id, vtkm::CopyFlag, const ComponentType*, const RemainingArrays*...)
.. load-example:: ArrayHandleSOAFromComponentArrays
:file: GuideExampleArrayHandle.cxx
:caption: Creating an SOA array handle from component arrays.
.. didyouknow::
In addition to constructing a :class:`vtkm::cont::ArrayHandleSOA` from its component arrays, you can get the component arrays back out using the :func:`vtkm::cont::ArrayHandleSOA::GetArray` method.
--------------------
Strided Arrays
--------------------
.. todo:: Should this be moved to the chapter/section on transformed arrays?
.. index::
double: array handle; stride
double: array handle; offset
double: array handle; modulo
double: array handle; divisor
:class:`vtkm::cont::ArrayHandleBasic` operates on a tightly packed array.
That is, each value follows immediately after the proceeding value in memory.
However, it is often convenient to access values at different strides or offsets.
This allows representations of data that are not tightly packed in memory.
The :class:`vtkm::cont::ArrayHandleStride` class allows arrays with different data packing.
.. doxygenclass:: vtkm::cont::ArrayHandleStride
:members:
The most common use of :class:`vtkm::cont::ArrayHandleStride` is to pull components out of arrays.
:class:`vtkm::cont::ArrayHandleStride` is seldom constructed directly.
Rather, |VTKm| has mechanisms to extract a component from an array.
To extract a component directly from a :class:`vtkm::cont::ArrayHandle`, use :func:`vtkm::cont::ArrayExtractComponent`.
.. doxygenfunction:: vtkm::cont::ArrayExtractComponent
The main advantage of extracting components this way is to convert data represented in different types of arrays into an array of a single type.
For example, :class:`vtkm::cont::ArrayHandleStride` can represent a component from either a :class:`vtkm::cont::ArrayHandleBasic` or a :class:`vtkm::cont::ArrayHandleSOA` by just using different stride values.
This is used by :func:`vtkm::cont::UnknownArrayHandle::ExtractComponent` and elsewhere to create a concrete array handle class without knowing the actual class.
.. commonerrors::
Many, but not all, of |VTKm|'s arrays can be represented by a :class:`vtkm::cont::ArrayHandleStride` directly without copying.
If |VTKm| cannot easily create a :class:`vtkm::cont::ArrayHandleStride` when attempting such an operation, it will use a slow copying fallback.
A warning will be issued whenever this happens.
Be on the lookout for such warnings and consider changing the data representation when that happens.
--------------------
Runtime Vec Arrays
--------------------
Because many of the devices |VTKm| runs on cannot efficiently allocate memory while an algorithm is running, the data held in :class:`vtkm::cont::ArrayHandle`'s are usually required to be a static size.
For example, the :class:`vtkm::Vec` object often used as the value type for :class:`vtkm::cont::ArrayHandle` has a number of components that must be defined at compile time.
This is a problem in cases where the size of a vector object cannot be determined at compile time.
One class to help alleviate this problem is :class:`vtkm::cont::ArrayHandleRuntimeVec`.
This array handle stores data in the same way as :class:`vtkm::cont::ArrayHandleBasic` with a :class:`vtkm::Vec` value type, but the size of the ``Vec`` can be set at runtime.
.. doxygenclass:: vtkm::cont::ArrayHandleRuntimeVec
:members:
A :class:`vtkm::cont::ArrayHandleRuntimeVec` is easily created from existing data using one of the :func:`vtkm::cont::make_ArrayHandleRuntimeVec` functions.
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent, const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVec(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&)
|VTKm| also provides several convenience functions to convert a basic C array or ``std::vector`` to a :class:`vtkm::cont::ArrayHandleRuntimeVec`.
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent, const T*, vtkm::Id, vtkm::CopyFlag)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVecMove(vtkm::IdComponent, T*&, vtkm::Id, vtkm::cont::internal::BufferInfo::Deleter, vtkm::cont::internal::BufferInfo::Reallocater)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent, const std::vector<T, Allocator>&, vtkm::CopyFlag)
.. doxygenfunction:: vtkm::cont::make_ArrayHandleRuntimeVecMove(vtkm::IdComponent, std::vector<T, Allocator>&&)
The advantage of this class is that a :class:`vtkm::cont::ArrayHandleRuntimeVec` can be created in a routine that does not know the number of components at runtime and then later retrieved as a basic :class:`vtkm::cont::ArrayHandle` with a :class:`vtkm::Vec` of the correct size.
This often consists of a file reader or other data ingestion creating :class:`vtkm::cont::ArrayHandleRuntimeVec` objects and storing them in :class:`vtkm::cont::UnknownArrayHandle`, which is used as an array container for :class:`vtkm::cont::DataSet`.
Filters that then subsequently operate on the :class:`vtkm::cont::DataSet` can retrieve the data as a :class:`vtkm::cont::ArrayHandle` of the appropriate :class:`vtkm::Vec` size.
.. load-example:: GroupWithRuntimeVec
:file: GuideExampleArrayHandleRuntimeVec.cxx
:caption: Loading a data with runtime component size and using with a static sized filter.
.. didyouknow::
Wrapping a basic array in a :class:`vtkm::cont::ArrayHandleRuntimeVec` has a similar effect as wrapping the array in a :class:`vtkm::cont::ArrayHandleGroupVec`.
The difference is in the context in which they are used.
If the size of the ``Vec`` is known at compile time *and* the array is going to immediately be used (such as operated on by a worklet), then :class:`vtkm::cont::ArrayHandleGroupVec` should be used.
However, if the ``Vec`` size is not known or the array will be stored in an object like :class:`vtkm::cont::UnknownArrayHandle`, then :class:`vtkm::cont::ArrayHandleRuntimeVec` is a better choice.
It is also possible to get a :class:`vtkm::cont::ArrayHandleRuntimeVec` from a :class:`vtkm::cont::UnknownArrayHandle` that was originally stored as a basic array.
This is convenient for operations that want to operate on arrays with an unknown ``Vec`` size.
.. load-example:: GetRuntimeVec
:file: GuideExampleArrayHandleRuntimeVec.cxx
:caption: Using :class:`vtkm::cont::ArrayHandleRuntimeVec` to get an array regardless of the size of the contained :class:`vtkm::Vec` values.

@ -12,3 +12,4 @@ Advanced Development
worklet-error-handling.rst
math.rst
working-with-cells.rst
memory-layout.rst

@ -101,6 +101,12 @@ public:
} // namespace internal
/// @brief Basic array storage for an array handle.
///
/// This array handle references a standard C array. It provides a level
/// of safety and management across devices.
/// This is the default used when no storage is specified. Using this subclass
/// allows access to the underlying raw array.
template <typename T>
class VTKM_ALWAYS_EXPORT ArrayHandleBasic : public ArrayHandle<T, vtkm::cont::StorageTagBasic>
{
@ -173,50 +179,89 @@ public:
{
}
/// @{
/// \brief Gets raw access to the `ArrayHandle`'s data.
/// @brief Gets raw access to the `ArrayHandle`'s data.
///
/// Note that the returned array may become invalidated by other operations on the ArryHandle
/// unless you provide a token.
/// Note that the returned array may become invalidated by other operations on the ArryHandle.
///
const T* GetReadPointer(vtkm::cont::Token& token) const
{
return reinterpret_cast<const T*>(this->GetBuffers()[0].ReadPointerHost(token));
}
const T* GetReadPointer() const
{
vtkm::cont::Token token;
return this->GetReadPointer(token);
}
T* GetWritePointer(vtkm::cont::Token& token) const
/// @brief Gets raw access to the `ArrayHandle`'s data.
///
/// @param token When a `vtkm::cont::Token` is provided, the array is locked
/// from being used by any write operations until the token goes out of scope.
///
const T* GetReadPointer(vtkm::cont::Token& token) const
{
return reinterpret_cast<T*>(this->GetBuffers()[0].WritePointerHost(token));
return reinterpret_cast<const T*>(this->GetBuffers()[0].ReadPointerHost(token));
}
/// @brief Gets raw write access to the `ArrayHandle`'s data.
///
/// Note that the returned array may become invalidated by other operations on the ArryHandle.
///
T* GetWritePointer() const
{
vtkm::cont::Token token;
return this->GetWritePointer(token);
}
const T* GetReadPointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const
/// @brief Gets raw write access to the `ArrayHandle`'s data.
///
/// @param token When a `vtkm::cont::Token` is provided, the array is locked
/// from being used by any read or write operations until the token goes out of scope.
///
T* GetWritePointer(vtkm::cont::Token& token) const
{
return reinterpret_cast<const T*>(this->GetBuffers()[0].ReadPointerDevice(device, token));
return reinterpret_cast<T*>(this->GetBuffers()[0].WritePointerHost(token));
}
/// @brief Gets raw access to the `ArrayHandle`'s data on a particular device.
///
/// Note that the returned array may become invalidated by other operations on the ArryHandle.
///
/// @param device The device ID or device tag specifying on which device the array will
/// be valid on.
///
const T* GetReadPointer(vtkm::cont::DeviceAdapterId device) const
{
vtkm::cont::Token token;
return this->GetReadPointer(device, token);
}
T* GetWritePointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const
/// @brief Gets raw access to the `ArrayHandle`'s data.
///
/// @param device The device ID or device tag specifying on which device the array will
/// be valid on.
/// @param token When a `vtkm::cont::Token` is provided, the array is locked
/// from being used by any write operations until the token goes out of scope.
///
const T* GetReadPointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const
{
return reinterpret_cast<T*>(this->GetBuffers()[0].WritePointerDevice(device, token));
return reinterpret_cast<const T*>(this->GetBuffers()[0].ReadPointerDevice(device, token));
}
/// @brief Gets raw write access to the `ArrayHandle`'s data.
///
/// Note that the returned array may become invalidated by other operations on the ArryHandle.
///
/// @param device The device ID or device tag specifying on which device the array will
/// be valid on.
///
T* GetWritePointer(vtkm::cont::DeviceAdapterId device) const
{
vtkm::cont::Token token;
return this->GetWritePointer(device, token);
}
/// @}
/// @brief Gets raw write access to the `ArrayHandle`'s data.
///
/// @param device The device ID or device tag specifying on which device the array will
/// be valid on.
/// @param token When a `vtkm::cont::Token` is provided, the array is locked
/// from being used by any read or write operations until the token goes out of scope.
///
T* GetWritePointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const
{
return reinterpret_cast<T*>(this->GetBuffers()[0].WritePointerDevice(device, token));
}
};
/// A convenience function for creating an ArrayHandle from a standard C array.

@ -296,7 +296,7 @@ public:
} // namespace internal
/// \brief Fancy array handle for a basic array with runtime selected vec size.
/// @brief Fancy array handle for a basic array with runtime selected vec size.
///
/// It is sometimes the case that you need to create an array of `Vec`s where
/// the number of components is not known until runtime. This is problematic
@ -341,6 +341,14 @@ private:
using ComponentsArrayType = vtkm::cont::ArrayHandle<ComponentType, StorageTagBasic>;
public:
/// @brief Construct an `ArrayHandleRuntimeVec` with a given number of components.
///
/// @param numComponents The size of the `Vec`s stored in the array. This must be
/// specified at the time of construction.
///
/// @param componentsArray This optional parameter allows you to supply a basic array
/// that holds the components. This provides a mechanism to group consecutive values
/// into vectors.
VTKM_CONT
ArrayHandleRuntimeVec(vtkm::IdComponent numComponents,
const ComponentsArrayType& componentsArray = ComponentsArrayType{})
@ -348,18 +356,22 @@ public:
{
}
/// @brief Return the number of components in each vec value.
VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const
{
return StorageType::GetNumberOfComponents(this->GetBuffers());
}
/// @brief Return a basic array containing the components stored in this array.
///
/// The returned array is shared with this object. Modifying the contents of one array
/// will modify the other.
VTKM_CONT vtkm::cont::ArrayHandleBasic<ComponentType> GetComponentsArray() const
{
return StorageType::GetComponentsArray(this->GetBuffers());
}
///@{
/// \brief Converts the array to that of a basic array handle.
/// @brief Converts the array to that of a basic array handle.
///
/// This method converts the `ArrayHandleRuntimeVec` to a simple `ArrayHandleBasic`.
/// This is useful if the `ArrayHandleRuntimeVec` is passed to a routine that works
@ -371,6 +383,7 @@ public:
StorageType::AsArrayHandleBasic(this->GetBuffers(), array);
}
/// @copydoc AsArrayHandleBasic
template <typename ArrayType>
ArrayType AsArrayHandleBasic() const
{
@ -378,7 +391,6 @@ public:
this->AsArrayHandleBasic(array);
return array;
}
///@}
};
/// `make_ArrayHandleRuntimeVec` is convenience function to generate an
@ -407,6 +419,8 @@ VTKM_CONT auto make_ArrayHandleRuntimeVec(
numComponents * UnrolledVec::NUM_COMPONENTS, flatComponents);
}
/// Converts a basic array handle into an `ArrayHandleRuntimeVec` with 1 component. The
/// constructed array is essentially equivalent but of a different type.
template <typename T>
VTKM_CONT auto make_ArrayHandleRuntimeVec(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>& componentsArray)

@ -229,7 +229,7 @@ public:
} // namespace internal
/// \brief An `ArrayHandle` that for Vecs stores each component in a separate physical array.
/// @brief An `ArrayHandle` that for Vecs stores each component in a separate physical array.
///
/// `ArrayHandleSOA` behaves like a regular `ArrayHandle` (with a basic storage) except that
/// if you specify a `ValueType` of a `Vec` or a `Vec-like`, it will actually store each
@ -264,6 +264,17 @@ public:
{
}
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// @code{.cpp}
/// vtkm::cont::ArrayHandle<T> components1;
/// vtkm::cont::ArrayHandle<T> components2;
/// vtkm::cont::ArrayHandle<T> components3;
/// // Fill arrays...
///
/// std::array<T, 3> allComponents{ components1, components2, components3 };
/// vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3>vecarray(allComponents);
/// @endcode
ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS>& componentArrays)
{
for (vtkm::IdComponent componentIndex = 0; componentIndex < NUM_COMPONENTS; ++componentIndex)
@ -272,6 +283,17 @@ public:
}
}
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// @code{.cpp}
/// vtkm::cont::ArrayHandle<T> components1;
/// vtkm::cont::ArrayHandle<T> components2;
/// vtkm::cont::ArrayHandle<T> components3;
/// // Fill arrays...
///
/// std::vector<T> allComponents{ components1, components2, components3 };
/// vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3>vecarray(allComponents);
/// @endcode
ArrayHandleSOA(const std::vector<ComponentArrayType>& componentArrays)
{
VTKM_ASSERT(componentArrays.size() == NUM_COMPONENTS);
@ -281,6 +303,17 @@ public:
}
}
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// @code{.cpp}
/// vtkm::cont::ArrayHandle<T> components1;
/// vtkm::cont::ArrayHandle<T> components2;
/// vtkm::cont::ArrayHandle<T> components3;
/// // Fill arrays...
///
/// vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3> vecarray(
/// { components1, components2, components3 });
/// @endcode
ArrayHandleSOA(std::initializer_list<ComponentArrayType>&& componentArrays)
{
VTKM_ASSERT(componentArrays.size() == NUM_COMPONENTS);
@ -292,6 +325,19 @@ public:
}
}
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// The data is copied from the `std::vector`s to the array handle.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>> vecarray(
/// { components1, components2, components3 });
/// @endcode
ArrayHandleSOA(std::initializer_list<std::vector<ComponentType>>&& componentVectors)
{
VTKM_ASSERT(componentVectors.size() == NUM_COMPONENTS);
@ -305,7 +351,22 @@ public:
}
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// The first argument is a `vtkm::CopyFlag` to determine whether the input arrays
/// should be copied.
/// The component arrays are listed as arguments.
/// This only works if all the templated arguments are of type `std::vector<ComponentType>`.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>> vecarray(
/// vtkm::CopyFlag::On, components1, components2, components3);
/// @endcode
template <typename Allocator, typename... RemainingVectors>
ArrayHandleSOA(vtkm::CopyFlag copy,
const std::vector<ComponentType, Allocator>& vector0,
@ -318,7 +379,25 @@ public:
VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == NUM_COMPONENTS);
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// The first argument is a `vtkm::CopyFlag` to determine whether the input arrays
/// should be copied.
/// The component arrays are listed as arguments.
/// This only works if all the templated arguments are rvalues of type
/// `std::vector<ComponentType>`.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, N> vecarray(vtkm::CopyFlag::Off,
/// std::move(components1),
/// std::move(components2),
/// std::move(components3);
/// @endcode
template <typename... RemainingVectors>
ArrayHandleSOA(vtkm::CopyFlag copy,
std::vector<ComponentType>&& vector0,
@ -331,6 +410,17 @@ public:
VTKM_STATIC_ASSERT(sizeof...(RemainingVectors) + 1 == NUM_COMPONENTS);
}
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// @code{.cpp}
/// T* components1;
/// T* components2;
/// T* components3;
/// // Fill arrays...
///
/// vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>>(
/// { components1, components2, components3 }, size, vtkm::CopyFlag::On);
/// @endcode
ArrayHandleSOA(std::initializer_list<const ComponentType*> componentArrays,
vtkm::Id length,
vtkm::CopyFlag copy)
@ -345,7 +435,20 @@ public:
}
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Construct an `ArrayHandleSOA` from a collection of component arrays.
///
/// The component arrays are listed as arguments.
/// This only works if all the templated arguments are of type `ComponentType*`.
///
/// @code{.cpp}
/// T* components1;
/// T* components2;
/// T* components3;
/// // Fill arrays...
///
/// vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>> vecarray(
/// size, vtkm::CopyFlag::On, components1, components2, components3);
/// @endcode
template <typename... RemainingArrays>
ArrayHandleSOA(vtkm::Id length,
vtkm::CopyFlag copy,
@ -358,17 +461,39 @@ public:
VTKM_STATIC_ASSERT(sizeof...(RemainingArrays) + 1 == NUM_COMPONENTS);
}
/// @brief Get a basic array representing the component for the given index.
VTKM_CONT vtkm::cont::ArrayHandleBasic<ComponentType> GetArray(vtkm::IdComponent index) const
{
return ComponentArrayType({ this->GetBuffers()[index] });
}
/// @brief Replace a component array.
VTKM_CONT void SetArray(vtkm::IdComponent index, const ComponentArrayType& array)
{
this->SetBuffer(index, array.GetBuffers()[0]);
}
};
namespace internal
{
template <typename... Remaining>
using VecSizeFromRemaining =
std::integral_constant<vtkm::IdComponent, vtkm::IdComponent(sizeof...(Remaining) + 1)>;
} // namespace internal
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with an initializer list of array handles.
///
/// @code{.cpp}
/// vtkm::cont::ArrayHandle<T> components1;
/// vtkm::cont::ArrayHandle<T> components2;
/// vtkm::cont::ArrayHandle<T> components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3>>(
/// { components1, components2, components3 });
/// @endcode
template <typename ValueType>
VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
std::initializer_list<vtkm::cont::ArrayHandle<typename vtkm::VecTraits<ValueType>::ComponentType,
@ -377,16 +502,43 @@ VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
return ArrayHandleSOA<ValueType>(std::move(componentArrays));
}
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with a number of array handles.
///
/// This only works if all the templated arguments are of type
/// `vtkm::cont::ArrayHandle<ComponentType>`.
///
/// @code{.cpp}
/// vtkm::cont::ArrayHandle<T> components1;
/// vtkm::cont::ArrayHandle<T> components2;
/// vtkm::cont::ArrayHandle<T> components3;
/// // Fill arrays...
///
/// auto vecarray =
/// vtkm::cont::make_ArrayHandleSOA(components1, components2, components3);
/// @endcode
template <typename ComponentType, typename... RemainingArrays>
VTKM_CONT
ArrayHandleSOA<vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingArrays) + 1)>>
make_ArrayHandleSOA(
const vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic>& componentArray0,
const RemainingArrays&... componentArrays)
VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>>
make_ArrayHandleSOA(
const vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic>& componentArray0,
const RemainingArrays&... componentArrays)
{
return { componentArray0, componentArrays... };
}
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with an initializer list of `std::vector`.
///
/// The data is copied from the `std::vector`s to the array handle.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3>>(
/// { components1, components2, components3 });
/// @endcode
template <typename ValueType>
VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
std::initializer_list<std::vector<typename vtkm::VecTraits<ValueType>::ComponentType>>&&
@ -395,13 +547,28 @@ VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
return ArrayHandleSOA<ValueType>(std::move(componentVectors));
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with a number of `std::vector`.
///
/// The first argument is a `vtkm::CopyFlag` to determine whether the input arrays
/// should be copied.
/// The component arrays are listed as arguments.
/// This only works if all the templated arguments are of type `std::vector<ComponentType>`.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA(
/// vtkm::CopyFlag::On, components1, components2, components3);
/// @endcode
template <typename ComponentType, typename... RemainingVectors>
VTKM_CONT
ArrayHandleSOA<vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingVectors) + 1)>>
make_ArrayHandleSOA(vtkm::CopyFlag copy,
const std::vector<ComponentType>& vector0,
RemainingVectors&&... componentVectors)
VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>>
make_ArrayHandleSOA(vtkm::CopyFlag copy,
const std::vector<ComponentType>& vector0,
RemainingVectors&&... componentVectors)
{
// Convert std::vector to ArrayHandle first so that it correctly handles a mix of rvalue args.
return { vtkm::cont::make_ArrayHandle(vector0, copy),
@ -409,32 +576,74 @@ VTKM_CONT
copy)... };
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with a number of `std::vector`.
///
/// The first argument is a `vtkm::CopyFlag` to determine whether the input arrays
/// should be copied.
/// The component arrays are listed as arguments.
/// This only works if all the templated arguments are rvalues of type
/// `std::vector<ComponentType>`.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag::Off,
/// std::move(components1),
/// std::move(components2),
/// std::move(components3);
/// @endcode
template <typename ComponentType, typename... RemainingVectors>
VTKM_CONT
ArrayHandleSOA<vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingVectors) + 1)>>
make_ArrayHandleSOA(vtkm::CopyFlag copy,
std::vector<ComponentType>&& vector0,
RemainingVectors&&... componentVectors)
VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>>
make_ArrayHandleSOA(vtkm::CopyFlag copy,
std::vector<ComponentType>&& vector0,
RemainingVectors&&... componentVectors)
{
// Convert std::vector to ArrayHandle first so that it correctly handles a mix of rvalue args.
return ArrayHandleSOA<
vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingVectors) + 1)>>(
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>>(
vtkm::cont::make_ArrayHandle(std::move(vector0), copy),
vtkm::cont::make_ArrayHandle(std::forward<RemainingVectors>(componentVectors), copy)...);
}
// This only works if all the templated arguments are rvalues of std::vector<ComponentType>.
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with a number of `std::vector`.
///
/// This only works if all the templated arguments are rvalues of type
/// `std::vector<ComponentType>`.
///
/// @code{.cpp}
/// std::vector<T> components1;
/// std::vector<T> components2;
/// std::vector<T> components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOAMove(
/// std::move(components1), std::move(components2), std::move(components3));
/// @endcode
template <typename ComponentType, typename... RemainingVectors>
VTKM_CONT
ArrayHandleSOA<vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingVectors) + 1)>>
make_ArrayHandleSOAMove(std::vector<ComponentType>&& vector0,
RemainingVectors&&... componentVectors)
VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>>
make_ArrayHandleSOAMove(std::vector<ComponentType>&& vector0,
RemainingVectors&&... componentVectors)
{
return { vtkm::cont::make_ArrayHandleMove(std::move(vector0)),
vtkm::cont::make_ArrayHandleMove(std::forward<RemainingVectors>(componentVectors))... };
}
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with an initializer list of C arrays.
///
/// @code{.cpp}
/// T* components1;
/// T* components2;
/// T* components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA<vtkm::Vec<T, 3>>(
/// { components1, components2, components3 }, size, vtkm::CopyFlag::On);
/// @endcode
template <typename ValueType>
VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
std::initializer_list<const typename vtkm::VecTraits<ValueType>::ComponentType*>&&
@ -445,14 +654,26 @@ VTKM_CONT ArrayHandleSOA<ValueType> make_ArrayHandleSOA(
return ArrayHandleSOA<ValueType>(std::move(componentVectors), length, copy);
}
// This only works if all the templated arguments are of type std::vector<ComponentType>.
/// @brief Create a `vtkm::cont::ArrayHandleSOA` with a number of C arrays.
///
/// This only works if all the templated arguments are of type `ComponentType*`.
///
/// @code{.cpp}
/// T* components1;
/// T* components2;
/// T* components3;
/// // Fill arrays...
///
/// auto vecarray = vtkm::cont::make_ArrayHandleSOA(
/// size, vtkm::CopyFlag::On, components1, components2, components3);
/// @endcode
template <typename ComponentType, typename... RemainingArrays>
VTKM_CONT
ArrayHandleSOA<vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingArrays) + 1)>>
make_ArrayHandleSOA(vtkm::Id length,
vtkm::CopyFlag copy,
const ComponentType* array0,
const RemainingArrays*... componentArrays)
VTKM_CONT ArrayHandleSOA<
vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>>
make_ArrayHandleSOA(vtkm::Id length,
vtkm::CopyFlag copy,
const ComponentType* array0,
const RemainingArrays*... componentArrays)
{
return ArrayHandleSOA<
vtkm::Vec<ComponentType, vtkm::IdComponent(sizeof...(RemainingArrays) + 1)>>(

@ -344,6 +344,7 @@ public:
{
}
/// @brief Construct an `ArrayHandleStride` from a basic array with specified access patterns.
ArrayHandleStride(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>& array,
vtkm::Id numValues,
vtkm::Id stride,
@ -368,11 +369,44 @@ public:
{
}
/// @brief Get the stride that values are accessed.
///
/// The stride is the spacing between consecutive values. The stride is measured
/// in terms of the number of values. A stride of 1 means a fully packed array.
/// A stride of 2 means selecting every other values.
vtkm::Id GetStride() const { return StorageType::GetInfo(this->GetBuffers()).Stride; }
/// @brief Get the offset to start reading values.
///
/// The offset is the number of values to skip before the first value. The offset
/// is measured in terms of the number of values. An offset of 0 means the first value
/// at the beginning of the array.
///
/// The offset is unaffected by the stride and dictates where the strides starts
/// counting. For example, given an array with size 3 vectors packed into an array,
/// a strided array referencing the middle component will have offset 1 and stride 3.
vtkm::Id GetOffset() const { return StorageType::GetInfo(this->GetBuffers()).Offset; }
/// @brief Get the modulus of the array index.
///
/// When the index is modulo a value, it becomes the remainder after dividing by that
/// value. The effect of the modulus is to cause the index to repeat over the values
/// in the array.
///
/// If the modulo is set to 0, then it is ignored.
vtkm::Id GetModulo() const { return StorageType::GetInfo(this->GetBuffers()).Modulo; }
/// @brief Get the divisor of the array index.
///
/// The index is divided by the divisor before the other effects. The default divisor of
/// 1 will have no effect on the indexing. Setting the divisor to a value greater than 1
/// has the effect of repeating each value that many times.
vtkm::Id GetDivisor() const { return StorageType::GetInfo(this->GetBuffers()).Divisor; }
/// @brief Return the underlying data as a basic array handle.
///
/// It is common for the same basic array to be shared among multiple
/// `vtkm::cont::ArrayHandleStride` objects.
vtkm::cont::ArrayHandleBasic<T> GetBasicArray() const
{
return StorageType::GetBasicArray(this->GetBuffers());