vtk-m/vtkm/worklet/internal/DispatcherBase.h

511 lines
20 KiB
C
Raw Normal View History

//============================================================================
// 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.
//
// Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_worklet_internal_DispatcherBase_h
#define vtk_m_worklet_internal_DispatcherBase_h
#include <vtkm/StaticAssert.h>
#include <vtkm/internal/FunctionInterface.h>
#include <vtkm/internal/Invocation.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/ErrorBadType.h>
#include <vtkm/cont/arg/ControlSignatureTagBase.h>
#include <vtkm/cont/arg/Transport.h>
#include <vtkm/cont/arg/TypeCheck.h>
#include <vtkm/cont/internal/DynamicTransform.h>
#include <vtkm/exec/arg/ExecutionSignatureTagBase.h>
#include <vtkm/internal/IntegerSequence.h>
#include <vtkm/internal/brigand.hpp>
#include <sstream>
2017-05-18 14:29:41 +00:00
namespace vtkm
{
namespace worklet
{
namespace internal
{
2017-05-18 14:29:41 +00:00
namespace detail
{
// This code is actually taking an error found at compile-time and not
// reporting it until run-time. This seems strange at first, but this
// behavior is actually important. With dynamic arrays and similar dynamic
// classes, there may be types that are technically possible (such as using a
// vector where a scalar is expected) but in reality never happen. Thus, for
// these unsupported combinations we just silently halt the compiler from
// attempting to create code for these errant conditions and throw a run-time
// error if one every tries to create one.
2017-05-18 14:29:41 +00:00
inline void PrintFailureMessage(int, std::true_type)
{
}
2016-08-29 15:13:00 +00:00
inline void PrintFailureMessage(int index, std::false_type)
{
std::stringstream message;
2017-05-18 14:29:41 +00:00
message << "Encountered bad type for parameter " << index
<< " when calling Invoke on a dispatcher.";
throw vtkm::cont::ErrorBadType(message.str());
}
// Is designed as a brigand fold operation.
2017-05-18 14:29:41 +00:00
template <typename T, typename State>
struct DetermineIfHasDynamicParameter
{
using DynamicTag = typename vtkm::cont::internal::DynamicTransformTraits<T>::DynamicTag;
2017-05-18 14:29:41 +00:00
using isDynamic =
typename std::is_same<DynamicTag, vtkm::cont::internal::DynamicTransformTagCastAndCall>::type;
2017-05-18 14:29:41 +00:00
using type = std::integral_constant<bool, (State::value || isDynamic::value)>;
};
// Is designed as a brigand fold operation.
template <typename Index, typename Params, typename SigTypes>
struct DetermineHasInCorrectParameters
{
2017-05-18 14:29:41 +00:00
using T = typename brigand::at_c<Params, Index::value>;
using ControlSignatureTag = typename brigand::at_c<SigTypes, Index::value>;
using TypeCheckTag = typename ControlSignatureTag::TypeCheckTag;
2017-05-18 14:29:41 +00:00
using type = std::integral_constant<bool, vtkm::cont::arg::TypeCheck<TypeCheckTag, T>::value>;
2017-05-18 14:29:41 +00:00
static_assert(type::value,
"Unable to match 'ValueType' to the signature tag 'ControlSignatureTag'");
};
// Checks that an argument in a ControlSignature is a valid control signature
// tag. Causes a compile error otherwise.
struct DispatcherBaseControlSignatureTagCheck
{
2017-05-18 14:29:41 +00:00
template <typename ControlSignatureTag, vtkm::IdComponent Index>
struct ReturnType
{
// If you get a compile error here, it means there is something that is
// not a valid control signature tag in a worklet's ControlSignature.
VTKM_IS_CONTROL_SIGNATURE_TAG(ControlSignatureTag);
typedef ControlSignatureTag type;
};
};
// Checks that an argument in a ExecutionSignature is a valid execution
// signature tag. Causes a compile error otherwise.
struct DispatcherBaseExecutionSignatureTagCheck
{
2017-05-18 14:29:41 +00:00
template <typename ExecutionSignatureTag, vtkm::IdComponent Index>
struct ReturnType
{
// If you get a compile error here, it means there is something that is not
// a valid execution signature tag in a worklet's ExecutionSignature.
VTKM_IS_EXECUTION_SIGNATURE_TAG(ExecutionSignatureTag);
typedef ExecutionSignatureTag type;
};
};
// Used in the dynamic cast to check to make sure that the type passed into
// the Invoke method matches the type accepted by the ControlSignature.
2017-05-18 14:29:41 +00:00
template <typename ContinueFunctor, typename TypeCheckTag, vtkm::IdComponent Index>
struct DispatcherBaseTypeCheckFunctor
{
2017-05-18 14:29:41 +00:00
const ContinueFunctor& Continue;
VTKM_CONT
2017-05-18 14:29:41 +00:00
DispatcherBaseTypeCheckFunctor(const ContinueFunctor& continueFunc)
: Continue(continueFunc)
{
}
2017-05-18 14:29:41 +00:00
template <typename T>
VTKM_CONT void operator()(const T& x) const
{
2017-05-18 14:29:41 +00:00
typedef std::integral_constant<bool, vtkm::cont::arg::TypeCheck<TypeCheckTag, T>::value>
CanContinueTagType;
2017-05-18 14:29:41 +00:00
vtkm::worklet::internal::detail::PrintFailureMessage(Index, CanContinueTagType());
this->WillContinue(x, CanContinueTagType());
}
private:
2017-05-18 14:29:41 +00:00
template <typename T>
VTKM_CONT void WillContinue(const T& x, std::true_type) const
{
this->Continue(x);
}
2017-05-18 14:29:41 +00:00
template <typename T>
VTKM_CONT void WillContinue(const T&, std::false_type) const
{
}
2017-05-18 14:29:41 +00:00
void operator=(const DispatcherBaseTypeCheckFunctor<ContinueFunctor, TypeCheckTag, Index>&) =
delete;
};
// Uses vtkm::cont::internal::DynamicTransform and the DynamicTransformCont
// method of FunctionInterface to convert all DynamicArrayHandles and any
// other arguments declaring themselves as dynamic to static versions.
2017-05-18 14:29:41 +00:00
template <typename ControlInterface>
struct DispatcherBaseDynamicTransform
{
Perform less unnecessary copies when deducing a worklets parameters. One of the causes of the large library size and slow compile times has been that vtkm has been creating unnecessary copies when not needed. When the objects being copied use shared_ptr this causes a bloom in library size. I presume this bloom is caused by the atomic increment/decrement that is required by shared_ptr. For testing I used the following example: ``` struct ExampleFieldWorklet : public vtkm::worklet::WorkletMapField { typedef void ControlSignature( FieldIn<>, FieldIn<>, FieldIn<>, FieldOut<>, FieldOut<>, FieldOut<> ); typedef void ExecutionSignature( _1, _2, _3, _4, _5, _6 ); template<typename T, typename U, typename V> VTKM_EXEC_EXPORT void operator()( const vtkm::Vec< T, 3 > & vec, const U & scalar1, const V& scalar2, vtkm::Vec<T, 3>& out_vec, U& out_scalar1, V& out_scalar2 ) const { out_vec = vec * scalar1; out_scalar1 = scalar1 + scalar2; out_scalar2 = scalar2; } template<typename T, typename U, typename V, typename W, typename X, typename Y> VTKM_EXEC_EXPORT void operator()( const T & vec, const U & scalar1, const V& scalar2, W& out_vec, X& out_scalar, Y& ) const { //no-op } }; int main(int argc, char** argv) { std::vector< vtkm::Vec<vtkm::Float32, 3> > inputVec; std::vector< vtkm::Int32 > inputScalar1; std::vector< vtkm::Float64 > inputScalar2; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleV = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleS1 = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleS2 = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOV; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOS1; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOS2; std::cout << "Making 3 output DynamicArrayHandles " << std::endl; vtkm::cont::DynamicArrayHandle out1(handleOV), out2(handleOS1), out3(handleOS2); typedef vtkm::worklet::DispatcherMapField<ExampleFieldWorklet> DispatcherType; std::cout << "Invoking ExampleFieldWorklet" << std::endl; DispatcherType dispatcher; dispatcher.Invoke(handleV, handleS1, handleS2, out1, out2, out3); } ``` Original vtkm would generate a binary of size 4684kb and would perform 91 ArrayHandle copies or assignments. With this branch the binary size is reduced to 2392kb and will perform 36 copies or assignments.
2016-01-15 22:14:02 +00:00
vtkm::cont::internal::DynamicTransform BasicDynamicTransform;
2017-05-18 14:29:41 +00:00
template <typename InputType, typename ContinueFunctor, vtkm::IdComponent Index>
VTKM_CONT void operator()(const InputType& input,
const ContinueFunctor& continueFunc,
2017-05-18 14:29:41 +00:00
const vtkm::internal::IndexTag<Index>& indexTag) const
{
2017-05-18 14:29:41 +00:00
typedef typename ControlInterface::template ParameterType<Index>::type ControlSignatureTag;
2017-05-18 14:29:41 +00:00
typedef DispatcherBaseTypeCheckFunctor<ContinueFunctor,
typename ControlSignatureTag::TypeCheckTag,
Index>
2017-05-18 14:29:41 +00:00
TypeCheckFunctor;
Perform less unnecessary copies when deducing a worklets parameters. One of the causes of the large library size and slow compile times has been that vtkm has been creating unnecessary copies when not needed. When the objects being copied use shared_ptr this causes a bloom in library size. I presume this bloom is caused by the atomic increment/decrement that is required by shared_ptr. For testing I used the following example: ``` struct ExampleFieldWorklet : public vtkm::worklet::WorkletMapField { typedef void ControlSignature( FieldIn<>, FieldIn<>, FieldIn<>, FieldOut<>, FieldOut<>, FieldOut<> ); typedef void ExecutionSignature( _1, _2, _3, _4, _5, _6 ); template<typename T, typename U, typename V> VTKM_EXEC_EXPORT void operator()( const vtkm::Vec< T, 3 > & vec, const U & scalar1, const V& scalar2, vtkm::Vec<T, 3>& out_vec, U& out_scalar1, V& out_scalar2 ) const { out_vec = vec * scalar1; out_scalar1 = scalar1 + scalar2; out_scalar2 = scalar2; } template<typename T, typename U, typename V, typename W, typename X, typename Y> VTKM_EXEC_EXPORT void operator()( const T & vec, const U & scalar1, const V& scalar2, W& out_vec, X& out_scalar, Y& ) const { //no-op } }; int main(int argc, char** argv) { std::vector< vtkm::Vec<vtkm::Float32, 3> > inputVec; std::vector< vtkm::Int32 > inputScalar1; std::vector< vtkm::Float64 > inputScalar2; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleV = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleS1 = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleS2 = vtkm::cont::make_ArrayHandle(inputVec); vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOV; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOS1; vtkm::cont::ArrayHandle< vtkm::Vec<vtkm::Float32, 3> > handleOS2; std::cout << "Making 3 output DynamicArrayHandles " << std::endl; vtkm::cont::DynamicArrayHandle out1(handleOV), out2(handleOS1), out3(handleOS2); typedef vtkm::worklet::DispatcherMapField<ExampleFieldWorklet> DispatcherType; std::cout << "Invoking ExampleFieldWorklet" << std::endl; DispatcherType dispatcher; dispatcher.Invoke(handleV, handleS1, handleS2, out1, out2, out3); } ``` Original vtkm would generate a binary of size 4684kb and would perform 91 ArrayHandle copies or assignments. With this branch the binary size is reduced to 2392kb and will perform 36 copies or assignments.
2016-01-15 22:14:02 +00:00
this->BasicDynamicTransform(input, TypeCheckFunctor(continueFunc), indexTag);
}
};
// A functor called at the end of the dynamic transform to call the next
// step in the dynamic transform.
2017-05-18 14:29:41 +00:00
template <typename DispatcherBaseType>
struct DispatcherBaseDynamicTransformHelper
{
2017-05-18 14:29:41 +00:00
const DispatcherBaseType* Dispatcher;
VTKM_CONT
2017-05-18 14:29:41 +00:00
DispatcherBaseDynamicTransformHelper(const DispatcherBaseType* dispatcher)
: Dispatcher(dispatcher)
{
}
2017-05-18 14:29:41 +00:00
template <typename FunctionInterface>
VTKM_CONT void operator()(const FunctionInterface& parameters) const
{
this->Dispatcher->DynamicTransformInvoke(parameters, std::true_type());
}
};
// A look up helper used by DispatcherBaseTransportFunctor to determine
//the types independent of the device we are templated on.
2017-05-18 14:29:41 +00:00
template <typename ControlInterface, vtkm::IdComponent Index>
struct DispatcherBaseTransportInvokeTypes
{
//Moved out of DispatcherBaseTransportFunctor to reduce code generation
2017-05-18 14:29:41 +00:00
typedef typename ControlInterface::template ParameterType<Index>::type ControlSignatureTag;
typedef typename ControlSignatureTag::TransportTag TransportTag;
};
VTKM_CONT
2017-05-18 14:29:41 +00:00
inline vtkm::Id FlatRange(vtkm::Id range)
{
return range;
}
VTKM_CONT
2017-05-18 14:29:41 +00:00
inline vtkm::Id FlatRange(const vtkm::Id3& range)
{
2017-05-18 14:29:41 +00:00
return range[0] * range[1] * range[2];
}
// A functor used in a StaticCast of a FunctionInterface to transport arguments
// from the control environment to the execution environment.
2017-05-18 14:29:41 +00:00
template <typename ControlInterface, typename InputDomainType, typename Device>
struct DispatcherBaseTransportFunctor
{
2017-05-18 14:29:41 +00:00
const InputDomainType& InputDomain; // Warning: this is a reference
vtkm::Id InputRange;
vtkm::Id OutputRange;
// TODO: We need to think harder about how scheduling on 3D arrays works.
// Chances are we need to allow the transport for each argument to manage
// 3D indices (for example, allocate a 3D array instead of a 1D array).
// But for now, just treat all transports as 1D arrays.
2017-05-18 14:29:41 +00:00
template <typename InputRangeType, typename OutputRangeType>
VTKM_CONT DispatcherBaseTransportFunctor(const InputDomainType& inputDomain,
const InputRangeType& inputRange,
const OutputRangeType& outputRange)
: InputDomain(inputDomain)
, InputRange(FlatRange(inputRange))
, OutputRange(FlatRange(outputRange))
{
}
template <typename ControlParameter, vtkm::IdComponent Index>
struct ReturnType
{
using TransportTag =
typename DispatcherBaseTransportInvokeTypes<ControlInterface, Index>::TransportTag;
using TransportType =
typename vtkm::cont::arg::Transport<TransportTag, ControlParameter, Device>;
using type = typename TransportType::ExecObjectType;
};
2017-05-18 14:29:41 +00:00
template <typename ControlParameter, vtkm::IdComponent Index>
VTKM_CONT typename ReturnType<ControlParameter, Index>::type operator()(
const ControlParameter& invokeData,
vtkm::internal::IndexTag<Index>) const
{
2017-05-18 14:29:41 +00:00
using TransportTag =
typename DispatcherBaseTransportInvokeTypes<ControlInterface, Index>::TransportTag;
vtkm::cont::arg::Transport<TransportTag, ControlParameter, Device> transport;
return transport(invokeData, this->InputDomain, this->InputRange, this->OutputRange);
}
private:
2017-05-18 14:29:41 +00:00
void operator=(const DispatcherBaseTransportFunctor&) = delete;
};
} // namespace detail
/// Base class for all dispatcher classes. Every worklet type should have its
/// own dispatcher.
///
2017-05-18 14:29:41 +00:00
template <typename DerivedClass, typename WorkletType, typename BaseWorkletType>
class DispatcherBase
{
private:
2017-05-18 14:29:41 +00:00
typedef DispatcherBase<DerivedClass, WorkletType, BaseWorkletType> MyType;
friend struct detail::DispatcherBaseDynamicTransformHelper<MyType>;
protected:
2017-05-18 14:29:41 +00:00
typedef vtkm::internal::FunctionInterface<typename WorkletType::ControlSignature>
ControlInterface;
typedef vtkm::internal::FunctionInterface<typename WorkletType::ExecutionSignature>
ExecutionInterface;
static const vtkm::IdComponent NUM_INVOKE_PARAMS = ControlInterface::ARITY;
private:
// We don't really need these types, but declaring them checks the arguments
// of the control and execution signatures.
2017-05-18 14:29:41 +00:00
typedef typename ControlInterface::template StaticTransformType<
detail::DispatcherBaseControlSignatureTagCheck>::type ControlSignatureCheck;
typedef typename ExecutionInterface::template StaticTransformType<
detail::DispatcherBaseExecutionSignatureTagCheck>::type ExecutionSignatureCheck;
template <typename Signature>
VTKM_CONT void StartInvoke(const vtkm::internal::FunctionInterface<Signature>& parameters) const
{
using ParameterInterface = vtkm::internal::FunctionInterface<Signature>;
2016-08-29 15:13:00 +00:00
VTKM_STATIC_ASSERT_MSG(ParameterInterface::ARITY == NUM_INVOKE_PARAMS,
"Dispatcher Invoke called with wrong number of arguments.");
2017-05-18 14:29:41 +00:00
static_assert(
std::is_base_of<BaseWorkletType, WorkletType>::value,
2016-08-29 15:13:00 +00:00
"The worklet being scheduled by this dispatcher doesn't match the type of the dispatcher");
//We need to determine if we have the need to do any dynamic
//transforms. This is fairly simple of a query. We just need to check
//everything in the FunctionInterface and see if any of them have the
//proper dynamic trait. Doing this, allows us to generate zero dynamic
//check & convert code when we already know all the types. This results
//in smaller executables and libraries.
using ParamTypes = typename ParameterInterface::ParameterSig;
using HasDynamicTypes =
brigand::fold<ParamTypes,
std::false_type,
2017-05-18 14:29:41 +00:00
detail::DetermineIfHasDynamicParameter<brigand::_element, brigand::_state>>;
2017-05-18 14:29:41 +00:00
this->StartInvokeDynamic(parameters, HasDynamicTypes());
}
2017-05-18 14:29:41 +00:00
template <typename Signature>
VTKM_CONT void StartInvokeDynamic(const vtkm::internal::FunctionInterface<Signature>& parameters,
std::true_type) const
{
// As we do the dynamic transform, we are also going to check the static
// type against the TypeCheckTag in the ControlSignature tags. To do this,
// the check needs access to both the parameter (in the parameters
// argument) and the ControlSignature tags (in the ControlInterface type).
// To make this possible, we call DynamicTransform with a functor containing
// the control signature tags. It uses the index provided by the
// dynamic transform mechanism to get the right tag and make sure that
// the dynamic type is correct. (This prevents the compiler from expanding
// worklets with types that should not be.)
2017-05-18 14:29:41 +00:00
parameters.DynamicTransformCont(detail::DispatcherBaseDynamicTransform<ControlInterface>(),
detail::DispatcherBaseDynamicTransformHelper<MyType>(this));
}
2017-05-18 14:29:41 +00:00
template <typename Signature>
VTKM_CONT void StartInvokeDynamic(const vtkm::internal::FunctionInterface<Signature>& parameters,
std::false_type) const
{
using ParameterInterface = vtkm::internal::FunctionInterface<Signature>;
//Nothing requires a conversion from dynamic to static types, so
//next we need to verify that each argument's type is correct. If not
//we need to throw a nice compile time error
using ParamTypes = typename ParameterInterface::ParameterSig;
using ContSigTypes = typename vtkm::internal::detail::FunctionSigInfo<
2017-05-18 14:29:41 +00:00
typename WorkletType::ControlSignature>::Parameters;
using NumParams = vtkm::internal::MakeIntegerSequence<ParameterInterface::ARITY>;
using isAllValid = brigand::fold<
NumParams,
std::true_type,
2017-05-18 14:29:41 +00:00
detail::DetermineHasInCorrectParameters<brigand::_element, ParamTypes, ContSigTypes>>;
//When isAllValid is false we produce a second static_assert
//stating that the static transform is not possible
2017-05-18 14:29:41 +00:00
static_assert(isAllValid::value, "Unable to match all parameter types");
this->DynamicTransformInvoke(parameters, isAllValid());
}
2017-05-18 14:29:41 +00:00
template <typename Signature>
VTKM_CONT void DynamicTransformInvoke(
const vtkm::internal::FunctionInterface<Signature>& parameters,
std::true_type) const
{
// TODO: Check parameters
2017-05-18 14:29:41 +00:00
static const vtkm::IdComponent INPUT_DOMAIN_INDEX = WorkletType::InputDomain::INDEX;
reinterpret_cast<const DerivedClass*>(this)->DoInvoke(
vtkm::internal::make_Invocation<INPUT_DOMAIN_INDEX>(
parameters, ControlInterface(), ExecutionInterface()));
}
2017-05-18 14:29:41 +00:00
template <typename Signature>
VTKM_CONT void DynamicTransformInvoke(const vtkm::internal::FunctionInterface<Signature>&,
std::false_type) const
{
}
public:
2017-05-18 14:29:41 +00:00
template <typename... ArgTypes>
VTKM_CONT void Invoke(ArgTypes... args) const
{
2017-05-18 14:29:41 +00:00
this->StartInvoke(vtkm::internal::make_FunctionInterface<void>(args...));
}
protected:
VTKM_CONT
2017-05-18 14:29:41 +00:00
DispatcherBase(const WorkletType& worklet)
: Worklet(worklet)
{
}
2017-05-18 14:29:41 +00:00
template <typename Invocation, typename DeviceAdapter>
VTKM_CONT void BasicInvoke(const Invocation& invocation,
vtkm::Id numInstances,
2017-05-18 14:29:41 +00:00
DeviceAdapter device) const
{
2015-09-24 22:38:42 +00:00
this->InvokeTransportParameters(
2017-05-18 14:29:41 +00:00
invocation, numInstances, this->Worklet.GetScatter().GetOutputRange(numInstances), device);
}
2017-05-18 14:29:41 +00:00
template <typename Invocation, typename DeviceAdapter>
VTKM_CONT void BasicInvoke(const Invocation& invocation,
vtkm::Id2 dimensions,
2017-05-18 14:29:41 +00:00
DeviceAdapter device) const
{
2017-05-18 14:29:41 +00:00
this->BasicInvoke(invocation, vtkm::Id3(dimensions[0], dimensions[1], 1), device);
}
2017-05-18 14:29:41 +00:00
template <typename Invocation, typename DeviceAdapter>
VTKM_CONT void BasicInvoke(const Invocation& invocation,
vtkm::Id3 dimensions,
2017-05-18 14:29:41 +00:00
DeviceAdapter device) const
{
this->InvokeTransportParameters(
invocation, dimensions, this->Worklet.GetScatter().GetOutputRange(dimensions), device);
}
WorkletType Worklet;
private:
2017-02-23 17:12:38 +00:00
// Dispatchers cannot be copied
2017-05-18 14:29:41 +00:00
DispatcherBase(const MyType&) = delete;
void operator=(const MyType&) = delete;
template <typename Invocation,
typename InputRangeType,
typename OutputRangeType,
2017-05-18 14:29:41 +00:00
typename DeviceAdapter>
VTKM_CONT void InvokeTransportParameters(const Invocation& invocation,
const InputRangeType& inputRange,
OutputRangeType&& outputRange,
2017-05-18 14:29:41 +00:00
DeviceAdapter device) const
{
// The first step in invoking a worklet is to transport the arguments to
// the execution environment. The invocation object passed to this function
// contains the parameters passed to Invoke in the control environment. We
// will use the template magic in the FunctionInterface class to invoke the
// appropriate Transport class on each parameter and get a list of
// execution objects (corresponding to the arguments of the Invoke in the
// control environment) in a FunctionInterface. Specifically, we use a
// static transform of the FunctionInterface to call the transport on each
// argument and return the corresponding execution environment object.
typedef typename Invocation::ParameterInterface ParameterInterfaceType;
2017-05-18 14:29:41 +00:00
const ParameterInterfaceType& parameters = invocation.Parameters;
typedef detail::DispatcherBaseTransportFunctor<typename Invocation::ControlInterface,
typename Invocation::InputDomainType,
DeviceAdapter>
2017-05-18 14:29:41 +00:00
TransportFunctorType;
typedef
typename ParameterInterfaceType::template StaticTransformType<TransportFunctorType>::type
ExecObjectParameters;
ExecObjectParameters execObjectParameters = parameters.StaticTransformCont(
TransportFunctorType(invocation.GetInputDomain(), inputRange, outputRange));
// Get the arrays used for scattering input to output.
typename WorkletType::ScatterType::OutputToInputMapType outputToInputMap =
2017-05-18 14:29:41 +00:00
this->Worklet.GetScatter().GetOutputToInputMap(inputRange);
typename WorkletType::ScatterType::VisitArrayType visitArray =
2017-05-18 14:29:41 +00:00
this->Worklet.GetScatter().GetVisitArray(inputRange);
// Replace the parameters in the invocation with the execution object and
// pass to next step of Invoke. Also add the scatter information.
2017-05-18 14:29:41 +00:00
this->InvokeSchedule(invocation.ChangeParameters(execObjectParameters)
.ChangeOutputToInputMap(outputToInputMap.PrepareForInput(device))
.ChangeVisitArray(visitArray.PrepareForInput(device)),
outputRange,
device);
}
2017-05-18 14:29:41 +00:00
template <typename Invocation, typename RangeType, typename DeviceAdapter>
VTKM_CONT void InvokeSchedule(const Invocation& invocation, RangeType range, DeviceAdapter) const
{
using Algorithm = vtkm::cont::DeviceAdapterAlgorithm<DeviceAdapter>;
using TaskTypes = typename vtkm::cont::DeviceTaskTypes<DeviceAdapter>;
// The TaskType class handles the magic of fetching values
// for each instance and calling the worklet's function.
// The TaskType will evaluate to one of the following classes:
//
// vtkm::exec::internal::TaskSingular
// vtkm::exec::internal::TaskTiling1D
// vtkm::exec::internal::TaskTiling3D
auto task = TaskTypes::MakeTask(this->Worklet, invocation, range);
Algorithm::ScheduleTask(task, range);
}
};
}
}
} // namespace vtkm::worklet::internal
#endif //vtk_m_worklet_internal_DispatcherBase_h