From 1f020a1fdbc09b04fdc0a4330f28afadc32266cb Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 9 Sep 2013 00:41:29 +0100 Subject: [PATCH] nested: Add a renderer using subsurfaces 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 | 214 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 209 insertions(+), 5 deletions(-) diff --git a/clients/nested.c b/clients/nested.c index 096abc55..c0e19350 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,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[]) {