xref: /aosp_15_r20/external/igt-gpu-tools/lib/igt_frame.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1*d83cc019SAndroid Build Coastguard Worker /*
2*d83cc019SAndroid Build Coastguard Worker  * Copyright © 2017 Intel Corporation
3*d83cc019SAndroid Build Coastguard Worker  *
4*d83cc019SAndroid Build Coastguard Worker  * Permission is hereby granted, free of charge, to any person obtaining a
5*d83cc019SAndroid Build Coastguard Worker  * copy of this software and associated documentation files (the "Software"),
6*d83cc019SAndroid Build Coastguard Worker  * to deal in the Software without restriction, including without limitation
7*d83cc019SAndroid Build Coastguard Worker  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*d83cc019SAndroid Build Coastguard Worker  * and/or sell copies of the Software, and to permit persons to whom the
9*d83cc019SAndroid Build Coastguard Worker  * Software is furnished to do so, subject to the following conditions:
10*d83cc019SAndroid Build Coastguard Worker  *
11*d83cc019SAndroid Build Coastguard Worker  * The above copyright notice and this permission notice (including the next
12*d83cc019SAndroid Build Coastguard Worker  * paragraph) shall be included in all copies or substantial portions of the
13*d83cc019SAndroid Build Coastguard Worker  * Software.
14*d83cc019SAndroid Build Coastguard Worker  *
15*d83cc019SAndroid Build Coastguard Worker  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*d83cc019SAndroid Build Coastguard Worker  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*d83cc019SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18*d83cc019SAndroid Build Coastguard Worker  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*d83cc019SAndroid Build Coastguard Worker  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*d83cc019SAndroid Build Coastguard Worker  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21*d83cc019SAndroid Build Coastguard Worker  * IN THE SOFTWARE.
22*d83cc019SAndroid Build Coastguard Worker  *
23*d83cc019SAndroid Build Coastguard Worker  * Authors:
24*d83cc019SAndroid Build Coastguard Worker  *  Paul Kocialkowski <[email protected]>
25*d83cc019SAndroid Build Coastguard Worker  */
26*d83cc019SAndroid Build Coastguard Worker 
27*d83cc019SAndroid Build Coastguard Worker #include "config.h"
28*d83cc019SAndroid Build Coastguard Worker 
29*d83cc019SAndroid Build Coastguard Worker #include <fcntl.h>
30*d83cc019SAndroid Build Coastguard Worker #include <pixman.h>
31*d83cc019SAndroid Build Coastguard Worker #include <cairo.h>
32*d83cc019SAndroid Build Coastguard Worker #include <gsl/gsl_statistics_double.h>
33*d83cc019SAndroid Build Coastguard Worker #include <gsl/gsl_fit.h>
34*d83cc019SAndroid Build Coastguard Worker 
35*d83cc019SAndroid Build Coastguard Worker #include "igt_frame.h"
36*d83cc019SAndroid Build Coastguard Worker #include "igt_core.h"
37*d83cc019SAndroid Build Coastguard Worker 
38*d83cc019SAndroid Build Coastguard Worker /**
39*d83cc019SAndroid Build Coastguard Worker  * SECTION:igt_frame
40*d83cc019SAndroid Build Coastguard Worker  * @short_description: Library for frame-related tests
41*d83cc019SAndroid Build Coastguard Worker  * @title: Frame
42*d83cc019SAndroid Build Coastguard Worker  * @include: igt_frame.h
43*d83cc019SAndroid Build Coastguard Worker  *
44*d83cc019SAndroid Build Coastguard Worker  * This library contains helpers for frame-related tests. This includes common
45*d83cc019SAndroid Build Coastguard Worker  * frame dumping as well as frame comparison helpers.
46*d83cc019SAndroid Build Coastguard Worker  */
47*d83cc019SAndroid Build Coastguard Worker 
48*d83cc019SAndroid Build Coastguard Worker /**
49*d83cc019SAndroid Build Coastguard Worker  * igt_frame_dump_is_enabled:
50*d83cc019SAndroid Build Coastguard Worker  *
51*d83cc019SAndroid Build Coastguard Worker  * Get whether frame dumping is enabled.
52*d83cc019SAndroid Build Coastguard Worker  *
53*d83cc019SAndroid Build Coastguard Worker  * Returns: A boolean indicating whether frame dumping is enabled
54*d83cc019SAndroid Build Coastguard Worker  */
igt_frame_dump_is_enabled(void)55*d83cc019SAndroid Build Coastguard Worker bool igt_frame_dump_is_enabled(void)
56*d83cc019SAndroid Build Coastguard Worker {
57*d83cc019SAndroid Build Coastguard Worker 	return igt_frame_dump_path != NULL;
58*d83cc019SAndroid Build Coastguard Worker }
59*d83cc019SAndroid Build Coastguard Worker 
igt_write_frame_to_png(cairo_surface_t * surface,int fd,const char * qualifier,const char * suffix)60*d83cc019SAndroid Build Coastguard Worker static void igt_write_frame_to_png(cairo_surface_t *surface, int fd,
61*d83cc019SAndroid Build Coastguard Worker 				   const char *qualifier, const char *suffix)
62*d83cc019SAndroid Build Coastguard Worker {
63*d83cc019SAndroid Build Coastguard Worker 	char path[PATH_MAX];
64*d83cc019SAndroid Build Coastguard Worker 	const char *test_name;
65*d83cc019SAndroid Build Coastguard Worker 	const char *subtest_name;
66*d83cc019SAndroid Build Coastguard Worker 	cairo_status_t status;
67*d83cc019SAndroid Build Coastguard Worker 	int index;
68*d83cc019SAndroid Build Coastguard Worker 
69*d83cc019SAndroid Build Coastguard Worker 	test_name = igt_test_name();
70*d83cc019SAndroid Build Coastguard Worker 	subtest_name = igt_subtest_name();
71*d83cc019SAndroid Build Coastguard Worker 
72*d83cc019SAndroid Build Coastguard Worker 	if (suffix)
73*d83cc019SAndroid Build Coastguard Worker 		snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s-%s.png",
74*d83cc019SAndroid Build Coastguard Worker 			 igt_frame_dump_path, test_name, subtest_name, qualifier,
75*d83cc019SAndroid Build Coastguard Worker 			 suffix);
76*d83cc019SAndroid Build Coastguard Worker 	else
77*d83cc019SAndroid Build Coastguard Worker 		snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.png",
78*d83cc019SAndroid Build Coastguard Worker 			 igt_frame_dump_path, test_name, subtest_name, qualifier);
79*d83cc019SAndroid Build Coastguard Worker 
80*d83cc019SAndroid Build Coastguard Worker 	igt_debug("Dumping %s frame to %s...\n", qualifier, path);
81*d83cc019SAndroid Build Coastguard Worker 
82*d83cc019SAndroid Build Coastguard Worker 	status = cairo_surface_write_to_png(surface, path);
83*d83cc019SAndroid Build Coastguard Worker 
84*d83cc019SAndroid Build Coastguard Worker 	igt_assert_eq(status, CAIRO_STATUS_SUCCESS);
85*d83cc019SAndroid Build Coastguard Worker 
86*d83cc019SAndroid Build Coastguard Worker 	index = strlen(path);
87*d83cc019SAndroid Build Coastguard Worker 
88*d83cc019SAndroid Build Coastguard Worker 	if (fd >= 0 && index < (PATH_MAX - 1)) {
89*d83cc019SAndroid Build Coastguard Worker 		path[index++] = '\n';
90*d83cc019SAndroid Build Coastguard Worker 		path[index] = '\0';
91*d83cc019SAndroid Build Coastguard Worker 
92*d83cc019SAndroid Build Coastguard Worker 		write(fd, path, strlen(path));
93*d83cc019SAndroid Build Coastguard Worker 	}
94*d83cc019SAndroid Build Coastguard Worker }
95*d83cc019SAndroid Build Coastguard Worker 
96*d83cc019SAndroid Build Coastguard Worker /**
97*d83cc019SAndroid Build Coastguard Worker  * igt_write_compared_frames_to_png:
98*d83cc019SAndroid Build Coastguard Worker  * @reference: The reference cairo surface
99*d83cc019SAndroid Build Coastguard Worker  * @capture: The captured cairo surface
100*d83cc019SAndroid Build Coastguard Worker  * @reference_suffix: The suffix to give to the reference png file
101*d83cc019SAndroid Build Coastguard Worker  * @capture_suffix: The suffix to give to the capture png file
102*d83cc019SAndroid Build Coastguard Worker  *
103*d83cc019SAndroid Build Coastguard Worker  * Write previously compared frames to png files.
104*d83cc019SAndroid Build Coastguard Worker  */
igt_write_compared_frames_to_png(cairo_surface_t * reference,cairo_surface_t * capture,const char * reference_suffix,const char * capture_suffix)105*d83cc019SAndroid Build Coastguard Worker void igt_write_compared_frames_to_png(cairo_surface_t *reference,
106*d83cc019SAndroid Build Coastguard Worker 				      cairo_surface_t *capture,
107*d83cc019SAndroid Build Coastguard Worker 				      const char *reference_suffix,
108*d83cc019SAndroid Build Coastguard Worker 				      const char *capture_suffix)
109*d83cc019SAndroid Build Coastguard Worker {
110*d83cc019SAndroid Build Coastguard Worker 	char *id;
111*d83cc019SAndroid Build Coastguard Worker 	const char *test_name;
112*d83cc019SAndroid Build Coastguard Worker 	const char *subtest_name;
113*d83cc019SAndroid Build Coastguard Worker 	char path[PATH_MAX];
114*d83cc019SAndroid Build Coastguard Worker 	int fd = -1;
115*d83cc019SAndroid Build Coastguard Worker 
116*d83cc019SAndroid Build Coastguard Worker 	if (!igt_frame_dump_is_enabled())
117*d83cc019SAndroid Build Coastguard Worker 		return;
118*d83cc019SAndroid Build Coastguard Worker 
119*d83cc019SAndroid Build Coastguard Worker 	id = getenv("IGT_FRAME_DUMP_ID");
120*d83cc019SAndroid Build Coastguard Worker 
121*d83cc019SAndroid Build Coastguard Worker 	test_name = igt_test_name();
122*d83cc019SAndroid Build Coastguard Worker 	subtest_name = igt_subtest_name();
123*d83cc019SAndroid Build Coastguard Worker 
124*d83cc019SAndroid Build Coastguard Worker 	if (id)
125*d83cc019SAndroid Build Coastguard Worker 		snprintf(path, PATH_MAX, "%s/frame-%s-%s-%s.txt",
126*d83cc019SAndroid Build Coastguard Worker 			 igt_frame_dump_path, test_name, subtest_name, id);
127*d83cc019SAndroid Build Coastguard Worker 	else
128*d83cc019SAndroid Build Coastguard Worker 		snprintf(path, PATH_MAX, "%s/frame-%s-%s.txt",
129*d83cc019SAndroid Build Coastguard Worker 			 igt_frame_dump_path, test_name, subtest_name);
130*d83cc019SAndroid Build Coastguard Worker 
131*d83cc019SAndroid Build Coastguard Worker 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
132*d83cc019SAndroid Build Coastguard Worker 	igt_assert(fd >= 0);
133*d83cc019SAndroid Build Coastguard Worker 
134*d83cc019SAndroid Build Coastguard Worker 	igt_debug("Writing dump report to %s...\n", path);
135*d83cc019SAndroid Build Coastguard Worker 
136*d83cc019SAndroid Build Coastguard Worker 	igt_write_frame_to_png(reference, fd, "reference", reference_suffix);
137*d83cc019SAndroid Build Coastguard Worker 	igt_write_frame_to_png(capture, fd, "capture", capture_suffix);
138*d83cc019SAndroid Build Coastguard Worker 
139*d83cc019SAndroid Build Coastguard Worker 	close(fd);
140*d83cc019SAndroid Build Coastguard Worker }
141*d83cc019SAndroid Build Coastguard Worker 
142*d83cc019SAndroid Build Coastguard Worker /**
143*d83cc019SAndroid Build Coastguard Worker  * igt_check_analog_frame_match:
144*d83cc019SAndroid Build Coastguard Worker  * @reference: The reference cairo surface
145*d83cc019SAndroid Build Coastguard Worker  * @capture: The captured cairo surface
146*d83cc019SAndroid Build Coastguard Worker  *
147*d83cc019SAndroid Build Coastguard Worker  * Checks that the analog image contained in the chamelium frame dump matches
148*d83cc019SAndroid Build Coastguard Worker  * the given framebuffer.
149*d83cc019SAndroid Build Coastguard Worker  *
150*d83cc019SAndroid Build Coastguard Worker  * In order to determine whether the frame matches the reference, the following
151*d83cc019SAndroid Build Coastguard Worker  * reasoning is implemented:
152*d83cc019SAndroid Build Coastguard Worker  * 1. The absolute error for each color value of the reference is collected.
153*d83cc019SAndroid Build Coastguard Worker  * 2. The average absolute error is calculated for each color value of the
154*d83cc019SAndroid Build Coastguard Worker  *    reference and must not go above 60 (23.5 % of the total range).
155*d83cc019SAndroid Build Coastguard Worker  * 3. A linear fit for the average absolute error from the pixel value is
156*d83cc019SAndroid Build Coastguard Worker  *    calculated, as a DAC-ADC chain is expected to have a linear error curve.
157*d83cc019SAndroid Build Coastguard Worker  * 4. The linear fit is correlated with the actual average absolute error for
158*d83cc019SAndroid Build Coastguard Worker  *    the frame and the correlation coefficient is checked to be > 0.985,
159*d83cc019SAndroid Build Coastguard Worker  *    indicating a match with the expected error trend.
160*d83cc019SAndroid Build Coastguard Worker  *
161*d83cc019SAndroid Build Coastguard Worker  * Most errors (e.g. due to scaling, rotation, color space, etc) can be
162*d83cc019SAndroid Build Coastguard Worker  * reliably detected this way, with a minimized number of false-positives.
163*d83cc019SAndroid Build Coastguard Worker  * However, the brightest values (250 and up) are ignored as the error trend
164*d83cc019SAndroid Build Coastguard Worker  * is often not linear there in practice due to clamping.
165*d83cc019SAndroid Build Coastguard Worker  *
166*d83cc019SAndroid Build Coastguard Worker  * Returns: a boolean indicating whether the frames match
167*d83cc019SAndroid Build Coastguard Worker  */
168*d83cc019SAndroid Build Coastguard Worker 
igt_check_analog_frame_match(cairo_surface_t * reference,cairo_surface_t * capture)169*d83cc019SAndroid Build Coastguard Worker bool igt_check_analog_frame_match(cairo_surface_t *reference,
170*d83cc019SAndroid Build Coastguard Worker 				  cairo_surface_t *capture)
171*d83cc019SAndroid Build Coastguard Worker {
172*d83cc019SAndroid Build Coastguard Worker 	pixman_image_t *reference_src, *capture_src;
173*d83cc019SAndroid Build Coastguard Worker 	int w, h;
174*d83cc019SAndroid Build Coastguard Worker 	int error_count[3][256][2] = { 0 };
175*d83cc019SAndroid Build Coastguard Worker 	double error_average[4][250];
176*d83cc019SAndroid Build Coastguard Worker 	double error_trend[250];
177*d83cc019SAndroid Build Coastguard Worker 	double c0, c1, cov00, cov01, cov11, sumsq;
178*d83cc019SAndroid Build Coastguard Worker 	double correlation;
179*d83cc019SAndroid Build Coastguard Worker 	unsigned char *reference_pixels, *capture_pixels;
180*d83cc019SAndroid Build Coastguard Worker 	unsigned char *p;
181*d83cc019SAndroid Build Coastguard Worker 	unsigned char *q;
182*d83cc019SAndroid Build Coastguard Worker 	bool match = true;
183*d83cc019SAndroid Build Coastguard Worker 	int diff;
184*d83cc019SAndroid Build Coastguard Worker 	int x, y;
185*d83cc019SAndroid Build Coastguard Worker 	int i, j;
186*d83cc019SAndroid Build Coastguard Worker 
187*d83cc019SAndroid Build Coastguard Worker 	w = cairo_image_surface_get_width(reference);
188*d83cc019SAndroid Build Coastguard Worker 	h = cairo_image_surface_get_height(reference);
189*d83cc019SAndroid Build Coastguard Worker 
190*d83cc019SAndroid Build Coastguard Worker 	reference_src = pixman_image_create_bits(
191*d83cc019SAndroid Build Coastguard Worker 	    PIXMAN_x8r8g8b8, w, h,
192*d83cc019SAndroid Build Coastguard Worker 	    (void*)cairo_image_surface_get_data(reference),
193*d83cc019SAndroid Build Coastguard Worker 	    cairo_image_surface_get_stride(reference));
194*d83cc019SAndroid Build Coastguard Worker 	reference_pixels = (unsigned char *) pixman_image_get_data(reference_src);
195*d83cc019SAndroid Build Coastguard Worker 
196*d83cc019SAndroid Build Coastguard Worker 	capture_src = pixman_image_create_bits(
197*d83cc019SAndroid Build Coastguard Worker 	    PIXMAN_x8r8g8b8, w, h,
198*d83cc019SAndroid Build Coastguard Worker 	    (void*)cairo_image_surface_get_data(capture),
199*d83cc019SAndroid Build Coastguard Worker 	    cairo_image_surface_get_stride(capture));
200*d83cc019SAndroid Build Coastguard Worker 	capture_pixels = (unsigned char *) pixman_image_get_data(capture_src);
201*d83cc019SAndroid Build Coastguard Worker 
202*d83cc019SAndroid Build Coastguard Worker 	/* Collect the absolute error for each color value */
203*d83cc019SAndroid Build Coastguard Worker 	for (x = 0; x < w; x++) {
204*d83cc019SAndroid Build Coastguard Worker 		for (y = 0; y < h; y++) {
205*d83cc019SAndroid Build Coastguard Worker 			p = &capture_pixels[(x + y * w) * 4];
206*d83cc019SAndroid Build Coastguard Worker 			q = &reference_pixels[(x + y * w) * 4];
207*d83cc019SAndroid Build Coastguard Worker 
208*d83cc019SAndroid Build Coastguard Worker 			for (i = 0; i < 3; i++) {
209*d83cc019SAndroid Build Coastguard Worker 				diff = (int) p[i] - q[i];
210*d83cc019SAndroid Build Coastguard Worker 				if (diff < 0)
211*d83cc019SAndroid Build Coastguard Worker 					diff = -diff;
212*d83cc019SAndroid Build Coastguard Worker 
213*d83cc019SAndroid Build Coastguard Worker 				error_count[i][q[i]][0] += diff;
214*d83cc019SAndroid Build Coastguard Worker 				error_count[i][q[i]][1]++;
215*d83cc019SAndroid Build Coastguard Worker 			}
216*d83cc019SAndroid Build Coastguard Worker 		}
217*d83cc019SAndroid Build Coastguard Worker 	}
218*d83cc019SAndroid Build Coastguard Worker 
219*d83cc019SAndroid Build Coastguard Worker 	/* Calculate the average absolute error for each color value */
220*d83cc019SAndroid Build Coastguard Worker 	for (i = 0; i < 250; i++) {
221*d83cc019SAndroid Build Coastguard Worker 		error_average[0][i] = i;
222*d83cc019SAndroid Build Coastguard Worker 
223*d83cc019SAndroid Build Coastguard Worker 		for (j = 1; j < 4; j++) {
224*d83cc019SAndroid Build Coastguard Worker 			error_average[j][i] = (double) error_count[j-1][i][0] /
225*d83cc019SAndroid Build Coastguard Worker 					      error_count[j-1][i][1];
226*d83cc019SAndroid Build Coastguard Worker 
227*d83cc019SAndroid Build Coastguard Worker 			if (error_average[j][i] > 60) {
228*d83cc019SAndroid Build Coastguard Worker 				igt_warn("Error average too high (%f)\n",
229*d83cc019SAndroid Build Coastguard Worker 					 error_average[j][i]);
230*d83cc019SAndroid Build Coastguard Worker 
231*d83cc019SAndroid Build Coastguard Worker 				match = false;
232*d83cc019SAndroid Build Coastguard Worker 				goto complete;
233*d83cc019SAndroid Build Coastguard Worker 			}
234*d83cc019SAndroid Build Coastguard Worker 		}
235*d83cc019SAndroid Build Coastguard Worker 	}
236*d83cc019SAndroid Build Coastguard Worker 
237*d83cc019SAndroid Build Coastguard Worker 	/*
238*d83cc019SAndroid Build Coastguard Worker 	 * Calculate error trend from linear fit.
239*d83cc019SAndroid Build Coastguard Worker 	 * A DAC-ADC chain is expected to have a linear absolute error on
240*d83cc019SAndroid Build Coastguard Worker 	 * most of its range
241*d83cc019SAndroid Build Coastguard Worker 	 */
242*d83cc019SAndroid Build Coastguard Worker 	for (i = 1; i < 4; i++) {
243*d83cc019SAndroid Build Coastguard Worker 		gsl_fit_linear((const double *) &error_average[0], 1,
244*d83cc019SAndroid Build Coastguard Worker 			       (const double *) &error_average[i], 1, 250,
245*d83cc019SAndroid Build Coastguard Worker 			       &c0, &c1, &cov00, &cov01, &cov11, &sumsq);
246*d83cc019SAndroid Build Coastguard Worker 
247*d83cc019SAndroid Build Coastguard Worker 		for (j = 0; j < 250; j++)
248*d83cc019SAndroid Build Coastguard Worker 			error_trend[j] = c0 + j * c1;
249*d83cc019SAndroid Build Coastguard Worker 
250*d83cc019SAndroid Build Coastguard Worker 		correlation = gsl_stats_correlation((const double *) &error_trend,
251*d83cc019SAndroid Build Coastguard Worker 						    1,
252*d83cc019SAndroid Build Coastguard Worker 						    (const double *) &error_average[i],
253*d83cc019SAndroid Build Coastguard Worker 						    1, 250);
254*d83cc019SAndroid Build Coastguard Worker 
255*d83cc019SAndroid Build Coastguard Worker 		if (correlation < 0.985) {
256*d83cc019SAndroid Build Coastguard Worker 			igt_warn("Error with reference not correlated (%f)\n",
257*d83cc019SAndroid Build Coastguard Worker 				 correlation);
258*d83cc019SAndroid Build Coastguard Worker 
259*d83cc019SAndroid Build Coastguard Worker 			match = false;
260*d83cc019SAndroid Build Coastguard Worker 			goto complete;
261*d83cc019SAndroid Build Coastguard Worker 		}
262*d83cc019SAndroid Build Coastguard Worker 	}
263*d83cc019SAndroid Build Coastguard Worker 
264*d83cc019SAndroid Build Coastguard Worker complete:
265*d83cc019SAndroid Build Coastguard Worker 	pixman_image_unref(reference_src);
266*d83cc019SAndroid Build Coastguard Worker 	pixman_image_unref(capture_src);
267*d83cc019SAndroid Build Coastguard Worker 
268*d83cc019SAndroid Build Coastguard Worker 	return match;
269*d83cc019SAndroid Build Coastguard Worker }
270*d83cc019SAndroid Build Coastguard Worker 
271*d83cc019SAndroid Build Coastguard Worker #define XR24_COLOR_VALUE(data, stride, x, y, c) \
272*d83cc019SAndroid Build Coastguard Worker 	*((uint8_t *)(data) + (y) * (stride) + 4 * (x) + (c))
273*d83cc019SAndroid Build Coastguard Worker 
274*d83cc019SAndroid Build Coastguard Worker /**
275*d83cc019SAndroid Build Coastguard Worker  * igt_check_checkerboard_frame_match:
276*d83cc019SAndroid Build Coastguard Worker  * @reference: The reference cairo surface
277*d83cc019SAndroid Build Coastguard Worker  * @capture: The captured cairo surface
278*d83cc019SAndroid Build Coastguard Worker  *
279*d83cc019SAndroid Build Coastguard Worker  * Checks that the reference frame matches the captured frame using a
280*d83cc019SAndroid Build Coastguard Worker  * method designed for checkerboard patterns. These patterns are made of
281*d83cc019SAndroid Build Coastguard Worker  * consecutive rectangular shapes with alternating solid colors.
282*d83cc019SAndroid Build Coastguard Worker  *
283*d83cc019SAndroid Build Coastguard Worker  * The intent of this method is to cover cases where the captured result is
284*d83cc019SAndroid Build Coastguard Worker  * not pixel-perfect due to features such as scaling or YUV conversion and
285*d83cc019SAndroid Build Coastguard Worker  * subsampling. Such effects are mostly noticeable on the edges of the
286*d83cc019SAndroid Build Coastguard Worker  * patterns, so they are detected and excluded from the comparison.
287*d83cc019SAndroid Build Coastguard Worker  *
288*d83cc019SAndroid Build Coastguard Worker  * The algorithm works with two major steps. First, the edges of the reference
289*d83cc019SAndroid Build Coastguard Worker  * pattern are detected on the x and y axis independently. The detection is done
290*d83cc019SAndroid Build Coastguard Worker  * by calculating an absolute difference with a span of a few pixels before and
291*d83cc019SAndroid Build Coastguard Worker  * after the current position on the given axis, accumulated for each color
292*d83cc019SAndroid Build Coastguard Worker  * component. If the sum is above a given threshold on one of the axis, the
293*d83cc019SAndroid Build Coastguard Worker  * position is marked as an edge. In the second step, the pixel values are
294*d83cc019SAndroid Build Coastguard Worker  * compared per-component, excluding positions that were marked as edges or
295*d83cc019SAndroid Build Coastguard Worker  * that are at a transition between an edge and a non-edge. An error threshold
296*d83cc019SAndroid Build Coastguard Worker  * (for each individual color component) is used to mark the position as
297*d83cc019SAndroid Build Coastguard Worker  * erroneous or not. The ratio of erroneous pixels over compared pixels (that
298*d83cc019SAndroid Build Coastguard Worker  * does not count excluded pixels) is then calculated and compared to the error
299*d83cc019SAndroid Build Coastguard Worker  * rate threshold to determine whether the frames match or not.
300*d83cc019SAndroid Build Coastguard Worker  *
301*d83cc019SAndroid Build Coastguard Worker  * Returns: a boolean indicating whether the frames match
302*d83cc019SAndroid Build Coastguard Worker  */
igt_check_checkerboard_frame_match(cairo_surface_t * reference,cairo_surface_t * capture)303*d83cc019SAndroid Build Coastguard Worker bool igt_check_checkerboard_frame_match(cairo_surface_t *reference,
304*d83cc019SAndroid Build Coastguard Worker 					cairo_surface_t *capture)
305*d83cc019SAndroid Build Coastguard Worker {
306*d83cc019SAndroid Build Coastguard Worker 	unsigned int width, height, ref_stride, cap_stride;
307*d83cc019SAndroid Build Coastguard Worker 	void *ref_data, *cap_data;
308*d83cc019SAndroid Build Coastguard Worker 	unsigned char *edges_map;
309*d83cc019SAndroid Build Coastguard Worker 	unsigned int x, y, c;
310*d83cc019SAndroid Build Coastguard Worker 	unsigned int errors = 0, pixels = 0;
311*d83cc019SAndroid Build Coastguard Worker 	unsigned int edge_threshold = 100;
312*d83cc019SAndroid Build Coastguard Worker 	unsigned int color_error_threshold = 24;
313*d83cc019SAndroid Build Coastguard Worker 	double error_rate_threshold = 0.01;
314*d83cc019SAndroid Build Coastguard Worker 	double error_rate;
315*d83cc019SAndroid Build Coastguard Worker 	unsigned int span = 2;
316*d83cc019SAndroid Build Coastguard Worker 	bool match = false;
317*d83cc019SAndroid Build Coastguard Worker 
318*d83cc019SAndroid Build Coastguard Worker 	width = cairo_image_surface_get_width(reference);
319*d83cc019SAndroid Build Coastguard Worker 	height = cairo_image_surface_get_height(reference);
320*d83cc019SAndroid Build Coastguard Worker 
321*d83cc019SAndroid Build Coastguard Worker 	ref_stride = cairo_image_surface_get_stride(reference);
322*d83cc019SAndroid Build Coastguard Worker 	ref_data = cairo_image_surface_get_data(reference);
323*d83cc019SAndroid Build Coastguard Worker 	igt_assert(ref_data);
324*d83cc019SAndroid Build Coastguard Worker 
325*d83cc019SAndroid Build Coastguard Worker 	cap_stride = cairo_image_surface_get_stride(capture);
326*d83cc019SAndroid Build Coastguard Worker 	cap_data = cairo_image_surface_get_data(capture);
327*d83cc019SAndroid Build Coastguard Worker 	igt_assert(cap_data);
328*d83cc019SAndroid Build Coastguard Worker 
329*d83cc019SAndroid Build Coastguard Worker 	edges_map = calloc(1, width * height);
330*d83cc019SAndroid Build Coastguard Worker 	igt_assert(edges_map);
331*d83cc019SAndroid Build Coastguard Worker 
332*d83cc019SAndroid Build Coastguard Worker 	/* First pass to detect the pattern edges. */
333*d83cc019SAndroid Build Coastguard Worker 	for (y = 0; y < height; y++) {
334*d83cc019SAndroid Build Coastguard Worker 		if (y < span || y > (height - span - 1))
335*d83cc019SAndroid Build Coastguard Worker 			continue;
336*d83cc019SAndroid Build Coastguard Worker 
337*d83cc019SAndroid Build Coastguard Worker 		for (x = 0; x < width; x++) {
338*d83cc019SAndroid Build Coastguard Worker 			unsigned int xdiff = 0, ydiff = 0;
339*d83cc019SAndroid Build Coastguard Worker 
340*d83cc019SAndroid Build Coastguard Worker 			if (x < span || x > (width - span - 1))
341*d83cc019SAndroid Build Coastguard Worker 				continue;
342*d83cc019SAndroid Build Coastguard Worker 
343*d83cc019SAndroid Build Coastguard Worker 			for (c = 0; c < 3; c++) {
344*d83cc019SAndroid Build Coastguard Worker 				xdiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x + span, y, c) -
345*d83cc019SAndroid Build Coastguard Worker 					     XR24_COLOR_VALUE(ref_data, ref_stride, x - span, y, c));
346*d83cc019SAndroid Build Coastguard Worker 				ydiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y + span, c) -
347*d83cc019SAndroid Build Coastguard Worker 					     XR24_COLOR_VALUE(ref_data, ref_stride, x, y - span, c));
348*d83cc019SAndroid Build Coastguard Worker 			}
349*d83cc019SAndroid Build Coastguard Worker 
350*d83cc019SAndroid Build Coastguard Worker 			edges_map[y * width + x] = (xdiff > edge_threshold ||
351*d83cc019SAndroid Build Coastguard Worker 						    ydiff > edge_threshold);
352*d83cc019SAndroid Build Coastguard Worker 		}
353*d83cc019SAndroid Build Coastguard Worker 	}
354*d83cc019SAndroid Build Coastguard Worker 
355*d83cc019SAndroid Build Coastguard Worker 	/* Second pass to detect errors. */
356*d83cc019SAndroid Build Coastguard Worker 	for (y = 0; y < height; y++) {
357*d83cc019SAndroid Build Coastguard Worker 		for (x = 0; x < width; x++) {
358*d83cc019SAndroid Build Coastguard Worker 			bool error = false;
359*d83cc019SAndroid Build Coastguard Worker 
360*d83cc019SAndroid Build Coastguard Worker 			if (edges_map[y * width + x])
361*d83cc019SAndroid Build Coastguard Worker 				continue;
362*d83cc019SAndroid Build Coastguard Worker 
363*d83cc019SAndroid Build Coastguard Worker 			for (c = 0; c < 3; c++) {
364*d83cc019SAndroid Build Coastguard Worker 				unsigned int diff;
365*d83cc019SAndroid Build Coastguard Worker 
366*d83cc019SAndroid Build Coastguard Worker 				/* Compare the reference and capture values. */
367*d83cc019SAndroid Build Coastguard Worker 				diff = abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y, c) -
368*d83cc019SAndroid Build Coastguard Worker 					   XR24_COLOR_VALUE(cap_data, cap_stride, x, y, c));
369*d83cc019SAndroid Build Coastguard Worker 
370*d83cc019SAndroid Build Coastguard Worker 				if (diff > color_error_threshold)
371*d83cc019SAndroid Build Coastguard Worker 					error = true;
372*d83cc019SAndroid Build Coastguard Worker 			}
373*d83cc019SAndroid Build Coastguard Worker 
374*d83cc019SAndroid Build Coastguard Worker 			/* Allow error if coming on or off an edge (on x). */
375*d83cc019SAndroid Build Coastguard Worker 			if (error && x >= span && x <= (width - span - 1) &&
376*d83cc019SAndroid Build Coastguard Worker 			    edges_map[y * width + (x - span)] !=
377*d83cc019SAndroid Build Coastguard Worker 			    edges_map[y * width + (x + span)])
378*d83cc019SAndroid Build Coastguard Worker 				continue;
379*d83cc019SAndroid Build Coastguard Worker 
380*d83cc019SAndroid Build Coastguard Worker 			/* Allow error if coming on or off an edge (on y). */
381*d83cc019SAndroid Build Coastguard Worker 			if (error && y >= span && y <= (height - span - 1) &&
382*d83cc019SAndroid Build Coastguard Worker 			    edges_map[(y - span) * width + x] !=
383*d83cc019SAndroid Build Coastguard Worker 			    edges_map[(y + span) * width + x] && error)
384*d83cc019SAndroid Build Coastguard Worker 				continue;
385*d83cc019SAndroid Build Coastguard Worker 
386*d83cc019SAndroid Build Coastguard Worker 			if (error)
387*d83cc019SAndroid Build Coastguard Worker 				errors++;
388*d83cc019SAndroid Build Coastguard Worker 
389*d83cc019SAndroid Build Coastguard Worker 			pixels++;
390*d83cc019SAndroid Build Coastguard Worker 		}
391*d83cc019SAndroid Build Coastguard Worker 	}
392*d83cc019SAndroid Build Coastguard Worker 
393*d83cc019SAndroid Build Coastguard Worker 	free(edges_map);
394*d83cc019SAndroid Build Coastguard Worker 
395*d83cc019SAndroid Build Coastguard Worker 	error_rate = (double) errors / pixels;
396*d83cc019SAndroid Build Coastguard Worker 
397*d83cc019SAndroid Build Coastguard Worker 	if (error_rate < error_rate_threshold)
398*d83cc019SAndroid Build Coastguard Worker 		match = true;
399*d83cc019SAndroid Build Coastguard Worker 
400*d83cc019SAndroid Build Coastguard Worker 	igt_debug("Checkerboard pattern %s with error rate %f %%\n",
401*d83cc019SAndroid Build Coastguard Worker 		  match ? "matched" : "not matched", error_rate * 100);
402*d83cc019SAndroid Build Coastguard Worker 
403*d83cc019SAndroid Build Coastguard Worker 	return match;
404*d83cc019SAndroid Build Coastguard Worker }
405