blob: 6984c02e9912e2ac0b60a349d5fdb41a7a3a13e9 [file] [log] [blame]
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +03001/*
2 * Copyright © 2017 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Paul Kocialkowski <paul.kocialkowski@linux.intel.com>
25 */
26
27#include "config.h"
28
29#include <fcntl.h>
30#include <pixman.h>
31#include <cairo.h>
Paul Kocialkowski8cf32fe2017-07-20 18:13:36 +030032#include <gsl/gsl_statistics_double.h>
33#include <gsl/gsl_fit.h>
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +030034
Daniel Vetter8c1fcc62017-09-08 11:23:15 +020035#include "igt_frame.h"
36#include "igt_core.h"
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +030037
38/**
39 * SECTION:igt_frame
40 * @short_description: Library for frame-related tests
41 * @title: Frame
42 * @include: igt_frame.h
43 *
44 * This library contains helpers for frame-related tests. This includes common
45 * frame dumping as well as frame comparison helpers.
46 */
47
48/**
49 * igt_frame_dump_is_enabled:
50 *
51 * Get whether frame dumping is enabled.
52 *
53 * Returns: A boolean indicating whether frame dumping is enabled
54 */
55bool igt_frame_dump_is_enabled(void)
56{
Daniel Vettera3a3e0f2017-09-05 14:36:08 +020057 return igt_frame_dump_path != NULL;
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +030058}
59
60static void igt_write_frame_to_png(cairo_surface_t *surface, int fd,
61 const char *qualifier, const char *suffix)
62{
63 char path[PATH_MAX];
64 const char *test_name;
65 const char *subtest_name;
66 cairo_status_t status;
67 int index;
68
69 test_name = igt_test_name();
70 subtest_name = igt_subtest_name();
71
72 if (suffix)
73 snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s-%s.png",
Daniel Vettera3a3e0f2017-09-05 14:36:08 +020074 igt_frame_dump_path, test_name, subtest_name, qualifier,
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +030075 suffix);
76 else
77 snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.png",
Daniel Vettera3a3e0f2017-09-05 14:36:08 +020078 igt_frame_dump_path, test_name, subtest_name, qualifier);
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +030079
80 igt_debug("Dumping %s frame to %s...\n", qualifier, path);
81
82 status = cairo_surface_write_to_png(surface, path);
83
84 igt_assert_eq(status, CAIRO_STATUS_SUCCESS);
85
86 index = strlen(path);
87
88 if (fd >= 0 && index < (PATH_MAX - 1)) {
89 path[index++] = '\n';
90 path[index] = '\0';
91
92 write(fd, path, strlen(path));
93 }
94}
95
96/**
97 * igt_write_compared_frames_to_png:
98 * @reference: The reference cairo surface
99 * @capture: The captured cairo surface
100 * @reference_suffix: The suffix to give to the reference png file
101 * @capture_suffix: The suffix to give to the capture png file
102 *
103 * Write previously compared frames to png files.
104 */
105void igt_write_compared_frames_to_png(cairo_surface_t *reference,
106 cairo_surface_t *capture,
107 const char *reference_suffix,
108 const char *capture_suffix)
109{
110 char *id;
111 const char *test_name;
112 const char *subtest_name;
113 char path[PATH_MAX];
114 int fd = -1;
115
116 if (!igt_frame_dump_is_enabled())
117 return;
118
119 id = getenv("IGT_FRAME_DUMP_ID");
120
121 test_name = igt_test_name();
122 subtest_name = igt_subtest_name();
123
124 if (id)
125 snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.txt",
Daniel Vettera3a3e0f2017-09-05 14:36:08 +0200126 igt_frame_dump_path, test_name, subtest_name, id);
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +0300127 else
128 snprintf(path, PATH_MAX, "%s/frame-%s-%s.txt",
Daniel Vettera3a3e0f2017-09-05 14:36:08 +0200129 igt_frame_dump_path, test_name, subtest_name);
Paul Kocialkowskiee31e0b2017-07-19 16:46:07 +0300130
131 fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
132 igt_assert(fd >= 0);
133
134 igt_debug("Writing dump report to %s...\n", path);
135
136 igt_write_frame_to_png(reference, fd, "reference", reference_suffix);
137 igt_write_frame_to_png(capture, fd, "capture", capture_suffix);
138
139 close(fd);
140}
Paul Kocialkowski8cf32fe2017-07-20 18:13:36 +0300141
142/**
143 * igt_check_analog_frame_match:
144 * @reference: The reference cairo surface
145 * @capture: The captured cairo surface
146 *
147 * Checks that the analog image contained in the chamelium frame dump matches
148 * the given framebuffer.
149 *
150 * In order to determine whether the frame matches the reference, the following
151 * reasoning is implemented:
152 * 1. The absolute error for each color value of the reference is collected.
153 * 2. The average absolute error is calculated for each color value of the
154 * reference and must not go above 60 (23.5 % of the total range).
155 * 3. A linear fit for the average absolute error from the pixel value is
156 * calculated, as a DAC-ADC chain is expected to have a linear error curve.
157 * 4. The linear fit is correlated with the actual average absolute error for
158 * the frame and the correlation coefficient is checked to be > 0.985,
159 * indicating a match with the expected error trend.
160 *
161 * Most errors (e.g. due to scaling, rotation, color space, etc) can be
162 * reliably detected this way, with a minimized number of false-positives.
163 * However, the brightest values (250 and up) are ignored as the error trend
164 * is often not linear there in practice due to clamping.
165 *
166 * Returns: a boolean indicating whether the frames match
167 */
168
169bool igt_check_analog_frame_match(cairo_surface_t *reference,
170 cairo_surface_t *capture)
171{
172 pixman_image_t *reference_src, *capture_src;
173 int w, h;
174 int error_count[3][256][2] = { 0 };
175 double error_average[4][250];
176 double error_trend[250];
177 double c0, c1, cov00, cov01, cov11, sumsq;
178 double correlation;
179 unsigned char *reference_pixels, *capture_pixels;
180 unsigned char *p;
181 unsigned char *q;
182 bool match = true;
183 int diff;
184 int x, y;
185 int i, j;
186
187 w = cairo_image_surface_get_width(reference);
188 h = cairo_image_surface_get_height(reference);
189
190 reference_src = pixman_image_create_bits(
191 PIXMAN_x8r8g8b8, w, h,
192 (void*)cairo_image_surface_get_data(reference),
193 cairo_image_surface_get_stride(reference));
194 reference_pixels = (unsigned char *) pixman_image_get_data(reference_src);
195
196 capture_src = pixman_image_create_bits(
197 PIXMAN_x8r8g8b8, w, h,
198 (void*)cairo_image_surface_get_data(capture),
199 cairo_image_surface_get_stride(capture));
200 capture_pixels = (unsigned char *) pixman_image_get_data(capture_src);
201
202 /* Collect the absolute error for each color value */
203 for (x = 0; x < w; x++) {
204 for (y = 0; y < h; y++) {
205 p = &capture_pixels[(x + y * w) * 4];
206 q = &reference_pixels[(x + y * w) * 4];
207
208 for (i = 0; i < 3; i++) {
209 diff = (int) p[i] - q[i];
210 if (diff < 0)
211 diff = -diff;
212
213 error_count[i][q[i]][0] += diff;
214 error_count[i][q[i]][1]++;
215 }
216 }
217 }
218
219 /* Calculate the average absolute error for each color value */
220 for (i = 0; i < 250; i++) {
221 error_average[0][i] = i;
222
223 for (j = 1; j < 4; j++) {
224 error_average[j][i] = (double) error_count[j-1][i][0] /
225 error_count[j-1][i][1];
226
227 if (error_average[j][i] > 60) {
228 igt_warn("Error average too high (%f)\n",
229 error_average[j][i]);
230
231 match = false;
232 goto complete;
233 }
234 }
235 }
236
237 /*
238 * Calculate error trend from linear fit.
239 * A DAC-ADC chain is expected to have a linear absolute error on
240 * most of its range
241 */
242 for (i = 1; i < 4; i++) {
243 gsl_fit_linear((const double *) &error_average[0], 1,
244 (const double *) &error_average[i], 1, 250,
245 &c0, &c1, &cov00, &cov01, &cov11, &sumsq);
246
247 for (j = 0; j < 250; j++)
248 error_trend[j] = c0 + j * c1;
249
250 correlation = gsl_stats_correlation((const double *) &error_trend,
251 1,
252 (const double *) &error_average[i],
253 1, 250);
254
255 if (correlation < 0.985) {
256 igt_warn("Error with reference not correlated (%f)\n",
257 correlation);
258
259 match = false;
260 goto complete;
261 }
262 }
263
264complete:
265 pixman_image_unref(reference_src);
266 pixman_image_unref(capture_src);
267
268 return match;
269}