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.
This commit is contained in:
Neil Roberts 2013-09-09 00:41:29 +01:00 committed by Kristian Høgsberg
parent 5e10a04481
commit 1f020a1fdb

View 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[])
{