From: Kristian Høgsberg Date: Mon, 25 Oct 2010 15:40:03 +0000 (-0400) Subject: Fall back to cairo image backend and shm surface if we don't have cairo gl X-Git-Tag: 0.85.0~403 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d0c3b9da225b0e3bd5102c977a17e66af928ab02;p=platform%2Fupstream%2Fwayland.git Fall back to cairo image backend and shm surface if we don't have cairo gl --- diff --git a/clients/window.c b/clients/window.c index 31c294f..1c1526b 100644 --- a/clients/window.c +++ b/clients/window.c @@ -20,6 +20,8 @@ * OF THIS SOFTWARE. */ +#include "../config.h" + #include #include #include @@ -33,13 +35,17 @@ #include #include #include +#include #define EGL_EGLEXT_PROTOTYPES 1 #define GL_GLEXT_PROTOTYPES 1 #include #include #include + +#ifdef HAVE_CAIRO_GL #include +#endif #include @@ -56,6 +62,7 @@ struct display { struct wl_compositor *compositor; struct wl_shell *shell; struct wl_drm *drm; + struct wl_shm *shm; struct wl_output *output; struct rectangle screen_allocation; int authenticated; @@ -89,6 +96,7 @@ struct window { struct input *grab_device; struct input *keyboard_device; uint32_t name; + enum window_buffer_type buffer_type; EGLImageKHR *image; cairo_surface_t *cairo_surface, *pending_surface; @@ -148,33 +156,110 @@ rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius) cairo_close_path(cr); } -static int -texture_from_png(const char *filename, int width, int height) +static const cairo_user_data_key_t surface_data_key; +struct surface_data { + struct wl_buffer *buffer; +}; + +#ifdef HAVE_CAIRO_GL + +struct drm_surface_data { + struct surface_data data; + EGLImageKHR image; + GLuint texture; + EGLDisplay dpy; +}; + +static void +drm_surface_data_destroy(void *p) +{ + struct drm_surface_data *data = p; + + glDeleteTextures(1, &data->texture); + eglDestroyImageKHR(data->dpy, data->image); + wl_buffer_destroy(data->data.buffer); +} + +cairo_surface_t * +display_create_drm_surface(struct display *display, + struct rectangle *rectangle) +{ + struct drm_surface_data *data; + EGLDisplay dpy = display->dpy; + cairo_surface_t *surface; + struct wl_visual *visual; + struct wl_buffer *buffer; + EGLint name, stride; + + EGLint image_attribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, + EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA, + EGL_NONE + }; + + data = malloc(sizeof *data); + if (data == NULL) + return NULL; + + image_attribs[1] = rectangle->width; + image_attribs[3] = rectangle->height; + data->image = eglCreateDRMImageMESA(dpy, image_attribs); + glGenTextures(1, &data->texture); + data->dpy = dpy; + glBindTexture(GL_TEXTURE_2D, data->texture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, data->image); + + eglExportDRMImageMESA(display->dpy, data->image, &name, NULL, &stride); + + visual = wl_display_get_premultiplied_argb_visual(display->display); + data->data.buffer = + wl_drm_create_buffer(display->drm, name, rectangle->width, + rectangle->height, stride, visual); + + surface = cairo_gl_surface_create_for_texture(display->device, + CAIRO_CONTENT_COLOR_ALPHA, + data->texture, + rectangle->width, + rectangle->height); + + cairo_surface_set_user_data (surface, &surface_data_key, + data, drm_surface_data_destroy); + + return surface; +} + +cairo_surface_t * +display_create_drm_surface_from_file(struct display *display, + const char *filename, + struct rectangle *rect) { + cairo_surface_t *surface; GdkPixbuf *pixbuf; GError *error = NULL; int stride, i; unsigned char *pixels, *p, *end; pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, - width, height, + rect->width, rect->height, FALSE, &error); if (error != NULL) - return -1; + return NULL; if (!gdk_pixbuf_get_has_alpha(pixbuf) || gdk_pixbuf_get_n_channels(pixbuf) != 4) { gdk_pixbuf_unref(pixbuf); - return -1; + return NULL; } stride = gdk_pixbuf_get_rowstride(pixbuf); pixels = gdk_pixbuf_get_pixels(pixbuf); - for (i = 0; i < height; i++) { + for (i = 0; i < rect->height; i++) { p = pixels + i * stride; - end = p + width * 4; + end = p + rect->width * 4; while (p < end) { unsigned int t; @@ -189,12 +274,181 @@ texture_from_png(const char *filename, int width, int height) } } + surface = display_create_drm_surface(display, rect); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + rect->width, rect->height, + 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); gdk_pixbuf_unref(pixbuf); - return 0; + return surface; +} + +#endif + +struct wl_buffer * +display_get_buffer_for_surface(struct display *display, + cairo_surface_t *surface) +{ + struct surface_data *data; + + data = cairo_surface_get_user_data (surface, &surface_data_key); + + return data->buffer; +} + +struct shm_surface_data { + struct surface_data data; + void *map; + size_t length; +}; + +void +shm_surface_data_destroy(void *p) +{ + struct shm_surface_data *data = p; + + wl_buffer_destroy(data->data.buffer); + munmap(data->map, data->length); +} + +cairo_surface_t * +display_create_shm_surface(struct display *display, + struct rectangle *rectangle) +{ + struct shm_surface_data *data; + cairo_surface_t *surface; + struct wl_visual *visual; + int stride, alloc, fd; + char filename[] = "/tmp/wayland-shm-XXXXXX"; + + data = malloc(sizeof *data); + if (data == NULL) + return NULL; + + stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, + rectangle->width); + data->length = stride * rectangle->height; + fd = mkstemp(filename); + if (fd < 0) { + fprintf(stderr, "open %s failed: %m", filename); + return NULL; + } + if (ftruncate(fd, data->length) < 0) { + fprintf(stderr, "ftruncate failed: %m"); + close(fd); + return NULL; + } + + data->map = mmap(NULL, data->length, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + unlink(filename); + + if (data->map == MAP_FAILED) { + fprintf(stderr, "mmap failed: %m"); + close(fd); + return NULL; + } + + surface = cairo_image_surface_create_for_data (data->map, + CAIRO_FORMAT_ARGB32, + rectangle->width, + rectangle->height, + stride); + + cairo_surface_set_user_data (surface, &surface_data_key, + data, shm_surface_data_destroy); + + visual = wl_display_get_premultiplied_argb_visual(display->display); + data->data.buffer = wl_shm_create_buffer(display->shm, + fd, + rectangle->width, + rectangle->height, + stride, visual); + + close(fd); + + return surface; +} + +cairo_surface_t * +display_create_shm_surface_from_file(struct display *display, + const char *filename, + struct rectangle *rect) +{ + cairo_surface_t *surface; + GdkPixbuf *pixbuf; + GError *error = NULL; + int stride, i; + unsigned char *pixels, *p, *end, *dest_data; + int dest_stride; + uint32_t *d; + + pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, + rect->width, rect->height, + FALSE, &error); + if (error != NULL) + return NULL; + + if (!gdk_pixbuf_get_has_alpha(pixbuf) || + gdk_pixbuf_get_n_channels(pixbuf) != 4) { + gdk_pixbuf_unref(pixbuf); + return NULL; + } + + stride = gdk_pixbuf_get_rowstride(pixbuf); + pixels = gdk_pixbuf_get_pixels(pixbuf); + + surface = display_create_shm_surface(display, rect); + dest_data = cairo_image_surface_get_data (surface); + dest_stride = cairo_image_surface_get_stride (surface); + + for (i = 0; i < rect->height; i++) { + d = (uint32_t *) (dest_data + i * dest_stride); + p = pixels + i * stride; + end = p + rect->width * 4; + while (p < end) { + unsigned int t; + unsigned char a, r, g, b; + +#define MULT(_d,c,a,t) \ + do { t = c * a + 0x7f; _d = ((t >> 8) + t) >> 8; } while (0) + + a = p[3]; + MULT(r, p[0], a, t); + MULT(g, p[1], a, t); + MULT(b, p[2], a, t); + p += 4; + *d++ = (a << 24) | (r << 16) | (g << 8) | b; + } + } + + gdk_pixbuf_unref(pixbuf); + + return surface; +} + +cairo_surface_t * +display_create_surface(struct display *display, + struct rectangle *rectangle) +{ +#ifdef HAVE_CAIRO_GL + display_create_drm_surface(display, rectangle); +#else + display_create_shm_surface(display, rectangle); +#endif +} + +cairo_surface_t * +display_create_surface_from_file(struct display *display, + const char *filename, + struct rectangle *rectangle) +{ +#ifdef HAVE_CAIRO_GL + display_create_drm_surface_from_file(display, filename, rectangle); +#else + display_create_shm_surface_from_file(display, filename, rectangle); +#endif } static const struct { @@ -229,96 +483,13 @@ create_pointer_surfaces(struct display *display) rect.height = height; for (i = 0; i < count; i++) { display->pointer_surfaces[i] = - display_create_surface(display, &rect); - texture_from_png(pointer_images[i].filename, width, height); + display_create_surface_from_file(display, + pointer_images[i].filename, + &rect); } } -static const cairo_user_data_key_t surface_data_key; -struct surface_data { - EGLImageKHR image; - GLuint texture; - EGLDisplay dpy; - struct wl_buffer *buffer; -}; - -static void -surface_data_destroy(void *p) -{ - struct surface_data *data = p; - - glDeleteTextures(1, &data->texture); - eglDestroyImageKHR(data->dpy, data->image); - if (data->buffer) - wl_buffer_destroy(data->buffer); -} - -cairo_surface_t * -display_create_surface(struct display *display, - struct rectangle *rectangle) -{ - struct surface_data *data; - EGLDisplay dpy = display->dpy; - cairo_surface_t *surface; - - EGLint image_attribs[] = { - EGL_WIDTH, 0, - EGL_HEIGHT, 0, - EGL_DRM_BUFFER_FORMAT_MESA, EGL_DRM_BUFFER_FORMAT_ARGB32_MESA, - EGL_DRM_BUFFER_USE_MESA, EGL_DRM_BUFFER_USE_SCANOUT_MESA, - EGL_NONE - }; - - data = malloc(sizeof *data); - image_attribs[1] = rectangle->width; - image_attribs[3] = rectangle->height; - data->image = eglCreateDRMImageMESA(dpy, image_attribs); - glGenTextures(1, &data->texture); - data->dpy = dpy; - glBindTexture(GL_TEXTURE_2D, data->texture); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, data->image); - data->buffer = NULL; - - surface = cairo_gl_surface_create_for_texture(display->device, - CAIRO_CONTENT_COLOR_ALPHA, - data->texture, - rectangle->width, - rectangle->height); - - cairo_surface_set_user_data (surface, &surface_data_key, - data, surface_data_destroy); - - return surface; -} - -struct wl_buffer * -display_get_buffer_for_surface(struct display *display, - cairo_surface_t *surface) -{ - struct surface_data *data; - struct wl_visual *visual; - struct wl_buffer *buffer; - EGLint name, stride; - int width, height; - - data = cairo_surface_get_user_data (surface, &surface_data_key); - if (data->buffer) - return data->buffer; - - width = cairo_gl_surface_get_width (surface); - height = cairo_gl_surface_get_height (surface); - - eglExportDRMImageMESA(display->dpy, data->image, &name, NULL, &stride); - - visual = wl_display_get_premultiplied_argb_visual(display->display); - buffer = wl_drm_create_buffer(display->drm, - name, width, height, stride, visual); - data->buffer = buffer; - - return buffer; -} - cairo_surface_t * display_get_pointer_surface(struct display *display, int pointer, int *width, int *height, @@ -383,6 +554,23 @@ window_flush(struct window *window) } static void +window_create_surface(struct window *window, struct rectangle *allocation) +{ + switch (window->buffer_type) { +#ifdef HAVE_CAIRO_GL + case WINDOW_BUFFER_TYPE_DRM: + window->cairo_surface = + display_create_surface(window->display, allocation); + break; +#endif + case WINDOW_BUFFER_TYPE_SHM: + window->cairo_surface = + display_create_shm_surface(window->display, allocation); + break; + } +} + +static void window_draw_decorations(struct window *window) { cairo_t *cr; @@ -391,8 +579,8 @@ window_draw_decorations(struct window *window) cairo_surface_t *frame; int width, height, shadow_dx = 3, shadow_dy = 3; - window->cairo_surface = - display_create_surface(window->display, &window->allocation); + window_create_surface(window, &window->allocation); + width = window->allocation.width; height = window->allocation.height; @@ -447,8 +635,7 @@ display_flush_cairo_device(struct display *display) static void window_draw_fullscreen(struct window *window) { - window->cairo_surface = - display_create_surface(window->display, &window->allocation); + window_create_surface(window, &window->allocation); } void @@ -985,6 +1172,13 @@ window_move(struct window *window, int32_t x, int32_t y) window->allocation.height); } +void +window_damage(struct window *window, int32_t x, int32_t y, + int32_t width, int32_t height) +{ + wl_surface_damage(window->surface, x, y, width, height); +} + struct window * window_create(struct display *display, const char *title, int32_t x, int32_t y, int32_t width, int32_t height) @@ -1007,12 +1201,24 @@ window_create(struct display *display, const char *title, window->margin = 16; window->decoration = 1; +#ifdef HAVE_CAIRO_GL + window->buffer_type = WINDOW_BUFFER_TYPE_DRM; +#else + window->buffer_type = WINDOW_BUFFER_TYPE_SHM; +#endif + wl_surface_set_user_data(window->surface, window); wl_list_insert(display->window_list.prev, &window->link); return window; } +void +window_set_buffer_type(struct window *window, enum window_buffer_type type) +{ + window->buffer_type = type; +} + static void drm_handle_device(void *data, struct wl_drm *drm, const char *device) { @@ -1091,6 +1297,8 @@ display_handle_global(struct wl_display *display, uint32_t id, } else if (strcmp(interface, "drm") == 0) { d->drm = wl_drm_create(display, id); wl_drm_add_listener(d->drm, &drm_listener, d); + } else if (strcmp(interface, "shm") == 0) { + d->shm = wl_shm_create(display, id); } else if (strcmp(interface, "drag_offer") == 0) { offer = wl_drag_offer_create(display, id); d->drag_offer_handler(offer, d); diff --git a/clients/window.h b/clients/window.h index 343d536..b5e3c37 100644 --- a/clients/window.h +++ b/clients/window.h @@ -138,6 +138,10 @@ window_schedule_redraw(struct window *window); void window_move(struct window *window, int32_t x, int32_t y); +void +window_damage(struct window *window, int32_t x, int32_t y, + int32_t width, int32_t height); + cairo_surface_t * window_get_surface(struct window *window); @@ -149,6 +153,14 @@ window_copy_surface(struct window *window, void window_flush(struct window *window); +enum window_buffer_type { + WINDOW_BUFFER_TYPE_DRM, + WINDOW_BUFFER_TYPE_SHM, +}; + +void +window_set_buffer_type(struct window *window, enum window_buffer_type type); + void window_set_fullscreen(struct window *window, int fullscreen); diff --git a/compositor/shm.c b/compositor/shm.c index 045eabc..cdade52 100644 --- a/compositor/shm.c +++ b/compositor/shm.c @@ -52,6 +52,12 @@ shm_buffer_attach(struct wl_buffer *buffer_base, struct wl_surface *surface) (struct wlsc_shm_buffer *) buffer_base; glBindTexture(GL_TEXTURE_2D, es->texture); + + /* Unbind any EGLImage texture that may be bound, so we don't + * overwrite it.*/ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + 0, 0, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->base.width, buffer->base.height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer->data); diff --git a/configure.ac b/configure.ac index fdfd61c..b510b51 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ AM_INIT_AUTOMAKE([foreign dist-bzip2]) AC_PROG_CC AC_PROG_LIBTOOL AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([config.h]) AM_SILENT_RULES([yes]) PKG_PROG_PKG_CONFIG() @@ -10,8 +11,12 @@ PKG_CHECK_MODULES(FFI, [libffi]) PKG_CHECK_MODULES(COMPOSITOR, [egl glesv2 gdk-pixbuf-2.0 libudev >= 136 libdrm >= 2.4.17] xcb-dri2 xcb-xfixes) -PKG_CHECK_MODULES(CLIENT, [egl gl cairo-gl gdk-pixbuf-2.0 glib-2.0 gobject-2.0 xkbcommon libdrm]) +PKG_CHECK_MODULES(CLIENT, [egl gl cairo gdk-pixbuf-2.0 glib-2.0 gobject-2.0 xkbcommon libdrm]) PKG_CHECK_MODULES(POPPLER, [poppler-glib gdk-2.0]) +PKG_CHECK_MODULES(CAIRO_GL, [cairo-gl], + [have_cairo_gl=yes], [have_cairo_gl=no]) +AS_IF([test "x$have_cairo_gl" = "xyes"], + [AC_DEFINE([HAVE_CAIRO_GL], [1], [Have cairo-gl])]) if test $CC = gcc; then GCC_CFLAGS="-Wall -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden"