[4/4] kms_plane: Add clipping subtests

Submitted by Imre Deak on Jan. 12, 2018, 2:56 p.m.

Details

Message ID 20180112145653.16384-4-imre.deak@intel.com
State New
Headers show
Series "Series without cover letter" ( rev: 1 ) in IGT (deprecated)

Not browsing as part of any series.

Commit Message

Imre Deak Jan. 12, 2018, 2:56 p.m.
Add plane clipping subtests displaying a single clipped plane, with the
following test cases:
a) plane covering the whole screen, so that clipping is done at all 4
   screen edges
b) plane at either of the 4 corners of the screen clipped, so that a
   4x4 pixel part of the plane is visible
c) plane at either of the 4 corners of the screen clipped, so that a
   2x2 pixel part of the plane is visible

Each of the above cases are tested with all supported planes, tiling
modes, rotation degrees and differing pixel format sizes (only 16 bpp
and 32 bpp for now). While the a) and b) cases above are supported on
all platforms c) is not fully supported on GLK and CNL (which was the
primary motivation for this testcase).

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
 tests/kms_plane.c | 441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 441 insertions(+)

Patch hide | download patch | download mbox

diff --git a/tests/kms_plane.c b/tests/kms_plane.c
index 358126b6..0d32f9ef 100644
--- a/tests/kms_plane.c
+++ b/tests/kms_plane.c
@@ -41,11 +41,34 @@  typedef struct {
 	int drm_fd;
 	igt_display_t display;
 	igt_pipe_crc_t *pipe_crc;
+	uint32_t devid;
+	uint64_t max_curw;
+	uint64_t max_curh;
 } data_t;
 
 static color_t red   = { 1.0f, 0.0f, 0.0f };
 static color_t green = { 0.0f, 1.0f, 0.0f };
+static color_t yellow = { 1.0f, 1.0f, 0.0f };
 static color_t blue  = { 0.0f, 0.0f, 1.0f };
+static color_t white = { 1.0f, 1.0f, 1.0f };
+
+
+/*
+ * Size of a square plane used to test clipping at the 4 courners of the
+ * display.
+ */
+#define CLIPPED_PLANE_SMALL_SIZE	64
+
+/*
+ * Visible plane size after clipping that works on all platforms for all plane
+ * positions.
+ * The exceptions are GLK/CNL where there must be at least this many pixels
+ * visible from the plane after it's clipped to the left/right edge of the
+ * screen. Not meeting this condition may trigger FIFO underflows and screen
+ * corruption. The cursor plane is an exception that doesn't have this problem
+ * even on GLK/CNL.
+ */
+#define CLIPPED_PLANE_MIN_VALID		4
 
 /*
  * Common code across all tests, acting on data_t
@@ -143,6 +166,111 @@  create_fb_for_mode__position(data_t *data, drmModeModeInfo *mode,
 	cairo_destroy(cr);
 }
 
+/*
+ * Create a square FB for the plane in the clipping test, divided into 4
+ * quarters solid filled with different colors. Use the given tiling, format
+ * and size and rotate the FB clockwise with the given rotation degrees, so
+ * that the counterclockwise rotation with the same degrees done by the HW
+ * will always result in the same reference FB image.
+ */
+static void
+create_fb_for_mode__clipping_plane(data_t *data, igt_rotation_t rotation,
+				   uint64_t tiling,
+				   uint32_t format,
+				   int size,
+				   struct igt_fb *fb /* out */)
+{
+	color_t corners[] = { red, white, yellow, blue };
+	color_t color;
+	unsigned int fb_id;
+	cairo_t *cr;
+	const int qsize = size / 2;
+	int idx;
+
+	fb_id = igt_create_fb(data->drm_fd, size, size, format, tiling, fb);
+	igt_assert(fb_id);
+
+	cr = igt_get_cairo_ctx(data->drm_fd, fb);
+
+	switch (rotation) {
+	case IGT_ROTATION_0:
+		idx = 0;
+		break;
+	case IGT_ROTATION_90:
+		idx = 3;
+		break;
+	case IGT_ROTATION_180:
+		idx = 2;
+		break;
+	case IGT_ROTATION_270:
+		idx = 1;
+		break;
+	default:
+		igt_assert(0);
+	}
+
+	color = corners[idx];
+	igt_paint_color(cr, 0, 0, qsize, qsize,
+			color.red, color.green, color.blue);
+
+	color = corners[(++idx) % 4];
+	igt_paint_color(cr, qsize, 0, qsize, qsize,
+			color.red, color.green, color.blue);
+
+	color = corners[(++idx) % 4];
+	igt_paint_color(cr, qsize, qsize, qsize, qsize,
+			color.red, color.green, color.blue);
+
+	color = corners[(++idx) % 4];
+	igt_paint_color(cr, 0, qsize, qsize, qsize,
+			color.red, color.green, color.blue);
+
+	igt_assert(cairo_status(cr) == 0);
+	cairo_destroy(cr);
+}
+
+/*
+ * Create a square reference FB for the whole screen in the clipping test,
+ * with the given test plane position and size. See
+ * create_fb_for_mode__clipping_plane() for the layout of the test plane.
+ */
+static void
+create_fb_for_mode__clipping_display(data_t *data, drmModeModeInfo *mode,
+				     int plane_x, int plane_y,
+				     int plane_size,
+				     struct igt_fb *fb /* out */)
+{
+	struct igt_fb plane_fb;
+	unsigned int fb_id;
+	cairo_t *cr;
+	cairo_surface_t *src;
+
+	fb_id = igt_create_fb(data->drm_fd,
+				  mode->hdisplay, mode->vdisplay,
+				  DRM_FORMAT_XRGB8888,
+				  LOCAL_DRM_FORMAT_MOD_NONE,
+				  fb);
+	igt_assert(fb_id);
+
+	create_fb_for_mode__clipping_plane(data, IGT_ROTATION_0,
+					   LOCAL_DRM_FORMAT_MOD_NONE,
+					   DRM_FORMAT_XRGB8888,
+					   plane_size, &plane_fb);
+
+	src = igt_get_cairo_surface(data->drm_fd, &plane_fb);
+
+	cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	igt_paint_color(cr, 0, 0, mode->hdisplay, mode->vdisplay,
+			0, 0, 0);
+
+	cairo_set_source_surface(cr, src, plane_x, plane_y);
+	cairo_rectangle(cr, plane_x, plane_y,
+			plane_size, plane_size);
+	cairo_fill(cr);
+	igt_assert(cairo_status(cr) == 0);
+	cairo_destroy(cr);
+}
+
 enum {
 	TEST_POSITION_FULLY_COVERED = 1 << 0,
 	TEST_DPMS = 1 << 1,
@@ -381,6 +509,303 @@  test_plane_panning(data_t *data, enum pipe pipe, unsigned int flags)
 	igt_skip_on(connected_outs == 0);
 }
 
+static bool
+bogus_plane_conf(uint32_t devid, igt_plane_t *plane_obj,
+		 int hdisplay, int plane_x, int plane_width)
+{
+	if (!(IS_GEMINILAKE(devid) || IS_CANNONLAKE(devid)))
+		return false;
+
+	if (plane_obj->type == DRM_PLANE_TYPE_CURSOR)
+		return false;
+
+	if (plane_x + plane_width >= CLIPPED_PLANE_MIN_VALID &&
+	    plane_x <= hdisplay - CLIPPED_PLANE_MIN_VALID)
+		return false;
+
+	return true;
+}
+
+static bool
+supported_plane_conf(data_t *data, int plane_type, uint64_t tiling,
+		     uint32_t format, igt_rotation_t rotation,
+		     drmModeModeInfo *mode,
+		     int plane_x, int plane_y, int plane_size)
+{
+	if (intel_gen(data->devid) < 9 &&
+	    tiling != LOCAL_DRM_FORMAT_MOD_NONE)
+		return false;
+
+	/* On GEN<9 the primary plane must cover the full screen. */
+	if (intel_gen(data->devid) < 9 &&
+	    plane_type == DRM_PLANE_TYPE_PRIMARY &&
+	    (plane_x > 0 || plane_y > 0 ||
+	     (plane_x + plane_size < mode->hdisplay) ||
+	     (plane_y + plane_size < mode->vdisplay)))
+		return false;
+
+	if (plane_type == DRM_PLANE_TYPE_CURSOR) {
+		/* For cursor planes only linear/alpha format is supported. */
+		if (tiling != LOCAL_DRM_FORMAT_MOD_NONE ||
+		    format != DRM_FORMAT_ARGB8888)
+			return false;
+
+		if (plane_size > data->max_curw || plane_size > data->max_curh)
+			return false;
+	} else {
+		/*
+		 * For non-cursor planes formats with alpha may result in
+		 * undeterministic CRCs, we use the same sized
+		 * non-alpha XRGB8888 format instead.
+		 */
+		if (format == DRM_FORMAT_ARGB8888)
+			return false;
+
+		if (format == DRM_FORMAT_RGB565 &&
+		    intel_gen(data->devid) < 9 &&
+		    !IS_VALLEYVIEW(data->devid) && !IS_CHERRYVIEW(data->devid))
+			return false;
+	}
+
+	/* RGB565 with rotation is not supported for now. */
+	if (format == DRM_FORMAT_RGB565 && rotation != IGT_ROTATION_0)
+		return false;
+
+	return true;
+}
+
+static void
+test_plane_clipping_format(data_t *data,
+			    enum pipe pipe,
+			    int plane,
+			    igt_output_t *output,
+			    drmModeModeInfo *mode,
+			    int plane_x, int plane_y,
+			    int plane_size,
+			    uint64_t tiling,
+			    igt_rotation_t rotation,
+			    uint32_t format,
+			    igt_crc_t *reference_crc)
+{
+	igt_plane_t *plane_obj;
+	struct igt_fb reference_fb;
+	igt_crc_t crc;
+	int ret;
+
+	igt_debug("  plane %d rotation %s tiling %s format %s\n",
+		  plane,
+		  igt_rotation_degrees_str(rotation),
+		  igt_tiling_str(tiling),
+		  igt_format_str(format));
+
+	igt_output_set_pipe(output, pipe);
+	plane_obj = igt_output_get_plane(output, plane);
+
+	if (!supported_plane_conf(data, plane_obj->type,
+				  tiling, format, rotation,
+				  mode,
+				  plane_x, plane_y,
+				  plane_size))
+		return;
+
+	igt_output_set_pipe(output, pipe);
+
+	create_fb_for_mode__clipping_plane(data, rotation, tiling, format,
+					   plane_size, &reference_fb);
+
+	igt_plane_set_fb(plane_obj, &reference_fb);
+	igt_fb_set_position(&reference_fb, plane_obj, 0, 0);
+
+	igt_plane_set_size(plane_obj, plane_size, plane_size);
+	igt_plane_set_rotation(plane_obj, rotation);
+	igt_plane_set_position(plane_obj, plane_x, plane_y);
+
+	ret = igt_display_try_commit2(&data->display, COMMIT_UNIVERSAL);
+
+	if (!bogus_plane_conf(data->devid, plane_obj, mode->hdisplay,
+			      plane_x, plane_size)) {
+		igt_assert(ret == 0);
+		igt_pipe_crc_collect_crc(data->pipe_crc, &crc);
+		igt_assert_crc_equal(reference_crc, &crc);
+	} else {
+		igt_assert(ret == -EINVAL);
+	}
+
+	igt_plane_set_fb(plane_obj, NULL);
+	igt_plane_set_rotation(plane_obj, IGT_ROTATION_0);
+	igt_plane_set_position(plane_obj, 0, 0);
+
+	igt_output_set_pipe(output, PIPE_ANY);
+
+	igt_remove_fb(data->drm_fd, &reference_fb);
+}
+
+static void
+test_plane_clipping_square(data_t *data, enum pipe pipe,
+			   igt_output_t *output, drmModeModeInfo *mode,
+			   int plane_x, int plane_y, int plane_size)
+{
+	const struct {
+		uint64_t tiling;
+		igt_rotation_t rotation;
+	} rotations[] = {
+		{ LOCAL_DRM_FORMAT_MOD_NONE,		IGT_ROTATION_0 },
+
+		{ LOCAL_I915_FORMAT_MOD_X_TILED,	IGT_ROTATION_0 },
+
+		{ LOCAL_I915_FORMAT_MOD_Y_TILED,	IGT_ROTATION_0 },
+		{ LOCAL_I915_FORMAT_MOD_Y_TILED,	IGT_ROTATION_90 },
+		{ LOCAL_I915_FORMAT_MOD_Y_TILED,	IGT_ROTATION_180 },
+		{ LOCAL_I915_FORMAT_MOD_Y_TILED,	IGT_ROTATION_270 },
+
+		{ LOCAL_I915_FORMAT_MOD_Yf_TILED,	IGT_ROTATION_0 },
+		{ LOCAL_I915_FORMAT_MOD_Yf_TILED,	IGT_ROTATION_90 },
+		{ LOCAL_I915_FORMAT_MOD_Yf_TILED,	IGT_ROTATION_180 },
+		{ LOCAL_I915_FORMAT_MOD_Yf_TILED,	IGT_ROTATION_270 },
+	};
+	const uint32_t formats[] = {
+		DRM_FORMAT_RGB565,
+		DRM_FORMAT_XRGB8888,
+		DRM_FORMAT_ARGB8888,
+	};
+	int n_planes = data->display.pipes[pipe].n_planes;
+	igt_fb_t reference_fb;
+	igt_crc_t reference_crc;
+
+	igt_info("Testing connector %s mode %dx%d using pipe %s: %d,%d@%d,%d\n",
+		 igt_output_name(output),
+		 mode->hdisplay, mode->vdisplay,
+		 kmstest_pipe_name(pipe),
+		 plane_size, plane_size,
+		 plane_x, plane_y);
+
+	test_init(data, pipe);
+
+	create_fb_for_mode__clipping_display(data, mode,
+					     plane_x, plane_y,
+					     plane_size,
+					     &reference_fb);
+
+	test_grab_crc_for_fb(data, output, pipe, &reference_fb,
+			     &reference_crc);
+
+	igt_remove_fb(data->drm_fd, &reference_fb);
+
+	for (int plane = 0; plane < n_planes; plane++)
+		for (int rotation = 0; rotation < ARRAY_SIZE(rotations);
+		     rotation++)
+			for (int format = 0; format < ARRAY_SIZE(formats);
+			     format++)
+				test_plane_clipping_format(data, pipe, plane,
+							    output,
+							    mode,
+							    plane_x, plane_y,
+							    plane_size,
+							    rotations[rotation].tiling,
+							    rotations[rotation].rotation,
+							    formats[format],
+							    &reference_crc);
+
+	test_fini(data);
+}
+
+static void
+test_plane_clipping(data_t *data, enum pipe pipe)
+{
+	igt_output_t *output;
+	int connected_outs = 0;
+
+	for_each_valid_output_on_pipe(&data->display, pipe, output) {
+		drmModeModeInfo *mode;
+		int sq_size;
+
+		igt_output_set_pipe(output, pipe);
+		mode = igt_output_get_mode(output);
+
+		/*
+		 * Test with a square plane bigger than either the width or
+		 * height of the mode. This should pass on all platforms.
+		 */
+		sq_size = mode->hdisplay > mode->vdisplay ?
+			  mode->hdisplay : mode->vdisplay;
+		test_plane_clipping_square(data, pipe, output, mode,
+					   -2,
+					   -2,
+					   sq_size + 4);
+
+		/*
+		 * Test positions in the 4 corners of the screen with a
+		 * CLIPPED_PLANE_MIN_VALID x CLIPPED_PLANE_MIN_VALID square
+		 * visible from the plane. These should pass on all platforms.
+		 */
+		test_plane_clipping_square(data, pipe, output, mode,
+					   -CLIPPED_PLANE_SMALL_SIZE +
+					    CLIPPED_PLANE_MIN_VALID,
+					   -CLIPPED_PLANE_SMALL_SIZE +
+					    CLIPPED_PLANE_MIN_VALID,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   -CLIPPED_PLANE_SMALL_SIZE +
+					    CLIPPED_PLANE_MIN_VALID,
+					   mode->vdisplay -
+					    CLIPPED_PLANE_MIN_VALID,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   mode->hdisplay -
+					    CLIPPED_PLANE_MIN_VALID,
+					   -CLIPPED_PLANE_SMALL_SIZE +
+					    CLIPPED_PLANE_MIN_VALID,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   mode->hdisplay -
+					    CLIPPED_PLANE_MIN_VALID,
+					   mode->vdisplay -
+					    CLIPPED_PLANE_MIN_VALID,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		/*
+		 * Test positions in the 4 corners of the screen with a
+		 * 2 x 2 square visible from the plane. These are valid on all
+		 * platforms except on GLK/CNL where less than
+		 * CLIPPED_PLANE_MIN_VALID pixels visible on the left/right
+		 * edges of the screen may cause FIFO underflow and display
+		 * corruption.
+		 *
+		 * The cursor plane is an exception without this problem.
+		 *
+		 * Use 2 x 2 size as other (odd) sizes may result in an
+		 * incorrect CRC for the cursor plane even though it displays
+		 * correctly and causes no underflow.
+		 */
+		test_plane_clipping_square(data, pipe, output, mode,
+					   -CLIPPED_PLANE_SMALL_SIZE + 2,
+					   -CLIPPED_PLANE_SMALL_SIZE + 2,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   -CLIPPED_PLANE_SMALL_SIZE + 2,
+					   mode->vdisplay - 2,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   mode->hdisplay - 2,
+					   -CLIPPED_PLANE_SMALL_SIZE + 2,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		test_plane_clipping_square(data, pipe, output, mode,
+					   mode->hdisplay - 2,
+					   mode->vdisplay - 2,
+					   CLIPPED_PLANE_SMALL_SIZE);
+
+		connected_outs++;
+	}
+
+	igt_skip_on(connected_outs == 0);
+}
+
 static void
 run_tests_for_pipe_plane(data_t *data, enum pipe pipe)
 {
@@ -413,6 +838,9 @@  run_tests_for_pipe_plane(data_t *data, enum pipe pipe)
 		      kmstest_pipe_name(pipe))
 		test_plane_panning(data, pipe, TEST_PANNING_BOTTOM_RIGHT |
 					       TEST_SUSPEND_RESUME);
+	igt_subtest_f("plane-clipping-pipe-%s-planes",
+		      kmstest_pipe_name(pipe))
+		test_plane_clipping(data, pipe);
 }
 
 
@@ -425,8 +853,21 @@  igt_main
 	igt_skip_on_simulation();
 
 	igt_fixture {
+		int ret;
+
 		data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
 
+		data.devid = intel_get_drm_devid(data.drm_fd);
+
+		data.max_curw = 64;
+		data.max_curh = 64;
+		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();
 
 		igt_require_pipe_crc(data.drm_fd);