/* * 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 #include #include #define MESA_EGL_NO_X11_HEADERS #include #include #include #include #include #include #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); 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; } 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 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; } void glamor_egl_destroy_pixmap_image(PixmapPtr 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_USE_EGL_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; }