/* * Copyright © 2015 Boyan Ding * * 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 <stdbool.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <xcb/xcb.h> #include <xcb/dri3.h> #include <xcb/present.h> #include <xf86drm.h> #include "util/macros.h" #include "egl_dri2.h" #include "egl_dri2_fallbacks.h" #include "platform_x11_dri3.h" #include "loader.h" #include "loader_dri3_helper.h" static struct dri3_egl_surface * loader_drawable_to_egl_surface(struct loader_dri3_drawable *draw) { size_t offset = offsetof(struct dri3_egl_surface, loader_drawable); return (struct dri3_egl_surface *)(((void*) draw) - offset); } static void egl_dri3_set_drawable_size(struct loader_dri3_drawable *draw, int width, int height) { struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); dri3_surf->surf.base.Width = width; dri3_surf->surf.base.Height = height; } static bool egl_dri3_in_current_context(struct loader_dri3_drawable *draw) { struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); _EGLContext *ctx = _eglGetCurrentContext(); return ctx->Resource.Display == dri3_surf->surf.base.Resource.Display; } static __DRIcontext * egl_dri3_get_dri_context(struct loader_dri3_drawable *draw) { _EGLContext *ctx = _eglGetCurrentContext(); struct dri2_egl_context *dri2_ctx; if (!ctx) return NULL; dri2_ctx = dri2_egl_context(ctx); return dri2_ctx->dri_context; } static __DRIscreen * egl_dri3_get_dri_screen(void) { _EGLContext *ctx = _eglGetCurrentContext(); struct dri2_egl_context *dri2_ctx; if (!ctx) return NULL; dri2_ctx = dri2_egl_context(ctx); return dri2_egl_display(dri2_ctx->base.Resource.Display)->dri_screen; } static void egl_dri3_flush_drawable(struct loader_dri3_drawable *draw, unsigned flags) { struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw); _EGLDisplay *disp = dri3_surf->surf.base.Resource.Display; dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->surf.base); } static const struct loader_dri3_vtable egl_dri3_vtable = { .set_drawable_size = egl_dri3_set_drawable_size, .in_current_context = egl_dri3_in_current_context, .get_dri_context = egl_dri3_get_dri_context, .get_dri_screen = egl_dri3_get_dri_screen, .flush_drawable = egl_dri3_flush_drawable, .show_fps = NULL, }; static EGLBoolean dri3_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); (void) drv; loader_dri3_drawable_fini(&dri3_surf->loader_drawable); dri2_fini_surface(surf); free(surf); return EGL_TRUE; } static EGLBoolean dri3_set_swap_interval(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, EGLint interval) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); dri3_surf->surf.base.SwapInterval = interval; loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval); return EGL_TRUE; } static _EGLSurface * dri3_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, _EGLConfig *conf, void *native_surface, const EGLint *attrib_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_config *dri2_conf = dri2_egl_config(conf); struct dri3_egl_surface *dri3_surf; const __DRIconfig *dri_config; xcb_drawable_t drawable; (void) drv; dri3_surf = calloc(1, sizeof *dri3_surf); if (!dri3_surf) { _eglError(EGL_BAD_ALLOC, "dri3_create_surface"); return NULL; } if (!dri2_init_surface(&dri3_surf->surf.base, disp, type, conf, attrib_list, false)) goto cleanup_surf; if (type == EGL_PBUFFER_BIT) { drawable = xcb_generate_id(dri2_dpy->conn); xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize, drawable, dri2_dpy->screen->root, dri3_surf->surf.base.Width, dri3_surf->surf.base.Height); } else { STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface)); drawable = (uintptr_t) native_surface; } dri_config = dri2_get_dri_config(dri2_conf, type, dri3_surf->surf.base.GLColorspace); if (loader_dri3_drawable_init(dri2_dpy->conn, drawable, dri2_dpy->dri_screen, dri2_dpy->is_different_gpu, dri_config, &dri2_dpy->loader_dri3_ext, &egl_dri3_vtable, &dri3_surf->loader_drawable)) { _eglError(EGL_BAD_ALLOC, "dri3_surface_create"); goto cleanup_pixmap; } return &dri3_surf->surf.base; cleanup_pixmap: if (type == EGL_PBUFFER_BIT) xcb_free_pixmap(dri2_dpy->conn, drawable); cleanup_surf: free(dri3_surf); return NULL; } static int dri3_authenticate(_EGLDisplay *disp, uint32_t id) { #ifdef HAVE_WAYLAND_PLATFORM struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); if (dri2_dpy->device_name) { _eglLog(_EGL_WARNING, "Wayland client render node authentication is unnecessary"); return 0; } _eglLog(_EGL_WARNING, "Wayland client primary node authentication isn't supported"); #endif return -1; } /** * Called via eglCreateWindowSurface(), drv->API.CreateWindowSurface(). */ static _EGLSurface * dri3_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, void *native_window, const EGLint *attrib_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); _EGLSurface *surf; surf = dri3_create_surface(drv, disp, EGL_WINDOW_BIT, conf, native_window, attrib_list); if (surf != NULL) dri3_set_swap_interval(drv, disp, surf, dri2_dpy->default_swap_interval); return surf; } static _EGLSurface * dri3_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, void *native_pixmap, const EGLint *attrib_list) { return dri3_create_surface(drv, disp, EGL_PIXMAP_BIT, conf, native_pixmap, attrib_list); } static _EGLSurface * dri3_create_pbuffer_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLConfig *conf, const EGLint *attrib_list) { return dri3_create_surface(drv, disp, EGL_PBUFFER_BIT, conf, NULL, attrib_list); } static EGLBoolean dri3_get_sync_values(_EGLDisplay *display, _EGLSurface *surface, EGLuint64KHR *ust, EGLuint64KHR *msc, EGLuint64KHR *sbc) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surface); return loader_dri3_wait_for_msc(&dri3_surf->loader_drawable, 0, 0, 0, (int64_t *) ust, (int64_t *) msc, (int64_t *) sbc) ? EGL_TRUE : EGL_FALSE; } static _EGLImage * dri3_create_image_khr_pixmap(_EGLDisplay *disp, _EGLContext *ctx, EGLClientBuffer buffer, const EGLint *attr_list) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_image *dri2_img; xcb_drawable_t drawable; xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie; xcb_dri3_buffer_from_pixmap_reply_t *bp_reply; unsigned int format; drawable = (xcb_drawable_t) (uintptr_t) buffer; bp_cookie = xcb_dri3_buffer_from_pixmap(dri2_dpy->conn, drawable); bp_reply = xcb_dri3_buffer_from_pixmap_reply(dri2_dpy->conn, bp_cookie, NULL); if (!bp_reply) { _eglError(EGL_BAD_ALLOC, "xcb_dri3_buffer_from_pixmap"); return NULL; } switch (bp_reply->depth) { case 16: format = __DRI_IMAGE_FORMAT_RGB565; break; case 24: format = __DRI_IMAGE_FORMAT_XRGB8888; break; case 30: format = __DRI_IMAGE_FORMAT_XRGB2101010; break; case 32: format = __DRI_IMAGE_FORMAT_ARGB8888; break; default: _eglError(EGL_BAD_PARAMETER, "dri3_create_image_khr: unsupported pixmap depth"); free(bp_reply); return EGL_NO_IMAGE_KHR; } dri2_img = malloc(sizeof *dri2_img); if (!dri2_img) { _eglError(EGL_BAD_ALLOC, "dri3_create_image_khr"); return EGL_NO_IMAGE_KHR; } _eglInitImage(&dri2_img->base, disp); dri2_img->dri_image = loader_dri3_create_image(dri2_dpy->conn, bp_reply, format, dri2_dpy->dri_screen, dri2_dpy->image, dri2_img); free(bp_reply); return &dri2_img->base; } static _EGLImage * dri3_create_image_khr(_EGLDriver *drv, _EGLDisplay *disp, _EGLContext *ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attr_list) { switch (target) { case EGL_NATIVE_PIXMAP_KHR: return dri3_create_image_khr_pixmap(disp, ctx, buffer, attr_list); default: return dri2_create_image_khr(drv, disp, ctx, target, buffer, attr_list); } } /** * Called by the driver when it needs to update the real front buffer with the * contents of its fake front buffer. */ static void dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate) { /* There does not seem to be any kind of consensus on whether we should * support front-buffer rendering or not: * http://lists.freedesktop.org/archives/mesa-dev/2013-June/040129.html */ _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering."); (void) driDrawable; (void) loaderPrivate; } const __DRIimageLoaderExtension dri3_image_loader_extension = { .base = { __DRI_IMAGE_LOADER, 1 }, .getBuffers = loader_dri3_get_buffers, .flushFrontBuffer = dri3_flush_front_buffer, }; static EGLBoolean dri3_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(draw); /* No-op for a pixmap or pbuffer surface */ if (draw->Type == EGL_PIXMAP_BIT || draw->Type == EGL_PBUFFER_BIT) return EGL_FALSE; return loader_dri3_swap_buffers_msc(&dri3_surf->loader_drawable, 0, 0, 0, 0, draw->SwapBehavior == EGL_BUFFER_PRESERVED) != -1; } static EGLBoolean dri3_copy_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf, void *native_pixmap_target) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); xcb_pixmap_t target; STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_pixmap_target)); target = (uintptr_t) native_pixmap_target; loader_dri3_copy_drawable(&dri3_surf->loader_drawable, target, dri3_surf->loader_drawable.drawable); return EGL_TRUE; } static int dri3_query_buffer_age(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); return loader_dri3_query_buffer_age(&dri3_surf->loader_drawable); } static EGLBoolean dri3_query_surface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surf, EGLint attribute, EGLint *value) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); switch (attribute) { case EGL_WIDTH: case EGL_HEIGHT: loader_dri3_update_drawable_geometry(&dri3_surf->loader_drawable); break; default: break; } return _eglQuerySurface(drv, dpy, surf, attribute, value); } static __DRIdrawable * dri3_get_dri_drawable(_EGLSurface *surf) { struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf); return dri3_surf->loader_drawable.dri_drawable; } static void dri3_close_screen_notify(_EGLDisplay *dpy) { struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy); loader_dri3_close_screen(dri2_dpy->dri_screen); } struct dri2_egl_display_vtbl dri3_x11_display_vtbl = { .authenticate = dri3_authenticate, .create_window_surface = dri3_create_window_surface, .create_pixmap_surface = dri3_create_pixmap_surface, .create_pbuffer_surface = dri3_create_pbuffer_surface, .destroy_surface = dri3_destroy_surface, .create_image = dri3_create_image_khr, .swap_interval = dri3_set_swap_interval, .swap_buffers = dri3_swap_buffers, .swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage, .swap_buffers_region = dri2_fallback_swap_buffers_region, .set_damage_region = dri2_fallback_set_damage_region, .post_sub_buffer = dri2_fallback_post_sub_buffer, .copy_buffers = dri3_copy_buffers, .query_buffer_age = dri3_query_buffer_age, .query_surface = dri3_query_surface, .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, .get_sync_values = dri3_get_sync_values, .get_dri_drawable = dri3_get_dri_drawable, .close_screen_notify = dri3_close_screen_notify, }; EGLBoolean dri3_x11_connect(struct dri2_egl_display *dri2_dpy) { xcb_dri3_query_version_reply_t *dri3_query; xcb_dri3_query_version_cookie_t dri3_query_cookie; xcb_present_query_version_reply_t *present_query; xcb_present_query_version_cookie_t present_query_cookie; xcb_generic_error_t *error; const xcb_query_extension_reply_t *extension; xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id); xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id); extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_dri3_id); if (!(extension && extension->present)) return EGL_FALSE; extension = xcb_get_extension_data(dri2_dpy->conn, &xcb_present_id); if (!(extension && extension->present)) return EGL_FALSE; dri3_query_cookie = xcb_dri3_query_version(dri2_dpy->conn, XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION); present_query_cookie = xcb_present_query_version(dri2_dpy->conn, XCB_PRESENT_MAJOR_VERSION, XCB_PRESENT_MINOR_VERSION); dri3_query = xcb_dri3_query_version_reply(dri2_dpy->conn, dri3_query_cookie, &error); if (dri3_query == NULL || error != NULL) { _eglLog(_EGL_WARNING, "DRI3: failed to query the version"); free(dri3_query); free(error); return EGL_FALSE; } free(dri3_query); present_query = xcb_present_query_version_reply(dri2_dpy->conn, present_query_cookie, &error); if (present_query == NULL || error != NULL) { _eglLog(_EGL_WARNING, "DRI3: failed to query Present version"); free(present_query); free(error); return EGL_FALSE; } free(present_query); dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, dri2_dpy->screen->root, 0); if (dri2_dpy->fd < 0) { int conn_error = xcb_connection_has_error(dri2_dpy->conn); _eglLog(_EGL_WARNING, "DRI3: Screen seems not DRI3 capable"); if (conn_error) _eglLog(_EGL_WARNING, "DRI3: Failed to initialize"); return EGL_FALSE; } dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu); dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd); if (!dri2_dpy->driver_name) { _eglLog(_EGL_WARNING, "DRI3: No driver found"); close(dri2_dpy->fd); return EGL_FALSE; } #ifdef HAVE_WAYLAND_PLATFORM /* Only try to get a render device name since dri3 doesn't provide a * mechanism for authenticating client opened device node fds. If this * fails then don't advertise the extension. */ dri2_dpy->device_name = drmGetRenderDeviceNameFromFd(dri2_dpy->fd); #endif return EGL_TRUE; }