amd-gfx.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH i-g-t v3] tests/amdgpu: Add test for native cursor fallback to overlay
@ 2025-07-23 17:27 IVAN.LIPSKI
  2025-08-21 18:28 ` Leo Li
  0 siblings, 1 reply; 2+ messages in thread
From: IVAN.LIPSKI @ 2025-07-23 17:27 UTC (permalink / raw)
  To: igt-dev; +Cc: sunpeng.li, harry.wentland, amd-gfx, Ivan Lipski

From: Ivan Lipski <ivan.lipski@amd.com>

[Why & How]
The AMD display hardware does not use dedicated cursor planes.
Instead, the cursor is rendered either using the primary plane
or an available overlay plane. This test verifies that the
cursor correctly falls back from native to overlay mode
when the underneath primary plane is incompatible. It also tests

It has 5 subtests:

rgb-to-yuv
Switches the primary plane to a NV12 format FB and verifies that
the cursor falls back from native to overlay.

non-full
Switches the primary planeto a FB that does not fill the entire CRTC
(currently sized at a quarter of the CRTC).

scaling-[50,75,125,150,175,200]
Switches the primary plane to a FB with a chosen scaling (50%-200%), which
is then filled in the CRTC.

max-planes
Enables 2 overlay planes, a primary plane and a cursor. Then
switches the primary plane to YUV to cause the cursor to fall back to
overlay. Verifies that the cursor can still fallback to overlay when 4
planes are enabled.

no-available-planes
Enables 3 available overlay planes, a primary plane and a cursor. Then
switches the primary plane to YUV to cause the cursor to fall back to
overlay. Verifies that the atomic commit fails due to no available overlay
planes.

Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
---
 tests/amdgpu/amd_cursor_overlay.c | 484 ++++++++++++++++++++++++++++++
 tests/amdgpu/meson.build          |   1 +
 2 files changed, 485 insertions(+)
 create mode 100644 tests/amdgpu/amd_cursor_overlay.c

diff --git a/tests/amdgpu/amd_cursor_overlay.c b/tests/amdgpu/amd_cursor_overlay.c
new file mode 100644
index 000000000..cf5ea83a0
--- /dev/null
+++ b/tests/amdgpu/amd_cursor_overlay.c
@@ -0,0 +1,484 @@
+// SPDX-License-Identifier: MIT
+// Copyright 2025 Advanced Micro Devices, Inc.
+
+#include "igt.h"
+#include "igt_kms.h"
+
+/**
+ * TEST: amd_cursor_overlay
+ * Category: Display
+ * Description: Tests cursor fall back from native to overlay
+ * Driver requirement: amdgpu
+ */
+
+/**
+ * SUBTEST: rgb-to-yuv
+ * Description: Tests native cursor fall back to overlay cursor when a top plane
+ *				switches from RGB to YUV.
+ * SUBTEST: non-full
+ * Description: Tests native cursor fall back to overlay cursor when a top plane
+ *				does not fill the crtc.
+ * SUBTEST: scaling-%d
+ * Description: Tests native cursor fall back to overlay cursor when a top plane
+ *				is scaled.
+ *
+ * arg[1].values: 50, 75, 125, 150, 175, 200
+ *
+ * SUBTEST: max-planes
+ * Description: Tests native cursor fall back to overlay cursor when a top plane
+ *				is YUV and there are two overlay planes used.
+ *
+ * SUBTEST: no-available-planes
+ * Description: Tests native cursor attempt to fall back to overlay cursor required,
+ *				but fails atomic commit due to no available overlay planes.
+ */
+
+enum {
+	TEST_YUV = 1,
+	TEST_QUARTER_FB = 1 << 1,
+	TEST_SCALING = 1 << 2,
+	TEST_MAX_PLANES = 1 << 3,
+	TEST_NO_PLANES = 1 << 4,
+};
+
+typedef struct {
+	int x;
+	int y;
+} pos_t;
+
+/* Common test data. */
+typedef struct data {
+	igt_display_t display;
+	igt_plane_t *primary;
+	igt_plane_t *cursor;
+	igt_plane_t *overlay1;
+	igt_plane_t *overlay2;
+	igt_plane_t *overlay3;
+	igt_output_t *output;
+	igt_pipe_t *pipe;
+	igt_pipe_crc_t *pipe_crc;
+	drmModeModeInfo *mode;
+	igt_fb_t rgb_fb;
+	igt_fb_t yuv_fb;
+	igt_fb_t rgb_fb_o1;
+	igt_fb_t rgb_fb_o2;
+	igt_fb_t rgb_fb_o3;
+	igt_fb_t quarter_fb;
+	igt_fb_t scale_fb;
+	igt_fb_t cfb;
+	enum pipe pipe_id;
+	int drm_fd;
+	uint64_t max_curw;
+	uint64_t max_curh;
+} data_t;
+
+/* Retuns the number of available overlay planes */
+static int get_overlay_planes_count(igt_display_t *display, enum pipe pipe)
+{
+	int count = 0;
+	igt_plane_t *plane;
+
+	for_each_plane_on_pipe(display, pipe, plane)
+		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+			count++;
+
+	return count;
+}
+
+/* Common test setup. */
+static void test_init(data_t *data, enum pipe pipe_id, igt_output_t *output,
+		      unsigned int flags)
+{
+	data->pipe_id = pipe_id;
+	data->pipe = &data->display.pipes[data->pipe_id];
+	data->output = output;
+	data->mode = igt_output_get_mode(data->output);
+	data->primary = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_PRIMARY);
+	data->cursor = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_CURSOR);
+
+	if (flags & TEST_NO_PLANES || flags & TEST_MAX_PLANES) {
+		data->overlay1 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 0);
+		data->overlay2 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 1);
+	}
+	if (flags & TEST_NO_PLANES)
+		data->overlay3 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 2);
+
+	igt_info("Using (pipe %s + %s) to run the subtest.\n",
+		 kmstest_pipe_name(data->pipe_id), igt_output_name(data->output));
+
+	igt_require_pipe_crc(data->drm_fd);
+	data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe_id,
+					  IGT_PIPE_CRC_SOURCE_AUTO);
+}
+
+/* Common test finish. */
+static void test_fini(data_t *data)
+{
+	igt_pipe_crc_free(data->pipe_crc);
+	igt_display_reset(&data->display);
+	igt_plane_set_fb(data->primary, NULL);
+	igt_plane_set_fb(data->cursor, NULL);
+	if (data->overlay1)
+		igt_plane_set_fb(data->overlay1, NULL);
+	if (data->overlay2)
+		igt_plane_set_fb(data->overlay2, NULL);
+	if (data->overlay3)
+                igt_plane_set_fb(data->overlay3, NULL);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+/* Common test cleanup. */
+static void test_cleanup(data_t *data)
+{
+	igt_remove_fb(data->drm_fd, &data->cfb);
+	igt_remove_fb(data->drm_fd, &data->rgb_fb);
+	igt_remove_fb(data->drm_fd, &data->yuv_fb);
+	if (data->overlay1)
+		igt_remove_fb(data->drm_fd, &data->rgb_fb_o1);
+	if (data->overlay2)
+		igt_remove_fb(data->drm_fd, &data->rgb_fb_o2);
+	if (data->overlay3)
+		igt_remove_fb(data->drm_fd, &data->rgb_fb_o3);
+	igt_remove_fb(data->drm_fd, &data->quarter_fb);
+	igt_remove_fb(data->drm_fd, &data->scale_fb);
+}
+
+
+static void test_cursor_pos(data_t *data, int x, int y, unsigned int flags, unsigned int scaling_factor)
+{
+	igt_crc_t ref_crc, test_crc;
+	cairo_t *cr;
+	igt_fb_t *rgb_fb = &data->rgb_fb;
+	igt_fb_t *yuv_fb = &data->yuv_fb;
+	igt_fb_t *quarter_fb = &data->quarter_fb;
+	igt_fb_t *rgb_fb_o1 = &data->rgb_fb_o1;
+	igt_fb_t *rgb_fb_o2 = &data->rgb_fb_o2;
+	igt_fb_t *rgb_fb_o3 = &data->rgb_fb_o3;
+	igt_fb_t *cfb = &data->cfb;
+	int cw = cfb->width;
+	int ch = cfb->height;
+	int ret;
+
+
+	cr = igt_get_cairo_ctx(rgb_fb->fd, rgb_fb);
+
+	igt_plane_set_fb(data->primary, rgb_fb);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	igt_paint_color(cr, 0, 0, rgb_fb->width, rgb_fb->height, 0.0, 0.0, 0.0);
+
+	/* Draw a magenta square where the cursor should be. */
+	igt_paint_color(cr, x, y, cw, ch, 1.0, 0.0, 1.0);
+	igt_put_cairo_ctx(cr);
+
+	if (flags & TEST_MAX_PLANES) {
+		/* Display the overlay planes. */
+		igt_plane_set_fb(data->overlay1, rgb_fb_o1);
+		igt_plane_set_position(data->overlay1, 0, 0);
+		igt_plane_set_fb(data->overlay2, rgb_fb_o2);
+		igt_plane_set_position(data->overlay2, data->rgb_fb_o1.width, data->rgb_fb_o1.height);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Display the cursor. */
+		igt_plane_set_fb(data->cursor, cfb);
+		igt_plane_set_position(data->cursor, x, y);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Trigger cursor fall back due to a YUV plane;
+		 * expect the atomic commit to fail due to no
+		 * available overlay planes.
+		 */
+		igt_plane_set_fb(data->primary, &data->yuv_fb);
+		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
+
+		/* Remove the overlay planes. */
+		igt_plane_set_fb(data->overlay1, NULL);
+		igt_plane_set_fb(data->overlay2, NULL);
+		igt_plane_set_fb(data->cursor, NULL);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Expected atomic commit to fail due to no available overlay planes. */
+		igt_assert_f(ret == 0, "Expected atomic commit to succeed.\n");
+		return;
+	}
+	if (flags & TEST_NO_PLANES) {
+
+		/* Display the overlay planes. */
+		igt_plane_set_fb(data->overlay1, rgb_fb_o1);
+		igt_plane_set_position(data->overlay1, 0, 0);
+		igt_plane_set_fb(data->overlay2, rgb_fb_o2);
+		igt_plane_set_position(data->overlay2, data->rgb_fb_o1.width, data->rgb_fb_o1.height);
+		igt_plane_set_fb(data->overlay3, rgb_fb_o3);
+                igt_plane_set_position(data->overlay3, data->rgb_fb_o1.width, 0);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Display the cursor. */
+		igt_plane_set_fb(data->cursor, cfb);
+		igt_plane_set_position(data->cursor, x, y);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Trigger cursor fall back due to a YUV plane;
+		 * expect the atomic commit to fail due to no
+		 * available overlay planes.
+		 */
+		igt_plane_set_fb(data->primary, &data->yuv_fb);
+		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
+
+		/* Remove the overlay planes. */
+		igt_plane_set_fb(data->overlay1, NULL);
+		igt_plane_set_fb(data->overlay2, NULL);
+		igt_plane_set_fb(data->overlay3, NULL);
+		igt_plane_set_fb(data->cursor, NULL);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+		/* Expected atomic commit to fail due to no available overlay planes. */
+		igt_assert_f(ret == -EINVAL, "Expected atomic commit to fail due to no available overlay planes.\n");
+		return;
+	}
+
+	/* Display the cursor. */
+	igt_plane_set_fb(data->cursor, cfb);
+	igt_plane_set_position(data->cursor, x, y);
+	igt_display_commit_atomic(&data->display, 0, NULL);
+
+	/** Record a reference CRC */
+	igt_pipe_crc_start(data->pipe_crc);
+	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &ref_crc);
+
+	/* Switch primary plane to use YUV Fb. */
+	if (flags & TEST_YUV) {
+		igt_plane_set_fb(data->primary, yuv_fb);
+		igt_plane_set_position(data->primary, 0, 0);
+		igt_plane_set_size(data->primary, yuv_fb->width, yuv_fb->height);
+		igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
+
+	/* Switch primary plane to use a quarter-sized FB. */
+	} else if (flags & TEST_QUARTER_FB) {
+		igt_plane_set_fb(data->primary, quarter_fb);
+		igt_plane_set_position(data->primary, 0, 0);
+		igt_display_commit_atomic(&data->display, 0, NULL);
+
+	/* Switch primary plane to use a scaled FB. */
+	} else if (flags & TEST_SCALING) {
+		igt_create_fb(data->drm_fd,
+			data->rgb_fb.width * scaling_factor / 100,
+			data->rgb_fb.height * scaling_factor / 100,
+			DRM_FORMAT_XRGB8888,
+			DRM_FORMAT_MOD_LINEAR, &data->scale_fb);
+
+		igt_plane_set_fb(data->primary, &data->scale_fb);
+		igt_plane_set_position(data->primary, 0, 0);
+		igt_plane_set_size(data->primary, data->mode->hdisplay, data->mode->vdisplay);
+
+		igt_display_commit_atomic(&data->display, 0, NULL);
+	}
+
+	/* Wait for one more vblank since cursor updates are not
+	 * synchronized to the same frame on AMD hw.
+	 */
+	if (is_amdgpu_device(data->drm_fd))
+		igt_wait_for_vblank_count(data->drm_fd, data->display.pipes[data->pipe_id].crtc_offset, 1);
+
+	/* Record the new CRC. */
+	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &test_crc);
+	igt_pipe_crc_stop(data->pipe_crc);
+
+	/** CRC Check is sufficient for this test */
+	igt_assert_crc_equal(&ref_crc, &test_crc);
+}
+
+/*
+ * Tests the cursor on a variety of positions on the screen.
+ * Specific edge cases that should be captured here are the negative edges
+ * of each plane and the centers.
+ */
+static void test_cursor_spots(data_t *data, int size, unsigned int flags, unsigned int scaling_factor)
+{
+	int sw = data->mode->hdisplay;
+	int sh = data->mode->vdisplay;
+	int i;
+		const pos_t pos[] = {
+		/* Test diagonally from top left to bottom right. */
+		{ -size / 3, -size / 3 },
+		{ 0, 0 },
+		{ sw / 4 - size, sh / 4 - size },
+		{ sw / 4 - size / 3, sh / 4 - size / 3 },
+		{ sw / 4, sh / 4 },
+		{ sw / 4 + size, sh / 4 + size },
+		{ sw / 2, sh / 2 },
+		{ sw / 4 + sw / 2 - size, sh / 4 + sh / 2 - size },
+		{ sw / 4 + sw / 2 - size / 3, sh / 4 + sh / 2 - size / 3 },
+		{ sw / 4 + sw / 2 + size, sh / 4 + sh / 2 + size },
+		{ sw - size, sh - size },
+		{ sw - size / 3, sh - size / 3 },
+		/* Test remaining corners. */
+		{ sw - size, 0 },
+		{ 0, sh - size },
+		{ sw / 4 + sw / 2 - size, sh / 4 },
+		{ sw / 4, sh / 4 + sh / 2 - size }
+	};
+
+	for (i = 0; i < ARRAY_SIZE(pos); ++i)
+		test_cursor_pos(data, pos[i].x, pos[i].y, flags, scaling_factor);
+}
+
+static void test_cursor(data_t *data, int size, unsigned int flags, unsigned int scaling_factor)
+{
+	int sw, sh;
+
+	igt_skip_on(size > data->max_curw || size > data->max_curh);
+
+	sw = data->mode->hdisplay;
+	sh = data->mode->vdisplay;
+
+	test_cleanup(data);
+
+	/* Create RGB FB for reference. */
+	igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888,
+			    DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->rgb_fb);
+
+	/* Create YUV FB for RGB-to-YUV, MAX_PLANES and NO_PLANES subtests */
+	if (flags & TEST_YUV || flags & TEST_MAX_PLANES ||flags & TEST_NO_PLANES)
+		igt_create_fb(data->drm_fd, sw, sh, DRM_FORMAT_NV12,
+					DRM_FORMAT_MOD_NONE, &data->yuv_fb);
+
+	/* Create a quarter-sized empty FB. */
+	if (flags & TEST_QUARTER_FB)
+		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
+					DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->quarter_fb);
+
+	/* Create two RGB FBs for the overlay planes. */
+	if (flags & TEST_MAX_PLANES || flags & TEST_NO_PLANES) {
+		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
+			DRM_FORMAT_MOD_NONE, 0.5, 0.0, 0.0, &data->rgb_fb_o1);
+		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
+			DRM_FORMAT_MOD_NONE, 0.0, 0.5, 0.0, &data->rgb_fb_o2);
+	}
+
+	/* Create a third overlay plane for TEST_NO_PLANES test */
+	if (flags & TEST_NO_PLANES) {
+		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
+			DRM_FORMAT_MOD_NONE, 0.0, 0.0, 0.5, &data->rgb_fb_o3);
+	}
+
+	/* Create a FB for scaling. */
+	if (flags & TEST_SCALING)
+		igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888,
+					DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->scale_fb);
+
+	/* Create a cursor FB. */
+	igt_create_color_fb(data->drm_fd, size, size, DRM_FORMAT_ARGB8888,
+				DRM_FORMAT_MOD_LINEAR, 1.0, 0.0, 1.0, &data->cfb);
+
+	igt_output_set_pipe(data->output, data->pipe_id);
+
+	/* Run the test for different cursor spots. */
+	test_cursor_spots(data, size, flags, scaling_factor);
+
+}
+
+igt_main
+{
+	static const int cursor_sizes[] = { 64, 128, 256 };
+	data_t data = { .max_curw = 64, .max_curh = 64 };
+	enum pipe pipe;
+	igt_output_t *output;
+	igt_display_t *display;
+	int i, j;
+	struct {
+		const char *name;
+		unsigned int flags;
+		unsigned int scale_factor;
+		const char *desc;
+	} tests[] = {
+		{ "rgb-to-yuv", TEST_YUV, 100,
+		"Tests native cursor fall back to overlay cursor when a top plane switches from RGB to YUV" },
+		{"non-full", TEST_QUARTER_FB, 100,
+		"Tests native cursor fall back to overlay cursor when a top plane does not fill the crtc"},
+		{"max-planes", TEST_MAX_PLANES, 100,
+		"Tests native cursor fall back to overlay cursor when a top plane is YUV and there are two overlay planes used."},
+		{"no-available-planes", TEST_NO_PLANES, 100,
+		"Tests native cursor attempt to fall back to overlay cursor required, but fails atomic commit due to no available overlay planes."},
+		{"scaling-50", TEST_SCALING, 50,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+		{"scaling-75", TEST_SCALING, 75,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+		{"scaling-125", TEST_SCALING, 125,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+		{"scaling-150", TEST_SCALING, 150,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+		{"scaling-175", TEST_SCALING, 175,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+		{"scaling-200", TEST_SCALING, 200,
+		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
+	};
+
+	igt_fixture {
+		int ret;
+
+		data.drm_fd = drm_open_driver_master(DRIVER_AMDGPU);
+
+		igt_display_require(&data.display, data.drm_fd);
+		igt_require(data.display.is_atomic);
+		igt_display_require_output(&data.display);
+		display = &data.display;
+
+		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_WIDTH, &data.max_curw);
+		igt_assert(ret == 0 || errno == EINVAL);
+		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_HEIGHT, &data.max_curh);
+		igt_assert(ret == 0 || errno == EINVAL);
+
+		kmstest_set_vt_graphics_mode();
+	}
+
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		igt_describe_f("%s", tests[i].desc);
+		igt_subtest_with_dynamic_f("%s", tests[i].name) {
+
+			/* Skip YUV and NO_PLANES subtests if YUV is not supported*/
+			if (tests[i].flags & TEST_YUV || tests[i].flags & TEST_NO_PLANES)
+				igt_require(igt_display_has_format_mod(display,
+							DRM_FORMAT_NV12,
+							DRM_FORMAT_MOD_LINEAR));
+
+			for_each_pipe_with_single_output(&data.display, pipe, output) {
+
+				igt_display_reset(display);
+
+				igt_output_set_pipe(output, pipe);
+				if (!intel_pipe_output_combo_valid(display))
+					continue;
+
+				/* Skip TEST_NO_PLANES if the ASIC has less than 3 overlay planes*/
+				if (tests[i].flags & TEST_NO_PLANES)
+					igt_require(get_overlay_planes_count(&data.display, pipe) >= 3);
+
+				/* Skip TEST_MAX_PLANES if the ASIC has less than 2 overlay planes*/
+				if (tests[i].flags & TEST_MAX_PLANES)
+					igt_require(get_overlay_planes_count(&data.display, pipe) >= 2);
+
+				test_init(&data, pipe, output, tests[i].flags);
+
+				for (j = 0; j < ARRAY_SIZE(cursor_sizes); j++) {
+					int size = cursor_sizes[j];
+
+					igt_dynamic_f("pipe-%s-%s-size-%d",
+						      kmstest_pipe_name(pipe),
+						      igt_output_name(output),
+						      size)
+						test_cursor(&data, size, tests[i].flags, tests[i].scale_factor);
+
+					test_cleanup(&data);
+				}
+
+				test_fini(&data);
+			}
+		}
+	}
+
+	igt_fixture {
+		igt_display_fini(&data.display);
+		drm_close_driver(data.drm_fd);
+	}
+}
diff --git a/tests/amdgpu/meson.build b/tests/amdgpu/meson.build
index 421e686d3..b9f1842e2 100644
--- a/tests/amdgpu/meson.build
+++ b/tests/amdgpu/meson.build
@@ -10,6 +10,7 @@ if libdrm_amdgpu.found()
 			  'amd_color',
 			  'amd_cp_dma_misc',
 			  'amd_cs_nop',
+			  'amd_cursor_overlay',
 			  'amd_deadlock',
 			  'amd_dp_dsc',
 			  'amd_freesync_video_mode',
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH i-g-t v3] tests/amdgpu: Add test for native cursor fallback to overlay
  2025-07-23 17:27 [PATCH i-g-t v3] tests/amdgpu: Add test for native cursor fallback to overlay IVAN.LIPSKI
@ 2025-08-21 18:28 ` Leo Li
  0 siblings, 0 replies; 2+ messages in thread
From: Leo Li @ 2025-08-21 18:28 UTC (permalink / raw)
  To: IVAN.LIPSKI, igt-dev; +Cc: harry.wentland, amd-gfx



On 2025-07-23 13:27, IVAN.LIPSKI@amd.com wrote:
> From: Ivan Lipski <ivan.lipski@amd.com>
> 
> [Why & How]
> The AMD display hardware does not use dedicated cursor planes.
> Instead, the cursor is rendered either using the primary plane
> or an available overlay plane. This test verifies that the
> cursor correctly falls back from native to overlay mode
> when the underneath primary plane is incompatible. It also tests

The last sentence seems like a typo?

> 
> It has 5 subtests:
> 
> rgb-to-yuv
> Switches the primary plane to a NV12 format FB and verifies that
> the cursor falls back from native to overlay.
> 
> non-full
> Switches the primary planeto a FB that does not fill the entire CRTC
> (currently sized at a quarter of the CRTC).
> 
> scaling-[50,75,125,150,175,200]
> Switches the primary plane to a FB with a chosen scaling (50%-200%), which
> is then filled in the CRTC.
> 
> max-planes
> Enables 2 overlay planes, a primary plane and a cursor. Then
> switches the primary plane to YUV to cause the cursor to fall back to
> overlay. Verifies that the cursor can still fallback to overlay when 4
> planes are enabled.
> 
> no-available-planes
> Enables 3 available overlay planes, a primary plane and a cursor. Then
> switches the primary plane to YUV to cause the cursor to fall back to
> overlay. Verifies that the atomic commit fails due to no available overlay
> planes.
> 
> Signed-off-by: Ivan Lipski <ivan.lipski@amd.com>
> ---
>  tests/amdgpu/amd_cursor_overlay.c | 484 ++++++++++++++++++++++++++++++
>  tests/amdgpu/meson.build          |   1 +
>  2 files changed, 485 insertions(+)
>  create mode 100644 tests/amdgpu/amd_cursor_overlay.c
> 
> diff --git a/tests/amdgpu/amd_cursor_overlay.c b/tests/amdgpu/amd_cursor_overlay.c
> new file mode 100644
> index 000000000..cf5ea83a0
> --- /dev/null
> +++ b/tests/amdgpu/amd_cursor_overlay.c
> @@ -0,0 +1,484 @@
> +// SPDX-License-Identifier: MIT
> +// Copyright 2025 Advanced Micro Devices, Inc.
> +
> +#include "igt.h"
> +#include "igt_kms.h"
> +
> +/**
> + * TEST: amd_cursor_overlay
> + * Category: Display
> + * Description: Tests cursor fall back from native to overlay
> + * Driver requirement: amdgpu
> + */
> +
> +/**
> + * SUBTEST: rgb-to-yuv
> + * Description: Tests native cursor fall back to overlay cursor when a top plane
> + *				switches from RGB to YUV.
> + * SUBTEST: non-full
> + * Description: Tests native cursor fall back to overlay cursor when a top plane
> + *				does not fill the crtc.
> + * SUBTEST: scaling-%d
> + * Description: Tests native cursor fall back to overlay cursor when a top plane
> + *				is scaled.
> + *
> + * arg[1].values: 50, 75, 125, 150, 175, 200
> + *
> + * SUBTEST: max-planes
> + * Description: Tests native cursor fall back to overlay cursor when a top plane
> + *				is YUV and there are two overlay planes used.
> + *
> + * SUBTEST: no-available-planes
> + * Description: Tests native cursor attempt to fall back to overlay cursor required,
> + *				but fails atomic commit due to no available overlay planes.
> + */
> +
> +enum {
> +	TEST_YUV = 1,
> +	TEST_QUARTER_FB = 1 << 1,
> +	TEST_SCALING = 1 << 2,
> +	TEST_MAX_PLANES = 1 << 3,
> +	TEST_NO_PLANES = 1 << 4,

nit: to avoid confusion, s/NO_PLANES/NO_AVAILABLE_PLANES

> +};
> +
> +typedef struct {
> +	int x;
> +	int y;
> +} pos_t;
> +
> +/* Common test data. */
> +typedef struct data {
> +	igt_display_t display;
> +	igt_plane_t *primary;
> +	igt_plane_t *cursor;
> +	igt_plane_t *overlay1;
> +	igt_plane_t *overlay2;
> +	igt_plane_t *overlay3;
> +	igt_output_t *output;
> +	igt_pipe_t *pipe;
> +	igt_pipe_crc_t *pipe_crc;
> +	drmModeModeInfo *mode;
> +	igt_fb_t rgb_fb;
> +	igt_fb_t yuv_fb;
> +	igt_fb_t rgb_fb_o1;
> +	igt_fb_t rgb_fb_o2;
> +	igt_fb_t rgb_fb_o3;
> +	igt_fb_t quarter_fb;
> +	igt_fb_t scale_fb;
> +	igt_fb_t cfb;
> +	enum pipe pipe_id;
> +	int drm_fd;
> +	uint64_t max_curw;
> +	uint64_t max_curh;
> +} data_t;
> +
> +/* Retuns the number of available overlay planes */
> +static int get_overlay_planes_count(igt_display_t *display, enum pipe pipe)
> +{
> +	int count = 0;
> +	igt_plane_t *plane;
> +
> +	for_each_plane_on_pipe(display, pipe, plane)
> +		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> +			count++;
> +
> +	return count;
> +}
> +
> +/* Common test setup. */
> +static void test_init(data_t *data, enum pipe pipe_id, igt_output_t *output,
> +		      unsigned int flags)
> +{
> +	data->pipe_id = pipe_id;
> +	data->pipe = &data->display.pipes[data->pipe_id];
> +	data->output = output;
> +	data->mode = igt_output_get_mode(data->output);
> +	data->primary = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_PRIMARY);
> +	data->cursor = igt_pipe_get_plane_type(data->pipe, DRM_PLANE_TYPE_CURSOR);
> +
> +	if (flags & TEST_NO_PLANES || flags & TEST_MAX_PLANES) {
> +		data->overlay1 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 0);
> +		data->overlay2 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 1);
> +	}
> +	if (flags & TEST_NO_PLANES)
> +		data->overlay3 = igt_pipe_get_plane_type_index(data->pipe, DRM_PLANE_TYPE_OVERLAY, 2);
> +
> +	igt_info("Using (pipe %s + %s) to run the subtest.\n",
> +		 kmstest_pipe_name(data->pipe_id), igt_output_name(data->output));
> +
> +	igt_require_pipe_crc(data->drm_fd);
> +	data->pipe_crc = igt_pipe_crc_new(data->drm_fd, data->pipe_id,
> +					  IGT_PIPE_CRC_SOURCE_AUTO);
> +}
> +
> +/* Common test finish. */
> +static void test_fini(data_t *data)
> +{
> +	igt_pipe_crc_free(data->pipe_crc);
> +	igt_display_reset(&data->display);
> +	igt_plane_set_fb(data->primary, NULL);
> +	igt_plane_set_fb(data->cursor, NULL);
> +	if (data->overlay1)
> +		igt_plane_set_fb(data->overlay1, NULL);
> +	if (data->overlay2)
> +		igt_plane_set_fb(data->overlay2, NULL);
> +	if (data->overlay3)
> +                igt_plane_set_fb(data->overlay3, NULL);
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +}
> +
> +/* Common test cleanup. */
> +static void test_cleanup(data_t *data)
> +{
> +	igt_remove_fb(data->drm_fd, &data->cfb);
> +	igt_remove_fb(data->drm_fd, &data->rgb_fb);
> +	igt_remove_fb(data->drm_fd, &data->yuv_fb);
> +	if (data->overlay1)
> +		igt_remove_fb(data->drm_fd, &data->rgb_fb_o1);
> +	if (data->overlay2)
> +		igt_remove_fb(data->drm_fd, &data->rgb_fb_o2);
> +	if (data->overlay3)
> +		igt_remove_fb(data->drm_fd, &data->rgb_fb_o3);
> +	igt_remove_fb(data->drm_fd, &data->quarter_fb);
> +	igt_remove_fb(data->drm_fd, &data->scale_fb);
> +}
> +
> +
> +static void test_cursor_pos(data_t *data, int x, int y, unsigned int flags, unsigned int scaling_factor)
> +{
> +	igt_crc_t ref_crc, test_crc;
> +	cairo_t *cr;
> +	igt_fb_t *rgb_fb = &data->rgb_fb;
> +	igt_fb_t *yuv_fb = &data->yuv_fb;
> +	igt_fb_t *quarter_fb = &data->quarter_fb;
> +	igt_fb_t *rgb_fb_o1 = &data->rgb_fb_o1;
> +	igt_fb_t *rgb_fb_o2 = &data->rgb_fb_o2;
> +	igt_fb_t *rgb_fb_o3 = &data->rgb_fb_o3;
> +	igt_fb_t *cfb = &data->cfb;
> +	int cw = cfb->width;
> +	int ch = cfb->height;
> +	int ret;
> +
> +
> +	cr = igt_get_cairo_ctx(rgb_fb->fd, rgb_fb);
> +
> +	igt_plane_set_fb(data->primary, rgb_fb);
> +	igt_display_commit2(&data->display, COMMIT_ATOMIC);
> +
> +	igt_paint_color(cr, 0, 0, rgb_fb->width, rgb_fb->height, 0.0, 0.0, 0.0);
> +
> +	/* Draw a magenta square where the cursor should be. */
> +	igt_paint_color(cr, x, y, cw, ch, 1.0, 0.0, 1.0);
> +	igt_put_cairo_ctx(cr);
> +
> +	if (flags & TEST_MAX_PLANES) {
> +		/* Display the overlay planes. */
> +		igt_plane_set_fb(data->overlay1, rgb_fb_o1);
> +		igt_plane_set_position(data->overlay1, 0, 0);
> +		igt_plane_set_fb(data->overlay2, rgb_fb_o2);
> +		igt_plane_set_position(data->overlay2, data->rgb_fb_o1.width, data->rgb_fb_o1.height);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Display the cursor. */
> +		igt_plane_set_fb(data->cursor, cfb);
> +		igt_plane_set_position(data->cursor, x, y);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Trigger cursor fall back due to a YUV plane;
> +		 * expect the atomic commit to fail due to no
> +		 * available overlay planes.
> +		 */
> +		igt_plane_set_fb(data->primary, &data->yuv_fb);
> +		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
> +
> +		/* Remove the overlay planes. */
> +		igt_plane_set_fb(data->overlay1, NULL);
> +		igt_plane_set_fb(data->overlay2, NULL);
> +		igt_plane_set_fb(data->cursor, NULL);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Expected atomic commit to fail due to no available overlay planes. */
> +		igt_assert_f(ret == 0, "Expected atomic commit to succeed.\n");
> +		return;
> +	}
> +	if (flags & TEST_NO_PLANES) {
> +
> +		/* Display the overlay planes. */
> +		igt_plane_set_fb(data->overlay1, rgb_fb_o1);
> +		igt_plane_set_position(data->overlay1, 0, 0);
> +		igt_plane_set_fb(data->overlay2, rgb_fb_o2);
> +		igt_plane_set_position(data->overlay2, data->rgb_fb_o1.width, data->rgb_fb_o1.height);
> +		igt_plane_set_fb(data->overlay3, rgb_fb_o3);
> +                igt_plane_set_position(data->overlay3, data->rgb_fb_o1.width, 0);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Display the cursor. */
> +		igt_plane_set_fb(data->cursor, cfb);
> +		igt_plane_set_position(data->cursor, x, y);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Trigger cursor fall back due to a YUV plane;
> +		 * expect the atomic commit to fail due to no
> +		 * available overlay planes.
> +		 */
> +		igt_plane_set_fb(data->primary, &data->yuv_fb);
> +		ret = igt_display_try_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
> +
> +		/* Remove the overlay planes. */
> +		igt_plane_set_fb(data->overlay1, NULL);
> +		igt_plane_set_fb(data->overlay2, NULL);
> +		igt_plane_set_fb(data->overlay3, NULL);
> +		igt_plane_set_fb(data->cursor, NULL);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +		/* Expected atomic commit to fail due to no available overlay planes. */
> +		igt_assert_f(ret == -EINVAL, "Expected atomic commit to fail due to no available overlay planes.\n");
> +		return;
> +	}

Is the TEST_MAX_PLANES testing the 
1 PRIMARY + 2 OVERLAY + 1 Overlay CURSOR = 4 planes scenario? 
If so, atomic commit is expected to succeed, not fail.

TEST_NO_PLANES here looks correct in that regard, since amdgpu_dm would 
reject 1 PRIMARY + 3 OVERLAYS + 1 Overlay CURSOR = 5 planes.

These tests would only be valid for APUs that have 4 hw planes though.
Some DGPUs have more than 4, for which these tests wouldn't make sense.

Perhaps we can programatically determine the number of OVERLAYs via
get_overlay_planes_count(), and enable the right amount (count-1 for
MAX_PLANES and count for NO_PLANES)? We should be able to use the same
fb for all of them, instead of creating a new fb for each.

> +
> +	/* Display the cursor. */
> +	igt_plane_set_fb(data->cursor, cfb);
> +	igt_plane_set_position(data->cursor, x, y);
> +	igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +	/** Record a reference CRC */
> +	igt_pipe_crc_start(data->pipe_crc);
> +	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &ref_crc);
> +
> +	/* Switch primary plane to use YUV Fb. */
> +	if (flags & TEST_YUV) {
> +		igt_plane_set_fb(data->primary, yuv_fb);
> +		igt_plane_set_position(data->primary, 0, 0);
> +		igt_plane_set_size(data->primary, yuv_fb->width, yuv_fb->height);
> +		igt_display_commit_atomic(&data->display, DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
> +
> +	/* Switch primary plane to use a quarter-sized FB. */
> +	} else if (flags & TEST_QUARTER_FB) {
> +		igt_plane_set_fb(data->primary, quarter_fb);
> +		igt_plane_set_position(data->primary, 0, 0);
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +
> +	/* Switch primary plane to use a scaled FB. */
> +	} else if (flags & TEST_SCALING) {
> +		igt_create_fb(data->drm_fd,
> +			data->rgb_fb.width * scaling_factor / 100,
> +			data->rgb_fb.height * scaling_factor / 100,
> +			DRM_FORMAT_XRGB8888,
> +			DRM_FORMAT_MOD_LINEAR, &data->scale_fb);
> +
> +		igt_plane_set_fb(data->primary, &data->scale_fb);
> +		igt_plane_set_position(data->primary, 0, 0);
> +		igt_plane_set_size(data->primary, data->mode->hdisplay, data->mode->vdisplay);
> +
> +		igt_display_commit_atomic(&data->display, 0, NULL);
> +	}
> +
> +	/* Wait for one more vblank since cursor updates are not
> +	 * synchronized to the same frame on AMD hw.
> +	 */
> +	if (is_amdgpu_device(data->drm_fd))
> +		igt_wait_for_vblank_count(data->drm_fd, data->display.pipes[data->pipe_id].crtc_offset, 1);
> +
> +	/* Record the new CRC. */
> +	igt_pipe_crc_get_current(data->drm_fd, data->pipe_crc, &test_crc);
> +	igt_pipe_crc_stop(data->pipe_crc);
> +
> +	/** CRC Check is sufficient for this test */
> +	igt_assert_crc_equal(&ref_crc, &test_crc);
> +}
> +
> +/*
> + * Tests the cursor on a variety of positions on the screen.
> + * Specific edge cases that should be captured here are the negative edges
> + * of each plane and the centers.
> + */
> +static void test_cursor_spots(data_t *data, int size, unsigned int flags, unsigned int scaling_factor)
> +{
> +	int sw = data->mode->hdisplay;
> +	int sh = data->mode->vdisplay;
> +	int i;
> +		const pos_t pos[] = {
> +		/* Test diagonally from top left to bottom right. */
> +		{ -size / 3, -size / 3 },
> +		{ 0, 0 },
> +		{ sw / 4 - size, sh / 4 - size },
> +		{ sw / 4 - size / 3, sh / 4 - size / 3 },
> +		{ sw / 4, sh / 4 },
> +		{ sw / 4 + size, sh / 4 + size },
> +		{ sw / 2, sh / 2 },
> +		{ sw / 4 + sw / 2 - size, sh / 4 + sh / 2 - size },
> +		{ sw / 4 + sw / 2 - size / 3, sh / 4 + sh / 2 - size / 3 },
> +		{ sw / 4 + sw / 2 + size, sh / 4 + sh / 2 + size },
> +		{ sw - size, sh - size },
> +		{ sw - size / 3, sh - size / 3 },
> +		/* Test remaining corners. */
> +		{ sw - size, 0 },
> +		{ 0, sh - size },
> +		{ sw / 4 + sw / 2 - size, sh / 4 },
> +		{ sw / 4, sh / 4 + sh / 2 - size }
> +	};
> +
> +	for (i = 0; i < ARRAY_SIZE(pos); ++i)
> +		test_cursor_pos(data, pos[i].x, pos[i].y, flags, scaling_factor);
> +}
> +
> +static void test_cursor(data_t *data, int size, unsigned int flags, unsigned int scaling_factor)
> +{
> +	int sw, sh;
> +
> +	igt_skip_on(size > data->max_curw || size > data->max_curh);
> +
> +	sw = data->mode->hdisplay;
> +	sh = data->mode->vdisplay;
> +
> +	test_cleanup(data);
> +
> +	/* Create RGB FB for reference. */
> +	igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888,
> +			    DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->rgb_fb);
> +
> +	/* Create YUV FB for RGB-to-YUV, MAX_PLANES and NO_PLANES subtests */
> +	if (flags & TEST_YUV || flags & TEST_MAX_PLANES ||flags & TEST_NO_PLANES)
> +		igt_create_fb(data->drm_fd, sw, sh, DRM_FORMAT_NV12,
> +					DRM_FORMAT_MOD_NONE, &data->yuv_fb);
> +
> +	/* Create a quarter-sized empty FB. */
> +	if (flags & TEST_QUARTER_FB)
> +		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
> +					DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->quarter_fb);
> +
> +	/* Create two RGB FBs for the overlay planes. */
> +	if (flags & TEST_MAX_PLANES || flags & TEST_NO_PLANES) {
> +		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
> +			DRM_FORMAT_MOD_NONE, 0.5, 0.0, 0.0, &data->rgb_fb_o1);
> +		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
> +			DRM_FORMAT_MOD_NONE, 0.0, 0.5, 0.0, &data->rgb_fb_o2);
> +	}
> +
> +	/* Create a third overlay plane for TEST_NO_PLANES test */
> +	if (flags & TEST_NO_PLANES) {
> +		igt_create_color_fb(data->drm_fd, sw / 2, sh / 2, DRM_FORMAT_XRGB8888,
> +			DRM_FORMAT_MOD_NONE, 0.0, 0.0, 0.5, &data->rgb_fb_o3);
> +	}
> +
> +	/* Create a FB for scaling. */
> +	if (flags & TEST_SCALING)
> +		igt_create_color_fb(data->drm_fd, sw, sh, DRM_FORMAT_XRGB8888,
> +					DRM_FORMAT_MOD_LINEAR, 0.0, 0.0, 0.0, &data->scale_fb);
> +
> +	/* Create a cursor FB. */
> +	igt_create_color_fb(data->drm_fd, size, size, DRM_FORMAT_ARGB8888,
> +				DRM_FORMAT_MOD_LINEAR, 1.0, 0.0, 1.0, &data->cfb);
> +
> +	igt_output_set_pipe(data->output, data->pipe_id);
> +
> +	/* Run the test for different cursor spots. */
> +	test_cursor_spots(data, size, flags, scaling_factor);
> +
> +}
> +
> +igt_main
> +{
> +	static const int cursor_sizes[] = { 64, 128, 256 };
> +	data_t data = { .max_curw = 64, .max_curh = 64 };
> +	enum pipe pipe;
> +	igt_output_t *output;
> +	igt_display_t *display;
> +	int i, j;
> +	struct {
> +		const char *name;
> +		unsigned int flags;
> +		unsigned int scale_factor;
> +		const char *desc;
> +	} tests[] = {
> +		{ "rgb-to-yuv", TEST_YUV, 100,
> +		"Tests native cursor fall back to overlay cursor when a top plane switches from RGB to YUV" },
> +		{"non-full", TEST_QUARTER_FB, 100,
> +		"Tests native cursor fall back to overlay cursor when a top plane does not fill the crtc"},
> +		{"max-planes", TEST_MAX_PLANES, 100,
> +		"Tests native cursor fall back to overlay cursor when a top plane is YUV and there are two overlay planes used."},
> +		{"no-available-planes", TEST_NO_PLANES, 100,
> +		"Tests native cursor attempt to fall back to overlay cursor required, but fails atomic commit due to no available overlay planes."},
> +		{"scaling-50", TEST_SCALING, 50,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +		{"scaling-75", TEST_SCALING, 75,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +		{"scaling-125", TEST_SCALING, 125,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +		{"scaling-150", TEST_SCALING, 150,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +		{"scaling-175", TEST_SCALING, 175,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +		{"scaling-200", TEST_SCALING, 200,
> +		"Tests native cursor fall back to overlay cursor when a top plane is scaled"},
> +	};
> +
> +	igt_fixture {
> +		int ret;
> +
> +		data.drm_fd = drm_open_driver_master(DRIVER_AMDGPU);
> +
> +		igt_display_require(&data.display, data.drm_fd);
> +		igt_require(data.display.is_atomic);
> +		igt_display_require_output(&data.display);
> +		display = &data.display;
> +
> +		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_WIDTH, &data.max_curw);
> +		igt_assert(ret == 0 || errno == EINVAL);
> +		ret = drmGetCap(data.drm_fd, DRM_CAP_CURSOR_HEIGHT, &data.max_curh);
> +		igt_assert(ret == 0 || errno == EINVAL);
> +
> +		kmstest_set_vt_graphics_mode();
> +	}
> +
> +
> +	for (i = 0; i < ARRAY_SIZE(tests); i++) {
> +		igt_describe_f("%s", tests[i].desc);
> +		igt_subtest_with_dynamic_f("%s", tests[i].name) {
> +
> +			/* Skip YUV and NO_PLANES subtests if YUV is not supported*/
> +			if (tests[i].flags & TEST_YUV || tests[i].flags & TEST_NO_PLANES)
> +				igt_require(igt_display_has_format_mod(display,
> +							DRM_FORMAT_NV12,
> +							DRM_FORMAT_MOD_LINEAR));
> +
> +			for_each_pipe_with_single_output(&data.display, pipe, output) {
> +
> +				igt_display_reset(display);
> +
> +				igt_output_set_pipe(output, pipe);
> +				if (!intel_pipe_output_combo_valid(display))
> +					continue;
> +
> +				/* Skip TEST_NO_PLANES if the ASIC has less than 3 overlay planes*/
> +				if (tests[i].flags & TEST_NO_PLANES)
> +					igt_require(get_overlay_planes_count(&data.display, pipe) >= 3);
> +
> +				/* Skip TEST_MAX_PLANES if the ASIC has less than 2 overlay planes*/
> +				if (tests[i].flags & TEST_MAX_PLANES)
> +					igt_require(get_overlay_planes_count(&data.display, pipe) >= 2);

The skip checks here don't quite make sense, see comment above.

Thanks,
Leo

> +
> +				test_init(&data, pipe, output, tests[i].flags);
> +
> +				for (j = 0; j < ARRAY_SIZE(cursor_sizes); j++) {
> +					int size = cursor_sizes[j];
> +
> +					igt_dynamic_f("pipe-%s-%s-size-%d",
> +						      kmstest_pipe_name(pipe),
> +						      igt_output_name(output),
> +						      size)
> +						test_cursor(&data, size, tests[i].flags, tests[i].scale_factor);
> +
> +					test_cleanup(&data);
> +				}
> +
> +				test_fini(&data);
> +			}
> +		}
> +	}
> +
> +	igt_fixture {
> +		igt_display_fini(&data.display);
> +		drm_close_driver(data.drm_fd);
> +	}
> +}
> diff --git a/tests/amdgpu/meson.build b/tests/amdgpu/meson.build
> index 421e686d3..b9f1842e2 100644
> --- a/tests/amdgpu/meson.build
> +++ b/tests/amdgpu/meson.build
> @@ -10,6 +10,7 @@ if libdrm_amdgpu.found()
>  			  'amd_color',
>  			  'amd_cp_dma_misc',
>  			  'amd_cs_nop',
> +			  'amd_cursor_overlay',
>  			  'amd_deadlock',
>  			  'amd_dp_dsc',
>  			  'amd_freesync_video_mode',


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-08-21 18:28 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-23 17:27 [PATCH i-g-t v3] tests/amdgpu: Add test for native cursor fallback to overlay IVAN.LIPSKI
2025-08-21 18:28 ` Leo Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).