[PATCH 8/9] nested: Add a renderer using subsurfaces

Neil Roberts neil at linux.intel.com
Mon Sep 9 08:41:44 PDT 2013


Adds a second renderer implementation to the nested compositor example
that creates a subsurface for each of the client's surfaces. The
client buffers are directly attached to the subsurface using the
EGL_WL_create_wayland_buffer_from_image extension instead of blitting
them in the redraw_handler.

The new renderer is always used if the parent compositor supports the
wl_subcompositor protocol and the EGL extension is available.
Otherwise it will fall back to the blit renderer.

There is an issue with this renderer in that it seems to cause the
client rendering to cycle between three buffers rather than just two.
I'm not exactly sure why but it could something to do with the
ordering of the frame callbacks and the release events.
---
 clients/nested.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 184 insertions(+), 5 deletions(-)

diff --git a/clients/nested.c b/clients/nested.c
index d229a9b..1ea91a1 100644
--- a/clients/nested.c
+++ b/clients/nested.c
@@ -67,16 +67,27 @@ struct nested_region {
 	pixman_region32_t region;
 };
 
+struct nested_buffer_reference {
+	struct nested_buffer *buffer;
+	struct wl_listener destroy_listener;
+};
+
 struct nested_buffer {
 	struct wl_resource *resource;
 	struct wl_signal destroy_signal;
 	struct wl_listener destroy_listener;
 	uint32_t busy_count;
-};
 
-struct nested_buffer_reference {
-	struct nested_buffer *buffer;
-	struct wl_listener destroy_listener;
+	/* A buffer in the parent compositor representing the same
+	 * data. This is created on-demand when the subsurface
+	 * renderer is used */
+	struct wl_buffer *parent_buffer;
+	/* This reference is used to mark when the parent buffer has
+	 * been attached to the subsurface. It will be unrefenced when
+	 * we receive a buffer release event. That way we won't inform
+	 * the client that the buffer is free until the parent
+	 * compositor is also finished with it */
+	struct nested_buffer_reference parent_ref;
 };
 
 struct nested_surface {
@@ -110,6 +121,13 @@ struct nested_blit_surface {
 	cairo_surface_t *cairo_surface;
 };
 
+/* Data used for the subsurface renderer */
+struct nested_ss_surface {
+	struct widget *widget;
+	struct wl_surface *surface;
+	struct wl_subsurface *subsurface;
+};
+
 struct nested_frame_callback {
 	struct wl_resource *resource;
 	struct wl_list link;
@@ -124,6 +142,7 @@ struct nested_renderer {
 };
 
 static const struct nested_renderer nested_blit_renderer;
+static const struct nested_renderer nested_ss_renderer;
 
 static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
 static PFNEGLCREATEIMAGEKHRPROC create_image;
@@ -131,6 +150,7 @@ static PFNEGLDESTROYIMAGEKHRPROC destroy_image;
 static PFNEGLBINDWAYLANDDISPLAYWL bind_display;
 static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display;
 static PFNEGLQUERYWAYLANDBUFFERWL query_buffer;
+static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image;
 
 static void
 nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
@@ -533,6 +553,10 @@ surface_commit(struct wl_client *client, struct wl_resource *resource)
 			    &surface->pending.frame_callback_list);
 	wl_list_init(&surface->pending.frame_callback_list);
 
+	/* FIXME: For the subsurface renderer we don't need to
+	 * actually redraw the window. However we do want to cause a
+	 * commit because the subsurface is synchronized. Ideally we
+	 * would just queue the commit */
 	window_schedule_redraw(nested->window);
 }
 
@@ -689,6 +713,7 @@ nested_init_compositor(struct nested *nested)
 {
 	const char *extensions;
 	struct wl_event_loop *loop;
+	int use_ss_renderer = 0;
 	int fd, ret;
 
 	wl_list_init(&nested->surface_list);
@@ -727,7 +752,25 @@ nested_init_compositor(struct nested *nested)
 		return -1;
 	}
 
-	nested->renderer = &nested_blit_renderer;
+	if (display_has_subcompositor(nested->display)) {
+		const char *func = "eglCreateWaylandBufferFromImageWL";
+		const char *ext = "EGL_WL_create_wayland_buffer_from_image";
+
+		if (strstr(extensions, ext)) {
+			create_wayland_buffer_from_image =
+				(void *) eglGetProcAddress(func);
+			use_ss_renderer = 1;
+		}
+	}
+
+	if (use_ss_renderer) {
+		printf("Using subsurfaces to render client surfaces\n");
+		nested->renderer = &nested_ss_renderer;
+	} else {
+		printf("Using local compositing with blits to "
+		       "render client surfaces\n");
+		nested->renderer = &nested_blit_renderer;
+	}
 
 	return 0;
 }
@@ -766,6 +809,8 @@ nested_destroy(struct nested *nested)
 	free(nested);
 }
 
+/*** blit renderer ***/
+
 static void
 blit_surface_init(struct nested_surface *surface)
 {
@@ -884,6 +929,140 @@ nested_blit_renderer = {
 	.surface_attach = blit_surface_attach
 };
 
+/*** subsurface renderer ***/
+
+static void
+ss_surface_init(struct nested_surface *surface)
+{
+	struct nested *nested = surface->nested;
+	struct nested_ss_surface *ss_surface =
+		zalloc(sizeof *ss_surface);
+	struct rectangle allocation;
+
+	ss_surface->widget =
+		window_add_subsurface(nested->window,
+				      nested,
+				      SUBSURFACE_SYNCHRONIZED);
+
+	ss_surface->surface = widget_get_wl_surface(ss_surface->widget);
+	ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget);
+
+	widget_get_allocation(nested->widget, &allocation);
+	wl_subsurface_set_position(ss_surface->subsurface,
+				   allocation.x + 10,
+				   allocation.y + 10);
+
+	surface->renderer_data = ss_surface;
+}
+
+static void
+ss_surface_fini(struct nested_surface *surface)
+{
+	struct nested_ss_surface *ss_surface = surface->renderer_data;
+
+	widget_destroy(ss_surface->widget);
+
+	free(ss_surface);
+}
+
+static void
+ss_render_clients(struct nested *nested,
+		  cairo_t *cr)
+{
+	/* The clients are composited by the parent compositor so we
+	 * don't need to do anything here */
+}
+
+static void
+ss_buffer_release(void *data, struct wl_buffer *wl_buffer)
+{
+	struct nested_buffer *buffer = data;
+
+	nested_buffer_reference(&buffer->parent_ref, NULL);
+}
+
+static struct wl_buffer_listener ss_buffer_listener = {
+   ss_buffer_release
+};
+
+static void
+ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time)
+{
+	struct nested_surface *surface = data;
+
+	flush_surface_frame_callback_list(surface, time);
+
+	if (callback)
+		wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener ss_frame_listener = {
+	ss_frame_callback
+};
+
+static void
+ss_surface_attach(struct nested_surface *surface,
+		  struct nested_buffer *buffer)
+{
+	struct nested *nested = surface->nested;
+	struct nested_ss_surface *ss_surface = surface->renderer_data;
+	struct wl_buffer *parent_buffer;
+	const pixman_box32_t *rects;
+	struct wl_callback *callback;
+	int n_rects, i;
+
+	if (buffer) {
+		/* Create a representation of the buffer in the parent
+		 * compositor if we haven't already */
+		if (buffer->parent_buffer == NULL) {
+			EGLDisplay *edpy = nested->egl_display;
+			EGLImageKHR image = surface->image;
+
+			buffer->parent_buffer =
+				create_wayland_buffer_from_image(edpy, image);
+
+			wl_buffer_add_listener(buffer->parent_buffer,
+					       &ss_buffer_listener,
+					       buffer);
+		}
+
+		parent_buffer = buffer->parent_buffer;
+
+		/* We'll take a reference to the buffer while the parent
+		 * compositor is using it so that we won't report the release
+		 * event until the parent has also finished with it */
+		nested_buffer_reference(&buffer->parent_ref, buffer);
+	} else {
+		parent_buffer = NULL;
+	}
+
+	wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0);
+
+	rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects);
+
+	for (i = 0; i < n_rects; i++) {
+		const pixman_box32_t *rect = rects + i;
+		wl_surface_damage(ss_surface->surface,
+				  rect->x1,
+				  rect->y1,
+				  rect->x2 - rect->x1,
+				  rect->y2 - rect->y1);
+	}
+
+	callback = wl_surface_frame(ss_surface->surface);
+	wl_callback_add_listener(callback, &ss_frame_listener, surface);
+
+	wl_surface_commit(ss_surface->surface);
+}
+
+static const struct nested_renderer
+nested_ss_renderer = {
+	.surface_init = ss_surface_init,
+	.surface_fini = ss_surface_fini,
+	.render_clients = ss_render_clients,
+	.surface_attach = ss_surface_attach
+};
+
 int
 main(int argc, char *argv[])
 {
-- 
1.8.3.1



More information about the wayland-devel mailing list