]> git.sesse.net Git - movit/blob - flat_input_test.cpp
Fix an issue where we'd add an unneeded bounce for mipmaps in some cases.
[movit] / flat_input_test.cpp
1 // Unit tests for FlatInput.
2
3 #include <epoxy/gl.h>
4 #include <stddef.h>
5
6 #include "effect_chain.h"
7 #include "flat_input.h"
8 #include "gtest/gtest.h"
9 #include "resource_pool.h"
10 #include "test_util.h"
11 #include "util.h"
12
13 using namespace std;
14
15 namespace movit {
16
17 TEST(FlatInput, SimpleGrayscale) {
18         const int size = 4;
19
20         float data[size] = {
21                 0.0,
22                 0.5,
23                 0.7,
24                 1.0,
25         };
26         float expected_data[4 * size] = {
27                 0.0, 0.0, 0.0, 1.0,
28                 0.5, 0.5, 0.5, 1.0,
29                 0.7, 0.7, 0.7, 1.0,
30                 1.0, 1.0, 1.0, 1.0,
31         };
32         float out_data[4 * size];
33
34         EffectChainTester tester(data, 1, size, FORMAT_GRAYSCALE, COLORSPACE_sRGB, GAMMA_LINEAR);
35         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
36
37         expect_equal(expected_data, out_data, 4, size);
38 }
39
40 TEST(FlatInput, RGB) {
41         const int size = 5;
42
43         float data[3 * size] = {
44                 0.0, 0.0, 0.0,
45                 0.5, 0.0, 0.0,
46                 0.0, 0.5, 0.0,
47                 0.0, 0.0, 0.7,
48                 0.0, 0.3, 0.7,
49         };
50         float expected_data[4 * size] = {
51                 0.0, 0.0, 0.0, 1.0,
52                 0.5, 0.0, 0.0, 1.0,
53                 0.0, 0.5, 0.0, 1.0,
54                 0.0, 0.0, 0.7, 1.0,
55                 0.0, 0.3, 0.7, 1.0,
56         };
57         float out_data[4 * size];
58
59         EffectChainTester tester(data, 1, size, FORMAT_RGB, COLORSPACE_sRGB, GAMMA_LINEAR);
60         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
61
62         expect_equal(expected_data, out_data, 4, size);
63 }
64
65 TEST(FlatInput, RGBA) {
66         const int size = 5;
67
68         float data[4 * size] = {
69                 0.0, 0.0, 0.0, 1.0,
70                 0.5, 0.0, 0.0, 0.3,
71                 0.0, 0.5, 0.0, 0.7,
72                 0.0, 0.0, 0.7, 1.0,
73                 0.0, 0.3, 0.7, 0.2,
74         };
75         float expected_data[4 * size] = {
76                 0.0, 0.0, 0.0, 1.0,
77                 0.5, 0.0, 0.0, 0.3,
78                 0.0, 0.5, 0.0, 0.7,
79                 0.0, 0.0, 0.7, 1.0,
80                 0.0, 0.3, 0.7, 0.2,
81         };
82         float out_data[4 * size];
83
84         EffectChainTester tester(data, 1, size, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
85         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
86
87         expect_equal(expected_data, out_data, 4, size);
88 }
89
90 // Note: The sRGB conversion itself is tested in EffectChainTester,
91 // since it also wants to test the chain building itself.
92 // Here, we merely test that alpha is left alone; the test will usually
93 // run using the sRGB OpenGL extension, but might be run with a
94 // GammaExpansionEffect if the card/driver happens not to support that.
95 TEST(FlatInput, AlphaIsNotModifiedBySRGBConversion) {
96         const int size = 5;
97
98         unsigned char data[4 * size] = {
99                 0, 0, 0, 0,
100                 0, 0, 0, 63,
101                 0, 0, 0, 127,
102                 0, 0, 0, 191,
103                 0, 0, 0, 255,
104         };
105         float expected_data[4 * size] = {
106                 0, 0, 0, 0.0 / 255.0,
107                 0, 0, 0, 63.0 / 255.0,
108                 0, 0, 0, 127.0 / 255.0,
109                 0, 0, 0, 191.0 / 255.0,
110                 0, 0, 0, 255.0 / 255.0,
111         };
112         float out_data[4 * size];
113
114         EffectChainTester tester(nullptr, 1, size);
115         tester.add_input(data, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_sRGB);
116         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
117
118         expect_equal(expected_data, out_data, 4, size);
119 }
120
121 TEST(FlatInput, BGR) {
122         const int size = 5;
123
124         float data[3 * size] = {
125                 0.0, 0.0, 0.0,
126                 0.5, 0.0, 0.0,
127                 0.0, 0.5, 0.0,
128                 0.0, 0.0, 0.7,
129                 0.0, 0.3, 0.7,
130         };
131         float expected_data[4 * size] = {
132                 0.0, 0.0, 0.0, 1.0,
133                 0.0, 0.0, 0.5, 1.0,
134                 0.0, 0.5, 0.0, 1.0,
135                 0.7, 0.0, 0.0, 1.0,
136                 0.7, 0.3, 0.0, 1.0,
137         };
138         float out_data[4 * size];
139
140         EffectChainTester tester(data, 1, size, FORMAT_BGR, COLORSPACE_sRGB, GAMMA_LINEAR);
141         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
142
143         expect_equal(expected_data, out_data, 4, size);
144 }
145
146 TEST(FlatInput, BGRA) {
147         const int size = 5;
148
149         float data[4 * size] = {
150                 0.0, 0.0, 0.0, 1.0,
151                 0.5, 0.0, 0.0, 0.3,
152                 0.0, 0.5, 0.0, 0.7,
153                 0.0, 0.0, 0.7, 1.0,
154                 0.0, 0.3, 0.7, 0.2,
155         };
156         float expected_data[4 * size] = {
157                 0.0, 0.0, 0.0, 1.0,
158                 0.0, 0.0, 0.5, 0.3,
159                 0.0, 0.5, 0.0, 0.7,
160                 0.7, 0.0, 0.0, 1.0,
161                 0.7, 0.3, 0.0, 0.2,
162         };
163         float out_data[4 * size];
164
165         EffectChainTester tester(data, 1, size, FORMAT_BGRA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
166         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
167
168         expect_equal(expected_data, out_data, 4, size);
169 }
170
171 TEST(FlatInput, Pitch) {
172         const int pitch = 3;
173         const int width = 2;
174         const int height = 4;
175
176         float data[pitch * height] = {
177                 0.0, 1.0, 999.0f,
178                 0.5, 0.5, 999.0f,
179                 0.7, 0.2, 999.0f,
180                 1.0, 0.6, 999.0f,
181         };
182         float expected_data[4 * width * height] = {
183                 0.0, 0.0, 0.0, 1.0,  1.0, 1.0, 1.0, 1.0,
184                 0.5, 0.5, 0.5, 1.0,  0.5, 0.5, 0.5, 1.0,
185                 0.7, 0.7, 0.7, 1.0,  0.2, 0.2, 0.2, 1.0,
186                 1.0, 1.0, 1.0, 1.0,  0.6, 0.6, 0.6, 1.0,
187         };
188         float out_data[4 * width * height];
189
190         EffectChainTester tester(nullptr, width, height);
191
192         ImageFormat format;
193         format.color_space = COLORSPACE_sRGB;
194         format.gamma_curve = GAMMA_LINEAR;
195
196         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
197         input->set_pitch(pitch);
198         input->set_pixel_data(data);
199         tester.get_chain()->add_input(input);
200
201         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
202         expect_equal(expected_data, out_data, 4 * width, height);
203 }
204
205 TEST(FlatInput, UpdatedData) {
206         const int width = 2;
207         const int height = 4;
208
209         float data[width * height] = {
210                 0.0, 1.0,
211                 0.5, 0.5,
212                 0.7, 0.2,
213                 1.0, 0.6,
214         };
215         float out_data[width * height];
216
217         EffectChainTester tester(nullptr, width, height);
218
219         ImageFormat format;
220         format.color_space = COLORSPACE_sRGB;
221         format.gamma_curve = GAMMA_LINEAR;
222
223         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
224         input->set_pixel_data(data);
225         tester.get_chain()->add_input(input);
226
227         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
228         expect_equal(data, out_data, width, height);
229
230         data[6] = 0.3;
231         input->invalidate_pixel_data();
232
233         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
234         expect_equal(data, out_data, width, height);
235 }
236
237 TEST(FlatInput, PBO) {
238         const int width = 3;
239         const int height = 2;
240
241         float data[width * height] = {
242                 0.0, 1.0, 0.5,
243                 0.5, 0.5, 0.2,
244         };
245         float expected_data[4 * width * height] = {
246                 0.0, 0.0, 0.0, 1.0,  1.0, 1.0, 1.0, 1.0,  0.5, 0.5, 0.5, 1.0,
247                 0.5, 0.5, 0.5, 1.0,  0.5, 0.5, 0.5, 1.0,  0.2, 0.2, 0.2, 1.0,
248         };
249         float out_data[4 * width * height];
250
251         GLuint pbo;
252         glGenBuffers(1, &pbo);
253         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbo);
254         glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, width * height * sizeof(float), data, GL_STREAM_DRAW);
255         glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
256
257         EffectChainTester tester(nullptr, width, height);
258
259         ImageFormat format;
260         format.color_space = COLORSPACE_sRGB;
261         format.gamma_curve = GAMMA_LINEAR;
262
263         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
264         input->set_pixel_data((float *)BUFFER_OFFSET(0), pbo);
265         tester.get_chain()->add_input(input);
266
267         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
268         expect_equal(expected_data, out_data, 4 * width, height);
269
270         glDeleteBuffers(1, &pbo);
271 }
272
273 TEST(FlatInput, ExternalTexture) {
274         const int size = 5;
275
276         float data[3 * size] = {
277                 0.0, 0.0, 0.0,
278                 0.5, 0.0, 0.0,
279                 0.0, 0.5, 0.0,
280                 0.0, 0.0, 0.7,
281                 0.0, 0.3, 0.7,
282         };
283         float expected_data[4 * size] = {
284                 0.0, 0.0, 0.0, 1.0,
285                 0.5, 0.0, 0.0, 1.0,
286                 0.0, 0.5, 0.0, 1.0,
287                 0.0, 0.0, 0.7, 1.0,
288                 0.0, 0.3, 0.7, 1.0,
289         };
290         float out_data[4 * size];
291
292         EffectChainTester tester(nullptr, 1, size, FORMAT_RGB, COLORSPACE_sRGB, GAMMA_LINEAR);
293
294         ImageFormat format;
295         format.color_space = COLORSPACE_sRGB;
296         format.gamma_curve = GAMMA_LINEAR;
297
298         ResourcePool pool;
299         GLuint tex = pool.create_2d_texture(GL_RGB8, 1, size);
300         check_error();
301         glBindTexture(GL_TEXTURE_2D, tex);
302         check_error();
303         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
304         check_error();
305         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
306         check_error();
307         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, size, GL_RGB, GL_FLOAT, data);
308         check_error();
309         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
310         check_error();
311         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
312         check_error();
313
314         FlatInput *input = new FlatInput(format, FORMAT_RGB, GL_FLOAT, 1, size);
315         input->set_texture_num(tex);
316         tester.get_chain()->add_input(input);
317
318         tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
319
320         pool.release_2d_texture(tex);
321
322         expect_equal(expected_data, out_data, 4, size);
323 }
324
325 // Just an IdentityEffect, but marks as needing mipmaps, so that we can use it
326 // for downscaling to verify mipmaps were used.
327 class MipmapNeedingEffect : public Effect {
328 public:
329         MipmapNeedingEffect() {}
330         MipmapRequirements needs_mipmaps() const override { return NEEDS_MIPMAPS; }
331
332         string effect_type_id() const override { return "MipmapNeedingEffect"; }
333         string output_fragment_shader() override { return read_file("identity.frag"); }
334 };
335
336 TEST(FlatInput, ExternalTextureMipmapState) {
337         const int width = 4;
338         const int height = 4;
339
340         float data[width * height] = {
341                 1.0, 0.0, 0.0, 0.0,
342                 0.0, 0.0, 0.0, 0.0,
343                 0.0, 0.0, 0.0, 0.0,
344                 0.0, 0.0, 0.0, 0.0,
345         };
346         float expected_data[] = {
347                 0.0625,
348         };
349         float out_data[1];
350
351         EffectChainTester tester(nullptr, 1, 1, FORMAT_RGB, COLORSPACE_sRGB, GAMMA_LINEAR);
352
353         ImageFormat format;
354         format.color_space = COLORSPACE_sRGB;
355         format.gamma_curve = GAMMA_LINEAR;
356
357         ResourcePool pool;
358         GLuint tex = pool.create_2d_texture(GL_R8, width, height);
359         check_error();
360         glBindTexture(GL_TEXTURE_2D, tex);
361         check_error();
362         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
363         check_error();
364         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
365         check_error();
366         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_FLOAT, data);
367         check_error();
368         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
369         check_error();
370         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
371         check_error();
372         glGenerateMipmap(GL_TEXTURE_2D);
373         check_error();
374
375         // Turn off mipmaps, so that we verify that Movit turns it back on.
376         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
377         check_error();
378
379         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
380         input->set_texture_num(tex);
381         tester.get_chain()->add_input(input);
382         tester.get_chain()->add_effect(new MipmapNeedingEffect);
383
384         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
385
386         pool.release_2d_texture(tex);
387
388         expect_equal(expected_data, out_data, 1, 1);
389 }
390
391 TEST(FlatInput, NoData) {
392         const int width = 2;
393         const int height = 4;
394
395         float out_data[width * height];
396
397         EffectChainTester tester(nullptr, width, height);
398
399         ImageFormat format;
400         format.color_space = COLORSPACE_sRGB;
401         format.gamma_curve = GAMMA_LINEAR;
402
403         FlatInput *input = new FlatInput(format, FORMAT_GRAYSCALE, GL_FLOAT, width, height);
404         tester.get_chain()->add_input(input);
405
406         tester.run(out_data, GL_RED, COLORSPACE_sRGB, GAMMA_LINEAR);
407
408         // Don't care what the output was, just that it does not crash.
409 }
410
411 }  // namespace movit