window: add toysurface abstraction and port EGL path
authorPekka Paalanen <ppaalanen@gmail.com>
Mon, 19 Nov 2012 15:15:58 +0000 (17:15 +0200)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 27 Nov 2012 20:49:34 +0000 (15:49 -0500)
We need more structure to the way we handle the backing storage in
toytoolkit, to make it possible to double-buffer the shm case properly.
The existing buffer handling is very complex with the three
different cases:
- EGLSurface backed Cairo surface with a window associated
- wl_shm backed Cairo surface with a window associated
- wl_shm backed Cairo surface without a window, as used by dnd.c

Introduce the toysurface abstraction, which defines the interface for
the both buffer handling cases that have a window associated. It also
means, that windows will not have a valid Cairo surface outside of their
repaint cycle.

Convert the EGLsurface case into toysurface for starters. For EGL-based
Cairo surfaces, the private data is no longer needed. Destroying
egl_window_surface will trigger the destruction of the cairo_surface_t,
not vice versa. This is possible because display_create_surface() is
shm-only.

The shm cases are left untouched.

As a side-effect, display_acquire_window_surface() and
display_release_window_surface() will no longer use the 'display'
argument. Instead, display will be the one inherited from the window.

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
clients/window.c

index c3365ba..8065f68 100644 (file)
@@ -142,6 +142,44 @@ struct window_output {
        struct wl_list link;
 };
 
+struct toysurface {
+       /*
+        * Prepare the surface for drawing. Makes sure there is a surface
+        * of the right size available for rendering, and returns it.
+        * dx,dy are the x,y of wl_surface.attach.
+        * width,height are the new surface size.
+        * Returns the Cairo surface to draw to.
+        */
+       cairo_surface_t *(*prepare)(struct toysurface *base, int dx, int dy,
+                                   int width, int height);
+
+       /*
+        * Post the surface to the server, returning the server allocation
+        * rectangle. The Cairo surface from prepare() must be destroyed
+        * after calling this.
+        */
+       void (*swap)(struct toysurface *base,
+                    struct rectangle *server_allocation);
+
+       /*
+        * Make the toysurface current with the given EGL context.
+        * Returns 0 on success, and negative of failure.
+        */
+       int (*acquire)(struct toysurface *base, EGLContext ctx);
+
+       /*
+        * Release the toysurface from the EGL context, returning control
+        * to Cairo.
+        */
+       void (*release)(struct toysurface *base);
+
+       /*
+        * Destroy the toysurface, including the Cairo surface, any
+        * backing storage, and the Wayland protocol objects.
+        */
+       void (*destroy)(struct toysurface *base);
+};
+
 struct window {
        struct display *display;
        struct window *parent;
@@ -166,6 +204,7 @@ struct window {
 
        enum window_buffer_type buffer_type;
 
+       struct toysurface *toysurface;
        cairo_surface_t *cairo_surface;
 
        struct shm_pool *pool;
@@ -347,68 +386,142 @@ enum window_location {
 };
 
 static const cairo_user_data_key_t shm_surface_data_key;
-static const cairo_user_data_key_t egl_window_surface_data_key;
 
 #ifdef HAVE_CAIRO_EGL
 
-struct egl_window_surface_data {
+struct egl_window_surface {
+       struct toysurface base;
+       cairo_surface_t *cairo_surface;
        struct display *display;
        struct wl_surface *surface;
-       struct wl_egl_window *window;
-       EGLSurface surf;
+       struct wl_egl_window *egl_window;
+       EGLSurface egl_surface;
 };
 
+static struct egl_window_surface *
+to_egl_window_surface(struct toysurface *base)
+{
+       return container_of(base, struct egl_window_surface, base);
+}
+
+static cairo_surface_t *
+egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
+                          int width, int height)
+{
+       struct egl_window_surface *surface = to_egl_window_surface(base);
+
+       wl_egl_window_resize(surface->egl_window, width, height, dx, dy);
+       cairo_gl_surface_set_size(surface->cairo_surface, width, height);
+
+       return cairo_surface_reference(surface->cairo_surface);
+}
+
 static void
-egl_window_surface_data_destroy(void *p)
+egl_window_surface_swap(struct toysurface *base,
+                       struct rectangle *server_allocation)
+{
+       struct egl_window_surface *surface = to_egl_window_surface(base);
+
+       cairo_gl_surface_swapbuffers(surface->cairo_surface);
+       wl_egl_window_get_attached_size(surface->egl_window,
+                                       &server_allocation->width,
+                                       &server_allocation->height);
+}
+
+static int
+egl_window_surface_acquire(struct toysurface *base, EGLContext ctx)
 {
-       struct egl_window_surface_data *data = p;
-       struct display *d = data->display;
+       struct egl_window_surface *surface = to_egl_window_surface(base);
+       cairo_device_t *device;
 
-       eglDestroySurface(d->dpy, data->surf);
-       wl_egl_window_destroy(data->window);
-       data->surface = NULL;
+       device = cairo_surface_get_device(surface->cairo_surface);
+       if (!device)
+               return -1;
 
-       free(p);
+       if (!ctx) {
+               if (device == surface->display->argb_device)
+                       ctx = surface->display->argb_ctx;
+               else
+                       assert(0);
+       }
+
+       cairo_device_flush(device);
+       cairo_device_acquire(device);
+       if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface,
+                           surface->egl_surface, ctx))
+               fprintf(stderr, "failed to make surface current\n");
+
+       return 0;
 }
 
-static cairo_surface_t *
-display_create_egl_window_surface(struct display *display,
-                                 struct wl_surface *surface,
-                                 uint32_t flags,
-                                 struct rectangle *rectangle)
+static void
+egl_window_surface_release(struct toysurface *base)
 {
-       cairo_surface_t *cairo_surface;
-       struct egl_window_surface_data *data;
-       EGLConfig config;
+       struct egl_window_surface *surface = to_egl_window_surface(base);
        cairo_device_t *device;
 
-       data = malloc(sizeof *data);
-       if (data == NULL)
-               return NULL;
+       device = cairo_surface_get_device(surface->cairo_surface);
+       if (!device)
+               return;
+
+       if (!eglMakeCurrent(surface->display->dpy, NULL, NULL,
+                           surface->display->argb_ctx))
+               fprintf(stderr, "failed to make context current\n");
+
+       cairo_device_release(device);
+}
+
+static void
+egl_window_surface_destroy(struct toysurface *base)
+{
+       struct egl_window_surface *surface = to_egl_window_surface(base);
+       struct display *d = surface->display;
+
+       cairo_surface_destroy(surface->cairo_surface);
+       eglDestroySurface(d->dpy, surface->egl_surface);
+       wl_egl_window_destroy(surface->egl_window);
+       surface->surface = NULL;
+
+       free(surface);
+}
+
+static struct toysurface *
+egl_window_surface_create(struct display *display,
+                         struct wl_surface *wl_surface,
+                         uint32_t flags,
+                         struct rectangle *rectangle)
+{
+       struct egl_window_surface *surface;
 
-       data->display = display;
-       data->surface = surface;
+       surface = calloc(1, sizeof *surface);
+       if (!surface)
+               return NULL;
 
-       config = display->argb_config;
-       device = display->argb_device;
+       surface->base.prepare = egl_window_surface_prepare;
+       surface->base.swap = egl_window_surface_swap;
+       surface->base.acquire = egl_window_surface_acquire;
+       surface->base.release = egl_window_surface_release;
+       surface->base.destroy = egl_window_surface_destroy;
 
-       data->window = wl_egl_window_create(surface,
-                                           rectangle->width,
-                                           rectangle->height);
+       surface->display = display;
+       surface->surface = wl_surface;
 
-       data->surf = eglCreateWindowSurface(display->dpy, config,
-                                           data->window, NULL);
+       surface->egl_window = wl_egl_window_create(surface->surface,
+                                                  rectangle->width,
+                                                  rectangle->height);
 
-       cairo_surface = cairo_gl_surface_create_for_egl(device,
-                                                       data->surf,
-                                                       rectangle->width,
-                                                       rectangle->height);
+       surface->egl_surface = eglCreateWindowSurface(display->dpy,
+                                                     display->argb_config,
+                                                     surface->egl_window,
+                                                     NULL);
 
-       cairo_surface_set_user_data(cairo_surface,
-                                   &egl_window_surface_data_key,
-                                   data, egl_window_surface_data_destroy);
+       surface->cairo_surface =
+               cairo_gl_surface_create_for_egl(display->argb_device,
+                                               surface->egl_surface,
+                                               rectangle->width,
+                                               rectangle->height);
 
-       return cairo_surface;
+       return &surface->base;
 }
 
 #endif
@@ -812,9 +925,6 @@ window_attach_surface(struct window *window)
 {
        struct display *display = window->display;
        struct wl_buffer *buffer;
-#ifdef HAVE_CAIRO_EGL
-       struct egl_window_surface_data *data;
-#endif
        int32_t x, y;
 
        if (window->type == TYPE_NONE) {
@@ -840,13 +950,8 @@ window_attach_surface(struct window *window)
        switch (window->buffer_type) {
 #ifdef HAVE_CAIRO_EGL
        case WINDOW_BUFFER_TYPE_EGL_WINDOW:
-               data = cairo_surface_get_user_data(window->cairo_surface,
-                                                  &egl_window_surface_data_key);
-
-               cairo_gl_surface_swapbuffers(window->cairo_surface);
-               wl_egl_window_get_attached_size(data->window,
-                               &window->server_allocation.width,
-                               &window->server_allocation.height);
+               window->toysurface->swap(window->toysurface,
+                                        &window->server_allocation);
                break;
 #endif
        case WINDOW_BUFFER_TYPE_SHM:
@@ -876,8 +981,12 @@ window_has_focus(struct window *window)
 static void
 window_flush(struct window *window)
 {
-       if (window->cairo_surface)
-               window_attach_surface(window);
+       if (!window->cairo_surface)
+               return;
+
+       window_attach_surface(window);
+       cairo_surface_destroy(window->cairo_surface);
+       window->cairo_surface = NULL;
 }
 
 static void
@@ -891,28 +1000,6 @@ window_set_surface(struct window *window, cairo_surface_t *surface)
        window->cairo_surface = surface;
 }
 
-#ifdef HAVE_CAIRO_EGL
-static void
-window_resize_cairo_window_surface(struct window *window)
-{
-       struct egl_window_surface_data *data;
-       int x, y;
-
-       data = cairo_surface_get_user_data(window->cairo_surface,
-                                          &egl_window_surface_data_key);
-
-       window_get_resize_dx_dy(window, &x, &y),
-       wl_egl_window_resize(data->window,
-                            window->allocation.width,
-                            window->allocation.height,
-                            x,y);
-
-       cairo_gl_surface_set_size(window->cairo_surface,
-                                 window->allocation.width,
-                                 window->allocation.height);
-}
-#endif
-
 struct display *
 window_get_display(struct window *window)
 {
@@ -924,21 +1011,28 @@ window_create_surface(struct window *window)
 {
        cairo_surface_t *surface;
        uint32_t flags = 0;
-       
+
        if (!window->transparent)
                flags = SURFACE_OPAQUE;
-       
+
        switch (window->buffer_type) {
 #ifdef HAVE_CAIRO_EGL
        case WINDOW_BUFFER_TYPE_EGL_WINDOW:
-               if (window->cairo_surface) {
-                       window_resize_cairo_window_surface(window);
-                       return;
-               }
-               if (window->display->dpy) {
-                       surface = display_create_egl_window_surface(
-                                       window->display, window->surface,
-                                       flags, &window->allocation);
+               if (!window->toysurface && window->display->dpy)
+                       window->toysurface =
+                               egl_window_surface_create(window->display,
+                                                         window->surface,
+                                                         flags,
+                                                         &window->allocation);
+
+               if (window->toysurface) {
+                       int dx, dy;
+
+                       window_get_resize_dx_dy(window, &dx, &dy);
+                       surface = window->toysurface->prepare(window->toysurface,
+                                                             dx, dy,
+                                                             window->allocation.width,
+                                                             window->allocation.height);
                        break;
                }
                /* fall through */
@@ -1001,6 +1095,9 @@ window_destroy(struct window *window)
        if (window->cairo_surface != NULL)
                cairo_surface_destroy(window->cairo_surface);
 
+       if (window->toysurface)
+               window->toysurface->destroy(window->toysurface);
+
        if (window->frame_cb)
                wl_callback_destroy(window->frame_cb);
        free(window->title);
@@ -3874,8 +3971,6 @@ display_create(int argc, char *argv[])
 {
        struct display *d;
 
-       assert(&egl_window_surface_data_key != &shm_surface_data_key);
-
        d = malloc(sizeof *d);
        if (d == NULL)
                return NULL;
@@ -4053,52 +4148,20 @@ display_acquire_window_surface(struct display *display,
                               struct window *window,
                               EGLContext ctx)
 {
-#ifdef HAVE_CAIRO_EGL
-       struct egl_window_surface_data *data;
-       cairo_device_t *device;
-
-       if (!window->cairo_surface)
-               return -1;
-       device = cairo_surface_get_device(window->cairo_surface);
-       if (!device)
+       if (window->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
                return -1;
 
-       if (!ctx) {
-               if (device == display->argb_device)
-                       ctx = display->argb_ctx;
-               else
-                       assert(0);
-       }
-
-       data = cairo_surface_get_user_data(window->cairo_surface,
-                                          &egl_window_surface_data_key);
-
-       cairo_device_flush(device);
-       cairo_device_acquire(device);
-       if (!eglMakeCurrent(display->dpy, data->surf, data->surf, ctx))
-               fprintf(stderr, "failed to make surface current\n");
-
-       return 0;
-#else
-       return -1;
-#endif
+       return window->toysurface->acquire(window->toysurface, ctx);
 }
 
 void
 display_release_window_surface(struct display *display,
                               struct window *window)
 {
-#ifdef HAVE_CAIRO_EGL
-       cairo_device_t *device;
-       
-       device = cairo_surface_get_device(window->cairo_surface);
-       if (!device)
+       if (window->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
                return;
 
-       if (!eglMakeCurrent(display->dpy, NULL, NULL, display->argb_ctx))
-               fprintf(stderr, "failed to make context current\n");
-       cairo_device_release(device);
-#endif
+       window->toysurface->release(window->toysurface);
 }
 
 void