diff --git a/configure.ac b/configure.ac index b20eda12f..3f9752572 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) fi + 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 + XWAYLAND_EGLSTREAM=yes + fi + fi + + 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.]) + fi + + if test "x$have_wl_eglstream" = xno; then + AC_MSG_ERROR([Xwayland eglstream support requires wayland-eglstream-protocols >= 1.0.2]) + fi + + AC_SUBST(WAYLAND_EGLSTREAM_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-eglstream-protocols`) + AC_DEFINE(XWL_HAS_EGLSTREAM, 1, + [Build xwayland with eglstream support]) + fi + XWAYLAND_LIBS="$FB_LIB $FIXES_LIB $MI_LIB $XEXT_LIB $DBE_LIB $RECORD_LIB $GLX_LIBS $RANDR_LIB $RENDER_LIB $DAMAGE_LIB $DRI3_LIB $PRESENT_LIB $MIEXT_SYNC_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $MAIN_LIB $DIX_LIB $OS_LIB" XWAYLAND_SYS_LIBS="$XWAYLANDMODULES_LIBS $GLX_SYS_LIBS" AC_SUBST([XWAYLAND_LIBS]) @@ -2406,6 +2429,7 @@ if test "x$XWAYLAND" = xyes; then AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`) fi +AM_CONDITIONAL(XWAYLAND_EGLSTREAM, [test "x$XWAYLAND_EGLSTREAM" = "xyes"]) dnl and the rest of these are generic, so they're in config.h diff --git a/hw/xwayland/Makefile.am b/hw/xwayland/Makefile.am index 3fd980d0e..bc1cb8506 100644 --- a/hw/xwayland/Makefile.am +++ b/hw/xwayland/Makefile.am @@ -42,6 +42,11 @@ Xwayland_SOURCES += \ xwayland-glamor-xv.c endif +if XWAYLAND_EGLSTREAM +Xwayland_SOURCES += \ + xwayland-glamor-eglstream.c +endif + glamor_built_sources = \ drm-client-protocol.h \ drm-protocol.c @@ -68,12 +73,19 @@ Xwayland_built_sources += \ linux-dmabuf-unstable-v1-client-protocol.h \ linux-dmabuf-unstable-v1-protocol.c +if XWAYLAND_EGLSTREAM +Xwayland_built_sources += \ + wayland-eglstream-client-protocol.h \ + wayland-eglstream-protocol.c \ + wayland-eglstream-controller-client-protocol.h \ + wayland-eglstream-controller-protocol.c +endif + nodist_Xwayland_SOURCES = $(Xwayland_built_sources) CLEANFILES = $(Xwayland_built_sources) EXTRA_DIST = drm.xml - $(Xwayland_SOURCES): $(Xwayland_built_sources) relink: @@ -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 $(AM_V_GEN)$(WAYLAND_SCANNER) @SCANNER_ARG@ < $< > $@ diff --git a/hw/xwayland/meson.build b/hw/xwayland/meson.build index ef4379aab..36bf2133a 100644 --- a/hw/xwayland/meson.build +++ b/hw/xwayland/meson.build @@ -51,12 +51,25 @@ srcs += code.process(xdg_output_xml) srcs += code.process(dmabuf_xml) xwayland_glamor = [] -if gbm_dep.found() - srcs += [ - 'xwayland-glamor.c', - 'xwayland-glamor-gbm.c', - 'xwayland-present.c', - ] +eglstream_srcs = [] +if build_glamor + srcs += 'xwayland-glamor.c' + if gbm_dep.found() + srcs += 'xwayland-glamor-gbm.c' + endif + 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' + endif + srcs += 'xwayland-present.c' if build_xv srcs += 'xwayland-glamor-xv.c' endif diff --git a/hw/xwayland/xwayland-glamor-eglstream.c b/hw/xwayland/xwayland-glamor-eglstream.c new file mode 100644 index 000000000..8dd1cc304 --- /dev/null +++ b/hw/xwayland/xwayland-glamor-eglstream.c @@ -0,0 +1,830 @@ +/* + * 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 + * + */ + +#include "xwayland.h" + +#include "wayland-eglstream-client-protocol.h" +#include "wayland-eglstream-controller-client-protocol.h" + +#define MESA_EGL_NO_X11_HEADERS +#include +#include +#include +#include + +#include + +#include + +struct xwl_eglstream_pending_stream { + PixmapPtr pixmap; + WindowPtr window; + + struct xwl_pixmap *xwl_pixmap; + struct wl_callback *cb; + + Bool is_valid; + + struct xorg_list link; +}; + +struct xwl_eglstream_private { + EGLDeviceEXT egl_device; + struct wl_eglstream_display *display; + struct wl_eglstream_controller *controller; + uint32_t display_caps; + + EGLConfig config; + + SetWindowPixmapProcPtr SetWindowPixmap; + + struct xorg_list pending_streams; + + Bool have_egl_damage; + + GLint blit_prog; + GLuint blit_vao; + GLuint blit_vbo; + GLuint blit_is_rgba_pos; +}; + +struct xwl_pixmap { + struct wl_buffer *buffer; + struct xwl_screen *xwl_screen; + + /* The stream and associated resources have their own lifetime seperate + * from the pixmap's */ + int refcount; + + EGLStreamKHR stream; + EGLSurface surface; +}; + +static DevPrivateKeyRec xwl_eglstream_private_key; +static DevPrivateKeyRec xwl_eglstream_window_private_key; + +static inline struct xwl_eglstream_private * +xwl_eglstream_get(struct xwl_screen *xwl_screen) +{ + return dixLookupPrivate(&xwl_screen->screen->devPrivates, + &xwl_eglstream_private_key); +} + +static inline struct xwl_eglstream_pending_stream * +xwl_eglstream_window_get_pending(WindowPtr window) +{ + return dixLookupPrivate(&window->devPrivates, + &xwl_eglstream_window_private_key); +} + +static inline void +xwl_eglstream_window_set_pending(WindowPtr window, + struct xwl_eglstream_pending_stream *stream) +{ + dixSetPrivate(&window->devPrivates, + &xwl_eglstream_window_private_key, stream); +} + +static GLint +xwl_eglstream_compile_glsl_prog(GLenum type, const char *source) +{ + GLint ok; + GLint prog; + + prog = glCreateShader(type); + glShaderSource(prog, 1, (const GLchar **) &source, NULL); + glCompileShader(prog); + glGetShaderiv(prog, GL_COMPILE_STATUS, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetShaderiv(prog, GL_INFO_LOG_LENGTH, &size); + info = malloc(size); + if (info) { + glGetShaderInfoLog(prog, size, NULL, info); + ErrorF("Failed to compile %s: %s\n", + type == GL_FRAGMENT_SHADER ? "FS" : "VS", info); + ErrorF("Program source:\n%s", source); + free(info); + } + else + ErrorF("Failed to get shader compilation info.\n"); + FatalError("GLSL compile failure\n"); + } + + return prog; +} + +static GLuint +xwl_eglstream_build_glsl_prog(GLuint vs, GLuint fs) +{ + GLint ok; + GLuint prog; + + prog = glCreateProgram(); + glAttachShader(prog, vs); + glAttachShader(prog, fs); + + glLinkProgram(prog); + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (!ok) { + GLchar *info; + GLint size; + + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); + info = malloc(size); + + glGetProgramInfoLog(prog, size, NULL, info); + ErrorF("Failed to link: %s\n", info); + FatalError("GLSL link failure\n"); + } + + return prog; +} + +static void +xwl_eglstream_cleanup(struct xwl_screen *xwl_screen) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + + if (xwl_eglstream->display) + wl_eglstream_display_destroy(xwl_eglstream->display); + if (xwl_eglstream->controller) + wl_eglstream_controller_destroy(xwl_eglstream->controller); + if (xwl_eglstream->blit_prog) { + glDeleteProgram(xwl_eglstream->blit_prog); + glDeleteBuffers(1, &xwl_eglstream->blit_vbo); + } + + free(xwl_eglstream); +} + +static void +xwl_eglstream_unref_pixmap_stream(struct xwl_pixmap *xwl_pixmap) +{ + struct xwl_screen *xwl_screen = xwl_pixmap->xwl_screen; + + if (--xwl_pixmap->refcount >= 1) + return; + + /* If we're using this stream in the current egl context, unbind it so the + * driver doesn't keep it around until the next eglMakeCurrent() + * don't have to keep it around until something else changes the surface + */ + xwl_glamor_egl_make_current(xwl_screen); + if (eglGetCurrentSurface(EGL_READ) == xwl_pixmap->surface || + eglGetCurrentSurface(EGL_DRAW) == xwl_pixmap->surface) { + eglMakeCurrent(xwl_screen->egl_display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + xwl_screen->egl_context); + } + + if (xwl_pixmap->surface) + eglDestroySurface(xwl_screen->egl_display, xwl_pixmap->surface); + + eglDestroyStreamKHR(xwl_screen->egl_display, xwl_pixmap->stream); + + wl_buffer_destroy(xwl_pixmap->buffer); + free(xwl_pixmap); +} + +static Bool +xwl_glamor_eglstream_destroy_pixmap(PixmapPtr pixmap) +{ + struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); + + if (xwl_pixmap && pixmap->refcnt == 1) + xwl_eglstream_unref_pixmap_stream(xwl_pixmap); + + return glamor_destroy_pixmap(pixmap); +} + +static struct wl_buffer * +xwl_glamor_eglstream_get_wl_buffer_for_pixmap(PixmapPtr pixmap, + unsigned short width, + unsigned short height, + Bool *created) +{ + /* XXX created? */ + return xwl_pixmap_get(pixmap)->buffer; +} + +static void +xwl_eglstream_set_window_pixmap(WindowPtr window, PixmapPtr pixmap) +{ + struct xwl_screen *xwl_screen = xwl_screen_get(window->drawable.pScreen); + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + struct xwl_eglstream_pending_stream *pending; + + pending = xwl_eglstream_window_get_pending(window); + if (pending) { + /* The pixmap for this window has changed before the compositor + * finished attaching the consumer for the window's pixmap's original + * eglstream. A producer can no longer be attached, so the stream's + * useless + */ + pending->is_valid = FALSE; + + /* The compositor may still be using the stream, so we can't destroy + * it yet. We'll only have a guarantee that the stream is safe to + * destroy once we receive the pending wl_display_sync() for this + * stream + */ + pending->xwl_pixmap->refcount++; + } + + xwl_screen->screen->SetWindowPixmap = xwl_eglstream->SetWindowPixmap; + (*xwl_screen->screen->SetWindowPixmap)(window, pixmap); + xwl_eglstream->SetWindowPixmap = xwl_screen->screen->SetWindowPixmap; + xwl_screen->screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap; +} + +/* Because we run asynchronously with our wayland compositor, it's possible + * that an X client event could cause us to begin creating a stream for a + * pixmap/window combo before the stream for the pixmap this window + * previously used has been fully initialized. An example: + * + * - Start processing X client events. + * - X window receives resize event, causing us to create a new pixmap and + * begin creating the corresponding eglstream. This pixmap is known as + * pixmap A. + * - X window receives another resize event, and again changes it's current + * pixmap causing us to create another corresponding eglstream for the same + * window. This pixmap is known as pixmap B. + * - Start handling events from the wayland compositor. + * + * Since both pixmap A and B will have scheduled wl_display_sync events to + * indicate when their respective streams are connected, we will receive each + * callback in the original order the pixmaps were created. This means the + * following would happen: + * + * - Receive pixmap A's stream callback, attach it's stream to the surface of + * the window that just orphaned it. + * - Receive pixmap B's stream callback, fall over and fail because the + * window's surface now incorrectly has pixmap A's stream attached to it. + * + * We work around this problem by keeping a queue of pending streams, and + * only allowing one queue entry to exist for each window. In the scenario + * listed above, this should happen: + * + * - Begin processing X events... + * - A window is resized, causing us to add an eglstream (known as eglstream + * A) waiting for it's consumer to finish attachment to be added to the + * queue. + * - Resize on same window happens. We invalidate the previously pending + * stream and add another one to the pending queue (known as eglstream B). + * - Begin processing Wayland events... + * - Receive invalidated callback from compositor for eglstream A, destroy + * stream. + * - Receive callback from compositor for eglstream B, create producer. + * - Success! + */ +static void +xwl_eglstream_consumer_ready_callback(void *data, + struct wl_callback *callback, + uint32_t time) +{ + struct xwl_screen *xwl_screen = data; + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + struct xwl_pixmap *xwl_pixmap; + struct xwl_eglstream_pending_stream *pending; + Bool found = FALSE; + + wl_callback_destroy(callback); + + xorg_list_for_each_entry(pending, &xwl_eglstream->pending_streams, link) { + if (pending->cb == callback) { + found = TRUE; + break; + } + } + assert(found); + + if (!pending->is_valid) { + xwl_eglstream_unref_pixmap_stream(pending->xwl_pixmap); + goto out; + } + + xwl_glamor_egl_make_current(xwl_screen); + + xwl_pixmap = pending->xwl_pixmap; + xwl_pixmap->surface = eglCreateStreamProducerSurfaceKHR( + xwl_screen->egl_display, xwl_eglstream->config, + xwl_pixmap->stream, (int[]) { + EGL_WIDTH, pending->pixmap->drawable.width, + EGL_HEIGHT, pending->pixmap->drawable.height, + EGL_NONE + }); + + DebugF("eglstream: win %d completes eglstream for pixmap %p, congrats!\n", + pending->window->drawable.id, pending->pixmap); + + xwl_eglstream_window_set_pending(pending->window, NULL); +out: + xorg_list_del(&pending->link); + free(pending); +} + +static const struct wl_callback_listener consumer_ready_listener = { + xwl_eglstream_consumer_ready_callback +}; + +static void +xwl_eglstream_queue_pending_stream(struct xwl_screen *xwl_screen, + WindowPtr window, PixmapPtr pixmap) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + struct xwl_eglstream_pending_stream *pending_stream; + +#ifdef DEBUG + if (!xwl_eglstream_window_get_pending(window)) + DebugF("eglstream: win %d begins new eglstream for pixmap %p\n", + window->drawable.id, pixmap); + else + DebugF("eglstream: win %d interrupts and replaces pending eglstream for pixmap %p\n", + window->drawable.id, pixmap); +#endif + + pending_stream = malloc(sizeof(*pending_stream)); + pending_stream->window = window; + pending_stream->pixmap = pixmap; + pending_stream->xwl_pixmap = xwl_pixmap_get(pixmap); + pending_stream->is_valid = TRUE; + xorg_list_init(&pending_stream->link); + xorg_list_add(&pending_stream->link, &xwl_eglstream->pending_streams); + xwl_eglstream_window_set_pending(window, pending_stream); + + pending_stream->cb = wl_display_sync(xwl_screen->display); + wl_callback_add_listener(pending_stream->cb, &consumer_ready_listener, + xwl_screen); +} + +static void +xwl_eglstream_buffer_release_callback(void *data, struct wl_buffer *wl_buffer) +{ + xwl_eglstream_unref_pixmap_stream(data); +} + +static const struct wl_buffer_listener xwl_eglstream_buffer_release_listener = { + xwl_eglstream_buffer_release_callback +}; + +static void +xwl_eglstream_create_pending_stream(struct xwl_screen *xwl_screen, + WindowPtr window, PixmapPtr pixmap) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + struct xwl_pixmap *xwl_pixmap; + struct xwl_window *xwl_window = xwl_window_from_window(window); + struct wl_array stream_attribs; + int stream_fd = -1; + + xwl_pixmap = calloc(sizeof(*xwl_pixmap), 1); + if (!xwl_pixmap) + FatalError("Not enough memory to create pixmap\n"); + xwl_pixmap_set_private(pixmap, xwl_pixmap); + + xwl_glamor_egl_make_current(xwl_screen); + + xwl_pixmap->xwl_screen = xwl_screen; + xwl_pixmap->refcount = 1; + xwl_pixmap->stream = eglCreateStreamKHR(xwl_screen->egl_display, NULL); + stream_fd = eglGetStreamFileDescriptorKHR(xwl_screen->egl_display, + xwl_pixmap->stream); + + wl_array_init(&stream_attribs); + xwl_pixmap->buffer = + wl_eglstream_display_create_stream(xwl_eglstream->display, + pixmap->drawable.width, + pixmap->drawable.height, + stream_fd, + WL_EGLSTREAM_HANDLE_TYPE_FD, + &stream_attribs); + + wl_buffer_add_listener(xwl_pixmap->buffer, + &xwl_eglstream_buffer_release_listener, + xwl_pixmap); + + wl_eglstream_controller_attach_eglstream_consumer( + xwl_eglstream->controller, xwl_window->surface, xwl_pixmap->buffer); + + xwl_eglstream_queue_pending_stream(xwl_screen, window, pixmap); + + close(stream_fd); +} + +static Bool +xwl_glamor_eglstream_allow_commits(struct xwl_window *xwl_window) +{ + struct xwl_screen *xwl_screen = xwl_window->xwl_screen; + struct xwl_eglstream_pending_stream *pending = + xwl_eglstream_window_get_pending(xwl_window->window); + PixmapPtr pixmap = + (*xwl_screen->screen->GetWindowPixmap)(xwl_window->window); + struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); + + if (xwl_pixmap) { + if (pending) { + /* Wait for the compositor to finish connecting the consumer for + * this eglstream */ + if (pending->is_valid) + return FALSE; + + /* The pixmap for this window was changed before the compositor + * finished connecting the eglstream for the window's previous + * pixmap. Begin creating a new eglstream. */ + } else { + return TRUE; + } + } + + /* Glamor pixmap has no backing stream yet; begin making one and disallow + * commits until then + */ + xwl_eglstream_create_pending_stream(xwl_screen, xwl_window->window, + pixmap); + + return FALSE; +} + +static void +xwl_glamor_eglstream_post_damage(struct xwl_window *xwl_window, + PixmapPtr pixmap, RegionPtr region) +{ + struct xwl_screen *xwl_screen = xwl_window->xwl_screen; + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + struct xwl_pixmap *xwl_pixmap = xwl_pixmap_get(pixmap); + BoxPtr box = RegionExtents(region); + EGLint egl_damage[] = { + box->x1, box->y1, + box->x2 - box->x1, box->y2 - box->y1 + }; + GLint saved_vao; + + /* Unbind the framebuffer BEFORE binding the EGLSurface, otherwise we + * won't actually draw to it + */ + xwl_glamor_egl_make_current(xwl_screen); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + if (eglGetCurrentSurface(EGL_READ) != xwl_pixmap->surface || + eglGetCurrentSurface(EGL_DRAW) != xwl_pixmap->surface) + eglMakeCurrent(xwl_screen->egl_display, + xwl_pixmap->surface, xwl_pixmap->surface, + xwl_screen->egl_context); + + /* Save current GL state */ + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao); + + /* Setup our GL state */ + glUseProgram(xwl_eglstream->blit_prog); + glViewport(0, 0, pixmap->drawable.width, pixmap->drawable.height); + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(xwl_eglstream->blit_vao); + glBindTexture(GL_TEXTURE_2D, glamor_get_pixmap_texture(pixmap)); + + glUniform1i(xwl_eglstream->blit_is_rgba_pos, + pixmap->drawable.depth >= 32); + + /* Blit rendered image into EGLStream surface */ + glDrawBuffer(GL_BACK); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + if (xwl_eglstream->have_egl_damage) + eglSwapBuffersWithDamageKHR(xwl_screen->egl_display, + xwl_pixmap->surface, egl_damage, 1); + else + eglSwapBuffers(xwl_screen->egl_display, xwl_pixmap->surface); + + /* Restore previous state */ + glBindVertexArray(saved_vao); + glBindTexture(GL_TEXTURE_2D, 0); + + /* After this we will hand off the eglstream's wl_buffer to the + * compositor, which will own it until it sends a release() event. */ + xwl_pixmap->refcount++; +} + +static void +xwl_eglstream_display_handle_caps(void *data, + struct wl_eglstream_display *disp, + int32_t caps) +{ + xwl_eglstream_get(data)->display_caps = caps; +} + +static void +xwl_eglstream_display_handle_swapinterval_override(void *data, + struct wl_eglstream_display *disp, + int32_t swapinterval, + struct wl_buffer *stream) +{ +} + +const struct wl_eglstream_display_listener eglstream_display_listener = { + .caps = xwl_eglstream_display_handle_caps, + .swapinterval_override = xwl_eglstream_display_handle_swapinterval_override, +}; + +static void +xwl_glamor_eglstream_init_wl_registry(struct xwl_screen *xwl_screen, + struct wl_registry *wl_registry, + const char *name, + uint32_t id, uint32_t version) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + + if (strcmp(name, "wl_eglstream_display") == 0) { + xwl_eglstream->display = wl_registry_bind( + wl_registry, id, &wl_eglstream_display_interface, version); + + wl_eglstream_display_add_listener(xwl_eglstream->display, + &eglstream_display_listener, + xwl_screen); + } else if (strcmp(name, "wl_eglstream_controller") == 0) { + xwl_eglstream->controller = wl_registry_bind( + wl_registry, id, &wl_eglstream_controller_interface, version); + } +} + +static inline void +xwl_eglstream_init_shaders(struct xwl_screen *xwl_screen) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + GLint fs, vs, attrib; + GLuint vbo; + + const char *blit_vs_src = + "attribute vec2 texcoord;\n" + "attribute vec2 position;\n" + "varying vec2 t;\n" + "void main() {\n" + " t = texcoord;\n" + " gl_Position = vec4(position, 0, 1);\n" + "}"; + + const char *blit_fs_src = + "varying vec2 t;\n" + "uniform sampler2D s;\n" + "uniform bool is_rgba;\n" + "void main() {\n" + " if (is_rgba)\n" + " gl_FragColor = texture2D(s, t);\n" + " else\n" + " gl_FragColor = vec4(texture2D(s, t).rgb, 1.0);\n" + "}"; + + static const float position[] = { + /* position */ + -1, -1, + 1, -1, + 1, 1, + -1, 1, + /* texcoord */ + 0, 1, + 1, 1, + 1, 0, + 0, 0, + }; + + vs = xwl_eglstream_compile_glsl_prog(GL_VERTEX_SHADER, blit_vs_src); + fs = xwl_eglstream_compile_glsl_prog(GL_FRAGMENT_SHADER, blit_fs_src); + + xwl_eglstream->blit_prog = xwl_eglstream_build_glsl_prog(vs, fs); + glDeleteShader(vs); + glDeleteShader(fs); + + /* Create the blitter's vao */ + glGenVertexArrays(1, &xwl_eglstream->blit_vao); + glBindVertexArray(xwl_eglstream->blit_vao); + + /* Set the data for both position and texcoord in the vbo */ + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW); + xwl_eglstream->blit_vbo = vbo; + + /* Define each shader attribute's data location in our vbo */ + attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "position"); + glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0, NULL); + glEnableVertexAttribArray(attrib); + + attrib = glGetAttribLocation(xwl_eglstream->blit_prog, "texcoord"); + glVertexAttribPointer(attrib, 2, GL_FLOAT, TRUE, 0, + (void*)(sizeof(float) * 8)); + glEnableVertexAttribArray(attrib); + + /* Save the location of uniforms we'll set later */ + xwl_eglstream->blit_is_rgba_pos = + glGetUniformLocation(xwl_eglstream->blit_prog, "is_rgba"); +} + +static Bool +xwl_glamor_eglstream_init_egl(struct xwl_screen *xwl_screen) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + EGLConfig config; + const EGLint attrib_list[] = { + 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_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, + EGL_NONE + }; + const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE, + }; + int n; + + xwl_screen->egl_display = glamor_egl_get_display( + EGL_PLATFORM_DEVICE_EXT, xwl_eglstream->egl_device); + if (!xwl_screen->egl_display) + goto error; + + if (!eglInitialize(xwl_screen->egl_display, NULL, NULL)) { + xwl_screen->egl_display = NULL; + goto error; + } + + eglChooseConfig(xwl_screen->egl_display, config_attribs, &config, 1, &n); + if (!n) { + ErrorF("No acceptable EGL configs found\n"); + goto error; + } + + xwl_eglstream->config = config; +#if 0 + xwl_screen->formats = + XWL_FORMAT_RGB565 | XWL_FORMAT_XRGB8888 | XWL_FORMAT_ARGB8888; +#endif + + eglBindAPI(EGL_OPENGL_API); + xwl_screen->egl_context = eglCreateContext( + xwl_screen->egl_display, config, EGL_NO_CONTEXT, attrib_list); + if (xwl_screen->egl_context == EGL_NO_CONTEXT) { + ErrorF("Failed to create main EGL context: 0x%x\n", eglGetError()); + goto error; + } + + if (!eglMakeCurrent(xwl_screen->egl_display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + xwl_screen->egl_context)) { + ErrorF("Failed to make EGL context current\n"); + goto error; + } + + xwl_eglstream->have_egl_damage = + epoxy_has_egl_extension(xwl_screen->egl_display, + "EGL_KHR_swap_buffers_with_damage"); + if (!xwl_eglstream->have_egl_damage) + ErrorF("Driver lacks EGL_KHR_swap_buffers_with_damage, performance " + "will be affected\n"); + + xwl_eglstream_init_shaders(xwl_screen); + + return TRUE; +error: + xwl_eglstream_cleanup(xwl_screen); + return FALSE; +} + +static Bool +xwl_glamor_eglstream_init_screen(struct xwl_screen *xwl_screen) +{ + struct xwl_eglstream_private *xwl_eglstream = + xwl_eglstream_get(xwl_screen); + ScreenPtr screen = xwl_screen->screen; + + if (!xwl_eglstream->controller) { + ErrorF("No eglstream controller was exposed in the wayland registry. " + "This means your version of nvidia's EGL wayland libraries " + "are too old, as we require support for this.\n"); + xwl_eglstream_cleanup(xwl_screen); + return FALSE; + } + + /* We can just let glamor handle CreatePixmap */ + screen->DestroyPixmap = xwl_glamor_eglstream_destroy_pixmap; + + xwl_eglstream->SetWindowPixmap = screen->SetWindowPixmap; + screen->SetWindowPixmap = xwl_eglstream_set_window_pixmap; + + if (!dixRegisterPrivateKey(&xwl_eglstream_window_private_key, + PRIVATE_WINDOW, 0)) + return FALSE; + + return TRUE; +} + +static EGLDeviceEXT +xwl_eglstream_get_device(struct xwl_screen *xwl_screen) +{ + void **devices = NULL; + const char *exts[] = { + "EGL_KHR_stream", + "EGL_KHR_stream_producer_eglsurface", + }; + int num_devices, i; + EGLDeviceEXT device = EGL_NO_DEVICE_EXT; + + /* No device specified by the user, so find one ourselves */ + devices = xwl_glamor_egl_get_devices(&num_devices); + if (!devices) + goto out; + + for (i = 0; i < num_devices; i++) { + if (xwl_glamor_egl_device_has_egl_extensions(devices[i], exts, + ARRAY_SIZE(exts))) { + device = devices[i]; + break; + } + } + + free(devices); +out: + if (!device) + ErrorF("glamor: No eglstream capable devices found\n"); + return device; +} + +Bool +xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen) +{ + struct xwl_eglstream_private *xwl_eglstream; + EGLDeviceEXT egl_device; + + egl_device = xwl_eglstream_get_device(xwl_screen); + if (egl_device == EGL_NO_DEVICE_EXT) + return FALSE; + + if (!dixRegisterPrivateKey(&xwl_eglstream_private_key, PRIVATE_SCREEN, 0)) + return FALSE; + + xwl_eglstream = calloc(sizeof(*xwl_eglstream), 1); + if (!xwl_eglstream) { + ErrorF("Failed to allocate memory required to init eglstream support\n"); + return FALSE; + } + + dixSetPrivate(&xwl_screen->screen->devPrivates, + &xwl_eglstream_private_key, xwl_eglstream); + + xwl_eglstream->egl_device = egl_device; + xorg_list_init(&xwl_eglstream->pending_streams); + + xwl_screen->egl_backend.init_egl = xwl_glamor_eglstream_init_egl; + xwl_screen->egl_backend.init_wl_registry = xwl_glamor_eglstream_init_wl_registry; + xwl_screen->egl_backend.init_screen = xwl_glamor_eglstream_init_screen; + xwl_screen->egl_backend.get_wl_buffer_for_pixmap = xwl_glamor_eglstream_get_wl_buffer_for_pixmap; + xwl_screen->egl_backend.post_damage = xwl_glamor_eglstream_post_damage; + xwl_screen->egl_backend.allow_commits = xwl_glamor_eglstream_allow_commits; + + ErrorF("glamor: Using nvidia's eglstream interface, direct rendering impossible.\n"); + ErrorF("glamor: Performance may be affected. Ask your vendor to support GBM!\n"); + return TRUE; +} diff --git a/hw/xwayland/xwayland-glamor-gbm.c b/hw/xwayland/xwayland-glamor-gbm.c index c03ba3da1..4f7062599 100644 --- a/hw/xwayland/xwayland-glamor-gbm.c +++ b/hw/xwayland/xwayland-glamor-gbm.c @@ -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_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx); - } - + xwl_glamor_egl_make_current(xwl_screen); xwl_pixmap->bo = bo; xwl_pixmap->buffer = NULL; xwl_pixmap->image = eglCreateImageKHR(xwl_screen->egl_display, diff --git a/hw/xwayland/xwayland-glamor.c b/hw/xwayland/xwayland-glamor.c index 7b24ce7e4..2bd16e7ac 100644 --- a/hw/xwayland/xwayland-glamor.c +++ b/hw/xwayland/xwayland-glamor.c @@ -32,7 +32,7 @@ #include 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, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -42,6 +42,94 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx) FatalError("Failed to make EGL context current\n"); } +void +xwl_glamor_egl_make_current(struct xwl_screen *xwl_screen) +{ + if (lastGLContext == xwl_screen->glamor_ctx) + return; + + lastGLContext = xwl_screen->glamor_ctx; + xwl_screen->glamor_ctx->make_current(xwl_screen->glamor_ctx); +} + +Bool +xwl_glamor_egl_supports_device_probing(void) +{ + return epoxy_has_egl() && + epoxy_has_egl_extension(NULL, "EGL_EXT_device_base"); +} + +void ** +xwl_glamor_egl_get_devices(int *num_devices) +{ +#ifdef XWL_HAS_EGLSTREAM + 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")) + continue; + + 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; + +error: + free(devices); +#endif + return NULL; +} + +Bool +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; + break; + } + } + + eglTerminate(egl_display); + return has_exts; +} + void 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; } +void +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); +} + +Bool +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); + else + return TRUE; +} + static Bool xwl_glamor_create_screen_resources(ScreenPtr screen) { diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c index c41a8a2d1..07fdc7c18 100644 --- a/hw/xwayland/xwayland-present.c +++ b/hw/xwayland/xwayland-present.c @@ -509,5 +509,14 @@ static present_wnmd_info_rec xwl_present_info = { Bool 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); } diff --git a/hw/xwayland/xwayland.c b/hw/xwayland/xwayland.c index a5b3df791..f7e2ce931 100644 --- a/hw/xwayland/xwayland.c +++ b/hw/xwayland/xwayland.c @@ -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"); +#ifdef XWL_HAS_EGLSTREAM + ErrorF("-eglstream use eglstream backend for nvidia GPUs\n"); +#endif } int @@ -114,6 +117,11 @@ ddxProcessArgument(int argc, char *argv[], int i) else if (strcmp(argv[i], "-shm") == 0) { return 1; } +#ifdef XWL_HAS_EGLSTREAM + else if (strcmp(argv[i], "-eglstream") == 0) { + return 1; + } +#endif return 0; } @@ -678,6 +686,11 @@ xwl_window_post_damage(struct xwl_window *xwl_window) #endif buffer = xwl_shm_pixmap_get_wl_buffer(pixmap); +#ifdef XWL_HAS_GLAMOR + if (xwl_screen->glamor) + xwl_glamor_post_damage(xwl_window, pixmap, region); +#endif + 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) continue; +#ifdef XWL_HAS_GLAMOR + if (!xwl_glamor_allow_commits(xwl_window)) + continue; +#endif + xwl_window_post_damage(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; +#ifdef XWL_HAS_EGLSTREAM + Bool use_eglstreams = FALSE; +#endif 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; } +#ifdef XWL_HAS_EGLSTREAM + else if (strcmp(argv[i], "-eglstream") == 0) { + use_eglstreams = TRUE; + } +#endif } #ifdef XWL_HAS_GLAMOR if (xwl_screen->glamor) { +#ifdef XWL_HAS_EGLSTREAM + 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 +#endif 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; diff --git a/hw/xwayland/xwayland.h b/hw/xwayland/xwayland.h index 73f9c6a99..985ba9d64 100644 --- a/hw/xwayland/xwayland.h +++ b/hw/xwayland/xwayland.h @@ -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); #ifdef GLAMOR_HAS_GBM 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); #ifdef GLAMOR_HAS_GBM Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen); +#else +static inline Bool xwl_glamor_init_gbm(struct xwl_screen *xwl_screen) +{ + return FALSE; +} +#endif + +#ifdef XWL_HAS_EGLSTREAM +Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen); +#else +static inline Bool xwl_glamor_init_eglstream(struct xwl_screen *xwl_screen) +{ + return FALSE; +} #endif #endif diff --git a/include/meson.build b/include/meson.build index d44ec4b97..06affc327 100644 --- a/include/meson.build +++ b/include/meson.build @@ -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()) conf_data.set('GLAMOR_HAS_GBM_LINEAR', - 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')) conf_data.set('GBM_BO_WITH_MODIFIERS', - 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 : 'xwayland-config.h.meson.in', diff --git a/include/xwayland-config.h.in b/include/xwayland-config.h.in index 333b53f23..9695aae6b 100644 --- a/include/xwayland-config.h.in +++ b/include/xwayland-config.h.in @@ -7,4 +7,7 @@ /* Build glamor support for Xwayland */ #undef XWL_HAS_GLAMOR +/* Build eglstream support for Xwayland */ +#undef XWL_HAS_EGLSTREAM + #endif /* _XWAYLAND_CONFIG_H_ */ diff --git a/include/xwayland-config.h.meson.in b/include/xwayland-config.h.meson.in index 6d37e8bf6..0943ff57d 100644 --- a/include/xwayland-config.h.meson.in +++ b/include/xwayland-config.h.meson.in @@ -6,3 +6,6 @@ /* Build glamor support for Xwayland */ #mesondefine XWL_HAS_GLAMOR + +/* Build eglstream support for Xwayland */ +#mesondefine XWL_HAS_EGLSTREAM diff --git a/meson.build b/meson.build index 5e794ccd0..a2e6a3d29 100644 --- a/meson.build +++ b/meson.build @@ -287,6 +287,21 @@ if build_glamor epoxy_dep = dependency('epoxy', required: false) endif +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() + else + build_eglstream = eglstream_option == 'true' + if build_eglstream and not eglstream_dep.found() + error('glamor EGLStream support requested, but wayland-eglstream-protocols not found') + endif + endif +else + build_eglstream = false +endif + # XXX: Add more sha1 options, because Linux is about choice sha1_dep = nettle_dep diff --git a/meson_options.txt b/meson_options.txt index 5c7be0e26..a296838a1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -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,