nested: Add a renderer using subsurfaces
authorNeil Roberts <neil@linux.intel.com>
Sun, 8 Sep 2013 23:41:29 +0000 (00:41 +0100)
committerKristian Høgsberg <krh@bitplanet.net>
Thu, 5 Dec 2013 00:34:07 +0000 (16:34 -0800)
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

index 096abc5..c0e1935 100644 (file)
@@ -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,14 @@ 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 wl_callback *frame_callback;
+};
+
 struct nested_frame_callback {
        struct wl_resource *resource;
        struct wl_list link;
@@ -124,6 +143,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 +151,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)
@@ -139,6 +160,10 @@ nested_buffer_destroy_handler(struct wl_listener *listener, void *data)
                container_of(listener, struct nested_buffer, destroy_listener);
 
        wl_signal_emit(&buffer->destroy_signal, buffer);
+
+       if (buffer->parent_buffer)
+               wl_buffer_destroy(buffer->parent_buffer);
+
        free(buffer);
 }
 
@@ -538,6 +563,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);
 }
 
@@ -694,6 +723,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);
@@ -732,7 +762,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;
 }
@@ -771,6 +819,8 @@ nested_destroy(struct nested *nested)
        free(nested);
 }
 
+/*** blit renderer ***/
+
 static void
 blit_surface_init(struct nested_surface *surface)
 {
@@ -889,6 +939,160 @@ 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);
+
+       if (ss_surface->frame_callback)
+               wl_callback_destroy(ss_surface->frame_callback);
+
+       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;
+       struct nested_ss_surface *ss_surface = surface->renderer_data;
+
+       flush_surface_frame_callback_list(surface, time);
+
+       if (callback)
+               wl_callback_destroy(callback);
+
+       ss_surface->frame_callback = NULL;
+}
+
+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;
+       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);
+       }
+
+       if (ss_surface->frame_callback)
+               wl_callback_destroy(ss_surface->frame_callback);
+
+       ss_surface->frame_callback = wl_surface_frame(ss_surface->surface);
+       wl_callback_add_listener(ss_surface->frame_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[])
 {