mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-16 17:22:55 +00:00
Add ArrayPortalDecorator.
This commit is contained in:
parent
29ea46fa52
commit
885cce3914
244
docs/changelog/array-handle-decorator.md
Normal file
244
docs/changelog/array-handle-decorator.md
Normal file
@ -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
|
||||
|
828
vtkm/cont/ArrayHandleDecorator.h
Normal file
828
vtkm/cont/ArrayHandleDecorator.h
Normal file
@ -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
|
||||
|
402
vtkm/cont/testing/UnitTestArrayHandleDecorator.cxx
Normal file
402
vtkm/cont/testing/UnitTestArrayHandleDecorator.cxx
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user