4.8. Memory Layout of Array Handles
Chapter 3.2 (Basic Array Handles) describes the basics of the vtkm::cont::ArrayHandle class, which is the interface to the arrays of data that VTK‑m operates on.
Recall that 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 VTK‑m can store and access arrays of data in different layouts.
4.8.1. Basic Memory Layout
If the second storage template parameter of 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, Section 3.2.1 (Creating Array Handles) provides examples of wrapping a default vtkm::cont::ArrayHandle around either a basic C array or a std::vector.
VTK‑m provides vtkm::cont::ArrayHandleBasic as a convenience class for working with basic array handles.
vtkm::cont::ArrayHandleBasic is a simple subclass of vtkm::cont::ArrayHandle with the default storage in the second template argument (which is vtkm::cont::StorageTagBasic).
vtkm::cont::ArrayHandleBasic and its superclass can be used more or less interchangeably.
-
template<typename T>
class ArrayHandleBasic : public vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> 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.
Public Functions
-
inline const T *GetReadPointer() const
Gets raw access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
-
inline const T *GetReadPointer(vtkm::cont::Token &token) const
Gets raw access to the
ArrayHandle’s data.- Parameters:
token – When a
vtkm::cont::Tokenis provided, the array is locked from being used by any write operations until the token goes out of scope.
-
inline T *GetWritePointer() const
Gets raw write access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
-
inline T *GetWritePointer(vtkm::cont::Token &token) const
Gets raw write access to the
ArrayHandle’s data.- Parameters:
token – When a
vtkm::cont::Tokenis provided, the array is locked from being used by any read or write operations until the token goes out of scope.
-
inline const T *GetReadPointer(vtkm::cont::DeviceAdapterId device) const
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.
- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
-
inline const T *GetReadPointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token &token) const
Gets raw access to the
ArrayHandle’s data.- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
token – When a
vtkm::cont::Tokenis provided, the array is locked from being used by any write operations until the token goes out of scope.
-
inline T *GetWritePointer(vtkm::cont::DeviceAdapterId device) const
Gets raw write access to the
ArrayHandle’s data.Note that the returned array may become invalidated by other operations on the ArryHandle.
- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
-
inline T *GetWritePointer(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token &token) const
Gets raw write access to the
ArrayHandle’s data.- Parameters:
device – The device ID or device tag specifying on which device the array will be valid on.
token – When a
vtkm::cont::Tokenis provided, the array is locked from being used by any read or write operations until the token goes out of scope.
-
inline const T *GetReadPointer() const
Because a vtkm::cont::ArrayHandleBasic represents arrays as a standard C array, it is possible to get a pointer to this array using either vtkm::cont::ArrayHandleBasic::GetReadPointer() or vtkm::cont::ArrayHandleBasic::GetWritePointer().
1void LegacyFunction(const int* data);
2
3void UseArrayWithLegacy(const vtkm::cont::ArrayHandle<vtkm::Int32> array)
4{
5 vtkm::cont::ArrayHandleBasic<vtkm::Int32> basicArray = array;
6 vtkm::cont::Token token; // Token prevents array from changing while in scope.
7 const int* cArray = basicArray.GetReadPointer(token);
8 LegacyFunction(cArray);
9 // When function returns, token goes out of scope and array can be modified.
10}
Did You Know?
When you get an array pointer this way, the vtkm::cont::ArrayHandle still has a reference to it.
If using multiple threads, you can use a 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.
Example 4.72 demonstrates using a vtkm::cont::Token.
4.8.2. Structure of Arrays
The basic vtkm::cont::ArrayHandle stores 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 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 vtkm::cont::ArrayHandleSOA, which is a subclass of vtkm::cont::StorageTagSOA.
-
template<typename T>
class ArrayHandleSOA : public vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagSOA> An
ArrayHandlethat for Vecs stores each component in a separate physical array.ArrayHandleSOAbehaves like a regularArrayHandle(with a basic storage) except that if you specify aValueTypeof aVecor aVec-like, it will actually store each component in a separate physical array. When data are retrieved from the array, they are reconstructed intoVecobjects as expected.The intention of this array type is to help cover the most common ways data is lain out in memory. Typically, arrays of data are either an “array of structures” like the basic storage where you have a single array of structures (like
Vec) or a “structure of arrays” where you have an array of a basic type (likefloat) for each component of the data being represented. TheArrayHandleSOAmakes it easy to cover this second case without creating special types.ArrayHandleSOAcan be constructed from a collection ofArrayHandlewith basic storage. This allows you to constructVecarrays from components without deep copies.Public Functions
-
inline ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.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);
-
inline ArrayHandleSOA(const std::vector<ComponentArrayType> &componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.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);
-
inline ArrayHandleSOA(std::initializer_list<ComponentArrayType> &&componentArrays)
Construct an
ArrayHandleSOAfrom a collection of component arrays.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 });
-
inline ArrayHandleSOA(std::initializer_list<std::vector<ComponentType>> &&componentVectors)
Construct an
ArrayHandleSOAfrom a collection of component arrays.The data is copied from the
std::vectors to the array handle.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 });
-
template<typename Allocator, typename ...RemainingVectors>
inline ArrayHandleSOA(vtkm::CopyFlag copy, const std::vector<ComponentType, Allocator> &vector0, RemainingVectors&&... componentVectors) Construct an
ArrayHandleSOAfrom a collection of component arrays.The first argument is a
vtkm::CopyFlagto 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 typestd::vector<ComponentType>.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);
-
template<typename ...RemainingVectors>
inline ArrayHandleSOA(vtkm::CopyFlag copy, std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Construct an
ArrayHandleSOAfrom a collection of component arrays.The first argument is a
vtkm::CopyFlagto 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 typestd::vector<ComponentType>.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);
-
inline ArrayHandleSOA(std::initializer_list<const ComponentType*> componentArrays, vtkm::Id length, vtkm::CopyFlag copy)
Construct an
ArrayHandleSOAfrom a collection of component arrays.T* components1; T* components2; T* components3; // Fill arrays... vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>>( { components1, components2, components3 }, size, vtkm::CopyFlag::On);
-
template<typename ...RemainingArrays>
inline ArrayHandleSOA(vtkm::Id length, vtkm::CopyFlag copy, const ComponentType *array0, const RemainingArrays&... componentArrays) Construct an
ArrayHandleSOAfrom a collection of component arrays.The component arrays are listed as arguments. This only works if all the templated arguments are of type
ComponentType*.T* components1; T* components2; T* components3; // Fill arrays... vtkm::cont::ArrayHandleSOA<vtkm::Vec<T, 3>> vecarray( size, vtkm::CopyFlag::On, components1, components2, components3);
-
inline vtkm::cont::ArrayHandleBasic<ComponentType> GetArray(vtkm::IdComponent index) const
Get a basic array representing the component for the given index.
-
inline void SetArray(vtkm::IdComponent index, const ComponentArrayType &array)
Replace a component array.
-
inline ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)
vtkm::cont::ArrayHandleSOA can be constructed and allocated just as a basic array handle.
Additionally, you can use its constructors or the vtkm::cont::make_ArrayHandleSOA() functions to build a vtkm::cont::ArrayHandleSOA from basic vtkm::cont::ArrayHandle’s that hold the components.
-
template<typename ValueType>
ArrayHandleSOA<ValueType> vtkm::cont::make_ArrayHandleSOA(std::initializer_list<vtkm::cont::ArrayHandle<typename vtkm::VecTraits<ValueType>::ComponentType, vtkm::cont::StorageTagBasic>> &&componentArrays) Create a
vtkm::cont::ArrayHandleSOAwith an initializer list of array handles.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 });
-
template<typename ComponentType, typename ...RemainingArrays>
ArrayHandleSOA<vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>> vtkm::cont::make_ArrayHandleSOA(const vtkm::cont::ArrayHandle<ComponentType, vtkm::cont::StorageTagBasic> &componentArray0, const RemainingArrays&... componentArrays) Create a
vtkm::cont::ArrayHandleSOAwith a number of array handles.This only works if all the templated arguments are of type
vtkm::cont::ArrayHandle<ComponentType>.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);
-
template<typename ValueType>
ArrayHandleSOA<ValueType> vtkm::cont::make_ArrayHandleSOA(std::initializer_list<std::vector<typename vtkm::VecTraits<ValueType>::ComponentType>> &&componentVectors) Create a
vtkm::cont::ArrayHandleSOAwith an initializer list ofstd::vector.The data is copied from the
std::vectors to the array handle.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 });
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag copy, const std::vector<ComponentType> &vector0, RemainingVectors&&... componentVectors) Create a
vtkm::cont::ArrayHandleSOAwith a number ofstd::vector.The first argument is a
vtkm::CopyFlagto 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 typestd::vector<ComponentType>.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);
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> vtkm::cont::make_ArrayHandleSOA(vtkm::CopyFlag copy, std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Create a
vtkm::cont::ArrayHandleSOAwith a number ofstd::vector.The first argument is a
vtkm::CopyFlagto 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 typestd::vector<ComponentType>.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);
-
template<typename ComponentType, typename ...RemainingVectors>
ArrayHandleSOA<vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingVectors...>::value>> vtkm::cont::make_ArrayHandleSOAMove(std::vector<ComponentType> &&vector0, RemainingVectors&&... componentVectors) Create a
vtkm::cont::ArrayHandleSOAwith a number ofstd::vector.This only works if all the templated arguments are rvalues of type
std::vector<ComponentType>.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));
-
template<typename ValueType>
ArrayHandleSOA<ValueType> vtkm::cont::make_ArrayHandleSOA(std::initializer_list<const typename vtkm::VecTraits<ValueType>::ComponentType*> &&componentVectors, vtkm::Id length, vtkm::CopyFlag copy) Create a
vtkm::cont::ArrayHandleSOAwith an initializer list of C arrays.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);
-
template<typename ComponentType, typename ...RemainingArrays>
ArrayHandleSOA<vtkm::Vec<ComponentType, internal::VecSizeFromRemaining<RemainingArrays...>::value>> vtkm::cont::make_ArrayHandleSOA(vtkm::Id length, vtkm::CopyFlag copy, const ComponentType *array0, const RemainingArrays*... componentArrays) Create a
vtkm::cont::ArrayHandleSOAwith a number of C arrays.This only works if all the templated arguments are of type
ComponentType*.T* components1; T* components2; T* components3; // Fill arrays... auto vecarray = vtkm::cont::make_ArrayHandleSOA( size, vtkm::CopyFlag::On, components1, components2, components3);
1 vtkm::cont::ArrayHandle<vtkm::FloatDefault> component1;
2 vtkm::cont::ArrayHandle<vtkm::FloatDefault> component2;
3 vtkm::cont::ArrayHandle<vtkm::FloatDefault> component3;
4 // Fill component arrays...
5
6 vtkm::cont::ArrayHandleSOA<vtkm::Vec3f> soaArray =
7 vtkm::cont::make_ArrayHandleSOA(component1, component2, component3);
Did You Know?
In addition to constructing a vtkm::cont::ArrayHandleSOA from its component arrays, you can get the component arrays back out using the vtkm::cont::ArrayHandleSOA::GetArray() method.
4.8.3. Strided Arrays
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 vtkm::cont::ArrayHandleStride class allows arrays with different data packing.
-
template<typename T>
class ArrayHandleStride : public vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagStride> An
ArrayHandlethat accesses a basic array with strides and offsets.ArrayHandleStrideis a simpleArrayHandlethat accesses data with a prescribed stride and offset. You specify the stride and offset at construction. So when a portal for thisArrayHandleGets orSets a value at a specific index, the value accessed in the underlying C array is:(index * stride) + offset
Optionally, you can also specify a modulo and divisor. If they are specified, the index mangling becomes:
(((index / divisor) % modulo) * stride) + offset
You can “disable” any of the aforementioned operations by setting them to the following values (most of which are arithmetic identities):
stride: 1
offset: 0
modulo: 0
divisor: 1
Note that all of these indices are referenced by the
ValueTypeof the array. So, anArrayHandleStride<vtkm::Float32>with an offset of 1 will actually offset by 4 bytes (the size of avtkm::Float32).ArrayHandleStrideis used to provide a unified type for pulling a component out of anArrayHandle. This way, you can iterate over multiple components in an array without having to implement a template instance for each vector size or representation.Public Functions
-
inline ArrayHandleStride(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> &array, vtkm::Id numValues, vtkm::Id stride, vtkm::Id offset, vtkm::Id modulo = 0, vtkm::Id divisor = 1)
Construct an
ArrayHandleStridefrom a basic array with specified access patterns.
-
inline vtkm::Id GetStride() const
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.
-
inline vtkm::Id GetOffset() const
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.
-
inline vtkm::Id GetModulo() const
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.
-
inline vtkm::Id GetDivisor() const
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.
-
inline vtkm::cont::ArrayHandleBasic<T> GetBasicArray() const
Return the underlying data as a basic array handle.
It is common for the same basic array to be shared among multiple
vtkm::cont::ArrayHandleStrideobjects.
The most common use of vtkm::cont::ArrayHandleStride is to pull components out of arrays.
vtkm::cont::ArrayHandleStride is seldom constructed directly.
Rather, VTK‑m has mechanisms to extract a component from an array.
To extract a component directly from a vtkm::cont::ArrayHandle, use vtkm::cont::ArrayExtractComponent().
-
template<typename T, typename S>
vtkm::cont::ArrayHandleStride<typename vtkm::VecTraits<T>::BaseComponentType> vtkm::cont::ArrayExtractComponent(const vtkm::cont::ArrayHandle<T, S> &src, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On) Pulls a component out of an
ArrayHandle.Given an
ArrayHandleof any type,ArrayExtractComponentreturns anArrayHandleStrideof the base component type that contains the data for the specified array component. This function can be used to apply an operation on anArrayHandleone component at a time. Because the array type is alwaysArrayHandleStride, you can drastically cut down on the number of templates to instantiate (at a possible cost to performance).Note that
ArrayExtractComponentwill flatten out the indices of any vec value type and return anArrayExtractComponentof the base component type. For example, if you callArrayExtractComponenton anArrayHandlewith a value type ofvtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 3>, you will get anArrayExtractComponent<vtkm::Float32>returned. ThecomponentIndexprovided will be applied to the nested vector in depth first order. So in the previous example, acomponentIndexof 0 gets the values at [0][0],componentIndexof 1 gets [0][1],componentIndexof 2 gets [1][0], and so on.Some
ArrayHandles allow this method to return anArrayHandleStridethat shares the same memory as the the originalArrayHandle. This form will be used if possible. In this case, if data are written into theArrayHandleStride, they are also written into the originalArrayHandle. However, other forms will require copies into a new array. In this case, writes intoArrayHandleStridewill not affect the originalArrayHandle.For some operations, such as writing into an output array, this behavior of shared arrays is necessary. For this case, the optional argument
allowCopycan be set tovtkm::CopyFlag::Offto prevent the copying behavior into the returnArrayHandleStride. If this is the case, anErrorBadValueis thrown. If the arrays can be shared, they always will be regardless of the value ofallowCopy.Many forms of
ArrayHandlehave optimized versions to pull out a component. Some, however, do not. In these cases, a fallback array copy, done in serial, will be performed. A warning will be logged to alert users of this likely performance bottleneck.As an implementation note, this function should not be overloaded directly. Instead,
ArrayHandleimplementations should provide a specialization ofvtkm::cont::internal::ArrayExtractComponentImpl.
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, vtkm::cont::ArrayHandleStride can represent a component from either a vtkm::cont::ArrayHandleBasic or a vtkm::cont::ArrayHandleSOA by just using different stride values.
This is used by vtkm::cont::UnknownArrayHandle::ExtractComponent() and elsewhere to create a concrete array handle class without knowing the actual class.
Common Errors
Many, but not all, of VTK‑m’s arrays can be represented by a vtkm::cont::ArrayHandleStride directly without copying.
If VTK‑m cannot easily create a 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.
4.8.4. Runtime Vec Arrays
Because many of the devices VTK‑m runs on cannot efficiently allocate memory while an algorithm is running, the data held in vtkm::cont::ArrayHandle’s are usually required to be a static size.
For example, the vtkm::Vec object often used as the value type for 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 vtkm::cont::ArrayHandleRuntimeVec.
This array handle stores data in the same way as vtkm::cont::ArrayHandleBasic with a vtkm::Vec value type, but the size of the Vec can be set at runtime.
-
template<typename ComponentType>
class ArrayHandleRuntimeVec : public vtkm::cont::ArrayHandle<vtkm::VecFromPortal<ArrayHandleBasic<ComponentType>::WritePortalType>, vtkm::cont::StorageTagRuntimeVec> 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
Vecs where the number of components is not known until runtime. This is problematic for normalArrayHandles because you have to specify the size of theVecs as a template parameter at compile time.ArrayHandleRuntimeVeccan be used in this case.Note that caution should be used with
ArrayHandleRuntimeVecbecause the size of theVecvalues is not known at compile time. Thus, the value type of this array is forced to a specialVecFromPortalclass that can cause surprises if treated as aVec. In particular, the staticNUM_COMPONENTSexpression does not exist. Furthermore, new variables of typeVecFromPortalcannot be created. This means that simple operators like+will not work because they require an intermediate object to be created. (Equal operators like+=do work because they are given an existing variable to place the output.)It is possible to provide an
ArrayHandleBasicof the same component type as the underlying storage for this array. In this case, the array will be accessed much in the same manner asArrayHandleGroupVec.ArrayHandleRuntimeVecalso allows you to convert the array to anArrayHandleBasicof the appropriateVectype (orcomponenttype). A runtime check will be performed to make sure the number of components matches.Public Functions
-
inline ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, const ComponentsArrayType &componentsArray = ComponentsArrayType{})
Construct an
ArrayHandleRuntimeVecwith a given number of components.- Parameters:
numComponents – The size of the
Vecs stored in the array. This must be specified at the time of construction.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.
-
inline vtkm::IdComponent GetNumberOfComponents() const
Return the number of components in each vec value.
-
inline vtkm::cont::ArrayHandleBasic<ComponentType> GetComponentsArray() const
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.
-
template<typename ValueType>
inline void AsArrayHandleBasic(vtkm::cont::ArrayHandle<ValueType> &array) const Converts the array to that of a basic array handle.
This method converts the
ArrayHandleRuntimeVecto a simpleArrayHandleBasic. This is useful if theArrayHandleRuntimeVecis passed to a routine that works on an array of a specificVecsize (or scalars). After a runtime check, the array can be converted to a typical array and used as such.
-
template<typename ArrayType>
inline ArrayType AsArrayHandleBasic() const Converts the array to that of a basic array handle.
This method converts the
ArrayHandleRuntimeVecto a simpleArrayHandleBasic. This is useful if theArrayHandleRuntimeVecis passed to a routine that works on an array of a specificVecsize (or scalars). After a runtime check, the array can be converted to a typical array and used as such.
-
inline ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, const ComponentsArrayType &componentsArray = ComponentsArrayType{})
A vtkm::cont::ArrayHandleRuntimeVec is easily created from existing data using one of the vtkm::cont::make_ArrayHandleRuntimeVec() functions.
-
template<typename T>
auto vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> &componentsArray = vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>{}) make_ArrayHandleRuntimeVecis convenience function to generate anArrayHandleRuntimeVec.It takes the number of components stored in each value’s
Vec, which must be specified on the construction of theArrayHandleRuntimeVec. If not specified, the number of components is set to 1.make_ArrayHandleRuntimeVeccan also optionally take an existing array of components, which will be grouped intoVecvalues based on the specified number of components.
-
template<typename T>
auto vtkm::cont::make_ArrayHandleRuntimeVec(const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic> &componentsArray) Converts a basic array handle into an
ArrayHandleRuntimeVecwith 1 component.The constructed array is essentially equivalent but of a different type.
VTK‑m also provides several convenience functions to convert a basic C array or std::vector to a vtkm::cont::ArrayHandleRuntimeVec.
-
template<typename T>
auto vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, const T *array, vtkm::Id numberOfValues, vtkm::CopyFlag copy) A convenience function for creating an
ArrayHandleRuntimeVecfrom a standard C array.
-
template<typename T>
auto vtkm::cont::make_ArrayHandleRuntimeVecMove(vtkm::IdComponent numComponents, T *&array, vtkm::Id numberOfValues, vtkm::cont::internal::BufferInfo::Deleter deleter = internal::SimpleArrayDeleter<T>, vtkm::cont::internal::BufferInfo::Reallocater reallocater = internal::SimpleArrayReallocater<T>) A convenience function to move a user-allocated array into an
ArrayHandleRuntimeVec.The provided array pointer will be reset to
nullptr. If the array was not allocated with thenew[]operator, then deleter and reallocater functions must be provided.
-
template<typename T, typename Allocator>
auto vtkm::cont::make_ArrayHandleRuntimeVec(vtkm::IdComponent numComponents, const std::vector<T, Allocator> &array, vtkm::CopyFlag copy) A convenience function for creating an
ArrayHandleRuntimeVecfrom anstd::vector.
-
template<typename T, typename Allocator>
auto vtkm::cont::make_ArrayHandleRuntimeVecMove(vtkm::IdComponent numComponents, std::vector<T, Allocator> &&array) Move an
std::vectorinto anArrayHandleRuntimeVec.
The advantage of this class is that a 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 vtkm::cont::ArrayHandle with a vtkm::Vec of the correct size.
This often consists of a file reader or other data ingestion creating vtkm::cont::ArrayHandleRuntimeVec objects and storing them in vtkm::cont::UnknownArrayHandle, which is used as an array container for vtkm::cont::DataSet.
Filters that then subsequently operate on the vtkm::cont::DataSet can retrieve the data as a vtkm::cont::ArrayHandle of the appropriate vtkm::Vec size.
1void ReadArray(std::vector<float>& data, int& numComponents);
2
3vtkm::cont::UnknownArrayHandle LoadData()
4{
5 // Read data from some external source where the vector size is determined at runtime.
6 std::vector<vtkm::Float32> data;
7 int numComponents;
8 ReadArray(data, numComponents);
9
10 // Resulting ArrayHandleRuntimeVec gets wrapped in an UnknownArrayHandle
11 return vtkm::cont::make_ArrayHandleRuntimeVecMove(
12 static_cast<vtkm::IdComponent>(numComponents), std::move(data));
13}
14
15void UseVecArray(const vtkm::cont::UnknownArrayHandle& array)
16{
17 using ExpectedArrayType = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
18 if (!array.CanConvert<ExpectedArrayType>())
19 {
20 throw vtkm::cont::ErrorBadType("Array unexpected type.");
21 }
22
23 ExpectedArrayType concreteArray = array.AsArrayHandle<ExpectedArrayType>();
24 // Do something with concreteArray...
25}
26
27void LoadAndRun()
28{
29 // Load data in a routine that does not know component size until runtime.
30 vtkm::cont::UnknownArrayHandle array = LoadData();
31
32 // Use the data in a method that requires an array of static size.
33 // This will work as long as the `Vec` size matches correctly (3 in this case).
34 UseVecArray(array);
35}
Did You Know?
Wrapping a basic array in a vtkm::cont::ArrayHandleRuntimeVec has a similar effect as wrapping the array in a 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 vtkm::cont::ArrayHandleGroupVec should be used.
However, if the Vec size is not known or the array will be stored in an object like vtkm::cont::UnknownArrayHandle, then vtkm::cont::ArrayHandleRuntimeVec is a better choice.
It is also possible to get a vtkm::cont::ArrayHandleRuntimeVec from a 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.
vtkm::cont::ArrayHandleRuntimeVec to get an array regardless of the size of the contained vtkm::Vec values. 1template<typename T>
2void WriteData(const T* data, std::size_t size, int numComponents);
3
4void WriteVTKmArray(const vtkm::cont::UnknownArrayHandle& array)
5{
6 bool writeSuccess = false;
7 auto doWrite = [&](auto componentType) {
8 using ComponentType = decltype(componentType);
9 using VecArrayType = vtkm::cont::ArrayHandleRuntimeVec<ComponentType>;
10 if (array.CanConvert<VecArrayType>())
11 {
12 // Get the array as a runtime Vec.
13 VecArrayType runtimeVecArray = array.AsArrayHandle<VecArrayType>();
14
15 // Get the component array.
16 vtkm::cont::ArrayHandleBasic<ComponentType> componentArray =
17 runtimeVecArray.GetComponentsArray();
18
19 // Use the general function to write the data.
20 WriteData(componentArray.GetReadPointer(),
21 componentArray.GetNumberOfValues(),
22 runtimeVecArray.GetNumberOfComponentsFlat());
23
24 writeSuccess = true;
25 }
26 };
27
28 // Figure out the base component type, retrieve the data (regardless
29 // of vec size), and write out the data.
30 vtkm::ListForEach(doWrite, vtkm::TypeListBaseC{});
31}
4.8.5. Recombined Vec Arrays of Strided Components
VTK‑m contains a special array, vtkm::cont::ArrayHandleRecombineVec, to combine component arrays represented in vtkm::cont::ArrayHandleStride together to form Vec values.
vtkm::cont::ArrayHandleRecombineVec is similar to vtkm::cont::ArrayHandleSOA (see Section 4.8.2 (Structure of Arrays)) except that (1) it holds stride arrays for its components instead of basic arrays and that (2) the number of components can be specified at runtime.
vtkm::cont::ArrayHandleRecombineVec is mainly provided for the implementation of extracting arrays out of a vtkm::cont::UnknownArrayHandle (see Section 3.5.4.3 (Extracting All Components)).
-
template<typename ComponentType>
class ArrayHandleRecombineVec : public vtkm::cont::ArrayHandle<internal::detail::RecombinedValueType<ComponentType>, vtkm::cont::internal::StorageTagRecombineVec> A grouping of
ArrayHandleStrides into anArrayHandleofvtkm::Vecs.The main intention of
ArrayHandleStrideis to pull out a component of anArrayHandlewithout knowing thereArrayHandle’s storage orvtkm::Vecshape. However, usually you want to do an operation on all the components together.ArrayHandleRecombineVecimplements the functionality to easily take a group of extracted components and treat them as a singleArrayHandleofvtkm::Vecvalues.Note that caution should be used with
ArrayHandleRecombineVecbecause the size of thevtkm::Vecvalues is not known at compile time. Thus, the value type of this array is forced to a specialRecombineVecclass that can cause surprises if treated as avtkm::Vec. In particular, the staticNUM_COMPONENTSexpression does not exist. Furthermore, new variables of typeRecombineVeccannot be created. This means that simple operators like+will not work because they require an intermediate object to be created. (Equal operators like+=do work because they are given an existing variable to place the output.)