3.2. Basic Array Handles
Chapter 2.4 (Data Sets) describes the basic data sets used by VTK‑m.
This chapter dives deeper into how VTK‑m represents data.
Ultimately, data structures like vtkm::cont::DataSet can be broken down into arrays of numbers.
Arrays in VTK‑m are managed by a unit called an array handle.
An array handle, which is implemented with the vtkm::cont::ArrayHandle class, manages an array of data that can be accessed or manipulated by VTK‑m algorithms.
It is typical to construct an array handle in the control environment to pass data to an algorithm running in the execution environment.
It is also typical for an algorithm running in the execution environment to populate an array handle, which can then be read back in the control environment.
It is also possible for an array handle to manage data created by one VTK‑m algorithm and passed to another, remaining in the execution environment the whole time and never copied to the control environment.
Did You Know?
The array handle may have multiple copies of the array, one for the control environment and one for each device. However, depending on the device and how the array is being used, the array handle will only have one copy when possible. Copies between the environments are implicit and lazy. They are copied only when an operation needs data in an environment where the data are not.
vtkm::cont::ArrayHandle behaves like a shared smart pointer in that when the C++ object is copied, each copy holds a reference to the same array.
These copies are reference counted so that when all copies of the vtkm::cont::ArrayHandle are destroyed, any allocated memory is released.
-
template<typename T, typename StorageTag_ = ::vtkm::cont::StorageTagBasic>
class ArrayHandle : public vtkm::cont::internal::ArrayHandleBase Manages an array-worth of data.
ArrayHandlemanages as array of data that can be manipulated by VTKm algorithms. TheArrayHandlemay have up to two copies of the array, one for the control environment and one for the execution environment, although depending on the device and how the array is being used, theArrayHandlewill only have one copy when possible.An
ArrayHandleis often constructed by instantiating one of theArrayHandlesubclasses. Several basicArrayHandletypes can also be constructed directly and then allocated. TheArrayHandleBasicsubclass provides mechanisms for importing user arrays into anArrayHandle.ArrayHandlebehaves like a shared smart pointer in that when it is copied each copy holds a reference to the same array. These copies are reference counted so that when all copies of theArrayHandleare destroyed, any allocated memory is released.Subclassed by vtkm::cont::ArrayHandleImplicit< detail::PhiloxFunctor >, vtkm::cont::ArrayHandleRuntimeVec< vtkm::Float32 >, vtkm::cont::ArrayHandleTransform< vtkm::cont::ArrayHandleRandomUniformBits, detail::CanonicalFunctor< vtkm::Float64 > >, vtkm::cont::ArrayHandleTransform< vtkm::cont::ArrayHandleZip< vtkm::cont::ArrayHandleRandomUniformReal< vtkm::Float64 >, vtkm::cont::ArrayHandleRandomUniformReal< vtkm::Float64 > >, detail::BoxMuller >, vtkm::cont::ArrayHandleCartesianProduct< FirstHandleType, SecondHandleType, ThirdHandleType >, vtkm::cont::ArrayHandleDecorator< DecoratorImplT, ArrayTs >, vtkm::cont::ArrayHandleDiscard< ValueType_ >, vtkm::cont::ArrayHandleImplicit< FunctorType >, vtkm::cont::ArrayHandleZip< FirstHandleType, SecondHandleType >
Public Types
-
using ReadPortalType = typename StorageType::ReadPortalType
The type of portal used when accessing data in a read-only mode.
-
using WritePortalType = typename StorageType::WritePortalType
The type of portal used when accessing data in a read-write mode.
Public Functions
-
inline ArrayHandle()
Constructs an empty ArrayHandle.
-
inline ArrayHandle(const vtkm::cont::ArrayHandle<ValueType, StorageTag> &src)
Copy constructor.
Implemented so that it is defined exclusively in the control environment. If there is a separate device for the execution environment (for example, with CUDA), then the automatically generated copy constructor could be created for all devices, and it would not be valid for all devices.
-
inline ArrayHandle(vtkm::cont::ArrayHandle<ValueType, StorageTag> &&src) noexcept
Move constructor.
Implemented so that it is defined exclusively in the control environment. If there is a separate device for the execution environment (for example, with CUDA), then the automatically generated move constructor could be created for all devices, and it would not be valid for all devices.
-
inline explicit ArrayHandle(const std::vector<vtkm::cont::internal::Buffer> &buffers)
Special constructor for subclass specializations that need to set the initial state array.
Used when pulling data from other sources.
-
inline explicit ArrayHandle(std::vector<vtkm::cont::internal::Buffer> &&buffers) noexcept
Special constructor for subclass specializations that need to set the initial state array.
Used when pulling data from other sources.
-
inline ~ArrayHandle()
Destructs an empty ArrayHandle.
Implemented so that it is defined exclusively in the control environment. If there is a separate device for the execution environment (for example, with CUDA), then the automatically generated destructor could be created for all devices, and it would not be valid for all devices.
-
inline vtkm::cont::ArrayHandle<ValueType, StorageTag> &operator=(const vtkm::cont::ArrayHandle<ValueType, StorageTag> &src)
Shallow copies an ArrayHandle.
-
inline vtkm::cont::ArrayHandle<ValueType, StorageTag> &operator=(vtkm::cont::ArrayHandle<ValueType, StorageTag> &&src) noexcept
Move and Assignment of an ArrayHandle.
-
inline bool operator==(const ArrayHandle<ValueType, StorageTag> &rhs) const
Like a pointer, two
ArrayHandles are considered equal if they point to the same location in memory.
-
inline StorageType GetStorage() const
Get the storage.
-
inline ReadPortalType ReadPortal() const
Get an array portal that can be used in the control environment.
The returned array can be used in the control environment to read values from the array. (It is not possible to write to the returned portal. That is
Getwill work on the portal, butSetwill not.)Note: The returned portal cannot be used in the execution environment. This is because the portal will not work on some devices like GPUs. To get a portal that will work in the execution environment, use
PrepareForInput.
-
inline ReadPortalType ReadPortal(vtkm::cont::Token &token) const
The type of portal used when accessing data in a read-only mode.
-
inline WritePortalType WritePortal() const
Get an array portal that can be used in the control environment.
The returned array can be used in the control environment to reand and write values to the array.
Note: The returned portal cannot be used in the execution environment. This is because the portal will not work on some devices like GPUs. To get a portal that will work in the execution environment, use
PrepareForInput.
-
inline WritePortalType WritePortal(vtkm::cont::Token &token) const
Get an array portal that can be used in the control environment.
The returned array can be used in the control environment to reand and write values to the array.
Note: The returned portal cannot be used in the execution environment. This is because the portal will not work on some devices like GPUs. To get a portal that will work in the execution environment, use
PrepareForInput.
-
inline vtkm::IdComponent GetNumberOfComponentsFlat() const
Returns the total number of components for each value in the array.
If the array holds
vtkm::Vecobjects, this will return the total number of components in each value assuming the object is flattened out to one level ofVecobjects. If the array holds a basic C type (such asfloat), this will return 1. If the array holds a simpleVec(such asvtkm::Vec3f), this will return the number of components (in this case 3). If the array holds a hierarchy ofVecs (such asvtkm::Vec<vtkm::Vec3f, 2>), this will return the total number of vecs (in this case 6).If this object is holding an array where the number of components can be selected at runtime (for example,
vtkm::cont::ArrayHandleRuntimeVec), this method will still return the correct number of components. However, if each value in the array can be aVecof a different size (such asvtkm::cont::ArrayHandleGroupVecVariable), this method will return 0 (because there is no consistent answer).
-
inline void Allocate(vtkm::Id numberOfValues, vtkm::CopyFlag preserve, vtkm::cont::Token &token) const
Allocates an array large enough to hold the given number of values.
The allocation may be done on an already existing array. If so, then the data are preserved as best as possible if the preserve flag is set to
vtkm::CopyFlag::On. If the preserve flag is set tovtkm::CopyFlag::Off(the default), any existing data could be wiped out.This method can throw
vtkm::cont::ErrorBadAllocationif the array cannot be allocated orvtkm::cont::ErrorBadValueif the allocation is not feasible (for example, the array storage is read-only).
-
inline void Allocate(vtkm::Id numberOfValues, vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const
Allocates an array large enough to hold the given number of values.
The allocation may be done on an already existing array. If so, then the data are preserved as best as possible if the preserve flag is set to
vtkm::CopyFlag::On. If the preserve flag is set tovtkm::CopyFlag::Off(the default), any existing data could be wiped out.This method can throw
vtkm::cont::ErrorBadAllocationif the array cannot be allocated orvtkm::cont::ErrorBadValueif the allocation is not feasible (for example, the array storage is read-only).
-
inline void AllocateAndFill(vtkm::Id numberOfValues, const ValueType &fillValue, vtkm::CopyFlag preserve, vtkm::cont::Token &token) const
Allocates an array and fills it with an initial value.
AllocateAndFillbehaves similar toAllocateexcept that after allocation it fills the array with a givenfillValue. This method is convenient when you wish to initialize the array.If the
preserveflag isvtkm::CopyFlag::On, then any data that existed before the call toAllocateAndFillwill remain after the call (assuming the new array size is large enough). If the array size is expanded, then the new values at the end will be filled.If the
preserveflag isvtkm::CopyFlag::Off(the default), the entire array is filled with the givenfillValue.
-
inline void AllocateAndFill(vtkm::Id numberOfValues, const ValueType &fillValue, vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const
Allocates an array and fills it with an initial value.
AllocateAndFillbehaves similar toAllocateexcept that after allocation it fills the array with a givenfillValue. This method is convenient when you wish to initialize the array.If the
preserveflag isvtkm::CopyFlag::On, then any data that existed before the call toAllocateAndFillwill remain after the call (assuming the new array size is large enough). If the array size is expanded, then the new values at the end will be filled.If the
preserveflag isvtkm::CopyFlag::Off(the default), the entire array is filled with the givenfillValue.
-
inline void Fill(const ValueType &fillValue, vtkm::Id startIndex, vtkm::Id endIndex, vtkm::cont::Token &token) const
Fills the array with a given value.
After calling this method, every entry in the array from
startIndex(inclusive) toendIndex(exclusive) of the array is set tofillValue. IfstartIndexorendIndexis not specified, then the fill happens from the begining or end, respectively.
-
inline void Fill(const ValueType &fillValue, vtkm::Id startIndex, vtkm::Id endIndex) const
Fills the array with a given value.
After calling this method, every entry in the array from
startIndex(inclusive) toendIndex(exclusive) of the array is set tofillValue. IfstartIndexorendIndexis not specified, then the fill happens from the begining or end, respectively.
-
inline void Fill(const ValueType &fillValue, vtkm::Id startIndex = 0) const
Fills the array with a given value.
After calling this method, every entry in the array from
startIndex(inclusive) toendIndex(exclusive) of the array is set tofillValue. IfstartIndexorendIndexis not specified, then the fill happens from the begining or end, respectively.
-
inline void ReleaseResourcesExecution() const
Releases any resources being used in the execution environment (that are not being shared by the control environment).
-
inline void ReleaseResources() const
Releases all resources in both the control and execution environments.
-
inline ReadPortalType PrepareForInput(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token &token) const
Prepares this array to be used as an input to an operation in the execution environment.
If necessary, copies data to the execution environment. Can throw an exception if this array does not yet contain any data. Returns a portal that can be used in code running in the execution environment.
The
Tokenobject provided will be attached to thisArrayHandle. The returned portal is guaranteed to be valid while theTokenis still attached and in scope. Other operations on thisArrayHandlethat would invalidate the returned portal will block until theTokenis released. Likewise, this method will block if anotherTokenis already attached. This can potentially lead to deadlocks.
-
inline WritePortalType PrepareForInPlace(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token &token) const
Prepares this array to be used in an in-place operation (both as input and output) in the execution environment.
If necessary, copies data to the execution environment. Can throw an exception if this array does not yet contain any data. Returns a portal that can be used in code running in the execution environment.
The
Tokenobject provided will be attached to thisArrayHandle. The returned portal is guaranteed to be valid while theTokenis still attached and in scope. Other operations on thisArrayHandlethat would invalidate the returned portal will block until theTokenis released. Likewise, this method will block if anotherTokenis already attached. This can potentially lead to deadlocks.
-
inline WritePortalType PrepareForOutput(vtkm::Id numberOfValues, vtkm::cont::DeviceAdapterId device, vtkm::cont::Token &token) const
Prepares (allocates) this array to be used as an output from an operation in the execution environment.
The internal state of this class is set to have valid data in the execution array with the assumption that the array will be filled soon (i.e. before any other methods of this object are called). Returns a portal that can be used in code running in the execution environment.
The
Tokenobject provided will be attached to thisArrayHandle. The returned portal is guaranteed to be valid while theTokenis still attached and in scope. Other operations on thisArrayHandlethat would invalidate the returned portal will block until theTokenis released. Likewise, this method will block if anotherTokenis already attached. This can potentially lead to deadlocks.
-
inline bool IsOnDevice(vtkm::cont::DeviceAdapterId device) const
Returns true if the ArrayHandle’s data is on the given device.
If the data are on the given device, then preparing for that device should not require any data movement.
-
inline bool IsOnHost() const
Returns true if the ArrayHandle’s data is on the host.
If the data are on the given device, then calling
ReadPortalorWritePortalshould not require any data movement.
-
inline void SyncControlArray() const
Synchronizes the control array with the execution array.
If either the user array or control array is already valid, this method does nothing (because the data is already available in the control environment). Although the internal state of this class can change, the method is declared const because logically the data does not.
-
inline void Enqueue(const vtkm::cont::Token &token) const
Enqueue a token for access to this ArrayHandle.
This method places the given
Tokeninto the queue ofTokens waiting for access to thisArrayHandleand then returns immediately. When this token is later used to get data from thisArrayHandle(for example, in a call toPrepareForInput), it will use this place in the queue while waiting for access.This method is to be used to ensure that a set of accesses to an
ArrayHandlethat happen on multiple threads occur in a specified order. For example, if you spawn of a job to modify data in anArrayHandleand then spawn off a job that reads that same data, you need to make sure that the first job gets access to theArrayHandlebefore the second. If they both just attempt to call their respectivePreparemethods, there is no guarantee which order they will occur. Having the spawning thread first call this method will ensure the order.Warning
After calling this method it is required to subsequently call a method like one of the
Preparemethods that attaches the token to thisArrayHandle. Otherwise, the enqueued token will block any subsequent access to theArrayHandle, even if theTokenis destroyed.
-
inline void DeepCopyFrom(const vtkm::cont::ArrayHandle<ValueType, StorageTag> &source) const
Deep copies the data in the array.
Takes the data that is in source and copies that data into this array.
-
inline const std::vector<vtkm::cont::internal::Buffer> &GetBuffers() const
Returns the internal
Bufferstructures that hold the data.Note that great care should be taken when modifying buffers outside of the ArrayHandle.
-
using ReadPortalType = typename StorageType::ReadPortalType
3.2.1. Creating Array Handles
vtkm::cont::ArrayHandle is templated on the type of values being stored in the array.
There are multiple ways to create and populate an array handle.
The default vtkm::cont::ArrayHandle constructor will create an empty array with nothing allocated in either the control or execution environment.
This is convenient for creating arrays used as the output for algorithms.
1 vtkm::cont::ArrayHandle<vtkm::Float32> outputArray;
There are times when you will wish to create a vtkm::cont::ArrayHandle populated with existing data.
This can be done with the vtkm::cont::make_ArrayHandle() function.
vtkm::cont::make_ArrayHandle() has many forms.
An easy form to use takes an initializer list and creates a basic vtkm::cont::ArrayHandle with it.
This allows you to create a short vtkm::cont::ArrayHandle from literals.
-
template<typename T>
vtkm::cont::ArrayHandleBasic<T> vtkm::cont::make_ArrayHandle(std::initializer_list<T> &&values) Create an ArrayHandle directly from an initializer list of values.
1 auto fibonacciArray = vtkm::cont::make_ArrayHandle({ 0, 1, 1, 2, 3, 5, 8, 13 });
One problem with creating an array from an initializer list like this is that it can be tricky to specify the exact value type of the vtkm::cont::ArrayHandle.
The value type of the vtkm::cont::ArrayHandle will be the same types as the literals in the initializer list, but that might not match the type you actually need.
This is particularly true for types like vtkm::Id and vtkm::FloatDefault, which can change depending on compile options.
To specify the exact value type to use, give that type as a template argument to the vtkm::cont::make_ArrayHandle() function.
1 vtkm::cont::ArrayHandle<vtkm::FloatDefault> inputArray =
2 vtkm::cont::make_ArrayHandle<vtkm::FloatDefault>({ 1.4142f, 2.7183f, 3.1416f });
Constructing an vtkm::cont::ArrayHandle that points to a provided C array is also straightforward.
To do this, call vtkm::cont::make_ArrayHandle() with the array pointer, the number of values in the C array, and a vtkm::CopyFlag.
This last argument can be either vtkm::CopyFlag::On to copy the array or vtkm::CopyFlag::Off to share the provided buffer.
-
template<typename T>
vtkm::cont::ArrayHandleBasic<T> vtkm::cont::make_ArrayHandle(const T *array, vtkm::Id numberOfValues, vtkm::CopyFlag copy) A convenience function for creating an ArrayHandle from a standard C array.
-
enum class vtkm::CopyFlag
Identifier used to specify whether a function should deep copy data.
Values:
-
enumerator Off
-
enumerator On
-
enumerator Off
1 vtkm::Float32 dataBuffer[50];
2 // Populate dataBuffer with meaningful data. Perhaps read data from a file.
3
4 vtkm::cont::ArrayHandle<vtkm::Float32> inputArray =
5 vtkm::cont::make_ArrayHandle(dataBuffer, 50, vtkm::CopyFlag::On);
Likewise, you can use vtkm::cont::make_ArrayHandle() to transfer data from a std::vector to an vtkm::cont::ArrayHandle.
This form of vtkm::cont::make_ArrayHandle() takes the std::vector as the first argument and a vtkm::CopyFlag as the second argument.
-
template<typename T, typename Allocator>
vtkm::cont::ArrayHandleBasic<T> vtkm::cont::make_ArrayHandle(const std::vector<T, Allocator> &array, vtkm::CopyFlag copy) A convenience function for creating an ArrayHandle from an std::vector.
1 std::vector<vtkm::Float32> dataBuffer;
2 // Populate dataBuffer with meaningful data. Perhaps read data from a file.
3
4 vtkm::cont::ArrayHandle<vtkm::Float32> inputArray =
5 vtkm::cont::make_ArrayHandle(dataBuffer, vtkm::CopyFlag::On);
As hinted at earlier, it is possible to send vtkm::CopyFlag::On to vtkm::cont::make_ArrayHandle() to wrap an vtkm::cont::ArrayHandle around an existing C array or std::vector.
Doing so allows you to send the data to the vtkm::cont::ArrayHandle without copying it.
It also provides a mechanism for VTK‑m to write directly into your array.
However, be aware that if you change or delete the data provided, the internal state of vtkm::cont::ArrayHandle becomes invalid and undefined behavior can ensue.
A common manifestation of this error happens when a std::vector goes out of scope.
This subtle interaction will cause the vtkm::cont::ArrayHandle to point to an unallocated portion of the memory heap.
The following example provides an erroneous use of vtkm::cont::ArrayHandle and some ways to fix it.
1VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Float32> BadDataLoad()
2{
3 std::vector<vtkm::Float32> dataBuffer;
4 // Populate dataBuffer with meaningful data. Perhaps read data from a file.
5
6 vtkm::cont::ArrayHandle<vtkm::Float32> inputArray =
7 vtkm::cont::make_ArrayHandle(dataBuffer, vtkm::CopyFlag::Off);
8
9 return inputArray;
10 // THIS IS WRONG! At this point dataBuffer goes out of scope and deletes its
11 // memory. However, inputArray has a pointer to that memory, which becomes an
12 // invalid pointer in the returned object. Bad things will happen when the
13 // ArrayHandle is used.
14}
15
16VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Float32> SafeDataLoad1()
17{
18 std::vector<vtkm::Float32> dataBuffer;
19 // Populate dataBuffer with meaningful data. Perhaps read data from a file.
20
21 vtkm::cont::ArrayHandle<vtkm::Float32> inputArray =
22 vtkm::cont::make_ArrayHandle(dataBuffer, vtkm::CopyFlag::On);
23
24 return inputArray;
25 // This is safe.
26}
27
28VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Float32> SafeDataLoad2()
29{
30 std::vector<vtkm::Float32> dataBuffer;
31 // Populate dataBuffer with meaningful data. Perhaps read data from a file.
32
33 vtkm::cont::ArrayHandle<vtkm::Float32> inputArray =
34 vtkm::cont::make_ArrayHandleMove(std::move(dataBuffer));
35
36 return inputArray;
37 // This is safe.
38}
An easy way around the problem of having an vtkm::cont::ArrayHandle’s data going out of scope is to copy the data into the vtkm::cont::ArrayHandle.
Simply make the vtkm::CopyFlag argument be vtkm::CopyFlag::On to copy the data.
This solution is shown in Example 3.8, line 22.
What if you have a std::vector that you want to pass to an vtkm::cont::ArrayHandle and then want to only use in the vtkm::cont::ArrayHandle?
In this case, it is wasteful to have to copy the data, but you also do not want to be responsible for keeping the std::vector in scope.
To handle this, there is a special vtkm::cont::make_ArrayHandleMove() that will move the memory out of the std::vector and into the vtkm::cont::ArrayHandle.
vtkm::cont::make_ArrayHandleMove() takes an “rvalue” version of a std::vector.
To create an “rvalue”, use the std::move function provided by C++.
Once vtkm::cont::make_ArrayHandleMove() is called, the provided std::vector becomes invalid and any further access to it is undefined.
This solution is shown in :exlineref:ex:ArrayOutOfScope:MoveVector`.
-
template<typename T, typename Allocator>
vtkm::cont::ArrayHandleBasic<T> vtkm::cont::make_ArrayHandleMove(std::vector<T, Allocator> &&array) Move an std::vector into an ArrayHandle.
-
template<typename T, typename Allocator>
vtkm::cont::ArrayHandleBasic<T> vtkm::cont::make_ArrayHandle(std::vector<T, Allocator> &&array, vtkm::CopyFlag) Move an std::vector into an ArrayHandle.
3.2.2. Allocating
vtkm::cont::ArrayHandle is capable of allocating its own memory.
The most straightforward way to allocate memory is to call the vtkm::cont::ArrayHandle::Allocate() method.
The vtkm::cont::ArrayHandle::Allocate() method takes a single argument, which is the number of elements to make the array.
1 vtkm::cont::ArrayHandle<vtkm::Float32> arrayHandle;
2
3 const vtkm::Id ARRAY_SIZE = 50;
4 arrayHandle.Allocate(ARRAY_SIZE);
By default when you vtkm::cont::ArrayHandle::Allocate() an array, it potentially destroys any existing data in it.
However, there are cases where you wish to grow or shrink an array while preserving the existing data.
To preserve the existing data when allocating an array, pass vtkm::CopyFlag::On as an optional second argument.
1 // Add space for 10 more values at the end of the array.
2 arrayHandle.Allocate(arrayHandle.GetNumberOfValues() + 10, vtkm::CopyFlag::On);
It is also possible to initialize new values in an allocated vtkm::cont::ArrayHandle by using the vtkm::cont::ArrayHandle::AllocateAndFill() method.
Did You Know?
The ability to allocate memory is a key difference between vtkm::cont::ArrayHandle and many other common forms of smart pointers.
When one vtkm::cont::ArrayHandle allocates new memory, all other vtkm::cont::ArrayHandle’s pointing to the same managed memory get the newly allocated memory.
This feature makes it possible to pass a vtkm::cont::ArrayHandle to a method to be reallocated and filled without worrying about C++ details on how to reference the vtkm::cont::ArrayHandle object itself.
3.2.3. Deep Array Copies
As stated previously, an vtkm::cont::ArrayHandle object behaves as a smart pointer that copies references to the data without copying the data itself.
This is clearly faster and more memory efficient than making copies of the data itself and usually the behavior desired.
However, it is sometimes the case that you need to make a separate copy of the data.
The easiest way to copy an vtkm::cont::ArrayHandle is to use the vtkm::cont::ArrayHandle::DeepCopyFrom() method.
1 destArray.DeepCopyFrom(srcArray);
However, the vtkm::cont::ArrayHandle::DeepCopyFrom() method only works if the two vtkm::cont::ArrayHandle objects are the exact same type.
To simplify copying the data between vtkm::cont::ArrayHandle objects of different types, VTK‑m comes with the vtkm::cont::ArrayCopy() convenience function defined in vtkm/cont/ArrayCopy.h.
vtkm::cont::ArrayCopy() takes the array to copy from (the source) as its first argument and the array to copy to (the destination) as its second argument.
The destination array will be properly reallocated to the correct size.
1 vtkm::cont::ArrayCopy(srcArray, destArray);
-
template<typename SourceArrayType, typename DestArrayType>
inline void vtkm::cont::ArrayCopy(const SourceArrayType &source, DestArrayType &destination) Does a deep copy from one array to another array.
Given a source
ArrayHandleand a destinationArrayHandle, this function allocates the destinationArrayHandleto the correct size and deeply copies all the values from the source to the destination.This method will attempt to copy the data using the device that the input data is already valid on. If the input data is only valid in the control environment, the runtime device tracker is used to try to find another device.
This should work on some non-writable array handles as well, as long as both source and destination are the same type.
This version of array copy uses a precompiled version of copy that is efficient for most standard memory layouts. However, there are some types of fancy
ArrayHandlethat cannot be handled directly, and the fallback for these arrays can be slow. If you see a warning in the log about an inefficient memory copy when extracting a component, pay heed and look for a different way to copy the data (perhaps usingArrayCopyDevice).
-
template<typename SourceArrayType>
inline void vtkm::cont::ArrayCopy(const SourceArrayType &source, vtkm::cont::UnknownArrayHandle &destination) Does a deep copy from one array to another array.
Given a source
ArrayHandleand a destinationArrayHandle, this function allocates the destinationArrayHandleto the correct size and deeply copies all the values from the source to the destination.This method will attempt to copy the data using the device that the input data is already valid on. If the input data is only valid in the control environment, the runtime device tracker is used to try to find another device.
This should work on some non-writable array handles as well, as long as both source and destination are the same type.
This version of array copy uses a precompiled version of copy that is efficient for most standard memory layouts. However, there are some types of fancy
ArrayHandlethat cannot be handled directly, and the fallback for these arrays can be slow. If you see a warning in the log about an inefficient memory copy when extracting a component, pay heed and look for a different way to copy the data (perhaps usingArrayCopyDevice).
3.2.4. Array Portals
The vtkm::cont::ArrayHandle class does not provide direct access to the data in the array.
This is because the control and access to arrays is often in different parts of the code in VTK‑m.
To get direct access to the data, you must retrieve an array portal to the array.
There is no single ArrayPortal class declared, but the structure of all such classes has the following members.
-
class ArrayPortal
A class that provides access to the data in an array. Each
vtkm::cont::ArrayHandletype defines its own array portal.
-
typedef T ArrayPortal::ValueType
The type for each item in the array.
-
vtkm::Id ArrayPortal::GetNumberOfValues() const
Returns the number of entries in the array.
-
ArrayPortal::ValueType ArrayPortal::Get(vtkm::Id index) const
Returns the value in the array at the given index.
-
void ArrayPortal::Set(vtkm::Id index, const ArrayPortal::ValueType &value) const
Sets the entry at the given index of the array to the provided value.
A vtkm::cont::ArrayHandle provides its own array portal of an internal type.
The correct type for the array portal is vtkm::cont::ArrayHandle::ReadPortalType for read-only access and vtkm::cont::ArrayHandle::WritePortalType for read-write access.
vtkm::cont::ArrayHandle provides the methods vtkm::cont::ArrayHandle::ReadPortal() and vtkm::cont::ArrayHandle::WritePortal() to get the associated array portal objects to access the data in the control environment.
These methods also have the side effect of refreshing the control environment copy of the data as if you called vtkm::cont::ArrayHandle::SyncControlArray().
Be aware that calling vtkm::cont::ArrayHandle::WritePortal() will invalidate any copy in the execution environment, meaning that any subsequent use will cause the data to be copied back again.
1 vtkm::cont::ArrayHandle<vtkm::Float32> arrayHandle;
2
3 const vtkm::Id ARRAY_SIZE = 50;
4 arrayHandle.Allocate(ARRAY_SIZE);
5
6 // Usually it is easier to just use the auto keyword.
7 using PortalType = vtkm::cont::ArrayHandle<vtkm::Float32>::WritePortalType;
8 PortalType portal = arrayHandle.WritePortal();
9
10 for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++)
11 {
12 portal.Set(index, GetValueForArray(index));
13 }
Did You Know?
Most operations on arrays in VTK‑m should really be done in the execution environment. Keep in mind that whenever doing an operation using a control array portal, that operation will likely be slow for large arrays. However, some operations, like performing file I/O, make sense in the control environment.
Common Errors
The portal returned from vtkm::cont::ArrayHandle::ReadPortal() or vtkm::cont::ArrayHandle::WritePortal() is only good as long as the data in the vtkm::cont::ArrayHandle are not moved or reallocated.
For example, if you call vtkm::cont::ArrayHandle::Allocate(), any previously created array portals are likely to become invalid, and using them will result in undefined behavior.
Thus, you should keep portals only as long as is necessary to complete an operation.
VTK‑m provides a pair of functions, vtkm::cont::ArrayPortalToIteratorBegin() and vtkm::cont::ArrayPortalToIterationEnd(), to convert an ArrayPortal into a C++ STL iterator.
This makes it easy to operate on VTK‑m arrays like other C++ STL containers, but keep in mind this will all be done in serial on the host processor.
-
template<typename PortalType>
vtkm::cont::ArrayPortalToIterators<PortalType>::IteratorType vtkm::cont::ArrayPortalToIteratorBegin(const PortalType &portal) Convenience function for converting an ArrayPortal to a begin iterator.
-
template<typename PortalType>
vtkm::cont::ArrayPortalToIterators<PortalType>::IteratorType vtkm::cont::ArrayPortalToIteratorEnd(const PortalType &portal) Convenience function for converting an ArrayPortal to an end iterator.
1template<typename T, typename Storage>
2void SortCheckArrayHandle(vtkm::cont::ArrayHandle<T, Storage> arrayHandle)
3{
4 using WritePortalType = typename vtkm::cont::ArrayHandle<T, Storage>::WritePortalType;
5 using ReadPortalType = typename vtkm::cont::ArrayHandle<T, Storage>::ReadPortalType;
6
7 WritePortalType readwritePortal = arrayHandle.WritePortal();
8 // This is actually pretty dumb. Sorting would be generally faster in
9 // parallel in the execution environment using the device adapter algorithms.
10 std::sort(vtkm::cont::ArrayPortalToIteratorBegin(readwritePortal),
11 vtkm::cont::ArrayPortalToIteratorEnd(readwritePortal));
12
13 ReadPortalType readPortal = arrayHandle.ReadPortal();
14 for (vtkm::Id index = 1; index < readPortal.GetNumberOfValues(); index++)
15 {
16 if (readPortal.Get(index - 1) > readPortal.Get(index))
17 {
18 std::cout << "Sorting is wrong!" << std::endl;
19 break;
20 }
21 }
22}
3.2.6. Mutability
One subtle feature of vtkm::cont::ArrayHandle is that the class is, in principle, a pointer to an array pointer.
This means that the data in an vtkm::cont::ArrayHandle is always mutable even if the class is declared const.
You can change the contents of “constant” arrays via methods like vtkm::cont::ArrayHandle::WritePortal() and vtkm::cont::ArrayHandle::PrepareForOutput().
It is even possible to change the underlying array allocation with methods like vtkm::cont::ArrayHandle::Allocate() and vtkm::cont::ArrayHandle::ReleaseResources().
The upshot is that you can (sometimes) pass output arrays as constant vtkm::cont::ArrayHandle references.
So if a constant vtkm::cont::ArrayHandle can have its contents modified, what is the difference between a constant reference and a non-constant reference?
The difference is that the constant reference can change the array’s content, but not the array itself.
Basically, this means that you cannot perform shallow copies into a const vtkm::cont::ArrayHandle.
This can be a pretty big limitation, and many of VTK‑m’s internal device algorithms still require non-constant references for outputs.