Files
test2/source/blender/compositor/tests/COM_BuffersIterator_test.cc
Sergey Sharybin c1bc70b711 Cleanup: Add a copyright notice to files and use SPDX format
A lot of files were missing copyright field in the header and
the Blender Foundation contributed to them in a sense of bug
fixing and general maintenance.

This change makes it explicit that those files are at least
partially copyrighted by the Blender Foundation.

Note that this does not make it so the Blender Foundation is
the only holder of the copyright in those files, and developers
who do not have a signed contract with the foundation still
hold the copyright as well.

Another aspect of this change is using SPDX format for the
header. We already used it for the license specification,
and now we state it for the copyright as well, following the
FAQ:

    https://reuse.software/faq/
2023-05-31 16:19:06 +02:00

286 lines
8.3 KiB
C++

/* SPDX-FileCopyrightText: 2021 Blender Foundation.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#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 & /*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 *> /*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 & /*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 /*x*/, const int /*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