Blender V4.3
COM_BuffersIterator_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "testing/testing.h"
6
7#include "BLI_array.hh"
9
11
12constexpr int BUFFER_WIDTH = 5;
13constexpr int BUFFER_HEIGHT = 4;
14constexpr int BUFFER_OFFSET_X = 5;
15constexpr int BUFFER_OFFSET_Y = 6;
16constexpr int NUM_CHANNELS = 4;
19constexpr int NUM_INPUTS = 2;
20
21static float *create_buffer(int len)
22{
23 return (float *)MEM_callocN(len * sizeof(float), "COM_BuffersIteratorTest");
24}
25
26static const float *create_input_buffer(int input_idx, bool is_a_single_elem)
27{
28 const int len = is_a_single_elem ? SINGLE_ELEM_BUFFER_LEN : FULL_BUFFER_LEN;
29 float *buf = create_buffer(len);
30 /* Fill buffer with variable data. */
31 for (int i = 0; i < len; i++) {
32 buf[i] = input_idx * 1.5f * (i + 1) + i * 0.9f;
33 }
34 return buf;
35}
36
37using IterFunc = std::function<void(BuffersIterator<float> &it, const rcti &area)>;
38using ValidateElemFunc = std::function<void(float *out, Span<const float *> ins, int x, int y)>;
39
40class BuffersIteratorTest : public testing::Test {
41 private:
42 float *output_;
43 bool use_offsets_;
44 bool use_single_elem_inputs_;
45 bool use_inputs_;
46
47 static rcti buffer_area;
48 static rcti buffer_offset_area;
49 static Array<const float *, NUM_INPUTS> single_elem_inputs;
50 static Array<const float *, NUM_INPUTS> full_buffer_inputs;
51
52 public:
53 void set_inputs_enabled(bool value)
54 {
55 use_inputs_ = value;
56 }
57
58 void test_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func = {})
59 {
60 use_single_elem_inputs_ = false;
61 validate_iteration(iter_func, validate_elem_func);
62 if (use_inputs_) {
63 use_single_elem_inputs_ = true;
64 validate_iteration(iter_func, validate_elem_func);
65 }
66 }
67
68 protected:
69 static void SetUpTestCase()
70 {
71 BLI_rcti_init(&buffer_area, 0, BUFFER_WIDTH, 0, BUFFER_HEIGHT);
72 BLI_rcti_init(&buffer_offset_area,
77 for (int i = 0; i < NUM_INPUTS; i++) {
78 single_elem_inputs[i] = create_input_buffer(i, true);
79 full_buffer_inputs[i] = create_input_buffer(i, false);
80 }
81 }
82
83 static void TearDownTestCase()
84 {
85 for (int i = 0; i < NUM_INPUTS; i++) {
86 MEM_freeN((void *)single_elem_inputs[i]);
87 single_elem_inputs[i] = nullptr;
88 MEM_freeN((void *)full_buffer_inputs[i]);
89 full_buffer_inputs[i] = nullptr;
90 }
91 }
92
93 void SetUp() override
94 {
95 use_offsets_ = false;
96 use_single_elem_inputs_ = false;
97 use_inputs_ = false;
99 }
100
101 void TearDown() override
102 {
103 MEM_freeN(output_);
104 }
105
106 private:
107 void validate_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func)
108 {
109 {
110 use_offsets_ = false;
111 BuffersIterator<float> it = iterate();
112 iter_func(it, buffer_area);
113 validate_result(buffer_area, validate_elem_func);
114 }
115 {
116 use_offsets_ = true;
117 BuffersIterator<float> it = offset_iterate(buffer_offset_area);
118 iter_func(it, buffer_offset_area);
119 validate_result(buffer_offset_area, validate_elem_func);
120 }
121 {
122 use_offsets_ = true;
123 rcti area = buffer_offset_area;
124 area.xmin += 1;
125 area.ymin += 1;
126 area.xmax -= 1;
127 area.ymax -= 1;
128 BuffersIterator<float> it = offset_iterate(area);
129 iter_func(it, area);
130 validate_result(area, validate_elem_func);
131 }
132 }
133
134 void validate_result(rcti &area, ValidateElemFunc validate_elem_func)
135 {
136 Span<const float *> inputs = get_inputs();
137 Array<const float *> ins(inputs.size());
138 for (int y = area.ymin; y < area.ymax; y++) {
139 for (int x = area.xmin; x < area.xmax; x++) {
140 const int out_offset = get_buffer_relative_y(y) * BUFFER_WIDTH * NUM_CHANNELS +
141 get_buffer_relative_x(x) * NUM_CHANNELS;
142 float *out = &output_[out_offset];
143
144 const int in_offset = use_single_elem_inputs_ ? 0 : out_offset;
145 for (int i = 0; i < inputs.size(); i++) {
146 ins[i] = &inputs[i][in_offset];
147 }
148
149 if (validate_elem_func) {
150 validate_elem_func(out, ins, x, y);
151 }
152 }
153 }
154 }
155
156 Span<const float *> get_inputs()
157 {
158 if (use_inputs_) {
159 return use_single_elem_inputs_ ? single_elem_inputs : full_buffer_inputs;
160 }
161 return {};
162 }
163
164 int get_buffer_relative_x(int x)
165 {
166 return use_offsets_ ? x - BUFFER_OFFSET_X : x;
167 }
168 int get_buffer_relative_y(int y)
169 {
170 return use_offsets_ ? y - BUFFER_OFFSET_Y : y;
171 }
172
174 BuffersIterator<float> iterate()
175 {
176 BLI_assert(!use_offsets_);
177 BuffersIteratorBuilder<float> builder(output_, BUFFER_WIDTH, BUFFER_HEIGHT, NUM_CHANNELS);
178 if (use_inputs_) {
179 const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
180 for (const float *input : get_inputs()) {
181 builder.add_input(input, BUFFER_WIDTH, input_stride);
182 }
183 }
184 return builder.build();
185 }
186
188 BuffersIterator<float> offset_iterate(const rcti &area)
189 {
190 BLI_assert(use_offsets_);
191 const rcti &buf_area = buffer_offset_area;
192 BuffersIteratorBuilder<float> builder(output_, buf_area, area, NUM_CHANNELS);
193 if (use_inputs_) {
194 const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
195 for (const float *input : get_inputs()) {
196 builder.add_input(input, buf_area, input_stride);
197 }
198 }
199 return builder.build();
200 }
201};
202
203rcti BuffersIteratorTest::buffer_area;
204rcti BuffersIteratorTest::buffer_offset_area;
205Array<const float *, NUM_INPUTS> BuffersIteratorTest::single_elem_inputs(NUM_INPUTS);
206Array<const float *, NUM_INPUTS> BuffersIteratorTest::full_buffer_inputs(NUM_INPUTS);
207
209{
210 int x = area.xmin;
211 int y = area.ymin;
212 for (; !it.is_end(); ++it) {
213 EXPECT_EQ(x, it.x);
214 EXPECT_EQ(y, it.y);
215 x++;
216 if (x == area.xmax) {
217 x = area.xmin;
218 y++;
219 }
220 }
221 EXPECT_EQ(x, area.xmin);
222 EXPECT_EQ(y, area.ymax);
223}
224
225TEST_F(BuffersIteratorTest, CoordinatesIterationWithNoInputs)
226{
227 set_inputs_enabled(false);
228 test_iteration(iterate_coordinates);
229}
230
231TEST_F(BuffersIteratorTest, CoordinatesIterationWithInputs)
232{
233 set_inputs_enabled(true);
234 test_iteration(iterate_coordinates);
235}
236
238{
239 set_inputs_enabled(false);
240 test_iteration(
241 [](BuffersIterator<float> &it, const rcti & /*area*/) {
242 EXPECT_EQ(it.get_num_inputs(), 0);
243 for (; !it.is_end(); ++it) {
244 const int dummy = it.y * BUFFER_WIDTH + it.x;
245 it.out[0] = dummy + 1.0f;
246 it.out[1] = dummy + 2.0f;
247 it.out[2] = dummy + 3.0f;
248 it.out[3] = dummy + 4.0f;
249 }
250 },
251 [](float *out, Span<const float *> /*ins*/, const int x, const int y) {
252 const int dummy = y * BUFFER_WIDTH + x;
253 EXPECT_NEAR(out[0], dummy + 1.0f, FLT_EPSILON);
254 EXPECT_NEAR(out[1], dummy + 2.0f, FLT_EPSILON);
255 EXPECT_NEAR(out[2], dummy + 3.0f, FLT_EPSILON);
256 EXPECT_NEAR(out[3], dummy + 4.0f, FLT_EPSILON);
257 });
258}
259
260TEST_F(BuffersIteratorTest, OutputAndInputsIteration)
261{
262 set_inputs_enabled(true);
263 test_iteration(
264 [](BuffersIterator<float> &it, const rcti & /*area*/) {
265 EXPECT_EQ(it.get_num_inputs(), NUM_INPUTS);
266 for (; !it.is_end(); ++it) {
267 const float *in1 = it.in(0);
268 const float *in2 = it.in(1);
269 it.out[0] = in1[0] + in2[0];
270 it.out[1] = in1[1] + in2[3];
271 it.out[2] = in1[2] - in2[2];
272 it.out[3] = in1[3] - in2[1];
273 }
274 },
275 [](float *out, Span<const float *> ins, const int /*x*/, const int /*y*/) {
276 const float *in1 = ins[0];
277 const float *in2 = ins[1];
278 EXPECT_NEAR(out[0], in1[0] + in2[0], FLT_EPSILON);
279 EXPECT_NEAR(out[1], in1[1] + in2[3], FLT_EPSILON);
280 EXPECT_NEAR(out[2], in1[2] - in2[2], FLT_EPSILON);
281 EXPECT_NEAR(out[3], in1[3] - in2[1], FLT_EPSILON);
282 });
283}
284
285} // namespace blender::compositor::tests
#define BLI_assert(a)
Definition BLI_assert.h:50
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.c:418
void test_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func={})
int len
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
static float * create_buffer(int len)
std::function< void(BuffersIterator< float > &it, const rcti &area)> IterFunc
std::function< void(float *out, Span< const float * > ins, int x, int y)> ValidateElemFunc
TEST_F(BuffersIteratorTest, CoordinatesIterationWithNoInputs)
static void iterate_coordinates(BuffersIterator< float > &it, const rcti &area)
static const float * create_input_buffer(int input_idx, bool is_a_single_elem)
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
int xmin