xref: /aosp_15_r20/external/igt-gpu-tools/tests/kms_plane_cursor.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright 2019 Advanced Micro Devices, Inc.
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 shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "igt.h"
24 
25 /*
26  * This file tests cursor interactions with primary and overlay planes.
27  *
28  * Some assumptions made:
29  * - Cursor planes can be placed on top of overlay planes
30  * - DRM index indicates z-ordering, higher index = higher z-order
31  */
32 
33 typedef struct {
34 	int x;
35 	int y;
36 } pos_t;
37 
38 typedef struct {
39 	int x;
40 	int y;
41 	int w;
42 	int h;
43 } rect_t;
44 
45 /* Common test data. */
46 typedef struct data {
47 	igt_display_t display;
48 	igt_plane_t *primary;
49 	igt_plane_t *overlay;
50 	igt_plane_t *cursor;
51 	igt_output_t *output;
52 	igt_pipe_t *pipe;
53 	igt_pipe_crc_t *pipe_crc;
54 	drmModeModeInfo *mode;
55 	enum pipe pipe_id;
56 	int drm_fd;
57 	rect_t or;
58 } data_t;
59 
60 /* Common test setup. */
test_init(data_t * data,enum pipe pipe_id)61 static void test_init(data_t *data, enum pipe pipe_id)
62 {
63 	igt_display_t *display = &data->display;
64 
65 	data->pipe_id = pipe_id;
66 	data->pipe = &data->display.pipes[data->pipe_id];
67 
68 	igt_display_reset(display);
69 
70 	data->output = igt_get_single_output_for_pipe(&data->display, pipe_id);
71 	igt_require(data->output);
72 
73 	data->mode = igt_output_get_mode(data->output);
74 
75 	data->primary = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_PRIMARY);
76 	data->overlay = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_OVERLAY);
77 	data->cursor = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_CURSOR);
78 
79 	data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe_id,
80 					  INTEL_PIPE_CRC_SOURCE_AUTO);
81 
82 	igt_output_set_pipe(data->output, data->pipe_id);
83 
84 	/* Overlay rectangle for a rect in the center of the screen */
85 	data->or.x = data->mode->hdisplay / 4;
86 	data->or.y = data->mode->vdisplay / 4;
87 	data->or.w = data->mode->hdisplay / 2;
88 	data->or.h = data->mode->vdisplay / 2;
89 }
90 
91 /* Common test cleanup. */
test_fini(data_t * data)92 static void test_fini(data_t *data)
93 {
94 	igt_pipe_crc_free(data->pipe_crc);
95 	igt_display_reset(&data->display);
96 }
97 
98 /* Fills a FB with the solid color given. */
draw_color(igt_fb_t * fb,double r,double g,double b)99 static void draw_color(igt_fb_t *fb, double r, double g, double b)
100 {
101 	cairo_t *cr = igt_get_cairo_ctx(fb->fd, fb);
102 
103 	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
104 	igt_paint_color(cr, 0, 0, fb->width, fb->height, r, g, b);
105 	igt_put_cairo_ctx(fb->fd, fb, cr);
106 }
107 
108 /*
109  * Draws white and gray (if overlay FB is given) on the primary FB.
110  * Draws a magenta square where the cursor should be over top both planes.
111  * Takes this as the reference CRC.
112  *
113  * Draws white on the primary FB and gray on the overlay FB if given.
114  * Places the cursor where the magenta square should be with a magenta FB.
115  * Takes this as the test CRC and compares it to the reference.
116  */
test_cursor_pos(data_t * data,igt_fb_t * pfb,igt_fb_t * ofb,igt_fb_t * cfb,const rect_t * or,int x,int y)117 static void test_cursor_pos(data_t *data, igt_fb_t *pfb, igt_fb_t *ofb,
118 			    igt_fb_t *cfb, const rect_t *or, int x, int y)
119 {
120 	igt_crc_t ref_crc, test_crc;
121 	cairo_t *cr;
122 	int cw = cfb->width;
123 	int ch = cfb->height;
124 
125 	cr = igt_get_cairo_ctx(pfb->fd, pfb);
126 	igt_paint_color(cr, 0, 0, pfb->width, pfb->height, 1.0, 1.0, 1.0);
127 
128 	if (ofb)
129 		igt_paint_color(cr, or->x, or->y, or->w, or->h, 0.5, 0.5, 0.5);
130 
131 	igt_paint_color(cr, x, y, cw, ch, 1.0, 0.0, 1.0);
132 	igt_put_cairo_ctx(pfb->fd, pfb, cr);
133 
134 	igt_plane_set_fb(data->overlay, NULL);
135 	igt_plane_set_fb(data->cursor, NULL);
136 	igt_display_commit_atomic(&data->display, 0, NULL);
137 
138 	igt_pipe_crc_start(data->pipe_crc);
139 	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &ref_crc);
140 
141 	draw_color(pfb, 1.0, 1.0, 1.0);
142 
143 	if (ofb) {
144 		igt_plane_set_fb(data->overlay, ofb);
145 		igt_plane_set_position(data->overlay, or->x, or->y);
146 		igt_plane_set_size(data->overlay, or->w, or->h);
147 		igt_fb_set_size(ofb, data->overlay, or->w, or->h);
148 		igt_fb_set_position(ofb, data->overlay,
149 				    (ofb->width - or->w) / 2,
150 				    (ofb->height - or->h) / 2);
151 	}
152 
153 	igt_plane_set_fb(data->cursor, cfb);
154 	igt_plane_set_position(data->cursor, x, y);
155 	igt_display_commit_atomic(&data->display, 0, NULL);
156 
157 	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &test_crc);
158 	igt_pipe_crc_stop(data->pipe_crc);
159 
160 	igt_assert_crc_equal(&ref_crc, &test_crc);
161 }
162 
163 /*
164  * Tests the cursor on a variety of positions on the screen.
165  * Specific edge cases that should be captured here are the negative edges
166  * of each plane and the centers.
167  */
test_cursor_spots(data_t * data,igt_fb_t * pfb,igt_fb_t * ofb,igt_fb_t * cfb,const rect_t * or,int size)168 static void test_cursor_spots(data_t *data, igt_fb_t *pfb, igt_fb_t *ofb,
169 			      igt_fb_t *cfb, const rect_t *or, int size)
170 {
171 	int sw = data->mode->hdisplay;
172 	int sh = data->mode->vdisplay;
173 	int i;
174 	const pos_t pos[] = {
175 		/* Test diagonally from top left to bottom right. */
176 		{ -size / 3, -size / 3 },
177 		{ 0, 0 },
178 		{ or->x - size, or->y - size },
179 		{ or->x - size / 3, or->y - size / 3 },
180 		{ or->x, or->y },
181 		{ or->x + size, or->y + size },
182 		{ sw / 2, sh / 2 },
183 		{ or->x + or->w - size, or->y + or->h - size },
184 		{ or->x + or->w - size / 3, or->y + or->h - size / 3 },
185 		{ or->x + or->w + size, or->y + or->h + size },
186 		{ sw - size, sh - size },
187 		{ sw - size / 3, sh - size / 3 },
188 		/* Test remaining corners. */
189 		{ sw - size, 0 },
190 		{ 0, sh - size },
191 		{ or->x + or->w - size, or->y },
192 		{ or->x, or->y + or->h - size }
193 	};
194 
195 	for (i = 0; i < ARRAY_SIZE(pos); ++i) {
196 		test_cursor_pos(data, pfb, ofb, cfb, or, pos[i].x, pos[i].y);
197 	}
198 }
199 
200 /*
201  * Tests atomic cursor positioning on a primary and overlay plane.
202  * Assumes the cursor can be placed on top of the overlay.
203  */
test_cursor_overlay(data_t * data,int size,enum pipe pipe_id)204 static void test_cursor_overlay(data_t *data, int size, enum pipe pipe_id)
205 {
206 	igt_fb_t pfb, ofb, cfb;
207 	int sw, sh;
208 
209 	test_init(data, pipe_id);
210 	igt_require(data->overlay);
211 
212 	sw = data->mode->hdisplay;
213 	sh = data->mode->vdisplay;
214 
215 	igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888, 0,
216 			    1.0, 1.0, 1.0, &pfb);
217 
218 	igt_create_color_fb(data->drm_fd, data->or.w, data->or.h,
219 			    DRM_FORMAT_XRGB8888, 0, 0.5, 0.5, 0.5, &ofb);
220 
221 	igt_create_color_fb(data->drm_fd, size, size, DRM_FORMAT_ARGB8888, 0,
222 			    1.0, 0.0, 1.0, &cfb);
223 
224 	igt_plane_set_fb(data->primary, &pfb);
225 	igt_display_commit2(&data->display, COMMIT_ATOMIC);
226 
227 	test_cursor_spots(data, &pfb, &ofb, &cfb, &data->or, size);
228 
229 	test_fini(data);
230 
231 	igt_remove_fb(data->drm_fd, &cfb);
232 	igt_remove_fb(data->drm_fd, &ofb);
233 	igt_remove_fb(data->drm_fd, &pfb);
234 }
235 
236 /* Tests atomic cursor positioning on a primary plane. */
test_cursor_primary(data_t * data,int size,enum pipe pipe_id)237 static void test_cursor_primary(data_t *data, int size, enum pipe pipe_id)
238 {
239 	igt_fb_t pfb, cfb;
240 	int sw, sh;
241 
242 	test_init(data, pipe_id);
243 
244 	sw = data->mode->hdisplay;
245 	sh = data->mode->vdisplay;
246 
247 	igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888, 0,
248 			    1.0, 1.0, 1.0, &pfb);
249 
250 	igt_create_color_fb(data->drm_fd, size, size, DRM_FORMAT_ARGB8888, 0,
251 			    1.0, 0.0, 1.0, &cfb);
252 
253 	igt_plane_set_fb(data->primary, &pfb);
254 	igt_display_commit2(&data->display, COMMIT_ATOMIC);
255 
256 	test_cursor_spots(data, &pfb, NULL, &cfb, &data->or, size);
257 
258 	test_fini(data);
259 
260 	igt_remove_fb(data->drm_fd, &cfb);
261 	igt_remove_fb(data->drm_fd, &pfb);
262 }
263 
264 /*
265  * Tests atomic cursor positioning on a primary and overlay plane.
266  * The overlay's buffer is larger than the viewport actually used
267  * for display.
268  */
test_cursor_viewport(data_t * data,int size,enum pipe pipe_id)269 static void test_cursor_viewport(data_t *data, int size, enum pipe pipe_id)
270 {
271 	igt_fb_t pfb, ofb, cfb;
272 	int sw, sh;
273 	int pad = 128;
274 
275 	test_init(data, pipe_id);
276 	igt_require(data->overlay);
277 
278 	sw = data->mode->hdisplay;
279 	sh = data->mode->vdisplay;
280 
281 	igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888, 0,
282 			    1.0, 1.0, 1.0, &pfb);
283 
284 	igt_create_color_fb(data->drm_fd, data->or.w + pad, data->or.h + pad,
285 			    DRM_FORMAT_XRGB8888, 0, 0.5, 0.5, 0.5, &ofb);
286 
287 	igt_create_color_fb(data->drm_fd, size, size, DRM_FORMAT_ARGB8888, 0,
288 			    1.0, 0.0, 1.0, &cfb);
289 
290 	igt_plane_set_fb(data->primary, &pfb);
291 	igt_display_commit2(&data->display, COMMIT_ATOMIC);
292 
293 	test_cursor_spots(data, &pfb, &ofb, &cfb, &data->or, size);
294 
295 	test_fini(data);
296 
297 	igt_remove_fb(data->drm_fd, &cfb);
298 	igt_remove_fb(data->drm_fd, &ofb);
299 	igt_remove_fb(data->drm_fd, &pfb);
300 }
301 
302 igt_main
303 {
304 	static const int cursor_sizes[] = { 64, 128, 256 };
305 	data_t data = { 0 };
306 	enum pipe pipe;
307 	int i;
308 
309 	igt_skip_on_simulation();
310 
311 	igt_fixture {
312 		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
313 
314 		kmstest_set_vt_graphics_mode();
315 
316 		igt_display_require(&data.display, data.drm_fd);
317 		igt_require(data.display.is_atomic);
318 		igt_display_require_output(&data.display);
319 	}
320 
321 	for_each_pipe_static(pipe)
322 		for (i = 0; i < ARRAY_SIZE(cursor_sizes); ++i) {
323 			int size = cursor_sizes[i];
324 
325 			igt_subtest_f("pipe-%s-overlay-size-%d",
326 				      kmstest_pipe_name(pipe), size)
327 				test_cursor_overlay(&data, size, pipe);
328 
329 			igt_subtest_f("pipe-%s-primary-size-%d",
330 				      kmstest_pipe_name(pipe), size)
331 				test_cursor_primary(&data, size, pipe);
332 
333 			igt_subtest_f("pipe-%s-viewport-size-%d",
334 				      kmstest_pipe_name(pipe), size)
335 				test_cursor_viewport(&data, size, pipe);
336 		}
337 
338 	igt_fixture {
339 		igt_display_fini(&data.display);
340 	}
341 }
342