mirror of https://gitlab.freedesktop.org/mesa/mesa
473 lines
15 KiB
C
473 lines
15 KiB
C
|
/*
|
||
|
* 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 "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 int
|
||
|
egl_dri3_get_swap_interval(struct loader_dri3_drawable *draw)
|
||
|
{
|
||
|
struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
|
||
|
|
||
|
return dri3_surf->base.SwapInterval;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
egl_dri3_clamp_swap_interval(struct loader_dri3_drawable *draw, int interval)
|
||
|
{
|
||
|
struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
|
||
|
|
||
|
if (interval > dri3_surf->base.Config->MaxSwapInterval)
|
||
|
interval = dri3_surf->base.Config->MaxSwapInterval;
|
||
|
else if (interval < dri3_surf->base.Config->MinSwapInterval)
|
||
|
interval = dri3_surf->base.Config->MinSwapInterval;
|
||
|
|
||
|
return interval;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
egl_dri3_set_swap_interval(struct loader_dri3_drawable *draw, int interval)
|
||
|
{
|
||
|
struct dri3_egl_surface *dri3_surf = loader_drawable_to_egl_surface(draw);
|
||
|
|
||
|
dri3_surf->base.SwapInterval = interval;
|
||
|
}
|
||
|
|
||
|
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->base.Width = width;
|
||
|
dri3_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->base.Resource.Display;
|
||
|
}
|
||
|
|
||
|
static __DRIcontext *
|
||
|
egl_dri3_get_dri_context(struct loader_dri3_drawable *draw)
|
||
|
{
|
||
|
_EGLContext *ctx = _eglGetCurrentContext();
|
||
|
struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
|
||
|
|
||
|
return dri2_ctx->dri_context;
|
||
|
}
|
||
|
|
||
|
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->base.Resource.Display;
|
||
|
|
||
|
dri2_flush_drawable_for_swapbuffers(disp, &dri3_surf->base);
|
||
|
}
|
||
|
|
||
|
static struct loader_dri3_vtable egl_dri3_vtable = {
|
||
|
.get_swap_interval = egl_dri3_get_swap_interval,
|
||
|
.clamp_swap_interval = egl_dri3_clamp_swap_interval,
|
||
|
.set_swap_interval = egl_dri3_set_swap_interval,
|
||
|
.set_drawable_size = egl_dri3_set_drawable_size,
|
||
|
.in_current_context = egl_dri3_in_current_context,
|
||
|
.get_dri_context = egl_dri3_get_dri_context,
|
||
|
.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;
|
||
|
|
||
|
if (!_eglPutSurface(surf))
|
||
|
return EGL_TRUE;
|
||
|
|
||
|
loader_dri3_drawable_fini(&dri3_surf->loader_drawable);
|
||
|
|
||
|
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);
|
||
|
|
||
|
loader_dri3_set_swap_interval(&dri3_surf->loader_drawable, interval);
|
||
|
|
||
|
return EGL_TRUE;
|
||
|
}
|
||
|
|
||
|
static xcb_screen_t *
|
||
|
get_xcb_screen(xcb_screen_iterator_t iter, int screen)
|
||
|
{
|
||
|
for (; iter.rem; --screen, xcb_screen_next(&iter))
|
||
|
if (screen == 0)
|
||
|
return iter.data;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
xcb_screen_iterator_t s;
|
||
|
xcb_screen_t *screen;
|
||
|
|
||
|
STATIC_ASSERT(sizeof(uintptr_t) == sizeof(native_surface));
|
||
|
drawable = (uintptr_t) native_surface;
|
||
|
|
||
|
(void) drv;
|
||
|
|
||
|
dri3_surf = calloc(1, sizeof *dri3_surf);
|
||
|
if (!dri3_surf) {
|
||
|
_eglError(EGL_BAD_ALLOC, "dri3_create_surface");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (!_eglInitSurface(&dri3_surf->base, disp, type, conf, attrib_list))
|
||
|
goto cleanup_surf;
|
||
|
|
||
|
if (type == EGL_PBUFFER_BIT) {
|
||
|
s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
|
||
|
screen = get_xcb_screen(s, dri2_dpy->screen);
|
||
|
if (!screen) {
|
||
|
_eglError(EGL_BAD_NATIVE_WINDOW, "dri3_create_surface");
|
||
|
goto cleanup_surf;
|
||
|
}
|
||
|
|
||
|
drawable = xcb_generate_id(dri2_dpy->conn);
|
||
|
xcb_create_pixmap(dri2_dpy->conn, conf->BufferSize,
|
||
|
drawable, screen->root,
|
||
|
dri3_surf->base.Width, dri3_surf->base.Height);
|
||
|
}
|
||
|
|
||
|
dri_config = dri2_get_dri_config(dri2_conf, type,
|
||
|
dri3_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->base;
|
||
|
|
||
|
cleanup_pixmap:
|
||
|
if (type == EGL_PBUFFER_BIT)
|
||
|
xcb_free_pixmap(dri2_dpy->conn, drawable);
|
||
|
cleanup_surf:
|
||
|
free(dri3_surf);
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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,
|
||
|
XCB_WINDOW_NONE, 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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 0;
|
||
|
|
||
|
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 __DRIdrawable *
|
||
|
dri3_get_dri_drawable(_EGLSurface *surf)
|
||
|
{
|
||
|
struct dri3_egl_surface *dri3_surf = dri3_egl_surface(surf);
|
||
|
|
||
|
return dri3_surf->loader_drawable.dri_drawable;
|
||
|
}
|
||
|
|
||
|
struct dri2_egl_display_vtbl dri3_x11_display_vtbl = {
|
||
|
.authenticate = NULL,
|
||
|
.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 = dri2_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,
|
||
|
.post_sub_buffer = dri2_fallback_post_sub_buffer,
|
||
|
.copy_buffers = dri3_copy_buffers,
|
||
|
.query_buffer_age = dri3_query_buffer_age,
|
||
|
.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,
|
||
|
};
|
||
|
|
||
|
static char *
|
||
|
dri3_get_device_name(int fd)
|
||
|
{
|
||
|
char *ret = NULL;
|
||
|
|
||
|
ret = drmGetRenderDeviceNameFromFd(fd);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
/* For dri3, render node support is required for WL_bind_wayland_display.
|
||
|
* In order not to regress on older systems without kernel or libdrm
|
||
|
* support, fall back to dri2. User can override it with environment
|
||
|
* variable if they don't need to use that extension.
|
||
|
*/
|
||
|
if (getenv("EGL_FORCE_DRI3") == NULL) {
|
||
|
_eglLog(_EGL_WARNING, "Render node support not available, falling back to dri2");
|
||
|
_eglLog(_EGL_WARNING, "If you want to force dri3, set EGL_FORCE_DRI3 environment variable");
|
||
|
} else
|
||
|
ret = loader_get_device_name_for_fd(fd);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
xcb_screen_iterator_t s;
|
||
|
xcb_screen_t *screen;
|
||
|
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, "DRI2: failed to query dri3 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, "DRI2: failed to query Present version");
|
||
|
free(present_query);
|
||
|
free(error);
|
||
|
return EGL_FALSE;
|
||
|
}
|
||
|
free(present_query);
|
||
|
|
||
|
s = xcb_setup_roots_iterator(xcb_get_setup(dri2_dpy->conn));
|
||
|
screen = get_xcb_screen(s, dri2_dpy->screen);
|
||
|
if (!screen) {
|
||
|
_eglError(EGL_BAD_NATIVE_WINDOW, "dri3_x11_connect");
|
||
|
return EGL_FALSE;
|
||
|
}
|
||
|
|
||
|
dri2_dpy->fd = loader_dri3_open(dri2_dpy->conn, screen->root, 0);
|
||
|
if (dri2_dpy->fd < 0) {
|
||
|
int conn_error = xcb_connection_has_error(dri2_dpy->conn);
|
||
|
_eglLog(_EGL_WARNING, "DRI2: Screen seem not DRI3 capable");
|
||
|
|
||
|
if (conn_error)
|
||
|
_eglLog(_EGL_WARNING, "DRI2: Failed to initialize DRI3");
|
||
|
|
||
|
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, 0);
|
||
|
if (!dri2_dpy->driver_name) {
|
||
|
_eglLog(_EGL_WARNING, "DRI2: No driver found");
|
||
|
close(dri2_dpy->fd);
|
||
|
return EGL_FALSE;
|
||
|
}
|
||
|
|
||
|
dri2_dpy->device_name = dri3_get_device_name(dri2_dpy->fd);
|
||
|
if (!dri2_dpy->device_name) {
|
||
|
close(dri2_dpy->fd);
|
||
|
return EGL_FALSE;
|
||
|
}
|
||
|
|
||
|
return EGL_TRUE;
|
||
|
}
|