Commit 54ac0971 authored by Lyude Paul's avatar Lyude Paul Committed by Adam Jackson
Browse files

xwayland: Add glamor egl_backend for EGLStreams

This adds initial support for displaying Xwayland applications through
the use of EGLStreams and nvidia's custom wayland protocol by adding
another egl_backend driver. This also adds some additional egl_backend
hooks that are required to make things work properly.

EGLStreams work a lot differently then the traditional way of handling
buffers with wayland. Unfortunately, there are also a LOT of various
pitfalls baked into it's design that need to be explained.

This has a very large and unfortunate implication: direct rendering is,
for the time being at least, impossible to do through EGLStreams. The
main reason being that the EGLStream spec mandates that we lose the
entire color buffer contents with each eglSwapBuffers(), which goes
against X's requirement of not losing data with pixmaps.  no way to use
an allocated EGLSurface as the storage for glamor rendering like we do
with GBM, we have to rely on blitting each pixmap to it's respective
EGLSurface producer each frame. In order to pull this off, we add two
different additional egl_backend hooks that GBM opts out of

- egl_backend.allow_commits for holding off displaying any EGLStream
  backed pixmaps until the point where it's stream is completely
  initialized and ready for use
- egl_backend.post_damage for blitting the content of the EGLStream
  surface producer before Xwayland actually damages and commits the
  wl_surface to the screen.

The other big pitfall here is that using nvidia's wayland-eglstreams
helper library is also not possible for the most part. All of it's API
for creating and destroying streams rely on being able to perform a
roundtrip in order to bring each stream to completion since the wayland
compositor must perform it's job of connecting a consumer to each
EGLstream. Because Xwayland has to potentially handle both responding to
the wayland compositor and it's own X clients, the situation of the
wayland compositor being one of our X clients must be considered. If we
perform a roundtrip with the Wayland compositor, it's possible that the
wayland compositor might currently be connected to us as an X client and
thus hang while both Xwayland and the wayland compositor await responses
from eachother. To avoid this, we work directly with the wayland
protocol and use wl_display_sync() events along with release() events to
set up and destroy EGLStreams asynchronously alongside handling X

Additionally, since setting up EGLStreams is not an atomic operation we
have to take into consideration the fact that an EGLStream can
potentially be created in response to a window resize, then immediately
deleted due to another pending window resize in the same X client's
pending reqests before Xwayland hits the part of it's event loop where
we read from the wayland compositor. To make this even more painful, we
also have to take into consideration that since EGLStreams are not
atomic that it's possible we could delete wayland resources for an
EGLStream before the compositor even finishes using them and thus run
into errors. So, we use quite a bit of tracking logic to keep EGLStream
objects alive until we know the compositor isn't using them (even if
this means the stream outlives the pixmap it backed).

While the default backend for glamor remains GBM, this patch exists for
users who have had to deal with the reprecussion of their GPU
manufacturers ignoring the advice of upstream and the standardization of
GBM across most major GPU manufacturers. It is not intended to be a
final solution to the GBM debate, but merely a baindaid so our users
don't have to suffer from the consequences of companies avoiding working
upstream. New drivers are strongly encouraged not to use this as a
backend, and use GBM like everyone else. We even spit this out as an
error from Xwayland when using the eglstream backend.
Signed-off-by: Lyude Paul's avatarLyude Paul <>
Acked-by: Daniel Stone's avatarDaniel Stone <>
Reviewed-by: Adam Jackson's avatarAdam Jackson <>
parent 994f7810
......@@ -590,6 +590,7 @@ AC_ARG_ENABLE(xvfb, AS_HELP_STRING([--enable-xvfb], [Build Xvfb server
AC_ARG_ENABLE(xnest, AS_HELP_STRING([--enable-xnest], [Build Xnest server (default: auto)]), [XNEST=$enableval], [XNEST=auto])
AC_ARG_ENABLE(xquartz, AS_HELP_STRING([--enable-xquartz], [Build Xquartz server for OS-X (default: auto)]), [XQUARTZ=$enableval], [XQUARTZ=auto])
AC_ARG_ENABLE(xwayland, AS_HELP_STRING([--enable-xwayland], [Build Xwayland server (default: auto)]), [XWAYLAND=$enableval], [XWAYLAND=auto])
AC_ARG_ENABLE(xwayland-eglstream, AS_HELP_STRING([--enable-xwayland-eglstream], [Build Xwayland eglstream support (default: no)]), [XWAYLAND_EGLSTREAM=$enableval], [XWAYLAND_EGLSTREAM=no])
AC_ARG_ENABLE(standalone-xpbproxy, AS_HELP_STRING([--enable-standalone-xpbproxy], [Build a standalone xpbproxy (in addition to the one integrated into Xquartz as a separate thread) (default: no)]), [STANDALONE_XPBPROXY=$enableval], [STANDALONE_XPBPROXY=no])
AC_ARG_ENABLE(xwin, AS_HELP_STRING([--enable-xwin], [Build XWin server (default: auto)]), [XWIN=$enableval], [XWIN=auto])
AC_ARG_ENABLE(glamor, AS_HELP_STRING([--enable-glamor], [Build glamor dix module (default: auto)]), [GLAMOR=$enableval], [GLAMOR=auto])
......@@ -2385,6 +2386,28 @@ if test "x$XWAYLAND" = xyes; then
[Build xwayland with glamor support])
PKG_CHECK_MODULES(WAYLAND_EGLSTREAM, [wayland-eglstream-protocols >= 1.0.2], [have_wl_eglstream=yes], [have_wl_eglstream=no])
if test "x$XWAYLAND_EGLSTREAM" = xauto; then
if test "x$have_wl_eglstream" = xyes && test "x$GLAMOR" = xyes; then
if test "x$XWAYLAND_EGLSTREAM" = xyes; then
if test "x$GLAMOR" != xyes; then
AC_MSG_ERROR([Xwayland eglstream support explicitly requested, but required modules not found.])
if test "x$have_wl_eglstream" = xno; then
AC_MSG_ERROR([Xwayland eglstream support requires wayland-eglstream-protocols >= 1.0.2])
AC_SUBST(WAYLAND_EGLSTREAM_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-eglstream-protocols`)
[Build xwayland with eglstream support])
......@@ -2406,6 +2429,7 @@ if test "x$XWAYLAND" = xyes; then
AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`)
dnl and the rest of these are generic, so they're in config.h
......@@ -42,6 +42,11 @@ Xwayland_SOURCES += \
Xwayland_SOURCES += \
glamor_built_sources = \
drm-client-protocol.h \
......@@ -68,12 +73,19 @@ Xwayland_built_sources += \
linux-dmabuf-unstable-v1-client-protocol.h \
Xwayland_built_sources += \
wayland-eglstream-client-protocol.h \
wayland-eglstream-protocol.c \
wayland-eglstream-controller-client-protocol.h \
nodist_Xwayland_SOURCES = $(Xwayland_built_sources)
CLEANFILES = $(Xwayland_built_sources)
EXTRA_DIST = drm.xml
$(Xwayland_SOURCES): $(Xwayland_built_sources)
......@@ -108,6 +120,16 @@ linux-dmabuf-unstable-v1-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linu
linux-dmabuf-unstable-v1-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
wayland-eglstream-client-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
wayland-eglstream-controller-client-protocol.h : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream-controller.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
wayland-eglstream-protocol.c : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
wayland-eglstream-controller-protocol.c : $(WAYLAND_EGLSTREAM_DATADIR)/wayland-eglstream-controller.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
%-protocol.c : %.xml
......@@ -51,12 +51,25 @@ srcs += code.process(xdg_output_xml)
srcs += code.process(dmabuf_xml)
xwayland_glamor = []
if gbm_dep.found()
srcs += [
eglstream_srcs = []
if build_glamor
srcs += 'xwayland-glamor.c'
if gbm_dep.found()
srcs += 'xwayland-glamor-gbm.c'
if build_eglstream
eglstream_protodir = eglstream_dep.get_pkgconfig_variable('pkgdatadir')
eglstream_xml = join_paths(eglstream_protodir, 'wayland-eglstream.xml')
eglstream_controller_xml = join_paths(eglstream_protodir, 'wayland-eglstream-controller.xml')
srcs += client_header.process(eglstream_xml)
srcs += client_header.process(eglstream_controller_xml)
srcs += code.process(eglstream_xml)
srcs += code.process(eglstream_controller_xml)
srcs += 'xwayland-glamor-eglstream.c'
srcs += 'xwayland-present.c'
if build_xv
srcs += 'xwayland-glamor-xv.c'
This diff is collapsed.
......@@ -146,11 +146,7 @@ xwl_glamor_gbm_create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo,
return NULL;
if (lastGLContext != xwl_screen->glamor_ctx) {
lastGLContext = xwl_screen->glamor_ctx;
xwl_pixmap->bo = bo;
xwl_pixmap->buffer = NULL;
xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display,
......@@ -32,7 +32,7 @@
#include <glamor_context.h>
static void
xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
glamor_egl_make_current(struct glamor_context *glamor_ctx)
eglMakeCurrent(glamor_ctx->display, EGL_NO_SURFACE,
......@@ -42,6 +42,94 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
FatalError("Failed to make EGL context current\n");
xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen)
if (lastGLContext == xwl_screen->glamor_ctx)
lastGLContext = xwl_screen->glamor_ctx;
return epoxy_has_egl() &&
epoxy_has_egl_extension(NULL, "EGL_EXT_device_base");
void **
xwl_glamor_egl_get_devices(int *num_devices)
EGLDeviceEXT *devices;
Bool ret;
int drm_dev_count = 0;
int i;
/* Get the number of devices */
ret = eglQueryDevicesEXT(0, NULL, num_devices);
if (!ret || *num_devices < 1)
return NULL;
devices = calloc(*num_devices, sizeof(EGLDeviceEXT));
if (!devices)
return NULL;
ret = eglQueryDevicesEXT(*num_devices, devices, num_devices);
if (!ret)
goto error;
/* We're only ever going to care about devices that support
* EGL_EXT_device_drm, so filter out the ones that don't
for (i = 0; i < *num_devices; i++) {
const char *extension_str =
eglQueryDeviceStringEXT(devices[i], EGL_EXTENSIONS);
if (!epoxy_extension_in_string(extension_str, "EGL_EXT_device_drm"))
devices[drm_dev_count++] = devices[i];
if (!drm_dev_count)
goto error;
*num_devices = drm_dev_count;
devices = realloc(devices, sizeof(EGLDeviceEXT) * drm_dev_count);
return devices;
return NULL;
xwl_glamor_egl_device_has_egl_extensions(void *device,
const char **ext_list, size_t size)
EGLDisplay egl_display;
int i;
Bool has_exts = TRUE;
egl_display = glamor_egl_get_display(EGL_PLATFORM_DEVICE_EXT, device);
if (!egl_display || !eglInitialize(egl_display, NULL, NULL))
return FALSE;
for (i = 0; i < size; i++) {
if (!epoxy_has_egl_extension(egl_display, ext_list[i])) {
has_exts = FALSE;
return has_exts;
glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
......@@ -50,7 +138,7 @@ glamor_egl_screen_init(ScreenPtr screen, struct glamor_context *glamor_ctx)
glamor_ctx->ctx = xwl_screen->egl_context;
glamor_ctx->display = xwl_screen->egl_display;
glamor_ctx->make_current = xwl_glamor_egl_make_current;
glamor_ctx->make_current = glamor_egl_make_current;
xwl_screen->glamor_ctx = glamor_ctx;
......@@ -83,6 +171,27 @@ xwl_glamor_pixmap_get_wl_buffer(PixmapPtr pixmap,
return NULL;
xwl_glamor_post_damage(struct xwl_window *xwl_window,
PixmapPtr pixmap, RegionPtr region)
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
if (xwl_screen->egl_backend.post_damage)
xwl_screen->egl_backend.post_damage(xwl_window, pixmap, region);
xwl_glamor_allow_commits(struct xwl_window *xwl_window)
struct xwl_screen *xwl_screen = xwl_window->xwl_screen;
if (xwl_screen->egl_backend.allow_commits)
return xwl_screen->egl_backend.allow_commits(xwl_window);
return TRUE;
static Bool
xwl_glamor_create_screen_resources(ScreenPtr screen)
......@@ -509,5 +509,14 @@ static present_wnmd_info_rec xwl_present_info = {
xwl_present_init(ScreenPtr screen)
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
* doesn't work with the streams backend. we don't have an explicit
* boolean for that, but we do know gbm doesn't fill in this hook...
if (xwl_screen->egl_backend.post_damage != NULL)
return FALSE;
return present_wnmd_screen_init(screen, &xwl_present_info);
......@@ -96,6 +96,9 @@ ddxUseMsg(void)
ErrorF("-rootless run rootless, requires wm support\n");
ErrorF("-wm fd create X client for wm on given fd\n");
ErrorF("-listen fd add give fd as a listen socket\n");
ErrorF("-eglstream use eglstream backend for nvidia GPUs\n");
......@@ -114,6 +117,11 @@ ddxProcessArgument(int argc, char *argv[], int i)
else if (strcmp(argv[i], "-shm") == 0) {
return 1;
else if (strcmp(argv[i], "-eglstream") == 0) {
return 1;
return 0;
......@@ -678,6 +686,11 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
buffer = xwl_shm_pixmap_get_wl_buffer(pixmap);
if (xwl_screen->glamor)
xwl_glamor_post_damage(xwl_window, pixmap, region);
wl_surface_attach(xwl_window->surface, buffer, 0, 0);
/* Arbitrary limit to try to avoid flooding the Wayland
......@@ -724,6 +737,11 @@ xwl_screen_post_damage(struct xwl_screen *xwl_screen)
if (!xwl_window->allow_commits)
if (!xwl_glamor_allow_commits(xwl_window))
......@@ -922,6 +940,9 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
struct xwl_screen *xwl_screen;
Pixel red_mask, blue_mask, green_mask;
int ret, bpc, green_bpc, i;
Bool use_eglstreams = FALSE;
xwl_screen = calloc(1, sizeof *xwl_screen);
if (xwl_screen == NULL)
......@@ -964,10 +985,23 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
else if (strcmp(argv[i], "-shm") == 0) {
xwl_screen->glamor = 0;
else if (strcmp(argv[i], "-eglstream") == 0) {
use_eglstreams = TRUE;
if (xwl_screen->glamor) {
if (use_eglstreams) {
if (!xwl_glamor_init_eglstream(xwl_screen)) {
ErrorF("xwayland glamor: failed to setup eglstream backend, falling back to swaccel\n");
xwl_screen->glamor = 0;
} else
if (!xwl_glamor_init_gbm(xwl_screen)) {
ErrorF("xwayland glamor: failed to setup GBM backend, falling back to sw accel\n");
xwl_screen->glamor = 0;
......@@ -141,6 +141,21 @@ struct xwl_screen {
unsigned short width,
unsigned short height,
Bool *created);
/* Called by Xwayland to perform any pre-wl_surface damage routines
* that are required by the backend. If your backend is poorly
* designed and lacks the ability to render directly to a surface,
* you should implement blitting from the glamor pixmap to the wayland
* pixmap here. Otherwise, this callback is optional.
void (*post_damage)(struct xwl_window *xwl_window,
PixmapPtr pixmap, RegionPtr region);
/* Called by Xwayland to confirm with the egl backend that the given
* pixmap is completely setup and ready for display on-screen. This
* callback is optional.
Bool (*allow_commits)(struct xwl_window *xwl_window);
} egl_backend;
struct glamor_context *glamor_ctx;
......@@ -412,6 +427,9 @@ void xwl_glamor_init_wl_registry(struct xwl_screen *xwl_screen,
struct wl_registry *registry,
uint32_t id, const char *interface,
uint32_t version);
void xwl_glamor_post_damage(struct xwl_window *xwl_window,
PixmapPtr pixmap, RegionPtr region);
Bool xwl_glamor_allow_commits(struct xwl_window *xwl_window);
Bool xwl_present_init(ScreenPtr screen);
......@@ -423,6 +441,13 @@ void xwl_screen_release_tablet_manager(struct xwl_screen *xwl_screen);
void xwl_output_get_xdg_output(struct xwl_output *xwl_output);
void xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen);
void xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen);
Bool xwl_glamor_egl_supports_device_probing(void);
void **xwl_glamor_egl_get_devices(int *num_devices);
Bool xwl_glamor_egl_device_has_egl_extensions(void *device,
const char **ext_list,
size_t size);
#ifdef XV
/* glamor Xv Adaptor */
Bool xwl_glamor_xv_init(ScreenPtr pScreen);
......@@ -434,6 +459,20 @@ void xwlVidModeExtensionInit(void);
Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen);
static inline Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen)
return FALSE;
Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen);
static inline Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen)
return FALSE;
......@@ -79,9 +79,9 @@ conf_data.set('GLXEXT', build_glx)
conf_data.set('GLAMOR', build_glamor)
conf_data.set('GLAMOR_HAS_GBM', gbm_dep.found())
gbm_dep.found() and gbm_dep.version().version_compare('>= 10.6'))
build_glamor and gbm_dep.found() and gbm_dep.version().version_compare('>= 10.6'))
gbm_dep.found() and gbm_dep.version().version_compare('>= 17.1'))
build_glamor and gbm_dep.found() and gbm_dep.version().version_compare('>= 17.1'))
conf_data.set_quoted('SERVER_MISC_CONFIG_PATH', serverconfigdir)
conf_data.set_quoted('PROJECTROOT', get_option('prefix'))
......@@ -360,7 +360,8 @@ configure_file(output : 'xwin-config.h',
configuration : xwin_data)
xwayland_data = configuration_data()
xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and gbm_dep.found())
xwayland_data.set('XWL_HAS_GLAMOR', build_glamor and (gbm_dep.found() or build_eglstream))
xwayland_data.set('XWL_HAS_EGLSTREAM', build_eglstream)
configure_file(output : 'xwayland-config.h',
input : '',
......@@ -7,4 +7,7 @@
/* Build glamor support for Xwayland */
/* Build eglstream support for Xwayland */
#endif /* _XWAYLAND_CONFIG_H_ */
......@@ -6,3 +6,6 @@
/* Build glamor support for Xwayland */
#mesondefine XWL_HAS_GLAMOR
/* Build eglstream support for Xwayland */
#mesondefine XWL_HAS_EGLSTREAM
......@@ -287,6 +287,21 @@ if build_glamor
epoxy_dep = dependency('epoxy', required: false)
eglstream_option = get_option('xwayland_eglstream')
if build_xwayland and build_glamor
eglstream_dep = dependency('wayland-eglstream-protocols', required:false)
if eglstream_option == 'auto'
build_eglstream = eglstream_dep.found()
build_eglstream = eglstream_option == 'true'
if build_eglstream and not eglstream_dep.found()
error('glamor EGLStream support requested, but wayland-eglstream-protocols not found')
build_eglstream = false
# XXX: Add more sha1 options, because Linux is about choice
sha1_dep = nettle_dep
......@@ -6,6 +6,8 @@ option('xwayland', type: 'combo', choices: ['true', 'false', 'auto'], value: 'au
description: 'Enable XWayland X server')
option('glamor', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
description: 'Enable glamor (default yes for Xorg/Xwayland builds)')
option('xwayland_eglstream', type: 'combo', choices: ['true', 'false', 'auto'],
value: 'auto', description: 'Enable EGLStream support for glamor on Xwayland')
option('xnest', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto',
description: 'Enable Xnest nested X server')
option('dmx', type: 'boolean', value: false,
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment