[Intel-gfx] [PATCH] libdrm: add KMS page flip interface

Kristian Høgsberg krh at bitplanet.net
Sat May 9 00:18:04 CEST 2009


From: Jesse Barnes <jbarnes at virtuousgeek.org>

Sits on top of KMS page flipping ioctl.
---

Userspace side of the async pageflip ioctl plus a test case
added to modetest.c


 libdrm/xf86drmMode.c      |   13 ++++
 libdrm/xf86drmMode.h      |    4 +-
 shared-core/drm.h         |   26 ++++++++-
 shared-core/drm_mode.h    |   16 +++++
 tests/modetest/modetest.c |  137 ++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 185 insertions(+), 11 deletions(-)

diff --git a/libdrm/xf86drmMode.c b/libdrm/xf86drmMode.c
index ea11207..80ac57d 100644
--- a/libdrm/xf86drmMode.c
+++ b/libdrm/xf86drmMode.c
@@ -664,3 +664,16 @@ int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
 
 	return 0;
 }
+
+int drmModePageFlip(int fd, uint32_t crtc_id,
+		    uint32_t fb_id, void *user_data)
+{
+	struct drm_mode_page_flip flip;
+
+	flip.fb_id = fb_id;
+	flip.crtc_id = crtc_id;
+	flip.user_data = VOID2U64(user_data);
+	flip.flags = 0;
+
+	return drmIoctl(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
+}
diff --git a/libdrm/xf86drmMode.h b/libdrm/xf86drmMode.h
index 62304bb..730184e 100644
--- a/libdrm/xf86drmMode.h
+++ b/libdrm/xf86drmMode.h
@@ -259,8 +259,6 @@ typedef struct _drmModeConnector {
 	uint32_t *encoders; /**< List of encoder ids */
 } drmModeConnector, *drmModeConnectorPtr;
 
-
-
 extern void drmModeFreeModeInfo( drmModeModeInfoPtr ptr );
 extern void drmModeFreeResources( drmModeResPtr ptr );
 extern void drmModeFreeFB( drmModeFBPtr ptr );
@@ -357,8 +355,8 @@ extern void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr);
 extern int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id,
 				    uint64_t value);
 extern int drmCheckModesettingSupported(const char *busid);
-
 extern int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
 extern int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size,
 			       uint16_t *red, uint16_t *green, uint16_t *blue);
+extern int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, void *user_data);
diff --git a/shared-core/drm.h b/shared-core/drm.h
index b97ba09..b01fc43 100644
--- a/shared-core/drm.h
+++ b/shared-core/drm.h
@@ -1107,7 +1107,7 @@ struct drm_gem_open {
 #define DRM_IOCTL_MODE_GETFB		DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_ADDFB		DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
 #define DRM_IOCTL_MODE_RMFB		DRM_IOWR(0xAF, uint32_t)
-#define DRM_IOCTL_MODE_REPLACEFB	DRM_IOWR(0xB0, struct drm_mode_fb_cmd)
+#define DRM_IOCTL_MODE_PAGE_FLIP	DRM_IOW( 0xB0, struct drm_mode_page_flip)
 
 /*@}*/
 
@@ -1122,6 +1122,30 @@ struct drm_gem_open {
 #define DRM_COMMAND_BASE                0x40
 #define DRM_COMMAND_END                 0xA0
 
+/**
+ * Header for events written back to userspace on the drm fd.  The
+ * type defines the type of event, the length specifies the total
+ * length of the event (including the header), and user_data is
+ * typically a 64 bit value passed with the ioctl that triggered the
+ * event.  A read on the drm fd will always only return complete
+ * events, that is, if for example the read buffer is 100 bytes, and
+ * there are two 64 byte events pending, only one will be returned.
+ */
+struct drm_event {
+	uint32_t type;
+	uint32_t length;
+	uint64_t user_data;
+};
+
+#define DRM_EVENT_MODE_PAGE_FLIP 0x01
+
+struct drm_event_page_flip {
+	struct drm_event base;
+	uint32_t tv_sec;
+	uint32_t tv_usec;
+	uint32_t frame;
+};
+
 /* typedef area */
 #ifndef __KERNEL__
 typedef struct drm_clip_rect drm_clip_rect_t;
diff --git a/shared-core/drm_mode.h b/shared-core/drm_mode.h
index 9b92733..bebe4e7 100644
--- a/shared-core/drm_mode.h
+++ b/shared-core/drm_mode.h
@@ -270,4 +270,20 @@ struct drm_mode_crtc_lut {
 	uint64_t blue;
 };
 
+#define DRM_MODE_PAGE_FLIP_WAIT		(1<<0) /* block on previous page flip */
+#define DRM_MODE_PAGE_FLIP_FLAGS_MASK	(DRM_MODE_PAGE_FLIP_WAIT)
+
+struct drm_mode_page_flip {
+	/** Handle of new front buffer */
+	uint32_t fb_id;
+	uint32_t crtc_id;
+
+	/* 64 bit cookie returned to userspace in the page flip event. */
+	uint64_t user_data;
+	/**
+	 * page flip flags (wait on flip only for now)
+	 */
+	uint32_t flags;
+};
+
 #endif
diff --git a/tests/modetest/modetest.c b/tests/modetest/modetest.c
index 58e0e4c..6eed564 100644
--- a/tests/modetest/modetest.c
+++ b/tests/modetest/modetest.c
@@ -46,6 +46,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <sys/poll.h>
 
 #include "xf86drm.h"
 #include "xf86drmMode.h"
@@ -271,6 +272,9 @@ struct connector {
 	drmModeModeInfo *mode;
 	drmModeEncoder *encoder;
 	int crtc;
+	unsigned int fb_id;
+	struct timeval start;
+	int swap_count;
 };	
 
 static void
@@ -447,7 +451,7 @@ create_test_buffer(drm_intel_bufmgr *bufmgr,
 		d = div(i, width);
 		fb_ptr[i] = 0x00130502 * (d.quot >> 6) + 0x000a1120 * (d.rem >> 6);
 	}
-	drm_intel_bo_unmap(bo);
+	drm_intel_gem_bo_unmap(bo);
 
 	*bo_out = bo;
 	*stride_out = stride;
@@ -457,15 +461,51 @@ create_test_buffer(drm_intel_bufmgr *bufmgr,
 
 #endif
 
+static int
+create_black_buffer(drm_intel_bufmgr *bufmgr,
+		    int width, int height, int *stride_out, drm_intel_bo **bo_out)
+{
+	drm_intel_bo *bo;
+	unsigned int *fb_ptr;
+	int size, ret, i, stride;
+	div_t d;
+
+	/* Mode size at 32 bpp */
+	stride = width * 4;
+	size = stride * height;
+
+	bo = drm_intel_bo_alloc(bufmgr, "frontbuffer", size, 4096);
+	if (!bo) {
+		fprintf(stderr, "failed to alloc buffer: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	ret = drm_intel_gem_bo_map_gtt(bo);
+	if (ret) {
+		fprintf(stderr, "failed to GTT map buffer: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	memset(bo->virtual, 0, size);
+	drm_intel_gem_bo_unmap_gtt(bo);
+
+	*bo_out = bo;
+	*stride_out = stride;
+
+	return 0;
+}
+
 static void
-set_mode(struct connector *c, int count)
+set_mode(struct connector *c, int count, int page_flip)
 {
 	drmModeConnector *connector;
 	drmModeEncoder *encoder = NULL;
 	struct drm_mode_modeinfo *mode = NULL;
 	drm_intel_bufmgr *bufmgr;
-	drm_intel_bo *bo;
-	unsigned int fb_id;
+	drm_intel_bo *bo, *other_bo;
+	unsigned int fb_id, other_fb_id;
 	int i, j, ret, width, height, x, stride;
 
 	width = 0;
@@ -497,7 +537,6 @@ set_mode(struct connector *c, int count)
 
 	x = 0;
 	for (i = 0; i < count; i++) {
-		int crtc_id;
 		if (c[i].mode == NULL)
 			continue;
 
@@ -507,17 +546,96 @@ set_mode(struct connector *c, int count)
 		ret = drmModeSetCrtc(fd, c[i].crtc, fb_id, x, 0,
 				     &c[i].id, 1, c[i].mode);
 		x += c[i].mode->hdisplay;
+		c[i].fb_id = fb_id;
 
 		if (ret) {
 			fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
 			return;
 		}
 	}
+
+	if (!page_flip)
+		return;
+
+	if (create_black_buffer(bufmgr, width, height, &stride, &other_bo))
+		return;
+
+	ret = drmModeAddFB(fd, width, height, 32, 32, stride, other_bo->handle,
+			   &other_fb_id);
+	if (ret) {
+		fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+		if (c[i].mode == NULL)
+			continue;
+
+		drmModePageFlip(fd, c[i].crtc, other_fb_id, &c[i]);
+		gettimeofday(&c[i].start, NULL);
+		c[i].swap_count = 0;
+	}
+
+	while (1) {
+		struct connector *c;
+		unsigned int new_fb_id;
+		int len, ms;
+		struct drm_event_page_flip event;
+		struct pollfd pfd[2];
+
+		pfd[0].fd = 0;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = fd;
+		pfd[1].events = POLLIN;
+
+		if (poll(pfd, 2, -1) < 0) {
+			fprintf(stderr, "poll error\n");
+			break;
+		}
+
+		if (pfd[0].revents)
+			break;
+
+		len = read(fd, &event, sizeof event);
+
+		if (len < sizeof event)
+			break;
+		if (event.base.type != DRM_EVENT_MODE_PAGE_FLIP) {
+			fprintf(stderr,
+				"got unhandled event %d\n", event.base.type);
+			continue;
+		}
+
+		fprintf(stderr, "flip done, frame %d, time %d.%03d\n",
+			event.frame, event.tv_sec % 100,
+			event.tv_usec / 1000);
+
+		c = (struct connector *) (long) event.base.user_data;
+		if (c->fb_id == fb_id)
+			new_fb_id = other_fb_id;
+		else
+			new_fb_id = fb_id;
+			
+		drmModePageFlip(fd, c->crtc, new_fb_id, c);
+		c->fb_id = new_fb_id;
+		c->swap_count++;
+		if (c->swap_count == 60) {
+			struct timeval end;
+			double t;
+
+			gettimeofday(&end, NULL);
+			t = end.tv_sec + end.tv_usec * 1e-6 -
+				(c->start.tv_sec + c->start.tv_usec * 1e-6);
+			fprintf(stderr, "freq: %.02fHz\n", c->swap_count / t);
+			c->swap_count = 0;
+			c->start = end;
+		}
+	}
 }
 
 extern char *optarg;
 extern int optind, opterr, optopt;
-static char optstr[] = "ecpmfs:";
+static char optstr[] = "ecpmfs:v";
 
 void usage(char *name)
 {
@@ -527,6 +645,7 @@ void usage(char *name)
 	fprintf(stderr, "\t-p\tlist CRTCs (pipes)\n");
 	fprintf(stderr, "\t-m\tlist modes\n");
 	fprintf(stderr, "\t-f\tlist framebuffers\n");
+	fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
 	fprintf(stderr, "\t-s <connector_id>:<mode>\tset a mode\n");
 	fprintf(stderr, "\t-s <connector_id>@<crtc_id>:<mode>\tset a mode\n");
 	fprintf(stderr, "\n\tDefault is to dump all info.\n");
@@ -539,6 +658,7 @@ int main(int argc, char **argv)
 {
 	int c;
 	int encoders = 0, connectors = 0, crtcs = 0, framebuffers = 0;
+	int test_vsync = 0;
 	char *modules[] = { "i915", "radeon" };
 	char *modeset = NULL, *mode, *connector;
 	int i, connector_id, count = 0;
@@ -562,6 +682,9 @@ int main(int argc, char **argv)
 		case 'f':
 			framebuffers = 1;
 			break;
+		case 'v':
+			test_vsync = 1;
+			break;
 		case 's':
 			modeset = strdup(optarg);
 			con_args[count].crtc = -1;
@@ -614,7 +737,7 @@ int main(int argc, char **argv)
 	dump_resource(framebuffers);
 
 	if (count > 0) {
-		set_mode(con_args, count);
+		set_mode(con_args, count, test_vsync);
 		getchar();
 	}
 
-- 
1.6.2




More information about the Intel-gfx mailing list