1 // Unit tests for DeinterlaceEffect.
4 #include <benchmark/benchmark.h>
11 #include "effect_chain.h"
12 #include "gtest/gtest.h"
13 #include "image_format.h"
15 #include "deinterlace_effect.h"
16 #include "test_util.h"
22 class DeinterlaceTest : public testing::TestWithParam<string> {
24 DeinterlaceTest() : disabler(GetParam() == "fragment") {}
25 bool should_skip() { return disabler.should_skip(); }
28 DisableComputeShadersTemporarily disabler;
31 TEST_P(DeinterlaceTest, ConstantColor) {
32 if (should_skip()) return;
38 float expected_data[] = {
47 EffectChainTester tester(nullptr, 2, 6);
48 Effect *input1 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
49 Effect *input2 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
50 Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
51 Effect *input4 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
52 Effect *input5 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
53 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
55 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
56 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
57 expect_equal(expected_data, out_data, 2, 6);
59 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
60 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
61 expect_equal(expected_data, out_data, 2, 6);
64 // Also tests that top/bottom change works like expected.
65 TEST_P(DeinterlaceTest, VerticalInterpolation) {
66 if (should_skip()) return;
69 float data[width * height] = {
70 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
71 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Differs from previous.
73 float expected_data_top[width * height * 2] = {
74 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Unchanged.
75 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
76 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Unchanged.
77 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Repeated.
79 float expected_data_bottom[width * height * 2] = {
80 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Repeated
81 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Unchanged.
82 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
83 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, // Unchanged.
85 float neg_blowout_data[width * height];
86 float pos_blowout_data[width * height];
87 float out_data[width * height * 2];
89 // Set previous and next fields to something so big that all the temporal checks
90 // are effectively turned off.
91 fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
92 fill(pos_blowout_data, pos_blowout_data + width * height, 100.0f);
94 EffectChainTester tester(nullptr, width, height * 2);
95 Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
96 Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
97 Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
98 Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
99 Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
100 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
102 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
103 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
104 expect_equal(expected_data_top, out_data, width, height * 2);
106 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
107 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
108 expect_equal(expected_data_bottom, out_data, width, height * 2);
111 TEST_P(DeinterlaceTest, DiagonalInterpolation) {
112 if (should_skip()) return;
113 const int width = 11;
114 const int height = 3;
115 float data[width * height] = {
116 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,
117 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, // Offset two pixels, one value modified.
118 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, // Offset four the other way.
121 // Expected degrees are marked in comments. Mostly we want +45 for the second line
122 // and -63 for the fourth, but due to the score being over three neighboring pixels,
123 // sometimes it doesn't work ideally like that.
124 float expected_data_top[width * height * 2] = {
125 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, // Unchanged.
126 // | / / / / / / / / / |
127 // 0 +45 +45 +45 +45 +45 +45 +45 +45 +45 0
128 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
129 // | / / / / / / / / / |
130 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f, // Unchanged.
132 // 0 -45 -63 -63 -63 -63 -63 -63 +63! +63! +63!
133 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.3f, 0.2f,
135 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, // Unchanged.
136 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, // Repeated.
138 float neg_blowout_data[width * height];
139 float pos_blowout_data[width * height];
140 float out_data[width * height * 2];
142 // Set previous and next fields to something so big that all the temporal checks
143 // are effectively turned off.
144 fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
145 fill(pos_blowout_data, pos_blowout_data + width * height, 100.0f);
147 EffectChainTester tester(nullptr, width, height * 2);
148 Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
149 Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
150 Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
151 Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
152 Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
153 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
155 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
156 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
157 expect_equal(expected_data_top, out_data, width, height * 2);
160 TEST_P(DeinterlaceTest, FlickerBox) {
161 if (should_skip()) return;
163 const int height = 4;
164 float white_data[width * height] = {
165 1.0f, 1.0f, 1.0f, 1.0f,
166 1.0f, 1.0f, 1.0f, 1.0f,
167 1.0f, 1.0f, 1.0f, 1.0f,
168 1.0f, 1.0f, 1.0f, 1.0f,
170 float black_data[width * height] = {
171 0.0f, 0.0f, 0.0f, 0.0f,
172 0.0f, 0.0f, 0.0f, 0.0f,
173 0.0f, 0.0f, 0.0f, 0.0f,
174 0.0f, 0.0f, 0.0f, 0.0f,
176 float striped_data[width * height * 2] = {
177 1.0f, 1.0f, 1.0f, 1.0f,
178 0.0f, 0.0f, 0.0f, 0.0f,
179 1.0f, 1.0f, 1.0f, 1.0f,
180 0.0f, 0.0f, 0.0f, 0.0f,
181 1.0f, 1.0f, 1.0f, 1.0f,
182 0.0f, 0.0f, 0.0f, 0.0f,
183 1.0f, 1.0f, 1.0f, 1.0f,
184 0.0f, 0.0f, 0.0f, 0.0f,
186 float out_data[width * height * 2];
189 EffectChainTester tester(nullptr, width, height * 2);
190 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
191 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
192 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
194 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
195 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
196 expect_equal(white_data, out_data, width, height);
197 expect_equal(white_data, out_data + width * height, width, height);
201 EffectChainTester tester(nullptr, width, height * 2);
202 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
203 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
204 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
206 ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
207 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
208 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
209 expect_equal(striped_data, out_data, width, height * 2);
213 INSTANTIATE_TEST_CASE_P(DeinterlaceTest,
215 testing::Values("fragment", "compute"));
217 #ifdef HAVE_BENCHMARK
221 MovitPixelFormat input_format;
222 GLenum output_format;
223 size_t bytes_per_pixel;
225 TestFormat gray_format = { FORMAT_GRAYSCALE, GL_RED, 1 };
226 TestFormat bgra_format = { FORMAT_BGRA_PREMULTIPLIED_ALPHA, GL_BGRA, 4 };
230 void BM_DeinterlaceEffect(benchmark::State &state, TestFormat format, bool spatial_interlacing_check, const std::string &shader_type)
232 DisableComputeShadersTemporarily disabler(shader_type == "fragment");
233 if (disabler.should_skip(&state)) return;
235 unsigned width = state.range(0), height = state.range(1);
236 unsigned field_height = height / 2;
238 unique_ptr<float[]> field1(new float[width * field_height * format.bytes_per_pixel]);
239 unique_ptr<float[]> field2(new float[width * field_height * format.bytes_per_pixel]);
240 unique_ptr<float[]> field3(new float[width * field_height * format.bytes_per_pixel]);
241 unique_ptr<float[]> field4(new float[width * field_height * format.bytes_per_pixel]);
242 unique_ptr<float[]> field5(new float[width * field_height * format.bytes_per_pixel]);
243 unique_ptr<float[]> out_data(new float[width * height * format.bytes_per_pixel]);
245 for (unsigned i = 0; i < width * field_height * format.bytes_per_pixel; ++i) {
246 field1[i] = rand() / (RAND_MAX + 1.0);
247 field2[i] = rand() / (RAND_MAX + 1.0);
248 field3[i] = rand() / (RAND_MAX + 1.0);
249 field4[i] = rand() / (RAND_MAX + 1.0);
250 field5[i] = rand() / (RAND_MAX + 1.0);
253 EffectChainTester tester(nullptr, width, height);
254 Effect *input1 = tester.add_input(field1.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
255 Effect *input2 = tester.add_input(field2.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
256 Effect *input3 = tester.add_input(field3.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
257 Effect *input4 = tester.add_input(field4.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
258 Effect *input5 = tester.add_input(field5.get(), format.input_format, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
259 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
261 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
262 ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", spatial_interlacing_check));
264 tester.benchmark(state, out_data.get(), format.output_format, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
266 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, Gray, gray_format, true, "fragment")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
267 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRA, bgra_format, true, "fragment")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
268 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRANoSpatialCheck, bgra_format, false, "fragment")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
269 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, GrayCompute, gray_format, true, "compute")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
270 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRACompute, bgra_format, true, "compute")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
271 BENCHMARK_CAPTURE(BM_DeinterlaceEffect, BGRANoSpatialCheckCompute, bgra_format, false, "compute")->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);