From 2495d2deb77c8474d7bbe467a0d033909ee3263c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 18 Feb 2020 23:24:11 +0100 Subject: X11: Use ephemeral Cairo surfaces for drawing This uses a similar approach to Gtk, where surfaces are created every draw call. It is a bit slower for a single application in most cases, but uses less memory when drawing isn't happening and plays more nicely with other views or applications. --- pugl/detail/x11_cairo.c | 117 +++++++++++++++++++++++++++--------------------- 1 file changed, 66 insertions(+), 51 deletions(-) diff --git a/pugl/detail/x11_cairo.c b/pugl/detail/x11_cairo.c index 7a02dcd..03a3f8e 100644 --- a/pugl/detail/x11_cairo.c +++ b/pugl/detail/x11_cairo.c @@ -22,6 +22,7 @@ #include "pugl/detail/x11.h" #include "pugl/pugl.h" #include "pugl/pugl_cairo.h" +#include "pugl/pugl_stub.h" #include #include @@ -31,36 +32,56 @@ #include #include -typedef struct { +typedef struct { cairo_surface_t* back; cairo_surface_t* front; cairo_t* cr; } PuglX11CairoSurface; +static void +puglX11CairoClose(PuglView* view) +{ + PuglInternals* const impl = view->impl; + PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; + + cairo_surface_destroy(surface->front); + cairo_surface_destroy(surface->back); + surface->front = surface->back = NULL; +} + static PuglStatus -puglX11CairoCreate(PuglView* view) +puglX11CairoOpen(PuglView* view) { - PuglInternals* const impl = view->impl; - const int width = (int)view->frame.width; - const int height = (int)view->frame.height; - PuglX11CairoSurface surface = { 0 }; - - surface.back = cairo_xlib_surface_create( - impl->display, impl->win, impl->vi->visual, width, height); - surface.front = cairo_surface_create_similar( - surface.back, CAIRO_CONTENT_COLOR, width, height); - - cairo_status_t st = CAIRO_STATUS_SUCCESS; - if (!surface.back || !surface.front || - (st = cairo_surface_status(surface.back)) || - (st = cairo_surface_status(surface.front))) { - cairo_surface_destroy(surface.front); - cairo_surface_destroy(surface.back); + PuglInternals* const impl = view->impl; + PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; + + surface->back = cairo_xlib_surface_create(impl->display, + impl->win, + impl->vi->visual, + (int)view->frame.width, + (int)view->frame.height); + + surface->front = cairo_surface_create_similar( + surface->back, + cairo_surface_get_content(surface->back), + (int)view->frame.width, + (int)view->frame.height); + + if (cairo_surface_status(surface->back) || + cairo_surface_status(surface->front)) { + puglX11CairoClose(view); return PUGL_CREATE_CONTEXT_FAILED; } - impl->surface = calloc(1, sizeof(PuglX11CairoSurface)); - *(PuglX11CairoSurface*)impl->surface = surface; + return PUGL_SUCCESS; +} + +static PuglStatus +puglX11CairoCreate(PuglView* view) +{ + PuglInternals* const impl = view->impl; + + impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface)); return PUGL_SUCCESS; } @@ -71,10 +92,9 @@ puglX11CairoDestroy(PuglView* view) PuglInternals* const impl = view->impl; PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - cairo_surface_destroy(surface->front); - cairo_surface_destroy(surface->back); + puglX11CairoClose(view); free(surface); - impl->surface = NULL; + return PUGL_SUCCESS; } @@ -83,15 +103,17 @@ puglX11CairoEnter(PuglView* view, const PuglEventExpose* expose) { PuglInternals* const impl = view->impl; PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; + PuglStatus st = PUGL_SUCCESS; - if (expose) { + if (expose && !(st = puglX11CairoOpen(view))) { surface->cr = cairo_create(surface->front); - if (!surface->cr || cairo_status(surface->cr)) { - return PUGL_CREATE_CONTEXT_FAILED; + + if (cairo_status(surface->cr)) { + st = PUGL_CREATE_CONTEXT_FAILED; } } - return PUGL_SUCCESS; + return st; } static PuglStatus @@ -101,35 +123,28 @@ puglX11CairoLeave(PuglView* view, const PuglEventExpose* expose) PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; if (expose) { - // Destroy front context that was used by the user for drawing + // Destroy front context and create a new one for drawing to the back cairo_destroy(surface->cr); - - // Create a new context for the back and paint the front onto it surface->cr = cairo_create(surface->back); + + // Clip to expose region + cairo_rectangle(surface->cr, + expose->x, + expose->y, + expose->width, + expose->height); + cairo_clip(surface->cr); + + // Paint front onto back cairo_set_source_surface(surface->cr, surface->front, 0, 0); + cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); cairo_paint(surface->cr); - cairo_destroy(surface->cr); - surface->cr = NULL; - // Flush back to X + // Flush to X and close everything + cairo_destroy(surface->cr); cairo_surface_flush(surface->back); - } - - return PUGL_SUCCESS; -} - -static PuglStatus -puglX11CairoResize(PuglView* view, int width, int height) -{ - PuglInternals* const impl = view->impl; - PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; - - cairo_xlib_surface_set_size(surface->back, width, height); - - cairo_surface_destroy(surface->front); - if (!(surface->front = cairo_surface_create_similar( - surface->back, CAIRO_CONTENT_COLOR, width, height))) { - return PUGL_CREATE_CONTEXT_FAILED; + puglX11CairoClose(view); + surface->cr = NULL; } return PUGL_SUCCESS; @@ -153,7 +168,7 @@ puglCairoBackend(void) puglX11CairoDestroy, puglX11CairoEnter, puglX11CairoLeave, - puglX11CairoResize, + puglStubResize, puglX11CairoGetContext }; -- cgit v1.2.1