Blender V5.0
BLI_array_utils.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#pragma once
10
11#include <numeric>
12
13#include "BLI_generic_span.hh"
15#include "BLI_index_mask.hh"
16#include "BLI_math_base.h"
17#include "BLI_offset_indices.hh"
18#include "BLI_task.hh"
19#include "BLI_virtual_array.hh"
20
22
27void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
28template<typename T>
29inline void copy(const VArray<T> &src, MutableSpan<T> dst, const int64_t grain_size = 4096)
30{
31 BLI_assert(src.size() == dst.size());
32 threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
33 src.materialize_to_uninitialized(range, dst);
34 });
35}
36
41template<typename T>
42inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size = 4096)
43{
44 BLI_assert(src.size() == dst.size());
45 threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
46 dst.slice(range).copy_from(src.slice(range));
47 });
48}
49
54void copy(const GVArray &src,
55 const IndexMask &selection,
56 GMutableSpan dst,
57 int64_t grain_size = 4096);
58
63template<typename T>
64inline void copy(const Span<T> src,
65 const IndexMask &selection,
67 const int64_t grain_size = 4096)
68{
69 BLI_assert(src.size() == dst.size());
70 selection.foreach_index_optimized<int64_t>(GrainSize(grain_size),
71 [&](const int64_t i) { dst[i] = src[i]; });
72}
73
74template<typename T> T compute_sum(const Span<T> data)
75{
76 /* Explicitly splitting work into chunks for a couple of reasons:
77 * - Improve numerical stability. While there are even more stable algorithms (e.g. Kahan
78 * summation), they also add more complexity to the hot code path. So far, this simple approach
79 * seems to solve the common issues people run into.
80 * - Support computing the sum using multiple threads.
81 * - Ensure deterministic results even with floating point numbers.
82 */
83 constexpr int64_t chunk_size = 1024;
84 const int64_t chunks_num = divide_ceil_ul(data.size(), chunk_size);
85 Array<T> partial_sums(chunks_num);
86 threading::parallel_for(partial_sums.index_range(), 1, [&](const IndexRange range) {
87 for (const int64_t i : range) {
88 const int64_t start = i * chunk_size;
89 const Span<T> chunk = data.slice_safe(start, chunk_size);
90 const T partial_sum = std::accumulate(chunk.begin(), chunk.end(), T());
91 partial_sums[i] = partial_sum;
92 }
93 });
94 return std::accumulate(partial_sums.begin(), partial_sums.end(), T());
95}
96
100template<typename T, typename IndexT>
101inline void scatter(const Span<T> src,
102 const Span<IndexT> indices,
103 MutableSpan<T> dst,
104 const int64_t grain_size = 4096)
105{
106 BLI_assert(indices.size() == src.size());
107 threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
108 for (const int64_t i : range) {
109 dst[indices[i]] = src[i];
110 }
111 });
112}
113
114template<typename T>
115inline void scatter(const Span<T> src,
116 const IndexMask &indices,
117 MutableSpan<T> dst,
118 const int64_t grain_size = 4096)
119{
120 BLI_assert(indices.size() == src.size());
121 BLI_assert(indices.min_array_size() <= dst.size());
122 indices.foreach_index_optimized<int64_t>(
123 GrainSize(grain_size),
124 [&](const int64_t index, const int64_t pos) { dst[index] = src[pos]; });
125}
126
130void gather(const GVArray &src,
131 const IndexMask &indices,
132 GMutableSpan dst,
133 int64_t grain_size = 4096);
134
138void gather(GSpan src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size = 4096);
139
143template<typename T>
144inline void gather(const VArray<T> &src,
145 const IndexMask &indices,
146 MutableSpan<T> dst,
147 const int64_t grain_size = 4096)
148{
149 BLI_assert(indices.size() == dst.size());
150 threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
151 src.materialize_compressed_to_uninitialized(indices.slice(range), dst.slice(range));
152 });
153}
154
158template<typename T>
159inline void gather(const Span<T> src,
160 const IndexMask &indices,
161 MutableSpan<T> dst,
162 const int64_t grain_size = 4096)
163{
164 BLI_assert(indices.size() == dst.size());
165 indices.foreach_segment(GrainSize(grain_size),
166 [&](const IndexMaskSegment segment, const int64_t segment_pos) {
167 for (const int64_t i : segment.index_range()) {
168 dst[segment_pos + i] = src[segment[i]];
169 }
170 });
171}
172
176template<typename T, typename IndexT>
177inline void gather(const Span<T> src,
178 const Span<IndexT> indices,
179 MutableSpan<T> dst,
180 const int64_t grain_size = 4096)
181{
182 BLI_assert(indices.size() == dst.size());
183 threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
184 for (const int64_t i : range) {
185 dst[i] = src[indices[i]];
186 }
187 });
188}
189
193template<typename T, typename IndexT>
194inline void gather(const VArray<T> &src,
195 const Span<IndexT> indices,
196 MutableSpan<T> dst,
197 const int64_t grain_size = 4096)
198{
199 BLI_assert(indices.size() == dst.size());
200 devirtualize_varray(src, [&](const auto &src) {
201 threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
202 for (const int64_t i : range) {
203 dst[i] = src[indices[i]];
204 }
205 });
206 });
207}
208
209template<typename T>
210inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
211 const OffsetIndices<int> dst_offsets,
212 const IndexMask &selection,
213 const Span<T> src,
214 MutableSpan<T> dst)
215{
216 selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
217 dst.slice(dst_offsets[dst_i]).copy_from(src.slice(src_offsets[src_i]));
218 });
219}
220
221template<typename T>
222inline void gather_group_to_group(const OffsetIndices<int> src_offsets,
223 const OffsetIndices<int> dst_offsets,
224 const IndexMask &selection,
225 const VArray<T> src,
226 MutableSpan<T> dst)
227{
228 selection.foreach_index(GrainSize(512), [&](const int64_t src_i, const int64_t dst_i) {
229 src.materialize_compressed(src_offsets[src_i], dst.slice(dst_offsets[dst_i]));
230 });
231}
232
233template<typename T>
234inline void gather_to_groups(const OffsetIndices<int> dst_offsets,
235 const IndexMask &src_selection,
236 const Span<T> src,
237 MutableSpan<T> dst)
238{
239 src_selection.foreach_index(GrainSize(1024), [&](const int src_i, const int dst_i) {
240 dst.slice(dst_offsets[dst_i]).fill(src[src_i]);
241 });
242}
243
249void copy_group_to_group(OffsetIndices<int> src_offsets,
250 OffsetIndices<int> dst_offsets,
251 const IndexMask &selection,
252 GSpan src,
253 GMutableSpan dst);
254template<typename T>
256 OffsetIndices<int> dst_offsets,
257 const IndexMask &selection,
258 Span<T> src,
259 MutableSpan<T> dst)
260{
261 copy_group_to_group(src_offsets, dst_offsets, selection, GSpan(src), GMutableSpan(dst));
262}
263
272void count_indices(Span<int> indices, MutableSpan<int> counts);
273
274void invert_booleans(MutableSpan<bool> span);
275void invert_booleans(MutableSpan<bool> span, const IndexMask &mask);
276
277int64_t count_booleans(const VArray<bool> &varray);
278int64_t count_booleans(const VArray<bool> &varray, const IndexMask &mask);
279
286BooleanMix booleans_mix_calc(const VArray<bool> &varray, IndexRange range_to_check);
288{
289 return booleans_mix_calc(varray, varray.index_range());
290}
291
293bool contains(const VArray<bool> &varray, const IndexMask &indices_to_check, bool value);
294
298template<typename T> inline Vector<IndexRange> find_all_ranges(const Span<T> span, const T &value)
299{
300 if (span.is_empty()) {
301 return Vector<IndexRange>();
302 }
303 Vector<IndexRange> ranges;
304 int64_t length = (span.first() == value) ? 1 : 0;
305 for (const int64_t i : span.index_range().drop_front(1)) {
306 if (span[i - 1] == value && span[i] != value) {
308 length = 0;
309 }
310 else if (span[i] == value) {
311 length++;
312 }
313 }
314 if (length > 0) {
316 }
317 return ranges;
318}
319
324template<typename T> inline void fill_index_range(MutableSpan<T> span, const T start = 0)
325{
326 std::iota(span.begin(), span.end(), start);
327}
328
329template<typename T>
330bool indexed_data_equal(const Span<T> all_values, const Span<int> indices, const Span<T> values)
331{
332 BLI_assert(indices.size() == values.size());
333 for (const int i : indices.index_range()) {
334 if (all_values[indices[i]] != values[i]) {
335 return false;
336 }
337 }
338 return true;
339}
340
341bool indices_are_range(Span<int> indices, IndexRange range);
342
343} // namespace blender::array_utils
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE uint64_t divide_ceil_ul(uint64_t a, uint64_t b)
BMesh const char void * data
long long int int64_t
IndexRange index_range() const
Definition BLI_array.hh:360
static constexpr IndexRange from_end_size(const int64_t end, const int64_t size)
constexpr IndexRange drop_front(int64_t n) const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
IndexRange index_range() const
void materialize_compressed(const IndexMask &mask, MutableSpan< T > r_span) const
void append(const T &value)
void foreach_index_optimized(Fn &&fn) const
void foreach_index(Fn &&fn) const
static ushort indices[]
uint pos
float length(VecOp< float, D >) RET
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
void gather_to_groups(const OffsetIndices< int > dst_offsets, const IndexMask &src_selection, const Span< T > src, MutableSpan< T > dst)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
T compute_sum(const Span< T > data)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
Vector< IndexRange > find_all_ranges(const Span< T > span, const T &value)
void gather_group_to_group(const OffsetIndices< int > src_offsets, const OffsetIndices< int > dst_offsets, const IndexMask &selection, const Span< T > src, MutableSpan< T > dst)
bool indexed_data_equal(const Span< T > all_values, const Span< int > indices, const Span< T > values)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void fill_index_range(MutableSpan< T > span, const T start=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
closure color scatter(string phase, float Anisotropy, float IOR, float Backscatter, float Alpha, float Diameter)
i
Definition text_draw.cc:230