Cleanup: Migrate all shader nodes to c++
[blender.git] / source / blender / compositor / tests / COM_BuffersIterator_test.cc
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2021, Blender Foundation.
17  */
18
19 #include "testing/testing.h"
20
21 #include "BLI_array.hh"
22 #include "COM_BuffersIterator.h"
23
24 namespace blender::compositor::tests {
25
26 constexpr int BUFFER_WIDTH = 5;
27 constexpr int BUFFER_HEIGHT = 4;
28 constexpr int BUFFER_OFFSET_X = 5;
29 constexpr int BUFFER_OFFSET_Y = 6;
30 constexpr int NUM_CHANNELS = 4;
31 constexpr int FULL_BUFFER_LEN = BUFFER_WIDTH * BUFFER_HEIGHT * NUM_CHANNELS;
32 constexpr int SINGLE_ELEM_BUFFER_LEN = NUM_CHANNELS;
33 constexpr int NUM_INPUTS = 2;
34
35 static float *create_buffer(int len)
36 {
37   return (float *)MEM_callocN(len * sizeof(float), "COM_BuffersIteratorTest");
38 }
39
40 static const float *create_input_buffer(int input_idx, bool is_a_single_elem)
41 {
42   const int len = is_a_single_elem ? SINGLE_ELEM_BUFFER_LEN : FULL_BUFFER_LEN;
43   float *buf = create_buffer(len);
44   /* Fill buffer with variable data. */
45   for (int i = 0; i < len; i++) {
46     buf[i] = input_idx * 1.5f * (i + 1) + i * 0.9f;
47   }
48   return buf;
49 }
50
51 using IterFunc = std::function<void(BuffersIterator<float> &it, const rcti &area)>;
52 using ValidateElemFunc = std::function<void(float *out, Span<const float *> ins, int x, int y)>;
53
54 class BuffersIteratorTest : public testing::Test {
55  private:
56   float *output_;
57   bool use_offsets_;
58   bool use_single_elem_inputs_;
59   bool use_inputs_;
60
61   static rcti buffer_area;
62   static rcti buffer_offset_area;
63   static Array<const float *, NUM_INPUTS> single_elem_inputs;
64   static Array<const float *, NUM_INPUTS> full_buffer_inputs;
65
66  public:
67   void set_inputs_enabled(bool value)
68   {
69     use_inputs_ = value;
70   }
71
72   void test_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func = {})
73   {
74     use_single_elem_inputs_ = false;
75     validate_iteration(iter_func, validate_elem_func);
76     if (use_inputs_) {
77       use_single_elem_inputs_ = true;
78       validate_iteration(iter_func, validate_elem_func);
79     }
80   }
81
82  protected:
83   static void SetUpTestCase()
84   {
85     BLI_rcti_init(&buffer_area, 0, BUFFER_WIDTH, 0, BUFFER_HEIGHT);
86     BLI_rcti_init(&buffer_offset_area,
87                   BUFFER_OFFSET_X,
88                   BUFFER_OFFSET_X + BUFFER_WIDTH,
89                   BUFFER_OFFSET_Y,
90                   BUFFER_OFFSET_Y + BUFFER_HEIGHT);
91     for (int i = 0; i < NUM_INPUTS; i++) {
92       single_elem_inputs[i] = create_input_buffer(i, true);
93       full_buffer_inputs[i] = create_input_buffer(i, false);
94     }
95   }
96
97   static void TearDownTestCase()
98   {
99     for (int i = 0; i < NUM_INPUTS; i++) {
100       MEM_freeN((void *)single_elem_inputs[i]);
101       single_elem_inputs[i] = nullptr;
102       MEM_freeN((void *)full_buffer_inputs[i]);
103       full_buffer_inputs[i] = nullptr;
104     }
105   }
106
107   void SetUp() override
108   {
109     use_offsets_ = false;
110     use_single_elem_inputs_ = false;
111     use_inputs_ = false;
112     output_ = create_buffer(FULL_BUFFER_LEN);
113   }
114
115   void TearDown() override
116   {
117     MEM_freeN(output_);
118   }
119
120  private:
121   void validate_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func)
122   {
123     {
124       use_offsets_ = false;
125       BuffersIterator<float> it = iterate();
126       iter_func(it, buffer_area);
127       validate_result(buffer_area, validate_elem_func);
128     }
129     {
130       use_offsets_ = true;
131       BuffersIterator<float> it = offset_iterate(buffer_offset_area);
132       iter_func(it, buffer_offset_area);
133       validate_result(buffer_offset_area, validate_elem_func);
134     }
135     {
136       use_offsets_ = true;
137       rcti area = buffer_offset_area;
138       area.xmin += 1;
139       area.ymin += 1;
140       area.xmax -= 1;
141       area.ymax -= 1;
142       BuffersIterator<float> it = offset_iterate(area);
143       iter_func(it, area);
144       validate_result(area, validate_elem_func);
145     }
146   }
147
148   void validate_result(rcti &area, ValidateElemFunc validate_elem_func)
149   {
150     Span<const float *> inputs = get_inputs();
151     Array<const float *> ins(inputs.size());
152     for (int y = area.ymin; y < area.ymax; y++) {
153       for (int x = area.xmin; x < area.xmax; x++) {
154         const int out_offset = get_buffer_relative_y(y) * BUFFER_WIDTH * NUM_CHANNELS +
155                                get_buffer_relative_x(x) * NUM_CHANNELS;
156         float *out = &output_[out_offset];
157
158         const int in_offset = use_single_elem_inputs_ ? 0 : out_offset;
159         for (int i = 0; i < inputs.size(); i++) {
160           ins[i] = &inputs[i][in_offset];
161         }
162
163         if (validate_elem_func) {
164           validate_elem_func(out, ins, x, y);
165         }
166       }
167     }
168   }
169
170   Span<const float *> get_inputs()
171   {
172     if (use_inputs_) {
173       return use_single_elem_inputs_ ? single_elem_inputs : full_buffer_inputs;
174     }
175     return {};
176   }
177
178   int get_buffer_relative_x(int x)
179   {
180     return use_offsets_ ? x - BUFFER_OFFSET_X : x;
181   }
182   int get_buffer_relative_y(int y)
183   {
184     return use_offsets_ ? y - BUFFER_OFFSET_Y : y;
185   }
186
187   /** Iterates whole buffers with no offsets. */
188   BuffersIterator<float> iterate()
189   {
190     BLI_assert(!use_offsets_);
191     BuffersIteratorBuilder<float> builder(output_, BUFFER_WIDTH, BUFFER_HEIGHT, NUM_CHANNELS);
192     if (use_inputs_) {
193       const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
194       for (const float *input : get_inputs()) {
195         builder.add_input(input, BUFFER_WIDTH, input_stride);
196       }
197     }
198     return builder.build();
199   }
200
201   /** Iterates a given buffers area with default offsets. */
202   BuffersIterator<float> offset_iterate(const rcti &area)
203   {
204     BLI_assert(use_offsets_);
205     const rcti &buf_area = buffer_offset_area;
206     BuffersIteratorBuilder<float> builder(output_, buf_area, area, NUM_CHANNELS);
207     if (use_inputs_) {
208       const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
209       for (const float *input : get_inputs()) {
210         builder.add_input(input, buf_area, input_stride);
211       }
212     }
213     return builder.build();
214   }
215 };
216
217 rcti BuffersIteratorTest::buffer_area;
218 rcti BuffersIteratorTest::buffer_offset_area;
219 Array<const float *, NUM_INPUTS> BuffersIteratorTest::single_elem_inputs(NUM_INPUTS);
220 Array<const float *, NUM_INPUTS> BuffersIteratorTest::full_buffer_inputs(NUM_INPUTS);
221
222 static void iterate_coordinates(BuffersIterator<float> &it, const rcti &area)
223 {
224   int x = area.xmin;
225   int y = area.ymin;
226   for (; !it.is_end(); ++it) {
227     EXPECT_EQ(x, it.x);
228     EXPECT_EQ(y, it.y);
229     x++;
230     if (x == area.xmax) {
231       x = area.xmin;
232       y++;
233     }
234   }
235   EXPECT_EQ(x, area.xmin);
236   EXPECT_EQ(y, area.ymax);
237 }
238
239 TEST_F(BuffersIteratorTest, CoordinatesIterationWithNoInputs)
240 {
241   set_inputs_enabled(false);
242   test_iteration(iterate_coordinates);
243 }
244
245 TEST_F(BuffersIteratorTest, CoordinatesIterationWithInputs)
246 {
247   set_inputs_enabled(true);
248   test_iteration(iterate_coordinates);
249 }
250
251 TEST_F(BuffersIteratorTest, OutputIteration)
252 {
253   set_inputs_enabled(false);
254   test_iteration(
255       [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
256         EXPECT_EQ(it.get_num_inputs(), 0);
257         for (; !it.is_end(); ++it) {
258           const int dummy = it.y * BUFFER_WIDTH + it.x;
259           it.out[0] = dummy + 1.0f;
260           it.out[1] = dummy + 2.0f;
261           it.out[2] = dummy + 3.0f;
262           it.out[3] = dummy + 4.0f;
263         }
264       },
265       [](float *out, Span<const float *> UNUSED(ins), const int x, const int y) {
266         const int dummy = y * BUFFER_WIDTH + x;
267         EXPECT_NEAR(out[0], dummy + 1.0f, FLT_EPSILON);
268         EXPECT_NEAR(out[1], dummy + 2.0f, FLT_EPSILON);
269         EXPECT_NEAR(out[2], dummy + 3.0f, FLT_EPSILON);
270         EXPECT_NEAR(out[3], dummy + 4.0f, FLT_EPSILON);
271       });
272 }
273
274 TEST_F(BuffersIteratorTest, OutputAndInputsIteration)
275 {
276   set_inputs_enabled(true);
277   test_iteration(
278       [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
279         EXPECT_EQ(it.get_num_inputs(), NUM_INPUTS);
280         for (; !it.is_end(); ++it) {
281           const float *in1 = it.in(0);
282           const float *in2 = it.in(1);
283           it.out[0] = in1[0] + in2[0];
284           it.out[1] = in1[1] + in2[3];
285           it.out[2] = in1[2] - in2[2];
286           it.out[3] = in1[3] - in2[1];
287         }
288       },
289       [](float *out, Span<const float *> ins, const int UNUSED(x), const int UNUSED(y)) {
290         const float *in1 = ins[0];
291         const float *in2 = ins[1];
292         EXPECT_NEAR(out[0], in1[0] + in2[0], FLT_EPSILON);
293         EXPECT_NEAR(out[1], in1[1] + in2[3], FLT_EPSILON);
294         EXPECT_NEAR(out[2], in1[2] - in2[2], FLT_EPSILON);
295         EXPECT_NEAR(out[3], in1[3] - in2[1], FLT_EPSILON);
296       });
297 }
298
299 }  // namespace blender::compositor::tests