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::Token is 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::Token is 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::Token is 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::Token is provided, the array is locked from being used by any read or write operations until the token goes out of scope.

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().

Example 4.72 Getting a standard C array from a basic array handle.
 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 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 component in a separate physical array. When data are retrieved from the array, they are reconstructed into Vec objects 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 (like float) for each component of the data being represented. The ArrayHandleSOA makes it easy to cover this second case without creating special types.

ArrayHandleSOA can be constructed from a collection of ArrayHandle with basic storage. This allows you to construct Vec arrays from components without deep copies.

Public Functions

inline ArrayHandleSOA(const std::array<ComponentArrayType, NUM_COMPONENTS> &componentArrays)

Construct an ArrayHandleSOA from 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 ArrayHandleSOA from 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 ArrayHandleSOA from 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 ArrayHandleSOA from 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 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>.

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

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 ArrayHandleSOA from 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 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*.

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.

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::ArrayHandleSOA with 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::ArrayHandleSOA with 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::ArrayHandleSOA with an initializer list of std::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::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>.

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

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::ArrayHandleSOA with a number of std::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::ArrayHandleSOA with 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::ArrayHandleSOA with 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);
Example 4.73 Creating an SOA array handle from component arrays.
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 ArrayHandle that accesses a basic array with strides and offsets.

ArrayHandleStride is a simple ArrayHandle that accesses data with a prescribed stride and offset. You specify the stride and offset at construction. So when a portal for this ArrayHandle Gets or Sets 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 ValueType of the array. So, an ArrayHandleStride<vtkm::Float32> with an offset of 1 will actually offset by 4 bytes (the size of a vtkm::Float32).

ArrayHandleStride is used to provide a unified type for pulling a component out of an ArrayHandle. 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 ArrayHandleStride from 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::ArrayHandleStride objects.

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 ArrayHandle of any type, ArrayExtractComponent returns an ArrayHandleStride of the base component type that contains the data for the specified array component. This function can be used to apply an operation on an ArrayHandle one component at a time. Because the array type is always ArrayHandleStride, you can drastically cut down on the number of templates to instantiate (at a possible cost to performance).

Note that ArrayExtractComponent will flatten out the indices of any vec value type and return an ArrayExtractComponent of the base component type. For example, if you call ArrayExtractComponent on an ArrayHandle with a value type of vtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 3>, you will get an ArrayExtractComponent<vtkm::Float32> returned. The componentIndex provided will be applied to the nested vector in depth first order. So in the previous example, a componentIndex of 0 gets the values at [0][0], componentIndex of 1 gets [0][1], componentIndex of 2 gets [1][0], and so on.

Some ArrayHandles allow this method to return an ArrayHandleStride that shares the same memory as the the original ArrayHandle. This form will be used if possible. In this case, if data are written into the ArrayHandleStride, they are also written into the original ArrayHandle. However, other forms will require copies into a new array. In this case, writes into ArrayHandleStride will not affect the original ArrayHandle.

For some operations, such as writing into an output array, this behavior of shared arrays is necessary. For this case, the optional argument allowCopy can be set to vtkm::CopyFlag::Off to prevent the copying behavior into the return ArrayHandleStride. If this is the case, an ErrorBadValue is thrown. If the arrays can be shared, they always will be regardless of the value of allowCopy.

Many forms of ArrayHandle have 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, ArrayHandle implementations should provide a specialization of vtkm::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 normal ArrayHandles because you have to specify the size of the Vecs as a template parameter at compile time. ArrayHandleRuntimeVec can be used in this case.

Note that caution should be used with ArrayHandleRuntimeVec because the size of the Vec values is not known at compile time. Thus, the value type of this array is forced to a special VecFromPortal class that can cause surprises if treated as a Vec. In particular, the static NUM_COMPONENTS expression does not exist. Furthermore, new variables of type VecFromPortal cannot 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 ArrayHandleBasic of the same component type as the underlying storage for this array. In this case, the array will be accessed much in the same manner as ArrayHandleGroupVec.

ArrayHandleRuntimeVec also allows you to convert the array to an ArrayHandleBasic of the appropriate Vec type (or component type). 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 ArrayHandleRuntimeVec with 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 ArrayHandleRuntimeVec to a simple ArrayHandleBasic. This is useful if the ArrayHandleRuntimeVec is passed to a routine that works on an array of a specific Vec size (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 ArrayHandleRuntimeVec to a simple ArrayHandleBasic. This is useful if the ArrayHandleRuntimeVec is passed to a routine that works on an array of a specific Vec size (or scalars). After a runtime check, the array can be converted to a typical array and used as such.

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_ArrayHandleRuntimeVec is convenience function to generate an ArrayHandleRuntimeVec.

It takes the number of components stored in each value’s Vec, which must be specified on the construction of the ArrayHandleRuntimeVec. If not specified, the number of components is set to 1. make_ArrayHandleRuntimeVec can also optionally take an existing array of components, which will be grouped into Vec values 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 ArrayHandleRuntimeVec with 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 ArrayHandleRuntimeVec from 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 the new[] 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 ArrayHandleRuntimeVec from an std::vector.

template<typename T, typename Allocator>
auto vtkm::cont::make_ArrayHandleRuntimeVecMove(vtkm::IdComponent numComponents, std::vector<T, Allocator> &&array)

Move an std::vector into an ArrayHandleRuntimeVec.

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.

Example 4.74 Loading a data with runtime component size and using with a static sized filter.
 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.

Example 4.75 Using 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 an ArrayHandle of vtkm::Vecs.

The main intention of ArrayHandleStride is to pull out a component of an ArrayHandle without knowing there ArrayHandle’s storage or vtkm::Vec shape. However, usually you want to do an operation on all the components together. ArrayHandleRecombineVec implements the functionality to easily take a group of extracted components and treat them as a single ArrayHandle of vtkm::Vec values.

Note that caution should be used with ArrayHandleRecombineVec because the size of the vtkm::Vec values is not known at compile time. Thus, the value type of this array is forced to a special RecombineVec class that can cause surprises if treated as a vtkm::Vec. In particular, the static NUM_COMPONENTS expression does not exist. Furthermore, new variables of type RecombineVec cannot 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.)