Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/compositor/tests/COM_BuffersIterator_test.cc')
-rw-r--r--source/blender/compositor/tests/COM_BuffersIterator_test.cc299
1 files changed, 299 insertions, 0 deletions
diff --git a/source/blender/compositor/tests/COM_BuffersIterator_test.cc b/source/blender/compositor/tests/COM_BuffersIterator_test.cc
new file mode 100644
index 00000000000..0a288cdc5f0
--- /dev/null
+++ b/source/blender/compositor/tests/COM_BuffersIterator_test.cc
@@ -0,0 +1,299 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2021, Blender Foundation.
+ */
+
+#include "testing/testing.h"
+
+#include "BLI_array.hh"
+#include "COM_BuffersIterator.h"
+
+namespace blender::compositor::tests {
+
+constexpr int BUFFER_WIDTH = 5;
+constexpr int BUFFER_HEIGHT = 4;
+constexpr int BUFFER_OFFSET_X = 5;
+constexpr int BUFFER_OFFSET_Y = 6;
+constexpr int NUM_CHANNELS = 4;
+constexpr int FULL_BUFFER_LEN = BUFFER_WIDTH * BUFFER_HEIGHT * NUM_CHANNELS;
+constexpr int SINGLE_ELEM_BUFFER_LEN = NUM_CHANNELS;
+constexpr int NUM_INPUTS = 2;
+
+static float *create_buffer(int len)
+{
+ return (float *)MEM_callocN(len * sizeof(float), "COM_BuffersIteratorTest");
+}
+
+static const float *create_input_buffer(int input_idx, bool is_a_single_elem)
+{
+ const int len = is_a_single_elem ? SINGLE_ELEM_BUFFER_LEN : FULL_BUFFER_LEN;
+ float *buf = create_buffer(len);
+ /* Fill buffer with variable data. */
+ for (int i = 0; i < len; i++) {
+ buf[i] = input_idx * 1.5f * (i + 1) + i * 0.9f;
+ }
+ return buf;
+}
+
+using IterFunc = std::function<void(BuffersIterator<float> &it, const rcti &area)>;
+using ValidateElemFunc = std::function<void(float *out, Span<const float *> ins, int x, int y)>;
+
+class BuffersIteratorTest : public testing::Test {
+ private:
+ float *output_;
+ bool use_offsets_;
+ bool use_single_elem_inputs_;
+ bool use_inputs_;
+
+ static rcti buffer_area;
+ static rcti buffer_offset_area;
+ static Array<const float *, NUM_INPUTS> single_elem_inputs;
+ static Array<const float *, NUM_INPUTS> full_buffer_inputs;
+
+ public:
+ void set_inputs_enabled(bool value)
+ {
+ use_inputs_ = value;
+ }
+
+ void test_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func = {})
+ {
+ use_single_elem_inputs_ = false;
+ validate_iteration(iter_func, validate_elem_func);
+ if (use_inputs_) {
+ use_single_elem_inputs_ = true;
+ validate_iteration(iter_func, validate_elem_func);
+ }
+ }
+
+ protected:
+ static void SetUpTestCase()
+ {
+ BLI_rcti_init(&buffer_area, 0, BUFFER_WIDTH, 0, BUFFER_HEIGHT);
+ BLI_rcti_init(&buffer_offset_area,
+ BUFFER_OFFSET_X,
+ BUFFER_OFFSET_X + BUFFER_WIDTH,
+ BUFFER_OFFSET_Y,
+ BUFFER_OFFSET_Y + BUFFER_HEIGHT);
+ for (int i = 0; i < NUM_INPUTS; i++) {
+ single_elem_inputs[i] = create_input_buffer(i, true);
+ full_buffer_inputs[i] = create_input_buffer(i, false);
+ }
+ }
+
+ static void TearDownTestCase()
+ {
+ for (int i = 0; i < NUM_INPUTS; i++) {
+ MEM_freeN((void *)single_elem_inputs[i]);
+ single_elem_inputs[i] = nullptr;
+ MEM_freeN((void *)full_buffer_inputs[i]);
+ full_buffer_inputs[i] = nullptr;
+ }
+ }
+
+ void SetUp() override
+ {
+ use_offsets_ = false;
+ use_single_elem_inputs_ = false;
+ use_inputs_ = false;
+ output_ = create_buffer(FULL_BUFFER_LEN);
+ }
+
+ void TearDown() override
+ {
+ MEM_freeN(output_);
+ }
+
+ private:
+ void validate_iteration(IterFunc iter_func, ValidateElemFunc validate_elem_func)
+ {
+ {
+ use_offsets_ = false;
+ BuffersIterator<float> it = iterate();
+ iter_func(it, buffer_area);
+ validate_result(buffer_area, validate_elem_func);
+ }
+ {
+ use_offsets_ = true;
+ BuffersIterator<float> it = offset_iterate(buffer_offset_area);
+ iter_func(it, buffer_offset_area);
+ validate_result(buffer_offset_area, validate_elem_func);
+ }
+ {
+ use_offsets_ = true;
+ rcti area = buffer_offset_area;
+ area.xmin += 1;
+ area.ymin += 1;
+ area.xmax -= 1;
+ area.ymax -= 1;
+ BuffersIterator<float> it = offset_iterate(area);
+ iter_func(it, area);
+ validate_result(area, validate_elem_func);
+ }
+ }
+
+ void validate_result(rcti &area, ValidateElemFunc validate_elem_func)
+ {
+ Span<const float *> inputs = get_inputs();
+ Array<const float *> ins(inputs.size());
+ for (int y = area.ymin; y < area.ymax; y++) {
+ for (int x = area.xmin; x < area.xmax; x++) {
+ const int out_offset = get_buffer_relative_y(y) * BUFFER_WIDTH * NUM_CHANNELS +
+ get_buffer_relative_x(x) * NUM_CHANNELS;
+ float *out = &output_[out_offset];
+
+ const int in_offset = use_single_elem_inputs_ ? 0 : out_offset;
+ for (int i = 0; i < inputs.size(); i++) {
+ ins[i] = &inputs[i][in_offset];
+ }
+
+ if (validate_elem_func) {
+ validate_elem_func(out, ins, x, y);
+ }
+ }
+ }
+ }
+
+ Span<const float *> get_inputs()
+ {
+ if (use_inputs_) {
+ return use_single_elem_inputs_ ? single_elem_inputs : full_buffer_inputs;
+ }
+ return {};
+ }
+
+ int get_buffer_relative_x(int x)
+ {
+ return use_offsets_ ? x - BUFFER_OFFSET_X : x;
+ }
+ int get_buffer_relative_y(int y)
+ {
+ return use_offsets_ ? y - BUFFER_OFFSET_Y : y;
+ }
+
+ /** Iterates whole buffers with no offsets. */
+ BuffersIterator<float> iterate()
+ {
+ BLI_assert(!use_offsets_);
+ BuffersIteratorBuilder<float> builder(output_, BUFFER_WIDTH, BUFFER_HEIGHT, NUM_CHANNELS);
+ if (use_inputs_) {
+ const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
+ for (const float *input : get_inputs()) {
+ builder.add_input(input, BUFFER_WIDTH, input_stride);
+ }
+ }
+ return builder.build();
+ }
+
+ /** Iterates a given buffers area with default offsets. */
+ BuffersIterator<float> offset_iterate(const rcti &area)
+ {
+ BLI_assert(use_offsets_);
+ const rcti &buf_area = buffer_offset_area;
+ BuffersIteratorBuilder<float> builder(output_, buf_area, area, NUM_CHANNELS);
+ if (use_inputs_) {
+ const int input_stride = use_single_elem_inputs_ ? 0 : NUM_CHANNELS;
+ for (const float *input : get_inputs()) {
+ builder.add_input(input, buf_area, input_stride);
+ }
+ }
+ return builder.build();
+ }
+};
+
+rcti BuffersIteratorTest::buffer_area;
+rcti BuffersIteratorTest::buffer_offset_area;
+Array<const float *, NUM_INPUTS> BuffersIteratorTest::single_elem_inputs(NUM_INPUTS);
+Array<const float *, NUM_INPUTS> BuffersIteratorTest::full_buffer_inputs(NUM_INPUTS);
+
+static void iterate_coordinates(BuffersIterator<float> &it, const rcti &area)
+{
+ int x = area.xmin;
+ int y = area.ymin;
+ for (; !it.is_end(); ++it) {
+ EXPECT_EQ(x, it.x);
+ EXPECT_EQ(y, it.y);
+ x++;
+ if (x == area.xmax) {
+ x = area.xmin;
+ y++;
+ }
+ }
+ EXPECT_EQ(x, area.xmin);
+ EXPECT_EQ(y, area.ymax);
+}
+
+TEST_F(BuffersIteratorTest, CoordinatesIterationWithNoInputs)
+{
+ set_inputs_enabled(false);
+ test_iteration(iterate_coordinates);
+}
+
+TEST_F(BuffersIteratorTest, CoordinatesIterationWithInputs)
+{
+ set_inputs_enabled(true);
+ test_iteration(iterate_coordinates);
+}
+
+TEST_F(BuffersIteratorTest, OutputIteration)
+{
+ set_inputs_enabled(false);
+ test_iteration(
+ [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
+ EXPECT_EQ(it.get_num_inputs(), 0);
+ for (; !it.is_end(); ++it) {
+ const int dummy = it.y * BUFFER_WIDTH + it.x;
+ it.out[0] = dummy + 1.0f;
+ it.out[1] = dummy + 2.0f;
+ it.out[2] = dummy + 3.0f;
+ it.out[3] = dummy + 4.0f;
+ }
+ },
+ [](float *out, Span<const float *> UNUSED(ins), const int x, const int y) {
+ const int dummy = y * BUFFER_WIDTH + x;
+ EXPECT_NEAR(out[0], dummy + 1.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[1], dummy + 2.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[2], dummy + 3.0f, FLT_EPSILON);
+ EXPECT_NEAR(out[3], dummy + 4.0f, FLT_EPSILON);
+ });
+}
+
+TEST_F(BuffersIteratorTest, OutputAndInputsIteration)
+{
+ set_inputs_enabled(true);
+ test_iteration(
+ [](BuffersIterator<float> &it, const rcti &UNUSED(area)) {
+ EXPECT_EQ(it.get_num_inputs(), NUM_INPUTS);
+ for (; !it.is_end(); ++it) {
+ const float *in1 = it.in(0);
+ const float *in2 = it.in(1);
+ it.out[0] = in1[0] + in2[0];
+ it.out[1] = in1[1] + in2[3];
+ it.out[2] = in1[2] - in2[2];
+ it.out[3] = in1[3] - in2[1];
+ }
+ },
+ [](float *out, Span<const float *> ins, const int UNUSED(x), const int UNUSED(y)) {
+ const float *in1 = ins[0];
+ const float *in2 = ins[1];
+ EXPECT_NEAR(out[0], in1[0] + in2[0], FLT_EPSILON);
+ EXPECT_NEAR(out[1], in1[1] + in2[3], FLT_EPSILON);
+ EXPECT_NEAR(out[2], in1[2] - in2[2], FLT_EPSILON);
+ EXPECT_NEAR(out[3], in1[3] - in2[1], FLT_EPSILON);
+ });
+}
+
+} // namespace blender::compositor::tests