Blender V4.3
FN_multi_function_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
10
12namespace {
13
14class AddFunction : public MultiFunction {
15 public:
16 AddFunction()
17 {
18 static Signature signature = []() {
19 Signature signature;
20 SignatureBuilder builder("Add", signature);
21 builder.single_input<int>("A");
22 builder.single_input<int>("B");
23 builder.single_output<int>("Result");
24 return signature;
25 }();
26 this->set_signature(&signature);
27 }
28
29 void call(const IndexMask &mask, Params params, Context /*context*/) const override
30 {
31 const VArray<int> &a = params.readonly_single_input<int>(0, "A");
32 const VArray<int> &b = params.readonly_single_input<int>(1, "B");
33 MutableSpan<int> result = params.uninitialized_single_output<int>(2, "Result");
34
35 mask.foreach_index([&](const int64_t i) { result[i] = a[i] + b[i]; });
36 }
37};
38
39TEST(multi_function, AddFunction)
40{
41 AddFunction fn;
42
43 Array<int> input1 = {4, 5, 6};
44 Array<int> input2 = {10, 20, 30};
45 Array<int> output(3, -1);
46
47 IndexMaskMemory memory;
48 const IndexMask mask = IndexMask::from_indices<int>({0, 2}, memory);
49 ParamsBuilder params(fn, &mask);
50 params.add_readonly_single_input(input1.as_span());
51 params.add_readonly_single_input(input2.as_span());
52 params.add_uninitialized_single_output(output.as_mutable_span());
53
54 ContextBuilder context;
55
56 fn.call(mask, params, context);
57
58 EXPECT_EQ(output[0], 14);
59 EXPECT_EQ(output[1], -1);
60 EXPECT_EQ(output[2], 36);
61}
62
63TEST(multi_function, AddPrefixFunction)
64{
65 AddPrefixFunction fn;
66
67 Array<std::string> strings = {
68 "Hello",
69 "World",
70 "This is a test",
71 "Another much longer string to trigger an allocation",
72 };
73
74 std::string prefix = "AB";
75
76 IndexMaskMemory memory;
77 const IndexMask mask = IndexMask::from_indices<int>({0, 2, 3}, memory);
78 ParamsBuilder params(fn, &mask);
79 params.add_readonly_single_input(&prefix);
80 params.add_single_mutable(strings.as_mutable_span());
81
82 ContextBuilder context;
83
84 fn.call(mask, params, context);
85
86 EXPECT_EQ(strings[0], "ABHello");
87 EXPECT_EQ(strings[1], "World");
88 EXPECT_EQ(strings[2], "ABThis is a test");
89 EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
90}
91
92TEST(multi_function, CreateRangeFunction)
93{
94 CreateRangeFunction fn;
95
96 GVectorArray ranges(CPPType::get<int>(), 5);
97 GVectorArray_TypedMutableRef<int> ranges_ref{ranges};
98 Array<int> sizes = {3, 0, 6, 1, 4};
99
100 IndexMaskMemory memory;
101 const IndexMask mask = IndexMask::from_indices<int>({0, 1, 2, 3}, memory);
102 ParamsBuilder params(fn, &mask);
103 params.add_readonly_single_input(sizes.as_span());
104 params.add_vector_output(ranges);
105
106 ContextBuilder context;
107
108 fn.call(mask, params, context);
109
110 EXPECT_EQ(ranges[0].size(), 3);
111 EXPECT_EQ(ranges[1].size(), 0);
112 EXPECT_EQ(ranges[2].size(), 6);
113 EXPECT_EQ(ranges[3].size(), 1);
114 EXPECT_EQ(ranges[4].size(), 0);
115
116 EXPECT_EQ(ranges_ref[0][0], 0);
117 EXPECT_EQ(ranges_ref[0][1], 1);
118 EXPECT_EQ(ranges_ref[0][2], 2);
119 EXPECT_EQ(ranges_ref[2][0], 0);
120 EXPECT_EQ(ranges_ref[2][1], 1);
121}
122
123TEST(multi_function, GenericAppendFunction)
124{
125 GenericAppendFunction fn(CPPType::get<int32_t>());
126
127 GVectorArray vectors(CPPType::get<int32_t>(), 4);
128 GVectorArray_TypedMutableRef<int> vectors_ref{vectors};
129 vectors_ref.append(0, 1);
130 vectors_ref.append(0, 2);
131 vectors_ref.append(2, 6);
132 Array<int> values = {5, 7, 3, 1};
133
134 const IndexMask mask(IndexRange(vectors.size()));
135 ParamsBuilder params(fn, &mask);
136 params.add_vector_mutable(vectors);
137 params.add_readonly_single_input(values.as_span());
138
139 ContextBuilder context;
140
141 fn.call(mask, params, context);
142
143 EXPECT_EQ(vectors[0].size(), 3);
144 EXPECT_EQ(vectors[1].size(), 1);
145 EXPECT_EQ(vectors[2].size(), 2);
146 EXPECT_EQ(vectors[3].size(), 1);
147
148 EXPECT_EQ(vectors_ref[0][0], 1);
149 EXPECT_EQ(vectors_ref[0][1], 2);
150 EXPECT_EQ(vectors_ref[0][2], 5);
151 EXPECT_EQ(vectors_ref[1][0], 7);
152 EXPECT_EQ(vectors_ref[2][0], 6);
153 EXPECT_EQ(vectors_ref[2][1], 3);
154 EXPECT_EQ(vectors_ref[3][0], 1);
155}
156
157TEST(multi_function, CustomMF_Constant)
158{
159 CustomMF_Constant<int> fn{42};
160
161 Array<int> outputs(4, 0);
162
163 IndexMaskMemory memory;
164 const IndexMask mask = IndexMask::from_indices<int>({0, 2, 3}, memory);
165 ParamsBuilder params(fn, &mask);
166 params.add_uninitialized_single_output(outputs.as_mutable_span());
167
168 ContextBuilder context;
169
170 fn.call(mask, params, context);
171
172 EXPECT_EQ(outputs[0], 42);
173 EXPECT_EQ(outputs[1], 0);
174 EXPECT_EQ(outputs[2], 42);
175 EXPECT_EQ(outputs[3], 42);
176}
177
178TEST(multi_function, CustomMF_GenericConstant)
179{
180 int value = 42;
181 CustomMF_GenericConstant fn{CPPType::get<int32_t>(), (const void *)&value, false};
182
183 Array<int> outputs(4, 0);
184
185 IndexMaskMemory memory;
186 const IndexMask mask = IndexMask::from_indices<int>({0, 1, 2}, memory);
187 ParamsBuilder params(fn, &mask);
188 params.add_uninitialized_single_output(outputs.as_mutable_span());
189
190 ContextBuilder context;
191
192 fn.call(mask, params, context);
193
194 EXPECT_EQ(outputs[0], 42);
195 EXPECT_EQ(outputs[1], 42);
196 EXPECT_EQ(outputs[2], 42);
197 EXPECT_EQ(outputs[3], 0);
198}
199
200TEST(multi_function, CustomMF_GenericConstantArray)
201{
202 std::array<int, 4> values = {3, 4, 5, 6};
203 CustomMF_GenericConstantArray fn{GSpan(Span(values))};
204
205 GVectorArray vector_array{CPPType::get<int32_t>(), 4};
206 GVectorArray_TypedMutableRef<int> vector_array_ref{vector_array};
207
208 IndexMaskMemory memory;
209 const IndexMask mask = IndexMask::from_indices<int>({1, 2, 3}, memory);
210 ParamsBuilder params(fn, &mask);
211 params.add_vector_output(vector_array);
212
213 ContextBuilder context;
214
215 fn.call(mask, params, context);
216
217 EXPECT_EQ(vector_array[0].size(), 0);
218 EXPECT_EQ(vector_array[1].size(), 4);
219 EXPECT_EQ(vector_array[2].size(), 4);
220 EXPECT_EQ(vector_array[3].size(), 4);
221 for (int i = 1; i < 4; i++) {
222 EXPECT_EQ(vector_array_ref[i][0], 3);
223 EXPECT_EQ(vector_array_ref[i][1], 4);
224 EXPECT_EQ(vector_array_ref[i][2], 5);
225 EXPECT_EQ(vector_array_ref[i][3], 6);
226 }
227}
228
229TEST(multi_function, IgnoredOutputs)
230{
231 OptionalOutputsFunction fn;
232 {
233 const IndexMask mask(10);
234 ParamsBuilder params(fn, &mask);
235 params.add_ignored_single_output("Out 1");
236 params.add_ignored_single_output("Out 2");
237 ContextBuilder context;
238 fn.call(mask, params, context);
239 }
240 {
241 Array<int> results_1(10);
242 Array<std::string> results_2(10, NoInitialization());
243 const IndexMask mask(10);
244
245 ParamsBuilder params(fn, &mask);
246 params.add_uninitialized_single_output(results_1.as_mutable_span(), "Out 1");
247 params.add_uninitialized_single_output(results_2.as_mutable_span(), "Out 2");
248 ContextBuilder context;
249 fn.call(mask, params, context);
250
251 EXPECT_EQ(results_1[0], 5);
252 EXPECT_EQ(results_1[3], 5);
253 EXPECT_EQ(results_1[9], 5);
254 EXPECT_EQ(results_2[0], "hello, this is a long string");
255 }
256}
257
258} // namespace
259} // namespace blender::fn::multi_function::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define output
static const CPPType & get()
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)
TEST(multi_function_procedure, ConstantOutput)
static blender::bke::bNodeSocketTemplate outputs[]
__int64 int64_t
Definition stdint.h:89