[RFCv3 weston 13/15] clients: add presentation-shm demo

Pekka Paalanen ppaalanen at gmail.com
Fri Mar 7 04:04:01 PST 2014


From: Pekka Paalanen <pekka.paalanen at collabora.co.uk>

This started as a copy of simple-shm.c before it was converted to
xdg_shell.

This demo excercises the presentation feedback interface in three
different mode:

- A continuous repaint loop triggered by frame callbacks, and using
  immediate commits, just gathering presentation feedback and computing
  some time intervals for statistics.

- A burst mode, where it queues around 60 frames, and manages to sleep
  without any Wayland protocol traffic for around 1 second on a 60 Hz
  display while still animating continuously, until it needs to send
  the next burst.

- A burst mode with presentation feedback for every frame.

In all modes, all frames are pre-rendered at startup, so no rendering
happens during the animation.

By default, queueing the frames happens in random order, to test the
compositor's ability to order queued updates by the timestamp.

The target timestamps for queued frames are produced by a trivial
algorithm, without any attempt to explicitly and quickly synchronize to
the refresh cycle.

Signed-off-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
---
 .gitignore                 |   1 +
 Makefile.am                |   8 +
 clients/presentation-shm.c | 929 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 938 insertions(+)
 create mode 100644 clients/presentation-shm.c

diff --git a/.gitignore b/.gitignore
index e0a73c0..851e205 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ weston-gears
 weston-image
 weston-nested
 weston-nested-client
+weston-presentation-shm
 weston-resizor
 weston-scaler
 weston-simple-egl
diff --git a/Makefile.am b/Makefile.am
index 79e6a7d..753e8b4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -377,6 +377,7 @@ if BUILD_SIMPLE_CLIENTS
 demo_clients +=					\
 	weston-simple-shm			\
 	weston-simple-touch			\
+	weston-presentation-shm			\
 	weston-multi-resource
 
 weston_simple_shm_SOURCES = clients/simple-shm.c
@@ -390,6 +391,13 @@ weston_simple_touch_SOURCES = clients/simple-touch.c
 weston_simple_touch_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS)
 weston_simple_touch_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la
 
+weston_presentation_shm_SOURCES = clients/presentation-shm.c
+nodist_weston_presentation_shm_SOURCES =		\
+	protocol/presentation_timing-protocol.c		\
+	protocol/presentation_timing-client-protocol.h
+weston_presentation_shm_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS)
+weston_presentation_shm_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la
+
 weston_multi_resource_SOURCES = clients/multi-resource.c
 weston_multi_resource_CFLAGS = $(AM_CFLAGS) $(SIMPLE_CLIENT_CFLAGS)
 weston_multi_resource_LDADD = $(SIMPLE_CLIENT_LIBS) libshared.la -lm
diff --git a/clients/presentation-shm.c b/clients/presentation-shm.c
new file mode 100644
index 0000000..612312d
--- /dev/null
+++ b/clients/presentation-shm.c
@@ -0,0 +1,929 @@
+/*
+ * Copyright © 2011 Benjamin Franzke
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2014 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <time.h>
+
+#include <wayland-client.h>
+#include "../shared/os-compatibility.h"
+#include "presentation_timing-client-protocol.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+enum run_mode {
+	RUN_MODE_BURST,
+	RUN_MODE_FEEDBACK,
+	RUN_MODE_QUEUE_FEEDBACK,
+};
+
+struct output {
+	struct wl_output *output;
+	uint32_t name;
+	struct wl_list link;
+};
+
+struct display {
+	struct wl_display *display;
+	struct wl_registry *registry;
+	struct wl_compositor *compositor;
+	struct wl_shell *shell;
+
+	struct wl_shm *shm;
+	uint32_t formats;
+
+	struct presentation *presentation;
+	clockid_t clk_id;
+
+	struct wl_list output_list; /* struct output::link */
+};
+
+struct feedback {
+	struct window *window;
+	unsigned frame_no;
+	struct presentation_feedback *feedback;
+	struct timespec commit;
+	struct timespec target;
+	uint32_t frame_stamp;
+	struct wl_list link;
+	struct timespec present;
+};
+
+struct buffer {
+	struct wl_buffer *buffer;
+	void *shm_data;
+	int busy;
+};
+
+struct window {
+	struct display *display;
+	int width, height;
+	enum run_mode mode;
+	int randomized;
+	struct wl_surface *surface;
+	struct wl_shell_surface *shell_surface;
+
+	struct buffer *buffers;
+	int num_buffers;
+	int next;
+	int refresh_nsec;
+
+	struct wl_callback *callback;
+	struct wl_list feedback_list;
+
+	struct feedback *received_feedback;
+};
+
+#define NSEC_PER_SEC 1000000000
+
+static void
+timespec_add_nsec(struct timespec *result,
+		  const struct timespec *base, int64_t ns)
+{
+	lldiv_t v;
+
+	v = lldiv(base->tv_nsec + ns, NSEC_PER_SEC);
+	if (v.rem < 0) {
+		--v.quot;
+		v.rem += NSEC_PER_SEC;
+	}
+
+	result->tv_sec = base->tv_sec + v.quot;
+	result->tv_nsec = v.rem;
+}
+
+static void
+buffer_release(void *data, struct wl_buffer *buffer)
+{
+	struct buffer *mybuf = data;
+
+	mybuf->busy = 0;
+}
+
+static const struct wl_buffer_listener buffer_listener = {
+	buffer_release
+};
+
+static int
+create_shm_buffers(struct display *display, struct buffer **buffers,
+		   int num_buffers, int width, int height, uint32_t format)
+{
+	struct buffer *bufs;
+	struct wl_shm_pool *pool;
+	int fd, size, stride, offset;
+	void *data;
+	int i;
+
+	stride = width * 4;
+	size = stride * height * num_buffers;
+
+	fd = os_create_anonymous_file(size);
+	if (fd < 0) {
+		fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
+			size);
+		return -1;
+	}
+
+	data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	if (data == MAP_FAILED) {
+		fprintf(stderr, "mmap failed: %m\n");
+		close(fd);
+		return -1;
+	}
+
+	pool = wl_shm_create_pool(display->shm, fd, size);
+	offset = 0;
+
+	bufs = calloc(num_buffers, sizeof(*bufs));
+	assert(bufs);
+
+	for (i = 0; i < num_buffers; i++) {
+		bufs[i].buffer = wl_shm_pool_create_buffer(pool, offset,
+							   width, height,
+							   stride, format);
+		assert(bufs[i].buffer);
+		wl_buffer_add_listener(bufs[i].buffer,
+				       &buffer_listener, &bufs[i]);
+
+		bufs[i].shm_data = (char *)data + offset;
+		offset += stride * height;
+	}
+
+	wl_shm_pool_destroy(pool);
+	close(fd);
+
+	*buffers = bufs;
+
+	return 0;
+}
+
+static void
+handle_ping(void *data, struct wl_shell_surface *shell_surface,
+							uint32_t serial)
+{
+	wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void
+handle_configure(void *data, struct wl_shell_surface *shell_surface,
+		 uint32_t edges, int32_t width, int32_t height)
+{
+}
+
+static void
+handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
+{
+}
+
+static const struct wl_shell_surface_listener shell_surface_listener = {
+	handle_ping,
+	handle_configure,
+	handle_popup_done
+};
+
+static struct window *
+create_window(struct display *display, int width, int height,
+	      enum run_mode mode, int randomized)
+{
+	struct window *window;
+	int ret;
+
+	window = calloc(1, sizeof *window);
+	if (!window)
+		return NULL;
+
+	window->mode = mode;
+	window->randomized = randomized;
+	window->callback = NULL;
+	wl_list_init(&window->feedback_list);
+	window->display = display;
+	window->width = width;
+	window->height = height;
+	window->surface = wl_compositor_create_surface(display->compositor);
+	window->shell_surface = wl_shell_get_shell_surface(display->shell,
+							   window->surface);
+
+	if (window->shell_surface)
+		wl_shell_surface_add_listener(window->shell_surface,
+					      &shell_surface_listener, window);
+
+	wl_shell_surface_set_title(window->shell_surface, "presentation-shm");
+
+	wl_shell_surface_set_toplevel(window->shell_surface);
+
+	window->num_buffers = 60;
+	window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
+	window->next = 0;
+	ret = create_shm_buffers(window->display,
+				 &window->buffers, window->num_buffers,
+				 window->width, window->height,
+				 WL_SHM_FORMAT_XRGB8888);
+	assert(ret == 0);
+
+	return window;
+}
+
+static void
+destroy_feedback(struct feedback *feedback)
+{
+	if (feedback->feedback)
+		presentation_feedback_destroy(feedback->feedback);
+
+	wl_list_remove(&feedback->link);
+	free(feedback);
+}
+
+static void
+destroy_window(struct window *window)
+{
+	int i;
+
+	while (!wl_list_empty(&window->feedback_list)) {
+		struct feedback *f;
+
+		f = wl_container_of(window->feedback_list.next, f, link);
+		printf("clean up feedback %u\n", f->frame_no);
+		destroy_feedback(f);
+	}
+
+	if (window->callback)
+		wl_callback_destroy(window->callback);
+
+	wl_shell_surface_destroy(window->shell_surface);
+	wl_surface_destroy(window->surface);
+
+	for (i = 0; i < window->num_buffers; i++)
+		wl_buffer_destroy(window->buffers[i].buffer);
+	/* munmap(window->buffers[0].shm_data, size); */
+	free(window->buffers);
+
+	free(window);
+}
+
+static struct buffer *
+window_next_buffer(struct window *window)
+{
+	struct buffer *buf = &window->buffers[window->next];
+
+	window->next = (window->next + 1) % window->num_buffers;
+
+	return buf;
+}
+
+static void
+paint_pixels(void *image, int width, int height, uint32_t phase)
+{
+	const int halfh = height / 2;
+	const int halfw = width / 2;
+	uint32_t *pixel = image;
+	int y, or;
+	double ang = M_PI * 2.0 / 1000000.0 * phase;
+	double s = sin(ang);
+	double c = cos(ang);
+
+	/* squared radii thresholds */
+	or = (halfw < halfh ? halfw : halfh) - 16;
+	or *= or;
+
+	for (y = 0; y < height; y++) {
+		int x;
+		int oy = y - halfh;
+		int y2 = oy * oy;
+
+		for (x = 0; x < width; x++) {
+			int ox = x - halfw;
+			uint32_t v = 0xff000000;
+			double rx, ry;
+
+			if (ox * ox + y2 > or) {
+				if (ox * oy > 0)
+					*pixel++ = 0xff000000;
+				else
+					*pixel++ = 0xffffffff;
+				continue;
+			}
+
+			rx = c * ox + s * oy;
+			ry = -s * ox + c * oy;
+
+			if (rx < 0.0)
+				v |= 0x00ff0000;
+			if (ry < 0.0)
+				v |= 0x0000ff00;
+			if ((rx < 0.0) == (ry < 0.0))
+				v |= 0x000000ff;
+
+			*pixel++ = v;
+		}
+	}
+}
+
+static void
+feedback_sync_output(void *data,
+		     struct presentation_feedback *presentation_feedback,
+		     struct wl_output *output)
+{
+	/* not interested */
+}
+
+static uint32_t
+timespec_to_ms(const struct timespec *ts)
+{
+	return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
+}
+
+static void
+timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi,
+		    uint32_t tv_sec_lo, uint32_t tv_nsec)
+{
+	tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
+	tm->tv_nsec = tv_nsec;
+}
+
+static int
+timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
+{
+	time_t secs = a->tv_sec - b->tv_sec;
+	long nsec = a->tv_nsec - b->tv_nsec;
+
+	return secs * 1000000 + nsec / 1000;
+}
+
+static void
+feedback_presented(void *data,
+		   struct presentation_feedback *presentation_feedback,
+		   uint32_t tv_sec_hi,
+		   uint32_t tv_sec_lo,
+		   uint32_t tv_nsec,
+		   uint32_t refresh_nsec,
+		   uint32_t seq_hi,
+		   uint32_t seq_lo)
+{
+	struct feedback *feedback = data;
+	struct window *window = feedback->window;
+	struct feedback *prev_feedback = window->received_feedback;
+	uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
+	const struct timespec *prevpresent;
+	uint32_t commit, present;
+	uint32_t f2c, c2p, f2p;
+	int p2p, t2p;
+
+	timespec_from_proto(&feedback->present, tv_sec_hi, tv_sec_lo, tv_nsec);
+	commit = timespec_to_ms(&feedback->commit);
+	present = timespec_to_ms(&feedback->present);
+
+	if (prev_feedback)
+		prevpresent = &prev_feedback->present;
+	else
+		prevpresent = &feedback->present;
+
+	f2c = commit - feedback->frame_stamp;
+	c2p = present - commit;
+	f2p = present - feedback->frame_stamp;
+	p2p = timespec_diff_to_usec(&feedback->present, prevpresent);
+	t2p = timespec_diff_to_usec(&feedback->present, &feedback->target);
+
+	if (window->mode == RUN_MODE_QUEUE_FEEDBACK) {
+		printf("%6u: c2p %4u ms, p2p %5d us, t2p %5d us, "
+			"seq %" PRIu64 "\n", feedback->frame_no, c2p,
+			p2p, t2p, seq);
+	} else {
+		printf("%6u: f2c %2u ms, c2p %2u ms, f2p %2u ms, p2p %5d us, "
+			"t2p %5d, seq %" PRIu64 "\n", feedback->frame_no,
+			f2c, c2p, f2p, p2p, t2p, seq);
+	}
+
+	if (window->received_feedback)
+		destroy_feedback(window->received_feedback);
+	window->received_feedback = feedback;
+}
+
+static void
+feedback_discarded(void *data,
+		   struct presentation_feedback *presentation_feedback)
+{
+	struct feedback *feedback = data;
+
+	printf("discarded %u\n", feedback->frame_no);
+
+	destroy_feedback(feedback);
+}
+
+static const struct presentation_feedback_listener feedback_listener = {
+	feedback_sync_output,
+	feedback_presented,
+	feedback_discarded
+};
+
+static void
+window_create_feedback(struct window *window, uint32_t frame_stamp,
+		       struct timespec *target)
+{
+	static unsigned seq;
+	struct presentation *pres = window->display->presentation;
+	struct feedback *feedback;
+
+	seq++;
+
+	if (!pres)
+		return;
+
+	feedback = calloc(1, sizeof *feedback);
+	if (!feedback)
+		return;
+
+	feedback->window = window;
+	feedback->feedback = presentation_feedback(pres, window->surface);
+	presentation_feedback_add_listener(feedback->feedback,
+					   &feedback_listener, feedback);
+
+	feedback->frame_no = seq;
+	clock_gettime(window->display->clk_id, &feedback->commit);
+	feedback->frame_stamp = frame_stamp;
+
+	if (target)
+		feedback->target = *target;
+	else
+		feedback->target = feedback->commit;
+
+	wl_list_insert(&window->feedback_list, &feedback->link);
+}
+
+static const struct wl_callback_listener frame_listener_mode_feedback;
+
+static void
+redraw_mode_feedback(void *data, struct wl_callback *callback, uint32_t time)
+{
+	struct window *window = data;
+	struct buffer *buffer;
+
+	buffer = window_next_buffer(window);
+	assert(buffer);
+
+	wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+	wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+
+	window_create_feedback(window, time, NULL);
+
+	if (callback)
+		wl_callback_destroy(callback);
+
+	window->callback = wl_surface_frame(window->surface);
+	wl_callback_add_listener(window->callback,
+				 &frame_listener_mode_feedback, window);
+	wl_surface_commit(window->surface);
+	buffer->busy = 1;
+}
+
+static const struct wl_callback_listener frame_listener_mode_feedback = {
+	redraw_mode_feedback
+};
+
+static void
+presentation_queue_timespec(struct presentation *pres, struct wl_surface *s,
+			    const struct timespec *ts)
+{
+	uint64_t secs = ts->tv_sec;
+
+	presentation_queue(pres, s, secs >> 32, secs & 0xffffffff, ts->tv_nsec);
+}
+
+struct index_element {
+	int index;
+	int key;
+};
+
+static int
+index_element_cmp(const void *a, const void * b)
+{
+	const struct index_element *ia = a;
+	const struct index_element *ib = b;
+
+	return ia->key - ib->key;
+}
+
+static void
+randomize_sequence(struct index_element *arr, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++) {
+		arr[i].index = i;
+		arr[i].key = random();
+	}
+
+	qsort(arr, n, sizeof *arr, index_element_cmp);
+}
+
+static const struct presentation_feedback_listener feedkick_listener;
+
+static void
+queue_more_frames(struct window *window, const struct timespec *base)
+{
+	struct presentation *pres = window->display->presentation;
+	const int n = window->num_buffers;
+	int begin = window->next;
+	int count = 0;
+	int i;
+	struct index_element ie[100];
+
+	assert(ARRAY_LENGTH(ie) >= (unsigned)n);
+
+	while (count < n && !window->buffers[(count + begin) % n].busy)
+		count++;
+
+	if (count < 1) {
+		fprintf(stderr, "Error: All busy?!\n");
+		exit(EXIT_FAILURE);
+	}
+
+	randomize_sequence(ie, count);
+	for (i = 0; i < count; i++) {
+		int ind;
+		struct buffer *buffer;
+		struct timespec target;
+
+		if (window->randomized)
+			ind = ie[i].index;
+		else
+			ind = i;
+
+		buffer = &window->buffers[(begin + ind) % n];
+
+		timespec_add_nsec(&target, base,
+				  (ind + 2) * window->refresh_nsec);
+		presentation_queue_timespec(pres, window->surface, &target);
+
+		if (ind == count - 2) {
+			struct presentation_feedback *fback;
+
+			fback = presentation_feedback(pres, window->surface);
+			presentation_feedback_add_listener(fback,
+							   &feedkick_listener,
+							   window);
+		}
+
+		if (window->mode == RUN_MODE_QUEUE_FEEDBACK)
+			window_create_feedback(window, 0, &target);
+
+		wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+		wl_surface_commit(window->surface);
+		buffer->busy = 1;
+	}
+
+	window->next = (begin + count) % n;
+}
+
+static void
+feedkick_presented(void *data,
+		   struct presentation_feedback *presentation_feedback,
+		   uint32_t tv_sec_hi,
+		   uint32_t tv_sec_lo,
+		   uint32_t tv_nsec,
+		   uint32_t refresh_nsec,
+		   uint32_t seq_hi,
+		   uint32_t seq_lo)
+{
+	struct window *window = data;
+	struct timespec base;
+
+	presentation_feedback_destroy(presentation_feedback);
+	timespec_from_proto(&base, tv_sec_hi, tv_sec_lo, tv_nsec);
+	window->refresh_nsec = refresh_nsec;
+	queue_more_frames(window, &base);
+}
+
+static void
+feedkick_discarded(void *data,
+		   struct presentation_feedback *presentation_feedback)
+{
+	struct window *window = data;
+	struct timespec base;
+
+	presentation_feedback_destroy(presentation_feedback);
+	clock_gettime(window->display->clk_id, &base);
+	queue_more_frames(window, &base);
+}
+
+static const struct presentation_feedback_listener feedkick_listener = {
+	feedback_sync_output,
+	feedkick_presented,
+	feedkick_discarded
+};
+
+static void
+firstdraw_mode_burst(struct window *window)
+{
+	struct presentation *pres = window->display->presentation;
+	struct presentation_feedback *fback;
+	struct buffer *buffer;
+
+	buffer = window_next_buffer(window);
+	assert(buffer);
+
+	fback = presentation_feedback(pres, window->surface);
+	presentation_feedback_add_listener(fback,
+					   &feedkick_listener, window);
+
+	if (window->mode == RUN_MODE_QUEUE_FEEDBACK)
+		window_create_feedback(window, 0, NULL);
+
+	wl_surface_attach(window->surface, buffer->buffer, 0, 0);
+	wl_surface_damage(window->surface, 0, 0, window->width, window->height);
+	wl_surface_commit(window->surface);
+	buffer->busy = 1;
+}
+
+static void
+window_prerender(struct window *window)
+{
+	int i;
+	int timefactor = 1000000 / window->num_buffers;
+
+	for (i = 0; i < window->num_buffers; i++) {
+		struct buffer *buf = &window->buffers[i];
+
+		if (buf->busy)
+			fprintf(stderr, "wl_buffer id %u) busy\n",
+				wl_proxy_get_id(
+					(struct wl_proxy *)buf->buffer));
+
+		paint_pixels(buf->shm_data, window->width, window->height,
+			     i * timefactor);
+	}
+}
+
+static void
+output_destroy(struct output *o)
+{
+	wl_output_destroy(o->output);
+	wl_list_remove(&o->link);
+	free(o);
+}
+
+static void
+display_add_output(struct display *d, uint32_t name, uint32_t version)
+{
+	struct output *o;
+
+	o = calloc(1, sizeof(*o));
+	assert(o);
+
+	o->output = wl_registry_bind(d->registry, name,
+				     &wl_output_interface, 1);
+	o->name = name;
+	wl_list_insert(&d->output_list, &o->link);
+}
+
+static void
+presentation_clock_id(void *data, struct presentation *presentation,
+		      uint32_t clk_id)
+{
+	struct display *d = data;
+
+	d->clk_id = clk_id;
+}
+
+static const struct presentation_listener presentation_listener = {
+	presentation_clock_id
+};
+
+static void
+shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
+{
+	struct display *d = data;
+
+	d->formats |= (1 << format);
+}
+
+static const struct wl_shm_listener shm_listener = {
+	shm_format
+};
+
+static void
+registry_handle_global(void *data, struct wl_registry *registry,
+		       uint32_t name, const char *interface, uint32_t version)
+{
+	struct display *d = data;
+
+	if (strcmp(interface, "wl_compositor") == 0) {
+		d->compositor =
+			wl_registry_bind(registry,
+					 name, &wl_compositor_interface, 1);
+	} else if (strcmp(interface, "wl_shell") == 0) {
+		d->shell = wl_registry_bind(registry,
+					    name, &wl_shell_interface, 1);
+	} else if (strcmp(interface, "wl_shm") == 0) {
+		d->shm = wl_registry_bind(registry,
+					  name, &wl_shm_interface, 1);
+		wl_shm_add_listener(d->shm, &shm_listener, d);
+	} else if (strcmp(interface, "wl_output") == 0) {
+		display_add_output(d, name, version);
+	} else if (strcmp(interface, "presentation") == 0) {
+		d->presentation =
+			wl_registry_bind(registry,
+					 name, &presentation_interface, 1);
+		presentation_add_listener(d->presentation,
+					  &presentation_listener, d);
+	}
+}
+
+static void
+registry_handle_global_remove(void *data, struct wl_registry *registry,
+			      uint32_t name)
+{
+	struct display *d = data;
+	struct output *output, *otmp;
+
+	wl_list_for_each_safe(output, otmp, &d->output_list, link) {
+		if (output->name != name)
+			continue;
+
+		output_destroy(output);
+	}
+}
+
+static const struct wl_registry_listener registry_listener = {
+	registry_handle_global,
+	registry_handle_global_remove
+};
+
+static struct display *
+create_display(void)
+{
+	struct display *display;
+
+	display = malloc(sizeof *display);
+	if (display == NULL) {
+		fprintf(stderr, "out of memory\n");
+		exit(1);
+	}
+	display->display = wl_display_connect(NULL);
+	assert(display->display);
+
+	display->formats = 0;
+	display->clk_id = -1;
+	wl_list_init(&display->output_list);
+	display->registry = wl_display_get_registry(display->display);
+	wl_registry_add_listener(display->registry,
+				 &registry_listener, display);
+	wl_display_roundtrip(display->display);
+	if (display->shm == NULL) {
+		fprintf(stderr, "No wl_shm global\n");
+		exit(1);
+	}
+
+	wl_display_roundtrip(display->display);
+
+	if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
+		fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
+		exit(1);
+	}
+
+	wl_display_get_fd(display->display);
+	
+	return display;
+}
+
+static void
+destroy_display(struct display *display)
+{
+	while (!wl_list_empty(&display->output_list)) {
+		struct output *o;
+
+		o = wl_container_of(display->output_list.next, o, link);
+		output_destroy(o);
+	}
+
+	if (display->shm)
+		wl_shm_destroy(display->shm);
+
+	if (display->shell)
+		wl_shell_destroy(display->shell);
+
+	if (display->compositor)
+		wl_compositor_destroy(display->compositor);
+
+	wl_registry_destroy(display->registry);
+	wl_display_flush(display->display);
+	wl_display_disconnect(display->display);
+	free(display);
+}
+
+static int running = 1;
+
+static void
+signal_int(int signum)
+{
+	running = 0;
+}
+
+static void
+usage(const char *prog, int exit_code)
+{
+	fprintf(stderr, "Usage: %s [mode] [options]\n"
+		"where 'mode' is one of\n"
+		"  -b\trun in burst mode\n"
+		"  -f\trun in feedback mode (default)\n"
+		"  -q\trun in queue (burst) mode with feedback\n"
+		"and 'options' may include\n"
+		"  -n\tdo not randomize the queueing order\n\n",
+		prog);
+
+	fprintf(stderr, "Printed timing statistics, depending on mode:\n"
+		"  commit sequence number\n"
+		"  f2c: time from frame callback timestamp to commit\n"
+		"  c2p: time from commit to presentation\n"
+		"  f2p: time from frame callback timestamp to presentation\n"
+		"  p2p: time from previous presentation to this one\n"
+		"  t2p: time from target timestamp to presentation\n"
+		"  seq: MSC\n");
+
+
+	exit(exit_code);
+}
+
+int
+main(int argc, char **argv)
+{
+	struct sigaction sigint;
+	struct display *display;
+	struct window *window;
+	int ret = 0;
+	enum run_mode mode = RUN_MODE_FEEDBACK;
+	int randomized = 1;
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		if (strcmp("-b", argv[i]) == 0)
+			mode = RUN_MODE_BURST;
+		else if (strcmp("-f", argv[i]) == 0)
+			mode = RUN_MODE_FEEDBACK;
+		else if (strcmp("-q", argv[i]) == 0)
+			mode = RUN_MODE_QUEUE_FEEDBACK;
+		else if (strcmp("-n", argv[i]) == 0)
+			randomized = 0;
+		else
+			usage(argv[0], EXIT_FAILURE);
+	}
+
+	display = create_display();
+	window = create_window(display, 250, 250, mode, randomized);
+	if (!window)
+		return 1;
+
+	sigint.sa_handler = signal_int;
+	sigemptyset(&sigint.sa_mask);
+	sigint.sa_flags = SA_RESETHAND;
+	sigaction(SIGINT, &sigint, NULL);
+
+	window_prerender(window);
+
+	switch (mode) {
+	case RUN_MODE_FEEDBACK:
+		redraw_mode_feedback(window, NULL, 0);
+		break;
+	case RUN_MODE_QUEUE_FEEDBACK:
+	case RUN_MODE_BURST:
+		firstdraw_mode_burst(window);
+		break;
+	}
+
+	while (running && ret != -1)
+		ret = wl_display_dispatch(display->display);
+
+	fprintf(stderr, "presentation-shm exiting\n");
+	destroy_window(window);
+	destroy_display(display);
+
+	return 0;
+}
-- 
1.8.3.2



More information about the wayland-devel mailing list