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.
dev
Neil Roberts 11 years ago committed by Kristian Høgsberg
parent 5e10a04481
commit 1f020a1fdb
  1. 212
      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;
}
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[])
{

Loading…
Cancel
Save