diff options
Diffstat (limited to 'xserver/hw/xwayland/xwayland-glamor-gbm.c')
-rw-r--r-- | xserver/hw/xwayland/xwayland-glamor-gbm.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/xserver/hw/xwayland/xwayland-glamor-gbm.c b/xserver/hw/xwayland/xwayland-glamor-gbm.c new file mode 100644 index 000000000..a211e0915 --- /dev/null +++ b/xserver/hw/xwayland/xwayland-glamor-gbm.c @@ -0,0 +1,1035 @@ +/* + * 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 <lyude@redhat.com> + * + */ + +#include "xwayland.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <xf86drm.h> +#include <drm_fourcc.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 { + char *device_name; + struct gbm_device *gbm; + struct wl_drm *drm; + struct zwp_linux_dmabuf_v1 *dmabuf; + int drm_fd; + int fd_render_node; + Bool drm_authenticated; + uint32_t capabilities; + int dmabuf_capable; +}; + +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; + case 30: + return GBM_FORMAT_ARGB2101010; + default: + ErrorF("unexpected depth: %d\n", depth); + case 32: + return GBM_FORMAT_ARGB8888; + } +} + +static uint32_t +wl_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; + case 30: + return WL_DRM_FORMAT_ARGB2101010; + 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 char +is_device_path_render_node (const char *device_path) +{ + char is_render_node; + int fd; + + fd = open(device_path, O_RDWR | O_CLOEXEC); + if (fd < 0) + return 0; + + is_render_node = is_fd_render_node(fd); + close(fd); + + return is_render_node; +} + +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; + } + + xwl_glamor_egl_make_current(xwl_screen); + 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)) { + uint32_t format = gbm_format_for_depth(depth); + +#ifdef GBM_BO_WITH_MODIFIERS + if (xwl_gbm->dmabuf_capable) { + uint32_t num_modifiers; + uint64_t *modifiers = NULL; + + glamor_get_modifiers(screen, format, &num_modifiers, &modifiers); + bo = gbm_bo_create_with_modifiers(xwl_gbm->gbm, width, height, + format, modifiers, num_modifiers); + free(modifiers); + } + else +#endif + { + bo = gbm_bo_create(xwl_gbm->gbm, width, height, format, + 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); + if (xwl_pixmap->bo) + 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, + Bool *created) +{ + 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); + unsigned short width = pixmap->drawable.width; + unsigned short height = pixmap->drawable.height; + int prime_fd; + int num_planes; + uint32_t strides[4]; + uint32_t offsets[4]; + uint64_t modifier; + int i; + + if (xwl_pixmap == NULL) + return NULL; + + if (xwl_pixmap->buffer) { + /* Buffer already exists. Return it and inform caller if interested. */ + if (created) + *created = FALSE; + return xwl_pixmap->buffer; + } + + /* Buffer does not exist yet. Create now and inform caller if interested. */ + if (created) + *created = TRUE; + + if (!xwl_pixmap->bo) + return NULL; + + prime_fd = gbm_bo_get_fd(xwl_pixmap->bo); + if (prime_fd == -1) + return NULL; + +#ifdef GBM_BO_WITH_MODIFIERS + num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo); + modifier = gbm_bo_get_modifier(xwl_pixmap->bo); + for (i = 0; i < num_planes; i++) { + strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i); + offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i); + } +#else + num_planes = 1; + modifier = DRM_FORMAT_MOD_INVALID; + strides[0] = gbm_bo_get_stride(xwl_pixmap->bo); + offsets[0] = 0; +#endif + + if (xwl_gbm->dmabuf && modifier != DRM_FORMAT_MOD_INVALID) { + struct zwp_linux_buffer_params_v1 *params; + + params = zwp_linux_dmabuf_v1_create_params(xwl_gbm->dmabuf); + for (i = 0; i < num_planes; i++) { + zwp_linux_buffer_params_v1_add(params, prime_fd, i, + offsets[i], strides[i], + modifier >> 32, modifier & 0xffffffff); + } + + xwl_pixmap->buffer = + zwp_linux_buffer_params_v1_create_immed(params, width, height, + wl_drm_format_for_depth(pixmap->drawable.depth), + 0); + zwp_linux_buffer_params_v1_destroy(params); + } else if (num_planes == 1) { + xwl_pixmap->buffer = + wl_drm_create_prime_buffer(xwl_gbm->drm, prime_fd, width, height, + wl_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; +} + +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; +} + +_X_EXPORT PixmapPtr +glamor_pixmap_from_fds(ScreenPtr screen, CARD8 num_fds, const int *fds, + CARD16 width, CARD16 height, + const CARD32 *strides, const CARD32 *offsets, + CARD8 depth, CARD8 bpp, uint64_t modifier) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct gbm_bo *bo = NULL; + PixmapPtr pixmap; + int i; + + if (width == 0 || height == 0 || num_fds == 0 || + depth < 15 || bpp != BitsPerPixel(depth) || + strides[0] < width * bpp / 8) + goto error; + + if (xwl_gbm->dmabuf_capable && modifier != DRM_FORMAT_MOD_INVALID) { +#ifdef GBM_BO_WITH_MODIFIERS + struct gbm_import_fd_modifier_data data; + + data.width = width; + data.height = height; + data.num_fds = num_fds; + data.format = gbm_format_for_depth(depth); + data.modifier = modifier; + for (i = 0; i < num_fds; i++) { + data.fds[i] = fds[i]; + data.strides[i] = strides[i]; + data.offsets[i] = offsets[i]; + } + bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD_MODIFIER, &data, 0); +#endif + } else if (num_fds == 1) { + struct gbm_import_fd_data data; + + data.fd = fds[0]; + data.width = width; + data.height = height; + data.stride = strides[0]; + 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); + } else { + goto error; + } + + if (bo == NULL) + goto error; + + pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth); + if (pixmap == NULL) { + gbm_bo_destroy(bo); + goto error; + } + + return pixmap; + +error: + return NULL; +} + +_X_EXPORT int +glamor_egl_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds, + uint32_t *strides, uint32_t *offsets, + uint64_t *modifier) +{ + struct xwl_pixmap *xwl_pixmap; +#ifdef GBM_BO_WITH_MODIFIERS + uint32_t num_fds; + int i; +#endif + + xwl_pixmap = xwl_pixmap_get(pixmap); + + if (xwl_pixmap == NULL) + return 0; + + if (!xwl_pixmap->bo) + return 0; + +#ifdef GBM_BO_WITH_MODIFIERS + num_fds = gbm_bo_get_plane_count(xwl_pixmap->bo); + *modifier = gbm_bo_get_modifier(xwl_pixmap->bo); + + for (i = 0; i < num_fds; i++) { + fds[i] = gbm_bo_get_fd(xwl_pixmap->bo); + strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i); + offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i); + } + + return num_fds; +#else + *modifier = DRM_FORMAT_MOD_INVALID; + fds[0] = gbm_bo_get_fd(xwl_pixmap->bo); + strides[0] = gbm_bo_get_stride(xwl_pixmap->bo); + offsets[0] = 0; + return 1; +#endif +} + +/* Not actually used, just defined here so there's something for + * _glamor_egl_fds_from_pixmap() to link against + */ +_X_EXPORT int +glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, + CARD16 *stride, CARD32 *size) +{ + return -1; +} + +_X_EXPORT Bool +glamor_get_formats(ScreenPtr screen, + CARD32 *num_formats, CARD32 **formats) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + int i; + + /* Explicitly zero the count as the caller may ignore the return value */ + *num_formats = 0; + + if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf) + return FALSE; + + if (xwl_screen->num_formats == 0) + return TRUE; + + *formats = calloc(xwl_screen->num_formats, sizeof(CARD32)); + if (*formats == NULL) + return FALSE; + + for (i = 0; i < xwl_screen->num_formats; i++) + (*formats)[i] = xwl_screen->formats[i].format; + *num_formats = xwl_screen->num_formats; + + return TRUE; +} + +_X_EXPORT Bool +glamor_get_modifiers(ScreenPtr screen, uint32_t format, + uint32_t *num_modifiers, uint64_t **modifiers) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(screen); + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + struct xwl_format *xwl_format = NULL; + int i; + + /* Explicitly zero the count as the caller may ignore the return value */ + *num_modifiers = 0; + + if (!xwl_gbm->dmabuf_capable || !xwl_gbm->dmabuf) + return FALSE; + + if (xwl_screen->num_formats == 0) + return TRUE; + + for (i = 0; i < xwl_screen->num_formats; i++) { + if (xwl_screen->formats[i].format == format) { + xwl_format = &xwl_screen->formats[i]; + break; + } + } + + if (!xwl_format) + return FALSE; + + *modifiers = calloc(xwl_format->num_modifiers, sizeof(uint64_t)); + if (*modifiers == NULL) + return FALSE; + + for (i = 0; i < xwl_format->num_modifiers; i++) + (*modifiers)[i] = xwl_format->modifiers[i]; + *num_modifiers = xwl_format->num_modifiers; + + return TRUE; +} + +static const dri3_screen_info_rec xwl_dri3_info = { + .version = 2, + .open = NULL, + .pixmap_from_fds = glamor_pixmap_from_fds, + .fds_from_pixmap = glamor_fds_from_pixmap, + .open_client = xwl_dri3_open_client, + .get_formats = glamor_get_formats, + .get_modifiers = glamor_get_modifiers, + .get_drawable_modifiers = glamor_get_drawable_modifiers, +}; + +static const char * +get_render_node_path_for_device(const drmDevicePtr drm_device, + const char *device_path) +{ + char *render_node_path = NULL; + char device_found = 0; + int i; + + for (i = 0; i < DRM_NODE_MAX; i++) { + if ((drm_device->available_nodes & (1 << i)) == 0) + continue; + + if (!strcmp (device_path, drm_device->nodes[i])) + device_found = 1; + + if (is_device_path_render_node(drm_device->nodes[i])) + render_node_path = drm_device->nodes[i]; + + if (device_found && render_node_path) + return render_node_path; + } + + return NULL; +} + +static char * +get_render_node_path(const char *device_path) +{ + drmDevicePtr *devices = NULL; + char *render_node_path = NULL; + int i, n_devices, max_devices; + + max_devices = drmGetDevices2(0, NULL, 0); + if (max_devices <= 0) + goto out; + + devices = calloc(max_devices, sizeof(drmDevicePtr)); + if (!devices) + goto out; + + n_devices = drmGetDevices2(0, devices, max_devices); + if (n_devices < 0) + goto out; + + for (i = 0; i < n_devices; i++) { + const char *node_path = get_render_node_path_for_device(devices[i], + device_path); + if (node_path) { + render_node_path = strdup(node_path); + break; + } + } + +out: + free(devices); + return render_node_path; +} + +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; + char *render_node_path = NULL; + + if (!is_device_path_render_node(device)) + render_node_path = get_render_node_path(device); + + if (render_node_path) + xwl_gbm->device_name = render_node_path; + else + 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) +{ +} + +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_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, + uint32_t format) +{ +} + +static void +xwl_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, + uint32_t format, uint32_t modifier_hi, + uint32_t modifier_lo) +{ + struct xwl_screen *xwl_screen = data; + struct xwl_format *xwl_format = NULL; + int i; + + for (i = 0; i < xwl_screen->num_formats; i++) { + if (xwl_screen->formats[i].format == format) { + xwl_format = &xwl_screen->formats[i]; + break; + } + } + + if (xwl_format == NULL) { + xwl_screen->num_formats++; + xwl_screen->formats = realloc(xwl_screen->formats, + xwl_screen->num_formats * sizeof(*xwl_format)); + if (!xwl_screen->formats) + return; + xwl_format = &xwl_screen->formats[xwl_screen->num_formats - 1]; + xwl_format->format = format; + xwl_format->num_modifiers = 0; + xwl_format->modifiers = NULL; + } + + xwl_format->num_modifiers++; + xwl_format->modifiers = realloc(xwl_format->modifiers, + xwl_format->num_modifiers * sizeof(uint64_t)); + if (!xwl_format->modifiers) + return; + xwl_format->modifiers[xwl_format->num_modifiers - 1] = (uint64_t) modifier_lo; + xwl_format->modifiers[xwl_format->num_modifiers - 1] |= (uint64_t) modifier_hi << 32; +} + +static const struct zwp_linux_dmabuf_v1_listener xwl_dmabuf_listener = { + .format = xwl_dmabuf_handle_format, + .modifier = xwl_dmabuf_handle_modifier +}; + +Bool +xwl_screen_set_drm_interface(struct xwl_screen *xwl_screen, + uint32_t id, uint32_t version) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (version < 2) + return FALSE; + + 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++; + + return TRUE; +} + +Bool +xwl_screen_set_dmabuf_interface(struct xwl_screen *xwl_screen, + uint32_t id, uint32_t version) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (version < 3) + return FALSE; + + xwl_gbm->dmabuf = + wl_registry_bind(xwl_screen->registry, id, &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(xwl_gbm->dmabuf, &xwl_dmabuf_listener, xwl_screen); + + return TRUE; +} + +static Bool +xwl_glamor_gbm_init_wl_registry(struct xwl_screen *xwl_screen, + struct wl_registry *wl_registry, + uint32_t id, const char *name, + uint32_t version) +{ + if (strcmp(name, "wl_drm") == 0) { + xwl_screen_set_drm_interface(xwl_screen, id, version); + return TRUE; + } else if (strcmp(name, "zwp_linux_dmabuf_v1") == 0) { + xwl_screen_set_dmabuf_interface(xwl_screen, id, version); + return TRUE; + } + + /* no match */ + return FALSE; +} + +static Bool +xwl_glamor_gbm_has_egl_extension(void) +{ + return (epoxy_has_egl_extension(NULL, "EGL_MESA_platform_gbm") || + epoxy_has_egl_extension(NULL, "EGL_KHR_platform_gbm")); +} + +static Bool +xwl_glamor_gbm_has_wl_interfaces(struct xwl_screen *xwl_screen) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (xwl_gbm->drm == NULL) { + ErrorF("glamor: 'wl_drm' not supported\n"); + return FALSE; + } + + return TRUE; +} + +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 + }; + const GLubyte *renderer; + + if (!xwl_gbm->fd_render_node && !xwl_gbm->drm_authenticated) { + ErrorF("Failed to get wl_drm, disabling Glamor and DRI3\n"); + return FALSE; + } + + 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; + } + + renderer = glGetString(GL_RENDERER); + if (!renderer) { + ErrorF("glGetString() returned NULL, your GL is broken\n"); + goto error; + } + if (strstr((const char *)renderer, "llvmpipe")) { + ErrorF("Refusing to try glamor on llvmpipe\n"); + goto error; + } + + if (!epoxy_has_gl_extension("GL_OES_EGL_image")) { + ErrorF("GL_OES_EGL_image not available\n"); + goto error; + } + + if (epoxy_has_egl_extension(xwl_screen->egl_display, + "EXT_image_dma_buf_import") && + epoxy_has_egl_extension(xwl_screen->egl_display, + "EXT_image_dma_buf_import_modifiers")) + xwl_gbm->dmabuf_capable = TRUE; + + 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) +{ + struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen); + + if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) { + ErrorF("Failed to initialize dri3\n"); + goto error; + } + + if (xwl_gbm->fd_render_node) + goto skip_drm_auth; + + 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; + } + +skip_drm_auth: + 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; +} + +void +xwl_glamor_init_gbm(struct xwl_screen *xwl_screen) +{ + struct xwl_gbm_private *xwl_gbm; + + xwl_screen->gbm_backend.is_available = FALSE; + + if (!xwl_glamor_gbm_has_egl_extension()) + return; + + if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0)) + return; + + xwl_gbm = calloc(sizeof(*xwl_gbm), 1); + if (!xwl_gbm) { + ErrorF("glamor: Not enough memory to setup GBM, disabling\n"); + return; + } + + dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key, + xwl_gbm); + + xwl_screen->gbm_backend.init_wl_registry = xwl_glamor_gbm_init_wl_registry; + xwl_screen->gbm_backend.has_wl_interfaces = xwl_glamor_gbm_has_wl_interfaces; + xwl_screen->gbm_backend.init_egl = xwl_glamor_gbm_init_egl; + xwl_screen->gbm_backend.init_screen = xwl_glamor_gbm_init_screen; + xwl_screen->gbm_backend.get_wl_buffer_for_pixmap = xwl_glamor_gbm_get_wl_buffer_for_pixmap; + xwl_screen->gbm_backend.is_available = TRUE; +} |