Add support for microbenchmarks.
[movit] / deinterlace_effect_test.cpp
1 // Unit tests for DeinterlaceEffect.
2
3 #ifdef HAVE_BENCHMARK
4 #include <benchmark/benchmark.h>
5 #endif
6 #include <epoxy/gl.h>
7
8 #include <algorithm>
9
10 #include "effect_chain.h"
11 #include "gtest/gtest.h"
12 #include "image_format.h"
13 #include "input.h"
14 #include "deinterlace_effect.h"
15 #include "test_util.h"
16
17 using namespace std;
18
19 namespace movit {
20
21 TEST(DeinterlaceTest, ConstantColor) {
22         float data[] = {
23                 0.3f, 0.3f,
24                 0.3f, 0.3f,
25                 0.3f, 0.3f,
26         };
27         float expected_data[] = {
28                 0.3f, 0.3f,
29                 0.3f, 0.3f,
30                 0.3f, 0.3f,
31                 0.3f, 0.3f,
32                 0.3f, 0.3f,
33                 0.3f, 0.3f,
34         };
35         float out_data[12];
36         EffectChainTester tester(NULL, 2, 6);
37         Effect *input1 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
38         Effect *input2 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
39         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
40         Effect *input4 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
41         Effect *input5 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, 2, 3);
42         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
43
44         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
45         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
46         expect_equal(expected_data, out_data, 2, 6);
47
48         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
49         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
50         expect_equal(expected_data, out_data, 2, 6);
51 }
52
53 // Also tests that top/bottom change works like expected.
54 TEST(DeinterlaceTest, VerticalInterpolation) {
55         const int width = 11;
56         const int height = 2;
57         float data[width * height] = {
58                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 
59                 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.
60         };
61         float expected_data_top[width * height * 2] = {
62                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
63                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
64                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
65                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated.
66         };
67         float expected_data_bottom[width * height * 2] = {
68                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Repeated
69                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,   // Unchanged.
70                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 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,   // Unchanged.
72         };
73         float neg_blowout_data[width * height];
74         float pos_blowout_data[width * height];
75         float out_data[width * height * 2];
76
77         // Set previous and next fields to something so big that all the temporal checks
78         // are effectively turned off.
79         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
80         fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
81
82         EffectChainTester tester(NULL, width, height * 2);
83         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
84         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
85         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
86         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
87         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
88         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
89
90         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
91         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
92         expect_equal(expected_data_top, out_data, width, height * 2);
93
94         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 1));
95         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
96         expect_equal(expected_data_bottom, out_data, width, height * 2);
97 }
98
99 TEST(DeinterlaceTest, DiagonalInterpolation) {
100         const int width = 11;
101         const int height = 3;
102         float data[width * height] = {
103                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,
104                 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.
105                 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.
106         };
107
108         // Expected degrees are marked in comments. Mostly we want +45 for the second line
109         // and -63 for the fourth, but due to the score being over three neighboring pixels,
110         // sometimes it doesn't work ideally like that.
111         float expected_data_top[width * height * 2] = {
112                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.2f, 0.6f, 0.8f, 0.0f, 0.0f,   // Unchanged.
113                 // |    /     /     /     /     /     /     /     /     /    |
114                 // 0  +45   +45   +45   +45   +45   +45   +45   +45   +45    0
115                 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.3f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f,
116                 // | /     /     /     /     /     /     /     /     /       |  
117                 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f, 0.0f, 0.0f, 0.0f, 0.0f,   // Unchanged.
118
119                 // 0  -45   -63   -63   -63   -63   -63   -63   +63!  +63!  +63!
120                 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.3f, 0.2f,
121
122                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Unchanged.
123                 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.4f, 0.6f, 0.4f, 0.6f, 0.8f,   // Repeated.
124         };
125         float neg_blowout_data[width * height];
126         float pos_blowout_data[width * height];
127         float out_data[width * height * 2];
128
129         // Set previous and next fields to something so big that all the temporal checks
130         // are effectively turned off.
131         fill(neg_blowout_data, neg_blowout_data + width * height, -100.0f);
132         fill(pos_blowout_data, pos_blowout_data + width * height,  100.0f);
133
134         EffectChainTester tester(NULL, width, height * 2);
135         Effect *input1 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
136         Effect *input2 = tester.add_input(neg_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
137         Effect *input3 = tester.add_input(data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
138         Effect *input4 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
139         Effect *input5 = tester.add_input(pos_blowout_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
140         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
141
142         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
143         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
144         expect_equal(expected_data_top, out_data, width, height * 2);
145 }
146
147 TEST(DeinterlaceTest, FlickerBox) {
148         const int width = 4;
149         const int height = 4;
150         float white_data[width * height] = {
151                 1.0f, 1.0f, 1.0f, 1.0f,
152                 1.0f, 1.0f, 1.0f, 1.0f,
153                 1.0f, 1.0f, 1.0f, 1.0f,
154                 1.0f, 1.0f, 1.0f, 1.0f,
155         };
156         float black_data[width * height] = {
157                 0.0f, 0.0f, 0.0f, 0.0f,
158                 0.0f, 0.0f, 0.0f, 0.0f,
159                 0.0f, 0.0f, 0.0f, 0.0f,
160                 0.0f, 0.0f, 0.0f, 0.0f,
161         };
162         float striped_data[width * height * 2] = {
163                 1.0f, 1.0f, 1.0f, 1.0f,
164                 0.0f, 0.0f, 0.0f, 0.0f,
165                 1.0f, 1.0f, 1.0f, 1.0f,
166                 0.0f, 0.0f, 0.0f, 0.0f,
167                 1.0f, 1.0f, 1.0f, 1.0f,
168                 0.0f, 0.0f, 0.0f, 0.0f,
169                 1.0f, 1.0f, 1.0f, 1.0f,
170                 0.0f, 0.0f, 0.0f, 0.0f,
171         };
172         float out_data[width * height * 2];
173
174         {
175                 EffectChainTester tester(NULL, width, height * 2);
176                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
177                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
178                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
179
180                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
181                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
182                 expect_equal(white_data, out_data, width, height);
183                 expect_equal(white_data, out_data + width * height, width, height);
184         }
185
186         {
187                 EffectChainTester tester(NULL, width, height * 2);
188                 Effect *white_input = tester.add_input(white_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
189                 Effect *black_input = tester.add_input(black_data, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, height);
190                 Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), white_input, black_input, white_input, black_input, white_input);
191
192                 ASSERT_TRUE(deinterlace_effect->set_int("enable_spatial_interlacing_check", 0));
193                 ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
194                 tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
195                 expect_equal(striped_data, out_data, width, height * 2);
196         }
197 }
198
199 #ifdef HAVE_BENCHMARK
200 void BM_DeinterlaceEffect_Gray(benchmark::State &state)
201 {
202         unsigned width = state.range(0), height = state.range(1);
203         unsigned field_height = height / 2;
204
205         float *field1 = new float[width * field_height];
206         float *field2 = new float[width * field_height];
207         float *field3 = new float[width * field_height];
208         float *field4 = new float[width * field_height];
209         float *field5 = new float[width * field_height];
210         float *out_data = new float[width * height];
211
212         for (unsigned i = 0; i < width * field_height; ++i) {
213                 field1[i] = rand();
214                 field2[i] = rand();
215                 field3[i] = rand();
216                 field4[i] = rand();
217                 field5[i] = rand();
218         }
219
220         EffectChainTester tester(NULL, width, height);
221         Effect *input1 = tester.add_input(field1, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
222         Effect *input2 = tester.add_input(field2, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
223         Effect *input3 = tester.add_input(field3, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
224         Effect *input4 = tester.add_input(field4, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
225         Effect *input5 = tester.add_input(field5, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR, width, field_height);
226         Effect *deinterlace_effect = tester.get_chain()->add_effect(new DeinterlaceEffect(), input1, input2, input3, input4, input5);
227
228         ASSERT_TRUE(deinterlace_effect->set_int("current_field_position", 0));
229
230         tester.benchmark(state, out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR, OUTPUT_ALPHA_FORMAT_PREMULTIPLIED);
231
232         delete[] field1;
233         delete[] field2;
234         delete[] field3;
235         delete[] field4;
236         delete[] field5;
237         delete[] out_data;
238 }
239 BENCHMARK(BM_DeinterlaceEffect_Gray)->Args({720, 576})->Args({1280, 720})->Args({1920, 1080})->UseRealTime()->Unit(benchmark::kMicrosecond);
240 #endif
241
242 }  // namespace movit