Allow ArrayHandleTransform to work with ExecObject

This change allows you to set a subclass of
vtkm::cont::ExecutionObjectBase as a functor
used in ArrayHandleTransform. This latter class will then detect that
the functor is an ExecObject and will call PrepareForExecution with the
appropriate device to get the actual Functor object.

This change allows you to use virtual objects and other device dependent
objects as functors for ArrayHandleTransform without knowing a priori
what device the portal will be used on.
This commit is contained in:
Kenneth Moreland 2018-09-05 10:07:23 -06:00
parent bc9772021c
commit 98a0a20feb
5 changed files with 511 additions and 38 deletions

@ -0,0 +1,97 @@
# Allow ArrayHandleTransform to work with ExecObject
Previously, the `ArrayHandleTransform` class only worked with plain old
data (POD) objects as is functors. For simple transforms, this makes sense
since all the data comes from a target `ArrayHandle` that will be sent to
the device through a different path. However, this also requires the
transform to be known at compile time.
However, there are cases where the functor cannot be a POD object and has
to be built for a specific device. There are numerous reasons for this. One
might be that you need some lookup tables. Another might be you want to
support a virtual object, which has to be initialized for a particular
device. The standard way to implement this in VTK-m is to create an
"executive object." This actually means that we create a wrapper around
executive objects that inherits from `vtkm::cont::ExecutionObjectBase` that
contains a `PrepareForExecution` method.
As an example, consider the use case of a special `ArrayHandle` that takes
the value in one array and returns the index of that value in another
sorted array. We can do that by creating a functor that finds a value in an
array and returns the index.
``` cpp
template <typename ArrayPortalType>
struct FindValueFunctor
{
ArrayPortalType SortedArrayPortal;
FindValueFunctor() = default;
VTKM_CONT FindValueFunctor(const ArrayPortalType& sortedPortal)
: SortedArrayPortal(sortedPortal)
{ }
VTKM_EXEC vtkm::Id operator()(const typename PortalType::ValueType& value)
{
vtkm::Id leftIndex = 0;
vtkm::Id rightIndex = this->SortedArrayPortal.GetNubmerOfValues();
while (leftIndex < rightIndex)
{
vtkm::Id middleIndex = (leftIndex + rightIndex) / 2;
auto middleValue = this->SortedArrayPortal.Get(middleIndex);
if (middleValue <= value)
{
rightIndex = middleValue;
}
else
{
leftIndex = middleValue + 1;
}
}
return leftIndex;
}
};
```
Simple enough, except that the type of `ArrayPortalType` depends on what
device the functor runs on (not to mention its memory might need to be
moved to different hardware). We can now solve this problem by creating an
execution object to set this up for a device.
``` cpp
template <typename ArrayHandleType>
struct FindValueExecutionObject : vtkm::cont::ExecutionObjectBase
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
ArrayHandleType SortedArray;
FindValueExecutionObject() = default;
VTKM_CONT FindValueExecutionObject(const ArrayHandleType& sortedArray)
: SortedArray(sortedArray)
{ }
template <typename Device>
VTKM_CONT
FindValueFunctor<decltype(std::declval<FunctorType>()(Device()))>
PrepareForExecution(Device device)
{
using FunctorType =
FindValueFunctor<decltype(std::declval<FunctorType>()(Device()))>
return FunctorType(this->SortedArray.PrepareForInput(device));
}
}
```
Now you can use this execution object in an `ArrayHandleTransform`. It will
automatically be detected as an execution object and be converted to a
functor in the execution environment.
``` cpp
auto transformArray =
vtkm::cont::make_ArrayHandleTransform(
inputArray, FindValueExecutionObject<decltype(sortedArray)>(sortedArray));
```

@ -25,6 +25,9 @@
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/ErrorInternal.h>
#include <vtkm/cont/ExecutionObjectBase.h>
#include <vtkm/cont/RuntimeDeviceTracker.h>
#include <vtkm/cont/serial/DeviceAdapterSerial.h>
namespace vtkm
{
@ -178,18 +181,94 @@ namespace cont
namespace internal
{
template <typename ProvidedFunctorType, typename Device, typename FunctorIsExecObject>
struct TransformFunctorManagerImpl;
template <typename ProvidedFunctorType, typename Device>
struct TransformFunctorManagerImpl<ProvidedFunctorType, Device, std::false_type>
{
using FunctorType = ProvidedFunctorType;
ProvidedFunctorType Functor;
TransformFunctorManagerImpl() = default;
VTKM_CONT
TransformFunctorManagerImpl(const ProvidedFunctorType& functor)
: Functor(functor)
{
}
VTKM_CONT
FunctorType GetFunctor() const { return this->Functor; }
};
template <typename ProvidedFunctorType, typename Device>
struct TransformFunctorManagerImpl<ProvidedFunctorType, Device, std::true_type>
{
using FunctorType = decltype(std::declval<ProvidedFunctorType>().PrepareForExecution(Device()));
ProvidedFunctorType Functor;
TransformFunctorManagerImpl() = default;
VTKM_CONT
TransformFunctorManagerImpl(const ProvidedFunctorType& functor)
: Functor(functor)
{
}
VTKM_CONT
FunctorType GetFunctor() const { return this->Functor.PrepareForExecution(Device()); }
};
template <typename ProvidedFunctorType, typename Device = vtkm::cont::DeviceAdapterTagSerial>
struct TransformFunctorManager
: TransformFunctorManagerImpl<
ProvidedFunctorType,
Device,
typename std::is_base_of<vtkm::cont::ExecutionObjectBase, ProvidedFunctorType>::type>
{
using Superclass = TransformFunctorManagerImpl<
ProvidedFunctorType,
Device,
typename std::is_base_of<vtkm::cont::ExecutionObjectBase, ProvidedFunctorType>::type>;
using FunctorType = typename Superclass::FunctorType;
VTKM_CONT TransformFunctorManager() = default;
VTKM_CONT TransformFunctorManager(const TransformFunctorManager& other) = default;
VTKM_CONT TransformFunctorManager(const ProvidedFunctorType& functor)
: Superclass(functor)
{
}
template <typename OtherDevice>
VTKM_CONT TransformFunctorManager(
const TransformFunctorManager<ProvidedFunctorType, OtherDevice>& other)
: Superclass(other.Functor)
{
}
template <typename ValueType>
using TransformedValueType = decltype(std::declval<FunctorType>()(ValueType{}));
};
template <typename ArrayHandleType,
typename FunctorType,
typename InverseFunctorType = NullFunctorType>
struct VTKM_ALWAYS_EXPORT StorageTagTransform
{
using ValueType = decltype(FunctorType{}(typename ArrayHandleType::ValueType{}));
using FunctorManager = TransformFunctorManager<FunctorType>;
using ValueType =
typename FunctorManager::template TransformedValueType<typename ArrayHandleType::ValueType>;
};
template <typename ArrayHandleType, typename FunctorType>
class Storage<typename StorageTagTransform<ArrayHandleType, FunctorType>::ValueType,
StorageTagTransform<ArrayHandleType, FunctorType>>
{
using FunctorManager = TransformFunctorManager<FunctorType>;
public:
using ValueType = typename StorageTagTransform<ArrayHandleType, FunctorType>::ValueType;
@ -204,7 +283,7 @@ public:
using PortalConstType =
vtkm::exec::internal::ArrayPortalTransform<ValueType,
typename ArrayHandleType::PortalConstControl,
FunctorType>;
typename FunctorManager::FunctorType>;
VTKM_CONT
Storage()
@ -223,15 +302,17 @@ public:
VTKM_CONT
PortalType GetPortal()
{
VTKM_ASSERT(this->Valid);
return PortalType(this->Array.GetPortalControl(), this->Functor);
throw vtkm::cont::ErrorBadType(
"ArrayHandleTransform is read only. Cannot get writable portal.");
}
VTKM_CONT
PortalConstType GetPortalConst() const
{
VTKM_ASSERT(this->Valid);
return PortalConstType(this->Array.GetPortalConstControl(), this->Functor);
vtkm::cont::ScopedGlobalRuntimeDeviceTracker trackerScope;
vtkm::cont::GetGlobalRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagSerial());
return PortalConstType(this->Array.GetPortalConstControl(), this->Functor.GetFunctor());
}
VTKM_CONT
@ -269,11 +350,11 @@ public:
}
VTKM_CONT
const FunctorType& GetFunctor() const { return this->Functor; }
const FunctorManager& GetFunctor() const { return this->Functor; }
private:
ArrayHandleType Array;
FunctorType Functor;
FunctorManager Functor;
bool Valid;
};
@ -282,6 +363,9 @@ class Storage<
typename StorageTagTransform<ArrayHandleType, FunctorType, InverseFunctorType>::ValueType,
StorageTagTransform<ArrayHandleType, FunctorType, InverseFunctorType>>
{
using FunctorManager = TransformFunctorManager<FunctorType>;
using InverseFunctorManager = TransformFunctorManager<InverseFunctorType>;
public:
using ValueType =
typename StorageTagTransform<ArrayHandleType, FunctorType, InverseFunctorType>::ValueType;
@ -289,13 +373,13 @@ public:
using PortalType =
vtkm::exec::internal::ArrayPortalTransform<ValueType,
typename ArrayHandleType::PortalControl,
FunctorType,
InverseFunctorType>;
typename FunctorManager::FunctorType,
typename InverseFunctorManager::FunctorType>;
using PortalConstType =
vtkm::exec::internal::ArrayPortalTransform<ValueType,
typename ArrayHandleType::PortalConstControl,
FunctorType,
InverseFunctorType>;
typename FunctorManager::FunctorType,
typename InverseFunctorManager::FunctorType>;
VTKM_CONT
Storage()
@ -318,15 +402,22 @@ public:
PortalType GetPortal()
{
VTKM_ASSERT(this->Valid);
return PortalType(this->Array.GetPortalControl(), this->Functor, this->InverseFunctor);
vtkm::cont::ScopedGlobalRuntimeDeviceTracker trackerScope;
vtkm::cont::GetGlobalRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagSerial());
return PortalType(this->Array.GetPortalControl(),
this->Functor.GetFunctor(),
this->InverseFunctor.GetFunctor());
}
VTKM_CONT
PortalConstType GetPortalConst() const
{
VTKM_ASSERT(this->Valid);
return PortalConstType(
this->Array.GetPortalConstControl(), this->Functor, this->InverseFunctor);
vtkm::cont::ScopedGlobalRuntimeDeviceTracker trackerScope;
vtkm::cont::GetGlobalRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagSerial());
return PortalConstType(this->Array.GetPortalConstControl(),
this->Functor.GetFunctor(),
this->InverseFunctor.GetFunctor());
}
VTKM_CONT
@ -361,15 +452,15 @@ public:
}
VTKM_CONT
const FunctorType& GetFunctor() const { return this->Functor; }
const FunctorManager& GetFunctor() const { return this->Functor; }
VTKM_CONT
const InverseFunctorType& GetInverseFunctor() const { return this->InverseFunctor; }
const InverseFunctorManager& GetInverseFunctor() const { return this->InverseFunctor; }
private:
ArrayHandleType Array;
FunctorType Functor;
InverseFunctorType InverseFunctor;
FunctorManager Functor;
InverseFunctorManager InverseFunctor;
bool Valid;
};
@ -379,6 +470,7 @@ class ArrayTransfer<typename StorageTagTransform<ArrayHandleType, FunctorType>::
Device>
{
using StorageTag = StorageTagTransform<ArrayHandleType, FunctorType>;
using FunctorManager = TransformFunctorManager<FunctorType, Device>;
public:
using ValueType = typename StorageTagTransform<ArrayHandleType, FunctorType>::ValueType;
@ -392,7 +484,7 @@ public:
using PortalConstExecution = vtkm::exec::internal::ArrayPortalTransform<
ValueType,
typename ArrayHandleType::template ExecutionTypes<Device>::PortalConst,
FunctorType>;
typename FunctorManager::FunctorType>;
VTKM_CONT
ArrayTransfer(StorageType* storage)
@ -407,7 +499,7 @@ public:
VTKM_CONT
PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData))
{
return PortalConstExecution(this->Array.PrepareForInput(Device()), this->Functor);
return PortalConstExecution(this->Array.PrepareForInput(Device()), this->Functor.GetFunctor());
}
VTKM_CONT
@ -443,7 +535,7 @@ public:
private:
ArrayHandleType Array;
FunctorType Functor;
FunctorManager Functor;
};
template <typename ArrayHandleType,
@ -456,6 +548,8 @@ class ArrayTransfer<
Device>
{
using StorageTag = StorageTagTransform<ArrayHandleType, FunctorType, InverseFunctorType>;
using FunctorManager = TransformFunctorManager<FunctorType, Device>;
using InverseFunctorManager = TransformFunctorManager<InverseFunctorType, Device>;
public:
using ValueType = typename StorageTagTransform<ArrayHandleType, FunctorType>::ValueType;
@ -467,13 +561,13 @@ public:
using PortalExecution = vtkm::exec::internal::ArrayPortalTransform<
ValueType,
typename ArrayHandleType::template ExecutionTypes<Device>::Portal,
FunctorType,
InverseFunctorType>;
typename FunctorManager::FunctorType,
typename InverseFunctorManager::FunctorType>;
using PortalConstExecution = vtkm::exec::internal::ArrayPortalTransform<
ValueType,
typename ArrayHandleType::template ExecutionTypes<Device>::PortalConst,
FunctorType,
InverseFunctorType>;
typename FunctorManager::FunctorType,
typename InverseFunctorManager::FunctorType>;
VTKM_CONT
ArrayTransfer(StorageType* storage)
@ -489,22 +583,25 @@ public:
VTKM_CONT
PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData))
{
return PortalConstExecution(
this->Array.PrepareForInput(Device()), this->Functor, this->InverseFunctor);
return PortalConstExecution(this->Array.PrepareForInput(Device()),
this->Functor.GetFunctor(),
this->InverseFunctor.GetFunctor());
}
VTKM_CONT
PortalExecution PrepareForInPlace(bool& vtkmNotUsed(updateData))
{
return PortalExecution(
this->Array.PrepareForInPlace(Device()), this->Functor, this->InverseFunctor);
return PortalExecution(this->Array.PrepareForInPlace(Device()),
this->Functor.GetFunctor(),
this->InverseFunctor.GetFunctor());
}
VTKM_CONT
PortalExecution PrepareForOutput(vtkm::Id numberOfValues)
{
return PortalExecution(
this->Array.PrepareForOutput(numberOfValues, Device()), this->Functor, this->InverseFunctor);
return PortalExecution(this->Array.PrepareForOutput(numberOfValues, Device()),
this->Functor.GetFunctor(),
this->InverseFunctor.GetFunctor());
}
VTKM_CONT
@ -522,8 +619,8 @@ public:
private:
ArrayHandleType Array;
FunctorType Functor;
InverseFunctorType InverseFunctor;
FunctorManager Functor;
InverseFunctorManager InverseFunctor;
};
} // namespace internal

@ -235,6 +235,29 @@ thread_local static vtkm::cont::RuntimeDeviceTracker runtimeDeviceTracker;
VTKM_CONT_EXPORT
VTKM_CONT
vtkm::cont::RuntimeDeviceTracker GetGlobalRuntimeDeviceTracker();
struct ScopedGlobalRuntimeDeviceTracker
{
vtkm::cont::RuntimeDeviceTracker SavedTracker;
VTKM_CONT ScopedGlobalRuntimeDeviceTracker()
: SavedTracker(vtkm::cont::GetGlobalRuntimeDeviceTracker().DeepCopy())
{
}
VTKM_CONT ScopedGlobalRuntimeDeviceTracker(vtkm::cont::RuntimeDeviceTracker tracker)
: SavedTracker(vtkm::cont::GetGlobalRuntimeDeviceTracker().DeepCopy())
{
vtkm::cont::GetGlobalRuntimeDeviceTracker().DeepCopy(tracker);
}
VTKM_CONT ~ScopedGlobalRuntimeDeviceTracker()
{
vtkm::cont::GetGlobalRuntimeDeviceTracker().DeepCopy(this->SavedTracker);
}
ScopedGlobalRuntimeDeviceTracker(const ScopedGlobalRuntimeDeviceTracker&) = delete;
};
}
} // namespace vtkm::cont

@ -26,7 +26,6 @@
#include <vtkm/UnaryPredicates.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/ErrorExecution.h>
#include <vtkm/cont/vtkm_cont_export.h>
@ -126,6 +125,26 @@ __global__ void SumExclusiveScan(T a, T b, T result, BinaryOperationType binary_
#if (defined(VTKM_GCC) || defined(VTKM_CLANG))
#pragma GCC diagnostic pop
#endif
template <typename PortalType, typename OutValueType>
struct CastPortal
{
using ValueType = OutValueType;
PortalType Portal;
VTKM_CONT
CastPortal(const PortalType& portal)
: Portal(portal)
{
}
VTKM_EXEC
vtkm::Id GetNumberOfValues() const { return this->Portal.GetNumberOfValues(); }
VTKM_EXEC
ValueType Get(vtkm::Id index) const { return static_cast<OutValueType>(this->Portal.Get(index)); }
};
}
} // end namespace cuda::internal
@ -313,9 +332,7 @@ private:
//The portal type and the initial value AREN'T the same type so we have
//to a slower approach, where we wrap the input portal inside a cast
//portal
using CastFunctor = vtkm::cont::internal::Cast<typename InputPortal::ValueType, T>;
vtkm::exec::internal::ArrayPortalTransform<T, InputPortal, CastFunctor> castPortal(input);
vtkm::cont::cuda::internal::CastPortal<InputPortal, T> castPortal(input);
vtkm::exec::cuda::internal::WrappedBinaryOperator<T, BinaryFunctor> bop(binary_functor);

@ -38,6 +38,7 @@
#include <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/ArrayHandleView.h>
#include <vtkm/cont/ArrayHandleZip.h>
#include <vtkm/cont/VirtualObjectHandle.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/WorkletMapField.h>
@ -73,7 +74,7 @@ struct ValueSquared
struct ValueScale
{
ValueScale()
: Factor()
: Factor(1.0)
{
}
@ -102,6 +103,107 @@ struct ValueScale
private:
vtkm::Float64 Factor;
};
struct InverseValueScale
{
InverseValueScale()
: InverseFactor(1.0)
{
}
InverseValueScale(vtkm::Float64 factor)
: InverseFactor(1.0 / factor)
{
}
template <typename ValueType>
VTKM_EXEC_CONT ValueType operator()(const ValueType& v) const
{
using Traits = vtkm::VecTraits<ValueType>;
using TTraits = vtkm::TypeTraits<ValueType>;
using ComponentType = typename Traits::ComponentType;
ValueType result = TTraits::ZeroInitialization();
for (vtkm::IdComponent i = 0; i < Traits::GetNumberOfComponents(v); ++i)
{
vtkm::Float64 vi = static_cast<vtkm::Float64>(Traits::GetComponent(v, i));
vtkm::Float64 ri = vi * this->InverseFactor;
Traits::SetComponent(result, i, static_cast<ComponentType>(ri));
}
return result;
}
private:
vtkm::Float64 InverseFactor;
};
template <typename ValueType>
struct VirtualTransformFunctorBase : public vtkm::VirtualObjectBase
{
VirtualTransformFunctorBase() = default;
VTKM_EXEC_CONT
virtual ValueType operator()(const ValueType& v) const = 0;
};
template <typename ValueType, typename FunctorType>
struct VirtualTransformFunctor : VirtualTransformFunctorBase<ValueType>
{
FunctorType Functor;
VTKM_CONT
VirtualTransformFunctor(const FunctorType& functor)
: Functor(functor)
{
}
VTKM_EXEC_CONT
ValueType operator()(const ValueType& v) const override { return this->Functor(v); }
};
template <typename ValueType>
struct TransformExecObject : public vtkm::cont::ExecutionObjectBase
{
vtkm::cont::VirtualObjectHandle<VirtualTransformFunctorBase<ValueType>> VirtualFunctor;
VTKM_CONT TransformExecObject() = default;
template <typename FunctorType>
VTKM_CONT TransformExecObject(const FunctorType& functor)
{
// Need to make sure the serial device is supported, since that is what is used on the
// control side.
vtkm::cont::ScopedGlobalRuntimeDeviceTracker scopedTracker;
vtkm::cont::GetGlobalRuntimeDeviceTracker().ResetDevice(vtkm::cont::DeviceAdapterTagSerial());
this->VirtualFunctor.Reset(new VirtualTransformFunctor<ValueType, FunctorType>(functor));
}
template <typename DeviceAdapterTag>
struct ExecutionObject
{
const VirtualTransformFunctorBase<ValueType>* FunctorPointer;
ExecutionObject() = default;
VTKM_CONT
ExecutionObject(const VirtualTransformFunctorBase<ValueType>* functorPointer)
: FunctorPointer(functorPointer)
{
}
template <typename InValueType>
VTKM_EXEC ValueType operator()(const InValueType& value) const
{
return (*this->FunctorPointer)(value);
}
};
template <typename DeviceAdapterTag>
VTKM_CONT ExecutionObject<DeviceAdapterTag> PrepareForExecution(DeviceAdapterTag device) const
{
return ExecutionObject<DeviceAdapterTag>(this->VirtualFunctor.PrepareForExecution(device));
}
};
}
namespace vtkm
@ -476,6 +578,44 @@ private:
}
};
struct TestTransformVirtualAsInput
{
template <typename ValueType>
VTKM_CONT void operator()(const ValueType vtkmNotUsed(v)) const
{
using FunctorType = fancy_array_detail::ValueScale;
using VirtualFunctorType = fancy_array_detail::TransformExecObject<ValueType>;
const vtkm::Id length = ARRAY_SIZE;
FunctorType functor(2.0);
VirtualFunctorType virtualFunctor(functor);
vtkm::cont::ArrayHandle<ValueType> input;
auto transformed = vtkm::cont::make_ArrayHandleTransform(input, virtualFunctor);
input.Allocate(length);
SetPortal(input.GetPortalControl());
vtkm::cont::printSummary_ArrayHandle(transformed, std::cout);
std::cout << std::endl;
vtkm::cont::ArrayHandle<ValueType> result;
vtkm::worklet::DispatcherMapField<PassThrough> dispatcher;
dispatcher.Invoke(transformed, result);
//verify that the control portal works
for (vtkm::Id i = 0; i < length; ++i)
{
const ValueType result_v = result.GetPortalConstControl().Get(i);
const ValueType correct_value = functor(TestValue(i, ValueType()));
const ValueType control_value = transformed.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(test_equal(result_v, correct_value), "Transform Handle Failed");
VTKM_TEST_ASSERT(test_equal(result_v, control_value), "Transform Handle Control Failed");
}
}
};
struct TestCountingTransformAsInput
{
template <typename ValueType>
@ -917,6 +1057,88 @@ private:
}
};
struct TestTransformAsOutput
{
template <typename ValueType>
VTKM_CONT void operator()(const ValueType vtkmNotUsed(v)) const
{
using FunctorType = fancy_array_detail::ValueScale;
using InverseFunctorType = fancy_array_detail::InverseValueScale;
const vtkm::Id length = ARRAY_SIZE;
FunctorType functor(2.0);
InverseFunctorType inverseFunctor(2.0);
vtkm::cont::ArrayHandle<ValueType> input;
input.Allocate(length);
SetPortal(input.GetPortalControl());
vtkm::cont::ArrayHandle<ValueType> output;
auto transformed = vtkm::cont::make_ArrayHandleTransform(output, functor, inverseFunctor);
vtkm::worklet::DispatcherMapField<PassThrough> dispatcher;
dispatcher.Invoke(input, transformed);
vtkm::cont::printSummary_ArrayHandle(transformed, std::cout);
std::cout << std::endl;
//verify that the control portal works
for (vtkm::Id i = 0; i < length; ++i)
{
const ValueType result_v = output.GetPortalConstControl().Get(i);
const ValueType correct_value = inverseFunctor(TestValue(i, ValueType()));
const ValueType control_value = transformed.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(test_equal(result_v, correct_value), "Transform Handle Failed");
VTKM_TEST_ASSERT(test_equal(functor(result_v), control_value),
"Transform Handle Control Failed");
}
}
};
struct TestTransformVirtualAsOutput
{
template <typename ValueType>
VTKM_CONT void operator()(const ValueType vtkmNotUsed(v)) const
{
using FunctorType = fancy_array_detail::ValueScale;
using InverseFunctorType = fancy_array_detail::InverseValueScale;
using VirtualFunctorType = fancy_array_detail::TransformExecObject<ValueType>;
const vtkm::Id length = ARRAY_SIZE;
FunctorType functor(2.0);
InverseFunctorType inverseFunctor(2.0);
VirtualFunctorType virtualFunctor(functor);
VirtualFunctorType virtualInverseFunctor(inverseFunctor);
vtkm::cont::ArrayHandle<ValueType> input;
input.Allocate(length);
SetPortal(input.GetPortalControl());
vtkm::cont::ArrayHandle<ValueType> output;
auto transformed =
vtkm::cont::make_ArrayHandleTransform(output, virtualFunctor, virtualInverseFunctor);
vtkm::worklet::DispatcherMapField<PassThrough> dispatcher;
dispatcher.Invoke(input, transformed);
vtkm::cont::printSummary_ArrayHandle(transformed, std::cout);
std::cout << std::endl;
//verify that the control portal works
for (vtkm::Id i = 0; i < length; ++i)
{
const ValueType result_v = output.GetPortalConstControl().Get(i);
const ValueType correct_value = inverseFunctor(TestValue(i, ValueType()));
const ValueType control_value = transformed.GetPortalConstControl().Get(i);
VTKM_TEST_ASSERT(test_equal(result_v, correct_value), "Transform Handle Failed");
VTKM_TEST_ASSERT(test_equal(functor(result_v), control_value),
"Transform Handle Control Failed");
}
}
};
struct TestZipAsOutput
{
template <typename KeyType, typename ValueType>
@ -1047,6 +1269,12 @@ private:
vtkm::testing::Testing::TryTypes(
TestingFancyArrayHandles<DeviceAdapterTag>::TestTransformAsInput(), HandleTypesToTest());
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleTransform with virtual as Input" << std::endl;
vtkm::testing::Testing::TryTypes(
TestingFancyArrayHandles<DeviceAdapterTag>::TestTransformVirtualAsInput(),
HandleTypesToTest());
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleTransform with Counting as Input" << std::endl;
vtkm::testing::Testing::TryTypes(
@ -1105,6 +1333,17 @@ private:
vtkm::testing::Testing::TryTypes(
TestingFancyArrayHandles<DeviceAdapterTag>::TestViewAsOutput(), HandleTypesToTest());
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleTransform as Output" << std::endl;
vtkm::testing::Testing::TryTypes(
TestingFancyArrayHandles<DeviceAdapterTag>::TestTransformAsOutput(), HandleTypesToTest());
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleTransform with virtual as Output" << std::endl;
vtkm::testing::Testing::TryTypes(
TestingFancyArrayHandles<DeviceAdapterTag>::TestTransformVirtualAsOutput(),
HandleTypesToTest());
std::cout << "-------------------------------------------" << std::endl;
std::cout << "Testing ArrayHandleDiscard as Output" << std::endl;
vtkm::testing::Testing::TryTypes(