Add ArrayPortalDecorator.

This commit is contained in:
Allison Vacanti 2019-09-06 18:24:27 -04:00
parent 29ea46fa52
commit 885cce3914
6 changed files with 1485 additions and 29 deletions

@ -0,0 +1,244 @@
# Add `ArrayHandleDecorator`.
`ArrayHandleDecorator` is given a `DecoratorImpl` class and a list of one or
more source `ArrayHandle`s. There are no restrictions on the size or type of
the source `ArrayHandle`s.
The decorator implementation class is described below:
```
struct ExampleDecoratorImplementation
{
// Takes one portal for each source array handle (only two shown).
// Returns a functor that defines:
//
// ValueType operator()(vtkm::Id id) const;
//
// which takes an index and returns a value which should be produced by
// the source arrays somehow. This ValueType will be the ValueType of the
// ArrayHandleDecorator.
//
// Both SomeFunctor::operator() and CreateFunctor must be const.
//
template <typename Portal1Type, typename Portal2Type>
SomeFunctor CreateFunctor(Portal1Type portal1, Portal2Type portal2) const;
// Takes one portal for each source array handle (only two shown).
// Returns a functor that defines:
//
// void operator()(vtkm::Id id, ValueType val) const;
//
// which takes an index and a value, which should be used to modify one
// or more of the source arrays.
//
// CreateInverseFunctor is optional; if not provided, the
// ArrayHandleDecorator will be read-only. In addition, if all of the
// source ArrayHandles are read-only, the inverse functor will not be used
// and the ArrayHandleDecorator will be read only.
//
// Both SomeInverseFunctor::operator() and CreateInverseFunctor must be
// const.
//
template <typename Portal1Type, typename Portal2Type>
SomeInverseFunctor CreateInverseFunctor(Portal1Type portal1,
Portal2Type portal2) const;
};
```
Some example implementation classes are provided below:
Reverse a ScanExtended:
```
// Decorator implementation that reverses the ScanExtended operation.
//
// The resulting ArrayHandleDecorator will take an array produced by the
// ScanExtended algorithm and return the original ScanExtended input.
//
// Some interesting things about this:
// - The ArrayHandleDecorator's ValueType will not be the same as the
// ScanPortal's ValueType. The Decorator ValueType is determined by the
// return type of Functor::operator().
// - The ScanPortal has more values than the ArrayHandleDecorator. The
// number of values the ArrayHandleDecorator should hold is set during
// construction and may differ from the arrays it holds.
template <typename ValueType>
struct ScanExtendedToNumIndicesDecorImpl
{
template <typename ScanPortalType>
struct Functor
{
ScanPortalType ScanPortal;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->ScanPortal.Get(idx + 1) -
this->ScanPortal.Get(idx));
}
};
template <typename ScanPortalType>
Functor<ScanPortalType> CreateFunctor(ScanPortalType portal) const
{
return {portal};
}
};
auto numIndicesOrig = vtkm::cont::make_ArrayHandleCounting(ValueType{0},
ValueType{1},
ARRAY_SIZE);
vtkm::cont::ArrayHandle<vtkm::Id> scan;
vtkm::cont::Algorithm::ScanExtended(
vtkm::cont::make_ArrayHandleCast<vtkm::Id>(numIndicesOrig),
scan);
auto numIndicesDecor = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE,
ScanExtendedToNumIndicesDecorImpl<ValueType>{},
scan);
```
Combine two other `ArrayHandle`s using an arbitrary binary operation:
```
// Decorator implementation that demonstrates how to create functors that
// hold custom state. Here, the functors have a customizable Operation
// member.
//
// This implementation is used to create a read-only ArrayHandleDecorator
// that combines the values in two other ArrayHandles using an arbitrary
// binary operation (e.g. vtkm::Maximum, vtkm::Add, etc).
template <typename ValueType, typename OperationType>
struct BinaryOperationDecorImpl
{
OperationType Operation;
// The functor use to read values. Note that it holds extra state in
// addition to the portals.
template <typename Portal1Type, typename Portal2Type>
struct Functor
{
Portal1Type Portal1;
Portal2Type Portal2;
OperationType Operation;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return this->Operation(static_cast<ValueType>(this->Portal1.Get(idx)),
static_cast<ValueType>(this->Portal2.Get(idx)));
}
};
// A non-variadic example of a factory function to produce a functor. This
// is where the extra state is passed into the functor.
template <typename P1T, typename P2T>
Functor<P1T, P2T> CreateFunctor(P1T p1, P2T p2) const
{
return {p1, p2, this->Operation};
}
};
BinaryOperationDecorImpl<ValueType, vtkm::Maximum> factory{vtkm::Maximum{}};
auto decorArray = vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE,
factory,
array1,
array2);
```
A factory that does a complex and invertible operation on three portals:
```
// Decorator implemenation that demonstrates how to write invertible functors
// that combine three array handles with complex access logic. The resulting
// ArrayHandleDecorator can be both read from and written to.
//
// Constructs functors that take three portals.
//
// The first portal's values are accessed in reverse order.
// The second portal's values are accessed in normal order.
// The third portal's values are accessed via ((idx + 3) % size).
//
// Functor will return the max of the first two added to the third.
//
// InverseFunctor will update the third portal such that the Functor would
// return the indicated value.
struct InvertibleDecorImpl
{
// The functor used for reading data from the three portals.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto idx1 = this->Portal1.GetNumberOfValues() - idx - 1;
const auto idx2 = idx;
const auto idx3 = (idx + 3) % this->Portal3.GetNumberOfValues();
const auto v1 = this->Portal1.Get(idx1);
const auto v2 = this->Portal2.Get(idx2);
const auto v3 = this->Portal3.Get(idx3);
return vtkm::Max(v1, v2) + v3;
}
};
// The functor used for writing. Only Portal3 is written to, the other
// portals may be read-only.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct InverseFunctor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT void operator()(vtkm::Id idx, const ValueType &vIn) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto vNew = static_cast<ValueType>(vIn - vtkm::Max(v1, v2));
this->Portal3.Set((idx + 3) % this->Portal3.GetNumberOfValues(), vNew);
}
};
// Factory function that takes 3 portals as input and creates an instance
// of Functor with them. Variadic template parameters are used here, but are
// not necessary.
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...>
CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return {std::forward<PortalTs>(portals)...};
}
// Factory function that takes 3 portals as input and creates an instance
// of InverseFunctor with them. Variadic template parameters are used here,
// but are not necessary.
template <typename... PortalTs>
InverseFunctor<typename std::decay<PortalTs>::type...>
CreateInverseFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return {std::forward<PortalTs>(portals)...};
}
};
// Note that only ah3 must be writable for ahInv to be writable. ah1 and ah2
// may be read-only arrays.
auto ahInv = vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE,
InvertibleDecorImpl{},
ah1,
ah2,
ah3);
```

@ -24,6 +24,8 @@
#include <vtkm/cont/Storage.h>
#include <vtkm/cont/StorageBasic.h>
#include <vtkm/internal/ArrayPortalHelpers.h>
#include <algorithm>
#include <iterator>
#include <memory>
@ -74,37 +76,15 @@ struct IsInValidArrayHandle
{
};
namespace detail
{
template <typename PortalType>
struct IsWritableArrayPortalImpl
{
private:
template <typename U,
typename VT = typename U::ValueType,
typename S = decltype(std::declval<U>().Set(0, std::declval<VT>()))>
static std::true_type hasSet(int);
template <typename U>
static std::false_type hasSet(...);
public:
using type = decltype(hasSet<PortalType>(0));
};
} // namespace detail
/// Checks to see if the ArrayHandle or ArrayPortal allows writing, as some
/// ArrayHandles (Implicit) don't support writing. These will be defined as
/// either std::true_type or std::false_type.
/// @{
template <typename PortalType>
using IsWritableArrayPortal = typename detail::IsWritableArrayPortalImpl<PortalType>::type;
/// Checks to see if the ArrayHandle allows writing, as some ArrayHandles
/// (Implicit) don't support writing. These will be defined as either
/// std::true_type or std::false_type.
///
/// \sa vtkm::internal::PortalSupportsSets
///
template <typename ArrayHandle>
using IsWritableArrayHandle =
IsWritableArrayPortal<typename std::decay<ArrayHandle>::type::PortalControl>;
vtkm::internal::PortalSupportsSets<typename std::decay<ArrayHandle>::type::PortalControl>;
/// @}
/// Checks to see if the given object is an array handle. This check is

@ -0,0 +1,828 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#ifndef vtk_m_ArrayHandleDecorator_h
#define vtk_m_ArrayHandleDecorator_h
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/Storage.h>
#include <vtkm/StaticAssert.h>
#include <vtkm/VecTraits.h>
#include <vtkm/internal/ArrayPortalHelpers.h>
#include <vtkm/internal/brigand.hpp>
#include <vtkmtaotuple/include/Tuple.h>
#include <type_traits>
#include <utility>
namespace vtkm
{
namespace cont
{
namespace internal
{
namespace decor
{
// Generic InverseFunctor implementation that does nothing.
struct NoOpInverseFunctor
{
NoOpInverseFunctor() = default;
template <typename... Ts>
VTKM_EXEC_CONT NoOpInverseFunctor(Ts...)
{
}
template <typename VT>
VTKM_EXEC_CONT void operator()(vtkm::Id, VT) const
{
}
};
} // namespace decor
// The portal for ArrayHandleDecorator. Get calls FunctorType::operator(), and
// Set calls InverseFunctorType::operator(), but only if the DecoratorImpl
// provides an inverse.
template <typename ValueType_, typename FunctorType_, typename InverseFunctorType_>
class VTKM_ALWAYS_EXPORT ArrayPortalDecorator
{
public:
using ValueType = ValueType_;
using FunctorType = FunctorType_;
using InverseFunctorType = InverseFunctorType_;
using ReadOnly = std::is_same<InverseFunctorType, decor::NoOpInverseFunctor>;
VTKM_EXEC_CONT
ArrayPortalDecorator() {}
VTKM_CONT
ArrayPortalDecorator(FunctorType func, InverseFunctorType iFunc, vtkm::Id numValues)
: Functor{ func }
, InverseFunctor{ iFunc }
, NumberOfValues{ numValues }
{
}
VTKM_EXEC_CONT
vtkm::Id GetNumberOfValues() const { return this->NumberOfValues; }
VTKM_EXEC_CONT
ValueType Get(vtkm::Id index) const { return this->Functor(index); }
VTKM_EXEC_CONT
template <typename ReadOnly_ = ReadOnly,
typename = typename std::enable_if<!ReadOnly_::value>::type>
void Set(vtkm::Id index, const ValueType& value) const
{
this->InverseFunctor(index, value);
}
private:
FunctorType Functor;
InverseFunctorType InverseFunctor;
vtkm::Id NumberOfValues;
};
namespace decor
{
// Ensures that all types in variadic container ArrayHandleList are subclasses
// of ArrayHandleBase.
template <typename ArrayHandleList>
using AllAreArrayHandles =
brigand::all<ArrayHandleList, std::is_base_of<ArrayHandleBase, brigand::_1>>;
namespace detail
{
// Tests whether DecoratorImplT has a CreateInverseFunctor(Portals...) method.
template <typename DecoratorImplT, typename PortalList>
struct IsFunctorInvertibleImpl;
template <typename DecoratorImplT, template <typename...> class List, typename... PortalTs>
struct IsFunctorInvertibleImpl<DecoratorImplT, List<PortalTs...>>
{
private:
using PortalList = brigand::list<typename std::decay<PortalTs>::type...>;
template <
typename T,
typename U = decltype(std::declval<T>().CreateInverseFunctor(std::declval<PortalTs>()...))>
static std::true_type InverseExistsTest(int);
template <typename T>
static std::false_type InverseExistsTest(...);
public:
using type = decltype(InverseExistsTest<DecoratorImplT>(0));
};
// Deduces the type returned by DecoratorImplT::CreateFunctor when given
// the specified portals.
template <typename DecoratorImplT, typename PortalList>
struct GetFunctorTypeImpl;
template <typename DecoratorImplT, template <typename...> class List, typename... PortalTs>
struct GetFunctorTypeImpl<DecoratorImplT, List<PortalTs...>>
{
using type = decltype(std::declval<DecoratorImplT>().CreateFunctor(std::declval<PortalTs>()...));
};
// Deduces the type returned by DecoratorImplT::CreateInverseFunctor when given
// the specified portals. If DecoratorImplT doesn't have a CreateInverseFunctor
// method, a NoOp functor will be used instead.
template <typename CanWrite, typename DecoratorImplT, typename PortalList>
struct GetInverseFunctorTypeImpl;
template <typename DecoratorImplT, template <typename...> class List, typename... PortalTs>
struct GetInverseFunctorTypeImpl<std::true_type, DecoratorImplT, List<PortalTs...>>
{
using type =
decltype(std::declval<DecoratorImplT>().CreateInverseFunctor(std::declval<PortalTs>()...));
};
template <typename DecoratorImplT, typename PortalList>
struct GetInverseFunctorTypeImpl<std::false_type, DecoratorImplT, PortalList>
{
using type = NoOpInverseFunctor;
};
// Get appropriate portals from a source array.
// See note below about using non-writable portals in invertible functors.
// We need to sub in const portals when writable ones don't exist.
template <typename ArrayT>
typename std::decay<ArrayT>::type::PortalControl GetPortalControlImpl(std::true_type,
ArrayT&& array)
{
return array.GetPortalControl();
}
template <typename ArrayT>
typename std::decay<ArrayT>::type::PortalConstControl GetPortalControlImpl(std::false_type,
ArrayT&& array)
{
return array.GetPortalConstControl();
}
template <typename ArrayT, typename Device>
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::Portal
GetPortalInPlaceImpl(std::true_type, ArrayT&& array, Device)
{
return array.PrepareForInPlace(Device{});
}
template <typename ArrayT, typename Device>
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::PortalConst
GetPortalInPlaceImpl(std::false_type, ArrayT&& array, Device)
{
// ArrayT is read-only -- prepare for input instead.
return array.PrepareForInput(Device{});
}
template <typename ArrayT, typename Device>
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::Portal
GetPortalOutputImpl(std::true_type, ArrayT&& array, Device)
{
// Prepare these for inplace usage instead -- we'll likely need to read
// from these in addition to writing.
return array.PrepareForInPlace(Device{});
}
template <typename ArrayT, typename Device>
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::PortalConst
GetPortalOutputImpl(std::false_type, ArrayT&& array, Device)
{
// ArrayT is read-only -- prepare for input instead.
return array.PrepareForInput(Device{});
}
} // namespace detail
// Get portal types:
// We allow writing to an AHDecorator if *any* of the ArrayHandles are writable.
// This means we have to avoid calling PrepareForOutput, etc on non-writable
// array handles, since these may throw. On non-writable handles, use the
// const array handles so we can at least read from them in the inverse
// functors.
template <typename ArrayT,
typename Portal = typename std::decay<ArrayT>::type::PortalControl,
typename PortalConst = typename std::decay<ArrayT>::type::PortalConstControl>
using GetPortalControlType =
typename brigand::if_<vtkm::internal::PortalSupportsSets<Portal>, Portal, PortalConst>::type;
template <typename ArrayT>
using GetPortalConstControlType = typename std::decay<ArrayT>::type::PortalConstControl;
template <typename ArrayT,
typename Device,
typename Portal =
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::Portal,
typename PortalConst =
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::PortalConst>
using GetPortalExecutionType =
typename brigand::if_<vtkm::internal::PortalSupportsSets<Portal>, Portal, PortalConst>::type;
template <typename ArrayT, typename Device>
using GetPortalConstExecutionType =
typename std::decay<ArrayT>::type::template ExecutionTypes<Device>::PortalConst;
// Get portal objects:
// See note above -- we swap in const portals sometimes.
template <typename ArrayT>
GetPortalControlType<typename std::decay<ArrayT>::type> GetPortalControl(ArrayT&& array)
{
return detail::GetPortalControlImpl(IsWritableArrayHandle<ArrayT>{}, std::forward<ArrayT>(array));
}
template <typename ArrayT>
GetPortalConstControlType<typename std::decay<ArrayT>::type> GetPortalConstControl(
const ArrayT& array)
{
return array.GetPortalConstControl();
}
template <typename ArrayT, typename Device>
GetPortalConstExecutionType<typename std::decay<ArrayT>::type, Device> GetPortalInput(
const ArrayT& array,
Device)
{
return array.PrepareForInput(Device{});
}
template <typename ArrayT, typename Device>
GetPortalExecutionType<typename std::decay<ArrayT>::type, Device> GetPortalInPlace(ArrayT&& array,
Device)
{
return detail::GetPortalInPlaceImpl(
IsWritableArrayHandle<ArrayT>{}, std::forward<ArrayT>(array), Device{});
}
template <typename ArrayT, typename Device>
GetPortalExecutionType<typename std::decay<ArrayT>::type, Device> GetPortalOutput(ArrayT&& array,
Device)
{
return detail::GetPortalOutputImpl(
IsWritableArrayHandle<ArrayT>{}, std::forward<ArrayT>(array), Device{});
}
// Equivalent to std::true_type if *any* portal in PortalList can be written to.
// If all are read-only, std::false_type is used instead.
template <typename PortalList>
using AnyPortalIsWritable =
typename brigand::any<PortalList,
brigand::bind<vtkm::internal::PortalSupportsSets, brigand::_1>>::type;
// Set to std::true_type if DecoratorImplT::CreateInverseFunctor can be called
// with the supplied portals, or std::false_type otherwise.
template <typename DecoratorImplT, typename PortalList>
using IsFunctorInvertible =
typename detail::IsFunctorInvertibleImpl<DecoratorImplT, PortalList>::type;
// std::true_type/std::false_type depending on whether the decorator impl has a
// CreateInversePortal method AND any of the arrays are writable.
template <typename DecoratorImplT, typename PortalList>
using CanWriteToFunctor = typename brigand::and_<IsFunctorInvertible<DecoratorImplT, PortalList>,
AnyPortalIsWritable<PortalList>>::type;
// The FunctorType for the provided implementation and portal types.
template <typename DecoratorImplT, typename PortalList>
using GetFunctorType = typename detail::GetFunctorTypeImpl<DecoratorImplT, PortalList>::type;
// The InverseFunctorType for the provided implementation and portal types.
// Will detect when inversion is not possible and return a NoOp functor instead.
template <typename DecoratorImplT, typename PortalList>
using GetInverseFunctorType =
typename detail::GetInverseFunctorTypeImpl<CanWriteToFunctor<DecoratorImplT, PortalList>,
DecoratorImplT,
PortalList>::type;
// Convert a sequence of array handle types to a list of portals:
// Some notes on this implementation:
// - MSVC 2015 ICEs when using brigand::transform to convert a brigand::list
// of arrayhandles to portals. So instead we pass the ArrayTs.
// - Just using brigand::list<GetPortalControlType<ArrayTs>...> fails, as
// apparently that is an improper parameter pack expansion
// - So we jump through some decltype/declval hoops here to get this to work:
template <typename... ArrayTs>
using GetPortalConstControlList =
brigand::list<decltype((GetPortalConstControl(std::declval<ArrayTs>())))...>;
template <typename Device, typename... ArrayTs>
using GetPortalConstExecutionList =
brigand::list<decltype((GetPortalInput(std::declval<ArrayTs>(), Device{})))...>;
template <typename... ArrayTs>
using GetPortalControlList =
brigand::list<decltype((GetPortalControl(std::declval<ArrayTs>())))...>;
template <typename Device, typename... ArrayTs>
using GetPortalExecutionList =
brigand::list<decltype((GetPortalInPlace(std::declval<ArrayTs>(), Device{})))...>;
template <typename DecoratorImplT, typename... ArrayTs>
struct DecoratorStorageTraits
{
using ArrayList = brigand::list<ArrayTs...>;
VTKM_STATIC_ASSERT_MSG(sizeof...(ArrayTs) > 0,
"Must specify at least one source array handle for "
"ArrayHandleDecorator. Consider using "
"ArrayHandleImplicit instead.");
// Need to check this here, since this traits struct is used in the
// ArrayHandleDecorator superclass definition before any other
// static_asserts could be used.
VTKM_STATIC_ASSERT_MSG(decor::AllAreArrayHandles<ArrayList>::value,
"Trailing template parameters for "
"ArrayHandleDecorator must be a list of ArrayHandle "
"types.");
using ArrayTupleType = vtkmstd::tuple<ArrayTs...>;
// size_t integral constants that index ArrayTs:
using IndexList = brigand::make_sequence<brigand::size_t<0>, sizeof...(ArrayTs)>;
// Portal lists:
// NOTE we have to pass the parameter pack here instead of using ArrayList
// with brigand::transform, since that's causing MSVC 2015 to ice:
using PortalControlList = GetPortalControlList<ArrayTs...>;
using PortalConstControlList = GetPortalConstControlList<ArrayTs...>;
template <typename Device>
using PortalExecutionList = GetPortalExecutionList<Device, ArrayTs...>;
template <typename Device>
using PortalConstExecutionList = GetPortalConstExecutionList<Device, ArrayTs...>;
// Functors:
using FunctorControlType = GetFunctorType<DecoratorImplT, PortalControlList>;
using FunctorConstControlType = GetFunctorType<DecoratorImplT, PortalConstControlList>;
template <typename Device>
using FunctorExecutionType = GetFunctorType<DecoratorImplT, PortalExecutionList<Device>>;
template <typename Device>
using FunctorConstExecutionType =
GetFunctorType<DecoratorImplT, PortalConstExecutionList<Device>>;
// Inverse functors:
using InverseFunctorControlType = GetInverseFunctorType<DecoratorImplT, PortalControlList>;
using InverseFunctorConstControlType = NoOpInverseFunctor;
template <typename Device>
using InverseFunctorExecutionType =
GetInverseFunctorType<DecoratorImplT, PortalExecutionList<Device>>;
template <typename Device>
using InverseFunctorConstExecutionType = NoOpInverseFunctor;
// Misc:
// ValueType is derived from DecoratorImplT::CreateFunctor(...)'s operator().
using ValueType = decltype(std::declval<FunctorControlType>()(0));
// Decorator portals:
using PortalControlType =
ArrayPortalDecorator<ValueType, FunctorControlType, InverseFunctorControlType>;
using PortalConstControlType =
ArrayPortalDecorator<ValueType, FunctorConstControlType, InverseFunctorConstControlType>;
template <typename Device>
using PortalExecutionType = ArrayPortalDecorator<ValueType,
FunctorExecutionType<Device>,
InverseFunctorExecutionType<Device>>;
template <typename Device>
using PortalConstExecutionType = ArrayPortalDecorator<ValueType,
FunctorConstExecutionType<Device>,
InverseFunctorConstExecutionType<Device>>;
// helper for constructing portals with the appropriate functors. This is
// where we decide whether or not to call `CreateInverseFunctor` on the
// implementation class.
// Do not use these directly, they are helpers for the MakePortal[...]
// methods below.
template <typename DecoratorPortalType, typename... PortalTs>
VTKM_CONT static
typename std::enable_if<DecoratorPortalType::ReadOnly::value, DecoratorPortalType>::type
CreatePortalDecorator(vtkm::Id numVals, const DecoratorImplT& impl, PortalTs&&... portals)
{ // Portal is read only:
return { impl.CreateFunctor(std::forward<PortalTs>(portals)...),
typename DecoratorPortalType::InverseFunctorType{},
numVals };
}
template <typename DecoratorPortalType, typename... PortalTs>
VTKM_CONT static
typename std::enable_if<!DecoratorPortalType::ReadOnly::value, DecoratorPortalType>::type
CreatePortalDecorator(vtkm::Id numVals, const DecoratorImplT& impl, PortalTs... portals)
{ // Portal is read/write:
return { impl.CreateFunctor(portals...), impl.CreateInverseFunctor(portals...), numVals };
}
// Portal construction methods. These actually create portals.
template <template <typename...> class List, typename... Indices>
VTKM_CONT static PortalControlType MakePortalControl(const DecoratorImplT& impl,
ArrayTupleType& arrays,
vtkm::Id numValues,
List<Indices...>)
{
return CreatePortalDecorator<PortalControlType>(
numValues,
impl,
// More MSVC ICE avoidance while expanding the Indices parameter pack,
// which is a list of std::integral_constant<size_t, ...>:
//
// Indices::value : Works everywhere but MSVC2017, which crashes on it.
// Indices{} : Works on MSVC2017, but won't compile on MSVC2015.
// Indices{}.value : Works on both MSVC2015 and MSVC2017.
//
// Don't touch the following line unless you really, really have to.
GetPortalControl(vtkmstd::get<Indices{}.value>(arrays))...);
}
template <template <typename...> class List, typename... Indices>
VTKM_CONT static PortalConstControlType MakePortalConstControl(const DecoratorImplT& impl,
const ArrayTupleType& arrays,
vtkm::Id numValues,
List<Indices...>)
{
return CreatePortalDecorator<PortalConstControlType>(
numValues,
impl,
// Don't touch the following line unless you really, really have to. See
// note in MakePortalControl.
GetPortalConstControl(vtkmstd::get<Indices{}.value>(arrays))...);
}
template <template <typename...> class List, typename... Indices, typename Device>
VTKM_CONT static PortalConstExecutionType<Device> MakePortalInput(const DecoratorImplT& impl,
const ArrayTupleType& arrays,
vtkm::Id numValues,
List<Indices...>,
Device dev)
{
return CreatePortalDecorator<PortalConstExecutionType<Device>>(
numValues,
impl,
// Don't touch the following line unless you really, really have to. See
// note in MakePortalControl.
GetPortalInput(vtkmstd::get<Indices{}.value>(arrays), dev)...);
}
template <template <typename...> class List, typename... Indices, typename Device>
VTKM_CONT static PortalExecutionType<Device> MakePortalInPlace(const DecoratorImplT& impl,
ArrayTupleType& arrays,
vtkm::Id numValues,
List<Indices...>,
Device dev)
{
return CreatePortalDecorator<PortalExecutionType<Device>>(
numValues,
impl,
// Don't touch the following line unless you really, really have to. See
// note in MakePortalControl.
GetPortalInPlace(vtkmstd::get<Indices{}.value>(arrays), dev)...);
}
template <template <typename...> class List, typename... Indices, typename Device>
VTKM_CONT static PortalExecutionType<Device> MakePortalOutput(const DecoratorImplT& impl,
ArrayTupleType& arrays,
vtkm::Id numValues,
List<Indices...>,
Device dev)
{
return CreatePortalDecorator<PortalExecutionType<Device>>(
numValues,
impl,
// Don't touch the following line unless you really, really have to. See
// note in MakePortalControl.
GetPortalOutput(vtkmstd::get<Indices{}.value>(arrays), dev)...);
}
};
} // end namespace decor
template <typename DecoratorImplT, typename... ArrayTs>
struct VTKM_ALWAYS_EXPORT StorageTagDecorator
{
};
template <typename DecoratorImplT, typename... ArrayTs>
class Storage<typename decor::DecoratorStorageTraits<DecoratorImplT, ArrayTs...>::ValueType,
StorageTagDecorator<DecoratorImplT, ArrayTs...>>
{
using Traits = decor::DecoratorStorageTraits<DecoratorImplT, ArrayTs...>;
using IndexList = typename Traits::IndexList;
public:
using ArrayTupleType = typename Traits::ArrayTupleType;
using ValueType = typename Traits::ValueType;
using PortalType = typename Traits::PortalControlType;
using PortalConstType = typename Traits::PortalConstControlType;
VTKM_CONT
Storage()
: Valid{ false }
{
}
VTKM_CONT
Storage(const DecoratorImplT& impl, const ArrayTupleType& arrayTuple, vtkm::Id numValues)
: Implementation{ impl }
, ArrayTuple{ arrayTuple }
, NumberOfValues(numValues)
, Valid{ true }
{
}
VTKM_CONT
PortalType GetPortal()
{
VTKM_ASSERT(this->Valid);
return Traits::MakePortalControl(
this->Implementation, this->ArrayTuple, this->NumberOfValues, IndexList{});
}
VTKM_CONT
PortalConstType GetPortalConst() const
{
VTKM_ASSERT(this->Valid);
return Traits::MakePortalConstControl(
this->Implementation, this->ArrayTuple, this->NumberOfValues, IndexList{});
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const
{
VTKM_ASSERT(this->Valid);
return this->NumberOfValues;
}
VTKM_CONT
void Allocate(vtkm::Id)
{
VTKM_ASSERT(this->Valid);
// No-op. I suppose eventually we could pass numValues down to the
// implementation class and let it do something intelligent.
}
VTKM_CONT
void Shrink(vtkm::Id)
{
VTKM_ASSERT(this->Valid);
// No-op. Again, could eventually be passed down to the implementation.
}
VTKM_CONT
void ReleaseResources()
{
VTKM_ASSERT(this->Valid);
// No-op. Again, could eventually be passed down to the implementation.
}
VTKM_CONT
const ArrayTupleType& GetArrayTuple() const
{
VTKM_ASSERT(this->Valid);
return this->ArrayTuple;
}
VTKM_CONT
ArrayTupleType& GetArrayTuple()
{
VTKM_ASSERT(this->Valid);
return this->ArrayTuple;
}
VTKM_CONT
const DecoratorImplT& GetImplementation() const
{
VTKM_ASSERT(this->Valid);
return this->Implementation;
}
VTKM_CONT
DecoratorImplT& GetImplementation()
{
VTKM_ASSERT(this->Valid);
return this->Implementation;
}
private:
DecoratorImplT Implementation;
ArrayTupleType ArrayTuple;
vtkm::Id NumberOfValues;
bool Valid;
};
template <typename DecoratorImplT, typename... ArrayTs>
struct DecoratorHandleTraits
{
using StorageTraits = decor::DecoratorStorageTraits<DecoratorImplT, ArrayTs...>;
using ValueType = typename StorageTraits::ValueType;
using StorageTag = StorageTagDecorator<DecoratorImplT, ArrayTs...>;
using StorageType = vtkm::cont::internal::Storage<ValueType, StorageTag>;
using Superclass = vtkm::cont::ArrayHandle<ValueType, StorageTag>;
};
template <typename DecoratorImplT, typename... ArrayTs, typename Device>
class ArrayTransfer<typename decor::DecoratorStorageTraits<DecoratorImplT, ArrayTs...>::ValueType,
StorageTagDecorator<DecoratorImplT, ArrayTs...>,
Device>
{
VTKM_IS_DEVICE_ADAPTER_TAG(Device);
using HandleTraits = DecoratorHandleTraits<DecoratorImplT, ArrayTs...>;
using Traits = typename HandleTraits::StorageTraits;
using IndexList = typename Traits::IndexList;
using StorageType = typename HandleTraits::StorageType;
public:
using ValueType = typename Traits::ValueType;
using PortalControl = typename Traits::PortalControlType;
using PortalConstControl = typename Traits::PortalConstControlType;
using PortalExecution = typename Traits::template PortalExecutionType<Device>;
using PortalConstExecution = typename Traits::template PortalConstExecutionType<Device>;
VTKM_CONT
ArrayTransfer(StorageType* storage)
: Storage(storage)
{
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const { return this->Storage->GetNumberOfValues(); }
VTKM_CONT
PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData)) const
{
return Traits::MakePortalInput(this->Storage->GetImplementation(),
this->Storage->GetArrayTuple(),
this->Storage->GetNumberOfValues(),
IndexList{},
Device{});
}
VTKM_CONT
PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData))
{
return Traits::MakePortalInPlace(this->Storage->GetImplementation(),
this->Storage->GetArrayTuple(),
this->Storage->GetNumberOfValues(),
IndexList{},
Device{});
}
VTKM_CONT
PortalExecution PrepareForOutput(vtkm::Id)
{
return Traits::MakePortalOutput(this->Storage->GetImplementation(),
this->Storage->GetArrayTuple(),
this->Storage->GetNumberOfValues(),
IndexList{},
Device{});
}
VTKM_CONT
void RetrieveOutputData(StorageType* vtkmNotUsed(storage)) const
{
// Implementation of this method should be unnecessary. The internal
// array handles should automatically retrieve the output data as
// necessary.
}
VTKM_CONT
void Shrink(vtkm::Id)
{
// no-op
}
VTKM_CONT
void ReleaseResources()
{
// no-op
}
private:
StorageType* Storage;
};
} // namespace internal
/// \brief A fancy ArrayHandle that can be used to modify the results from one
/// or more source ArrayHandle.
///
/// ArrayHandleDecorator is given a `DecoratorImplT` class and a list of one or
/// more source ArrayHandles. There are no restrictions on the size or type of
/// the source ArrayHandles.
///
/// The decorator implementation class is described below:
///
/// ```
/// struct ExampleDecoratorImplementation
/// {
///
/// // Takes one portal for each source array handle (only two shown).
/// // Returns a functor that defines:
/// //
/// // ValueType operator()(vtkm::Id id) const;
/// //
/// // which takes an index and returns a value which should be produced by
/// // the source arrays somehow. This ValueType will be the ValueType of the
/// // ArrayHandleDecorator.
/// //
/// // Both SomeFunctor::operator() and CreateFunctor must be const.
/// //
/// template <typename Portal1Type, typename Portal2Type>
/// SomeFunctor CreateFunctor(Portal1Type portal1, Portal2Type portal2) const;
///
/// // Takes one portal for each source array handle (only two shown).
/// // Returns a functor that defines:
/// //
/// // void operator()(vtkm::Id id, ValueType val) const;
/// //
/// // which takes an index and a value, which should be used to modify one
/// // or more of the source arrays.
/// //
/// // CreateInverseFunctor is optional; if not provided, the
/// // ArrayHandleDecorator will be read-only. In addition, if all of the
/// // source ArrayHandles are read-only, the inverse functor will not be used
/// // and the ArrayHandleDecorator will be read only.
/// //
/// // Both SomeInverseFunctor::operator() and CreateInverseFunctor must be
/// // const.
/// //
/// template <typename Portal1Type, typename Portal2Type>
/// SomeInverseFunctor CreateInverseFunctor(Portal1Type portal1,
/// Portal2Type portal2) const;
///
/// };
/// ```
///
/// There are several example DecoratorImpl classes provided in the
/// UnitTestArrayHandleDecorator test file.
///
template <typename DecoratorImplT, typename... ArrayTs>
class ArrayHandleDecorator
: public internal::DecoratorHandleTraits<typename std::decay<DecoratorImplT>::type,
typename std::decay<ArrayTs>::type...>::Superclass
{
private:
using Traits = internal::DecoratorHandleTraits<typename std::decay<DecoratorImplT>::type,
typename std::decay<ArrayTs>::type...>;
using StorageType = typename Traits::StorageType;
public:
VTKM_ARRAY_HANDLE_SUBCLASS(ArrayHandleDecorator,
(ArrayHandleDecorator<typename std::decay<DecoratorImplT>::type,
typename std::decay<ArrayTs>::type...>),
(typename Traits::Superclass));
VTKM_CONT
ArrayHandleDecorator(vtkm::Id numValues,
const typename std::decay<DecoratorImplT>::type& impl,
const typename std::decay<ArrayTs>::type&... arrays)
: Superclass{ StorageType{ impl, vtkmstd::make_tuple(arrays...), numValues } }
{
}
};
/// Create an ArrayHandleDecorator with the specified number of values that
/// uses the provided DecoratorImplT and source ArrayHandles.
///
template <typename DecoratorImplT, typename... ArrayTs>
VTKM_CONT ArrayHandleDecorator<typename std::decay<DecoratorImplT>::type,
typename std::decay<ArrayTs>::type...>
make_ArrayHandleDecorator(vtkm::Id numValues, DecoratorImplT&& f, ArrayTs&&... arrays)
{
using AHList = brigand::list<typename std::decay<ArrayTs>::type...>;
VTKM_STATIC_ASSERT_MSG(sizeof...(ArrayTs) > 0,
"Must specify at least one source array handle for "
"ArrayHandleDecorator. Consider using "
"ArrayHandleImplicit instead.");
VTKM_STATIC_ASSERT_MSG(internal::decor::AllAreArrayHandles<AHList>::value,
"Trailing template parameters for "
"ArrayHandleDecorator must be a list of ArrayHandle "
"types.");
return { numValues, std::forward<DecoratorImplT>(f), std::forward<ArrayTs>(arrays)... };
}
}
} // namespace vtkm::cont
#endif //vtk_m_ArrayHandleDecorator_h

@ -20,6 +20,7 @@ set(headers
ArrayHandleConcatenate.h
ArrayHandleConstant.h
ArrayHandleCounting.h
ArrayHandleDecorator.h
ArrayHandleDiscard.h
ArrayHandleExtractComponent.h
ArrayHandleExtrudeCoords.h

@ -39,6 +39,7 @@ set(unit_tests
UnitTestArrayHandleCartesianProduct.cxx
UnitTestArrayHandleCompositeVector.cxx
UnitTestArrayHandleCounting.cxx
UnitTestArrayHandleDecorator.cxx
UnitTestArrayHandleDiscard.cxx
UnitTestArrayHandleExtractComponent.cxx
UnitTestArrayHandleExtrude.cxx

@ -0,0 +1,402 @@
//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/ArrayHandleDecorator.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/BinaryOperators.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
struct DecoratorTests
{
static constexpr vtkm::Id ARRAY_SIZE = 10;
// Decorator implemenation that demonstrates how to write invertible functors
// that combine three array handles with complex access logic. The resulting
// ArrayHandleDecorator can be both read from and written to.
//
// Constructs functors that take three portals.
//
// The first portal's values are accessed in reverse order.
// The second portal's values are accessed in normal order.
// The third portal's values are accessed via ((idx + 3) % size).
//
// Functor will return the max of the first two added to the third.
//
// InverseFunctor will update the third portal such that the Functor would
// return the indicated value.
struct InvertibleDecorImpl
{
// The functor used for reading data from the three portals.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto idx1 = this->Portal1.GetNumberOfValues() - idx - 1;
const auto idx2 = idx;
const auto idx3 = (idx + 3) % this->Portal3.GetNumberOfValues();
const auto v1 = this->Portal1.Get(idx1);
const auto v2 = this->Portal2.Get(idx2);
const auto v3 = this->Portal3.Get(idx3);
return vtkm::Max(v1, v2) + v3;
}
};
// The functor used for writing. Only Portal3 is written to, the other
// portals may be read-only.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct InverseFunctor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT void operator()(vtkm::Id idx, const ValueType& vIn) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto vNew = static_cast<ValueType>(vIn - vtkm::Max(v1, v2));
this->Portal3.Set((idx + 3) % this->Portal3.GetNumberOfValues(), vNew);
}
};
// Factory function that takes 3 portals as input and creates an instance
// of Functor with them. Variadic template parameters are used here, but are
// not necessary.
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
// Factory function that takes 3 portals as input and creates an instance
// of InverseFunctor with them. Variadic template parameters are used here,
// but are not necessary.
template <typename... PortalTs>
InverseFunctor<typename std::decay<PortalTs>::type...> CreateInverseFunctor(
PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Same as above, but cannot be inverted. The resulting ArrayHandleDecorator
// will be read-only.
struct NonInvertibleDecorImpl
{
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto v3 = this->Portal3.Get((idx + 3) % this->Portal3.GetNumberOfValues());
return vtkm::Max(v1, v2) + v3;
}
};
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Decorator implementation that demonstrates how to create functors that
// hold custom state. Here, the functors have a customizable Operation
// member.
//
// This implementation is used to create a read-only ArrayHandleDecorator
// that combines the values in two other ArrayHandles using an arbitrary
// binary operation (e.g. vtkm::Maximum, vtkm::Add, etc).
template <typename ValueType, typename OperationType>
struct BinaryOperationDecorImpl
{
OperationType Operation;
// The functor use to read values. Note that it holds extra state in
// addition to the portals.
template <typename Portal1Type, typename Portal2Type>
struct Functor
{
Portal1Type Portal1;
Portal2Type Portal2;
OperationType Operation;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return this->Operation(static_cast<ValueType>(this->Portal1.Get(idx)),
static_cast<ValueType>(this->Portal2.Get(idx)));
}
};
// A non-variadic example of a factory function to produce a functor. This
// is where the extra state is passed into the functor.
template <typename P1T, typename P2T>
Functor<P1T, P2T> CreateFunctor(P1T p1, P2T p2) const
{
return { p1, p2, this->Operation };
}
};
// Decorator implementation that reverses the ScanExtended operation.
//
// The resulting ArrayHandleDecorator will take an array produced by the
// ScanExtended algorithm and return the original ScanExtended input.
//
// Some interesting things about this:
// - The ArrayHandleDecorator's ValueType will not be the same as the
// ScanPortal's ValueType. The Decorator ValueType is determined by the
// return type of Functor::operator().
// - The ScanPortal has more values than the ArrayHandleDecorator. The
// number of values the ArrayHandleDecorator should hold is set during
// construction and may differ from the arrays it holds.
template <typename ValueType>
struct ScanExtendedToNumIndicesDecorImpl
{
template <typename ScanPortalType>
struct Functor
{
ScanPortalType ScanPortal;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->ScanPortal.Get(idx + 1) - this->ScanPortal.Get(idx));
}
};
template <typename ScanPortalType>
Functor<ScanPortalType> CreateFunctor(ScanPortalType portal) const
{
return { portal };
}
};
template <typename ValueType>
void InversionTest() const
{
auto ah1 = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 2 }, ARRAY_SIZE);
auto ah2 = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<ValueType> ah3;
vtkm::cont::Algorithm::Fill(ah3, ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
auto ah3Const = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
{ // Has a writable handle and an invertible functor:
auto ahInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(vtkm::cont::internal::IsWritableArrayHandle<decltype(ahInv)>::value);
}
{ // Has no writable handles and an invertible functor:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has writable handles, but the functor cannot be inverted:
auto ahNInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has no writable handles and the functor cannot be inverted:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Test reading/writing to an invertible handle:
// Copy ah3 since we'll be modifying it:
vtkm::cont::ArrayHandle<ValueType> ah3Copy;
vtkm::cont::ArrayCopy(ah3, ah3Copy);
auto ahDecor =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Copy);
{
auto portalDecor = ahDecor.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 23 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 21 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 19 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 17 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 15 });
}
// Copy a constant array into the decorator. This should modify ah3Copy.
vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(ValueType{ 25 }, ARRAY_SIZE),
ahDecor);
{ // Accessing portal should give all 25s:
auto portalDecor = ahDecor.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 25 });
}
{ // ah3Copy should have updated values:
auto portalAH3Copy = ah3Copy.GetPortalConstControl();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.Get(0) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(1) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(2) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(3) == ValueType{ 7 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(4) == ValueType{ 9 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(5) == ValueType{ 11 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(6) == ValueType{ 13 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(9) == ValueType{ 15 });
}
}
}
template <typename ValueType, typename OperationType>
void BinaryOperatorTest() const
{
auto ahCount = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
auto ahConst = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
const OperationType op;
BinaryOperationDecorImpl<ValueType, OperationType> impl{ op };
auto decorArray = vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, impl, ahCount, ahConst);
{
auto decorPortal = decorArray.GetPortalConstControl();
auto countPortal = ahCount.GetPortalConstControl();
auto constPortal = ahConst.GetPortalConstControl();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(decorPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
vtkm::cont::ArrayHandle<ValueType> copiedInExec;
vtkm::cont::ArrayCopy(decorArray, copiedInExec);
{
auto copiedPortal = copiedInExec.GetPortalConstControl();
auto countPortal = ahCount.GetPortalConstControl();
auto constPortal = ahConst.GetPortalConstControl();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(copiedPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
}
template <typename ValueType>
void ScanExtendedToNumIndicesTest() const
{
auto numIndicesOrig =
vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<vtkm::Id> scan;
vtkm::cont::Algorithm::ScanExtended(vtkm::cont::make_ArrayHandleCast<vtkm::Id>(numIndicesOrig),
scan);
// Some interesting things to notice:
// - `numIndicesDecor` will have `ARRAY_SIZE` entries, while `scan` has
// `ARRAY_SIZE + 1`.
// - `numIndicesDecor` uses the current function scope `ValueType`, since
// that is what the functor from the implementation class returns. `scan`
// uses `vtkm::Id`.
auto numIndicesDecor = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, ScanExtendedToNumIndicesDecorImpl<ValueType>{}, scan);
{
auto origPortal = numIndicesOrig.GetPortalConstControl();
auto decorPortal = numIndicesDecor.GetPortalConstControl();
VTKM_STATIC_ASSERT(VTKM_PASS_COMMAS(
std::is_same<decltype(origPortal.Get(0)), decltype(decorPortal.Get(0))>::value));
VTKM_TEST_ASSERT(origPortal.GetNumberOfValues() == decorPortal.GetNumberOfValues());
for (vtkm::Id i = 0; i < origPortal.GetNumberOfValues(); ++i)
{
VTKM_TEST_ASSERT(origPortal.Get(i) == decorPortal.Get(i));
}
}
}
template <typename ValueType>
void operator()(const ValueType) const
{
InversionTest<ValueType>();
BinaryOperatorTest<ValueType, vtkm::Maximum>();
BinaryOperatorTest<ValueType, vtkm::Minimum>();
BinaryOperatorTest<ValueType, vtkm::Add>();
BinaryOperatorTest<ValueType, vtkm::Subtract>();
BinaryOperatorTest<ValueType, vtkm::Multiply>();
ScanExtendedToNumIndicesTest<ValueType>();
}
};
void TestArrayHandleDecorator()
{
vtkm::testing::Testing::TryTypes(DecoratorTests{}, vtkm::TypeListTagScalarAll{});
}
} // anonymous namespace
int UnitTestArrayHandleDecorator(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(TestArrayHandleDecorator, argc, argv);
}