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