Blender V4.3
multi_function.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
6
7#include "BLI_task.hh"
8#include "BLI_threads.h"
9
11
13
18
23
25{
26 for (const int i : fn.param_indices()) {
27 const ParamType param_type = fn.param_type(i);
28 if (ELEM(param_type.interface_type(),
31 {
32 if (param_type.data_type().is_vector()) {
33 return false;
34 }
35 }
36 }
37 return true;
38}
39
40static int64_t compute_grain_size(const ExecutionHints &hints, const IndexMask &mask)
41{
42 int64_t grain_size = hints.min_grain_size;
43 if (hints.uniform_execution_time) {
44 const int thread_count = BLI_system_thread_count();
45 /* Avoid using a small grain size even if it is not necessary. */
46 const int64_t thread_based_grain_size = mask.size() / thread_count / 4;
47 grain_size = std::max(grain_size, thread_based_grain_size);
48 }
49 if (hints.allocates_array) {
50 const int64_t max_grain_size = 10000;
51 /* Avoid allocating many large intermediate arrays. Better process data in smaller chunks to
52 * keep peak memory usage lower. */
53 grain_size = std::min(grain_size, max_grain_size);
54 }
55 return grain_size;
56}
57
58static int64_t compute_alignment(const int64_t grain_size)
59{
60 if (grain_size <= 512) {
61 /* Don't use a number that's too large, or otherwise the work will be split quite unevenly. */
62 return 8;
63 }
64 /* It's not common that more elements are processed in a loop at once. */
65 return 32;
66}
67
68static void add_sliced_parameters(const Signature &signature,
69 Params &full_params,
70 const IndexRange slice_range,
71 ParamsBuilder &r_sliced_params)
72{
73 for (const int param_index : signature.params.index_range()) {
74 const ParamType &param_type = signature.params[param_index].type;
75 switch (param_type.category()) {
77 const GVArray &varray = full_params.readonly_single_input(param_index);
78 r_sliced_params.add_readonly_single_input(varray.slice(slice_range));
79 break;
80 }
82 const GMutableSpan span = full_params.single_mutable(param_index);
83 const GMutableSpan sliced_span = span.slice(slice_range);
84 r_sliced_params.add_single_mutable(sliced_span);
85 break;
86 }
88 if (bool(signature.params[param_index].flag & ParamFlag::SupportsUnusedOutput)) {
90 param_index);
91 if (span.is_empty()) {
92 r_sliced_params.add_ignored_single_output();
93 }
94 else {
95 const GMutableSpan sliced_span = span.slice(slice_range);
96 r_sliced_params.add_uninitialized_single_output(sliced_span);
97 }
98 }
99 else {
100 const GMutableSpan span = full_params.uninitialized_single_output(param_index);
101 const GMutableSpan sliced_span = span.slice(slice_range);
102 r_sliced_params.add_uninitialized_single_output(sliced_span);
103 }
104 break;
105 }
110 break;
111 }
112 }
113 }
114}
115
116void MultiFunction::call_auto(const IndexMask &mask, Params params, Context context) const
117{
118 if (mask.is_empty()) {
119 return;
120 }
121 const ExecutionHints hints = this->execution_hints();
122 const int64_t grain_size = compute_grain_size(hints, mask);
123
124 if (mask.size() <= grain_size) {
125 this->call(mask, params, context);
126 return;
127 }
128
129 const bool supports_threading = supports_threading_by_slicing_params(*this);
130 if (!supports_threading) {
131 this->call(mask, params, context);
132 return;
133 }
134
135 const int64_t alignment = compute_alignment(grain_size);
137 mask.index_range(), grain_size, alignment, [&](const IndexRange sub_range) {
138 const IndexMask sliced_mask = mask.slice(sub_range);
139 if (!hints.allocates_array) {
140 /* There is no benefit to changing indices in this case. */
141 this->call(sliced_mask, params, context);
142 return;
143 }
144 if (sliced_mask[0] < grain_size) {
145 /* The indices are low, no need to offset them. */
146 this->call(sliced_mask, params, context);
147 return;
148 }
149 const int64_t input_slice_start = sliced_mask[0];
150 const int64_t input_slice_size = sliced_mask.last() - input_slice_start + 1;
151 const IndexRange input_slice_range{input_slice_start, input_slice_size};
152
153 IndexMaskMemory memory;
154 const int64_t offset = -input_slice_start;
155 const IndexMask shifted_mask = mask.slice_and_shift(sub_range, offset, memory);
156
157 ParamsBuilder sliced_params{*this, &shifted_mask};
158 add_sliced_parameters(*signature_ref_, params, input_slice_range, sliced_params);
159 this->call(shifted_mask, sliced_params, context);
160 });
161}
162
163std::string MultiFunction::debug_name() const
164{
165 return signature_ref_->function_name;
166}
167
168} // namespace blender::fn::multi_function
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
int BLI_system_thread_count(void)
Definition threads.cc:253
#define ELEM(...)
GMutableSpan slice(const int64_t start, int64_t size) const
GVArray slice(IndexRange slice) const
ParamType param_type(int param_index) const
virtual void call(const IndexMask &mask, Params params, Context context) const =0
virtual ExecutionHints get_execution_hints() const
void call_auto(const IndexMask &mask, Params params, Context context) const
void add_single_mutable(GMutableSpan ref, StringRef expected_name="")
void add_ignored_single_output(StringRef expected_name="")
void add_uninitialized_single_output(T *value, StringRef expected_name="")
void add_readonly_single_input(const T *value, StringRef expected_name="")
MutableSpan< T > uninitialized_single_output_if_required(int param_index, StringRef name="")
MutableSpan< T > uninitialized_single_output(int param_index, StringRef name="")
MutableSpan< T > single_mutable(int param_index, StringRef name="")
VArray< T > readonly_single_input(int param_index, StringRef name="")
IndexMask slice_and_shift(IndexRange range, int64_t offset, IndexMaskMemory &memory) const
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static bool supports_threading_by_slicing_params(const MultiFunction &fn)
static void add_sliced_parameters(const Signature &signature, Params &full_params, const IndexRange slice_range, ParamsBuilder &r_sliced_params)
static int64_t compute_alignment(const int64_t grain_size)
static int64_t compute_grain_size(const ExecutionHints &hints, const IndexMask &mask)
void parallel_for_aligned(const IndexRange range, const int64_t grain_size, const int64_t alignment, const Function &function)
Definition BLI_task.hh:141
__int64 int64_t
Definition stdint.h:89