window: add toysurface abstraction and port EGL path
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>
This commit is contained in:
committed by
Kristian Høgsberg
parent
b88b68fa42
commit
03fc316000
+178
-115
@@ -142,6 +142,44 @@ struct window_output {
|
|||||||
struct wl_list link;
|
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 window {
|
||||||
struct display *display;
|
struct display *display;
|
||||||
struct window *parent;
|
struct window *parent;
|
||||||
@@ -166,6 +204,7 @@ struct window {
|
|||||||
|
|
||||||
enum window_buffer_type buffer_type;
|
enum window_buffer_type buffer_type;
|
||||||
|
|
||||||
|
struct toysurface *toysurface;
|
||||||
cairo_surface_t *cairo_surface;
|
cairo_surface_t *cairo_surface;
|
||||||
|
|
||||||
struct shm_pool *pool;
|
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 shm_surface_data_key;
|
||||||
static const cairo_user_data_key_t egl_window_surface_data_key;
|
|
||||||
|
|
||||||
#ifdef HAVE_CAIRO_EGL
|
#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 display *display;
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
struct wl_egl_window *window;
|
struct wl_egl_window *egl_window;
|
||||||
EGLSurface surf;
|
EGLSurface egl_surface;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static struct egl_window_surface *
|
||||||
egl_window_surface_data_destroy(void *p)
|
to_egl_window_surface(struct toysurface *base)
|
||||||
{
|
{
|
||||||
struct egl_window_surface_data *data = p;
|
return container_of(base, struct egl_window_surface, base);
|
||||||
struct display *d = data->display;
|
|
||||||
|
|
||||||
eglDestroySurface(d->dpy, data->surf);
|
|
||||||
wl_egl_window_destroy(data->window);
|
|
||||||
data->surface = NULL;
|
|
||||||
|
|
||||||
free(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static cairo_surface_t *
|
static cairo_surface_t *
|
||||||
display_create_egl_window_surface(struct display *display,
|
egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
|
||||||
struct wl_surface *surface,
|
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_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 *surface = to_egl_window_surface(base);
|
||||||
|
cairo_device_t *device;
|
||||||
|
|
||||||
|
device = cairo_surface_get_device(surface->cairo_surface);
|
||||||
|
if (!device)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
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 void
|
||||||
|
egl_window_surface_release(struct toysurface *base)
|
||||||
|
{
|
||||||
|
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||||
|
cairo_device_t *device;
|
||||||
|
|
||||||
|
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,
|
uint32_t flags,
|
||||||
struct rectangle *rectangle)
|
struct rectangle *rectangle)
|
||||||
{
|
{
|
||||||
cairo_surface_t *cairo_surface;
|
struct egl_window_surface *surface;
|
||||||
struct egl_window_surface_data *data;
|
|
||||||
EGLConfig config;
|
|
||||||
cairo_device_t *device;
|
|
||||||
|
|
||||||
data = malloc(sizeof *data);
|
surface = calloc(1, sizeof *surface);
|
||||||
if (data == NULL)
|
if (!surface)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
data->display = display;
|
surface->base.prepare = egl_window_surface_prepare;
|
||||||
data->surface = surface;
|
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;
|
||||||
|
|
||||||
config = display->argb_config;
|
surface->display = display;
|
||||||
device = display->argb_device;
|
surface->surface = wl_surface;
|
||||||
|
|
||||||
data->window = wl_egl_window_create(surface,
|
surface->egl_window = wl_egl_window_create(surface->surface,
|
||||||
rectangle->width,
|
rectangle->width,
|
||||||
rectangle->height);
|
rectangle->height);
|
||||||
|
|
||||||
data->surf = eglCreateWindowSurface(display->dpy, config,
|
surface->egl_surface = eglCreateWindowSurface(display->dpy,
|
||||||
data->window, NULL);
|
display->argb_config,
|
||||||
|
surface->egl_window,
|
||||||
|
NULL);
|
||||||
|
|
||||||
cairo_surface = cairo_gl_surface_create_for_egl(device,
|
surface->cairo_surface =
|
||||||
data->surf,
|
cairo_gl_surface_create_for_egl(display->argb_device,
|
||||||
|
surface->egl_surface,
|
||||||
rectangle->width,
|
rectangle->width,
|
||||||
rectangle->height);
|
rectangle->height);
|
||||||
|
|
||||||
cairo_surface_set_user_data(cairo_surface,
|
return &surface->base;
|
||||||
&egl_window_surface_data_key,
|
|
||||||
data, egl_window_surface_data_destroy);
|
|
||||||
|
|
||||||
return cairo_surface;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -812,9 +925,6 @@ window_attach_surface(struct window *window)
|
|||||||
{
|
{
|
||||||
struct display *display = window->display;
|
struct display *display = window->display;
|
||||||
struct wl_buffer *buffer;
|
struct wl_buffer *buffer;
|
||||||
#ifdef HAVE_CAIRO_EGL
|
|
||||||
struct egl_window_surface_data *data;
|
|
||||||
#endif
|
|
||||||
int32_t x, y;
|
int32_t x, y;
|
||||||
|
|
||||||
if (window->type == TYPE_NONE) {
|
if (window->type == TYPE_NONE) {
|
||||||
@@ -840,13 +950,8 @@ window_attach_surface(struct window *window)
|
|||||||
switch (window->buffer_type) {
|
switch (window->buffer_type) {
|
||||||
#ifdef HAVE_CAIRO_EGL
|
#ifdef HAVE_CAIRO_EGL
|
||||||
case WINDOW_BUFFER_TYPE_EGL_WINDOW:
|
case WINDOW_BUFFER_TYPE_EGL_WINDOW:
|
||||||
data = cairo_surface_get_user_data(window->cairo_surface,
|
window->toysurface->swap(window->toysurface,
|
||||||
&egl_window_surface_data_key);
|
&window->server_allocation);
|
||||||
|
|
||||||
cairo_gl_surface_swapbuffers(window->cairo_surface);
|
|
||||||
wl_egl_window_get_attached_size(data->window,
|
|
||||||
&window->server_allocation.width,
|
|
||||||
&window->server_allocation.height);
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case WINDOW_BUFFER_TYPE_SHM:
|
case WINDOW_BUFFER_TYPE_SHM:
|
||||||
@@ -876,8 +981,12 @@ window_has_focus(struct window *window)
|
|||||||
static void
|
static void
|
||||||
window_flush(struct window *window)
|
window_flush(struct window *window)
|
||||||
{
|
{
|
||||||
if (window->cairo_surface)
|
if (!window->cairo_surface)
|
||||||
|
return;
|
||||||
|
|
||||||
window_attach_surface(window);
|
window_attach_surface(window);
|
||||||
|
cairo_surface_destroy(window->cairo_surface);
|
||||||
|
window->cairo_surface = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -891,28 +1000,6 @@ window_set_surface(struct window *window, cairo_surface_t *surface)
|
|||||||
window->cairo_surface = 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 *
|
struct display *
|
||||||
window_get_display(struct window *window)
|
window_get_display(struct window *window)
|
||||||
{
|
{
|
||||||
@@ -931,14 +1018,21 @@ window_create_surface(struct window *window)
|
|||||||
switch (window->buffer_type) {
|
switch (window->buffer_type) {
|
||||||
#ifdef HAVE_CAIRO_EGL
|
#ifdef HAVE_CAIRO_EGL
|
||||||
case WINDOW_BUFFER_TYPE_EGL_WINDOW:
|
case WINDOW_BUFFER_TYPE_EGL_WINDOW:
|
||||||
if (window->cairo_surface) {
|
if (!window->toysurface && window->display->dpy)
|
||||||
window_resize_cairo_window_surface(window);
|
window->toysurface =
|
||||||
return;
|
egl_window_surface_create(window->display,
|
||||||
}
|
window->surface,
|
||||||
if (window->display->dpy) {
|
flags,
|
||||||
surface = display_create_egl_window_surface(
|
&window->allocation);
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
/* fall through */
|
/* fall through */
|
||||||
@@ -1001,6 +1095,9 @@ window_destroy(struct window *window)
|
|||||||
if (window->cairo_surface != NULL)
|
if (window->cairo_surface != NULL)
|
||||||
cairo_surface_destroy(window->cairo_surface);
|
cairo_surface_destroy(window->cairo_surface);
|
||||||
|
|
||||||
|
if (window->toysurface)
|
||||||
|
window->toysurface->destroy(window->toysurface);
|
||||||
|
|
||||||
if (window->frame_cb)
|
if (window->frame_cb)
|
||||||
wl_callback_destroy(window->frame_cb);
|
wl_callback_destroy(window->frame_cb);
|
||||||
free(window->title);
|
free(window->title);
|
||||||
@@ -3874,8 +3971,6 @@ display_create(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
struct display *d;
|
struct display *d;
|
||||||
|
|
||||||
assert(&egl_window_surface_data_key != &shm_surface_data_key);
|
|
||||||
|
|
||||||
d = malloc(sizeof *d);
|
d = malloc(sizeof *d);
|
||||||
if (d == NULL)
|
if (d == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -4053,52 +4148,20 @@ display_acquire_window_surface(struct display *display,
|
|||||||
struct window *window,
|
struct window *window,
|
||||||
EGLContext ctx)
|
EGLContext ctx)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CAIRO_EGL
|
if (window->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
|
||||||
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)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!ctx) {
|
return window->toysurface->acquire(window->toysurface, 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
display_release_window_surface(struct display *display,
|
display_release_window_surface(struct display *display,
|
||||||
struct window *window)
|
struct window *window)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_CAIRO_EGL
|
if (window->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
|
||||||
cairo_device_t *device;
|
|
||||||
|
|
||||||
device = cairo_surface_get_device(window->cairo_surface);
|
|
||||||
if (!device)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!eglMakeCurrent(display->dpy, NULL, NULL, display->argb_ctx))
|
window->toysurface->release(window->toysurface);
|
||||||
fprintf(stderr, "failed to make context current\n");
|
|
||||||
cairo_device_release(device);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
Reference in New Issue
Block a user