aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/hw/xwayland/xwayland-glamor.c
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/hw/xwayland/xwayland-glamor.c')
-rw-r--r--xorg-server/hw/xwayland/xwayland-glamor.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/xorg-server/hw/xwayland/xwayland-glamor.c b/xorg-server/hw/xwayland/xwayland-glamor.c
new file mode 100644
index 000000000..4be883fa3
--- /dev/null
+++ b/xorg-server/hw/xwayland/xwayland-glamor.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright © 2011-2014 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "xwayland.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <xf86drm.h>
+
+#define MESA_EGL_NO_X11_HEADERS
+#include <gbm.h>
+#include <epoxy/egl.h>
+#include <epoxy/gl.h>
+
+#include <glamor.h>
+#include <glamor_context.h>
+#include <dri3.h>
+#include "drm-client-protocol.h"
+
+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)
+{
+ eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ if (!eglMakeCurrent(glamor_ctx->display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
+ glamor_ctx->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)
+{
+ struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+
+ glamor_ctx->ctx = xwl_screen->egl_context;
+ glamor_ctx->display = xwl_screen->egl_display;
+
+ glamor_ctx->make_current = xwl_glamor_egl_make_current;
+
+ 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);
+ 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;
+}
+
+static PixmapPtr
+xwl_glamor_create_pixmap(ScreenPtr screen,
+ int width, int height, int depth, unsigned int hint)
+{
+ 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);
+}
+
+static Bool
+xwl_glamor_create_screen_resources(ScreenPtr screen)
+{
+ struct xwl_screen *xwl_screen = xwl_screen_get(screen);
+ int ret;
+
+ screen->CreateScreenResources = xwl_screen->CreateScreenResources;
+ ret = (*screen->CreateScreenResources) (screen);
+ xwl_screen->CreateScreenResources = screen->CreateScreenResources;
+ screen->CreateScreenResources = xwl_glamor_create_screen_resources;
+
+ if (!ret)
+ return ret;
+
+ if (xwl_screen->rootless)
+ screen->devPrivate =
+ 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);
+ if (screen->devPrivate)
+ glamor_set_screen_pixmap(screen->devPrivate, NULL);
+ }
+
+ 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;
+ const char *version;
+
+ 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 = eglGetDisplay(xwl_screen->gbm);
+ if (xwl_screen->egl_display == EGL_NO_DISPLAY) {
+ ErrorF("eglGetDisplay() failed\n");
+ return;
+ }
+
+ eglBindAPI(EGL_OPENGL_API);
+ if (!eglInitialize(xwl_screen->egl_display, &major, &minor)) {
+ ErrorF("eglInitialize() failed\n");
+ return;
+ }
+
+ version = eglQueryString(xwl_screen->egl_display, EGL_VERSION);
+ ErrorF("glamor: EGL version %s:\n", version);
+
+ 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 no available");
+ 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)",
+ 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;
+}
+
+void
+glamor_egl_destroy_textured_pixmap(PixmapPtr pixmap)
+{
+ glamor_destroy_textured_pixmap(pixmap);
+}
+
+int
+glamor_egl_dri3_fd_name_from_tex(ScreenPtr screen,
+ PixmapPtr pixmap,
+ unsigned int tex,
+ Bool want_name, CARD16 *stride, CARD32 *size)
+{
+ return 0;
+}
+
+unsigned int
+glamor_egl_create_argb8888_based_texture(ScreenPtr screen, int w, int h)
+{
+ return 0;
+}
+
+struct xwl_auth_state {
+ int fd;
+ ClientPtr client;
+};
+
+static void
+sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
+{
+ struct xwl_auth_state *state = data;
+
+ dri3_send_open_reply(state->client, state->fd);
+ AttendClient(state->client);
+ free(state);
+ wl_callback_destroy(callback);
+}
+
+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;
+ struct wl_callback *callback;
+ 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);
+ callback = wl_display_sync(xwl_screen->display);
+ wl_callback_add_listener(callback, &sync_listener, 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)
+{
+ ScreenPtr screen = xwl_screen->screen;
+
+ if (xwl_screen->egl_context == EGL_NO_CONTEXT) {
+ ErrorF("Disabling glamor and dri3, EGL setup failed\n");
+ return FALSE;
+ }
+
+ if (!glamor_init(xwl_screen->screen,
+ GLAMOR_INVERTED_Y_AXIS |
+ GLAMOR_USE_EGL_SCREEN |
+ GLAMOR_USE_SCREEN |
+ GLAMOR_USE_PICTURE_SCREEN)) {
+ ErrorF("Failed to initialize glamor\n");
+ return FALSE;
+ }
+
+ if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
+ ErrorF("Failed to initialize dri3\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;
+
+ return TRUE;
+}