From b3add192edf89b586fcf7dcd8c561672d9dffc85 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Mon, 7 Jan 2013 20:39:50 +0300 Subject: [PATCH] x11 backend: add option to use pixman renderer When --use-shm is passed to weston and x11 backend is active, it will use SHM surfaces with pixman renderer instead of EGL Signed-off-by: Vasily Khoruzhick --- src/compositor-x11.c | 355 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 342 insertions(+), 13 deletions(-) diff --git a/src/compositor-x11.c b/src/compositor-x11.c index 04a1803..a994a97 100644 --- a/src/compositor-x11.c +++ b/src/compositor-x11.c @@ -1,6 +1,7 @@ /* * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2010-2011 Intel Corporation + * Copyright © 2013 Vasily Khoruzhick * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided @@ -33,9 +34,11 @@ #include #include #include +#include #include #include +#include #ifdef HAVE_XCB_XKB #include #endif @@ -47,6 +50,7 @@ #include "compositor.h" #include "gl-renderer.h" +#include "pixman-renderer.h" #include "../shared/config-parser.h" #include "../shared/cairo-util.h" @@ -79,6 +83,7 @@ struct x11_compositor { struct xkb_keymap *xkb_keymap; unsigned int has_xkb; uint8_t xkb_event_base; + int use_shm; /* We could map multi-pointer X to multiple wayland seats, but * for now we only support core X input. */ @@ -107,6 +112,15 @@ struct x11_output { xcb_window_t window; struct weston_mode mode; struct wl_event_source *finish_frame_timer; + + xcb_gc_t gc; + xcb_shm_seg_t segment; + pixman_image_t *hw_surface; + pixman_image_t *shadow_surface; + int shm_id; + void *buf; + void *shadow_buf; + uint8_t depth; }; static struct xkb_keymap * @@ -307,16 +321,95 @@ x11_input_destroy(struct x11_compositor *compositor) } static void -x11_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage) +x11_output_repaint_gl(struct weston_output *output_base, + pixman_region32_t *damage) +{ + struct x11_output *output = (struct x11_output *)output_base; + struct weston_compositor *ec = output->base.compositor; + + ec->renderer->repaint_output(output_base, damage); + + pixman_region32_subtract(&ec->primary_plane.damage, + &ec->primary_plane.damage, damage); + + wl_event_source_timer_update(output->finish_frame_timer, 10); +} + +static void +x11_output_repaint_shm(struct weston_output *output_base, + pixman_region32_t *damage) { struct x11_output *output = (struct x11_output *)output_base; struct weston_compositor *ec = output->base.compositor; + struct x11_compositor *c = (struct x11_compositor *)ec; + pixman_box32_t *rects; + int nrects, i, src_x, src_y, x1, y1, x2, y2, width, height; + xcb_void_cookie_t cookie; + xcb_generic_error_t *err; + pixman_renderer_output_set_buffer(output_base, output->shadow_surface); ec->renderer->repaint_output(output_base, damage); + width = pixman_image_get_width(output->shadow_surface); + height = pixman_image_get_height(output->shadow_surface); + rects = pixman_region32_rectangles(damage, &nrects); + for (i = 0; i < nrects; i++) { + switch (output_base->transform) { + default: + case WL_OUTPUT_TRANSFORM_NORMAL: + x1 = rects[i].x1; + x2 = rects[i].x2; + y1 = rects[i].y1; + y2 = rects[i].y2; + break; + case WL_OUTPUT_TRANSFORM_180: + x1 = width - rects[i].x2; + x2 = width - rects[i].x1; + y1 = height - rects[i].y2; + y2 = height - rects[i].y1; + break; + case WL_OUTPUT_TRANSFORM_90: + x1 = height - rects[i].y2; + x2 = height - rects[i].y1; + y1 = rects[i].x1; + y2 = rects[i].x2; + break; + case WL_OUTPUT_TRANSFORM_270: + x1 = rects[i].y1; + x2 = rects[i].y2; + y1 = width - rects[i].x2; + y2 = width - rects[i].x1; + break; + } + src_x = x1; + src_y = y1; + + pixman_image_composite32(PIXMAN_OP_SRC, + output->shadow_surface, /* src */ + NULL /* mask */, + output->hw_surface, /* dest */ + src_x, src_y, /* src_x, src_y */ + 0, 0, /* mask_x, mask_y */ + x1, y1, /* dest_x, dest_y */ + x2 - x1, /* width */ + y2 - y1 /* height */); + } + pixman_region32_subtract(&ec->primary_plane.damage, &ec->primary_plane.damage, damage); + cookie = xcb_shm_put_image_checked(c->conn, output->window, output->gc, + pixman_image_get_width(output->hw_surface), + pixman_image_get_height(output->hw_surface), + 0, 0, + pixman_image_get_width(output->hw_surface), + pixman_image_get_height(output->hw_surface), + 0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP, + 0, output->segment, 0); + err = xcb_request_check(c->conn, cookie); + if (err != NULL) { + weston_log("Failed to put shm image, err: %d\n", err->error_code); + free(err); + } wl_event_source_timer_update(output->finish_frame_timer, 10); } @@ -336,6 +429,28 @@ finish_frame_handler(void *data) } static void +x11_output_deinit_shm(struct x11_compositor *c, struct x11_output *output) +{ + xcb_void_cookie_t cookie; + xcb_generic_error_t *err; + xcb_free_gc(c->conn, output->gc); + + pixman_image_unref(output->hw_surface); + output->hw_surface = NULL; + cookie = xcb_shm_detach_checked(c->conn, output->segment); + err = xcb_request_check(c->conn, cookie); + if (err) { + weston_log("xcb_shm_detach failed, error %d\n", err->error_code); + free(err); + } + shmdt(output->buf); + + pixman_image_unref(output->shadow_surface); + output->shadow_surface = NULL; + free(output->shadow_buf); +} + +static void x11_output_destroy(struct weston_output *output_base) { struct x11_output *output = (struct x11_output *)output_base; @@ -345,7 +460,11 @@ x11_output_destroy(struct weston_output *output_base) wl_list_remove(&output->base.link); wl_event_source_remove(output->finish_frame_timer); - gl_renderer_output_destroy(output_base); + if (compositor->use_shm) { + pixman_renderer_output_destroy(output_base); + x11_output_deinit_shm(compositor, output); + } else + gl_renderer_output_destroy(output_base); xcb_destroy_window(compositor->conn, output->window); @@ -456,6 +575,186 @@ x11_output_wait_for_map(struct x11_compositor *c, struct x11_output *output) } } +static xcb_visualtype_t * +find_visual_by_id(xcb_screen_t *screen, + xcb_visualid_t id) +{ + xcb_depth_iterator_t i; + xcb_visualtype_iterator_t j; + for (i = xcb_screen_allowed_depths_iterator(screen); + i.rem; + xcb_depth_next(&i)) { + for (j = xcb_depth_visuals_iterator(i.data); + j.rem; + xcb_visualtype_next(&j)) { + if (j.data->visual_id == id) + return j.data; + } + } + return 0; +} + +static uint8_t +get_depth_of_visual(xcb_screen_t *screen, + xcb_visualid_t id) +{ + xcb_depth_iterator_t i; + xcb_visualtype_iterator_t j; + for (i = xcb_screen_allowed_depths_iterator(screen); + i.rem; + xcb_depth_next(&i)) { + for (j = xcb_depth_visuals_iterator(i.data); + j.rem; + xcb_visualtype_next(&j)) { + if (j.data->visual_id == id) + return i.data->depth; + } + } + return 0; +} + +static int +x11_output_init_shm(struct x11_compositor *c, struct x11_output *output, + int width, int height) +{ + xcb_screen_iterator_t iter; + xcb_visualtype_t *visual_type; + xcb_format_iterator_t fmt; + xcb_void_cookie_t cookie; + xcb_generic_error_t *err; + int shadow_width, shadow_height; + pixman_transform_t transform; + const xcb_query_extension_reply_t *ext; + int bitsperpixel = 0; + pixman_format_code_t pixman_format; + + /* Check if SHM is available */ + ext = xcb_get_extension_data(c->conn, &xcb_shm_id); + if (ext == NULL || !ext->present) { + /* SHM is missing */ + weston_log("SHM extension is not available\n"); + errno = ENOENT; + return -1; + } + + iter = xcb_setup_roots_iterator(xcb_get_setup(c->conn)); + visual_type = find_visual_by_id(iter.data, iter.data->root_visual); + if (!visual_type) { + weston_log("Failed to lookup visual for root window\n"); + errno = ENOENT; + return -1; + } + weston_log("Found visual, bits per value: %d, red_mask: %.8x, green_mask: %.8x, blue_mask: %.8x\n", + visual_type->bits_per_rgb_value, + visual_type->red_mask, + visual_type->green_mask, + visual_type->blue_mask); + output->depth = get_depth_of_visual(iter.data, iter.data->root_visual); + weston_log("Visual depth is %d\n", output->depth); + + for (fmt = xcb_setup_pixmap_formats_iterator(xcb_get_setup(c->conn)); + fmt.rem; + xcb_format_next(&fmt)) { + if (fmt.data->depth == output->depth) { + bitsperpixel = fmt.data->bits_per_pixel; + break; + } + } + weston_log("Found format for depth %d, bpp: %d\n", + output->depth, bitsperpixel); + + if (bitsperpixel == 32 && + visual_type->red_mask == 0xff0000 && + visual_type->green_mask == 0x00ff00 && + visual_type->blue_mask == 0x0000ff) { + weston_log("Will use x8r8g8b8 format for SHM surfaces\n"); + pixman_format = PIXMAN_x8r8g8b8; + } else { + weston_log("Can't find appropriate format for SHM pixmap\n"); + errno = ENOTSUP; + return -1; + } + + + /* Create SHM segment and attach it */ + output->shm_id = shmget(IPC_PRIVATE, width * height * (bitsperpixel / 8), IPC_CREAT | S_IRWXU); + if (output->shm_id == -1) { + weston_log("x11shm: failed to allocate SHM segment\n"); + return -1; + } + output->buf = shmat(output->shm_id, NULL, 0 /* read/write */); + if (-1 == (long)output->buf) { + weston_log("x11shm: failed to attach SHM segment\n"); + return -1; + } + output->segment = xcb_generate_id(c->conn); + cookie = xcb_shm_attach_checked(c->conn, output->segment, output->shm_id, 1); + err = xcb_request_check(c->conn, cookie); + if (err) { + weston_log("x11shm: xcb_shm_attach error %d\n", err->error_code); + free(err); + return -1; + } + + shmctl(output->shm_id, IPC_RMID, NULL); + + /* Now create pixman image */ + output->hw_surface = pixman_image_create_bits(pixman_format, width, height, output->buf, + width * (bitsperpixel / 8)); + pixman_transform_init_identity(&transform); + switch (output->base.transform) { + default: + case WL_OUTPUT_TRANSFORM_NORMAL: + shadow_width = width; + shadow_height = height; + pixman_transform_rotate(&transform, + NULL, 0, 0); + pixman_transform_translate(&transform, NULL, + 0, 0); + break; + case WL_OUTPUT_TRANSFORM_180: + shadow_width = width; + shadow_height = height; + pixman_transform_rotate(&transform, + NULL, -pixman_fixed_1, 0); + pixman_transform_translate(NULL, &transform, + pixman_int_to_fixed(shadow_width), + pixman_int_to_fixed(shadow_height)); + break; + case WL_OUTPUT_TRANSFORM_270: + shadow_width = height; + shadow_height = width; + pixman_transform_rotate(&transform, + NULL, 0, pixman_fixed_1); + pixman_transform_translate(&transform, + NULL, + pixman_int_to_fixed(shadow_width), + 0); + break; + case WL_OUTPUT_TRANSFORM_90: + shadow_width = height; + shadow_height = width; + pixman_transform_rotate(&transform, + NULL, 0, -pixman_fixed_1); + pixman_transform_translate(&transform, + NULL, + 0, + pixman_int_to_fixed(shadow_height)); + break; + } + output->shadow_buf = malloc(width * height * (bitsperpixel / 8)); + output->shadow_surface = pixman_image_create_bits(pixman_format, shadow_width, shadow_height, + output->shadow_buf, shadow_width * (bitsperpixel / 8)); + /* No need in transform for normal output */ + if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL) + pixman_image_set_transform(output->shadow_surface, &transform); + + output->gc = xcb_generate_id(c->conn); + xcb_create_gc(c->conn, output->gc, output->window, 0, NULL); + + return 0; +} + static struct x11_output * x11_compositor_create_output(struct x11_compositor *c, int x, int y, int width, int height, int fullscreen, @@ -562,7 +861,10 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, x11_output_wait_for_map(c, output); output->base.origin = output->base.current; - output->base.repaint = x11_output_repaint; + if (c->use_shm) + output->base.repaint = x11_output_repaint_shm; + else + output->base.repaint = x11_output_repaint_gl; output->base.destroy = x11_output_destroy; output->base.assign_planes = NULL; output->base.set_backlight = NULL; @@ -574,8 +876,17 @@ x11_compositor_create_output(struct x11_compositor *c, int x, int y, weston_output_init(&output->base, &c->base, x, y, width, height, transform); - if (gl_renderer_output_create(&output->base, output->window) < 0) - return NULL; + if (c->use_shm) { + if (x11_output_init_shm(c, output, width, height) < 0) + return NULL; + if (pixman_renderer_output_create(&output->base) < 0) { + x11_output_deinit_shm(c, output); + return NULL; + } + } else { + if (gl_renderer_output_create(&output->base, output->window) < 0) + return NULL; + } loop = wl_display_get_event_loop(c->base.wl_display); output->finish_frame_timer = @@ -1108,7 +1419,10 @@ x11_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); /* destroys outputs, too */ - gl_renderer_destroy(ec); + if (compositor->use_shm) + pixman_renderer_destroy(ec); + else + gl_renderer_destroy(ec); XCloseDisplay(compositor->dpy); free(ec); @@ -1118,6 +1432,7 @@ static struct weston_compositor * x11_compositor_create(struct wl_display *display, int fullscreen, int no_input, + int use_shm, int argc, char *argv[], const char *config_file) { struct x11_compositor *c; @@ -1156,15 +1471,23 @@ x11_compositor_create(struct wl_display *display, x11_compositor_get_resources(c); c->base.wl_display = display; - if (gl_renderer_create(&c->base, c->dpy, gl_renderer_opaque_attribs, - NULL) < 0) - goto err_xdisplay; + c->use_shm = use_shm; + if (c->use_shm) { + if (pixman_renderer_init(&c->base) < 0) + goto err_xdisplay; + } + else { + if (gl_renderer_create(&c->base, c->dpy, gl_renderer_opaque_attribs, + NULL) < 0) + goto err_xdisplay; + } + weston_log("Using %s renderer\n", use_shm ? "pixman" : "gl"); c->base.destroy = x11_destroy; c->base.restore = x11_restore; if (x11_input_create(c, no_input) < 0) - goto err_gl; + goto err_renderer; width = option_width ? option_width : 1024; height = option_height ? option_height : 640; @@ -1208,8 +1531,11 @@ x11_compositor_create(struct wl_display *display, err_x11_input: x11_input_destroy(c); -err_gl: - gl_renderer_destroy(&c->base); +err_renderer: + if (c->use_shm) + pixman_renderer_destroy(&c->base); + else + gl_renderer_destroy(&c->base); err_xdisplay: XCloseDisplay(c->dpy); err_free: @@ -1298,6 +1624,7 @@ backend_init(struct wl_display *display, int argc, char *argv[], { int fullscreen = 0; int no_input = 0; + int use_shm = 0; const struct weston_option x11_options[] = { { WESTON_OPTION_INTEGER, "width", 0, &option_width }, @@ -1305,6 +1632,7 @@ backend_init(struct wl_display *display, int argc, char *argv[], { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &fullscreen }, { WESTON_OPTION_INTEGER, "output-count", 0, &option_count }, { WESTON_OPTION_BOOLEAN, "no-input", 0, &no_input }, + { WESTON_OPTION_BOOLEAN, "use-shm", 0, &use_shm }, }; parse_options(x11_options, ARRAY_LENGTH(x11_options), argc, argv); @@ -1328,5 +1656,6 @@ backend_init(struct wl_display *display, int argc, char *argv[], return x11_compositor_create(display, fullscreen, no_input, + use_shm, argc, argv, config_file); } -- 2.7.4