summaryrefslogtreecommitdiff
path: root/hw/xwayland
diff options
context:
space:
mode:
Diffstat (limited to 'hw/xwayland')
-rw-r--r--hw/xwayland/Makefile.am24
-rw-r--r--hw/xwayland/meson.build25
-rw-r--r--hw/xwayland/xwayland-glamor-eglstream.c830
-rw-r--r--hw/xwayland/xwayland-glamor-gbm.c6
-rw-r--r--hw/xwayland/xwayland-glamor.c113
-rw-r--r--hw/xwayland/xwayland-present.c9
-rw-r--r--hw/xwayland/xwayland.c34
-rw-r--r--hw/xwayland/xwayland.h39
8 files changed, 1066 insertions, 14 deletions
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 <lyude@redhat.com>
+ *
+ */
+
+#include "xwayland.h"
+
+#include "wayland-eglstream-client-protocol.h"
+#include "wayland-eglstream-controller-client-protocol.h"
+
+#define MESA_EGL_NO_X11_HEADERS
+#include <glamor_egl.h>
+#include <glamor.h>
+#include <glamor_transform.h>
+#include <glamor_transfer.h>
+
+#include <xf86drm.h>
+
+#include <epoxy/egl.h>
+
+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 <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,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
@@ -43,6 +43,94 @@ xwl_glamor_egl_make_current(struct glamor_context *glamor_ctx)
}
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)
{
struct xwl_screen *xwl_screen = xwl_screen_get(screen);
@@ -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