x11 backend: add option to use pixman renderer
authorVasily Khoruzhick <anarsoul@gmail.com>
Mon, 7 Jan 2013 17:39:50 +0000 (20:39 +0300)
committerKristian Høgsberg <krh@bitplanet.net>
Mon, 7 Jan 2013 18:03:41 +0000 (13:03 -0500)
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 <anarsoul@gmail.com>
src/compositor-x11.c

index 04a1803..a994a97 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright © 2008-2011 Kristian Høgsberg
  * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2013 Vasily Khoruzhick <anarsoul@gmail.com>
  *
  * Permission to use, copy, modify, distribute, and sell this software and
  * its documentation for any purpose is hereby granted without fee, provided
 #include <unistd.h>
 #include <errno.h>
 #include <sys/time.h>
+#include <sys/shm.h>
 #include <linux/input.h>
 
 #include <xcb/xcb.h>
+#include <xcb/shm.h>
 #ifdef HAVE_XCB_XKB
 #include <xcb/xkb.h>
 #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);
 }