Blender V5.0
array_utils.cc
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#include <functional>
10
11#include "BLI_array_utils.hh"
12#include "BLI_threads.h"
13
14#include "atomic_ops.h"
15
16namespace blender::array_utils {
17
18void copy(const GVArray &src, GMutableSpan dst, const int64_t grain_size)
19{
20 BLI_assert(src.type() == dst.type());
21 BLI_assert(src.size() == dst.size());
22 threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
23 src.materialize_to_uninitialized(range, dst.data());
24 });
25}
26
27void copy(const GVArray &src,
28 const IndexMask &selection,
29 GMutableSpan dst,
30 const int64_t grain_size)
31{
32 BLI_assert(src.type() == dst.type());
33 BLI_assert(src.size() >= selection.min_array_size());
34 BLI_assert(dst.size() >= selection.min_array_size());
35 threading::parallel_for(selection.index_range(), grain_size, [&](const IndexRange range) {
36 src.materialize_to_uninitialized(selection.slice(range), dst.data());
37 });
38}
39
40void gather(const GVArray &src,
41 const IndexMask &indices,
42 GMutableSpan dst,
43 const int64_t grain_size)
44{
45 BLI_assert(src.type() == dst.type());
46 BLI_assert(indices.size() == dst.size());
47 threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
48 src.materialize_compressed_to_uninitialized(indices.slice(range), dst.slice(range).data());
49 });
50}
51
52void gather(const GSpan src, const IndexMask &indices, GMutableSpan dst, const int64_t grain_size)
53{
54 gather(GVArray::from_span(src), indices, dst, grain_size);
55}
56
58 const OffsetIndices<int> dst_offsets,
59 const IndexMask &selection,
60 const GSpan src,
61 GMutableSpan dst)
62{
63 /* Each group might be large, so a threaded copy might make sense here too. */
64 selection.foreach_index(GrainSize(512), [&](const int i) {
65 dst.slice(dst_offsets[i]).copy_from(src.slice(src_offsets[i]));
66 });
67}
68
70{
71 if (indices.size() < 8192 || BLI_system_thread_count() < 4) {
72 for (const int i : indices) {
73 counts[i]++;
74 }
75 }
76 else {
77 threading::parallel_for(indices.index_range(), 4096, [&](const IndexRange range) {
78 for (const int i : indices.slice(range)) {
79 atomic_add_and_fetch_int32(&counts[i], 1);
80 }
81 });
82 }
83}
84
86{
87 threading::parallel_for(span.index_range(), 4096, [&](IndexRange range) {
88 for (const int i : range) {
89 span[i] = !span[i];
90 }
91 });
92}
93
95{
96 mask.foreach_index_optimized<int64_t>([&](const int64_t i) { span[i] = !span[i]; });
97}
98
99static bool all_equal(const Span<bool> span, const bool test)
100{
101 return std::all_of(span.begin(), span.end(), [&](const bool value) { return value == test; });
102}
103
104static bool all_equal(const VArray<bool> &varray, const IndexRange range, const bool test)
105{
106 return std::all_of(
107 range.begin(), range.end(), [&](const int64_t i) { return varray[i] == test; });
108}
109
110BooleanMix booleans_mix_calc(const VArray<bool> &varray, const IndexRange range_to_check)
111{
112 if (varray.is_empty()) {
113 return BooleanMix::None;
114 }
115 const CommonVArrayInfo info = varray.common_info();
117 return *static_cast<const bool *>(info.data) ? BooleanMix::AllTrue : BooleanMix::AllFalse;
118 }
120 const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
122 range_to_check,
123 4096,
125 [&](const IndexRange range, const BooleanMix init) {
126 if (init == BooleanMix::Mixed) {
127 return init;
128 }
129 const Span<bool> slice = span.slice(range);
130 const bool compare = (init == BooleanMix::None) ? slice.first() :
132 if (all_equal(slice, compare)) {
133 return compare ? BooleanMix::AllTrue : BooleanMix::AllFalse;
134 }
135 return BooleanMix::Mixed;
136 },
137 [&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; });
138 }
140 range_to_check,
141 2048,
143 [&](const IndexRange range, const BooleanMix init) {
144 if (init == BooleanMix::Mixed) {
145 return init;
146 }
147 /* Alternatively, this could use #materialize to retrieve many values at once. */
148 const bool compare = (init == BooleanMix::None) ? varray[range.first()] :
150 if (all_equal(varray, range, compare)) {
151 return compare ? BooleanMix::AllTrue : BooleanMix::AllFalse;
152 }
153 return BooleanMix::Mixed;
154 },
155 [&](BooleanMix a, BooleanMix b) { return (a == b) ? a : BooleanMix::Mixed; });
156}
157
159{
160 if (varray.is_empty() || mask.is_empty()) {
161 return 0;
162 }
163 /* Check if mask is full. */
164 if (varray.size() == mask.size()) {
165 const CommonVArrayInfo info = varray.common_info();
167 return *static_cast<const bool *>(info.data) ? varray.size() : 0;
168 }
170 const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
172 varray.index_range(),
173 4096,
174 0,
175 [&](const IndexRange range, const int64_t init) {
176 const Span<bool> slice = span.slice(range);
177 return init + std::count(slice.begin(), slice.end(), true);
178 },
179 std::plus<>());
180 }
182 varray.index_range(),
183 2048,
184 0,
185 [&](const IndexRange range, const int64_t init) {
186 int64_t value = init;
187 /* Alternatively, this could use #materialize to retrieve many values at once. */
188 for (const int64_t i : range) {
189 value += int64_t(varray[i]);
190 }
191 return value;
192 },
193 std::plus<>());
194 }
195 const CommonVArrayInfo info = varray.common_info();
197 return *static_cast<const bool *>(info.data) ? mask.size() : 0;
198 }
199 int64_t value = 0;
200 mask.foreach_segment([&](const IndexMaskSegment segment) {
201 for (const int64_t i : segment) {
202 value += int64_t(varray[i]);
203 }
204 });
205 return value;
206}
207
208bool contains(const VArray<bool> &varray, const IndexMask &indices_to_check, const bool value)
209{
210 const CommonVArrayInfo info = varray.common_info();
212 return *static_cast<const bool *>(info.data) == value;
213 }
215 const Span<bool> span(static_cast<const bool *>(info.data), varray.size());
217 indices_to_check.index_range(),
218 4096,
219 false,
220 [&](const IndexRange range, const bool init) {
221 if (init) {
222 return init;
223 }
224 const IndexMask sliced_mask = indices_to_check.slice(range);
225 if (std::optional<IndexRange> range = sliced_mask.to_range()) {
226 return span.slice(*range).contains(value);
227 }
228 for (const int64_t segment_i : IndexRange(sliced_mask.segments_num())) {
229 const IndexMaskSegment segment = sliced_mask.segment(segment_i);
230 for (const int i : segment) {
231 if (span[i] == value) {
232 return true;
233 }
234 }
235 }
236 return false;
237 },
238 std::logical_or());
239 }
240 return threading::parallel_reduce(
241 indices_to_check.index_range(),
242 2048,
243 false,
244 [&](const IndexRange range, const bool init) {
245 if (init) {
246 return init;
247 }
248 constexpr int64_t MaxChunkSize = 512;
249 const int64_t slice_end = range.one_after_last();
250 for (int64_t start = range.start(); start < slice_end; start += MaxChunkSize) {
251 const int64_t end = std::min<int64_t>(start + MaxChunkSize, slice_end);
252 const int64_t size = end - start;
253 const IndexMask sliced_mask = indices_to_check.slice(start, size);
254 std::array<bool, MaxChunkSize> values;
255 auto values_end = values.begin() + size;
256 varray.materialize_compressed(sliced_mask, values);
257 if (std::find(values.begin(), values_end, value) != values_end) {
258 return true;
259 }
260 }
261 return false;
262 },
263 std::logical_or());
264}
265
267{
268 return count_booleans(varray, IndexMask(varray.size()));
269}
270
272{
273 if (indices.size() != range.size()) {
274 return false;
275 }
277 range.index_range(),
278 4096,
279 true,
280 [&](const IndexRange part, const bool is_range) {
281 const Span<int> local_indices = indices.slice(part);
282 const IndexRange local_range = range.slice(part);
283 return is_range &&
284 std::equal(local_indices.begin(), local_indices.end(), local_range.begin());
285 },
286 std::logical_and<>());
287}
288
289} // namespace blender::array_utils
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_system_thread_count(void)
Definition threads.cc:253
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
long long int int64_t
constexpr int64_t one_after_last() const
constexpr int64_t start() const
void copy_from(GSpan values)
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
GSpan slice(const int64_t start, int64_t size) const
static GVArray from_span(GSpan span)
constexpr int64_t first() const
constexpr Iterator end() const
constexpr int64_t size() const
constexpr Iterator begin() const
constexpr IndexRange index_range() const
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
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 const T * end() const
Definition BLI_span.hh:224
constexpr const T * begin() const
Definition BLI_span.hh:220
IndexRange index_range() const
CommonVArrayInfo common_info() const
IndexMask slice(IndexRange range) const
void foreach_index(Fn &&fn) const
static ushort indices[]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void invert_booleans(MutableSpan< bool > span)
void count_indices(Span< int > indices, MutableSpan< int > counts)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
bool indices_are_range(Span< int > indices, IndexRange range)
void copy_group_to_group(OffsetIndices< int > src_offsets, OffsetIndices< int > dst_offsets, const IndexMask &selection, GSpan src, GMutableSpan dst)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
bool contains(const VArray< bool > &varray, const IndexMask &indices_to_check, bool value)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
static bool all_equal(const Span< bool > span, const bool test)
int64_t count_booleans(const VArray< bool > &varray)
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
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
static void init(bNodeTree *, bNode *node)
i
Definition text_draw.cc:230