This takes all of the gbm related code in wayland-glamor.c and moves it into it's own EGL backend for Xwayland, xwayland-glamor-gbm.c. Additionally, we add the egl_backend struct into xwl_screen in order to provide hooks for alternative EGL backends such as nvidia's EGLStreams.
Signed-off-by: Lyude Paul <ly...@redhat.com> --- hw/xwayland/Makefile.am | 3 +- hw/xwayland/meson.build | 1 + hw/xwayland/xwayland-glamor-gbm.c | 632 ++++++++++++++++++++++++++++++++++++++ hw/xwayland/xwayland-glamor.c | 500 +----------------------------- hw/xwayland/xwayland.c | 13 +- hw/xwayland/xwayland.h | 54 +++- 6 files changed, 705 insertions(+), 498 deletions(-) create mode 100644 hw/xwayland/xwayland-glamor-gbm.c diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am index 7204591e3..d9e9fe8b6 100644 --- a/hw/xwayland/Makefile.am +++ b/hw/xwayland/Makefile.am @@ -33,7 +33,8 @@ Xwayland_built_sources = if GLAMOR_EGL Xwayland_SOURCES += \ - xwayland-glamor.c + xwayland-glamor.c \ + xwayland-glamor-gbm.c if XV Xwayland_SOURCES += \ xwayland-glamor-xv.c diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index 24203c63e..d219e2f44 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -43,6 +43,7 @@ srcs += code.process(xdg_output_xml) xwayland_glamor = [] if gbm_dep.found() srcs += 'xwayland-glamor.c' + srcs += 'xwayland-glamor-gbm.c' if build_xv srcs += 'xwayland-glamor-xv.c' endif diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c new file mode 100644 index 000000000..4b973019f --- /dev/null +++ b/hw/xwayland/xwayland-glamor-gbm.c @@ -0,0 +1,632 @@ +/* + * Copyright © 2011-2014 Intel Corporation + * Copyright © 2017 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including + * the next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Lyude Paul <ly...@redhat.com> + * + */ + +#include "xwayland.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <xf86drm.h> + +#define MESA_EGL_NO_X11_HEADERS +#include <gbm.h> +#include <glamor_egl.h> + +#include <glamor.h> +#include <glamor_context.h> +#include <dri3.h> +#include "drm-client-protocol.h" + +struct xwl_gbm_private { + struct gbm_device *gbm; + struct wl_drm *drm; + char *device_name; + int drm_fd; + int fd_render_node; + Bool drm_authenticated; + uint32_t capabilities; +}; + +struct xwl_pixmap { + struct wl_buffer *buffer; + EGLImage image; + unsigned int texture; + struct gbm_bo *bo; +}; + +static DevPrivateKeyRec xwl_gbm_private_key; +static DevPrivateKeyRec xwl_auth_state_private_key; + +static inline struct xwl_gbm_private * +xwl_gbm_get(struct xwl_screen *xwl_screen) +{ + return dixLookupPrivate(&xwl_screen->screen->devPrivates, + &xwl_gbm_private_key); +} + +static uint32_t +gbm_format_for_depth(int depth) +{ + switch (depth) { + case 16: + return GBM_FORMAT_RGB565; + case 24: + return GBM_FORMAT_XRGB8888; + default: + ErrorF("unexpected depth: %d\n", depth); + case 32: + return GBM_FORMAT_ARGB8888; + } +} + +static uint32_t +drm_format_for_depth(int depth) +{ + switch (depth) { + case 15: + return WL_DRM_FORMAT_XRGB1555; + case 16: + return WL_DRM_FORMAT_RGB565; + case 24: + return WL_DRM_FORMAT_XRGB8888; + default: + ErrorF("unexpected depth: %d\n", depth); + case 32: + return WL_DRM_FORMAT_ARGB8888; + } +} + +static char +is_fd_render_node(int fd) +{ + struct stat render; + + if (fstat(fd, &render)) + return 0; + if (!S_ISCHR(render.st_mode)) + return 0; + if (render.st_rdev & 0x80) + return 1; + + return 0; +} + +static PixmapPtr +xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo, + int depth) +{ + PixmapPtr pixmap; + struct xwl_pixmap *xwl_pixmap; + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + + xwl_pixmap = malloc(sizeof *xwl_pixmap); + if (xwl_pixmap == NULL) + return NULL; + + pixmap = glamor_create_pixmap(screen, + gbm_bo_get_width(bo), + gbm_bo_get_height(bo), + depth, + GLAMOR_CREATE_PIXMAP_NO_TEXTURE); + if (!pixmap) { + free(xwl_pixmap); + return NULL; + } + + if (lastGLContext != xwl_screen->glamor_ctx) { + lastGLContext = xwl_screen->glamor_ctx; + xwl_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx); + } + + xwl_pixmap->bo = bo; + xwl_pixmap->buffer = NULL; + xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display, + xwl_screen->egl_context, + EGL_NATIVE_PIXMAP_KHR, + xwl_pixmap->bo, NULL); + + glGenTextures(1, &xwl_pixmap->texture); + glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image); + glBindTexture(GL_TEXTURE_2D, 0); + + xwl_pixmap_set_private(pixmap, xwl_pixmap); + + glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture); + glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM); + + return pixmap; +} + +static PixmapPtr +xwl_glamor_gbm_create_pixmap(ScreenPtr screen, + int width, int height, int depth, + unsigned int hint) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct gbm_bo *bo; + + if (width > 0 && height > 0 && depth >= 15 && + (hint == 0 || + hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP || + hint == CREATE_PIXMAP_USAGE_SHARED)) { + bo = gbm_bo_create(xwl_gbm->gbm, width, height, + gbm_format_for_depth(depth), + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + + if (bo) + return xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth); + } + + return glamor_create_pixmap(screen, width, height, depth, hint); +} + +static Bool +xwl_glamor_gbm_destroy_pixmap(PixmapPtr pixmap) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen); + struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); + + if (xwl_pixmap && pixmap->refcnt == 1) { + if (xwl_pixmap->buffer) + wl_buffer_destroy(xwl_pixmap->buffer); + + eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image); + gbm_bo_destroy(xwl_pixmap->bo); + free(xwl_pixmap); + } + + return glamor_destroy_pixmap(pixmap); +} + +static struct wl_buffer * +xwl_glamor_gbm_get_wl_buffer_for_pixmap(PixmapPtr pixmap) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen); + struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct wl_buffer *buffer; + int prime_fd; + + if (xwl_pixmap->buffer) + return xwl_pixmap->buffer; + + prime_fd = gbm_bo_get_fd(xwl_pixmap->bo); + if (prime_fd == -1) + return NULL; + + buffer = wl_drm_create_prime_buffer( + xwl_gbm->drm, prime_fd, + pixmap->drawable.width, pixmap->drawable.height, + drm_format_for_depth(pixmap->drawable.depth), + 0, gbm_bo_get_stride(xwl_pixmap->bo), 0, 0, 0, 0); + + close(prime_fd); + + return buffer; +} + +static void +xwl_glamor_gbm_cleanup(struct xwl_screen *xwl_screen) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (xwl_gbm->device_name) + free(xwl_gbm->device_name); + if (xwl_gbm->drm_fd) + close(xwl_gbm->drm_fd); + if (xwl_gbm->drm) + wl_drm_destroy(xwl_gbm->drm); + if (xwl_gbm->gbm) + gbm_device_destroy(xwl_gbm->gbm); + + free(xwl_gbm); +} + +struct xwl_auth_state { + int fd; + ClientPtr client; + struct wl_callback *callback; +}; + +static void +free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state) +{ + dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL); + if (state) { + wl_callback_destroy(state->callback); + free(state); + } +} + +static void +xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data) +{ + NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; + ClientPtr pClient = clientinfo->client; + struct xwl_auth_state *state; + + switch (pClient->clientState) { + case ClientStateGone: + case ClientStateRetained: + state = dixLookupPrivate(&pClient->devPrivates, + &xwl_auth_state_private_key); + free_xwl_auth_state(pClient, state); + break; + default: + break; + } +} + +static void +sync_callback(void *data, struct wl_callback *callback, uint32_t serial) +{ + struct xwl_auth_state *state = data; + ClientPtr client = state->client; + + /* if the client is gone, the callback is cancelled so it's safe to + * assume the client is still in ClientStateRunning at this point... + */ + dri3_send_open_reply(client, state->fd); + AttendClient(client); + free_xwl_auth_state(client, state); +} + +static const struct wl_callback_listener sync_listener = { + sync_callback +}; + +static int +xwl_dri3_open_client(ClientPtr client, + ScreenPtr screen, + RRProviderPtr provider, + int *pfd) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct xwl_auth_state *state; + drm_magic_t magic; + int fd; + + fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC); + if (fd < 0) + return BadAlloc; + if (xwl_gbm->fd_render_node) { + *pfd = fd; + return Success; + } + + state = malloc(sizeof *state); + if (state == NULL) { + close(fd); + return BadAlloc; + } + + state->client = client; + state->fd = fd; + + if (drmGetMagic(state->fd, &magic) < 0) { + close(state->fd); + free(state); + return BadMatch; + } + + wl_drm_authenticate(xwl_gbm->drm, magic); + state->callback = wl_display_sync(xwl_screen->display); + wl_callback_add_listener(state->callback, &sync_listener, state); + dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state); + + IgnoreClient(client); + + return Success; +} + +static PixmapPtr +xwl_dri3_pixmap_from_fd(ScreenPtr screen, int fd, + CARD16 width, CARD16 height, CARD16 stride, + CARD8 depth, CARD8 bpp) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct gbm_import_fd_data data; + struct gbm_bo *bo; + PixmapPtr pixmap; + + if (width == 0 || height == 0 || + depth < 15 || bpp != BitsPerPixel(depth) || stride < width * bpp / 8) + return NULL; + + data.fd = fd; + data.width = width; + data.height = height; + data.stride = stride; + data.format = gbm_format_for_depth(depth); + bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD, &data, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (bo == NULL) + return NULL; + + pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth); + if (pixmap == NULL) { + gbm_bo_destroy(bo); + return NULL; + } + + return pixmap; +} + +static int +xwl_dri3_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, + CARD16 *stride, CARD32 *size) +{ + struct xwl_pixmap *xwl_pixmap; + + xwl_pixmap = xwl_pixmap_get(pixmap); + + *stride = gbm_bo_get_stride(xwl_pixmap->bo); + *size = pixmap->drawable.width * *stride; + + return gbm_bo_get_fd(xwl_pixmap->bo); +} + +static dri3_screen_info_rec xwl_dri3_info = { + .version = 1, + .open = NULL, + .pixmap_from_fd = xwl_dri3_pixmap_from_fd, + .fd_from_pixmap = xwl_dri3_fd_from_pixmap, + .open_client = xwl_dri3_open_client, +}; + +static void +xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device) +{ + struct xwl_screen *xwl_screen = data; + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + drm_magic_t magic; + + xwl_gbm->device_name = strdup(device); + if (!xwl_gbm->device_name) { + xwl_glamor_gbm_cleanup(xwl_screen); + return; + } + + xwl_gbm->drm_fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC); + if (xwl_gbm->drm_fd == -1) { + ErrorF("wayland-egl: could not open %s (%s)\n", + xwl_gbm->device_name, strerror(errno)); + xwl_glamor_gbm_cleanup(xwl_screen); + return; + } + + if (is_fd_render_node(xwl_gbm->drm_fd)) { + xwl_gbm->fd_render_node = 1; + xwl_screen->expecting_event--; + } else { + drmGetMagic(xwl_gbm->drm_fd, &magic); + wl_drm_authenticate(xwl_gbm->drm, magic); + } +} + +static void +xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format) +{ + struct xwl_screen *xwl_screen = data; + + switch (format) { + case WL_DRM_FORMAT_ARGB8888: + xwl_screen->formats |= XWL_FORMAT_ARGB8888; + break; + case WL_DRM_FORMAT_XRGB8888: + xwl_screen->formats |= XWL_FORMAT_XRGB8888; + break; + case WL_DRM_FORMAT_RGB565: + xwl_screen->formats |= XWL_FORMAT_RGB565; + break; + } +} + +static void +xwl_drm_handle_authenticated(void *data, struct wl_drm *drm) +{ + struct xwl_screen *xwl_screen = data; + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + xwl_gbm->drm_authenticated = TRUE; + xwl_screen->expecting_event--; +} + +static void +xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value) +{ + xwl_gbm_get(data)->capabilities = value; +} + +static const struct wl_drm_listener xwl_drm_listener = { + xwl_drm_handle_device, + xwl_drm_handle_format, + xwl_drm_handle_authenticated, + xwl_drm_handle_capabilities +}; + +static void +xwl_glamor_gbm_init_wl_registry(struct xwl_screen *xwl_screen, + struct wl_registry *wl_registry, + const char *name, + uint32_t id, uint32_t version) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (strcmp(name, "wl_drm") != 0) + return; + + if (version < 2) { + ErrorF("glamor gbm: wl_drm version %d is too old, we require at least v2\n", + version); + xwl_glamor_gbm_cleanup(xwl_screen); + return; + } + + xwl_gbm->drm = wl_registry_bind(xwl_screen->registry, + id, &wl_drm_interface, 2); + wl_drm_add_listener(xwl_gbm->drm, &xwl_drm_listener, xwl_screen); + xwl_screen->expecting_event++; +} + +static Bool +xwl_glamor_gbm_init_egl(struct xwl_screen *xwl_screen) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + EGLint major, minor; + Bool egl_initialized = FALSE; + static const EGLint config_attribs_core[] = { + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_CONTEXT_MAJOR_VERSION_KHR, + GLAMOR_GL_CORE_VER_MAJOR, + EGL_CONTEXT_MINOR_VERSION_KHR, + GLAMOR_GL_CORE_VER_MINOR, + EGL_NONE + }; + + xwl_gbm->gbm = gbm_create_device(xwl_gbm->drm_fd); + if (!xwl_gbm->gbm) { + ErrorF("couldn't create gbm device\n"); + goto error; + } + + xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA, + xwl_gbm->gbm); + if (xwl_screen->egl_display == EGL_NO_DISPLAY) { + ErrorF("glamor_egl_get_display() failed\n"); + goto error; + } + + egl_initialized = eglInitialize(xwl_screen->egl_display, &major, &minor); + if (!egl_initialized) { + ErrorF("eglInitialize() failed\n"); + goto error; + } + + eglBindAPI(EGL_OPENGL_API); + + xwl_screen->egl_context = eglCreateContext( + xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, config_attribs_core); + if (xwl_screen->egl_context == EGL_NO_CONTEXT) { + xwl_screen->egl_context = eglCreateContext( + xwl_screen->egl_display, NULL, EGL_NO_CONTEXT, NULL); + } + + if (xwl_screen->egl_context == EGL_NO_CONTEXT) { + ErrorF("Failed to create EGL context\n"); + goto error; + } + + if (!eglMakeCurrent(xwl_screen->egl_display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + xwl_screen->egl_context)) { + ErrorF("Failed to make EGL context current\n"); + goto error; + } + + if (!epoxy_has_gl_extension("GL_OES_EGL_image")) + ErrorF("GL_OES_EGL_image not available\n"); + + return TRUE; +error: + if (xwl_screen->egl_context != EGL_NO_CONTEXT) { + eglDestroyContext(xwl_screen->egl_display, xwl_screen->egl_context); + xwl_screen->egl_context = EGL_NO_CONTEXT; + } + + if (xwl_screen->egl_display != EGL_NO_DISPLAY) { + eglTerminate(xwl_screen->egl_display); + xwl_screen->egl_display = EGL_NO_DISPLAY; + } + + xwl_glamor_gbm_cleanup(xwl_screen); + return FALSE; +} + +static Bool +xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen) +{ + if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) { + ErrorF("Failed to initialize dri3\n"); + goto error; + } + + if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT, + 0)) { + ErrorF("Failed to register private key\n"); + goto error; + } + + if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback, + NULL)) { + ErrorF("Failed to add client state callback\n"); + goto error; + } + + xwl_screen->screen->CreatePixmap = xwl_glamor_gbm_create_pixmap; + xwl_screen->screen->DestroyPixmap = xwl_glamor_gbm_destroy_pixmap; + + return TRUE; +error: + xwl_glamor_gbm_cleanup(xwl_screen); + return FALSE; +} + +Bool +xwl_glamor_init_gbm(struct xwl_screen *xwl_screen) +{ + struct xwl_gbm_private *xwl_gbm; + + if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0)) + return FALSE; + + xwl_gbm = calloc(sizeof(*xwl_gbm), 1); + if (!xwl_gbm) { + ErrorF("glamor: Not enough memory to setup GBM, disabling\n"); + return FALSE; + } + + dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key, + xwl_gbm); + + xwl_screen->egl_backend.init_wl_registry = xwl_glamor_gbm_init_wl_registry; + xwl_screen->egl_backend.init_egl = xwl_glamor_gbm_init_egl; + xwl_screen->egl_backend.init_screen = xwl_glamor_gbm_init_screen; + xwl_screen->egl_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap; + + return TRUE; +} diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c index 8ffb40d6f..2089c2848 100644 --- a/hw/xwayland/xwayland-glamor.c +++ b/hw/xwayland/xwayland-glamor.c @@ -35,17 +35,6 @@ #include <glamor.h> #include <glamor_context.h> -#include <dri3.h> -#include "drm-client-protocol.h" - -static DevPrivateKeyRec xwl_auth_state_private_key; - -struct xwl_pixmap { - struct wl_buffer *buffer; - struct gbm_bo *bo; - void *image; - unsigned int texture; -}; static void xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx) @@ -58,38 +47,6 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx) FatalError("Failed to make EGL context current\n"); } -static uint32_t -drm_format_for_depth(int depth) -{ - switch (depth) { - case 15: - return WL_DRM_FORMAT_XRGB1555; - case 16: - return WL_DRM_FORMAT_RGB565; - case 24: - return WL_DRM_FORMAT_XRGB8888; - default: - ErrorF("unexpected depth: %d\n", depth); - case 32: - return WL_DRM_FORMAT_ARGB8888; - } -} - -static uint32_t -gbm_format_for_depth(int depth) -{ - switch (depth) { - case 16: - return GBM_FORMAT_RGB565; - case 24: - return GBM_FORMAT_XRGB8888; - default: - ErrorF("unexpected depth: %d\n", depth); - case 32: - return GBM_FORMAT_ARGB8888; - } -} - void glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx) { @@ -103,121 +60,23 @@ glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx) xwl_screen->glamor_ctx = glamor_ctx; } -static PixmapPtr -xwl_glamor_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo, int depth) -{ - PixmapPtr pixmap; - struct xwl_pixmap *xwl_pixmap; - struct xwl_screen *xwl_screen = xwl_screen_get(screen); - - xwl_pixmap = malloc(sizeof *xwl_pixmap); - if (xwl_pixmap == NULL) - return NULL; - - pixmap = glamor_create_pixmap(screen, - gbm_bo_get_width(bo), - gbm_bo_get_height(bo), - depth, - GLAMOR_CREATE_PIXMAP_NO_TEXTURE); - if (pixmap == NULL) { - free(xwl_pixmap); - return NULL; - } - - if (lastGLContext != xwl_screen->glamor_ctx) { - lastGLContext = xwl_screen->glamor_ctx; - xwl_glamor_egl_make_current(xwl_screen->glamor_ctx); - } - - xwl_pixmap->bo = bo; - xwl_pixmap->buffer = NULL; - xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display, - xwl_screen->egl_context, - EGL_NATIVE_PIXMAP_KHR, - xwl_pixmap->bo, NULL); - - glGenTextures(1, &xwl_pixmap->texture); - glBindTexture(GL_TEXTURE_2D, xwl_pixmap->texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, xwl_pixmap->image); - glBindTexture(GL_TEXTURE_2D, 0); - - xwl_pixmap_set_private(pixmap, xwl_pixmap); - - glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture); - glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM); - - return pixmap; -} - struct wl_buffer * xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap) { struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen); - struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); - int prime_fd; - if (xwl_pixmap->buffer) - return xwl_pixmap->buffer; - - prime_fd = gbm_bo_get_fd(xwl_pixmap->bo); - if (prime_fd == -1) - return NULL; - - xwl_pixmap->buffer = - wl_drm_create_prime_buffer(xwl_screen->drm, prime_fd, - pixmap->drawable.width, - pixmap->drawable.height, - drm_format_for_depth(pixmap->drawable.depth), - 0, gbm_bo_get_stride(xwl_pixmap->bo), - 0, 0, - 0, 0); - - close(prime_fd); - - return xwl_pixmap->buffer; + return xwl_screen->egl_backend.get_wl_buffer_for_pixmap(pixmap); } -static PixmapPtr -xwl_glamor_create_pixmap(ScreenPtr screen, - int width, int height, int depth, unsigned int hint) +void +xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen, + struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t version) { - struct xwl_screen *xwl_screen = xwl_screen_get(screen); - struct gbm_bo *bo; - - if (width > 0 && height > 0 && depth >= 15 && - (hint == 0 || - hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP || - hint == CREATE_PIXMAP_USAGE_SHARED)) { - bo = gbm_bo_create(xwl_screen->gbm, width, height, - gbm_format_for_depth(depth), - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - - if (bo) - return xwl_glamor_create_pixmap_for_bo(screen, bo, depth); - } - - return glamor_create_pixmap(screen, width, height, depth, hint); -} - -static Bool -xwl_glamor_destroy_pixmap(PixmapPtr pixmap) -{ - struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen); - struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); - - if (xwl_pixmap && pixmap->refcnt == 1) { - if (xwl_pixmap->buffer) - wl_buffer_destroy(xwl_pixmap->buffer); - - eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image); - gbm_bo_destroy(xwl_pixmap->bo); - free(xwl_pixmap); - } - - return glamor_destroy_pixmap(pixmap); + if (xwl_screen->egl_backend.init_wl_registry) + xwl_screen->egl_backend.init_wl_registry(xwl_screen, registry, + interface, id, version); } static Bool @@ -239,10 +98,9 @@ xwl_glamor_create_screen_resources(ScreenPtr screen) fbCreatePixmap(screen, 0, 0, screen->rootDepth, 0); } else { - screen->devPrivate = - xwl_glamor_create_pixmap(screen, screen->width, screen->height, - screen->rootDepth, - CREATE_PIXMAP_USAGE_BACKING_PIXMAP); + screen->devPrivate = screen->CreatePixmap( + screen, screen->width, screen->height, screen->rootDepth, + CREATE_PIXMAP_USAGE_BACKING_PIXMAP); } SetRootClip(screen, xwl_screen->root_clip_mode); @@ -250,169 +108,6 @@ xwl_glamor_create_screen_resources(ScreenPtr screen) return screen->devPrivate != NULL; } -static char -is_fd_render_node(int fd) -{ - struct stat render; - - if (fstat(fd, &render)) - return 0; - if (!S_ISCHR(render.st_mode)) - return 0; - if (render.st_rdev & 0x80) - return 1; - - return 0; -} - -static void -xwl_drm_init_egl(struct xwl_screen *xwl_screen) -{ - EGLint major, minor; - static const EGLint config_attribs_core[] = { - EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, - EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, - EGL_CONTEXT_MAJOR_VERSION_KHR, - GLAMOR_GL_CORE_VER_MAJOR, - EGL_CONTEXT_MINOR_VERSION_KHR, - GLAMOR_GL_CORE_VER_MINOR, - EGL_NONE - }; - - if (xwl_screen->egl_display) - return; - - xwl_screen->expecting_event--; - - xwl_screen->gbm = gbm_create_device(xwl_screen->drm_fd); - if (xwl_screen->gbm == NULL) { - ErrorF("couldn't get display device\n"); - return; - } - - xwl_screen->egl_display = glamor_egl_get_display(EGL_PLATFORM_GBM_MESA, - xwl_screen->gbm); - if (xwl_screen->egl_display == EGL_NO_DISPLAY) { - ErrorF("glamor_egl_get_display() failed\n"); - return; - } - - if (!eglInitialize(xwl_screen->egl_display, &major, &minor)) { - ErrorF("eglInitialize() failed\n"); - return; - } - - eglBindAPI(EGL_OPENGL_API); - - xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display, - NULL, EGL_NO_CONTEXT, config_attribs_core); - if (!xwl_screen->egl_context) - xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display, - NULL, EGL_NO_CONTEXT, NULL); - - if (xwl_screen->egl_context == EGL_NO_CONTEXT) { - ErrorF("Failed to create EGL context\n"); - return; - } - - if (!eglMakeCurrent(xwl_screen->egl_display, - EGL_NO_SURFACE, EGL_NO_SURFACE, - xwl_screen->egl_context)) { - ErrorF("Failed to make EGL context current\n"); - return; - } - - if (!epoxy_has_gl_extension("GL_OES_EGL_image")) { - ErrorF("GL_OES_EGL_image not available\n"); - return; - } - - return; -} - -static void -xwl_drm_handle_device(void *data, struct wl_drm *drm, const char *device) -{ - struct xwl_screen *xwl_screen = data; - drm_magic_t magic; - - xwl_screen->device_name = strdup(device); - if (!xwl_screen->device_name) - return; - - xwl_screen->drm_fd = open(xwl_screen->device_name, O_RDWR | O_CLOEXEC); - if (xwl_screen->drm_fd == -1) { - ErrorF("wayland-egl: could not open %s (%s)\n", - xwl_screen->device_name, strerror(errno)); - return; - } - - if (is_fd_render_node(xwl_screen->drm_fd)) { - xwl_screen->fd_render_node = 1; - xwl_drm_init_egl(xwl_screen); - } else { - drmGetMagic(xwl_screen->drm_fd, &magic); - wl_drm_authenticate(xwl_screen->drm, magic); - } -} - -static void -xwl_drm_handle_format(void *data, struct wl_drm *drm, uint32_t format) -{ - struct xwl_screen *xwl_screen = data; - - switch (format) { - case WL_DRM_FORMAT_ARGB8888: - xwl_screen->formats |= XWL_FORMAT_ARGB8888; - break; - case WL_DRM_FORMAT_XRGB8888: - xwl_screen->formats |= XWL_FORMAT_XRGB8888; - break; - case WL_DRM_FORMAT_RGB565: - xwl_screen->formats |= XWL_FORMAT_RGB565; - break; - } -} - -static void -xwl_drm_handle_authenticated(void *data, struct wl_drm *drm) -{ - struct xwl_screen *xwl_screen = data; - - if (!xwl_screen->egl_display) - xwl_drm_init_egl(xwl_screen); -} - -static void -xwl_drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t value) -{ - struct xwl_screen *xwl_screen = data; - - xwl_screen->capabilities = value; -} - -static const struct wl_drm_listener xwl_drm_listener = { - xwl_drm_handle_device, - xwl_drm_handle_format, - xwl_drm_handle_authenticated, - xwl_drm_handle_capabilities -}; - -Bool -xwl_screen_init_glamor(struct xwl_screen *xwl_screen, - uint32_t id, uint32_t version) -{ - if (version < 2) - return FALSE; - - xwl_screen->drm = - wl_registry_bind(xwl_screen->registry, id, &wl_drm_interface, 2); - wl_drm_add_listener(xwl_screen->drm, &xwl_drm_listener, xwl_screen); - xwl_screen->expecting_event++; - - return TRUE; -} - int glamor_egl_dri3_fd_name_from_tex(ScreenPtr screen, PixmapPtr pixmap, @@ -422,157 +117,6 @@ glamor_egl_dri3_fd_name_from_tex(ScreenPtr screen, return 0; } -struct xwl_auth_state { - int fd; - ClientPtr client; - struct wl_callback *callback; -}; - -static void -free_xwl_auth_state(ClientPtr pClient, struct xwl_auth_state *state) -{ - dixSetPrivate(&pClient->devPrivates, &xwl_auth_state_private_key, NULL); - if (state) { - wl_callback_destroy(state->callback); - free(state); - } -} - -static void -xwl_auth_state_client_callback(CallbackListPtr *pcbl, void *unused, void *data) -{ - NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; - ClientPtr pClient = clientinfo->client; - struct xwl_auth_state *state; - - switch (pClient->clientState) { - case ClientStateGone: - case ClientStateRetained: - state = dixLookupPrivate(&pClient->devPrivates, &xwl_auth_state_private_key); - free_xwl_auth_state(pClient, state); - break; - default: - break; - } -} - -static void -sync_callback(void *data, struct wl_callback *callback, uint32_t serial) -{ - struct xwl_auth_state *state = data; - ClientPtr client = state->client; - - /* if the client is gone, the callback is cancelled so it's safe to - * assume the client is still in ClientStateRunning at this point... - */ - dri3_send_open_reply(client, state->fd); - AttendClient(client); - free_xwl_auth_state(client, state); -} - -static const struct wl_callback_listener sync_listener = { - sync_callback -}; - -static int -xwl_dri3_open_client(ClientPtr client, - ScreenPtr screen, - RRProviderPtr provider, - int *pfd) -{ - struct xwl_screen *xwl_screen = xwl_screen_get(screen); - struct xwl_auth_state *state; - drm_magic_t magic; - int fd; - - fd = open(xwl_screen->device_name, O_RDWR | O_CLOEXEC); - if (fd < 0) - return BadAlloc; - if (xwl_screen->fd_render_node) { - *pfd = fd; - return Success; - } - - state = malloc(sizeof *state); - if (state == NULL) { - close(fd); - return BadAlloc; - } - - state->client = client; - state->fd = fd; - - if (drmGetMagic(state->fd, &magic) < 0) { - close(state->fd); - free(state); - return BadMatch; - } - - wl_drm_authenticate(xwl_screen->drm, magic); - state->callback = wl_display_sync(xwl_screen->display); - wl_callback_add_listener(state->callback, &sync_listener, state); - dixSetPrivate(&client->devPrivates, &xwl_auth_state_private_key, state); - - IgnoreClient(client); - - return Success; -} - -static PixmapPtr -xwl_dri3_pixmap_from_fd(ScreenPtr screen, int fd, - CARD16 width, CARD16 height, CARD16 stride, - CARD8 depth, CARD8 bpp) -{ - struct xwl_screen *xwl_screen = xwl_screen_get(screen); - struct gbm_import_fd_data data; - struct gbm_bo *bo; - PixmapPtr pixmap; - - if (width == 0 || height == 0 || - depth < 15 || bpp != BitsPerPixel(depth) || stride < width * bpp / 8) - return NULL; - - data.fd = fd; - data.width = width; - data.height = height; - data.stride = stride; - data.format = gbm_format_for_depth(depth); - bo = gbm_bo_import(xwl_screen->gbm, GBM_BO_IMPORT_FD, &data, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (bo == NULL) - return NULL; - - pixmap = xwl_glamor_create_pixmap_for_bo(screen, bo, depth); - if (pixmap == NULL) { - gbm_bo_destroy(bo); - return NULL; - } - - return pixmap; -} - -static int -xwl_dri3_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, - CARD16 *stride, CARD32 *size) -{ - struct xwl_pixmap *xwl_pixmap; - - xwl_pixmap = xwl_pixmap_get(pixmap); - - *stride = gbm_bo_get_stride(xwl_pixmap->bo); - *size = pixmap->drawable.width * *stride; - - return gbm_bo_get_fd(xwl_pixmap->bo); -} - -static dri3_screen_info_rec xwl_dri3_info = { - .version = 1, - .open = NULL, - .pixmap_from_fd = xwl_dri3_pixmap_from_fd, - .fd_from_pixmap = xwl_dri3_fd_from_pixmap, - .open_client = xwl_dri3_open_client, -}; - Bool xwl_glamor_init(struct xwl_screen *xwl_screen) { @@ -585,8 +129,8 @@ xwl_glamor_init(struct xwl_screen *xwl_screen) return FALSE; } - if (xwl_screen->egl_context == EGL_NO_CONTEXT) { - ErrorF("Disabling glamor and dri3, EGL setup failed\n"); + if (!xwl_screen->egl_backend.init_egl(xwl_screen)) { + ErrorF("EGL setup failed, disabling glamor\n"); return FALSE; } @@ -595,25 +139,13 @@ xwl_glamor_init(struct xwl_screen *xwl_screen) return FALSE; } - if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) { - ErrorF("Failed to initialize dri3\n"); - return FALSE; - } - - if (!dixRegisterPrivateKey(&xwl_auth_state_private_key, PRIVATE_CLIENT, 0)) { - ErrorF("Failed to register private key\n"); - return FALSE; - } - - if (!AddCallback(&ClientStateCallback, xwl_auth_state_client_callback, NULL)) { - ErrorF("Failed to add client state callback\n"); + if (!xwl_screen->egl_backend.init_screen(xwl_screen)) { + ErrorF("EGL backend init_screen() failed, disabling glamor\n"); return FALSE; } xwl_screen->CreateScreenResources = screen->CreateScreenResources; screen->CreateScreenResources = xwl_glamor_create_screen_resources; - screen->CreatePixmap = xwl_glamor_create_pixmap; - screen->DestroyPixmap = xwl_glamor_destroy_pixmap; #ifdef XV if (!xwl_glamor_xv_init(screen)) diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c index 9b1d85674..979b72918 100644 --- a/hw/xwayland/xwayland.c +++ b/hw/xwayland/xwayland.c @@ -726,9 +726,9 @@ registry_global(void *data, struct wl_registry *registry, uint32_t id, xwl_screen_init_xdg_output(xwl_screen); } #ifdef GLAMOR_HAS_GBM - else if (xwl_screen->glamor && - strcmp(interface, "wl_drm") == 0 && version >= 2) { - xwl_screen_init_glamor(xwl_screen, id, version); + else if (xwl_screen->glamor) { + xwl_glamor_init_wl_registry(xwl_screen, registry, id, interface, + version); } #endif } @@ -937,6 +937,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv) } } + if (xwl_screen->glamor) { + if (!xwl_glamor_init_gbm(xwl_screen)) { + ErrorF("xwayland glamor: failed to setup GBM backend, falling back to sw accel\n"); + xwl_screen->glamor = 0; + } + } + /* In rootless mode, we don't have any screen storage, and the only * rendering should be to redirected mode. */ if (xwl_screen->rootless) diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h index ffa0d7297..88e9ec832 100644 --- a/hw/xwayland/xwayland.h +++ b/hw/xwayland/xwayland.h @@ -48,6 +48,9 @@ #include "xwayland-keyboard-grab-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" +struct xwl_pixmap; +struct xwl_window; + struct xwl_screen { int width; int height; @@ -93,14 +96,41 @@ struct xwl_screen { int prepare_read; int wait_flush; - char *device_name; - int drm_fd; - int fd_render_node; - struct wl_drm *drm; uint32_t formats; - uint32_t capabilities; void *egl_display, *egl_context; - struct gbm_device *gbm; + + /* the current backend for creating pixmaps on wayland */ + struct { + /* Called once for each interface in the global registry. Backends + * should use this to bind to any wayland interfaces they need. This + * callback is optional. + */ + void (*init_wl_registry)(struct xwl_screen *xwl_screen, + struct wl_registry *wl_registry, + const char *name, uint32_t id, + uint32_t version); + + /* Called before glamor has been initialized. Backends should setup a + * valid, glamor compatible EGL context in this hook. + */ + Bool (*init_egl)(struct xwl_screen *xwl_screen); + + /* Called after glamor has been initialized, and after all of the + * common Xwayland DDX hooks have been connected. Backends should use + * this to setup any required wraps around X server callbacks like + * CreatePixmap. + */ + Bool (*init_screen)(struct xwl_screen *xwl_screen); + + /* Called by Xwayland to retrieve a pointer to a valid wl_buffer for + * the given window/pixmap combo so that damage to the pixmap may be + * displayed on-screen. Backends should use this to create a new + * wl_buffer for a currently buffer-less pixmap, or simply return the + * pixmap they've prepared beforehand. + */ + struct wl_buffer *(*get_wl_buffer_for_pixmap)(PixmapPtr pixmap); + } egl_backend; + struct glamor_context *glamor_ctx; Atom allow_commits_prop; @@ -272,8 +302,6 @@ struct xwl_output { Bool xdg_output_done; }; -struct xwl_pixmap; - void xwl_sync_events (struct xwl_screen *xwl_screen); Bool xwl_screen_init_cursor(struct xwl_screen *xwl_screen); @@ -326,9 +354,11 @@ struct wl_buffer *xwl_shm_pixmap_get_wl_buffer(PixmapPtr pixmap); Bool xwl_glamor_init(struct xwl_screen *xwl_screen); -Bool xwl_screen_init_glamor(struct xwl_screen *xwl_screen, - uint32_t id, uint32_t version); struct wl_buffer *xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap); +void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen, + struct wl_registry *registry, + uint32_t id, const char *interface, + uint32_t version); void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen); @@ -344,4 +374,8 @@ Bool xwl_glamor_xv_init(ScreenPtr pScreen); void xwlVidModeExtensionInit(void); #endif +#ifdef GLAMOR_HAS_GBM +Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen); +#endif + #endif -- 2.14.3 _______________________________________________ xorg-devel@lists.x.org: X.Org development Archives: http://lists.x.org/archives/xorg-devel Info: https://lists.x.org/mailman/listinfo/xorg-devel