Blender V4.3
FN_multi_function_procedure_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "testing/testing.h"
6
11
13
14TEST(multi_function_procedure, ConstantOutput)
15{
23 CustomMF_Constant<int> constant_fn{5};
24 auto add_fn = build::SI2_SO<int, int, int>("Add", [](int a, int b) { return a + b; });
25
26 Procedure procedure;
27 ProcedureBuilder builder{procedure};
28
29 auto [var1] = builder.add_call<1>(constant_fn);
30 auto [var2] = builder.add_call<1>(add_fn, {var1, var1});
31 builder.add_destruct(*var1);
32 builder.add_return();
33 builder.add_output_parameter(*var2);
34
35 EXPECT_TRUE(procedure.validate());
36
37 ProcedureExecutor executor{procedure};
38
39 const IndexMask mask(2);
40 ParamsBuilder params{executor, &mask};
41 ContextBuilder context;
42
43 Array<int> output_array(2);
44 params.add_uninitialized_single_output(output_array.as_mutable_span());
45
46 executor.call(mask, params, context);
47
48 EXPECT_EQ(output_array[0], 10);
49 EXPECT_EQ(output_array[1], 10);
50}
51
52TEST(multi_function_procedure, SimpleTest)
53{
62 auto add_fn = mf::build::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
63 auto add_10_fn = mf::build::SM<int>("add_10", [](int &a) { a += 10; });
64
65 Procedure procedure;
66 ProcedureBuilder builder{procedure};
67
68 Variable *var1 = &builder.add_single_input_parameter<int>();
69 Variable *var2 = &builder.add_single_input_parameter<int>();
70 auto [var3] = builder.add_call<1>(add_fn, {var1, var2});
71 auto [var4] = builder.add_call<1>(add_fn, {var2, var3});
72 builder.add_call(add_10_fn, {var4});
73 builder.add_destruct({var1, var2, var3});
74 builder.add_return();
75 builder.add_output_parameter(*var4);
76
77 EXPECT_TRUE(procedure.validate());
78
79 ProcedureExecutor executor{procedure};
80
81 const IndexMask mask(3);
82 ParamsBuilder params{executor, &mask};
83 ContextBuilder context;
84
85 Array<int> input_array = {1, 2, 3};
86 params.add_readonly_single_input(input_array.as_span());
87 params.add_readonly_single_input_value(3);
88
89 Array<int> output_array(3);
90 params.add_uninitialized_single_output(output_array.as_mutable_span());
91
92 executor.call(mask, params, context);
93
94 EXPECT_EQ(output_array[0], 17);
95 EXPECT_EQ(output_array[1], 18);
96 EXPECT_EQ(output_array[2], 19);
97}
98
99TEST(multi_function_procedure, BranchTest)
100{
113 auto add_10_fn = build::SM<int>("add_10", [](int &a) { a += 10; });
114 auto add_100_fn = build::SM<int>("add_100", [](int &a) { a += 100; });
115
116 Procedure procedure;
117 ProcedureBuilder builder{procedure};
118
119 Variable *var1 = &builder.add_single_mutable_parameter<int>();
120 Variable *var2 = &builder.add_single_input_parameter<bool>();
121
122 ProcedureBuilder::Branch branch = builder.add_branch(*var2);
123 branch.branch_false.add_call(add_10_fn, {var1});
124 branch.branch_true.add_call(add_100_fn, {var1});
125 builder.set_cursor_after_branch(branch);
126 builder.add_call(add_10_fn, {var1});
127 builder.add_destruct({var2});
128 builder.add_return();
129
130 EXPECT_TRUE(procedure.validate());
131
132 ProcedureExecutor procedure_fn{procedure};
133 const IndexMask mask(IndexRange(1, 4));
134 ParamsBuilder params(procedure_fn, &mask);
135
136 Array<int> values_a = {1, 5, 3, 6, 2};
137 Array<bool> values_cond = {true, false, true, true, false};
138
139 params.add_single_mutable(values_a.as_mutable_span());
140 params.add_readonly_single_input(values_cond.as_span());
141
142 ContextBuilder context;
143 procedure_fn.call(mask, params, context);
144
145 EXPECT_EQ(values_a[0], 1);
146 EXPECT_EQ(values_a[1], 25);
147 EXPECT_EQ(values_a[2], 113);
148 EXPECT_EQ(values_a[3], 116);
149 EXPECT_EQ(values_a[4], 22);
150}
151
152TEST(multi_function_procedure, EvaluateOne)
153{
160 int tot_evaluations = 0;
161 const auto add_10_fn = mf::build::SI1_SO<int, int>("add_10", [&](int a) {
162 tot_evaluations++;
163 return a + 10;
164 });
165
166 Procedure procedure;
167 ProcedureBuilder builder{procedure};
168
169 Variable *var1 = &builder.add_single_input_parameter<int>();
170 auto [var2] = builder.add_call<1>(add_10_fn, {var1});
171 builder.add_destruct(*var1);
172 builder.add_return();
173 builder.add_output_parameter(*var2);
174
175 ProcedureExecutor procedure_fn{procedure};
176 IndexMaskMemory memory;
177 const IndexMask mask = IndexMask::from_indices<int>({0, 1, 3, 4}, memory);
178 ParamsBuilder params{procedure_fn, &mask};
179
180 Array<int> values_out = {1, 2, 3, 4, 5};
181 params.add_readonly_single_input_value(1);
182 params.add_uninitialized_single_output(values_out.as_mutable_span());
183
184 ContextBuilder context;
185 procedure_fn.call(mask, params, context);
186
187 EXPECT_EQ(values_out[0], 11);
188 EXPECT_EQ(values_out[1], 11);
189 EXPECT_EQ(values_out[2], 3);
190 EXPECT_EQ(values_out[3], 11);
191 EXPECT_EQ(values_out[4], 11);
192 /* We expect only one evaluation, because the input is constant. */
193 EXPECT_EQ(tot_evaluations, 1);
194}
195
196TEST(multi_function_procedure, SimpleLoop)
197{
213 CustomMF_Constant<int> const_1_fn{1};
214 CustomMF_Constant<int> const_0_fn{0};
215 auto greater_or_equal_fn = mf::build::SI2_SO<int, int, bool>(
216 "greater or equal", [](int a, int b) { return a >= b; });
217 auto double_fn = build::SM<int>("double", [](int &a) { a *= 2; });
218 auto add_1000_fn = build::SM<int>("add 1000", [](int &a) { a += 1000; });
219 auto add_1_fn = build::SM<int>("add 1", [](int &a) { a += 1; });
220
221 Procedure procedure;
222 ProcedureBuilder builder{procedure};
223
224 Variable *var_count = &builder.add_single_input_parameter<int>("count");
225 auto [var_out] = builder.add_call<1>(const_1_fn);
226 var_out->set_name("out");
227 auto [var_index] = builder.add_call<1>(const_0_fn);
228 var_index->set_name("index");
229
230 ProcedureBuilder::Loop loop = builder.add_loop();
231 auto [var_condition] = builder.add_call<1>(greater_or_equal_fn, {var_index, var_count});
232 var_condition->set_name("condition");
233 ProcedureBuilder::Branch branch = builder.add_branch(*var_condition);
234 branch.branch_true.add_destruct(*var_condition);
235 branch.branch_true.add_loop_break(loop);
236 branch.branch_false.add_destruct(*var_condition);
237 builder.set_cursor_after_branch(branch);
238 builder.add_call(double_fn, {var_out});
239 builder.add_call(add_1_fn, {var_index});
240 builder.add_loop_continue(loop);
241 builder.set_cursor_after_loop(loop);
242 builder.add_call(add_1000_fn, {var_out});
243 builder.add_destruct({var_count, var_index});
244 builder.add_return();
245 builder.add_output_parameter(*var_out);
246
247 EXPECT_TRUE(procedure.validate());
248
249 ProcedureExecutor procedure_fn{procedure};
250 IndexMaskMemory memory;
251 const IndexMask mask = IndexMask::from_indices<int>({0, 1, 3, 4}, memory);
252 ParamsBuilder params{procedure_fn, &mask};
253
254 Array<int> counts = {4, 3, 7, 6, 4};
255 Array<int> results(5, -1);
256
257 params.add_readonly_single_input(counts.as_span());
258 params.add_uninitialized_single_output(results.as_mutable_span());
259
260 ContextBuilder context;
261 procedure_fn.call(mask, params, context);
262
263 EXPECT_EQ(results[0], 1016);
264 EXPECT_EQ(results[1], 1008);
265 EXPECT_EQ(results[2], -1);
266 EXPECT_EQ(results[3], 1064);
267 EXPECT_EQ(results[4], 1016);
268}
269
270TEST(multi_function_procedure, Vectors)
271{
283 CreateRangeFunction create_range_fn;
284 ConcatVectorsFunction extend_fn;
286 SumVectorFunction sum_elements_fn;
287 CustomMF_Constant<int> constant_5_fn{5};
288
289 Procedure procedure;
290 ProcedureBuilder builder{procedure};
291
292 Variable *var_v1 = &builder.add_input_parameter(DataType::ForVector<int>());
293 Variable *var_v2 = &builder.add_parameter(ParamType::ForMutableVector(CPPType::get<int>()));
294 builder.add_call(extend_fn, {var_v1, var_v2});
295 auto [var_constant] = builder.add_call<1>(constant_5_fn);
296 builder.add_call(append_fn, {var_v2, var_constant});
297 builder.add_destruct(*var_constant);
298 builder.add_call(extend_fn, {var_v2, var_v1});
299 auto [var_len] = builder.add_call<1>(sum_elements_fn, {var_v2});
300 auto [var_v3] = builder.add_call<1>(create_range_fn, {var_len});
301 builder.add_destruct({var_v1, var_len});
302 builder.add_return();
303 builder.add_output_parameter(*var_v3);
304
305 EXPECT_TRUE(procedure.validate());
306
307 ProcedureExecutor procedure_fn{procedure};
308 IndexMaskMemory memory;
309 const IndexMask mask = IndexMask::from_indices<int>({0, 1, 3, 4}, memory);
310 ParamsBuilder params{procedure_fn, &mask};
311
312 Array<int> v1 = {5, 2, 3};
315
316 int value_10 = 10;
317 v2.append(0, &value_10);
318 v2.append(4, &value_10);
319
320 params.add_readonly_vector_input(v1.as_span());
321 params.add_vector_mutable(v2);
322 params.add_vector_output(v3);
323
324 ContextBuilder context;
325 procedure_fn.call(mask, params, context);
326
327 EXPECT_EQ(v2[0].size(), 6);
328 EXPECT_EQ(v2[1].size(), 4);
329 EXPECT_EQ(v2[2].size(), 0);
330 EXPECT_EQ(v2[3].size(), 4);
331 EXPECT_EQ(v2[4].size(), 6);
332
333 EXPECT_EQ(v3[0].size(), 35);
334 EXPECT_EQ(v3[1].size(), 15);
335 EXPECT_EQ(v3[2].size(), 0);
336 EXPECT_EQ(v3[3].size(), 15);
337 EXPECT_EQ(v3[4].size(), 35);
338}
339
340TEST(multi_function_procedure, BufferReuse)
341{
352 auto add_10_fn = build::SI1_SO<int, int>("add 10", [](int a) { return a + 10; });
353
354 Procedure procedure;
355 ProcedureBuilder builder{procedure};
356
357 Variable *var_a = &builder.add_single_input_parameter<int>();
358 auto [var_b] = builder.add_call<1>(add_10_fn, {var_a});
359 builder.add_destruct(*var_a);
360 auto [var_c] = builder.add_call<1>(add_10_fn, {var_b});
361 builder.add_destruct(*var_b);
362 auto [var_d] = builder.add_call<1>(add_10_fn, {var_c});
363 builder.add_destruct(*var_c);
364 auto [var_e] = builder.add_call<1>(add_10_fn, {var_d});
365 builder.add_destruct(*var_d);
366 auto [var_out] = builder.add_call<1>(add_10_fn, {var_e});
367 builder.add_destruct(*var_e);
368 builder.add_return();
369 builder.add_output_parameter(*var_out);
370
371 EXPECT_TRUE(procedure.validate());
372
373 ProcedureExecutor procedure_fn{procedure};
374
375 Array<int> inputs = {4, 1, 6, 2, 3};
376 Array<int> results(5, -1);
377
378 IndexMaskMemory memory;
379 const IndexMask mask = IndexMask::from_indices<int>({0, 2, 3, 4}, memory);
380 ParamsBuilder params{procedure_fn, &mask};
381
382 params.add_readonly_single_input(inputs.as_span());
383 params.add_uninitialized_single_output(results.as_mutable_span());
384
385 ContextBuilder context;
386 procedure_fn.call(mask, params, context);
387
388 EXPECT_EQ(results[0], 54);
389 EXPECT_EQ(results[1], -1);
390 EXPECT_EQ(results[2], 56);
391 EXPECT_EQ(results[3], 52);
392 EXPECT_EQ(results[4], 53);
393}
394
395TEST(multi_function_procedure, OutputBufferReplaced)
396{
397 Procedure procedure;
398 ProcedureBuilder builder{procedure};
399
400 const int output_value = 42;
401 CustomMF_GenericConstant constant_fn(CPPType::get<int>(), &output_value, false);
402 Variable &var_o = procedure.new_variable(DataType::ForSingle<int>());
403 builder.add_output_parameter(var_o);
404 builder.add_call_with_all_variables(constant_fn, {&var_o});
405 builder.add_destruct(var_o);
406 builder.add_call_with_all_variables(constant_fn, {&var_o});
407 builder.add_return();
408
409 EXPECT_TRUE(procedure.validate());
410
411 ProcedureExecutor procedure_fn{procedure};
412
413 Array<int> output(3, 0);
414 IndexMask mask(output.size());
415 mf::ParamsBuilder params(procedure_fn, &mask);
416 params.add_uninitialized_single_output(output.as_mutable_span());
417
418 mf::ContextBuilder context;
419 procedure_fn.call(mask, params, context);
420
421 EXPECT_EQ(output[0], output_value);
422 EXPECT_EQ(output[1], output_value);
423 EXPECT_EQ(output[2], output_value);
424}
425
426} // namespace blender::fn::multi_function::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define output
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
static const CPPType & get()
static ParamType ForMutableVector(const CPPType &base_type)
Vector< Variable * > add_call(const MultiFunction &fn, Span< Variable * > input_and_mutable_variables={})
Variable & new_variable(DataType data_type, std::string name="")
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 mask(const int4 mask, const float4 a)
auto SI1_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SM(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::AllSpanOrSingle())
auto SI2_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
TEST(multi_function_procedure, ConstantOutput)