From ff2fd46e9ca7644231931136af62069d74a49397 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Sun, 27 Oct 2013 22:24:58 -0500 Subject: [PATCH] compositor-wayland: Add pixman/SHM fallback mode This also adds a --use-pixman command-line option to enable the pixman renderer explicitly. Signed-off-by: Jason Ekstrand --- src/compositor-wayland.c | 468 +++++++++++++++++++++++++++++++-------- src/compositor.c | 1 + 2 files changed, 374 insertions(+), 95 deletions(-) diff --git a/src/compositor-wayland.c b/src/compositor-wayland.c index a661f404..bbadfc32 100644 --- a/src/compositor-wayland.c +++ b/src/compositor-wayland.c @@ -1,5 +1,6 @@ /* * Copyright © 2010-2011 Benjamin Franzke + * Copyright © 2013 Jason Ekstrand * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -36,6 +37,7 @@ #include "compositor.h" #include "gl-renderer.h" +#include "pixman-renderer.h" #include "../shared/image-loader.h" #include "../shared/os-compatibility.h" #include "../shared/cairo-util.h" @@ -59,6 +61,8 @@ struct wayland_compositor { uint32_t event_mask; } parent; + int use_pixman; + struct theme *theme; cairo_device_t *frame_device; struct wl_cursor_theme *cursor_theme; @@ -74,22 +78,45 @@ struct wayland_output { int draw_initial_frame; struct wl_surface *surface; struct wl_shell_surface *shell_surface; - struct wl_egl_window *egl_window; } parent; int keyboard_count; struct frame *frame; + struct { - cairo_surface_t *top; - cairo_surface_t *left; - cairo_surface_t *right; - cairo_surface_t *bottom; - } border; + struct wl_egl_window *egl_window; + struct { + cairo_surface_t *top; + cairo_surface_t *left; + cairo_surface_t *right; + cairo_surface_t *bottom; + } border; + } gl; + + struct { + struct wl_list buffers; + struct wl_list free_buffers; + } shm; struct weston_mode mode; }; +struct wayland_shm_buffer { + struct wayland_output *output; + struct wl_list link; + struct wl_list free_link; + + struct wl_buffer *buffer; + void *data; + size_t size; + pixman_region32_t damage; + int frame_damaged; + + pixman_image_t *pm_image; + cairo_surface_t *c_surface; +}; + struct wayland_input { struct weston_seat base; struct wayland_compositor *compositor; @@ -117,42 +144,59 @@ struct wayland_input { struct gl_renderer_interface *gl_renderer; static void -frame_done(void *data, struct wl_callback *callback, uint32_t time) +wayland_shm_buffer_destroy(struct wayland_shm_buffer *buffer) { - struct weston_output *output = data; + cairo_surface_destroy(buffer->c_surface); + pixman_image_unref(buffer->pm_image); - wl_callback_destroy(callback); - weston_output_finish_frame(output, time); -} + wl_buffer_destroy(buffer->buffer); + munmap(buffer->data, buffer->size); -static const struct wl_callback_listener frame_listener = { - frame_done -}; + pixman_region32_fini(&buffer->damage); + + wl_list_remove(&buffer->link); + wl_list_remove(&buffer->free_link); + free(buffer); +} static void buffer_release(void *data, struct wl_buffer *buffer) { - wl_buffer_destroy(buffer); + struct wayland_shm_buffer *sb = data; + + if (sb->output) { + wl_list_insert(&sb->output->shm.free_buffers, &sb->free_link); + } else { + wayland_shm_buffer_destroy(sb); + } } static const struct wl_buffer_listener buffer_listener = { buffer_release }; -static void -draw_initial_frame(struct wayland_output *output) +static struct wayland_shm_buffer * +wayland_output_get_shm_buffer(struct wayland_output *output) { struct wayland_compositor *c = (struct wayland_compositor *) output->base.compositor; struct wl_shm *shm = c->parent.shm; - struct wl_surface *surface = output->parent.surface; - struct wl_shm_pool *pool; - struct wl_buffer *buffer; + struct wayland_shm_buffer *sb; + struct wl_shm_pool *pool; int width, height, stride; - int size; + int32_t fx, fy; int fd; - void *data; + unsigned char *data; + + if (!wl_list_empty(&output->shm.free_buffers)) { + sb = container_of(output->shm.free_buffers.next, + struct wayland_shm_buffer, free_link); + wl_list_remove(&sb->free_link); + wl_list_init(&sb->free_link); + + return sb; + } if (output->frame) { width = frame_width(output->frame); @@ -162,39 +206,92 @@ draw_initial_frame(struct wayland_output *output) height = output->mode.height; } - stride = width * 4; - size = height * stride; + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - fd = os_create_anonymous_file(size); + fd = os_create_anonymous_file(height * stride); if (fd < 0) { perror("os_create_anonymous_file"); - return; + return NULL; } - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + data = mmap(NULL, height * stride, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { perror("mmap"); close(fd); - return; + return NULL; } - pool = wl_shm_create_pool(shm, fd, size); + sb = zalloc(sizeof *sb); + + sb->output = output; + wl_list_init(&sb->free_link); + wl_list_insert(&output->shm.buffers, &sb->link); + + pixman_region32_init_rect(&sb->damage, 0, 0, + output->base.width, output->base.height); + sb->frame_damaged = 1; - buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, - stride, - WL_SHM_FORMAT_ARGB8888); - wl_buffer_add_listener(buffer, &buffer_listener, buffer); + sb->data = data; + sb->size = height * stride; + + pool = wl_shm_create_pool(shm, fd, sb->size); + + sb->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, + stride, + WL_SHM_FORMAT_ARGB8888); + wl_buffer_add_listener(sb->buffer, &buffer_listener, sb); wl_shm_pool_destroy(pool); close(fd); - memset(data, 0, size); + memset(data, 0, sb->size); + + sb->c_surface = + cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, + width, height, stride); - wl_surface_attach(surface, buffer, 0, 0); + fx = 0; + fy = 0; + if (output->frame) + frame_interior(output->frame, &fx, &fy, 0, 0); + sb->pm_image = + pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, + (uint32_t *)(data + fy * stride) + fx, + stride); + + return sb; +} + +static void +frame_done(void *data, struct wl_callback *callback, uint32_t time) +{ + struct weston_output *output = data; + + wl_callback_destroy(callback); + weston_output_finish_frame(output, time); +} + +static const struct wl_callback_listener frame_listener = { + frame_done +}; + +static void +draw_initial_frame(struct wayland_output *output) +{ + struct wayland_shm_buffer *sb; + + sb = wayland_output_get_shm_buffer(output); + + /* If we are rendering with GL, then orphan it so that it gets + * destroyed immediately */ + if (output->gl.egl_window) + sb->output = NULL; + + wl_surface_attach(output->parent.surface, sb->buffer, 0, 0); /* We only need to damage some part, as its only transparant * pixels anyway. */ - wl_surface_damage(surface, 0, 0, 1, 1); + wl_surface_damage(output->parent.surface, 0, 0, 1, 1); } static void @@ -212,59 +309,59 @@ wayland_output_update_gl_border(struct wayland_output *output) fheight = frame_height(output->frame); frame_interior(output->frame, &ix, &iy, &iwidth, &iheight); - if (!output->border.top) - output->border.top = + if (!output->gl.border.top) + output->gl.border.top = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, fwidth, iy); - cr = cairo_create(output->border.top); + cr = cairo_create(output->gl.border.top); frame_repaint(output->frame, cr); cairo_destroy(cr); gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_TOP, fwidth, iy, - cairo_image_surface_get_stride(output->border.top) / 4, - cairo_image_surface_get_data(output->border.top)); + cairo_image_surface_get_stride(output->gl.border.top) / 4, + cairo_image_surface_get_data(output->gl.border.top)); - if (!output->border.left) - output->border.left = + if (!output->gl.border.left) + output->gl.border.left = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ix, 1); - cr = cairo_create(output->border.left); + cr = cairo_create(output->gl.border.left); cairo_translate(cr, 0, -iy); frame_repaint(output->frame, cr); cairo_destroy(cr); gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_LEFT, ix, 1, - cairo_image_surface_get_stride(output->border.left) / 4, - cairo_image_surface_get_data(output->border.left)); + cairo_image_surface_get_stride(output->gl.border.left) / 4, + cairo_image_surface_get_data(output->gl.border.left)); - if (!output->border.right) - output->border.right = + if (!output->gl.border.right) + output->gl.border.right = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, fwidth - (ix + iwidth), 1); - cr = cairo_create(output->border.right); + cr = cairo_create(output->gl.border.right); cairo_translate(cr, -(iwidth + ix), -iy); frame_repaint(output->frame, cr); cairo_destroy(cr); gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_RIGHT, fwidth - (ix + iwidth), 1, - cairo_image_surface_get_stride(output->border.right) / 4, - cairo_image_surface_get_data(output->border.right)); + cairo_image_surface_get_stride(output->gl.border.right) / 4, + cairo_image_surface_get_data(output->gl.border.right)); - if (!output->border.bottom) - output->border.bottom = + if (!output->gl.border.bottom) + output->gl.border.bottom = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, fwidth, fheight - (iy + iheight)); - cr = cairo_create(output->border.bottom); + cr = cairo_create(output->gl.border.bottom); cairo_translate(cr, 0, -(iy + iheight)); frame_repaint(output->frame, cr); cairo_destroy(cr); gl_renderer->output_set_border(&output->base, GL_RENDERER_BORDER_BOTTOM, fwidth, fheight - (iy + iheight), - cairo_image_surface_get_stride(output->border.bottom) / 4, - cairo_image_surface_get_data(output->border.bottom)); + cairo_image_surface_get_stride(output->gl.border.bottom) / 4, + cairo_image_surface_get_data(output->gl.border.bottom)); } static void @@ -293,8 +390,8 @@ wayland_output_start_repaint_loop(struct weston_output *output_base) } static int -wayland_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage) +wayland_output_repaint_gl(struct weston_output *output_base, + pixman_region32_t *damage) { struct wayland_output *output = (struct wayland_output *) output_base; struct weston_compositor *ec = output->base.compositor; @@ -312,23 +409,154 @@ wayland_output_repaint(struct weston_output *output_base, return 0; } +static void +wayland_output_update_shm_border(struct wayland_shm_buffer *buffer) +{ + int32_t ix, iy, iwidth, iheight, fwidth, fheight; + cairo_t *cr; + + if (!buffer->output->frame || !buffer->frame_damaged) + return; + + cr = cairo_create(buffer->c_surface); + + frame_interior(buffer->output->frame, &ix, &iy, &iwidth, &iheight); + fwidth = frame_width(buffer->output->frame); + fheight = frame_height(buffer->output->frame); + + /* Set the clip so we don't unnecisaraly damage the surface */ + cairo_move_to(cr, ix, iy); + cairo_rel_line_to(cr, iwidth, 0); + cairo_rel_line_to(cr, 0, iheight); + cairo_rel_line_to(cr, -iwidth, 0); + cairo_line_to(cr, ix, iy); + cairo_line_to(cr, 0, iy); + cairo_line_to(cr, 0, fheight); + cairo_line_to(cr, fwidth, fheight); + cairo_line_to(cr, fwidth, 0); + cairo_line_to(cr, 0, 0); + cairo_line_to(cr, 0, iy); + cairo_close_path(cr); + cairo_clip(cr); + + /* Draw using a pattern so that the final result gets clipped */ + cairo_push_group(cr); + frame_repaint(buffer->output->frame, cr); + cairo_pop_group_to_source(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + + cairo_destroy(cr); +} + +static void +wayland_shm_buffer_attach(struct wayland_shm_buffer *sb) +{ + pixman_region32_t damage; + pixman_box32_t *rects; + int32_t ix, iy, iwidth, iheight, fwidth, fheight; + int i, n; + + if (sb->output->frame) { + frame_interior(sb->output->frame, &ix, &iy, &iwidth, &iheight); + fwidth = frame_width(sb->output->frame); + fheight = frame_height(sb->output->frame); + + pixman_region32_init(&damage); + pixman_region32_copy(&damage, &sb->damage); + pixman_region32_translate(&damage, ix, iy); + + if (sb->frame_damaged) { + pixman_region32_union_rect(&damage, &damage, + 0, 0, fwidth, iy); + pixman_region32_union_rect(&damage, &damage, + 0, iy, ix, iheight); + pixman_region32_union_rect(&damage, &damage, + ix + iwidth, iy, + fwidth - (ix + iwidth), iheight); + pixman_region32_union_rect(&damage, &damage, + 0, iy + iheight, + fwidth, fheight - (iy + iheight)); + } + rects = pixman_region32_rectangles(&damage, &n); + } else { + rects = pixman_region32_rectangles(&sb->damage, &n); + } + + wl_surface_attach(sb->output->parent.surface, sb->buffer, 0, 0); + for (i = 0; i < n; ++i) + wl_surface_damage(sb->output->parent.surface, rects[i].x1, + rects[i].y1, rects[i].x2 - rects[i].x1, + rects[i].y2 - rects[i].y1); + + if (sb->output->frame) + pixman_region32_fini(&damage); +} + +static int +wayland_output_repaint_pixman(struct weston_output *output_base, + pixman_region32_t *damage) +{ + struct wayland_output *output = (struct wayland_output *) output_base; + struct wayland_compositor *c = + (struct wayland_compositor *)output->base.compositor; + struct wl_callback *callback; + struct wayland_shm_buffer *sb; + + if (output->frame) { + if (frame_status(output->frame) & FRAME_STATUS_REPAINT) + wl_list_for_each(sb, &output->shm.buffers, link) + sb->frame_damaged = 1; + } + + wl_list_for_each(sb, &output->shm.buffers, link) + pixman_region32_union(&sb->damage, &sb->damage, damage); + + sb = wayland_output_get_shm_buffer(output); + + wayland_output_update_shm_border(sb); + pixman_renderer_output_set_buffer(output_base, sb->pm_image); + c->base.renderer->repaint_output(output_base, &sb->damage); + + wayland_shm_buffer_attach(sb); + + callback = wl_surface_frame(output->parent.surface); + wl_callback_add_listener(callback, &frame_listener, output); + wl_surface_commit(output->parent.surface); + wl_display_flush(c->parent.wl_display); + + pixman_region32_fini(&sb->damage); + pixman_region32_init(&sb->damage); + sb->frame_damaged = 0; + + pixman_region32_subtract(&c->base.primary_plane.damage, + &c->base.primary_plane.damage, damage); + return 0; +} + static void wayland_output_destroy(struct weston_output *output_base) { struct wayland_output *output = (struct wayland_output *) output_base; + struct wayland_compositor *c = + (struct wayland_compositor *) output->base.compositor; - gl_renderer->output_destroy(output_base); + if (c->use_pixman) { + pixman_renderer_output_destroy(output_base); + } else { + gl_renderer->output_destroy(output_base); + } - wl_egl_window_destroy(output->parent.egl_window); + wl_egl_window_destroy(output->gl.egl_window); wl_surface_destroy(output->parent.surface); wl_shell_surface_destroy(output->parent.shell_surface); if (output->frame) { frame_destroy(output->frame); - cairo_surface_destroy(output->border.top); - cairo_surface_destroy(output->border.left); - cairo_surface_destroy(output->border.right); - cairo_surface_destroy(output->border.bottom); + cairo_surface_destroy(output->gl.border.top); + cairo_surface_destroy(output->gl.border.left); + cairo_surface_destroy(output->gl.border.right); + cairo_surface_destroy(output->gl.border.bottom); } weston_output_destroy(&output->base); @@ -339,6 +567,52 @@ wayland_output_destroy(struct weston_output *output_base) static const struct wl_shell_surface_listener shell_surface_listener; +static int +wayland_output_init_gl_renderer(struct wayland_output *output) +{ + int32_t fx, fy, fwidth, fheight; + + if (output->frame) { + fwidth = frame_width(output->frame); + fheight = frame_height(output->frame); + } else { + fwidth = output->base.width; + fheight = output->base.height; + } + + output->gl.egl_window = + wl_egl_window_create(output->parent.surface, + fwidth, fheight); + if (!output->gl.egl_window) { + weston_log("failure to create wl_egl_window\n"); + return -1; + } + + if (gl_renderer->output_create(&output->base, + output->gl.egl_window) < 0) + goto cleanup_window; + + if (output->frame) { + frame_interior(output->frame, &fx, &fy, NULL, NULL); + output->base.border.left = fx; + output->base.border.top = fy; + output->base.border.right = fwidth - output->base.width - fx; + output->base.border.bottom = fheight - output->base.height - fy; + } + + return 0; + +cleanup_window: + wl_egl_window_destroy(output->gl.egl_window); + return -1; +} + +static int +wayland_output_init_pixman_renderer(struct wayland_output *output) +{ + return pixman_renderer_output_create(&output->base); +} + static int wayland_compositor_create_output(struct wayland_compositor *c, int width, int height) @@ -368,39 +642,31 @@ wayland_compositor_create_output(struct wayland_compositor *c, weston_output_move(&output->base, 0, 0); + wl_list_init(&output->shm.buffers); + wl_list_init(&output->shm.free_buffers); + if (!c->theme) c->theme = theme_create(); output->frame = frame_create(c->theme, width, height, FRAME_BUTTON_CLOSE, "Weston"); frame_resize_inside(output->frame, width, height); - frame_interior(output->frame, &fx, &fy, NULL, NULL); - fwidth = frame_width(output->frame); - fheight = frame_height(output->frame); - weston_log("Creating %dx%d wayland output (%dx%d actual)\n", - width, height, fwidth, fheight); + weston_log("Creating %dx%d wayland output\n", width, height); output->parent.surface = wl_compositor_create_surface(c->parent.compositor); wl_surface_set_user_data(output->parent.surface, output); - output->parent.egl_window = - wl_egl_window_create(output->parent.surface, - fwidth, fheight); - if (!output->parent.egl_window) { - weston_log("failure to create wl_egl_window\n"); - goto cleanup_output; + if (c->use_pixman) { + if (wayland_output_init_pixman_renderer(output) < 0) + goto cleanup_output; + output->base.repaint = wayland_output_repaint_pixman; + } else { + if (wayland_output_init_gl_renderer(output) < 0) + goto cleanup_output; + output->base.repaint = wayland_output_repaint_gl; } - if (gl_renderer->output_create(&output->base, - output->parent.egl_window) < 0) - goto cleanup_window; - - output->base.border.left = fx; - output->base.border.top = fy; - output->base.border.right = fwidth - width - fx; - output->base.border.bottom = fheight - height - fy; - frame_input_rect(output->frame, &fx, &fy, &fwidth, &fheight); region = wl_compositor_create_region(c->parent.compositor); wl_region_add(region, fx, fy, fwidth, fheight); @@ -422,7 +688,6 @@ wayland_compositor_create_output(struct wayland_compositor *c, wl_shell_surface_set_toplevel(output->parent.shell_surface); output->base.start_repaint_loop = wayland_output_start_repaint_loop; - output->base.repaint = wayland_output_repaint; output->base.destroy = wayland_output_destroy; output->base.assign_planes = NULL; output->base.set_backlight = NULL; @@ -433,8 +698,6 @@ wayland_compositor_create_output(struct wayland_compositor *c, return 0; -cleanup_window: - wl_egl_window_destroy(output->parent.egl_window); cleanup_output: /* FIXME: cleanup weston_output */ free(output); @@ -1001,7 +1264,7 @@ create_cursor(struct wayland_compositor *c, struct weston_config *config) } static struct weston_compositor * -wayland_compositor_create(struct wl_display *display, +wayland_compositor_create(struct wl_display *display, int use_pixman, int width, int height, const char *display_name, int *argc, char *argv[], struct weston_config *config) @@ -1039,10 +1302,23 @@ wayland_compositor_create(struct wl_display *display, if (!gl_renderer) goto err_display; - if (gl_renderer->create(&c->base, c->parent.wl_display, - gl_renderer->alpha_attribs, - NULL) < 0) - goto err_display; + c->use_pixman = use_pixman; + if (!use_pixman) { + if (gl_renderer->create(&c->base, c->parent.wl_display, + gl_renderer->alpha_attribs, + NULL) < 0) { + weston_log("Failed to initialize the GL renderer; " + "falling back to pixman.\n"); + c->use_pixman = 1; + } + } + + if (c->use_pixman) { + if (pixman_renderer_init(&c->base) < 0) { + weston_log("Failed to initialize pixman renderer\n"); + goto err_display; + } + } c->base.destroy = wayland_destroy; c->base.restore = wayland_restore; @@ -1081,16 +1357,18 @@ backend_init(struct wl_display *display, int *argc, char *argv[], { int width = 1024, height = 640; char *display_name = NULL; + int use_pixman = 0; const struct weston_option wayland_options[] = { { WESTON_OPTION_INTEGER, "width", 0, &width }, { WESTON_OPTION_INTEGER, "height", 0, &height }, { WESTON_OPTION_STRING, "display", 0, &display_name }, + { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &use_pixman }, }; parse_options(wayland_options, ARRAY_LENGTH(wayland_options), argc, argv); - return wayland_compositor_create(display, width, height, display_name, - argc, argv, config); + return wayland_compositor_create(display, use_pixman, width, height, + display_name, argc, argv, config); } diff --git a/src/compositor.c b/src/compositor.c index 31c01c54..edb13a2f 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -3611,6 +3611,7 @@ usage(int error_code) "Options for wayland-backend.so:\n\n" " --width=WIDTH\t\tWidth of Wayland surface\n" " --height=HEIGHT\tHeight of Wayland surface\n" + " --use-pixman\t\tUse the pixman (CPU) renderer\n" " --display=DISPLAY\tWayland display to connect to\n\n"); #if defined(BUILD_RPI_COMPOSITOR) && defined(HAVE_BCM_HOST)