VTK-m  2.0
DispatcherBase.h
Go to the documentation of this file.
1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 #ifndef vtk_m_worklet_internal_DispatcherBase_h
11 #define vtk_m_worklet_internal_DispatcherBase_h
12 
13 #include <vtkm/List.h>
14 #include <vtkm/StaticAssert.h>
15 
18 
19 #include <vtkm/cont/CastAndCall.h>
20 #include <vtkm/cont/ErrorBadType.h>
21 #include <vtkm/cont/Logging.h>
22 #include <vtkm/cont/TryExecute.h>
23 
27 
29 
31 
33 
34 #include <vtkmstd/integer_sequence.h>
35 #include <vtkmstd/is_trivial.h>
36 
37 #include <sstream>
38 
39 namespace vtkm
40 {
41 namespace worklet
42 {
43 namespace internal
44 {
45 
46 template <typename Domain>
47 inline auto SchedulingRange(const Domain& inputDomain) -> decltype(inputDomain.GetNumberOfValues())
48 {
49  return inputDomain.GetNumberOfValues();
50 }
51 
52 template <typename Domain>
53 inline auto SchedulingRange(const Domain* const inputDomain)
54  -> decltype(inputDomain->GetNumberOfValues())
55 {
56  return inputDomain->GetNumberOfValues();
57 }
58 
59 template <typename Domain, typename SchedulingRangeType>
60 inline auto SchedulingRange(const Domain& inputDomain, SchedulingRangeType type)
61  -> decltype(inputDomain.GetSchedulingRange(type))
62 {
63  return inputDomain.GetSchedulingRange(type);
64 }
65 
66 template <typename Domain, typename SchedulingRangeType>
67 inline auto SchedulingRange(const Domain* const inputDomain, SchedulingRangeType type)
68  -> decltype(inputDomain->GetSchedulingRange(type))
69 {
70  return inputDomain->GetSchedulingRange(type);
71 }
72 
73 namespace detail
74 {
75 
76 // This code is actually taking an error found at compile-time and not
77 // reporting it until run-time. This seems strange at first, but this
78 // behavior is actually important. With dynamic arrays and similar dynamic
79 // classes, there may be types that are technically possible (such as using a
80 // vector where a scalar is expected) but in reality never happen. Thus, for
81 // these unsupported combinations we just silently halt the compiler from
82 // attempting to create code for these errant conditions and throw a run-time
83 // error if one every tries to create one.
84 inline void PrintFailureMessage(int index)
85 {
86  std::stringstream message;
87  message << "Encountered bad type for parameter " << index
88  << " when calling Invoke on a dispatcher.";
89  throw vtkm::cont::ErrorBadType(message.str());
90 }
91 
92 inline void PrintNullPtrMessage(int index, int mode)
93 {
94  std::stringstream message;
95  if (mode == 0)
96  {
97  message << "Encountered nullptr for parameter " << index;
98  }
99  else
100  {
101  message << "Encountered nullptr for " << index << " from last parameter ";
102  }
103  message << " when calling Invoke on a dispatcher.";
104  throw vtkm::cont::ErrorBadValue(message.str());
105 }
106 
107 template <typename T>
108 inline void not_nullptr(T* ptr, int index, int mode = 0)
109 {
110  if (!ptr)
111  {
112  PrintNullPtrMessage(index, mode);
113  }
114 }
115 template <typename T>
116 inline void not_nullptr(T&&, int, int mode = 0)
117 {
118  (void)mode;
119 }
120 
121 template <typename T>
122 inline T& as_ref(T* ptr)
123 {
124  return *ptr;
125 }
126 template <typename T>
127 inline T&& as_ref(T&& t)
128 {
129  return std::forward<T>(t);
130 }
131 
132 
133 template <typename T, bool noError>
134 struct ReportTypeOnError : std::integral_constant<bool, noError>
135 {
136 };
137 
138 template <int Value, bool noError>
139 struct ReportValueOnError : std::integral_constant<bool, noError>
140 {
141 };
142 
143 template <typename Type>
144 struct IsDynamicTypeImpl
145 {
146  using T = vtkm::internal::remove_pointer_and_decay<Type>;
147  using DynamicTag = typename vtkm::cont::internal::DynamicTransformTraits<T>::DynamicTag;
148  using type =
149  typename std::is_same<DynamicTag, vtkm::cont::internal::DynamicTransformTagCastAndCall>::type;
150 };
151 template <typename T>
152 using IsDynamicType = typename IsDynamicTypeImpl<T>::type;
153 
154 template <typename SigTagList, typename ParamList, typename IndexSequence>
155 struct ZipControlParamImpl;
156 template <typename... SigTags, typename... Params, vtkm::IdComponent... Indices>
157 struct ZipControlParamImpl<vtkm::List<SigTags...>,
158  vtkm::List<Params...>,
159  vtkmstd::integer_sequence<vtkm::IdComponent, Indices...>>
160 {
161  VTKM_STATIC_ASSERT(sizeof...(SigTags) == sizeof...(Params));
162  VTKM_STATIC_ASSERT(sizeof...(SigTags) == sizeof...(Indices));
163 
164  using type = vtkm::List<
165  vtkm::List<SigTags, Params, std::integral_constant<vtkm::IdComponent, (Indices + 1)>>...>;
166 };
167 template <typename SigTagList, typename ParamList, typename IndexSequence>
168 using ZipControlParam = typename ZipControlParamImpl<SigTagList, ParamList, IndexSequence>::type;
169 
170 template <typename WorkletType>
171 struct ControlArgumentValidator
172 {
173  template <typename SigTag, typename Param, vtkm::IdComponent Index>
174  void operator()(vtkm::List<SigTag, Param, std::integral_constant<vtkm::IdComponent, Index>>) const
175  {
176  using T = std::remove_pointer_t<Param>;
177  using TypeCheckTag = typename SigTag::TypeCheckTag;
178  static constexpr bool isCorrect = vtkm::cont::arg::TypeCheck<TypeCheckTag, T>::value;
179 
180  static_assert(
181  isCorrect,
182  "If you get an error here, that means that your code has invoked a worklet,"
183  " and one of the arguments of the Invoke is the wrong type. Each argument of the invoke"
184  " corresponds to a tag in the arguments of the ControlSignature of the worklet. If there"
185  " is a mismatch, then you get an error here (instead of where you called invoke). For"
186  " example, if the worklet has a control signature as ControlSignature(CellSetIn, ...) and"
187  " the first argument passed to the invoke is an ArrayHandle, you will get an error here"
188  " because you cannot use an ArrayHandle in place of a CellSetIn argument. (You need to use"
189  " a CellSet.) If the compiler supports it, the next few errors on the following lines of"
190  " code will give information about where the error actually occurred.");
191 
192  // If you are getting the error described above, the following lines will give you some
193  // diagnostics (in the form of compile errors). Each one will result in a compile error
194  // reporting an undefined type for ReportTypeOnError (or ReportValueOnError). What we are
195  // really reporting is the first template argument, which is one of the types or values that
196  // should help pinpoint where the error is. The comment for static_assert provides the
197  // type/value being reported. (Note that some compilers report better types than others. If
198  // your compiler is giving unhelpful types like "T" or "WorkletType", you may need to try a
199  // different compiler.)
200  static_assert(ReportTypeOnError<WorkletType, isCorrect>::value,
201  "The first template argument to ReportTypeOnError is the worklet being invoked");
202  static_assert(ReportValueOnError<Index, isCorrect>::value,
203  "The first template argument to ReportTypeOnError is the index of Invoke "
204  "parameter (starting at index 1)");
205  static_assert(ReportTypeOnError<T, isCorrect>::value,
206  "The first template argument to ReportTypeOnError is the type passed to Invoke");
207  static_assert(ReportTypeOnError<TypeCheckTag, isCorrect>::value,
208  "The first template argument to ReportTypeOnError is the type check tag used");
209  }
210 };
211 
212 // Checks that an argument in a ControlSignature is a valid control signature
213 // tag. Causes a compile error otherwise.
214 struct DispatcherBaseControlSignatureTagCheck
215 {
216  template <typename ControlSignatureTag, vtkm::IdComponent Index>
217  struct ReturnType
218  {
219  // If you get a compile error here, it means there is something that is
220  // not a valid control signature tag in a worklet's ControlSignature.
221  VTKM_IS_CONTROL_SIGNATURE_TAG(ControlSignatureTag);
222  using type = ControlSignatureTag;
223  };
224 };
225 
226 // Checks that an argument in a ExecutionSignature is a valid execution
227 // signature tag. Causes a compile error otherwise.
228 struct DispatcherBaseExecutionSignatureTagCheck
229 {
230  template <typename ExecutionSignatureTag, vtkm::IdComponent Index>
231  struct ReturnType
232  {
233  // If you get a compile error here, it means there is something that is not
234  // a valid execution signature tag in a worklet's ExecutionSignature.
235  VTKM_IS_EXECUTION_SIGNATURE_TAG(ExecutionSignatureTag);
236  using type = ExecutionSignatureTag;
237  };
238 };
239 
240 struct DispatcherBaseTryExecuteFunctor
241 {
242  template <typename Device, typename DispatcherBaseType, typename Invocation, typename RangeType>
243  VTKM_CONT bool operator()(Device device,
244  const DispatcherBaseType* self,
245  Invocation& invocation,
246  const RangeType& dimensions)
247  {
248  auto outputRange = self->Scatter.GetOutputRange(dimensions);
249  self->InvokeTransportParameters(
250  invocation, dimensions, outputRange, self->Mask.GetThreadRange(outputRange), device);
251  return true;
252  }
253 };
254 
255 // A look up helper used by DispatcherBaseTransportFunctor to determine
256 //the types independent of the device we are templated on.
257 template <typename ControlInterface, vtkm::IdComponent Index>
258 struct DispatcherBaseTransportInvokeTypes
259 {
260  //Moved out of DispatcherBaseTransportFunctor to reduce code generation
261  using ControlSignatureTag = typename ControlInterface::template ParameterType<Index>::type;
262  using TransportTag = typename ControlSignatureTag::TransportTag;
263 };
264 
265 VTKM_CONT
266 inline vtkm::Id FlatRange(vtkm::Id range)
267 {
268  return range;
269 }
270 
271 VTKM_CONT
272 inline vtkm::Id FlatRange(const vtkm::Id3& range)
273 {
274  return range[0] * range[1] * range[2];
275 }
276 
277 // A functor used in a StaticCast of a FunctionInterface to transport arguments
278 // from the control environment to the execution environment.
279 template <typename ControlInterface, typename InputDomainType, typename Device>
280 struct DispatcherBaseTransportFunctor
281 {
282  const InputDomainType& InputDomain; // Warning: this is a reference
283  vtkm::Id InputRange;
284  vtkm::Id OutputRange;
285  vtkm::cont::Token& Token; // Warning: this is a reference
286 
287  // TODO: We need to think harder about how scheduling on 3D arrays works.
288  // Chances are we need to allow the transport for each argument to manage
289  // 3D indices (for example, allocate a 3D array instead of a 1D array).
290  // But for now, just treat all transports as 1D arrays.
291  template <typename InputRangeType, typename OutputRangeType>
292  VTKM_CONT DispatcherBaseTransportFunctor(const InputDomainType& inputDomain,
293  const InputRangeType& inputRange,
294  const OutputRangeType& outputRange,
295  vtkm::cont::Token& token)
296  : InputDomain(inputDomain)
297  , InputRange(FlatRange(inputRange))
298  , OutputRange(FlatRange(outputRange))
299  , Token(token)
300  {
301  }
302 
303 
304  template <typename ControlParameter, vtkm::IdComponent Index>
305  struct ReturnType
306  {
307  using TransportTag =
308  typename DispatcherBaseTransportInvokeTypes<ControlInterface, Index>::TransportTag;
309  using T = vtkm::internal::remove_pointer_and_decay<ControlParameter>;
310  using TransportType = typename vtkm::cont::arg::Transport<TransportTag, T, Device>;
311  using type = typename std::decay<typename TransportType::ExecObjectType>::type;
312 
313  // If you get a compile error here, it means that an execution object type is not
314  // trivially copyable. This is strictly disallowed. All execution objects must be
315  // trivially copyable so that the can be memcpy-ed between host and devices.
316  // Note that it is still legal for execution objects to have pointers or other
317  // references to resources on a particular device. It is up to the generating code
318  // to ensure that all referenced resources are valid on the target device.
319  VTKM_IS_TRIVIALLY_COPYABLE(type);
320  };
321 
322  template <typename ControlParameter, vtkm::IdComponent Index>
323  VTKM_CONT typename ReturnType<ControlParameter, Index>::type operator()(
324  ControlParameter&& invokeData,
325  vtkm::internal::IndexTag<Index>) const
326  {
327  using TransportTag =
328  typename DispatcherBaseTransportInvokeTypes<ControlInterface, Index>::TransportTag;
329  using T = vtkm::internal::remove_pointer_and_decay<ControlParameter>;
331 
332  not_nullptr(invokeData, Index);
333  return transport(as_ref(invokeData),
334  as_ref(this->InputDomain),
335  this->InputRange,
336  this->OutputRange,
337  this->Token);
338  }
339 
340 
341 
342 private:
343  void operator=(const DispatcherBaseTransportFunctor&) = delete;
344 };
345 
346 // Should this functionality be added to List.h? Should there be the general ability to
347 // remove some number of items from the beginning or end of a list?
348 template <typename L>
349 struct ListRemoveFirstImpl;
350 template <typename T, typename... Ts>
351 struct ListRemoveFirstImpl<vtkm::List<T, Ts...>>
352 {
353  using type = vtkm::List<Ts...>;
354 };
355 template <typename L>
356 using ListRemoveFirst = typename ListRemoveFirstImpl<L>::type;
357 
358 //forward declares
359 template <std::size_t LeftToProcess>
360 struct for_each_dynamic_arg;
361 
362 template <std::size_t LeftToProcess, typename TypeCheckTag>
363 struct convert_arg_wrapper
364 {
365  template <typename T, typename... Args>
366  void operator()(T&& t, Args&&... args) const
367  {
368  using Type = typename std::decay<T>::type;
369  using valid =
370  std::integral_constant<bool, vtkm::cont::arg::TypeCheck<TypeCheckTag, Type>::value>;
371  this->WillContinue(valid(), std::forward<T>(t), std::forward<Args>(args)...);
372  }
373  template <typename T, typename... Args>
374  void WillContinue(std::true_type, T&& t, Args&&... args) const
375  {
376  for_each_dynamic_arg<LeftToProcess - 1>()(std::forward<Args>(args)..., std::forward<T>(t));
377  }
378  template <typename... Args>
379  void WillContinue(std::false_type, Args&&...) const
380  {
381  vtkm::worklet::internal::detail::PrintFailureMessage(LeftToProcess);
382  }
383 };
384 
385 template <std::size_t LeftToProcess,
386  typename T,
387  typename ContParams,
388  typename Trampoline,
389  typename... Args>
390 inline void convert_arg(vtkm::cont::internal::DynamicTransformTagStatic,
391  T&& t,
392  const ContParams&,
393  const Trampoline& trampoline,
394  Args&&... args)
395 { //This is a static array, so just push it to the back
396  using popped_sig = ListRemoveFirst<ContParams>;
397  for_each_dynamic_arg<LeftToProcess - 1>()(
398  trampoline, popped_sig(), std::forward<Args>(args)..., std::forward<T>(t));
399 }
400 
401 template <std::size_t LeftToProcess,
402  typename T,
403  typename ContParams,
404  typename Trampoline,
405  typename... Args>
406 inline void convert_arg(vtkm::cont::internal::DynamicTransformTagCastAndCall,
407  T&& t,
408  const ContParams&,
409  const Trampoline& trampoline,
410  Args&&... args)
411 { //This is something dynamic so cast and call
412  using tag_check = typename vtkm::ListAt<ContParams, 0>::TypeCheckTag;
413  using popped_sig = ListRemoveFirst<ContParams>;
414 
415  not_nullptr(t, LeftToProcess, 1);
416  vtkm::cont::CastAndCall(as_ref(t),
417  convert_arg_wrapper<LeftToProcess, tag_check>(),
418  trampoline,
419  popped_sig(),
420  std::forward<Args>(args)...);
421 }
422 
423 template <std::size_t LeftToProcess>
424 struct for_each_dynamic_arg
425 {
426  template <typename Trampoline, typename ContParams, typename T, typename... Args>
427  void operator()(const Trampoline& trampoline, ContParams&& sig, T&& t, Args&&... args) const
428  {
429  //Determine that state of T when it is either a `cons&` or a `* const&`
430  using Type = vtkm::internal::remove_pointer_and_decay<T>;
431  using tag = typename vtkm::cont::internal::DynamicTransformTraits<Type>::DynamicTag;
432  //convert the first item to a known type
433  convert_arg<LeftToProcess>(
434  tag(), std::forward<T>(t), sig, trampoline, std::forward<Args>(args)...);
435  }
436 };
437 
438 template <>
439 struct for_each_dynamic_arg<0>
440 {
441  template <typename Trampoline, typename ContParams, typename... Args>
442  void operator()(const Trampoline& trampoline, ContParams&&, Args&&... args) const
443  {
444  trampoline.StartInvokeDynamic(std::false_type(), std::forward<Args>(args)...);
445  }
446 };
447 
448 template <typename Trampoline, typename ContParams, typename... Args>
449 inline void deduce(Trampoline&& trampoline, ContParams&& sig, Args&&... args)
450 {
451  for_each_dynamic_arg<sizeof...(Args)>()(std::forward<Trampoline>(trampoline), sig, args...);
452 }
453 
454 } // namespace detail
455 
458 template <vtkm::IdComponent MaxIndexAllowed>
459 struct PlaceholderValidator
460 {
461  PlaceholderValidator() {}
462 
463  // An overload operator to detect possible out of bound placeholder
464  template <int N>
465  void operator()(vtkm::internal::meta::Type<vtkm::placeholders::Arg<N>>) const
466  {
467  static_assert(N <= MaxIndexAllowed,
468  "An argument in the execution signature"
469  " (usually _2, _3, _4, etc.) refers to a control signature argument that"
470  " does not exist. For example, you will get this error if you have _3 (or"
471  " _4 or _5 or so on) as one of the execution signature arguments, but you"
472  " have fewer than 3 (or 4 or 5 or so on) arguments in the control signature.");
473  }
474 
475  template <typename DerivedType>
476  void operator()(vtkm::internal::meta::Type<DerivedType>) const
477  {
478  }
479 };
480 
484 template <typename DerivedClass, typename WorkletType, typename BaseWorkletType>
485 class DispatcherBase
486 {
487 private:
488  using MyType = DispatcherBase<DerivedClass, WorkletType, BaseWorkletType>;
489 
490  friend struct detail::for_each_dynamic_arg<0>;
491 
492 protected:
493  using ControlInterface =
494  vtkm::internal::FunctionInterface<typename WorkletType::ControlSignature>;
495 
496  // We go through the GetExecSig as that generates a default ExecutionSignature
497  // if one doesn't exist on the worklet
498  using ExecutionSignature =
499  typename vtkm::placeholders::GetExecSig<WorkletType>::ExecutionSignature;
500  using ExecutionInterface = vtkm::internal::FunctionInterface<ExecutionSignature>;
501 
502  static constexpr vtkm::IdComponent NUM_INVOKE_PARAMS = ControlInterface::ARITY;
503 
504 private:
505  // We don't really need these types, but declaring them checks the arguments
506  // of the control and execution signatures.
507  using ControlSignatureCheck = typename ControlInterface::template StaticTransformType<
508  detail::DispatcherBaseControlSignatureTagCheck>::type;
509  using ExecutionSignatureCheck = typename ExecutionInterface::template StaticTransformType<
510  detail::DispatcherBaseExecutionSignatureTagCheck>::type;
511 
512  template <typename... Args>
513  VTKM_CONT void StartInvoke(Args&&... args) const
514  {
515  using ParameterInterface =
516  vtkm::internal::FunctionInterface<void(vtkm::internal::remove_cvref<Args>...)>;
517 
518  VTKM_STATIC_ASSERT_MSG(ParameterInterface::ARITY == NUM_INVOKE_PARAMS,
519  "Dispatcher Invoke called with wrong number of arguments.");
520 
521  static_assert(
522  std::is_base_of<BaseWorkletType, WorkletType>::value,
523  "The worklet being scheduled by this dispatcher doesn't match the type of the dispatcher");
524 
525  // Check if the placeholders defined in the execution environment exceed the max bound
526  // defined in the control environment by throwing a nice compile error.
527  using ComponentSig = typename ExecutionInterface::ComponentSig;
528  vtkm::ListForEach(PlaceholderValidator<NUM_INVOKE_PARAMS>{},
530 
531  //We need to determine if we have the need to do any dynamic
532  //transforms. This is fairly simple of a query. We just need to check
533  //everything in the FunctionInterface and see if any of them have the
534  //proper dynamic trait. Doing this, allows us to generate zero dynamic
535  //check & convert code when we already know all the types. This results
536  //in smaller executables and libraries.
537  using ParamTypes = typename ParameterInterface::ParameterSig;
538  using HasDynamicTypes = vtkm::ListAny<ParamTypes, detail::IsDynamicType>;
539 
540  this->StartInvokeDynamic(HasDynamicTypes(), std::forward<Args>(args)...);
541  }
542 
543  template <typename... Args>
544  VTKM_CONT void StartInvokeDynamic(std::true_type, Args&&... args) const
545  {
546  // As we do the dynamic transform, we are also going to check the static
547  // type against the TypeCheckTag in the ControlSignature tags. To do this,
548  // the check needs access to both the parameter (in the parameters
549  // argument) and the ControlSignature tags (in the ControlInterface type).
550  using ContParamsInfo =
551  vtkm::internal::detail::FunctionSigInfo<typename WorkletType::ControlSignature>;
552  typename ContParamsInfo::Parameters parameters;
553  detail::deduce(*this, parameters, std::forward<Args>(args)...);
554  }
555 
556  template <typename... Args>
557  VTKM_CONT void StartInvokeDynamic(std::false_type, Args&&... args) const
558  {
559  using ParameterInterface =
560  vtkm::internal::FunctionInterface<void(vtkm::internal::remove_cvref<Args>...)>;
561 
562  //Nothing requires a conversion from dynamic to static types, so
563  //next we need to verify that each argument's type is correct. If not
564  //we need to throw a nice compile time error
565  using ParamTypes = typename ParameterInterface::ParameterSig;
566  using ContSigTypes = typename vtkm::internal::detail::FunctionSigInfo<
567  typename WorkletType::ControlSignature>::Parameters;
568  using ParamZip = detail::ZipControlParam<
569  ContSigTypes,
570  ParamTypes,
571  vtkmstd::make_integer_sequence<vtkm::IdComponent, vtkm::ListSize<ParamTypes>::value>>;
572 
573  // This will cause compile errors if there is an argument mismatch.
574  vtkm::ListForEach(detail::ControlArgumentValidator<WorkletType>{}, ParamZip{});
575 
576  auto fi =
577  vtkm::internal::make_FunctionInterface<void, vtkm::internal::remove_cvref<Args>...>(args...);
578  auto ivc = vtkm::internal::Invocation<ParameterInterface,
579  ControlInterface,
580  ExecutionInterface,
581  WorkletType::InputDomain::INDEX,
582  vtkm::internal::NullType,
583  vtkm::internal::NullType>(
584  fi, vtkm::internal::NullType{}, vtkm::internal::NullType{});
585  static_cast<const DerivedClass*>(this)->DoInvoke(ivc);
586  }
587 
588 public:
594  VTKM_CONT
595  void SetDevice(vtkm::cont::DeviceAdapterId device) { this->Device = device; }
596 
597  VTKM_CONT vtkm::cont::DeviceAdapterId GetDevice() const { return this->Device; }
599 
600  using ScatterType = typename WorkletType::ScatterType;
601  using MaskType = typename WorkletType::MaskType;
602 
603  template <typename... Args>
604  VTKM_CONT void Invoke(Args&&... args) const
605  {
607  "Invoking Worklet: '%s'",
608  vtkm::cont::TypeToString<DerivedClass>().c_str());
609  this->StartInvoke(std::forward<Args>(args)...);
610  }
611 
612 protected:
613  // If you get a compile error here about there being no appropriate constructor for ScatterType
614  // or MapType, then that probably means that the worklet you are trying to execute has defined a
615  // custom ScatterType or MaskType and that you need to create one (because there is no default
616  // way to construct the scatter or mask).
617  VTKM_CONT
618  DispatcherBase(const WorkletType& worklet = WorkletType(),
619  const ScatterType& scatter = ScatterType(),
620  const MaskType& mask = MaskType())
621  : Worklet(worklet)
622  , Scatter(scatter)
623  , Mask(mask)
624  , Device(vtkm::cont::DeviceAdapterTagAny())
625  {
626  }
627 
628  // If you get a compile error here about there being no appropriate constructor for MaskType,
629  // then that probably means that the worklet you are trying to execute has defined a custom
630  // MaskType and that you need to create one (because there is no default way to construct the
631  // mask).
632  VTKM_CONT
633  DispatcherBase(const ScatterType& scatter, const MaskType& mask = MaskType())
634  : Worklet(WorkletType())
635  , Scatter(scatter)
636  , Mask(mask)
637  , Device(vtkm::cont::DeviceAdapterTagAny())
638  {
639  }
640 
641  // If you get a compile error here about there being no appropriate constructor for ScatterType,
642  // then that probably means that the worklet you are trying to execute has defined a custom
643  // ScatterType and that you need to create one (because there is no default way to construct the
644  // scatter).
645  VTKM_CONT
646  DispatcherBase(const WorkletType& worklet,
647  const MaskType& mask,
648  const ScatterType& scatter = ScatterType())
649  : Worklet(worklet)
650  , Scatter(scatter)
651  , Mask(mask)
652  , Device(vtkm::cont::DeviceAdapterTagAny())
653  {
654  }
655 
656  // If you get a compile error here about there being no appropriate constructor for ScatterType,
657  // then that probably means that the worklet you are trying to execute has defined a custom
658  // ScatterType and that you need to create one (because there is no default way to construct the
659  // scatter).
660  VTKM_CONT
661  DispatcherBase(const MaskType& mask, const ScatterType& scatter = ScatterType())
662  : Worklet(WorkletType())
663  , Scatter(scatter)
664  , Mask(mask)
665  , Device(vtkm::cont::DeviceAdapterTagAny())
666  {
667  }
668 
669  friend struct internal::detail::DispatcherBaseTryExecuteFunctor;
670 
671  template <typename Invocation>
672  VTKM_CONT void BasicInvoke(Invocation& invocation, vtkm::Id numInstances) const
673  {
674  bool success =
675  vtkm::cont::TryExecuteOnDevice(this->Device,
676  internal::detail::DispatcherBaseTryExecuteFunctor(),
677  this,
678  invocation,
679  numInstances);
680  if (!success)
681  {
682  throw vtkm::cont::ErrorExecution("Failed to execute worklet on any device.");
683  }
684  }
685 
686  template <typename Invocation>
687  VTKM_CONT void BasicInvoke(Invocation& invocation, vtkm::Id2 dimensions) const
688  {
689  this->BasicInvoke(invocation, vtkm::Id3(dimensions[0], dimensions[1], 1));
690  }
691 
692  template <typename Invocation>
693  VTKM_CONT void BasicInvoke(Invocation& invocation, vtkm::Id3 dimensions) const
694  {
695  bool success =
696  vtkm::cont::TryExecuteOnDevice(this->Device,
697  internal::detail::DispatcherBaseTryExecuteFunctor(),
698  this,
699  invocation,
700  dimensions);
701  if (!success)
702  {
703  throw vtkm::cont::ErrorExecution("Failed to execute worklet on any device.");
704  }
705  }
706 
707  WorkletType Worklet;
708  ScatterType Scatter;
709  MaskType Mask;
710 
711 private:
712  // Dispatchers cannot be copied
713  DispatcherBase(const MyType&) = delete;
714  void operator=(const MyType&) = delete;
715 
717 
718  template <typename Invocation,
719  typename InputRangeType,
720  typename OutputRangeType,
721  typename ThreadRangeType,
722  typename DeviceAdapter>
723  VTKM_CONT void InvokeTransportParameters(Invocation& invocation,
724  const InputRangeType& inputRange,
725  OutputRangeType&& outputRange,
726  ThreadRangeType&& threadRange,
727  DeviceAdapter device) const
728  {
729  // This token represents the scope of the execution objects. It should
730  // exist as long as things run on the device.
731  vtkm::cont::Token token;
732 
733  // The first step in invoking a worklet is to transport the arguments to
734  // the execution environment. The invocation object passed to this function
735  // contains the parameters passed to Invoke in the control environment. We
736  // will use the template magic in the FunctionInterface class to invoke the
737  // appropriate Transport class on each parameter and get a list of
738  // execution objects (corresponding to the arguments of the Invoke in the
739  // control environment) in a FunctionInterface. Specifically, we use a
740  // static transform of the FunctionInterface to call the transport on each
741  // argument and return the corresponding execution environment object.
742  using ParameterInterfaceType = typename Invocation::ParameterInterface;
743  ParameterInterfaceType& parameters = invocation.Parameters;
744 
745  using TransportFunctorType =
746  detail::DispatcherBaseTransportFunctor<typename Invocation::ControlInterface,
747  typename Invocation::InputDomainType,
748  DeviceAdapter>;
749  using ExecObjectParameters =
750  typename ParameterInterfaceType::template StaticTransformType<TransportFunctorType>::type;
751 
752  ExecObjectParameters execObjectParameters = parameters.StaticTransformCont(
753  TransportFunctorType(invocation.GetInputDomain(), inputRange, outputRange, token));
754 
755  // Get the arrays used for scattering input to output.
756  typename ScatterType::OutputToInputMapType outputToInputMap =
757  this->Scatter.GetOutputToInputMap(inputRange);
758  typename ScatterType::VisitArrayType visitArray = this->Scatter.GetVisitArray(inputRange);
759 
760  // Get the arrays used for masking output elements.
761  typename MaskType::ThreadToOutputMapType threadToOutputMap =
762  this->Mask.GetThreadToOutputMap(inputRange);
763 
764  // Replace the parameters in the invocation with the execution object and
765  // pass to next step of Invoke. Also add the scatter information.
766  vtkm::internal::Invocation<ExecObjectParameters,
767  typename Invocation::ControlInterface,
768  typename Invocation::ExecutionInterface,
769  Invocation::InputDomainIndex,
770  decltype(outputToInputMap.PrepareForInput(device, token)),
771  decltype(visitArray.PrepareForInput(device, token)),
772  decltype(threadToOutputMap.PrepareForInput(device, token)),
773  DeviceAdapter>
774  changedInvocation(execObjectParameters,
775  outputToInputMap.PrepareForInput(device, token),
776  visitArray.PrepareForInput(device, token),
777  threadToOutputMap.PrepareForInput(device, token));
778 
779  this->InvokeSchedule(changedInvocation, threadRange, device);
780  }
781 
782  template <typename Invocation, typename RangeType, typename DeviceAdapter>
783  VTKM_CONT void InvokeSchedule(const Invocation& invocation, RangeType range, DeviceAdapter) const
784  {
786  using TaskTypes = typename vtkm::cont::DeviceTaskTypes<DeviceAdapter>;
787 
788  // The TaskType class handles the magic of fetching values
789  // for each instance and calling the worklet's function.
790  // The TaskType will evaluate to one of the following classes:
791  //
792  // vtkm::exec::internal::TaskSingular
793  // vtkm::exec::internal::TaskTiling1D
794  // vtkm::exec::internal::TaskTiling3D
795  auto task = TaskTypes::MakeTask(this->Worklet, invocation, range);
796  Algorithm::ScheduleTask(task, range);
797  }
798 };
799 }
800 }
801 } // namespace vtkm::worklet::internal
802 
803 #endif //vtk_m_worklet_internal_DispatcherBase_h
VTKM_LOG_SCOPE
#define VTKM_LOG_SCOPE(level,...)
Definition: Logging.h:265
VTKM_IS_CONTROL_SIGNATURE_TAG
#define VTKM_IS_CONTROL_SIGNATURE_TAG(tag)
Checks that the argument is a proper tag for an ControlSignature.
Definition: ControlSignatureTagBase.h:57
vtkm
Groups connected points that have the same field value.
Definition: Atomic.h:19
vtkm::IdComponent
vtkm::Int32 IdComponent
Represents a component ID (index of component in a vector).
Definition: Types.h:168
ControlSignatureTagBase.h
vtkm::ListForEach
VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT void ListForEach(Functor &&f, vtkm::List< Ts... >, Args &&... args)
Definition: List.h:720
vtkm::cont::ErrorBadType
This class is thrown when VTK-m encounters data of a type that is incompatible with the current opera...
Definition: ErrorBadType.h:25
vtkm::cont::CastAndCall
void CastAndCall(const DynamicObject &dynamicObject, Functor &&f, Args &&... args)
A Generic interface to CastAndCall.
Definition: CastAndCall.h:47
vtkm::Id
vtkm::Int32 Id
Represents an ID (index into arrays).
Definition: Types.h:191
vtkm::ListAt
typename detail::ListAtImpl< List, Index >::type ListAt
Finds the type at the given index.
Definition: List.h:344
ExecutionSignatureTagBase.h
vtkm::cont::TryExecuteOnDevice
VTKM_CONT bool TryExecuteOnDevice(vtkm::cont::DeviceAdapterId devId, Functor &&functor)
Try to execute a functor on a specific device selected at runtime.
Definition: TryExecute.h:178
vtkm::cont::Token
A token to hold the scope of an ArrayHandle or other object.
Definition: Token.h:35
CastAndCall.h
VTKM_STATIC_ASSERT
#define VTKM_STATIC_ASSERT(condition)
Definition: StaticAssert.h:16
TryExecute.h
vtkm::ListTransform
typename detail::ListTransformImpl< List, Transform >::type ListTransform
Constructs a list containing all types in a source list applied to a transform template.
Definition: List.h:592
WorkletBase.h
VTKM_STATIC_ASSERT_MSG
#define VTKM_STATIC_ASSERT_MSG(condition, message)
Definition: StaticAssert.h:18
vtkm::cont::arg::TypeCheck
Class for checking that a type matches the semantics for an argument.
Definition: TypeCheck.h:34
Index
int Index
Definition: ChooseCudaDevice.h:87
FunctionInterface.h
vtkm::cont::DeviceTaskTypes
Class providing a device-specific support for selecting the optimal Task type for a given worklet.
Definition: DeviceAdapterAlgorithm.h:744
VTKM_CONT
#define VTKM_CONT
Definition: ExportMacros.h:57
vtkm::cont::DeviceAdapterAlgorithm
Struct containing device adapter algorithms.
Definition: DeviceAdapterAlgorithm.h:41
Transport.h
vtkm::cont::DeviceAdapterId
Definition: DeviceAdapterTag.h:52
vtkm::Vec< vtkm::Id, 3 >
vtkm::cont::ErrorBadValue
This class is thrown when a VTKm function or method encounters an invalid value that inhibits progres...
Definition: ErrorBadValue.h:25
StaticAssert.h
TypeCheck.h
Invocation.h
vtkm::cont::arg::Transport
Class for transporting from the control to the execution environment.
Definition: Transport.h:38
vtkm::List
Definition: List.h:34
ErrorBadType.h
Logging.h
Logging utilities.
VTKM_IS_EXECUTION_SIGNATURE_TAG
#define VTKM_IS_EXECUTION_SIGNATURE_TAG(tag)
Checks that the argument is a proper tag for an ExecutionSignature.
Definition: ExecutionSignatureTagBase.h:58
DecayHelpers.h
vtkm::cont::ErrorExecution
This class is thrown in the control environment whenever an error occurs in the execution environment...
Definition: ErrorExecution.h:25
vtkm::ListAny
vtkm::ListReduce< vtkm::ListTransform< List, Predicate >, vtkm::internal::meta::Or, std::false_type > ListAny
Determines whether any of the types in the list are "true.".
Definition: List.h:864
vtkm::cont::LogLevel::Perf
@ Perf
General timing data and algorithm flow information, such as filter execution, worklet dispatches,...
List.h