diff mbox

[i-g-t,12/12] testdisplay: Test the stereo 3D modes

Message ID 1378494530-3600-13-git-send-email-damien.lespiau@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lespiau, Damien Sept. 6, 2013, 7:08 p.m. UTC
Now that modes have flags to describe which 3d formats the sink
supports, it's time to test them.

The new test cycles through the supported 3D formats and paint 3D
stereoscopic images taken from publicly available samples:
  http://www.quantumdata.com/apps/3D/sample_BMP.asp

Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
---
 tests/testdisplay.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 252 insertions(+), 3 deletions(-)
diff mbox

Patch

diff --git a/tests/testdisplay.c b/tests/testdisplay.c
index df3b4d4..a8b39fe 100644
--- a/tests/testdisplay.c
+++ b/tests/testdisplay.c
@@ -54,10 +54,13 @@ 
 #include <math.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <strings.h>
 #include <unistd.h>
 #include <sys/poll.h>
 #include <sys/time.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "i915_drm.h"
 #include "drmtest.h"
@@ -69,7 +72,7 @@ 
 drmModeRes *resources;
 int drm_fd, modes;
 int test_all_modes = 0, test_preferred_mode = 0, force_mode = 0, test_plane,
-    enable_tiling;
+    test_3d_modes, enable_tiling;
 int sleep_between_modes = 5;
 uint32_t depth = 24, stride, bpp;
 int qr_code = 0;
@@ -362,6 +365,9 @@  set_mode(struct connector *c)
 		if (test_all_modes)
 			c->mode = c->connector->modes[j];
 
+		/* set_mode() only tests 2D modes */
+		c->mode.flags &= ~DRMTEST_MODE_FLAG_3D_MASK;
+
 		if (!c->mode_valid)
 			continue;
 
@@ -404,6 +410,223 @@  set_mode(struct connector *c)
 	drmModeFreeConnector(c->connector);
 }
 
+static void adjust_3d_timings(drmModeModeInfo *mode, unsigned int format)
+{
+	uint16_t vdisplay, vactive_space;
+
+	/* just set the 3D format we are setting (this is not used by the
+	 * kernel, it's just for kmstest_dump_mode()) */
+	mode->flags &= ~DRMTEST_MODE_FLAG_3D_MASK;
+	mode->flags |= format;
+
+	switch (format) {
+	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
+	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
+		return;
+	case DRM_MODE_FLAG_3D_FRAME_PACKING:
+		vactive_space = mode->vtotal - mode->vdisplay;
+		vdisplay = mode->vdisplay;
+
+		mode->vdisplay += vdisplay + vactive_space;
+		mode->vsync_start += vdisplay + vactive_space;
+		mode->vsync_end += vdisplay + vactive_space;
+		mode->vtotal += vdisplay + vactive_space;
+		mode->clock = (mode->vtotal * mode->htotal * mode->vrefresh) /
+			      1000;
+		return;
+	default:
+		assert(0);
+	}
+}
+
+struct box {
+	int x, y, width, height;
+};
+
+struct s3d_fb_layout {
+	int fb_width, fb_height;
+	struct box left, right;
+};
+
+static void box_init(struct box *box, int x, int y, int bwidth, int bheight)
+{
+	box->x = x;
+	box->y = y;
+	box->width = bwidth;
+	box->height = bheight;
+}
+
+static void box_print(const char * prefix, struct box *box)
+{
+	printf("%s: %d, %d, %d, %d\n", prefix,
+			box->x, box->y, box->width, box->height);
+}
+
+static void s3d_fb_layout_from_mode(struct s3d_fb_layout *layout,
+				    drmModeModeInfo *mode)
+{
+	unsigned int format = mode->flags & DRMTEST_MODE_FLAG_3D_MASK;
+	const int hdisplay = mode->hdisplay, vdisplay = mode->vdisplay;
+	int middle;
+
+	switch (format) {
+	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
+		layout->fb_width = hdisplay;
+		layout->fb_height = vdisplay;
+
+		middle = vdisplay / 2;
+		box_init(&layout->left, 0, 0, hdisplay, middle);
+		box_init(&layout->right,
+			 0, middle, hdisplay, vdisplay - middle);
+		break;
+	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
+		layout->fb_width = hdisplay;
+		layout->fb_height = vdisplay;
+
+		middle = hdisplay / 2;
+		box_init(&layout->left, 0, 0, middle, vdisplay);
+		box_init(&layout->right,
+			 middle, 0, hdisplay - middle, vdisplay);
+		break;
+	case DRM_MODE_FLAG_3D_FRAME_PACKING:
+	{
+		int vactive_space = mode->vtotal - vdisplay;
+
+		layout->fb_width = hdisplay;
+		layout->fb_height = 2 * vdisplay + vactive_space;
+
+		box_init(&layout->left,
+			 0, 0, hdisplay, vdisplay);
+		box_init(&layout->right,
+			 0, vdisplay + vactive_space, hdisplay, vdisplay);
+		break;
+	}
+	default:
+		assert(0);
+	}
+}
+
+static const char *s3d_mode_str(unsigned format)
+{
+	switch (format) {
+	case DRM_MODE_FLAG_3D_TOP_AND_BOTTOM:
+		return "TB";
+	case DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF:
+		return "SbSH";
+	case DRM_MODE_FLAG_3D_FRAME_PACKING:
+		return "FP";
+	default:
+		assert(0);
+	}
+}
+
+static uint32_t create_s3d_fb(drmModeModeInfo *mode, struct kmstest_fb *fb)
+{
+	struct s3d_fb_layout layout;
+	cairo_t *cr;
+	uint32_t fb_id;
+
+	s3d_fb_layout_from_mode(&layout, mode);
+	box_print("left: ", &layout.left);
+	box_print("right: ", &layout.right);
+	fb_id = kmstest_create_fb(drm_fd, layout.fb_width, layout.fb_height,
+				  bpp, depth, enable_tiling, fb);
+	cr = kmstest_get_cairo_ctx(drm_fd, fb);
+
+	kmstest_paint_image(cr, IGT_DATADIR"/1080p-left.png",
+			    layout.left.x, layout.left.y,
+			    layout.left.width, layout.left.height);
+	kmstest_paint_image(cr, IGT_DATADIR"/1080p-right.png",
+			    layout.right.x, layout.right.y,
+			    layout.right.width, layout.right.height);
+
+#if 0 /* Comment this out if you want to see what the composited fb looks
+	 like */
+	{
+		char buffer[64];
+
+		snprintf(buffer, sizeof(buffer), "%dx%d@%dHz-%s.png",
+			 mode->hdisplay,
+			 mode->vdisplay,
+			 mode->vrefresh,
+			 s3d_mode_str(mode->flags & DRMTEST_MODE_FLAG_3D_MASK));
+
+		kmstest_write_fb(drm_fd, fb, buffer);
+	}
+#endif
+
+	return fb_id;
+}
+
+static void do_set_3d_format(struct connector *c, unsigned int format)
+{
+	uint32_t fb_id;
+	struct kmstest_fb fb_info;
+
+	fb_id = create_s3d_fb(&c->mode, &fb_info);
+	adjust_3d_timings(&c->mode, format);
+
+	if (drmModeSetCrtc(drm_fd, c->crtc, fb_id, 0, 0,
+			   &c->id, 1, &c->mode)) {
+		fprintf(stderr, "failed to set mode (%dx%d@%dHz): %s\n",
+			width, height, c->mode.vrefresh,
+			strerror(errno));
+	}
+}
+
+static void
+set_3d_mode(struct connector *c)
+{
+	int i;
+
+	for (i = 0; i < c->connector->count_modes; i++) {
+		unsigned int s3d_formats, format;
+
+		c->mode = c->connector->modes[i];
+
+		if (!c->mode_valid)
+			continue;
+
+		s3d_formats = c->mode.flags & DRMTEST_MODE_FLAG_3D_MASK;
+		if (!s3d_formats)
+			continue;
+
+		do {
+			format = 1 << (ffs(s3d_formats) - 1);
+
+			/*
+			 * Modify the mode flags to specify which 3D format is
+			 * being set.
+			 *
+			 * XXX: One would need to also clear the upper bits of
+			 * flags in case extra modes/flags are added
+			 */
+			c->mode.flags &= ~DRMTEST_MODE_FLAG_3D_MASK;
+			c->mode.flags |= format;
+
+			do_set_3d_format(c, format);
+
+			/*
+			 * The mode may have been adjusted for this format,
+			 * reset it to its origin timings
+			 */
+			c->mode = c->connector->modes[i];
+
+			if (qr_code){
+				set_single();
+				pause();
+			} else if (sleep_between_modes)
+				sleep(sleep_between_modes);
+
+			s3d_formats &= ~format;
+		} while (s3d_formats);
+
+	}
+
+	drmModeFreeEncoder(c->encoder);
+	drmModeFreeConnector(c->connector);
+}
+
 /*
  * Re-probe outputs and light up as many as possible.
  *
@@ -458,12 +681,29 @@  int update_display(void)
 		}
 	}
 
+	if (test_3d_modes) {
+		for (c = 0; c < resources->count_connectors; c++) {
+			struct connector *connector = &connectors[c];
+
+			connector->id = resources->connectors[c];
+
+			connector_find_preferred_mode(connector->id,
+						      -1UL,
+						      specified_mode_num,
+						      connector);
+			if (!connector->mode_valid)
+				continue;
+
+			set_3d_mode(connector);
+		}
+	}
+
 	free(connectors);
 	drmModeFreeResources(resources);
 	return 1;
 }
 
-static char optstr[] = "hiaf:s:d:p:mrto:";
+static char optstr[] = "3hiaf:s:d:p:mrto:";
 
 static void __attribute__((noreturn)) usage(char *name)
 {
@@ -474,6 +714,7 @@  static void __attribute__((noreturn)) usage(char *name)
 	fprintf(stderr, "\t-d\t<depth>\tbit depth of scanout buffer\n");
 	fprintf(stderr, "\t-p\t<planew,h>,<crtcx,y>,<crtcw,h> test overlay plane\n");
 	fprintf(stderr, "\t-m\ttest the preferred mode\n");
+	fprintf(stderr, "\t-3\ttest all 3D modes\n");
 	fprintf(stderr, "\t-t\tuse a tiled framebuffer\n");
 	fprintf(stderr, "\t-r\tprint a QR code on the screen whose content is \"pass\" for the automatic test\n");
 	fprintf(stderr, "\t-o\t<id of the display>,<number of the mode>\tonly test specified mode on the specified display\n");
@@ -536,6 +777,9 @@  int main(int argc, char **argv)
 	opterr = 0;
 	while ((c = getopt(argc, argv, optstr)) != -1) {
 		switch (c) {
+		case '3':
+			test_3d_modes = 1;
+			break;
 		case 'i':
 			opt_dump_info = true;
 			break;
@@ -594,11 +838,16 @@  int main(int argc, char **argv)
 		bpp = 32;
 
 	if (!test_all_modes && !force_mode && !test_preferred_mode &&
-	    specified_mode_num == -1)
+	    specified_mode_num == -1 && !test_3d_modes)
 		test_all_modes = 1;
 
 	drm_fd = drm_open_any();
 
+	if (test_3d_modes && drmSetCap(drm_fd, DRM_CAP_STEREO_3D, 1) < 0) {
+		fprintf(stderr, "DRM_CAP_STEREO_3D isn't supported\n");
+		goto out_close;
+	}
+
 	if (opt_dump_info) {
 		dump_info();
 		goto out_close;