Blender  V3.3
BLI_devirtualize_parameters.hh
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #pragma once
4 
35 #include "BLI_virtual_array.hh"
36 
38 
42 enum class DeviMode {
43  /* This is used as zero-value to compare to, to avoid casting to int. */
44  None = 0,
45  /* Don't use devirtualization for that parameter, just pass it along. */
46  Keep = (1 << 0),
47  /* Devirtualize #Varray as #Span. */
48  Span = (1 << 1),
49  /* Devirtualize #VArray as #SingleAsSpan. */
50  Single = (1 << 2),
51  /* Devirtualize #IndexMask as #IndexRange. */
52  Range = (1 << 3),
53 };
55 
57 template<DeviMode... Mode> using DeviModeSequence = ValueSequence<DeviMode, Mode...>;
58 
62 template<typename Fn, typename... SourceTypes> class Devirtualizer {
63  private:
65  template<size_t I>
66  using type_at_index = typename TypeSequence<SourceTypes...>::template at_index<I>;
67  static constexpr size_t SourceTypesNum = sizeof...(SourceTypes);
68 
70  Fn fn_;
71 
76  std::tuple<const SourceTypes *...> sources_;
77 
79  bool executed_ = false;
80 
81  public:
82  Devirtualizer(Fn fn, const SourceTypes *...sources) : fn_(std::move(fn)), sources_{sources...}
83  {
84  }
85 
89  bool executed() const
90  {
91  return executed_;
92  }
93 
105  template<DeviMode... AllowedModes>
107  {
108  BLI_assert(!executed_);
109  static_assert(sizeof...(AllowedModes) == SourceTypesNum);
110  this->try_execute_devirtualized_impl(DeviModeSequence<>(),
112  }
113 
118  {
119  BLI_assert(!executed_);
120  this->try_execute_devirtualized_impl_call(
122  std::make_index_sequence<SourceTypesNum>());
123  }
124 
125  private:
137  template<DeviMode... Mode, DeviMode... AllowedModes>
138  bool try_execute_devirtualized_impl(
139  /* Initially empty, but then extended by one element in each recursive step. */
140  DeviModeSequence<Mode...> /* modes */,
141  /* Bit flag for every parameter. */
142  DeviModeSequence<AllowedModes...> /* allowed_modes */)
143  {
144  static_assert(SourceTypesNum == sizeof...(AllowedModes));
145  if constexpr (SourceTypesNum == sizeof...(Mode)) {
146  /* End of recursion, now call the function with the determined #DeviModes. */
147  this->try_execute_devirtualized_impl_call(DeviModeSequence<Mode...>(),
148  std::make_index_sequence<SourceTypesNum>());
149  return true;
150  }
151  else {
152  /* Index of the parameter that is checked in the current recursive step. */
153  constexpr size_t I = sizeof...(Mode);
154  /* Non-devirtualized parameter type. */
155  using SourceType = type_at_index<I>;
156  /* A bit flag indicating what devirtualizations are allowed in this step. */
157  [[maybe_unused]] constexpr DeviMode allowed_modes =
158  DeviModeSequence<AllowedModes...>::template at_index<I>();
159 
160  /* Handle #VArray types. */
161  if constexpr (is_VArray_v<SourceType>) {
162  /* The actual virtual array, used for dynamic dispatch at run-time. */
163  const SourceType &varray = *std::get<I>(sources_);
164  /* Check if the virtual array is a single value. */
165  if constexpr ((allowed_modes & DeviMode::Single) != DeviMode::None) {
166  if (varray.is_single()) {
167  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Single>(),
168  DeviModeSequence<AllowedModes...>())) {
169  return true;
170  }
171  }
172  }
173  /* Check if the virtual array is a span. */
174  if constexpr ((allowed_modes & DeviMode::Span) != DeviMode::None) {
175  if (varray.is_span()) {
176  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Span>(),
177  DeviModeSequence<AllowedModes...>())) {
178  return true;
179  }
180  }
181  }
182  /* Check if it is ok if the virtual array is not devirtualized. */
183  if constexpr ((allowed_modes & DeviMode::Keep) != DeviMode::None) {
184  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Keep>(),
185  DeviModeSequence<AllowedModes...>())) {
186  return true;
187  }
188  }
189  }
190 
191  /* Handle #IndexMask. */
192  else if constexpr (std::is_same_v<IndexMask, SourceType>) {
193  /* Check if the mask is actually a contiguous range. */
194  if constexpr ((allowed_modes & DeviMode::Range) != DeviMode::None) {
195  /* The actual mask used for dynamic dispatch at run-time. */
196  const IndexMask &mask = *std::get<I>(sources_);
197  if (mask.is_range()) {
198  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Range>(),
199  DeviModeSequence<AllowedModes...>())) {
200  return true;
201  }
202  }
203  }
204  /* Check if mask is also allowed to stay a span. */
205  if constexpr ((allowed_modes & DeviMode::Span) != DeviMode::None) {
206  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Span>(),
207  DeviModeSequence<AllowedModes...>())) {
208  return true;
209  }
210  }
211  }
212 
213  /* Handle unknown types. */
214  else {
215  if (this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Keep>(),
216  DeviModeSequence<AllowedModes...>())) {
217  return true;
218  }
219  }
220  }
221  return false;
222  }
223 
227  template<DeviMode... Mode, size_t... I>
228  void try_execute_devirtualized_impl_call(DeviModeSequence<Mode...> /* modes */,
229  std::index_sequence<I...> /* indices */)
230  {
231 
232  BLI_assert(!executed_);
233  fn_(this->get_devirtualized_parameter<I, Mode>()...);
234  executed_ = true;
235  }
236 
244  template<size_t I, DeviMode Mode> decltype(auto) get_devirtualized_parameter()
245  {
246  using SourceType = type_at_index<I>;
247  static_assert(Mode != DeviMode::None);
248  if constexpr (Mode == DeviMode::Keep) {
249  /* Don't change the original parameter at all. */
250  return *std::get<I>(sources_);
251  }
252  if constexpr (is_VArray_v<SourceType>) {
253  const SourceType &varray = *std::get<I>(sources_);
254  if constexpr (Mode == DeviMode::Single) {
255  /* Devirtualize virtual array as single value. */
256  return SingleAsSpan(varray);
257  }
258  else if constexpr (Mode == DeviMode::Span) {
259  /* Devirtualize virtual array as span. */
260  return varray.get_internal_span();
261  }
262  }
263  else if constexpr (std::is_same_v<IndexMask, SourceType>) {
264  const IndexMask &mask = *std::get<I>(sources_);
265  if constexpr (ELEM(Mode, DeviMode::Span)) {
266  /* Don't devirtualize mask, it's still a span. */
267  return mask;
268  }
269  else if constexpr (Mode == DeviMode::Range) {
270  /* Devirtualize the mask as range. */
271  return mask.as_range();
272  }
273  }
274  }
275 };
276 
277 } // namespace blender::devirtualize_parameters
278 
279 namespace blender {
280 
289 template<typename T, typename Func>
290 inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool enable = true)
291 {
292  using namespace devirtualize_parameters;
293  if (enable) {
294  Devirtualizer<decltype(func), VArray<T>> devirtualizer(func, &varray);
295  constexpr DeviMode devi_mode = DeviMode::Single | DeviMode::Span;
296  devirtualizer.try_execute_devirtualized(DeviModeSequence<devi_mode>());
297  if (devirtualizer.executed()) {
298  return;
299  }
300  }
301  func(varray);
302 }
303 
309 template<typename T1, typename T2, typename Func>
310 inline void devirtualize_varray2(const VArray<T1> &varray1,
311  const VArray<T2> &varray2,
312  const Func &func,
313  bool enable = true)
314 {
315  using namespace devirtualize_parameters;
316  if (enable) {
317  Devirtualizer<decltype(func), VArray<T1>, VArray<T2>> devirtualizer(func, &varray1, &varray2);
318  constexpr DeviMode devi_mode = DeviMode::Single | DeviMode::Span;
319  devirtualizer.try_execute_devirtualized(DeviModeSequence<devi_mode, devi_mode>());
320  if (devirtualizer.executed()) {
321  return;
322  }
323  }
324  func(varray1, varray2);
325 }
326 
327 } // namespace blender
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define ELEM(...)
void try_execute_devirtualized(DeviModeSequence< AllowedModes... >)
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
ValueSequence< DeviMode, Mode... > DeviModeSequence
ENUM_OPERATORS(DeviMode, DeviMode::Range)
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
void devirtualize_varray2(const VArray< T1 > &varray1, const VArray< T2 > &varray2, const Func &func, bool enable=true)
decltype(detail::make_value_sequence_impl< T, Value >(std::make_index_sequence< Size >())) make_value_sequence
#define I