Welcome to mirror list, hosted at ThFree Co, Russian Federation.

BLI_virtual_array_devirtualize.hh « blenlib « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2293b55b66316d7272a1991ddcb6f93dd742991b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* SPDX-License-Identifier: GPL-2.0-or-later */

#pragma once

#include <tuple>

#include "BLI_virtual_array.hh"

namespace blender {

struct SingleInputTagBase {
};
template<typename T> struct SingleInputTag : public SingleInputTagBase {
  using BaseType = T;
};
struct SingleOutputTagBase {
};
template<typename T> struct SingleOutputTag : public SingleOutputTagBase {
  using BaseType = T;
};

template<typename T> struct ParamType {
};

template<typename T> struct ParamType<SingleInputTag<T>> {
  using type = VArray<T>;
};

template<typename T> struct ParamType<SingleOutputTag<T>> {
  using type = MutableSpan<T>;
};

struct DevirtualizeNone {
};
struct DevirtualizeSpan {
};
struct DevirtualizeSingle {
};

template<typename TagsTuple, size_t I>
using BaseType = typename std::tuple_element_t<I, TagsTuple>::BaseType;

template<typename Fn, typename... Args> class ArrayDevirtualizer {
 private:
  using TagsTuple = std::tuple<Args...>;

  Fn fn_;
  IndexMask mask_;
  std::tuple<const typename ParamType<Args>::type *...> params_;

  std::array<bool, sizeof...(Args)> varray_is_span_;
  std::array<bool, sizeof...(Args)> varray_is_single_;

  bool executed_ = false;

 public:
  ArrayDevirtualizer(Fn fn, const IndexMask *mask, const typename ParamType<Args>::type *...params)
      : fn_(std::move(fn)), mask_(*mask), params_{params...}
  {
    this->init(std::make_index_sequence<sizeof...(Args)>{});
  }

  void execute_fallback()
  {
    BLI_assert(!executed_);
    this->execute_fallback_impl(std::make_index_sequence<sizeof...(Args)>{});
  }

  bool try_execute_devirtualized()
  {
    BLI_assert(!executed_);
    return this->try_execute_devirtualized_impl();
  }

  void execute_materialized()
  {
    BLI_assert(!executed_);
    this->execute_materialized_impl(std::make_index_sequence<sizeof...(Args)>{});
  }

 private:
  template<size_t... I> void execute_materialized_impl(std::index_sequence<I...> /* indices */)
  {
    static constexpr int64_t MaxChunkSize = 32;
    const int64_t mask_size = mask_.size();
    std::tuple<TypedBuffer<BaseType<TagsTuple, I>, MaxChunkSize>...> buffers_owner;
    std::tuple<MutableSpan<BaseType<TagsTuple, I>>...> buffers = {
        MutableSpan{std::get<I>(buffers_owner).ptr(), std::min(mask_size, MaxChunkSize)}...};

    for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) {
      const int64_t chunk_size = std::min(mask_size - chunk_start, MaxChunkSize);
      const IndexMask sliced_mask = mask_.slice(chunk_start, chunk_size);
      const int64_t sliced_mask_size = sliced_mask.size();
      (
          [&]() {
            using ParamTag = std::tuple_element_t<I, TagsTuple>;
            using T = typename ParamTag::BaseType;
            if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
              MutableSpan in_chunk = std::get<I>(buffers).take_front(sliced_mask_size);
              const VArray<T> *varray = std::get<I>(params_);
              varray->materialize_compressed_to_uninitialized(sliced_mask, in_chunk);
            }
          }(),
          ...);

      fn_(IndexRange(sliced_mask_size), sliced_mask, [&]() {
        using ParamTag = std::tuple_element_t<I, TagsTuple>;
        using T = typename ParamTag::BaseType;
        if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
          MutableSpan<T> in_chunk = std::get<I>(buffers).take_front(sliced_mask_size);
          return in_chunk;
        }
        else if constexpr (std::is_base_of_v<SingleOutputTagBase, ParamTag>) {
          MutableSpan<T> out_span = *std::get<I>(params_);
          return out_span.data();
        }
      }()...);

      (
          [&]() {
            using ParamTag = std::tuple_element_t<I, TagsTuple>;
            using T = typename ParamTag::BaseType;
            if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
              MutableSpan<T> in_chunk = std::get<I>(buffers);
              destruct_n(in_chunk.data(), sliced_mask_size);
            }
          }(),
          ...);
    }
  }

  template<typename... Mode> bool try_execute_devirtualized_impl()
  {
    if constexpr (sizeof...(Mode) == sizeof...(Args)) {
      this->try_execute_devirtualized_impl_call(std::tuple<Mode...>(),
                                                std::make_index_sequence<sizeof...(Args)>());
      return true;
    }
    else {
      constexpr size_t I = sizeof...(Mode);
      using ParamTag = std::tuple_element_t<I, TagsTuple>;
      if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
        if (varray_is_single_[I]) {
          return this->try_execute_devirtualized_impl<Mode..., DevirtualizeSingle>();
        }
        else if (varray_is_span_[I]) {
          return this->try_execute_devirtualized_impl<Mode..., DevirtualizeSpan>();
        }
        else {
          return false;
        }
      }
      else {
        return this->try_execute_devirtualized_impl<Mode..., DevirtualizeNone>();
      }
    }
  }

  template<typename... Mode, size_t... I>
  void try_execute_devirtualized_impl_call(std::tuple<Mode...> /* modes */,
                                           std::index_sequence<I...> /* indices */)
  {
    mask_.to_best_mask_type([&](auto mask) {
      fn_(mask,
          mask,
          this->get_execute_param<I, std::tuple_element_t<I, std::tuple<Mode...>>>()...);
    });
    executed_ = true;
  }

  template<size_t... I> void init(std::index_sequence<I...> /* indices */)
  {
    varray_is_span_.fill(false);
    varray_is_single_.fill(false);
    (this->init_param<I>(), ...);
  }

  template<size_t I> void init_param()
  {
    using ParamTag = std::tuple_element_t<I, TagsTuple>;
    if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
      const typename ParamType<ParamTag>::type *varray = std::get<I>(params_);
      varray_is_span_[I] = varray->is_span();
      varray_is_single_[I] = varray->is_single();
    }
  }

  template<size_t... I> void execute_fallback_impl(std::index_sequence<I...> /* indices */)
  {
    fn_(mask_, mask_, this->get_execute_param<I, DevirtualizeNone>()...);
    executed_ = true;
  }

  template<size_t I, typename Mode> auto get_execute_param()
  {
    using ParamTag = std::tuple_element_t<I, TagsTuple>;
    if constexpr (std::is_base_of_v<SingleInputTagBase, ParamTag>) {
      using T = typename ParamTag::BaseType;
      const VArray<T> *varray = std::get<I>(params_);
      if constexpr (std::is_same_v<Mode, DevirtualizeNone>) {
        return *varray;
      }
      else if constexpr (std::is_same_v<Mode, DevirtualizeSingle>) {
        return SingleAsSpan(*varray);
      }
      else if constexpr (std::is_same_v<Mode, DevirtualizeSpan>) {
        return varray->get_internal_span();
      }
    }
    else if constexpr (std::is_base_of_v<SingleOutputTagBase, ParamTag>) {
      return std::get<I>(params_)->data();
    }
  }
};

}  // namespace blender