[igt-dev] [PATCH i-g-t] tests/kms_chamelium: add a latency test

Simon Ser simon.ser at intel.com
Fri Sep 6 11:46:00 UTC 2019


This new test creates a red and a green framebuffer, then alternatively shows
one and the other for a while. We check that we correctly capture the
alternating pattern with Chamelium.

We do our best to capture the pattern, which is tricky because of
synchronization issues. We first show the red FB, start the capture, then
play the alternating pattern, and stop the capture. We capture more frames than
there are in the pattern to make sure the delay after starting the capture
doesn't make us completely miss the pattern.

Signed-off-by: Simon Ser <simon.ser at intel.com>
Cc: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Martin Peres <martin.peres at linux.intel.com>
---
 tests/kms_chamelium.c | 163 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 163 insertions(+)

diff --git a/tests/kms_chamelium.c b/tests/kms_chamelium.c
index 6b6292497cd7..ba9f8acb600c 100644
--- a/tests/kms_chamelium.c
+++ b/tests/kms_chamelium.c
@@ -2522,6 +2522,161 @@ test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width)
 	igt_hpd_storm_reset(data->drm_fd);
 }
 
+static void latency_page_flip_handler(int fd, unsigned seq, unsigned tv_sec,
+				      unsigned tv_usec, void *data)
+{
+	int64_t *seq_ptr = data;
+
+	igt_debug("Got page-flip event (seq=%u)\n", seq);
+	*seq_ptr = seq;
+}
+
+#define TEST_LATENCY_PLAYBACK_FRAMES 20
+#define TEST_LATENCY_CAPTURE_FRAMES (TEST_LATENCY_PLAYBACK_FRAMES + 10)
+
+static const char test_display_latency_desc[] =
+	"Alternate between two different buffers at each page-flip, "
+	"check there is no latency or tearing";
+static void
+test_display_latency(data_t *data, struct chamelium_port *port)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	struct igt_fb fbs[2];
+	unsigned int fb_ids[2];
+	struct chamelium_fb_crc_async_data *fb_crcs[2];
+	drmModeModeInfo mode;
+	drmModeConnector *connector;
+	struct pollfd pfd = {0};
+	drmEventContext drm_event = {0};
+	int i, j, ret, captured_frames_count, start, end;
+	int64_t prev_seq, seq;
+	igt_crc_t *crcs, *expected_crcs[2];
+
+	reset_state(data, port);
+
+	output = prepare_output(data, port, TEST_EDID_BASE);
+	connector = chamelium_port_get_connector(data->chamelium, port, false);
+	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	igt_assert(primary);
+
+	igt_assert(connector->count_modes > 0);
+	mode = connector->modes[0];
+
+	drmModeFreeConnector(connector);
+
+	fb_ids[0] = igt_create_color_pattern_fb(data->drm_fd,
+						mode.hdisplay, mode.vdisplay,
+						DRM_FORMAT_XRGB8888,
+						LOCAL_DRM_FORMAT_MOD_NONE,
+						1, 0, 0, &fbs[0]);
+	igt_assert(fb_ids[0] > 0);
+	fb_ids[1] = igt_create_color_pattern_fb(data->drm_fd,
+						mode.hdisplay, mode.vdisplay,
+						DRM_FORMAT_XRGB8888,
+						LOCAL_DRM_FORMAT_MOD_NONE,
+						0, 1, 0, &fbs[1]);
+	igt_assert(fb_ids[1] > 0);
+
+	fb_crcs[0] = chamelium_calculate_fb_crc_async_start(data->drm_fd,
+							    &fbs[0]);
+	fb_crcs[1] = chamelium_calculate_fb_crc_async_start(data->drm_fd,
+							    &fbs[1]);
+
+	igt_plane_set_fb(primary, &fbs[0]);
+	igt_plane_set_size(primary, mode.hdisplay, mode.vdisplay);
+	igt_output_override_mode(output, &mode);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	chamelium_start_capture(data->chamelium, port, 0, 0, 0, 0);
+
+	pfd.fd = data->drm_fd;
+	pfd.events = POLLIN;
+
+	drm_event.version = 2;
+	drm_event.page_flip_handler = latency_page_flip_handler;
+
+	/* Make sure we stop the alternating pattern with fb[1] */
+	assert(TEST_LATENCY_PLAYBACK_FRAMES % 2 == 0);
+
+	prev_seq = -1;
+	for (i = 0; i < TEST_LATENCY_PLAYBACK_FRAMES; i++) {
+		igt_plane_set_fb(primary, &fbs[i % 2]);
+
+		igt_display_commit_atomic(&data->display,
+					  DRM_MODE_ATOMIC_NONBLOCK |
+					  DRM_MODE_PAGE_FLIP_EVENT,
+					  &seq);
+
+		ret = poll(&pfd, 1, 1000);
+		igt_assert_f(ret == 1, "poll returned %d, expected 1\n", ret);
+		seq = -1;
+		drmHandleEvent(data->drm_fd, &drm_event);
+		igt_assert(seq >= 0);
+
+		if (prev_seq >= 0) {
+			igt_assert((unsigned) (prev_seq + 1) == (unsigned) seq);
+		}
+		prev_seq = seq;
+	}
+
+	/* Capture more frames than necessary, because we don't want to miss
+	 * our alternating pattern. */
+	chamelium_stop_capture(data->chamelium, TEST_LATENCY_CAPTURE_FRAMES);
+	crcs = chamelium_read_captured_crcs(data->chamelium,
+					    &captured_frames_count);
+	igt_assert(captured_frames_count == TEST_LATENCY_CAPTURE_FRAMES);
+
+	igt_debug("Captured %d frames\n", captured_frames_count);
+
+	expected_crcs[0] = chamelium_calculate_fb_crc_async_finish(fb_crcs[0]);
+	expected_crcs[1] = chamelium_calculate_fb_crc_async_finish(fb_crcs[1]);
+
+	/* Assuming reference CRCs A and B, the captured CRCs should look like:
+	 * A A A A A B A B A B A B A B A B A B … A B A B B B B B B B
+	 *           ^                                 ^
+	 *           playback starts                   playback ends
+	 */
+	igt_debug("Reference CRC #0: %s\n", igt_crc_to_string(expected_crcs[0]));
+	igt_debug("Reference CRC #1: %s\n", igt_crc_to_string(expected_crcs[1]));
+	for (i = 0; i < captured_frames_count; i++) {
+		igt_debug("Captured CRC #%d: %s\n", i,
+			  igt_crc_to_string(&crcs[i]));
+	}
+
+	igt_debug("Searching start and end of alternating pattern...\n");
+	for (start = 0; start < captured_frames_count; start++) {
+		if (!igt_check_crc_equal(expected_crcs[0], &crcs[start]))
+			break;
+	}
+	for (end = captured_frames_count - 1; end >= 0; end--) {
+		if (!igt_check_crc_equal(expected_crcs[1], &crcs[end]))
+			break;
+	}
+	igt_debug("Alternating pattern starts at frame #%d and ends at "
+		  "frame #%d\n", start, end);
+	/* Make sure we captured a big enough chunk of the pattern */
+	igt_assert_f(end - start > TEST_LATENCY_PLAYBACK_FRAMES / 2,
+		     "Captured only %d frames of the alternating pattern, "
+		     "that's not enough (expected more than %d)\n",
+		     end - start, TEST_LATENCY_PLAYBACK_FRAMES / 2);
+
+	j = 1; /* crcs[start] should match expected_crcs[1] */
+	for (i = start; i <= end; i++) {
+		igt_assert_f(igt_check_crc_equal(expected_crcs[j % 2], &crcs[i]),
+			     "Captured CRC #%d mismatches reference CRC #%d\n",
+			     i, j % 2);
+		j++;
+	}
+
+	free(expected_crcs[0]);
+	free(expected_crcs[1]);
+	free(crcs);
+
+	igt_remove_fb(data->drm_fd, &fbs[0]);
+	igt_remove_fb(data->drm_fd, &fbs[1]);
+}
+
 static const struct edid *get_edid(enum test_edid edid)
 {
 	switch (edid) {
@@ -2659,6 +2814,10 @@ igt_main
 		connector_subtest("dp-audio-edid", DisplayPort)
 			test_display_audio_edid(&data, port,
 						TEST_EDID_DP_AUDIO);
+
+		igt_describe(test_display_latency_desc);
+		connector_subtest("dp-latency", DisplayPort)
+			test_display_latency(&data, port);
 	}
 
 	igt_subtest_group {
@@ -2820,6 +2979,10 @@ igt_main
 
 		connector_subtest("hdmi-aspect-ratio", HDMIA)
 			test_display_aspect_ratio(&data, port);
+
+		igt_describe(test_display_latency_desc);
+		connector_subtest("hdmi-latency", HDMIA)
+			test_display_latency(&data, port);
 	}
 
 	igt_subtest_group {
-- 
2.23.0



More information about the igt-dev mailing list