mirror of
https://github.com/X11Libre/xserver.git
synced 2026-11-01 03:03:58 +00:00
The cleanup function for GBM is called on the various error paths. Once xwl_glamor_gbm_cleanup() has been called, GBM support is no longer usable (and the corresponding data structures are freed), so there is no way we can keep using GLAMOR after that point. Make sure to explicitly disable GLAMOR support in that case, so we do not crash later on trying to use GBM. Signed-off-by: Olivier Fourdan <ofourdan@redhat.com> Part-of: <https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1768>
1880 lines
58 KiB
C
1880 lines
58 KiB
C
/*
|
|
* 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-config.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#ifdef DRI3
|
|
#include <sys/eventfd.h>
|
|
#endif /* DRI3 */
|
|
#include <sys/stat.h>
|
|
#include <xf86drm.h>
|
|
#include <drm_fourcc.h>
|
|
#if defined(__linux__)
|
|
#include <linux/dma-buf.h>
|
|
#include <linux/sync_file.h>
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#define MESA_EGL_NO_X11_HEADERS
|
|
#define EGL_NO_X11
|
|
#include <gbm.h>
|
|
#include <glamor_egl.h>
|
|
|
|
#include <glamor.h>
|
|
#include <glamor_context.h>
|
|
#include <dri3.h>
|
|
#include "drm-client-protocol.h"
|
|
|
|
#include "xwayland-glamor.h"
|
|
#include "xwayland-glamor-gbm.h"
|
|
#include "xwayland-pixmap.h"
|
|
#include "xwayland-screen.h"
|
|
#include "xwayland-window-buffers.h"
|
|
|
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
#include "linux-drm-syncobj-v1-client-protocol.h"
|
|
|
|
struct xwl_gbm_private {
|
|
drmDevice *device;
|
|
char *device_name;
|
|
struct gbm_device *gbm;
|
|
int drm_fd;
|
|
Bool fd_render_node;
|
|
Bool drm_authenticated;
|
|
Bool dmabuf_capable;
|
|
Bool glamor_gles;
|
|
Bool implicit_sync;
|
|
Bool supports_syncobjs;
|
|
|
|
/* Set if wl_drm is available */
|
|
struct wl_drm *drm;
|
|
uint32_t capabilities;
|
|
};
|
|
|
|
struct xwl_pixmap {
|
|
struct wl_buffer *buffer;
|
|
EGLImage image;
|
|
unsigned int texture;
|
|
struct gbm_bo *bo;
|
|
Bool implicit_modifier;
|
|
#ifdef DRI3
|
|
struct dri3_syncobj *syncobj;
|
|
uint64_t timeline_point;
|
|
int efd;
|
|
struct xwl_window_buffer *xwl_window_buffer;
|
|
#endif /* DRI3 */
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
Bool
|
|
xwl_glamor_has_wl_drm(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
return !!(xwl_gbm->drm != NULL);
|
|
}
|
|
|
|
/* There is a workaround for Mesa behaviour, which will cause black windows
|
|
* when RGBX formats is using. Why exactly? There is an explanation:
|
|
* 1. We create GL_RGBA texture with GL_UNSIGNED_BYTE type, all allowed by ES.
|
|
* 2 .We export these texture to GBM bo with GBM_FORMAT_XRGB8888, and Mesa sets internal
|
|
* format of these textures as GL_RGB8 (mesa/mesa!5034 (merged))
|
|
* 3. We import these BO at some point, and use glTexSubImage on it with GL_RGBA format
|
|
* and with GL_UNSIGNED_BYTE type, as we creates. Mesa checks its internalformat
|
|
* in glTexSubImage2D and fails due to GLES internal format limitation
|
|
* (see https://registry.khronos.org/OpenGL/specs/es/2.0/es_full_spec_2.0.pdf, section 3.7.1).
|
|
*/
|
|
static uint32_t
|
|
gbm_format_for_depth(int depth, int gles)
|
|
{
|
|
switch (depth) {
|
|
case 15:
|
|
return GBM_FORMAT_ARGB1555;
|
|
case 16:
|
|
return GBM_FORMAT_RGB565;
|
|
case 24:
|
|
if (gles)
|
|
return GBM_FORMAT_ARGB8888;
|
|
return GBM_FORMAT_XRGB8888;
|
|
case 30:
|
|
return GBM_FORMAT_ARGB2101010;
|
|
default:
|
|
ErrorF("unexpected depth: %d\n", depth);
|
|
case 32:
|
|
return GBM_FORMAT_ARGB8888;
|
|
}
|
|
}
|
|
|
|
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 = (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER);
|
|
close(fd);
|
|
|
|
return is_render_node;
|
|
}
|
|
|
|
static PixmapPtr
|
|
xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
|
|
int depth,
|
|
Bool implicit_modifier)
|
|
{
|
|
PixmapPtr pixmap;
|
|
struct xwl_pixmap *xwl_pixmap;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
uint64_t modifier = gbm_bo_get_modifier(bo);
|
|
const int num_planes = gbm_bo_get_plane_count(bo);
|
|
int fds[GBM_MAX_PLANES];
|
|
int plane;
|
|
int attr_num = 0;
|
|
EGLint img_attrs[64] = {0};
|
|
enum PlaneAttrs {
|
|
PLANE_FD,
|
|
PLANE_OFFSET,
|
|
PLANE_PITCH,
|
|
PLANE_MODIFIER_LO,
|
|
PLANE_MODIFIER_HI,
|
|
NUM_PLANE_ATTRS
|
|
};
|
|
static const EGLint planeAttrs[][NUM_PLANE_ATTRS] = {
|
|
{
|
|
EGL_DMA_BUF_PLANE0_FD_EXT,
|
|
EGL_DMA_BUF_PLANE0_OFFSET_EXT,
|
|
EGL_DMA_BUF_PLANE0_PITCH_EXT,
|
|
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
|
|
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
|
|
},
|
|
{
|
|
EGL_DMA_BUF_PLANE1_FD_EXT,
|
|
EGL_DMA_BUF_PLANE1_OFFSET_EXT,
|
|
EGL_DMA_BUF_PLANE1_PITCH_EXT,
|
|
EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
|
|
EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
|
|
},
|
|
{
|
|
EGL_DMA_BUF_PLANE2_FD_EXT,
|
|
EGL_DMA_BUF_PLANE2_OFFSET_EXT,
|
|
EGL_DMA_BUF_PLANE2_PITCH_EXT,
|
|
EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
|
|
EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
|
|
},
|
|
{
|
|
EGL_DMA_BUF_PLANE3_FD_EXT,
|
|
EGL_DMA_BUF_PLANE3_OFFSET_EXT,
|
|
EGL_DMA_BUF_PLANE3_PITCH_EXT,
|
|
EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
|
|
EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
|
|
},
|
|
};
|
|
|
|
for (plane = 0; plane < num_planes; plane++) fds[plane] = -1;
|
|
#endif
|
|
|
|
xwl_pixmap = calloc(1, 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->implicit_modifier = implicit_modifier;
|
|
#if defined(XWL_HAS_GLAMOR) && defined(DRI3)
|
|
xwl_pixmap->efd = -1;
|
|
#endif /* defined(XWL_HAS_GLAMOR) && defined(DRI3) */
|
|
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
if (xwl_gbm->dmabuf_capable) {
|
|
#define ADD_ATTR(attrs, num, attr) \
|
|
do { \
|
|
assert(((num) + 1) < (sizeof(attrs) / sizeof((attrs)[0]))); \
|
|
(attrs)[(num)++] = (attr); \
|
|
} while (0)
|
|
ADD_ATTR(img_attrs, attr_num, EGL_WIDTH);
|
|
ADD_ATTR(img_attrs, attr_num, gbm_bo_get_width(bo));
|
|
ADD_ATTR(img_attrs, attr_num, EGL_HEIGHT);
|
|
ADD_ATTR(img_attrs, attr_num, gbm_bo_get_height(bo));
|
|
ADD_ATTR(img_attrs, attr_num, EGL_LINUX_DRM_FOURCC_EXT);
|
|
ADD_ATTR(img_attrs, attr_num, gbm_bo_get_format(bo));
|
|
|
|
for (plane = 0; plane < num_planes; plane++) {
|
|
fds[plane] = gbm_bo_get_fd_for_plane(bo, plane);
|
|
ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_FD]);
|
|
ADD_ATTR(img_attrs, attr_num, fds[plane]);
|
|
ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_OFFSET]);
|
|
ADD_ATTR(img_attrs, attr_num, gbm_bo_get_offset(bo, plane));
|
|
ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_PITCH]);
|
|
ADD_ATTR(img_attrs, attr_num, gbm_bo_get_stride_for_plane(bo, plane));
|
|
ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_MODIFIER_LO]);
|
|
ADD_ATTR(img_attrs, attr_num, (uint32_t)(modifier & 0xFFFFFFFFULL));
|
|
ADD_ATTR(img_attrs, attr_num, planeAttrs[plane][PLANE_MODIFIER_HI]);
|
|
ADD_ATTR(img_attrs, attr_num, (uint32_t)(modifier >> 32ULL));
|
|
}
|
|
ADD_ATTR(img_attrs, attr_num, EGL_NONE);
|
|
#undef ADD_ATTR
|
|
|
|
xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
|
|
EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
NULL,
|
|
img_attrs);
|
|
|
|
for (plane = 0; plane < num_planes; plane++) {
|
|
close(fds[plane]);
|
|
fds[plane] = -1;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
|
|
EGL_NO_CONTEXT,
|
|
EGL_NATIVE_PIXMAP_KHR,
|
|
xwl_pixmap->bo, NULL);
|
|
}
|
|
|
|
if (xwl_pixmap->image == EGL_NO_IMAGE_KHR)
|
|
goto error;
|
|
|
|
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);
|
|
if (eglGetError() != EGL_SUCCESS)
|
|
goto error;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
if (!glamor_set_pixmap_texture(pixmap, xwl_pixmap->texture))
|
|
goto error;
|
|
|
|
glamor_set_pixmap_type(pixmap, GLAMOR_TEXTURE_DRM);
|
|
xwl_pixmap_set_private(pixmap, xwl_pixmap);
|
|
|
|
return pixmap;
|
|
|
|
error:
|
|
if (xwl_pixmap->image != EGL_NO_IMAGE_KHR)
|
|
eglDestroyImageKHR(xwl_screen->egl_display, xwl_pixmap->image);
|
|
if (pixmap)
|
|
glamor_destroy_pixmap(pixmap);
|
|
free(xwl_pixmap);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PixmapPtr
|
|
xwl_glamor_gbm_create_pixmap_internal(struct xwl_screen *xwl_screen,
|
|
DrawablePtr drawable,
|
|
int width, int height, int depth,
|
|
unsigned int hint,
|
|
Bool implicit_scanout)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
struct gbm_bo *bo = NULL;
|
|
PixmapPtr pixmap = NULL;
|
|
uint32_t num_modifiers = 0;
|
|
uint64_t *modifiers = NULL;
|
|
|
|
if (width > 0 && height > 0 && depth >= 15 &&
|
|
(hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP ||
|
|
hint == CREATE_PIXMAP_USAGE_SHARED ||
|
|
(xwl_screen->rootless && hint == 0))) {
|
|
uint32_t format = gbm_format_for_depth(depth, xwl_gbm->glamor_gles);
|
|
Bool implicit = FALSE;
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
if (xwl_gbm->dmabuf_capable) {
|
|
Bool supports_scanout = FALSE;
|
|
|
|
if (drawable) {
|
|
xwl_glamor_get_drawable_modifiers_and_scanout(drawable,
|
|
format,
|
|
&num_modifiers,
|
|
&modifiers,
|
|
&supports_scanout);
|
|
}
|
|
|
|
if (num_modifiers == 0) {
|
|
xwl_glamor_get_modifiers(xwl_screen->screen, format,
|
|
&num_modifiers, &modifiers);
|
|
}
|
|
|
|
if (num_modifiers > 0) {
|
|
#ifdef GBM_BO_WITH_MODIFIERS2
|
|
uint32_t usage = GBM_BO_USE_RENDERING;
|
|
if (supports_scanout)
|
|
usage |= GBM_BO_USE_SCANOUT;
|
|
bo = gbm_bo_create_with_modifiers2(xwl_gbm->gbm, width, height,
|
|
format, modifiers, num_modifiers,
|
|
usage);
|
|
#else
|
|
bo = gbm_bo_create_with_modifiers(xwl_gbm->gbm, width, height,
|
|
format, modifiers, num_modifiers);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
if (bo == NULL) {
|
|
uint32_t usage = GBM_BO_USE_RENDERING;
|
|
implicit = TRUE;
|
|
if (implicit_scanout)
|
|
usage |= GBM_BO_USE_SCANOUT;
|
|
|
|
if (num_modifiers > 0) {
|
|
Bool has_mod_invalid = FALSE, has_mod_linear = FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < num_modifiers; i++) {
|
|
if (modifiers[i] == DRM_FORMAT_MOD_INVALID)
|
|
has_mod_invalid = TRUE;
|
|
else if (modifiers[i] == DRM_FORMAT_MOD_LINEAR)
|
|
has_mod_linear = TRUE;
|
|
}
|
|
|
|
if (!has_mod_invalid && has_mod_linear)
|
|
usage |= GBM_BO_USE_LINEAR;
|
|
}
|
|
|
|
bo = gbm_bo_create(xwl_gbm->gbm, width, height, format, usage);
|
|
}
|
|
|
|
if (bo) {
|
|
pixmap = xwl_glamor_gbm_create_pixmap_for_bo(xwl_screen->screen, bo, depth, implicit);
|
|
|
|
if (!pixmap) {
|
|
gbm_bo_destroy(bo);
|
|
}
|
|
else if (xwl_screen->rootless && hint == CREATE_PIXMAP_USAGE_BACKING_PIXMAP) {
|
|
glamor_clear_pixmap(pixmap);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pixmap)
|
|
pixmap = glamor_create_pixmap(xwl_screen->screen, width, height, depth, hint);
|
|
|
|
free(modifiers);
|
|
return pixmap;
|
|
}
|
|
|
|
static PixmapPtr
|
|
xwl_glamor_gbm_create_pixmap(ScreenPtr screen,
|
|
int width, int height, int depth,
|
|
unsigned int hint)
|
|
{
|
|
return xwl_glamor_gbm_create_pixmap_internal(xwl_screen_get(screen), NULL,
|
|
width, height, depth, hint, FALSE);
|
|
}
|
|
|
|
PixmapPtr
|
|
xwl_glamor_create_pixmap_for_window(struct xwl_window *xwl_window)
|
|
{
|
|
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
|
|
WindowPtr window = xwl_window->surface_window;
|
|
unsigned border_width = 2 * window->borderWidth;
|
|
|
|
if (!xwl_screen->glamor)
|
|
return NullPixmap;
|
|
|
|
return xwl_glamor_gbm_create_pixmap_internal(xwl_screen,
|
|
&window->drawable,
|
|
window->drawable.width + border_width,
|
|
window->drawable.height + border_width,
|
|
window->drawable.depth,
|
|
CREATE_PIXMAP_USAGE_BACKING_PIXMAP,
|
|
xwl_window->has_implicit_scanout_support);
|
|
}
|
|
|
|
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) {
|
|
xwl_pixmap_del_buffer_release_cb(pixmap);
|
|
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);
|
|
xwl_glamor_gbm_dispose_syncpts(pixmap);
|
|
free(xwl_pixmap);
|
|
}
|
|
|
|
return glamor_destroy_pixmap(pixmap);
|
|
}
|
|
|
|
static const struct wl_buffer_listener xwl_glamor_gbm_buffer_listener = {
|
|
xwl_pixmap_buffer_release_cb,
|
|
};
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
static Bool
|
|
init_buffer_params_with_modifiers(struct xwl_pixmap *xwl_pixmap,
|
|
uint64_t *modifier,
|
|
int *num_planes,
|
|
int *prime_fds,
|
|
uint32_t *strides,
|
|
uint32_t *offsets)
|
|
{
|
|
#ifndef GBM_BO_FD_FOR_PLANE
|
|
int32_t first_handle;
|
|
#endif
|
|
int i;
|
|
|
|
*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++) {
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
prime_fds[i] = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, i);
|
|
#else
|
|
union gbm_bo_handle plane_handle;
|
|
|
|
plane_handle = gbm_bo_get_handle_for_plane(xwl_pixmap->bo, i);
|
|
if (i == 0)
|
|
first_handle = plane_handle.s32;
|
|
|
|
/* If all planes point to the same object as the first plane, i.e. they
|
|
* all have the same handle, we can fall back to the non-planar
|
|
* gbm_bo_get_fd without losing information. If they point to different
|
|
* objects we are out of luck and need to give up.
|
|
*/
|
|
if (first_handle == plane_handle.s32)
|
|
prime_fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
else
|
|
prime_fds[i] = -1;
|
|
#endif
|
|
if (prime_fds[i] == -1) {
|
|
while (--i >= 0)
|
|
close(prime_fds[i]);
|
|
return FALSE;
|
|
}
|
|
strides[i] = gbm_bo_get_stride_for_plane(xwl_pixmap->bo, i);
|
|
offsets[i] = gbm_bo_get_offset(xwl_pixmap->bo, i);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
static Bool
|
|
init_buffer_params_fallback(struct xwl_pixmap *xwl_pixmap,
|
|
uint64_t *modifier,
|
|
int *num_planes,
|
|
int *prime_fds,
|
|
uint32_t *strides,
|
|
uint32_t *offsets)
|
|
{
|
|
*num_planes = 1;
|
|
*modifier = DRM_FORMAT_MOD_INVALID;
|
|
prime_fds[0] = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
if (prime_fds[0] == -1)
|
|
return FALSE;
|
|
|
|
strides[0] = gbm_bo_get_stride(xwl_pixmap->bo);
|
|
offsets[0] = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
unsigned short width = pixmap->drawable.width;
|
|
unsigned short height = pixmap->drawable.height;
|
|
uint32_t format;
|
|
int num_planes;
|
|
int prime_fds[4];
|
|
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 xwl_pixmap->buffer;
|
|
}
|
|
|
|
if (!xwl_pixmap->bo)
|
|
return NULL;
|
|
|
|
format = wl_drm_format_for_depth(pixmap->drawable.depth);
|
|
|
|
#ifdef GBM_BO_WITH_MODIFIERS
|
|
if (!xwl_pixmap->implicit_modifier) {
|
|
if (!init_buffer_params_with_modifiers(xwl_pixmap,
|
|
&modifier,
|
|
&num_planes,
|
|
prime_fds,
|
|
strides,
|
|
offsets))
|
|
return NULL;
|
|
} else
|
|
#endif
|
|
{
|
|
if (!init_buffer_params_fallback(xwl_pixmap,
|
|
&modifier,
|
|
&num_planes,
|
|
prime_fds,
|
|
strides,
|
|
offsets))
|
|
return NULL;
|
|
}
|
|
|
|
if (xwl_screen->dmabuf &&
|
|
xwl_glamor_is_modifier_supported(xwl_screen, format, modifier)) {
|
|
struct zwp_linux_buffer_params_v1 *params;
|
|
|
|
params = zwp_linux_dmabuf_v1_create_params(xwl_screen->dmabuf);
|
|
for (i = 0; i < num_planes; i++) {
|
|
zwp_linux_buffer_params_v1_add(params, prime_fds[i], i,
|
|
offsets[i], strides[i],
|
|
modifier >> 32, modifier & 0xffffffff);
|
|
}
|
|
|
|
xwl_pixmap->buffer =
|
|
zwp_linux_buffer_params_v1_create_immed(params, width, height,
|
|
format, 0);
|
|
zwp_linux_buffer_params_v1_destroy(params);
|
|
} else if (num_planes == 1 && modifier == DRM_FORMAT_MOD_INVALID &&
|
|
xwl_gbm->drm) {
|
|
xwl_pixmap->buffer =
|
|
wl_drm_create_prime_buffer(xwl_gbm->drm, prime_fds[0], width, height,
|
|
format,
|
|
0, gbm_bo_get_stride(xwl_pixmap->bo),
|
|
0, 0,
|
|
0, 0);
|
|
}
|
|
|
|
for (i = 0; i < num_planes; i++)
|
|
close(prime_fds[i]);
|
|
|
|
/* Add our listener now */
|
|
if (xwl_pixmap->buffer)
|
|
wl_buffer_add_listener(xwl_pixmap->buffer,
|
|
&xwl_glamor_gbm_buffer_listener, pixmap);
|
|
|
|
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)
|
|
return;
|
|
|
|
/* Cannot use GBM after clean-up, disable GLAMOR support from now on */
|
|
ErrorF("XWAYLAND: Disabling GLAMOR support\n");
|
|
xwl_screen->glamor = XWL_GLAMOR_NONE;
|
|
|
|
if (xwl_gbm->device_name)
|
|
free(xwl_gbm->device_name);
|
|
drmFreeDevice(&xwl_gbm->device);
|
|
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);
|
|
if (xwl_screen->explicit_sync)
|
|
wp_linux_drm_syncobj_manager_v1_destroy(xwl_screen->explicit_sync);
|
|
|
|
dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key,
|
|
NULL);
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifdef DRI3
|
|
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;
|
|
}
|
|
#endif /* DRI3 */
|
|
|
|
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;
|
|
Bool implicit = FALSE;
|
|
|
|
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, xwl_gbm->glamor_gles);
|
|
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,
|
|
GBM_BO_USE_RENDERING);
|
|
#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, xwl_gbm->glamor_gles);
|
|
bo = gbm_bo_import(xwl_gbm->gbm, GBM_BO_IMPORT_FD, &data,
|
|
GBM_BO_USE_RENDERING);
|
|
implicit = TRUE;
|
|
} else {
|
|
goto error;
|
|
}
|
|
|
|
if (bo == NULL)
|
|
goto error;
|
|
|
|
pixmap = xwl_glamor_gbm_create_pixmap_for_bo(screen, bo, depth, implicit);
|
|
if (pixmap == NULL) {
|
|
gbm_bo_destroy(bo);
|
|
goto error;
|
|
}
|
|
|
|
return pixmap;
|
|
|
|
error:
|
|
return NULL;
|
|
}
|
|
|
|
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
|
|
#ifndef GBM_BO_FD_FOR_PLANE
|
|
int32_t first_handle;
|
|
#endif
|
|
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++) {
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
fds[i] = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, i);
|
|
#else
|
|
union gbm_bo_handle plane_handle;
|
|
|
|
plane_handle = gbm_bo_get_handle_for_plane(xwl_pixmap->bo, i);
|
|
if (i == 0)
|
|
first_handle = plane_handle.s32;
|
|
|
|
/* If all planes point to the same object as the first plane, i.e. they
|
|
* all have the same handle, we can fall back to the non-planar
|
|
* gbm_bo_get_fd without losing information. If they point to different
|
|
* objects we are out of luck and need to give up.
|
|
*/
|
|
if (first_handle == plane_handle.s32)
|
|
fds[i] = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
else
|
|
fds[i] = -1;
|
|
#endif
|
|
if (fds[i] == -1) {
|
|
while (--i >= 0)
|
|
close(fds[i]);
|
|
return 0;
|
|
}
|
|
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);
|
|
if (fds[0] == -1)
|
|
return 0;
|
|
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
|
|
*/
|
|
int
|
|
glamor_egl_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
|
|
CARD16 *stride, CARD32 *size)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
xwl_glamor_dmabuf_export_sync_file(PixmapPtr pixmap)
|
|
{
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
struct xwl_pixmap *xwl_pixmap;
|
|
int num_planes;
|
|
int sync_file = -1;
|
|
int p;
|
|
#ifndef GBM_BO_FD_FOR_PLANE
|
|
int32_t first_handle;
|
|
#endif
|
|
|
|
if (!xwl_screen->glamor)
|
|
return -1;
|
|
|
|
#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
|
|
xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
|
|
|
|
for (p = 0; p < num_planes; ++p) {
|
|
int plane_fd;
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
plane_fd = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, p);
|
|
#else
|
|
union gbm_bo_handle plane_handle =
|
|
gbm_bo_get_handle_for_plane(xwl_pixmap->bo, p);
|
|
if (p == 0)
|
|
first_handle = plane_handle.s32;
|
|
|
|
if (plane_handle.s32 == first_handle)
|
|
plane_fd = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
else
|
|
continue;
|
|
#endif /* GBM_BO_FD_FOR_PLANE */
|
|
struct dma_buf_export_sync_file export_args = { 0 };
|
|
export_args.fd = -1;
|
|
export_args.flags = DMA_BUF_SYNC_READ;
|
|
drmIoctl(plane_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &export_args);
|
|
close(plane_fd);
|
|
if (sync_file == -1) {
|
|
sync_file = export_args.fd;
|
|
} else {
|
|
struct sync_merge_data merge_args = { 0 };
|
|
merge_args.fd2 = export_args.fd;
|
|
ioctl(sync_file, SYNC_IOC_MERGE, &merge_args);
|
|
close(export_args.fd);
|
|
close(sync_file);
|
|
sync_file = merge_args.fence;
|
|
}
|
|
}
|
|
#endif /* DMA_BUF_IOCTL_EXPORT_SYNC_FILE */
|
|
return sync_file;
|
|
}
|
|
|
|
void
|
|
xwl_glamor_dmabuf_import_sync_file(PixmapPtr pixmap, int sync_file)
|
|
{
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
struct xwl_pixmap *xwl_pixmap;
|
|
int num_planes;
|
|
int p;
|
|
#ifndef GBM_BO_FD_FOR_PLANE
|
|
int32_t first_handle;
|
|
#endif
|
|
|
|
if (!xwl_screen->glamor)
|
|
return;
|
|
|
|
#ifdef DMA_BUF_IOCTL_IMPORT_SYNC_FILE
|
|
xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
num_planes = gbm_bo_get_plane_count(xwl_pixmap->bo);
|
|
|
|
for (p = 0; p < num_planes; ++p) {
|
|
int plane_fd;
|
|
#ifdef GBM_BO_FD_FOR_PLANE
|
|
plane_fd = gbm_bo_get_fd_for_plane(xwl_pixmap->bo, p);
|
|
#else
|
|
union gbm_bo_handle plane_handle =
|
|
gbm_bo_get_handle_for_plane(xwl_pixmap->bo, p);
|
|
if (p == 0)
|
|
first_handle = plane_handle.s32;
|
|
|
|
if (plane_handle.s32 == first_handle)
|
|
plane_fd = gbm_bo_get_fd(xwl_pixmap->bo);
|
|
else
|
|
continue;
|
|
#endif /* GBM_BO_FD_FOR_PLANE */
|
|
struct dma_buf_import_sync_file import_args = { 0 };
|
|
import_args.fd = sync_file;
|
|
import_args.flags = DMA_BUF_SYNC_WRITE;
|
|
drmIoctl(plane_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &import_args);
|
|
close(plane_fd);
|
|
}
|
|
#endif /* DMA_BUF_IOCTL_IMPORT_SYNC_FILE */
|
|
close(sync_file);
|
|
}
|
|
|
|
#ifdef DRI3
|
|
struct xwl_dri3_syncobj
|
|
{
|
|
struct dri3_syncobj base;
|
|
uint32_t handle;
|
|
struct wp_linux_drm_syncobj_timeline_v1 *timeline;
|
|
};
|
|
|
|
void
|
|
xwl_glamor_dri3_syncobj_passthrough(struct xwl_window *xwl_window,
|
|
struct dri3_syncobj *acquire_syncobj,
|
|
struct dri3_syncobj *release_syncobj,
|
|
uint64_t acquire_point,
|
|
uint64_t release_point)
|
|
{
|
|
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
|
|
struct xwl_dri3_syncobj *xwl_acquire_syncobj = (struct xwl_dri3_syncobj *)acquire_syncobj;
|
|
struct xwl_dri3_syncobj *xwl_release_syncobj = (struct xwl_dri3_syncobj *)release_syncobj;
|
|
uint32_t acquire_hi = acquire_point >> 32;
|
|
uint32_t acquire_lo = acquire_point & 0xffffffff;
|
|
uint32_t release_hi = release_point >> 32;
|
|
uint32_t release_lo = release_point & 0xffffffff;
|
|
|
|
if (!xwl_window->surface_sync)
|
|
xwl_window->surface_sync =
|
|
wp_linux_drm_syncobj_manager_v1_get_surface(xwl_screen->explicit_sync,
|
|
xwl_window->surface);
|
|
|
|
wp_linux_drm_syncobj_surface_v1_set_acquire_point(xwl_window->surface_sync,
|
|
xwl_acquire_syncobj->timeline,
|
|
acquire_hi, acquire_lo);
|
|
wp_linux_drm_syncobj_surface_v1_set_release_point(xwl_window->surface_sync,
|
|
xwl_release_syncobj->timeline,
|
|
release_hi, release_lo);
|
|
}
|
|
|
|
static Bool
|
|
xwl_dri3_check_syncobj(struct dri3_syncobj *syncobj, uint64_t point, Bool check_avail)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
return !drmSyncobjTimelineWait(xwl_gbm->drm_fd,
|
|
&xwl_syncobj->handle, &point, 1,
|
|
0 /* timeout */,
|
|
check_avail ?
|
|
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE :
|
|
DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT,
|
|
NULL /* first_signaled */);
|
|
}
|
|
|
|
static Bool
|
|
xwl_dri3_syncobj_has_fence(struct dri3_syncobj *syncobj, uint64_t point)
|
|
{
|
|
return xwl_dri3_check_syncobj(syncobj, point, TRUE /* check_avail */);
|
|
}
|
|
|
|
static Bool
|
|
xwl_dri3_syncobj_is_signaled(struct dri3_syncobj *syncobj, uint64_t point)
|
|
{
|
|
return xwl_dri3_check_syncobj(syncobj, point, FALSE /* check_avail */);
|
|
}
|
|
|
|
static int
|
|
xwl_dri3_syncobj_export_fence(struct dri3_syncobj *syncobj, uint64_t point)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
uint32_t temp_syncobj;
|
|
int fd = -1;
|
|
|
|
drmSyncobjCreate(xwl_gbm->drm_fd, 0, &temp_syncobj);
|
|
drmSyncobjTransfer(xwl_gbm->drm_fd, temp_syncobj, 0,
|
|
xwl_syncobj->handle, point, 0);
|
|
drmSyncobjExportSyncFile(xwl_gbm->drm_fd, temp_syncobj, &fd);
|
|
drmSyncobjDestroy(xwl_gbm->drm_fd, temp_syncobj);
|
|
return fd;
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_syncobj_import_fence(struct dri3_syncobj *syncobj,
|
|
uint64_t point, int fd)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
uint32_t temp_syncobj;
|
|
|
|
drmSyncobjCreate(xwl_gbm->drm_fd, 0, &temp_syncobj);
|
|
drmSyncobjImportSyncFile(xwl_gbm->drm_fd, temp_syncobj, fd);
|
|
drmSyncobjTransfer(xwl_gbm->drm_fd, xwl_syncobj->handle, point,
|
|
temp_syncobj, 0, 0);
|
|
drmSyncobjDestroy(xwl_gbm->drm_fd, temp_syncobj);
|
|
close(fd);
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_signal_syncobj(struct dri3_syncobj *syncobj, uint64_t point)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
drmSyncobjTimelineSignal(xwl_gbm->drm_fd, &xwl_syncobj->handle, &point, 1);
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_free_syncobj(struct dri3_syncobj *syncobj)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
if (xwl_syncobj->timeline)
|
|
wp_linux_drm_syncobj_timeline_v1_destroy(xwl_syncobj->timeline);
|
|
|
|
if (xwl_syncobj->handle)
|
|
drmSyncobjDestroy(xwl_gbm->drm_fd, xwl_syncobj->handle);
|
|
|
|
free(xwl_syncobj);
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_syncobj_eventfd(struct dri3_syncobj *syncobj, uint64_t point,
|
|
int efd, Bool wait_avail)
|
|
{
|
|
struct xwl_dri3_syncobj *xwl_syncobj = (struct xwl_dri3_syncobj *)syncobj;
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(syncobj->screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
drmSyncobjEventfd(xwl_gbm->drm_fd, xwl_syncobj->handle, point, efd,
|
|
wait_avail ? DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE : 0);
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_syncobj_submitted_eventfd(struct dri3_syncobj *syncobj,
|
|
uint64_t point, int efd)
|
|
{
|
|
xwl_dri3_syncobj_eventfd(syncobj, point, efd, TRUE /* wait_avail */);
|
|
}
|
|
|
|
static void
|
|
xwl_dri3_syncobj_signaled_eventfd(struct dri3_syncobj *syncobj,
|
|
uint64_t point, int efd)
|
|
{
|
|
xwl_dri3_syncobj_eventfd(syncobj, point, efd, FALSE /* wait_avail */);
|
|
}
|
|
|
|
static struct dri3_syncobj *
|
|
xwl_dri3_create_syncobj(struct xwl_screen *xwl_screen, uint32_t handle)
|
|
{
|
|
struct xwl_dri3_syncobj *syncobj = calloc(1, sizeof (*syncobj));
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
Bool create = !handle;
|
|
|
|
if (!syncobj)
|
|
return NULL;
|
|
|
|
if (create && drmSyncobjCreate(xwl_gbm->drm_fd, 0, &handle))
|
|
goto fail;
|
|
|
|
if (xwl_screen->explicit_sync) {
|
|
int syncobj_fd = -1;
|
|
if (drmSyncobjHandleToFD(xwl_gbm->drm_fd, handle, &syncobj_fd))
|
|
goto fail;
|
|
|
|
syncobj->timeline =
|
|
wp_linux_drm_syncobj_manager_v1_import_timeline(xwl_screen->explicit_sync,
|
|
syncobj_fd);
|
|
close(syncobj_fd);
|
|
if (!syncobj->timeline)
|
|
goto fail;
|
|
}
|
|
|
|
syncobj->handle = handle;
|
|
syncobj->base.screen = xwl_screen->screen;
|
|
syncobj->base.refcount = 1;
|
|
|
|
syncobj->base.free = xwl_dri3_free_syncobj;
|
|
syncobj->base.has_fence = xwl_dri3_syncobj_has_fence;
|
|
syncobj->base.is_signaled = xwl_dri3_syncobj_is_signaled;
|
|
syncobj->base.export_fence = xwl_dri3_syncobj_export_fence;
|
|
syncobj->base.import_fence = xwl_dri3_syncobj_import_fence;
|
|
syncobj->base.signal = xwl_dri3_signal_syncobj;
|
|
syncobj->base.signaled_eventfd = xwl_dri3_syncobj_signaled_eventfd;
|
|
syncobj->base.submitted_eventfd = xwl_dri3_syncobj_submitted_eventfd;
|
|
return &syncobj->base;
|
|
|
|
fail:
|
|
if (create && handle)
|
|
drmSyncobjDestroy(xwl_gbm->drm_fd, handle);
|
|
free(syncobj);
|
|
return NULL;
|
|
}
|
|
|
|
struct dri3_syncobj *
|
|
xwl_glamor_dri3_syncobj_create(struct xwl_screen *xwl_screen)
|
|
{
|
|
return xwl_dri3_create_syncobj(xwl_screen, 0 /* allocate new handle */);
|
|
}
|
|
|
|
static struct dri3_syncobj *
|
|
xwl_dri3_import_syncobj(ClientPtr client, ScreenPtr screen, XID id, int fd)
|
|
{
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
struct xwl_dri3_syncobj *syncobj = NULL;
|
|
uint32_t handle;
|
|
|
|
if (drmSyncobjFDToHandle(xwl_gbm->drm_fd, fd, &handle))
|
|
return NULL;
|
|
|
|
syncobj = (struct xwl_dri3_syncobj *)xwl_dri3_create_syncobj(xwl_screen, handle);
|
|
if (!syncobj) {
|
|
drmSyncobjDestroy(xwl_gbm->drm_fd, handle);
|
|
return NULL;
|
|
}
|
|
|
|
syncobj->base.id = id;
|
|
|
|
return &syncobj->base;
|
|
}
|
|
|
|
static Bool
|
|
xwl_gbm_supports_syncobjs(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
uint64_t syncobj_cap = 0;
|
|
|
|
if (drmGetCap(xwl_gbm->drm_fd, DRM_CAP_SYNCOBJ_TIMELINE,
|
|
&syncobj_cap) || !syncobj_cap)
|
|
return FALSE;
|
|
|
|
/* Check if syncobj eventfd is supported. */
|
|
drmSyncobjEventfd(xwl_gbm->drm_fd, 0, 0, -1, 0);
|
|
if (errno != ENOENT)
|
|
return FALSE;
|
|
|
|
#if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE) || \
|
|
!defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE)
|
|
return FALSE;
|
|
#else
|
|
return TRUE;
|
|
#endif
|
|
}
|
|
|
|
static 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 = xwl_glamor_get_formats,
|
|
.get_modifiers = xwl_glamor_get_modifiers,
|
|
.get_drawable_modifiers = xwl_glamor_get_drawable_modifiers,
|
|
.import_syncobj = NULL, /* need to check for kernel support */
|
|
};
|
|
#endif /* DRI3 */
|
|
|
|
static const char *
|
|
get_render_node_path_for_device(const drmDevicePtr drm_device,
|
|
const char *device_path)
|
|
{
|
|
const char *render_node_path;
|
|
int i;
|
|
|
|
if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER)))
|
|
return NULL;
|
|
render_node_path = drm_device->nodes[DRM_NODE_RENDER];
|
|
|
|
for (i = 0; i < DRM_NODE_MAX; i++) {
|
|
if (!(drm_device->available_nodes & (1 << i)))
|
|
continue;
|
|
if (strcmp(device_path, drm_device->nodes[i]) == 0)
|
|
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);
|
|
xwl_screen->expecting_event--;
|
|
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);
|
|
xwl_screen->expecting_event--;
|
|
return;
|
|
}
|
|
|
|
if (drmGetDevice2(xwl_gbm->drm_fd, 0, &xwl_gbm->device) != 0) {
|
|
ErrorF("wayland-egl: Could not fetch DRM device %s\n",
|
|
xwl_gbm->device_name);
|
|
xwl_screen->expecting_event--;
|
|
return;
|
|
}
|
|
|
|
if (drmGetNodeTypeFromFd(xwl_gbm->drm_fd) == DRM_NODE_RENDER) {
|
|
xwl_gbm->fd_render_node = TRUE;
|
|
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
|
|
};
|
|
|
|
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_syncobj_interface(struct xwl_screen *xwl_screen,
|
|
uint32_t id, uint32_t version)
|
|
{
|
|
xwl_screen->explicit_sync =
|
|
wl_registry_bind(xwl_screen->registry, id,
|
|
&wp_linux_drm_syncobj_manager_v1_interface,
|
|
version);
|
|
return TRUE;
|
|
}
|
|
|
|
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"));
|
|
}
|
|
|
|
#ifdef DRI3
|
|
static void
|
|
xwl_glamor_gbm_release_fence_avail(int fd, int xevents, void *data)
|
|
{
|
|
struct xwl_pixmap *xwl_pixmap = data;
|
|
struct xwl_window_buffer *xwl_window_buffer = xwl_pixmap->xwl_window_buffer;
|
|
|
|
SetNotifyFd(fd, NULL, 0, NULL);
|
|
close(fd);
|
|
xwl_pixmap->efd = -1;
|
|
|
|
xwl_window_buffer_release(xwl_window_buffer);
|
|
}
|
|
#endif /* DRI3 */
|
|
|
|
Bool
|
|
xwl_glamor_supports_implicit_sync(struct xwl_screen *xwl_screen)
|
|
{
|
|
/* absent glamor, implicit sync is irrelevant so just return TRUE */
|
|
return !xwl_screen->glamor ||
|
|
xwl_gbm_get(xwl_screen)->implicit_sync;
|
|
}
|
|
|
|
Bool
|
|
xwl_glamor_supports_syncobjs(struct xwl_screen *xwl_screen)
|
|
{
|
|
return xwl_screen->glamor &&
|
|
xwl_gbm_get(xwl_screen)->supports_syncobjs;
|
|
}
|
|
|
|
Bool
|
|
xwl_glamor_gbm_set_syncpts(struct xwl_window *xwl_window, PixmapPtr pixmap)
|
|
{
|
|
#ifdef DRI3
|
|
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
uint64_t acquire_point;
|
|
uint64_t release_point;
|
|
int fence_fd;
|
|
|
|
if (!xwl_screen->glamor)
|
|
return FALSE;
|
|
|
|
if (!xwl_pixmap) {
|
|
ErrorF("XWAYLAND: Failed to set synchronization point, no backing xwl_pixmap!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
acquire_point = ++xwl_pixmap->timeline_point;
|
|
release_point = ++xwl_pixmap->timeline_point;
|
|
|
|
if (!xwl_pixmap->syncobj) {
|
|
struct dri3_syncobj *syncobj = xwl_glamor_dri3_syncobj_create(xwl_screen);
|
|
if (!syncobj)
|
|
goto fail;
|
|
xwl_pixmap->syncobj = syncobj;
|
|
}
|
|
|
|
fence_fd = xwl_glamor_get_fence(xwl_screen);
|
|
if (fence_fd >= 0)
|
|
xwl_pixmap->syncobj->import_fence(xwl_pixmap->syncobj, acquire_point, fence_fd);
|
|
else
|
|
goto fail;
|
|
|
|
xwl_glamor_dri3_syncobj_passthrough(xwl_window,
|
|
xwl_pixmap->syncobj,
|
|
xwl_pixmap->syncobj,
|
|
acquire_point,
|
|
release_point);
|
|
return TRUE;
|
|
|
|
fail:
|
|
/* can't use explicit sync, we will do a glFinish() before presenting */
|
|
if (xwl_pixmap->syncobj) {
|
|
xwl_pixmap->syncobj->free(xwl_pixmap->syncobj);
|
|
xwl_pixmap->syncobj = NULL;
|
|
}
|
|
#endif /* DRI3 */
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
xwl_glamor_gbm_dispose_syncpts(PixmapPtr pixmap)
|
|
{
|
|
#ifdef DRI3
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
|
|
if (!xwl_screen->glamor || !xwl_pixmap)
|
|
return;
|
|
|
|
if (xwl_pixmap->syncobj) {
|
|
xwl_pixmap->syncobj->free(xwl_pixmap->syncobj);
|
|
xwl_pixmap->syncobj = NULL;
|
|
}
|
|
|
|
if (xwl_pixmap->efd >= 0) {
|
|
SetNotifyFd(xwl_pixmap->efd, NULL, 0, NULL);
|
|
close(xwl_pixmap->efd);
|
|
}
|
|
#endif /* DRI3 */
|
|
}
|
|
|
|
void
|
|
xwl_glamor_gbm_wait_syncpts(PixmapPtr pixmap)
|
|
{
|
|
#ifdef DRI3
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
|
|
if (!xwl_screen->glamor || !xwl_pixmap)
|
|
return;
|
|
|
|
if (xwl_pixmap->syncobj) {
|
|
int fence_fd = xwl_pixmap->syncobj->export_fence(xwl_pixmap->syncobj,
|
|
xwl_pixmap->timeline_point);
|
|
|
|
xwl_glamor_wait_fence(xwl_screen, fence_fd);
|
|
}
|
|
#endif /* DRI3 */
|
|
}
|
|
|
|
void
|
|
xwl_glamor_gbm_wait_release_fence(struct xwl_window *xwl_window,
|
|
PixmapPtr pixmap,
|
|
struct xwl_window_buffer *xwl_window_buffer)
|
|
{
|
|
#ifdef DRI3
|
|
struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap);
|
|
struct xwl_screen *xwl_screen = xwl_screen_get(pixmap->drawable.pScreen);
|
|
|
|
if (!xwl_screen->glamor || !xwl_pixmap || !xwl_window_buffer)
|
|
return;
|
|
|
|
xwl_pixmap->xwl_window_buffer = xwl_window_buffer;
|
|
/* wait until the release fence is available before re-using this buffer */
|
|
xwl_pixmap->efd = eventfd(0, EFD_CLOEXEC);
|
|
SetNotifyFd(xwl_pixmap->efd, xwl_glamor_gbm_release_fence_avail, X_NOTIFY_READ,
|
|
xwl_pixmap);
|
|
xwl_pixmap->syncobj->submitted_eventfd(xwl_pixmap->syncobj,
|
|
xwl_pixmap->timeline_point,
|
|
xwl_pixmap->efd);
|
|
#endif /* DRI3 */
|
|
}
|
|
|
|
static Bool
|
|
xwl_glamor_try_to_make_context_current(struct xwl_screen *xwl_screen)
|
|
{
|
|
if (xwl_screen->egl_context == EGL_NO_CONTEXT)
|
|
return FALSE;
|
|
|
|
return eglMakeCurrent(xwl_screen->egl_display, EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE, xwl_screen->egl_context);
|
|
}
|
|
|
|
static void
|
|
xwl_glamor_maybe_destroy_context(struct xwl_screen *xwl_screen)
|
|
{
|
|
if (xwl_screen->egl_context == EGL_NO_CONTEXT)
|
|
return;
|
|
|
|
eglMakeCurrent(xwl_screen->egl_display, EGL_NO_SURFACE,
|
|
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
eglDestroyContext(xwl_screen->egl_display, xwl_screen->egl_context);
|
|
xwl_screen->egl_context = EGL_NO_CONTEXT;
|
|
}
|
|
|
|
static Bool
|
|
xwl_glamor_try_big_gl_api(struct xwl_screen *xwl_screen)
|
|
{
|
|
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
|
|
};
|
|
int gl_version;
|
|
|
|
if (!(xwl_screen->glamor & XWL_GLAMOR_GL))
|
|
return FALSE;
|
|
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
|
|
xwl_screen->egl_context =
|
|
eglCreateContext(xwl_screen->egl_display, EGL_NO_CONFIG_KHR,
|
|
EGL_NO_CONTEXT, config_attribs_core);
|
|
|
|
if (xwl_screen->egl_context == EGL_NO_CONTEXT)
|
|
xwl_screen->egl_context =
|
|
eglCreateContext(xwl_screen->egl_display, EGL_NO_CONFIG_KHR,
|
|
EGL_NO_CONTEXT, NULL);
|
|
|
|
if (!xwl_glamor_try_to_make_context_current(xwl_screen)) {
|
|
ErrorF("Failed to make EGL context current with GL\n");
|
|
xwl_glamor_maybe_destroy_context(xwl_screen);
|
|
return FALSE;
|
|
}
|
|
|
|
/* glamor needs at least GL 2.1, if the GL version is less than 2.1,
|
|
* drop the context we created, it's useless.
|
|
*/
|
|
gl_version = epoxy_gl_version();
|
|
if (gl_version < 21) {
|
|
ErrorF("Supported GL version is not sufficient (required 21, found %i)\n",
|
|
gl_version);
|
|
xwl_glamor_maybe_destroy_context(xwl_screen);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
xwl_glamor_try_gles_api(struct xwl_screen *xwl_screen)
|
|
{
|
|
const EGLint gles_attribs[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION,
|
|
2,
|
|
EGL_NONE,
|
|
};
|
|
|
|
if (!(xwl_screen->glamor & XWL_GLAMOR_GLES))
|
|
return FALSE;
|
|
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
|
|
xwl_screen->egl_context = eglCreateContext(xwl_screen->egl_display,
|
|
EGL_NO_CONFIG_KHR,
|
|
EGL_NO_CONTEXT, gles_attribs);
|
|
|
|
if (!xwl_glamor_try_to_make_context_current(xwl_screen)) {
|
|
ErrorF("Failed to make EGL context current with GLES2\n");
|
|
xwl_glamor_maybe_destroy_context(xwl_screen);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool
|
|
xwl_glamor_gbm_init_main_dev(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
drmDevice *main_dev;
|
|
|
|
while (!xwl_screen->default_feedback.feedback_done) {
|
|
if (wl_display_dispatch(xwl_screen->display) < 0) {
|
|
ErrorF("Failed to dispatch Wayland display\n");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
main_dev = xwl_screen->default_feedback.main_dev;
|
|
if (!main_dev) {
|
|
ErrorF("No main linux-dmabuf device advertised by compositor\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(main_dev->available_nodes & (1 << DRM_NODE_RENDER))) {
|
|
ErrorF("Main linux-dmabuf device has no render node\n");
|
|
return FALSE;
|
|
}
|
|
|
|
xwl_gbm->device_name = strdup(main_dev->nodes[DRM_NODE_RENDER]);
|
|
if (!xwl_gbm->device_name) {
|
|
return FALSE;
|
|
}
|
|
|
|
xwl_gbm->drm_fd = open(xwl_gbm->device_name, O_RDWR | O_CLOEXEC);
|
|
if (xwl_gbm->drm_fd < 0) {
|
|
ErrorF("wayland-egl: could not open %s (%s)\n",
|
|
xwl_gbm->device_name, strerror(errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if (drmGetDevice2(xwl_gbm->drm_fd, 0, &xwl_gbm->device) != 0) {
|
|
ErrorF("wayland-egl: Could not fetch DRM device %s\n",
|
|
xwl_gbm->device_name);
|
|
return FALSE;
|
|
}
|
|
|
|
xwl_gbm->fd_render_node = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
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;
|
|
const GLubyte *renderer;
|
|
const char *gbm_backend_name, *egl_vendor;
|
|
|
|
if (!xwl_gbm->drm && !xwl_glamor_gbm_init_main_dev(xwl_screen))
|
|
return FALSE;
|
|
|
|
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;
|
|
}
|
|
|
|
if (!eglInitialize(xwl_screen->egl_display, &major, &minor)) {
|
|
ErrorF("eglInitialize() failed\n");
|
|
goto error;
|
|
}
|
|
|
|
if (!xwl_glamor_try_big_gl_api(xwl_screen) &&
|
|
!xwl_glamor_try_gles_api(xwl_screen)) {
|
|
ErrorF("Cannot use neither GL nor GLES2\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, "softpipe")) {
|
|
ErrorF("Refusing to try glamor on softpipe\n");
|
|
goto error;
|
|
}
|
|
if (!strncmp("llvmpipe", (const char *)renderer, strlen("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;
|
|
|
|
gbm_backend_name = gbm_device_get_backend_name(xwl_gbm->gbm);
|
|
/* Mesa uses "drm" as backend name, in that case, just do nothing */
|
|
if (gbm_backend_name && strcmp(gbm_backend_name, "drm") != 0)
|
|
xwl_screen->glvnd_vendor = gbm_backend_name;
|
|
xwl_gbm->glamor_gles = !epoxy_is_desktop_gl();
|
|
|
|
egl_vendor = eglQueryString(xwl_screen->egl_display, EGL_VENDOR);
|
|
if (!egl_vendor) {
|
|
ErrorF("Could not determine EGL vendor\n");
|
|
goto error;
|
|
}
|
|
/* NVIDIA driver does not support implicit sync */
|
|
xwl_gbm->implicit_sync = !strstr(egl_vendor, "NVIDIA");
|
|
#ifdef DRI3
|
|
if (xwl_gbm_supports_syncobjs(xwl_screen) &&
|
|
epoxy_has_egl_extension(xwl_screen->egl_display,
|
|
"ANDROID_native_fence_sync"))
|
|
xwl_gbm->supports_syncobjs = TRUE;
|
|
|
|
if (!xwl_gbm->supports_syncobjs && xwl_screen->explicit_sync) {
|
|
/* explicit sync requires syncobj support */
|
|
wp_linux_drm_syncobj_manager_v1_destroy(xwl_screen->explicit_sync);
|
|
xwl_screen->explicit_sync = NULL;
|
|
}
|
|
#endif /* DRI3 */
|
|
return TRUE;
|
|
error:
|
|
if (xwl_screen->egl_display != EGL_NO_DISPLAY) {
|
|
xwl_glamor_maybe_destroy_context(xwl_screen);
|
|
eglTerminate(xwl_screen->egl_display);
|
|
xwl_screen->egl_display = EGL_NO_DISPLAY;
|
|
}
|
|
|
|
xwl_glamor_gbm_cleanup(xwl_screen);
|
|
return FALSE;
|
|
}
|
|
|
|
Bool
|
|
xwl_glamor_gbm_init_screen(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
#ifdef DRI3
|
|
if (xwl_gbm->supports_syncobjs) {
|
|
xwl_dri3_info.version = 4;
|
|
xwl_dri3_info.import_syncobj = xwl_dri3_import_syncobj;
|
|
}
|
|
|
|
if (!dri3_screen_init(xwl_screen->screen, &xwl_dri3_info)) {
|
|
ErrorF("Failed to initialize dri3\n");
|
|
goto error;
|
|
}
|
|
#endif /* DRI3 */
|
|
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;
|
|
}
|
|
|
|
drmDevice *xwl_gbm_get_main_device(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm = xwl_gbm_get(xwl_screen);
|
|
|
|
return xwl_gbm->device;
|
|
}
|
|
|
|
Bool
|
|
xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
|
|
{
|
|
struct xwl_gbm_private *xwl_gbm;
|
|
|
|
if (!xwl_glamor_gbm_has_egl_extension())
|
|
return FALSE;
|
|
|
|
if (!dixRegisterPrivateKey(&xwl_gbm_private_key, PRIVATE_SCREEN, 0))
|
|
return FALSE;
|
|
|
|
xwl_gbm = calloc(1, sizeof(*xwl_gbm));
|
|
if (!xwl_gbm) {
|
|
ErrorF("glamor: Not enough memory to setup GBM, disabling\n");
|
|
return FALSE;
|
|
}
|
|
|
|
dixSetPrivate(&xwl_screen->screen->devPrivates, &xwl_gbm_private_key,
|
|
xwl_gbm);
|
|
|
|
return TRUE;
|
|
}
|