[PATCH v2] nested: Add a renderer using subsurfaces

Neil Roberts neil at linux.intel.com
Tue Sep 10 09:05:00 PDT 2013


I wrote:

> • For some reason if you move the cursor to somewhere near the
> middle of the surface then it will change to the resize grabber and
> it will let you resize the window.

It looks like this is happening because the toy toolkit gets confused
about the pointer position when it gets motion events for subsurfaces.
It assumes the position is always relative to the main surface of the
window. I suppose it wouldn't be too tricky to keep track of an offset
to add to the pointer position that you could update on enter events.
However in this version 2 of the patch I've just made it set a null
input region on the subsurface instead which is what the subsurfaces
client does as well.

-- >8 --

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.
---
 clients/nested.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 194 insertions(+), 5 deletions(-)

diff --git a/clients/nested.c b/clients/nested.c
index d229a9b..f52bed5 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,150 @@ 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 wl_compositor *compositor =
+		display_get_compositor(nested->display);
+	struct nested_ss_surface *ss_surface =
+		zalloc(sizeof *ss_surface);
+	struct rectangle allocation;
+	struct wl_region *region;
+
+	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);
+
+	/* The toy toolkit gets confused about the pointer position
+	 * when it gets motion events for a subsurface so we'll just
+	 * disable input on it */
+	region = wl_compositor_create_region(compositor);
+	wl_surface_set_input_region(ss_surface->surface, region);
+	wl_region_destroy(region);
+
+	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