pax_global_header00006660000000000000000000000064147415303260014517gustar00rootroot0000000000000052 comment=26ba0e3ee448ff83644bc2ffbe5d06d21c60ce44 egl-wayland-1.1.18/000077500000000000000000000000001474153032600140135ustar00rootroot00000000000000egl-wayland-1.1.18/.github/000077500000000000000000000000001474153032600153535ustar00rootroot00000000000000egl-wayland-1.1.18/.github/workflows/000077500000000000000000000000001474153032600174105ustar00rootroot00000000000000egl-wayland-1.1.18/.github/workflows/arch-build.yml000066400000000000000000000007601474153032600221500ustar00rootroot00000000000000name: Arch Build on: [push, pull_request] jobs: Meson-Build: runs-on: ubuntu-24.04 container: image: archlinux:latest steps: - uses: actions/checkout@v4 - run: pacman --noconfirm -Syy - run: pacman --noconfirm -S wayland-protocols libdrm libglvnd pkgconf - run: pacman --noconfirm -S wayland eglexternalplatform - run: pacman --noconfirm -S meson ninja gcc - run: meson build - run: ninja -C build - run: ninja -C build install egl-wayland-1.1.18/.github/workflows/autoconf-build.yml000066400000000000000000000007321474153032600230500ustar00rootroot00000000000000name: Autotools GCC Build on: [push, pull_request] jobs: Meson-Build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: sudo apt update - run: sudo apt install -y wayland-protocols libdrm-dev libegl-dev - run: sudo apt install -y libwayland-dev libwayland-egl-backend-dev eglexternalplatform-dev - run: sudo apt install -y meson ninja-build gcc - run: ./autogen.sh - run: make - run: sudo make install egl-wayland-1.1.18/.github/workflows/meson-build.yml000066400000000000000000000007511474153032600223540ustar00rootroot00000000000000name: Meson GCC Build on: [push, pull_request] jobs: Meson-Build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: sudo apt update - run: sudo apt install -y wayland-protocols libdrm-dev libegl-dev - run: sudo apt install -y libwayland-dev libwayland-egl-backend-dev eglexternalplatform-dev - run: sudo apt install -y meson ninja-build gcc - run: meson build - run: ninja -C build - run: sudo ninja -C build install egl-wayland-1.1.18/.github/workflows/meson-llvm-build.yml000066400000000000000000000010471474153032600233230ustar00rootroot00000000000000name: Meson LLVM Build on: [push, pull_request] jobs: Meson-Build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: sudo apt update - run: sudo apt install -y wayland-protocols libdrm-dev libegl-dev - run: sudo apt install -y libwayland-dev libwayland-egl-backend-dev eglexternalplatform-dev - run: sudo apt install -y meson ninja-build clang - name: meson build run: meson build env: CC: clang - run: ninja -C build - run: sudo ninja -C build install egl-wayland-1.1.18/.gitignore000066400000000000000000000013651474153032600160100ustar00rootroot00000000000000# Taken from https://github.com/github/gitignore # Build system ignores # http://www.gnu.org/software/automake Makefile.in /ar-lib /mdate-sh /test-driver /ylwrap # http://www.gnu.org/software/autoconf /autom4te.cache /autoscan.log /autoscan-*.log /aclocal.m4 /compile /config.guess /config.h.in /config.sub /configure /configure.scan /depcomp /install-sh /missing /stamp-h1 # other stuff generated by us /m4/* /build/* # C ignores # Object files *.o *.ko *.obj *.elf # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects *.so *.so.* *.dylib # Executables *.out *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb egl-wayland-1.1.18/COPYING000066400000000000000000000021761474153032600150540ustar00rootroot00000000000000 Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 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 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. egl-wayland-1.1.18/Makefile.am000066400000000000000000000170731474153032600160570ustar00rootroot00000000000000 # Install libraries lib_LTLIBRARIES = libnvidia-egl-wayland.la # Include paths libnvidia_egl_wayland_la_CFLAGS = \ -I$(top_srcdir)/wayland-egl \ -I$(top_srcdir)/include \ -I$(top_builddir)/wayland-eglstream \ -I$(top_builddir)/wayland-drm # Required library flags libnvidia_egl_wayland_la_CFLAGS += \ $(PTHREAD_CFLAGS) \ $(EGL_CFLAGS) \ $(EGL_EXTERNAL_PLATFORM_CFLAGS) \ $(WAYLAND_CFLAGS) \ $(LIBDRM_CFLAGS) \ $(COMPILER_FLAG_VISIBILITY_HIDDEN) # Make sure we don't use deprecated stuff libnvidia_egl_wayland_la_CFLAGS += \ -DWL_HIDE_DEPRECATED libnvidia_egl_wayland_la_LDFLAGS = \ -shared \ -Wl,-Bsymbolic \ -ldl \ $(WAYLAND_LIBS) \ $(LIBDRM_LIBS) \ -version-number $(WAYLAND_EXTERNAL_MAJOR_VERSION):$(WAYLAND_EXTERNAL_MINOR_VERSION):$(WAYLAND_EXTERNAL_MICRO_VERSION) \ $(LINKER_FLAG_NO_UNDEFINED) libnvidia_egl_wayland_la_SOURCES = \ src/wayland-thread.c \ src/wayland-egldevice.c \ src/wayland-egldisplay.c \ src/wayland-eglstream.c \ src/wayland-eglstream-server.c \ src/wayland-eglsurface.c \ src/wayland-eglswap.c \ src/wayland-eglutils.c \ src/wayland-eglhandle.c \ src/wayland-drm.c \ src/wayland-external-exports.c libnvidia_egl_wayland_la_SOURCES += \ include/wayland-drm.h \ include/wayland-egldevice.h \ include/wayland-egldisplay.h \ include/wayland-eglhandle.h \ include/wayland-eglstream.h \ include/wayland-eglstream-server.h \ include/wayland-eglsurface.h \ include/wayland-eglsurface-internal.h \ include/wayland-eglswap.h \ include/wayland-eglutils.h \ include/wayland-external-exports.h \ include/wayland-thread.h \ wayland-egl/wayland-egl-ext.h libnvidia_egl_wayland_la_built_public_protocols = \ wayland-eglstream/wayland-eglstream-controller-protocol.c \ wayland-drm/wayland-drm-protocol.c libnvidia_egl_wayland_la_built_private_protocols = \ wayland-eglstream/wayland-eglstream-protocol.c libnvidia_egl_wayland_la_built_client_headers = \ wayland-eglstream/wayland-eglstream-client-protocol.h \ wayland-eglstream/wayland-eglstream-controller-client-protocol.h \ wayland-drm/wayland-drm-client-protocol.h libnvidia_egl_wayland_la_built_server_headers = \ wayland-eglstream/wayland-eglstream-server-protocol.h \ wayland-drm/wayland-drm-server-protocol.h libnvidia_egl_wayland_la_dmabuf_built_client_headers = \ linux-dmabuf-unstable-v1-client-protocol.h libnvidia_egl_wayland_la_dmabuf_built_private_protocols = \ linux-dmabuf-unstable-v1-protocol.c libnvidia_egl_wayland_la_drm_syncobj_built_client_headers = \ linux-drm-syncobj-v1-client-protocol.h libnvidia_egl_wayland_la_drm_syncobj_built_private_protocols = \ linux-drm-syncobj-v1-protocol.c libnvidia_egl_wayland_la_presentation_time_built_client_headers = \ presentation-time-client-protocol.h libnvidia_egl_wayland_la_presentation_time_private_protocols = \ presentation-time-protocol.c libnvidia_egl_wayland_la_built_sources = \ $(libnvidia_egl_wayland_la_built_public_protocols) \ $(libnvidia_egl_wayland_la_built_private_protocols) \ $(libnvidia_egl_wayland_la_built_client_headers) \ $(libnvidia_egl_wayland_la_built_server_headers) \ $(libnvidia_egl_wayland_la_dmabuf_built_client_headers) \ $(libnvidia_egl_wayland_la_dmabuf_built_private_protocols) \ $(libnvidia_egl_wayland_la_drm_syncobj_built_client_headers) \ $(libnvidia_egl_wayland_la_drm_syncobj_built_private_protocols) \ $(libnvidia_egl_wayland_la_presentation_time_built_client_headers) \ $(libnvidia_egl_wayland_la_presentation_time_private_protocols) nodist_libnvidia_egl_wayland_la_SOURCES = $(libnvidia_egl_wayland_la_built_sources) dist_pkgdata_DATA = \ wayland-eglstream/wayland-eglstream.xml \ wayland-eglstream/wayland-eglstream-controller.xml \ wayland-drm/wayland-drm.xml wayland_eglstream_pkgconfig_files = \ wayland-eglstream.pc \ wayland-eglstream-protocols.pc noarch_pkgconfig_DATA = $(wayland_eglstream_pkgconfig_files) CLEANFILES = \ $(libnvidia_egl_wayland_la_built_sources) \ $(wayland_eglstream_pkgconfig_files) $(libnvidia_egl_wayland_la_SOURCES): $(libnvidia_egl_wayland_la_built_sources) if WAYLAND_SCANNER_HAS_PRIVATE_CODE WAYLAND_PUBLIC_CODEGEN = public-code WAYLAND_PRIVATE_CODEGEN = private-code else WAYLAND_PUBLIC_CODEGEN = code WAYLAND_PRIVATE_CODEGEN = code endif $(libnvidia_egl_wayland_la_dmabuf_built_private_protocols):%-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linux-dmabuf/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) $(WAYLAND_PRIVATE_CODEGEN) < $< > $@ $(libnvidia_egl_wayland_la_dmabuf_built_client_headers):%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/unstable/linux-dmabuf/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@ $(libnvidia_egl_wayland_la_drm_syncobj_built_private_protocols):%-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/staging/linux-drm-syncobj/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) $(WAYLAND_PRIVATE_CODEGEN) < $< > $@ $(libnvidia_egl_wayland_la_drm_syncobj_built_client_headers):%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/staging/linux-drm-syncobj/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@ $(libnvidia_egl_wayland_la_presentation_time_private_protocols):%-protocol.c : $(WAYLAND_PROTOCOLS_DATADIR)/stable/presentation-time/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) $(WAYLAND_PRIVATE_CODEGEN) < $< > $@ $(libnvidia_egl_wayland_la_presentation_time_built_client_headers):%-client-protocol.h : $(WAYLAND_PROTOCOLS_DATADIR)/stable/presentation-time/%.xml $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@ $(libnvidia_egl_wayland_la_built_public_protocols):%-protocol.c : %.xml $(AM_V_GEN)$(WAYLAND_SCANNER) $(WAYLAND_PUBLIC_CODEGEN) < $< > $@ $(libnvidia_egl_wayland_la_built_private_protocols):%-protocol.c : %.xml $(AM_V_GEN)$(WAYLAND_SCANNER) $(WAYLAND_PRIVATE_CODEGEN) < $< > $@ $(libnvidia_egl_wayland_la_built_client_headers):%-client-protocol.h : %.xml $(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@ $(libnvidia_egl_wayland_la_built_server_headers):%-server-protocol.h : %.xml $(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@ egl-wayland-1.1.18/README.md000066400000000000000000000110331474153032600152700ustar00rootroot00000000000000Wayland EGL External Platform library ===================================== Overview -------- This is a work-in-progress implementation of a EGL External Platform library to add client-side Wayland support to EGL on top of EGLDevice and EGLStream families of extensions. This library implements an EGL External Platform interface to work along with EGL drivers that support the external platform mechanism. More information about EGL External platforms and the interface can be found at: https://github.com/NVIDIA/eglexternalplatform Building and Installing the library ----------------------------------- This library build-depends on: * EGL headers https://www.khronos.org/registry/EGL/ * Wayland libraries & protocols https://wayland.freedesktop.org/ * EGL External Platform interface https://github.com/NVIDIA/eglexternalplatform To build, run: ./autogen.sh make To install, run: make install You can also use meson build system to build and install: meson builddir cd builddir ninja ninja install *Notes*: The NVIDIA EGL driver uses a JSON-based loader to load all EGL External platforms available on the system. If this library is not installed as part of a NVIDIA driver installation, a JSON configuration file must be manually added in order to make the library work with the NVIDIA driver. The default EGL External platform JSON configuration directory is: `/usr/share/egl/egl_external_platform.d/` Acknowledgements ---------------- Thanks to James Jones for the original implementation of the Wayland EGL platform. ### Wayland EGL External platform library ### The Wayland EGL External platform library itself is licensed as follows: Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 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 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. ### buildconf ### The Wayland EGL External platform library uses the buildconf autotools bootstrapping script 'autogen.sh': http://freecode.com/projects/buildconf This script carries the following copyright notice: Copyright (c) 2005-2009 United States Government as represented by the U.S. Army Research Laboratory. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. egl-wayland-1.1.18/autogen.sh000077500000000000000000000003071474153032600160140ustar00rootroot00000000000000#!/bin/sh test -n "$srcdir" || srcdir=`dirname "$0"` test -n "$srcdir" || srcdir=. ( cd "$srcdir" && autoreconf --force -v --install ) || exit test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" egl-wayland-1.1.18/configure.ac000066400000000000000000000072571474153032600163140ustar00rootroot00000000000000AC_PREREQ([2.64]) m4_define([wayland_eglstream_major_version], [1]) m4_define([wayland_eglstream_minor_version], [1]) m4_define([wayland_eglstream_micro_version], [18]) m4_define([wayland_eglstream_version], [wayland_eglstream_major_version.wayland_eglstream_minor_version.wayland_eglstream_micro_version]) AC_INIT([wayland-eglstream], [wayland_eglstream_version], [mvicomoya@nvidia.com]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build]) AC_CONFIG_SRCDIR([config.h.in]) AC_CONFIG_HEADERS([config.h]) AC_GNU_SOURCE AC_SUBST([WAYLAND_EXTERNAL_MAJOR_VERSION], [wayland_eglstream_major_version]) AC_SUBST([WAYLAND_EXTERNAL_MINOR_VERSION], [wayland_eglstream_minor_version]) AC_SUBST([WAYLAND_EXTERNAL_MICRO_VERSION], [wayland_eglstream_micro_version]) AC_SUBST([WAYLAND_EXTERNAL_VERSION], [wayland_eglstream_version]) AC_SUBST([EGL_EXTERNAL_PLATFORM_MIN_VERSION], [${WAYLAND_EXTERNAL_MAJOR_VERSION}.${WAYLAND_EXTERNAL_MINOR_VERSION}]) AC_SUBST([EGL_EXTERNAL_PLATFORM_MAX_VERSION], [$(($WAYLAND_EXTERNAL_MAJOR_VERSION + 1))]) # Add an --enable-debug option AX_CHECK_ENABLE_DEBUG(no, DEBUG) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([1.11 foreign subdir-objects]) AM_SILENT_RULES([yes]) PKG_PROG_PKG_CONFIG() # Checks for programs. AC_PROG_CC AC_PROG_CXX AM_PROG_AS AC_PROG_LIBTOOL AC_ARG_VAR([WAYLAND_SCANNER], [The wayland-scanner executable]) AC_PATH_PROG([WAYLAND_SCANNER], [wayland-scanner]) # User didn't specify wayland-scanner location manually, so find it ourselves if test x$WAYLAND_SCANNER = x; then PKG_CHECK_MODULES(WAYLAND_SCANNER, [wayland-scanner]) WAYLAND_SCANNER=`$PKG_CONFIG --variable=wayland_scanner wayland-scanner` fi AM_CONDITIONAL([WAYLAND_SCANNER_HAS_PRIVATE_CODE], [test x$WAYLAND_SCANNER = x`$PKG_CONFIG --variable=wayland_scanner "wayland-scanner >= 1.14.91"`]) # Check for protocols. PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.8]) AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, `$PKG_CONFIG --variable=pkgdatadir wayland-protocols`) # Initialize libtool LT_PREREQ([2.2]) LT_INIT # Checks for libraries. AX_PTHREAD() AC_CHECK_LIB([dl], [dlsym], [], [AC_MSG_ERROR("dlsym is needed to compile wayland-egldisplay")]) PKG_CHECK_MODULES([EGL_HEADERS], [egl >= 1.5 egl < 2]) PKG_CHECK_MODULES([EGL_EXTERNAL_PLATFORM], [eglexternalplatform >= ${EGL_EXTERNAL_PLATFORM_MIN_VERSION} eglexternalplatform < ${EGL_EXTERNAL_PLATFORM_MAX_VERSION}]) PKG_CHECK_MODULES([WAYLAND], [wayland-server wayland-client wayland-egl-backend >= 3]) PKG_CHECK_MODULES([LIBDRM], [libdrm]) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h stddef.h stdint.h stdlib.h string.h sys/socket.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_INT32_T AC_TYPE_SIZE_T AC_TYPE_UINT32_T # Checks for library functions. AC_FUNC_MALLOC AC_CHECK_FUNCS([getpagesize inet_ntoa memset socket strcasecmp strstr]) # See if the compiler supports the -fvisibility=hidden flag. AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [COMPILER_FLAG_VISIBILITY_HIDDEN="-fvisibility=hidden"], [COMPILER_FLAG_VISIBILITY_HIDDEN=""]) AC_SUBST([COMPILER_FLAG_VISIBILITY_HIDDEN]) # See if the linker supports the --no-undefined flag. AX_CHECK_LINK_FLAG([-Xlinker --no-undefined], [LINKER_FLAG_NO_UNDEFINED="-Xlinker --no-undefined"], [LINKER_FLAG_NO_UNDEFINED=""]) AC_SUBST([LINKER_FLAG_NO_UNDEFINED]) # Default CFLAGS CFLAGS="$CFLAGS -Wall -Werror -include config.h" PKG_NOARCH_INSTALLDIR AC_CONFIG_FILES([ wayland-eglstream.pc wayland-eglstream-protocols.pc Makefile ]) AC_OUTPUT AC_MSG_RESULT([ Version ${WAYLAND_EXTERNAL_VERSION} Prefix ${prefix} ]) egl-wayland-1.1.18/include/000077500000000000000000000000001474153032600154365ustar00rootroot00000000000000egl-wayland-1.1.18/include/wayland-drm.h000066400000000000000000000030521474153032600200260ustar00rootroot00000000000000/* * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_DRM_H #define WAYLAND_DRM_H extern const char * wl_drm_get_dev_name(const WlEglPlatformData *data, EGLDisplay dpy); extern EGLBoolean wl_drm_display_bind(struct wl_display *display, struct wl_eglstream_display *wlStreamDpy, const char *dev_name); extern void wl_drm_display_unbind(struct wl_eglstream_display *wlStreamDpy); #endif /* WAYLAND_DRM_H */ egl-wayland-1.1.18/include/wayland-egldevice.h000066400000000000000000000061331474153032600211760ustar00rootroot00000000000000/* * Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLDEVICE_H #define WAYLAND_EGLDEVICE_H #include #include #include #include "wayland-external-exports.h" #include "wayland-eglhandle.h" #include #ifdef __cplusplus extern "C" { #endif /** * Keeps track of an internal display. * * Since the same internal display can be shared by multiple external displays, * the internal displays are always created with the EGL_TRACK_REFERENCES_KHR * flag set. If the driver doesn't support that extension, then we keep track * of an initialization count to produce the same behavior. */ typedef struct WlEglDeviceDpyRec { EGLDeviceEXT eglDevice; EGLDisplay eglDisplay; WlEglPlatformData *data; unsigned int initCount; EGLint major; EGLint minor; /* The EGL DRM device */ dev_t dev; /* The EGL DRM render node */ dev_t renderNode; struct { unsigned int stream : 1; unsigned int stream_attrib : 1; unsigned int stream_cross_process_fd : 1; unsigned int stream_remote : 1; unsigned int stream_producer_eglsurface : 1; unsigned int stream_fifo_synchronous : 1; unsigned int stream_sync : 1; unsigned int stream_flush : 1; unsigned int stream_consumer_eglimage : 1; unsigned int image_dma_buf_export : 1; } exts; struct wl_list link; } WlEglDeviceDpy; /** * Returns the WlEglDeviceDpy structure for a given device. * Note that the same device will always return the same WlEglDeviceDpy. */ WlEglDeviceDpy *wlGetInternalDisplay(WlEglPlatformData *data, EGLDeviceEXT device); /** * Frees all of the WlEglDeviceDpy structures. */ void wlFreeAllInternalDisplays(WlEglPlatformData *data); EGLBoolean wlInternalInitialize(WlEglDeviceDpy *devDpy); EGLBoolean wlInternalTerminate(WlEglDeviceDpy *devDpy); #ifdef __cplusplus } #endif #endif // WAYLAND_EGLDEVICE_H egl-wayland-1.1.18/include/wayland-egldisplay.h000066400000000000000000000176171474153032600214150ustar00rootroot00000000000000/* * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLDISPLAY_H #define WAYLAND_EGLDISPLAY_H #include #include #include #include #include #include "wayland-external-exports.h" #include "wayland-eglhandle.h" #include "wayland-egldevice.h" #ifdef __cplusplus extern "C" { #endif /* This define represents the version of the wl_eglstream_controller interface when the attach_eglstream_consumer_attrib() request was first available" */ #define WL_EGLSTREAM_CONTROLLER_ATTACH_EGLSTREAM_CONSUMER_ATTRIB_SINCE 2 /* * Our representation of a dmabuf format. It has a drm_fourcc.h code * and a collection of modifiers that are supported. */ typedef struct WlEglDmaBufFormatRec { uint32_t format; uint32_t numModifiers; uint64_t *modifiers; } WlEglDmaBufFormat; /* * This is a helper struct for a collection of dmabuf formats. We have * a couple areas of the code that want to manage sets of formats and * this allows us to share code. */ typedef struct WlEglDmaBufFormatSetRec { uint32_t numFormats; WlEglDmaBufFormat *dmaBufFormats; } WlEglDmaBufFormatSet; /* * Container for all formats supported by a device. A "tranche" is * really just a set of formats and modifiers supported by a device * with a certain set of flags (really just a scanout flag). */ typedef struct WlEglDmaBufTrancheRec { dev_t drmDev; int supportsScanout; WlEglDmaBufFormatSet formatSet; } WlEglDmaBufTranche; /* * This is one item in the format table that the compositor sent * us. */ typedef struct WlEglDmaBufFormatTableEntryRec{ uint32_t format; uint32_t pad; uint64_t modifier; } WlEglDmaBufFormatTableEntry; /* * In linux_dmabuf_feedback.format_table the compositor will advertise all * (format, modifier) pairs that it supports importing buffers with. We * the client mmap this format table and refer to it during the tranche * events to construct WlEglDmaBufFormatSets that the compositor * supports. */ typedef struct WlEglDmaBufFormatTableRec { int len; /* This is mmapped from the fd given to us by the compositor */ WlEglDmaBufFormatTableEntry *entry; } WlEglDmaBufFormatTable; /* * A dmabuf feedback object. This will record all tranches sent by the * compositor. It can be used either for per-surface feedback or for * the default feedback for any surface. */ typedef struct WlEglDmaBufFeedbackRec { struct zwp_linux_dmabuf_feedback_v1 *wlDmaBufFeedback; int numTranches; WlEglDmaBufTranche *tranches; WlEglDmaBufFormatTable formatTable; dev_t mainDev; /* * This will be filled in during wl events and copied to * dev_formats on dmabuf_feedback.tranche_done */ WlEglDmaBufTranche tmpTranche; int feedbackDone; /* * This will be set to true if the compositor notified us of new * modifiers but we haven't reallocated our surface yet. */ int unprocessedFeedback; } WlEglDmaBufFeedback; typedef struct WlEglDisplayRec { WlEglDeviceDpy *devDpy; /* Supports EGL_ANDROID_native_fence_sync */ int supports_native_fence_sync; /* Underlying driver version is recent enough for explicit sync */ int supports_explicit_sync; EGLBoolean ownNativeDpy; struct wl_display *nativeDpy; struct wl_registry *wlRegistry; struct wl_eglstream_display *wlStreamDpy; struct wl_eglstream_controller *wlStreamCtl; struct zwp_linux_dmabuf_v1 *wlDmaBuf; struct wp_linux_drm_syncobj_manager_v1 *wlDrmSyncobj; unsigned int wlStreamCtlVer; struct wp_presentation *wpPresentation; struct wl_event_queue *wlEventQueue; struct { unsigned int stream_fd : 1; unsigned int stream_inet : 1; unsigned int stream_socket : 1; } caps; WlEglPlatformData *data; /* DRM device in use */ int drmFd; EGLBoolean useInitRefCount; EGLDeviceEXT requestedDevice; /** * The number of times that eglTerminate has to be called before the * display is termianted. * * If \c useInitRefCount is true, then this is incremented each time * eglInitialize is called, and decremented each time eglTerminate is * called. * * If \c useInitRefCount is false, then this value is capped at 1. * * In all cases, the display is initialized if (initCount > 0). */ unsigned int initCount; pthread_mutex_t mutex; int refCount; struct wl_list wlEglSurfaceList; struct wl_list link; /* The formats given to us by the linux_dmabuf.modifiers event */ WlEglDmaBufFormatSet formatSet; /* The linux_dmabuf protocol version in use. Will be >= 3 */ unsigned int dmaBufProtocolVersion; WlEglDmaBufFeedback defaultFeedback; EGLBoolean primeRenderOffload; } WlEglDisplay; typedef struct WlEventQueueRec { WlEglDisplay *display; struct wl_event_queue *queue; int refCount; struct wl_list dpyLink; struct wl_list dangLink; struct wl_list threadLink; } WlEventQueue; int WlEglRegisterFeedback(WlEglDmaBufFeedback *feedback); void wlEglDestroyFeedback(WlEglDmaBufFeedback *feedback); EGLBoolean wlEglIsValidNativeDisplayExport(void *data, void *nativeDpy); EGLBoolean wlEglBindDisplaysHook(void *data, EGLDisplay dpy, void *nativeDpy); EGLBoolean wlEglUnbindDisplaysHook(EGLDisplay dpy, void *nativeDpy); EGLDisplay wlEglGetPlatformDisplayExport(void *data, EGLenum platform, void *nativeDpy, const EGLAttrib *attribs); EGLBoolean wlEglInitializeHook(EGLDisplay dpy, EGLint *major, EGLint *minor); EGLBoolean wlEglTerminateHook(EGLDisplay dpy); WlEglDisplay *wlEglAcquireDisplay(EGLDisplay dpy); void wlEglReleaseDisplay(WlEglDisplay *display); EGLBoolean wlEglChooseConfigHook(EGLDisplay dpy, EGLint const * attribs, EGLConfig * configs, EGLint configSize, EGLint * numConfig); EGLBoolean wlEglGetConfigAttribHook(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint * value); EGLBoolean wlEglQueryDisplayAttribHook(EGLDisplay dpy, EGLint name, EGLAttrib *value); EGLBoolean wlEglIsWaylandDisplay(void *nativeDpy); EGLBoolean wlEglIsWlEglDisplay(WlEglDisplay *display); EGLBoolean wlEglDestroyAllDisplays(WlEglPlatformData *data); const char* wlEglQueryStringExport(void *data, EGLDisplay dpy, EGLExtPlatformString name); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglhandle.h000066400000000000000000000174511474153032600211770ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLHANDLE_H #define WAYLAND_EGLHANDLE_H #include #include #include "wayland-external-exports.h" #include "wayland-egl-ext.h" #include #ifdef __cplusplus extern "C" { #endif /* * Define function pointers for EGL core functions */ typedef const char* (*PWLEGLFNQUERYSTRINGCOREPROC) (EGLDisplay dpy, EGLint name); typedef EGLContext (*PWLEGLFNGETCURRENTCONTEXTCOREPROC) (void); typedef EGLSurface (*PWLEGLFNGETCURRENTSURFACECOREPROC) (EGLint readdraw); typedef EGLBoolean (*PWLEGLFNRELEASETHREADCOREPROC) (void); typedef EGLint (*PWLEGLFNGETERRORCOREPROC) (void); typedef void* (*PWLEGLFNGETPROCADDRESSCOREPROC) (const char *name); typedef EGLBoolean (*PWLEGLFNINITIALIZECOREPROC) (EGLDisplay dpy, EGLint *major, EGLint *minor); typedef EGLBoolean (*PWLEGLFNTERMINATECOREPROC) (EGLDisplay dpy); typedef EGLBoolean (*PWLEGLFNCHOOSECONFIGCOREPROC) (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config); typedef EGLBoolean (*PWLEGLFNGETCONFIGATTRIBCOREPROC) (EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value); typedef EGLSurface (*PWLEGLFNCREATEPBUFFERSURFACECOREPROC) (EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list); typedef EGLBoolean (*PWLEGLFNDESTROYSURFACECOREPROC) (EGLDisplay dpy, EGLSurface surface); typedef EGLBoolean (*PWLEGLFNMAKECURRENTCOREPROC) (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx); typedef EGLBoolean (*PWLEGLFNSWAPBUFFERSCOREPROC) (EGLDisplay dpy, EGLSurface surface); typedef EGLBoolean (*PWLEGLFNSWAPBUFFERSWITHDAMAGEKHRPROC) (EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); typedef EGLBoolean (*PWLEGLFNSWAPINTERVALCOREPROC) (EGLDisplay dpy, EGLint interval); /* * WlEglPlatformData structure * * Keeps all EGL driver-specific methods provided by a specific EGL * implementation that are required by the Wayland external platform * implementation to manage resources associated with a specific backing * EGLDisplay. */ typedef struct WlEglPlatformDataRec { /* Application-facing callbacks fetched from the EGL driver */ struct { int major; int minor; PWLEGLFNQUERYSTRINGCOREPROC queryString; PFNEGLQUERYDEVICESEXTPROC queryDevices; PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay; PWLEGLFNINITIALIZECOREPROC initialize; PWLEGLFNTERMINATECOREPROC terminate; PWLEGLFNCHOOSECONFIGCOREPROC chooseConfig; PWLEGLFNGETCONFIGATTRIBCOREPROC getConfigAttrib; PFNEGLQUERYSURFACEPROC querySurface; PWLEGLFNGETCURRENTCONTEXTCOREPROC getCurrentContext; PWLEGLFNGETCURRENTSURFACECOREPROC getCurrentSurface; PWLEGLFNMAKECURRENTCOREPROC makeCurrent; PFNEGLCREATESTREAMKHRPROC createStream; PFNEGLCREATESTREAMFROMFILEDESCRIPTORKHRPROC createStreamFromFD; PFNEGLCREATESTREAMATTRIBNVPROC createStreamAttrib; PFNEGLGETSTREAMFILEDESCRIPTORKHRPROC getStreamFileDescriptor; PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC createStreamProducerSurface; PWLEGLFNCREATEPBUFFERSURFACECOREPROC createPbufferSurface; PFNEGLDESTROYSTREAMKHRPROC destroyStream; PWLEGLFNDESTROYSURFACECOREPROC destroySurface; PWLEGLFNSWAPBUFFERSCOREPROC swapBuffers; PWLEGLFNSWAPBUFFERSWITHDAMAGEKHRPROC swapBuffersWithDamage; PWLEGLFNSWAPINTERVALCOREPROC swapInterval; PWLEGLFNGETERRORCOREPROC getError; PWLEGLFNRELEASETHREADCOREPROC releaseThread; PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttrib; PFNEGLQUERYDEVICESTRINGEXTPROC queryDeviceString; /* Used for fifo_synchronous support */ PFNEGLQUERYSTREAMKHRPROC queryStream; PFNEGLQUERYSTREAMU64KHRPROC queryStreamu64; PFNEGLCREATESTREAMSYNCNVPROC createStreamSync; PFNEGLCLIENTWAITSYNCKHRPROC clientWaitSync; PFNEGLSIGNALSYNCKHRPROC signalSync; PFNEGLDESTROYSYNCKHRPROC destroySync; PFNEGLCREATESYNCKHRPROC createSync; PFNEGLSTREAMFLUSHNVPROC streamFlush; PFNEGLDUPNATIVEFENCEFDANDROIDPROC dupNativeFenceFD; /* Used for dma-buf surfaces */ PFNEGLSTREAMIMAGECONSUMERCONNECTNVPROC streamImageConsumerConnect; PFNEGLSTREAMACQUIREIMAGENVPROC streamAcquireImage; PFNEGLSTREAMRELEASEIMAGENVPROC streamReleaseImage; PFNEGLQUERYSTREAMCONSUMEREVENTNVPROC queryStreamConsumerEvent; PFNEGLEXPORTDMABUFIMAGEMESAPROC exportDMABUFImage; PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC exportDMABUFImageQuery; PFNEGLCREATEIMAGEKHRPROC createImage; PFNEGLDESTROYIMAGEKHRPROC destroyImage; } egl; /* Non-application-facing callbacks provided by the EGL driver */ struct { PEGLEXTFNSETERROR setError; PEGLEXTFNSTREAMSWAPINTERVAL streamSwapInterval; } callbacks; /* True if the driver supports the EGL_KHR_display_reference extension. */ EGLBoolean supportsDisplayReference; /* A linked list of WlEglDeviceDpy structs. */ struct wl_list deviceDpyList; /* pthread key for TLS */ pthread_key_t tlsKey; } WlEglPlatformData; /* * wlEglCreatePlatformData() * * Creates a new platform data structure and fills it out with all the required * application-facing EGL methods provided by . * * . correspond to the EGL External Platform interface * version supported by the driver. * * Returns a pointer to the newly created structure upon success; otherwise, * returns NULL. */ WlEglPlatformData* wlEglCreatePlatformData(int apiMajor, int apiMinor, const EGLExtDriver *driver); /* * wlEglDestroyPlatformData() * * Destroys the given platform data, previously created with * wlEglCreatePlatformData(). */ void wlEglDestroyPlatformData(WlEglPlatformData *data); void* wlEglGetInternalHandleExport(EGLDisplay dpy, EGLenum type, void *handle); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglstream-server.h000066400000000000000000000102061474153032600225320ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLSTREAM_SERVER_H #define WAYLAND_EGLSTREAM_SERVER_H #include #include #include #include "wayland-eglhandle.h" #ifdef __cplusplus extern "C" { #endif /* * Forward declarations */ struct wl_eglstream_display; struct wl_eglstream; /* * wl_eglstream_display_bind() * * Creates and initializes a wl_eglstream_display connection associated to the * given wl_display and EGLDisplay. */ EGLBoolean wl_eglstream_display_bind(WlEglPlatformData *data, struct wl_display *wlDisplay, EGLDisplay eglDisplay, const char *exts, const char *dev_name); /* * wl_eglstream_display_unbind() * * Destroys the given wl_eglstream_display connection result of a previous * wl_eglstream_display_bind() call. */ void wl_eglstream_display_unbind(struct wl_eglstream_display *wlStreamDpy); /* * wl_eglstream_display_get() * * Given an EGL display, returns its associated wl_eglstream_display connection. */ struct wl_eglstream_display* wl_eglstream_display_get(EGLDisplay eglDisplay); /* * wl_eglstream_display_get_stream() * * Given a generic wl_resource, returns its associated wl_eglstream. */ struct wl_eglstream* wl_eglstream_display_get_stream(struct wl_eglstream_display *wlStreamDpy, struct wl_resource *resource); /* wl_eglstream_display definition */ struct wl_eglstream_display { WlEglPlatformData *data; struct wl_global *global; struct wl_display *wlDisplay; EGLDisplay eglDisplay; struct { int stream_attrib : 1; int stream_cross_process_fd : 1; int stream_remote : 1; int stream_socket : 1; int stream_socket_inet : 1; int stream_socket_unix : 1; int stream_origin : 1; } exts; struct { const char *device_name; struct wl_global *global; } *drm; int caps_override : 1; int supported_caps; struct wl_buffer_interface wl_eglstream_interface; struct wl_list link; }; /* wl_eglstream definition */ struct wl_eglstream { struct wl_resource *resource; struct wl_eglstream_display *wlStreamDpy; int width, height; EGLBoolean fromFd; EGLBoolean isInet; int handle; EGLStreamKHR eglStream; /* * The following attribute encodes the default value for a * stream's image inversion relative to wayland protocol * convention. Vulkan apps will be set to 'true', while * OpenGL apps will be set to 'false'. * NOTE: EGL_NV_stream_origin is the authorative source of * truth regarding a stream's frame orientation and should be * queried for an accurate value. The following attribute is a * 'best guess' fallback mechanism which should only be used * when a query to EGL_NV_stream_origin fails. */ EGLBoolean yInverted; }; #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglstream.h000066400000000000000000000032401474153032600212260ustar00rootroot00000000000000/* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLSTREAM_H #define WAYLAND_EGLSTREAM_H #include #include #ifdef __cplusplus extern "C" { #endif /* wlEglCreateStreamAttribHook() * * Creates an EGLStream from the given external attribute list. If suceeded, * the new stream handle is returned; otherwise, EGL_NO_STREAM_KHR is returned * and the appropriate EGL error is generated. */ EGLStreamKHR wlEglCreateStreamAttribHook(EGLDisplay dpy, const EGLAttrib *attribs); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglsurface-internal.h000066400000000000000000000202041474153032600231740ustar00rootroot00000000000000/* * Copyright (c) 2014-2024, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLSURFACE_INTERNAL_H #define WAYLAND_EGLSURFACE_INTERNAL_H #include #include #include #include #include "wayland-egldisplay.h" #include "wayland-eglutils.h" #include "wayland-eglsurface.h" #ifdef __cplusplus extern "C" { #endif typedef struct WlEglStreamImageRec { /* Pointer back to the parent surface for use in Wayland callbacks */ struct WlEglSurfaceRec *surface; EGLImageKHR eglImage; struct wl_buffer *buffer; EGLBoolean attached; struct wl_list acquiredLink; struct wp_linux_drm_syncobj_timeline_v1 *wlReleaseTimeline; uint32_t drmSyncobjHandle; int releasePending; /* Latest release point the compositor will signal with explicit sync */ uint64_t releasePoint; /* Cached acquire EGLSync from acquireImage */ EGLSyncKHR acquireSync; /* * Used for delaying the destruction of the image if we are waiting the * buffer release thread to use it later. */ EGLBoolean destructionPending; struct wl_list link; } WlEglStreamImage; typedef struct WlEglSurfaceCtxRec { EGLBoolean isOffscreen; EGLSurface eglSurface; EGLStreamKHR eglStream; void *wlStreamResource; EGLBoolean isAttached; int useDamageThread; pthread_t damageThreadId; EGLSyncKHR damageThreadSync; int damageThreadFlush; int damageThreadShutdown; EGLuint64KHR framesProduced; EGLuint64KHR framesFinished; EGLuint64KHR framesProcessed; /* * Use an individual mutex to guard access to streamImages. This helps us * to avoid sharing the surface lock between the app and buffer release * event threads, resulting in simplified lock management and smaller * critical sections. */ pthread_mutex_t streamImagesMutex; struct wl_list streamImages; struct wl_list acquiredImages; struct wl_buffer *currentBuffer; struct wl_list link; } WlEglSurfaceCtx; struct WlEglSurfaceRec { WlEglDisplay *wlEglDpy; EGLConfig eglConfig; EGLint *attribs; EGLBoolean pendingSwapIntervalUpdate; struct wl_egl_window *wlEglWin; long int wlEglWinVer; struct wl_surface *wlSurface; int width, height; int dx, dy; WlEglSurfaceCtx ctx; struct wl_list oldCtxList; EGLint swapInterval; EGLint fifoLength; int (*present_update_callback)(void*, uint64_t, int); struct wl_event_queue *presentFeedbackQueue; int inFlightPresentFeedbackCount; int landedPresentFeedbackCount; struct wl_callback *throttleCallback; struct wl_event_queue *wlEventQueue; /* Asynchronous wl_buffer.release event processing */ struct { struct wl_event_queue *wlBufferEventQueue; pthread_t bufferReleaseThreadId; int bufferReleaseThreadPipe[2]; }; struct wl_list link; EGLBoolean isSurfaceProducer; /* The refCount is initialized to 1 during EGLSurface creation, * gets incremented/decrementsd in wlEglSurfaceRef()/wlEglSurfaceUnref(), * when we enter/exit from eglSwapBuffers(). */ unsigned int refCount; /* * Set to EGL_TRUE before destroying the EGLSurface in eglDestroySurface(). */ EGLBoolean isDestroyed; /* The lock is used to serialize eglSwapBuffers()/eglDestroySurface(), * Using wlExternalApiLock() for this requires that we release lock * before dispatching frame sync events in wlEglWaitFrameSync(). */ pthread_mutex_t mutexLock; /* True when the EGL_PRESENT_OPAQUE_EXT surface attrib is set by the app */ EGLBoolean presentOpaque; /* This pair of mutex and conditional variable is used * for sychronization between eglSwapBuffers() and damage * thread on creating frame sync and waiting for it. */ pthread_mutex_t mutexFrameSync; pthread_cond_t condFrameSync; /* We want to delay the resizing of the window surface until the next * eglSwapBuffers(), so just set a resize flag. */ EGLBoolean isResized; WlEglDmaBufFeedback feedback; /* per-surface Explicit Sync objects */ struct wp_linux_drm_syncobj_surface_v1 *wlSyncobjSurf; struct wp_linux_drm_syncobj_timeline_v1 *wlAcquireTimeline; uint32_t drmSyncobjHandle; /* Last acquire point used. This starts at 1, zero means invalid. */ uint64_t syncPoint; }; void wlEglReallocSurface(WlEglDisplay *display, WlEglPlatformData *pData, WlEglSurface *surface); EGLSurface wlEglCreatePlatformWindowSurfaceHook(EGLDisplay dpy, EGLConfig config, void *nativeWin, const EGLAttrib *attribs); EGLSurface wlEglCreatePlatformPixmapSurfaceHook(EGLDisplay dpy, EGLConfig config, void *nativePixmap, const EGLAttrib *attribs); EGLSurface wlEglCreatePbufferSurfaceHook(EGLDisplay dpy, EGLConfig config, const EGLint *attribs); EGLSurface wlEglCreateStreamProducerSurfaceHook(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, const EGLint *attribs); EGLBoolean wlEglDestroySurfaceHook(EGLDisplay dpy, EGLSurface eglSurface); EGLBoolean wlEglDestroyAllSurfaces(WlEglDisplay *display); EGLBoolean wlEglIsWaylandWindowValid(struct wl_egl_window *window); EGLBoolean wlEglIsWlEglSurfaceForDisplay(WlEglDisplay *display, WlEglSurface *wlEglSurface); EGLBoolean wlEglQuerySurfaceHook(EGLDisplay dpy, EGLSurface eglSurface, EGLint attribute, EGLint *value); EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy, void *nativeResource, EGLint attribute, int *value); EGLBoolean wlEglSurfaceCheckReleasePoints(WlEglDisplay *display, WlEglSurface *surface); EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface, struct wl_event_queue *queue, EGLint *rects, EGLint n_rects); void wlEglCreateFrameSync(WlEglSurface *surface); EGLint wlEglWaitFrameSync(WlEglSurface *surface); EGLBoolean wlEglSurfaceRef(WlEglDisplay *display, WlEglSurface *surface); void wlEglSurfaceUnref(WlEglSurface *surface); EGLint wlEglHandleImageStreamEvents(WlEglSurface *surface); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglsurface.h000066400000000000000000000046661474153032600214000ustar00rootroot00000000000000/* * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLSURFACE_H #define WAYLAND_EGLSURFACE_H #include #include #include #include #include "wayland-egldisplay.h" #include "wayland-eglutils.h" #ifdef __cplusplus extern "C" { #endif typedef struct WlEglSurfaceRec WlEglSurface; WL_EXPORT EGLStreamKHR wlEglGetSurfaceStreamExport(WlEglSurface *surface); WL_EXPORT WlEglSurface *wlEglCreateSurfaceExport(EGLDisplay dpy, int width, int height, struct wl_surface *native_surface, int fifo_length); WL_EXPORT WlEglSurface *wlEglCreateSurfaceExport2(EGLDisplay dpy, int width, int height, struct wl_surface *native_surface, int fifo_length, int (*present_update_callback)(void*, uint64_t, int), const EGLAttrib *attribs); WL_EXPORT int wlEglWaitAllPresentationFeedbacksExport(WlEglSurface *surface); WL_EXPORT int wlEglProcessPresentationFeedbacksExport(WlEglSurface *surface); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglswap.h000066400000000000000000000043531474153032600207130ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLSWAP_H #define WAYLAND_EGLSWAP_H #include #include #include "wayland-eglhandle.h" #include "wayland-eglsurface.h" #ifdef __cplusplus extern "C" { #endif EGLBoolean wlEglSwapBuffersHook(EGLDisplay dpy, EGLSurface eglSurface); EGLBoolean wlEglSwapBuffersWithDamageHook(EGLDisplay eglDisplay, EGLSurface eglSurface, EGLint *rects, EGLint n_rects); EGLBoolean wlEglSwapIntervalHook(EGLDisplay eglDisplay, EGLint interval); EGLint wlEglStreamSwapIntervalCallback(WlEglPlatformData *data, EGLStreamKHR stream, EGLint *interval); WL_EXPORT EGLBoolean wlEglPrePresentExport(WlEglSurface *surface); WL_EXPORT EGLBoolean wlEglPostPresentExport(WlEglSurface *surface); WL_EXPORT EGLBoolean wlEglPostPresentExport2(WlEglSurface *surface, uint64_t presentId, void *presentInfo); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-eglutils.h000066400000000000000000000043211474153032600210740ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGLUTILS_H #define WAYLAND_EGLUTILS_H #include #include #include "wayland-external-exports.h" #include "wayland-eglhandle.h" #ifdef NDEBUG #define wlEglSetError(data, err) \ wlEglSetErrorCallback(data, err, 0, 0) #else #define wlEglSetError(data, err) \ wlEglSetErrorCallback(data, err, __FILE__, __LINE__) #endif #ifndef WL_LIST_INITIALIZER #define WL_LIST_INITIALIZER(head) { .prev = (head), .next = (head) } #endif #ifndef WL_LIST_INIT #define WL_LIST_INIT(head) \ do { (head)->prev = (head)->next = (head); } while (0); #endif #ifdef __cplusplus extern "C" { #endif EGLBoolean wlEglFindExtension(const char *extension, const char *extensions); EGLBoolean wlEglMemoryIsReadable(const void *p, size_t len); EGLBoolean wlEglCheckInterfaceType(struct wl_object *obj, const char *ifname); void wlEglSetErrorCallback(WlEglPlatformData *data, EGLint err, const char *file, int line); #ifdef __cplusplus } #endif #endif egl-wayland-1.1.18/include/wayland-external-exports.h000066400000000000000000000053441474153032600225760ustar00rootroot00000000000000/* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EXTERNAL_EXPORTS_H #define WAYLAND_EXTERNAL_EXPORTS_H /* * .. * defines the EGL external Wayland * implementation version. * * The includer of this file can override either WAYLAND_EXTERNAL_VERSION_MAJOR * or WAYLAND_EXTERNAL_VERSION_MINOR in order to build against a certain EGL * external API version. * * * How to update this version numbers: * * - WAYLAND_EXTERNAL_VERSION_MAJOR must match the EGL external API major * number this platform implements * * - WAYLAND_EXTERNAL_VERSION_MINOR must match the EGL external API minor * number this platform implements * * - If the platform implementation is changed in any way, increase * WAYLAND_EXTERNAL_VERSION_MICRO by 1 */ #if !defined(WAYLAND_EXTERNAL_VERSION_MAJOR) #define WAYLAND_EXTERNAL_VERSION_MAJOR 1 #if !defined(WAYLAND_EXTERNAL_VERSION_MINOR) #define WAYLAND_EXTERNAL_VERSION_MINOR 1 #endif #elif !defined(WAYLAND_EXTERNAL_VERSION_MINOR) #define WAYLAND_EXTERNAL_VERSION_MINOR 0 #endif #define WAYLAND_EXTERNAL_VERSION_MICRO 18 #define EGL_EXTERNAL_PLATFORM_VERSION_MAJOR WAYLAND_EXTERNAL_VERSION_MAJOR #define EGL_EXTERNAL_PLATFORM_VERSION_MINOR WAYLAND_EXTERNAL_VERSION_MINOR #include #include WL_EXPORT EGLBoolean loadEGLExternalPlatform(int major, int minor, const EGLExtDriver *driver, EGLExtPlatform *platform); #endif egl-wayland-1.1.18/include/wayland-thread.h000066400000000000000000000046731474153032600205250ustar00rootroot00000000000000/* * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_THREAD_H #define WAYLAND_THREAD_H #include #include #include /* * wlExternalApiLock() * * Tries to acquire the external API lock. If the lock is already acquired by * another thread, it will block until the lock is released. * * Calling this function twice without calling wlExternalApiUnlock() in between * will fail. * * First call to wlExternalApiLock() will initialize the external API lock * resources. * * Returns 0 upon success; otherwise returns -1. */ int wlExternalApiLock(void); /* * wlExternalApiUnlock() * * Releases the external API lock. * * Calling this function without a previous call to wlExternalApiLock() will * fail. * * Returns 0 upon success; otherwise returns -1. */ int wlExternalApiUnlock(void); /* * wlExternalApiDestroyLock() * * Releases and frees the the external API lock resources. This call should only * be called as part of the global teardown. */ void wlExternalApiDestroyLock(void); /* * wlEglInitializeMutex(pthread_mutex_t *mutex) * * Initialises the pthread mutex referenced by mutex. */ bool wlEglInitializeMutex(pthread_mutex_t *mutex); /* * wlEglMutexDestroy(pthread_mutex_t *mutex) * * Destroys the pthread mutex referenced by mutex. */ void wlEglMutexDestroy(pthread_mutex_t *mutex); #endif egl-wayland-1.1.18/m4/000077500000000000000000000000001474153032600143335ustar00rootroot00000000000000egl-wayland-1.1.18/m4/ax_check_compile_flag.m4000066400000000000000000000040701474153032600210440ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS egl-wayland-1.1.18/m4/ax_check_enable_debug.m4000066400000000000000000000107301474153032600210170ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_enable_debug.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_ENABLE_DEBUG([enable by default=yes/info/profile/no], [ENABLE DEBUG VARIABLES ...], [DISABLE DEBUG VARIABLES NDEBUG ...], [IS-RELEASE]) # # DESCRIPTION # # Check for the presence of an --enable-debug option to configure, with # the specified default value used when the option is not present. Return # the value in the variable $ax_enable_debug. # # Specifying 'yes' adds '-g -O0' to the compilation flags for all # languages. Specifying 'info' adds '-g' to the compilation flags. # Specifying 'profile' adds '-g -pg' to the compilation flags and '-pg' to # the linking flags. Otherwise, nothing is added. # # Define the variables listed in the second argument if debug is enabled, # defaulting to no variables. Defines the variables listed in the third # argument if debug is disabled, defaulting to NDEBUG. All lists of # variables should be space-separated. # # If debug is not enabled, ensure AC_PROG_* will not add debugging flags. # Should be invoked prior to any AC_PROG_* compiler checks. # # IS-RELEASE can be used to change the default to 'no' when making a # release. Set IS-RELEASE to 'yes' or 'no' as appropriate. By default, it # uses the value of $ax_is_release, so if you are using the AX_IS_RELEASE # macro, there is no need to pass this parameter. # # AX_IS_RELEASE([git-directory]) # AX_CHECK_ENABLE_DEBUG() # # LICENSE # # Copyright (c) 2011 Rhys Ulerich # Copyright (c) 2014, 2015 Philip Withnall # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. #serial 5 AC_DEFUN([AX_CHECK_ENABLE_DEBUG],[ AC_BEFORE([$0],[AC_PROG_CC])dnl AC_BEFORE([$0],[AC_PROG_CXX])dnl AC_BEFORE([$0],[AC_PROG_F77])dnl AC_BEFORE([$0],[AC_PROG_FC])dnl AC_MSG_CHECKING(whether to enable debugging) ax_enable_debug_default=m4_tolower(m4_normalize(ifelse([$1],,[no],[$1]))) ax_enable_debug_is_release=m4_tolower(m4_normalize(ifelse([$4],, [$ax_is_release], [$4]))) # If this is a release, override the default. AS_IF([test "$ax_enable_debug_is_release" = "yes"], [ax_enable_debug_default="no"]) m4_define(ax_enable_debug_vars,[m4_normalize(ifelse([$2],,,[$2]))]) m4_define(ax_disable_debug_vars,[m4_normalize(ifelse([$3],,[NDEBUG],[$3]))]) AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug=]@<:@yes/info/profile/no@:>@,[compile with debugging])], [],enable_debug=$ax_enable_debug_default) # empty mean debug yes AS_IF([test "x$enable_debug" = "x"], [enable_debug="yes"]) # case of debug AS_CASE([$enable_debug], [yes],[ AC_MSG_RESULT(yes) CFLAGS="${CFLAGS} -g -O0" CXXFLAGS="${CXXFLAGS} -g -O0" FFLAGS="${FFLAGS} -g -O0" FCFLAGS="${FCFLAGS} -g -O0" OBJCFLAGS="${OBJCFLAGS} -g -O0" ], [info],[ AC_MSG_RESULT(info) CFLAGS="${CFLAGS} -g" CXXFLAGS="${CXXFLAGS} -g" FFLAGS="${FFLAGS} -g" FCFLAGS="${FCFLAGS} -g" OBJCFLAGS="${OBJCFLAGS} -g" ], [profile],[ AC_MSG_RESULT(profile) CFLAGS="${CFLAGS} -g -pg" CXXFLAGS="${CXXFLAGS} -g -pg" FFLAGS="${FFLAGS} -g -pg" FCFLAGS="${FCFLAGS} -g -pg" OBJCFLAGS="${OBJCFLAGS} -g -pg" LDFLAGS="${LDFLAGS} -pg" ], [ AC_MSG_RESULT(no) dnl Ensure AC_PROG_CC/CXX/F77/FC/OBJC will not enable debug flags dnl by setting any unset environment flag variables AS_IF([test "x${CFLAGS+set}" != "xset"], [CFLAGS=""]) AS_IF([test "x${CXXFLAGS+set}" != "xset"], [CXXFLAGS=""]) AS_IF([test "x${FFLAGS+set}" != "xset"], [FFLAGS=""]) AS_IF([test "x${FCFLAGS+set}" != "xset"], [FCFLAGS=""]) AS_IF([test "x${OBJCFLAGS+set}" != "xset"], [OBJCFLAGS=""]) ]) dnl Define various variables if debugging is disabled. dnl assert.h is a NOP if NDEBUG is defined, so define it by default. AS_IF([test "x$enable_debug" = "xyes"], [m4_map_args_w(ax_enable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is enabled])])], [m4_map_args_w(ax_disable_debug_vars, [AC_DEFINE(], [,,[Define if debugging is disabled])])]) ax_enable_debug=$enable_debug ]) egl-wayland-1.1.18/m4/ax_check_link_flag.m4000066400000000000000000000057601474153032600203600ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_LINK_FLAG], [AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS egl-wayland-1.1.18/m4/ax_pthread.m4000066400000000000000000000312671474153032600167250ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 20 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case ${host_os} in solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; darwin*) ax_pthread_flags="-pthread $ax_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $attr; return attr /* ; */])], [attr_name=$attr; break], []) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case ${host_os} in aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; osf* | hpux*) flag="-D_REENTRANT";; solaris*) if test "$GCC" = "yes"; then flag="-D_REENTRANT" else flag="-mt -D_REENTRANT" fi ;; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], ax_cv_PTHREAD_PRIO_INHERIT, [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.])) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: compile with *_r variant if test "x$GCC" != xyes; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD egl-wayland-1.1.18/meson.build000066400000000000000000000045051474153032600161610ustar00rootroot00000000000000project('wayland-eglstream', 'c', version : '1.1.18', default_options : [ 'buildtype=debugoptimized', 'c_std=gnu99', 'warning_level=1', ], license : 'MIT', meson_version : '>= 0.50' ) cc = meson.get_compiler('c') wayland_eglstream_version = meson.project_version() ver_arr = wayland_eglstream_version.split('.') wayland_eglstream_major_version = ver_arr[0] wayland_eglstream_minor_version = ver_arr[1] wayland_eglstream_micro_version = ver_arr[2] egl = dependency('egl', version : ['>=1.5', '<2']) egl_headers = egl.partial_dependency(includes : true, compile_args : true) eglexternalplatform = dependency('eglexternalplatform', version : ['>=1.1', '<2']) wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_egl_backend = dependency('wayland-egl-backend', version : ['>=3']) threads = dependency('threads') wl_scanner = dependency('wayland-scanner', native: true) prog_scanner = find_program(wl_scanner.get_pkgconfig_variable('wayland_scanner')) inc = include_directories( 'include', 'wayland-egl', ) pkgconf = configuration_data() pkgconf.set('prefix', get_option('prefix')) pkgconf.set('exec_prefix', '${prefix}') pkgconf.set('libdir', '${exec_prefix}/@0@'.format(get_option('libdir'))) pkgconf.set('includedir', '${prefix}/@0@'.format(get_option('includedir'))) pkgconf.set('datadir', '${datarootdir}') pkgconf.set('datarootdir', '${prefix}/@0@'.format(get_option('datadir'))) pkgconf.set('PACKAGE', meson.project_name()) pkgconf.set('WAYLAND_EXTERNAL_VERSION', meson.project_version()) pkgconf.set('EGL_EXTERNAL_PLATFORM_MIN_VERSION', '@0@.@1@'.format(wayland_eglstream_major_version, wayland_eglstream_minor_version)) pkgconf.set('EGL_EXTERNAL_PLATFORM_MAX_VERSION', wayland_eglstream_major_version.to_int() + 1) configure_file( input : 'wayland-eglstream.pc.in', output : '@BASENAME@', configuration : pkgconf, install : true, install_dir : join_paths(get_option('libdir'), 'pkgconfig') ) configure_file( input : 'wayland-eglstream-protocols.pc.in', output : '@BASENAME@', configuration : pkgconf, install : true, install_dir : join_paths(get_option('datadir'), 'pkgconfig') ) subdir('wayland-eglstream') subdir('wayland-drm') subdir('src') egl-wayland-1.1.18/src/000077500000000000000000000000001474153032600146025ustar00rootroot00000000000000egl-wayland-1.1.18/src/10_nvidia_wayland.json000066400000000000000000000001631474153032600207660ustar00rootroot00000000000000{ "file_format_version" : "1.0.0", "ICD" : { "library_path" : "libnvidia-egl-wayland.so.1" } } egl-wayland-1.1.18/src/meson.build000066400000000000000000000053061474153032600167500ustar00rootroot00000000000000if not cc.has_function('dlsym') libdl = cc.find_library('dl') else libdl = [] endif add_project_arguments('-Wall', language : 'c') add_project_arguments('-Werror', language : 'c') add_project_arguments('-fvisibility=hidden', language : 'c') add_project_arguments('-DWL_HIDE_DEPRECATED', language : 'c') add_project_arguments('-D_GNU_SOURCE', language : 'c') add_project_link_arguments('-Wl,-Bsymbolic', language : 'c') if cc.has_argument('-Wpedantic') add_project_arguments('-Wno-pedantic', language : 'c') endif wl_protos = dependency('wayland-protocols', version: '>= 1.8') libdrm = dependency('libdrm') wl_protos_dir = wl_protos.get_pkgconfig_variable('pkgdatadir') wl_dmabuf_xml = join_paths(wl_protos_dir, 'unstable', 'linux-dmabuf', 'linux-dmabuf-unstable-v1.xml') wp_presentation_time_xml = join_paths(wl_protos_dir, 'stable', 'presentation-time', 'presentation-time.xml') wl_drm_syncobj_xml = join_paths(wl_protos_dir, 'staging', 'linux-drm-syncobj', 'linux-drm-syncobj-v1.xml') client_header = generator(prog_scanner, output : '@BASENAME@-client-protocol.h', arguments : ['client-header', '@INPUT@', '@OUTPUT@'] ) if wl_scanner.version().version_compare('>= 1.14.91') code_arg = 'private-code' else code_arg = 'code' endif code = generator(prog_scanner, output : '@BASENAME@-protocol.c', arguments : [code_arg, '@INPUT@', '@OUTPUT@'] ) src = [ 'wayland-thread.c', 'wayland-egldevice.c', 'wayland-egldisplay.c', 'wayland-eglstream.c', 'wayland-eglstream-server.c', 'wayland-eglsurface.c', 'wayland-eglswap.c', 'wayland-eglutils.c', 'wayland-eglhandle.c', 'wayland-external-exports.c', 'wayland-drm.c', wayland_eglstream_protocol_c, wayland_eglstream_client_protocol_h, wayland_eglstream_server_protocol_h, wayland_eglstream_controller_protocol_c, wayland_eglstream_controller_client_protocol_h, wayland_drm_protocol_c, wayland_drm_client_protocol_h, wayland_drm_server_protocol_h, ] src += client_header.process(wl_dmabuf_xml) src += code.process(wl_dmabuf_xml) src += client_header.process(wp_presentation_time_xml) src += code.process(wp_presentation_time_xml) src += client_header.process(wl_drm_syncobj_xml) src += code.process(wl_drm_syncobj_xml) egl_wayland = library('nvidia-egl-wayland', src, dependencies : [ egl_headers, eglexternalplatform, wayland_server, wayland_client, wayland_egl_backend, threads, libdrm, libdl, ], include_directories : inc, version : meson.project_version(), install : true, ) install_data('10_nvidia_wayland.json', install_dir: '@0@/egl/egl_external_platform.d'.format(get_option('datadir'))) egl-wayland-1.1.18/src/wayland-drm.c000066400000000000000000000142011474153032600171630ustar00rootroot00000000000000/* * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include #include #include #include "wayland-eglstream-server.h" #include "wayland-drm.h" #include "wayland-eglutils.h" #include "wayland-drm-server-protocol.h" static void authenticate(struct wl_client *client, struct wl_resource *resource, uint32_t id) { (void)client; (void)id; /* * This implementation will only ever report render node files, and * authentication is not supported nor required for render node devices. */ wl_resource_post_error(resource, WL_DRM_ERROR_AUTHENTICATE_FAIL, "authenticate failed"); } static void create_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t name, int32_t width, int32_t height, uint32_t stride, uint32_t format) { (void)client; (void)id; (void)name; (void)width; (void)height; (void)stride; (void)format; wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT, "invalid format"); } static void create_planar_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t name, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { (void)client; (void)id; (void)name; (void)width; (void)height; (void)format; (void)offset0; (void)stride0; (void)offset1; (void)stride1; (void)offset2; (void)stride2; wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT, "invalid format"); } static void create_prime_buffer(struct wl_client *client, struct wl_resource *resource, uint32_t id, int fd, int32_t width, int32_t height, uint32_t format, int32_t offset0, int32_t stride0, int32_t offset1, int32_t stride1, int32_t offset2, int32_t stride2) { (void)client; (void)id; (void)fd; (void)width; (void)height; (void)format; (void)offset0; (void)stride0; (void)offset1; (void)stride1; (void)offset2; (void)stride2; wl_resource_post_error(resource, WL_DRM_ERROR_INVALID_FORMAT, "invalid format"); close(fd); } static const struct wl_drm_interface interface = { authenticate, create_buffer, create_planar_buffer, create_prime_buffer, }; static void bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_eglstream_display *wlStreamDpy = data; struct wl_resource *resource = wl_resource_create(client, &wl_drm_interface, version > 2 ? 2 : version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &interface, data, NULL); wl_resource_post_event(resource, WL_DRM_DEVICE, wlStreamDpy->drm->device_name); /* * Don't send any format events. This implementation doesn't support * creating Wayland buffers of any format. */ /* * Don't report any capabilities beyond the baseline, as currently all * capabilities are only relevant when buffer creation is supported. */ if (version >= 2) wl_resource_post_event(resource, WL_DRM_CAPABILITIES, 0); } const char * wl_drm_get_dev_name(const WlEglPlatformData *data, EGLDisplay dpy) { EGLDeviceEXT egl_dev; const char *dev_exts; if (!data->egl.queryDisplayAttrib(dpy, EGL_DEVICE_EXT, (EGLAttribKHR*)&egl_dev)) { return NULL; } dev_exts = data->egl.queryDeviceString(egl_dev, EGL_EXTENSIONS); if (!dev_exts) { return NULL; } if (!wlEglFindExtension("EGL_EXT_device_drm_render_node", dev_exts)) { return NULL; } return data->egl.queryDeviceString(egl_dev, EGL_DRM_RENDER_NODE_FILE_EXT); } EGLBoolean wl_drm_display_bind(struct wl_display *display, struct wl_eglstream_display *wlStreamDpy, const char *dev_name) { if (!dev_name) { return EGL_FALSE; } wlStreamDpy->drm = calloc(1, sizeof *wlStreamDpy->drm); if (!wlStreamDpy->drm) { return EGL_FALSE; } wlStreamDpy->drm->device_name = dev_name; wlStreamDpy->drm->global = wl_global_create(display, &wl_drm_interface, 2, wlStreamDpy, bind); return EGL_TRUE; } void wl_drm_display_unbind(struct wl_eglstream_display *wlStreamDpy) { if (wlStreamDpy->drm) { wl_global_destroy(wlStreamDpy->drm->global); free(wlStreamDpy->drm); wlStreamDpy->drm = NULL; } } egl-wayland-1.1.18/src/wayland-egldevice.c000066400000000000000000000130541474153032600203350ustar00rootroot00000000000000/* * Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-egldevice.h" #include #include #include #include #include #include #include "wayland-eglhandle.h" #include "wayland-eglutils.h" WlEglDeviceDpy *wlGetInternalDisplay(WlEglPlatformData *data, EGLDeviceEXT device) { static const EGLint TRACK_REFS_ATTRIBS[] = { EGL_TRACK_REFERENCES_KHR, EGL_TRUE, EGL_NONE }; WlEglDeviceDpy *devDpy = NULL; const EGLint *attribs = NULL; const char *drmName = NULL, *renderName = NULL; struct stat sb, render_sb; // First, see if we've already created an EGLDisplay for this device. wl_list_for_each(devDpy, &data->deviceDpyList, link) { if (devDpy->data == data && devDpy->eglDevice == device) { return devDpy; } } // We didn't find a matching display, so create one. if (data->supportsDisplayReference) { // Always use EGL_KHR_display_reference if the driver supports it. // We'll do our own refcounting so that we can work without it, but // setting EGL_TRACK_REFERENCES_KHR means that it's less likely that // something else might grab the same EGLDevice-based display and // call eglTerminate on it. attribs = TRACK_REFS_ATTRIBS; } devDpy = calloc(1, sizeof(WlEglDeviceDpy)); if (devDpy == NULL) { return NULL; } devDpy->eglDevice = device; devDpy->data = data; devDpy->eglDisplay = data->egl.getPlatformDisplay(EGL_PLATFORM_DEVICE_EXT, device, attribs); if (devDpy->eglDisplay == EGL_NO_DISPLAY) { goto fail; } /* Get the device in use, * calling eglQueryDeviceStringEXT(EGL_DRM_RENDER_NODE_FILE_EXT) to get drm fd. * We will be getting the dev_t for the render node and the normal node, since * we don't know for sure which one the compositor will happen to use. */ drmName = data->egl.queryDeviceString(devDpy->eglDevice, EGL_DRM_DEVICE_FILE_EXT); if (!drmName) { goto fail; } /* Then use stat to get the dev_t for this device */ if (stat(drmName, &sb) != 0) { goto fail; } renderName = data->egl.queryDeviceString(devDpy->eglDevice, EGL_DRM_RENDER_NODE_FILE_EXT); if (!renderName) { goto fail; } if (stat(renderName, &render_sb) != 0) { goto fail; } devDpy->dev = sb.st_rdev; devDpy->renderNode = render_sb.st_rdev; wl_list_insert(&data->deviceDpyList, &devDpy->link); return devDpy; fail: free(devDpy); return NULL; } static void wlFreeInternalDisplay(WlEglDeviceDpy *devDpy) { if (devDpy->initCount > 0) { devDpy->data->egl.terminate(devDpy->eglDisplay); } wl_list_remove(&devDpy->link); free(devDpy); } void wlFreeAllInternalDisplays(WlEglPlatformData *data) { WlEglDeviceDpy *devDpy, *devNext; wl_list_for_each_safe(devDpy, devNext, &data->deviceDpyList, link) { assert (devDpy->data == data); wlFreeInternalDisplay(devDpy); } } EGLBoolean wlInternalInitialize(WlEglDeviceDpy *devDpy) { if (devDpy->initCount == 0) { const char *exts; if (!devDpy->data->egl.initialize(devDpy->eglDisplay, &devDpy->major, &devDpy->minor)) { return EGL_FALSE; } exts = devDpy->data->egl.queryString(devDpy->eglDisplay, EGL_EXTENSIONS); #define CACHE_EXT(_PREFIX_, _NAME_) \ devDpy->exts._NAME_ = \ !!wlEglFindExtension("EGL_" #_PREFIX_ "_" #_NAME_, exts) CACHE_EXT(KHR, stream); CACHE_EXT(NV, stream_attrib); CACHE_EXT(KHR, stream_cross_process_fd); CACHE_EXT(NV, stream_remote); CACHE_EXT(KHR, stream_producer_eglsurface); CACHE_EXT(NV, stream_fifo_synchronous); CACHE_EXT(NV, stream_sync); CACHE_EXT(NV, stream_flush); CACHE_EXT(NV, stream_consumer_eglimage); CACHE_EXT(MESA, image_dma_buf_export); #undef CACHE_EXT } devDpy->initCount++; return EGL_TRUE; } EGLBoolean wlInternalTerminate(WlEglDeviceDpy *devDpy) { if (devDpy->initCount > 0) { if (devDpy->initCount == 1) { if (!devDpy->data->egl.terminate(devDpy->eglDisplay)) { return EGL_FALSE; } } devDpy->initCount--; } return EGL_TRUE; } egl-wayland-1.1.18/src/wayland-egldisplay.c000066400000000000000000001564201474153032600205500ustar00rootroot00000000000000/* * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-egldisplay.h" #include "wayland-eglstream-client-protocol.h" #include "wayland-eglstream-controller-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "wayland-eglstream-server.h" #include "wayland-thread.h" #include "wayland-eglsurface-internal.h" #include "wayland-eglhandle.h" #include "wayland-eglutils.h" #include "wayland-drm-client-protocol.h" #include "wayland-drm.h" #include "presentation-time-client-protocol.h" #include "linux-drm-syncobj-v1-client-protocol.h" #include #include #include #include #include #include #include #include typedef struct WlServerProtocolsRec { EGLBoolean hasEglStream; EGLBoolean hasDmaBuf; struct zwp_linux_dmabuf_v1 *wlDmaBuf; dev_t devId; struct wl_drm *wlDrm; char *drm_name; } WlServerProtocols; /* TODO: Make global display lists hang off platform data */ static struct wl_list wlEglDisplayList = WL_LIST_INITIALIZER(&wlEglDisplayList); static bool getDeviceFromDevIdInitialised = false; static int (*getDeviceFromDevId)(dev_t dev_id, uint32_t flags, drmDevice **device) = NULL; EGLBoolean wlEglIsWaylandDisplay(void *nativeDpy) { if (!wlEglMemoryIsReadable(nativeDpy, sizeof (void *))) { return EGL_FALSE; } return wlEglCheckInterfaceType(nativeDpy, "wl_display"); } EGLBoolean wlEglIsValidNativeDisplayExport(void *data, void *nativeDpy) { char *val = getenv("EGL_PLATFORM"); (void)data; if (val && !strcasecmp(val, "wayland")) { return EGL_TRUE; } return wlEglIsWaylandDisplay(nativeDpy); } EGLBoolean wlEglBindDisplaysHook(void *data, EGLDisplay dpy, void *nativeDpy) { /* Retrieve extension string and device name before taking external API lock */ const char *exts = ((WlEglPlatformData *)data)->egl.queryString(dpy, EGL_EXTENSIONS), *dev_name = wl_drm_get_dev_name(data, dpy); EGLBoolean res = EGL_FALSE; wlExternalApiLock(); res = wl_eglstream_display_bind((WlEglPlatformData *)data, (struct wl_display *)nativeDpy, dpy, exts, dev_name); wlExternalApiUnlock(); return res; } EGLBoolean wlEglUnbindDisplaysHook(EGLDisplay dpy, void *nativeDpy) { struct wl_eglstream_display *wlStreamDpy; EGLBoolean res = EGL_FALSE; wlExternalApiLock(); wlStreamDpy = wl_eglstream_display_get(dpy); if (wlStreamDpy && (wlStreamDpy->wlDisplay == (struct wl_display *)nativeDpy)) { wl_eglstream_display_unbind(wlStreamDpy); res = EGL_TRUE; } wlExternalApiUnlock(); return res; } static void wlEglDestroyFormatSet(WlEglDmaBufFormatSet *set) { for (unsigned int i = 0; i < set->numFormats; i++) { free(set->dmaBufFormats[i].modifiers); } free(set->dmaBufFormats); } static void wlEglFeedbackResetTranches(WlEglDmaBufFeedback *feedback) { if (feedback->numTranches == 0) return; wlEglDestroyFormatSet(&feedback->tmpTranche.formatSet); for (int i = 0; i < feedback->numTranches; i++) { wlEglDestroyFormatSet(&feedback->tranches[i].formatSet); } free(feedback->tranches); feedback->tranches = NULL; feedback->numTranches = 0; } #if defined(NV_SUNOS) // Solaris uses the obsolete 'caddr_t' type unless _X_OPEN_SOURCE is set. // Unfortunately, setting that flag breaks a lot of other code so instead // just cast the pointer to the appropriate type. // // caddr_t is defined in sys/types.h to be char*. typedef caddr_t pointer_t; #else typedef void *pointer_t; #endif void wlEglDestroyFeedback(WlEglDmaBufFeedback *feedback) { wlEglFeedbackResetTranches(feedback); munmap((pointer_t)feedback->formatTable.entry, sizeof(feedback->formatTable.entry[0]) * feedback->formatTable.len); if (feedback->wlDmaBufFeedback) { zwp_linux_dmabuf_feedback_v1_destroy(feedback->wlDmaBufFeedback); } } static void wlEglDmaBufFormatAddModifier(WlEglDmaBufFormat *format, const uint64_t modifier) { uint64_t *newModifiers; uint32_t m; for (m = 0; m < format->numModifiers; m++) { if (format->modifiers[m] == modifier) { return; } } newModifiers = realloc(format->modifiers, sizeof(format->modifiers[0]) * (format->numModifiers + 1)); if (!newModifiers) { return; } newModifiers[format->numModifiers] = modifier; format->modifiers = newModifiers; format->numModifiers++; } static void wlEglFormatSetAdd(WlEglDmaBufFormatSet *set, uint32_t format, const uint64_t modifier) { uint32_t f; WlEglDmaBufFormat *newFormats; for (f = 0; f < set->numFormats; f++) { if (set->dmaBufFormats[f].format == format) { wlEglDmaBufFormatAddModifier(&set->dmaBufFormats[f], modifier); return; } } newFormats = realloc(set->dmaBufFormats, sizeof(set->dmaBufFormats[0]) * (set->numFormats + 1)); if (!newFormats) { return; } newFormats[set->numFormats].format = format; newFormats[set->numFormats].numModifiers = 0; newFormats[set->numFormats].modifiers = NULL; wlEglDmaBufFormatAddModifier(&newFormats[set->numFormats], modifier); set->dmaBufFormats = newFormats; set->numFormats++; } static void dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, uint32_t format) { (void)data; (void)dmabuf; (void)format; /* Only use formats that include an associated modifier */ } static void dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf, uint32_t format, uint32_t mod_hi, uint32_t mod_lo) { WlEglDisplay *display = data; const uint64_t modifier = ((uint64_t)mod_hi << 32ULL) | (uint64_t)mod_lo; (void)dmabuf; wlEglFormatSetAdd(&display->formatSet, format, modifier); } static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { .format = dmabuf_handle_format, .modifier = dmabuf_handle_modifier, }; /* * We need to check if the compositor is resending all of the tranche * information. Each tranche event will call this method to see * if the existing format info should be cleared before refilling. */ static void dmabuf_feedback_check_reset_tranches(WlEglDmaBufFeedback *feedback) { if (!feedback->feedbackDone) return; feedback->feedbackDone = false; wlEglFeedbackResetTranches(feedback); } static void dmabuf_feedback_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { WlEglDmaBufFeedback *feedback = data; dev_t devid; (void) dmabuf_feedback; dmabuf_feedback_check_reset_tranches(feedback); assert(dev->size == sizeof(dev_t)); memcpy(&devid, dev->data, sizeof(dev_t)); feedback->mainDev = devid; } static void dmabuf_feedback_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { WlEglDmaBufFeedback *feedback = data; (void) dmabuf_feedback; dmabuf_feedback_check_reset_tranches(feedback); assert(dev->size == sizeof(dev_t)); memcpy(&feedback->tmpTranche.drmDev, dev->data, sizeof(dev_t)); } static void dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, uint32_t flags) { WlEglDmaBufFeedback *feedback = data; (void) dmabuf_feedback; dmabuf_feedback_check_reset_tranches(feedback); if (flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) feedback->tmpTranche.supportsScanout = true; } static void dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *indices) { WlEglDmaBufFeedback *feedback = data; WlEglDmaBufTranche *tmp = &feedback->tmpTranche; uint16_t *index; WlEglDmaBufFormatTableEntry *entry; (void) dmabuf_feedback; dmabuf_feedback_check_reset_tranches(feedback); wl_array_for_each(index, indices) { if (*index >= feedback->formatTable.len) { /* * Index given to us by the compositor is too large to fit in the format table. * This is a compositor bug, just skip it. */ continue; } /* Look up this format/mod in the format table */ entry = &feedback->formatTable.entry[*index]; /* Add it to the in-progress */ wlEglFormatSetAdd(&tmp->formatSet, entry->format, entry->modifier); } } static void dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { WlEglDmaBufFeedback *feedback = data; (void) dmabuf_feedback; /* * No need to call dmabuf_feedback_check_reset_tranches, the other events should have been * triggered first */ feedback->numTranches++; feedback->tranches = realloc(feedback->tranches, sizeof(WlEglDmaBufTranche) * feedback->numTranches); assert(feedback->tranches); /* copy the temporary tranche into the official array */ memcpy(&feedback->tranches[feedback->numTranches - 1], &feedback->tmpTranche, sizeof(WlEglDmaBufTranche)); /* reset the tranche */ memset(&feedback->tmpTranche, 0, sizeof(WlEglDmaBufTranche)); } static void dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { WlEglDmaBufFeedback *feedback = data; (void) dmabuf_feedback; feedback->feedbackDone = feedback->unprocessedFeedback = true; } _Static_assert(sizeof(WlEglDmaBufFormatTableEntry) == 16, "Validate that this struct's layout wasn't modified by the compiler"); static void dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, int32_t fd, uint32_t size) { WlEglDmaBufFeedback *feedback = data; (void) dmabuf_feedback; assert(size % sizeof(WlEglDmaBufFormatTableEntry) == 0); feedback->formatTable.len = size / sizeof(WlEglDmaBufFormatTableEntry); feedback->formatTable.entry = (WlEglDmaBufFormatTableEntry *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (feedback->formatTable.entry == MAP_FAILED) { /* * Could not map the format table: Compositor bug or out of resources */ feedback->formatTable.len = 0; } } static const struct zwp_linux_dmabuf_feedback_v1_listener dmabuf_feedback_listener = { .done = dmabuf_feedback_done, .format_table = dmabuf_feedback_format_table, .main_device = dmabuf_feedback_main_device, .tranche_done = dmabuf_feedback_tranche_done, .tranche_target_device = dmabuf_feedback_tranche_target_device, .tranche_formats = dmabuf_feedback_tranche_formats, .tranche_flags = dmabuf_feedback_tranche_flags, }; int WlEglRegisterFeedback(WlEglDmaBufFeedback *feedback) { return zwp_linux_dmabuf_feedback_v1_add_listener(feedback->wlDmaBufFeedback, &dmabuf_feedback_listener, feedback); } static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { WlEglDisplay *display = (WlEglDisplay *)data; if (strcmp(interface, "wl_eglstream_display") == 0) { display->wlStreamDpy = wl_registry_bind(registry, name, &wl_eglstream_display_interface, 1); } else if (strcmp(interface, "wl_eglstream_controller") == 0) { display->wlStreamCtl = wl_registry_bind(registry, name, &wl_eglstream_controller_interface, version > 1 ? 2 : 1); display->wlStreamCtlVer = version; } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { /* * Version 3 added format modifier support, which the dmabuf * support in this library relies on. */ if (version >= 3) { display->wlDmaBuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version > 3 ? 4 : 3); } display->dmaBufProtocolVersion = version; } else if (strcmp(interface, "wp_presentation") == 0) { display->wpPresentation = wl_registry_bind(registry, name, &wp_presentation_interface, version); } else if (strcmp(interface, "wp_linux_drm_syncobj_manager_v1") == 0 && display->supports_native_fence_sync && display->supports_explicit_sync) { display->wlDrmSyncobj = wl_registry_bind(registry, name, &wp_linux_drm_syncobj_manager_v1_interface, 1); } } static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { (void) data; (void) registry; (void) name; } static const struct wl_registry_listener registry_listener = { registry_handle_global, registry_handle_global_remove }; static void wl_drm_device(void *data, struct wl_drm *wl_drm, const char *name) { WlServerProtocols *protocols = (WlServerProtocols *)data; (void) wl_drm; free(protocols->drm_name); protocols->drm_name = strdup(name); } static void wl_drm_authenticated(void *data, struct wl_drm *wl_drm) { (void) data; (void) wl_drm; } static void wl_drm_format(void *data, struct wl_drm *wl_drm, uint32_t format) { (void) data; (void) wl_drm; (void) format; } static void wl_drm_capabilities(void *data, struct wl_drm *wl_drm, uint32_t value) { (void) data; (void) wl_drm; (void) value; } static const struct wl_drm_listener drmListener = { .device = wl_drm_device, .authenticated = wl_drm_authenticated, .format = wl_drm_format, .capabilities = wl_drm_capabilities, }; static void dmabuf_feedback_check_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { WlServerProtocols *protocols = (WlServerProtocols *)data; (void) dmabuf_feedback; assert(dev->size == sizeof(dev_t)); memcpy(&protocols->devId, dev->data, sizeof(dev_t)); } static void dmabuf_feedback_check_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *dev) { (void) data; (void) dmabuf_feedback; (void) dev; } static void dmabuf_feedback_check_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, uint32_t flags) { (void) data; (void) dmabuf_feedback; (void) flags; } static void dmabuf_feedback_check_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, struct wl_array *indices) { (void) data; (void) dmabuf_feedback; (void) indices; } static void dmabuf_feedback_check_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { (void) data; (void) dmabuf_feedback; } static void dmabuf_feedback_check_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback) { WlServerProtocols *protocols = (WlServerProtocols *)data; drmDevice *drm_device; (void) dmabuf_feedback; assert(getDeviceFromDevId); if (getDeviceFromDevId(protocols->devId, 0, &drm_device) == 0) { if (drm_device->available_nodes & (1 << DRM_NODE_RENDER)) { free(protocols->drm_name); protocols->drm_name = strdup(drm_device->nodes[DRM_NODE_RENDER]); } drmFreeDevice(&drm_device); } } static void dmabuf_feedback_check_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback, int32_t fd, uint32_t size) { (void) data; (void) dmabuf_feedback; (void) fd; (void) size; } static const struct zwp_linux_dmabuf_feedback_v1_listener dmabuf_feedback_check_listener = { .done = dmabuf_feedback_check_done, .format_table = dmabuf_feedback_check_format_table, .main_device = dmabuf_feedback_check_main_device, .tranche_done = dmabuf_feedback_check_tranche_done, .tranche_target_device = dmabuf_feedback_check_tranche_target_device, .tranche_formats = dmabuf_feedback_check_tranche_formats, .tranche_flags = dmabuf_feedback_check_tranche_flags, }; static void registry_handle_global_check_protocols( void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { WlServerProtocols *protocols = (WlServerProtocols *)data; (void) registry; (void) name; (void) version; if (strcmp(interface, "wl_eglstream_display") == 0) { protocols->hasEglStream = EGL_TRUE; } if ((strcmp(interface, "zwp_linux_dmabuf_v1") == 0) && (version >= 3)) { protocols->hasDmaBuf = EGL_TRUE; /* Version 4 introduced default_feedback which allows us to determine the device used by the compositor */ if (version >= 4) { protocols->wlDmaBuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, 4); } } if ((strcmp(interface, "wl_drm") == 0) && (version >= 2)) { protocols->wlDrm = wl_registry_bind(registry, name, &wl_drm_interface, 2); wl_drm_add_listener(protocols->wlDrm, &drmListener, protocols); } } static void eglstream_display_handle_caps(void *data, struct wl_eglstream_display *wlStreamDpy, int32_t caps) { WlEglDisplay *dpy = (WlEglDisplay *)data; (void) wlStreamDpy; #define IS_CAP_SET(CAPS, CAP) (((CAPS)&(CAP)) != 0) dpy->caps.stream_fd = IS_CAP_SET(caps, WL_EGLSTREAM_DISPLAY_CAP_STREAM_FD); dpy->caps.stream_inet = IS_CAP_SET(caps, WL_EGLSTREAM_DISPLAY_CAP_STREAM_INET); dpy->caps.stream_socket = IS_CAP_SET(caps, WL_EGLSTREAM_DISPLAY_CAP_STREAM_SOCKET); #undef IS_CAP_SET } static void eglstream_display_handle_swapinterval_override( void *data, struct wl_eglstream_display *wlStreamDpy, int32_t swapinterval, struct wl_buffer *streamResource) { WlEglDisplay *dpy = (WlEglDisplay *)data; WlEglSurface *surf = NULL; (void) wlStreamDpy; wl_list_for_each(surf, &dpy->wlEglSurfaceList, link) { if (surf->ctx.wlStreamResource == streamResource) { WlEglPlatformData *pData = surf->wlEglDpy->data; EGLDisplay dpy = surf->wlEglDpy->devDpy->eglDisplay; if (pData->egl.swapInterval(dpy, swapinterval)) { surf->swapInterval = swapinterval; } break; } } } static const struct wl_eglstream_display_listener eglstream_display_listener = { eglstream_display_handle_caps, eglstream_display_handle_swapinterval_override, }; /* On wayland, when a wl_display backed EGLDisplay is created and then * wl_display is destroyed without terminating EGLDisplay first, some * driver allocated resources associated with wl_display could not be * destroyed properly during EGL teardown. * Per EGL spec: Termination of a display that has already been terminated, * or has not yet been initialized, is allowed, but the only effect of such * a call is to return EGL_TRUE, since there are no EGL resources associated * with the display to release. * However, in our wayland egl driver, we do allocate some resources * which are associated with wl_display even eglInitialize is not called. * If the app does not terminate EGLDisplay before closing wl_display, * it can hit assertion or hang in pthread_mutex_lock during EGL teardown. * To WAR the issue, in case wl_display has been destroyed, we skip * destroying some resources during EGL system termination, only when * terminateDisplay is called from wlEglDestroyAllDisplays. */ static EGLBoolean terminateDisplay(WlEglDisplay *display, EGLBoolean globalTeardown) { if (display->initCount == 0) { return EGL_TRUE; } /* If globalTeardown is true, then ignore the refcount and terminate the display. That's used when the library is unloaded. */ if (display->initCount > 1 && !globalTeardown) { display->initCount--; return EGL_TRUE; } if (!wlInternalTerminate(display->devDpy)) { if (!globalTeardown) { return EGL_FALSE; } } display->initCount = 0; /* First, destroy any surface associated to the given display. Then * destroy the display connection itself */ wlEglDestroyAllSurfaces(display); if (!globalTeardown || display->ownNativeDpy) { wlEglDestroyFormatSet(&display->formatSet); wlEglDestroyFeedback(&display->defaultFeedback); if (display->wlRegistry) { wl_registry_destroy(display->wlRegistry); display->wlRegistry = NULL; } if (display->wlStreamDpy) { wl_eglstream_display_destroy(display->wlStreamDpy); display->wlStreamDpy = NULL; } if (display->wlStreamCtl) { wl_eglstream_controller_destroy(display->wlStreamCtl); display->wlStreamCtl = NULL; } if (display->wpPresentation) { wp_presentation_destroy(display->wpPresentation); display->wpPresentation = NULL; } if (display->wlDrmSyncobj) { wp_linux_drm_syncobj_manager_v1_destroy(display->wlDrmSyncobj); display->wlDrmSyncobj = NULL; } if (display->wlDmaBuf) { zwp_linux_dmabuf_v1_destroy(display->wlDmaBuf); display->wlDmaBuf = NULL; } /* all proxies using the queue must be destroyed first! */ if (display->wlEventQueue) { wl_event_queue_destroy(display->wlEventQueue); display->wlEventQueue = NULL; } } return EGL_TRUE; } EGLBoolean wlEglTerminateHook(EGLDisplay dpy) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); EGLBoolean res; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); res = terminateDisplay(display, EGL_FALSE); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return res; } static bool getServerProtocolsInfo(struct wl_display *nativeDpy, WlServerProtocols *protocols) { struct wl_display *wrapper = NULL; struct wl_registry *wlRegistry = NULL; struct wl_event_queue *queue = wl_display_create_queue(nativeDpy); int ret = 0; bool result = false; const struct wl_registry_listener registryListener = { registry_handle_global_check_protocols, registry_handle_global_remove }; if (queue == NULL) { goto done; } wrapper = wl_proxy_create_wrapper(nativeDpy); if (wrapper == NULL) { goto done; } wl_proxy_set_queue((struct wl_proxy *)wrapper, queue); /* Listen to wl_registry events and make a roundtrip in order to find the * wl_eglstream_display global object. */ wlRegistry = wl_display_get_registry(wrapper); if (wlRegistry == NULL) { goto done; } ret = wl_registry_add_listener(wlRegistry, ®istryListener, protocols); if (ret == 0) { wl_display_roundtrip_queue(nativeDpy, queue); /* use a second roundtrip to handle any wl_drm events triggered by binding the protocol */ wl_display_roundtrip_queue(nativeDpy, queue); if (!getDeviceFromDevIdInitialised) { getDeviceFromDevId = dlsym(RTLD_DEFAULT, "drmGetDeviceFromDevId"); getDeviceFromDevIdInitialised = true; } /* * if dmabuf feedback is available then use that. This will potentially * replace the drm_name provided by wl_drm, assuming the feedback provides * a valid dev_t. */ if (protocols->wlDmaBuf && getDeviceFromDevId) { struct zwp_linux_dmabuf_feedback_v1 *default_feedback = zwp_linux_dmabuf_v1_get_default_feedback(protocols->wlDmaBuf); if (default_feedback) { zwp_linux_dmabuf_feedback_v1_add_listener(default_feedback, &dmabuf_feedback_check_listener, protocols); wl_display_roundtrip_queue(nativeDpy, queue); zwp_linux_dmabuf_feedback_v1_destroy(default_feedback); } } /* Check that one of our two protocols provided the device name */ result = protocols->drm_name != NULL; if (protocols->wlDmaBuf) { zwp_linux_dmabuf_v1_destroy(protocols->wlDmaBuf); } if (protocols->wlDrm) { wl_drm_destroy(protocols->wlDrm); } } done: if (wrapper) { wl_proxy_wrapper_destroy(wrapper); } if (wlRegistry) { wl_registry_destroy(wlRegistry); } if (queue) { wl_event_queue_destroy(queue); } return result; } static EGLBoolean checkNvidiaDrmDevice(WlServerProtocols *protocols) { int fd = -1; EGLBoolean result = EGL_FALSE; drmVersion *version = NULL; drmDevice *dev = NULL; if (protocols->drm_name == NULL) { goto done; } fd = open(protocols->drm_name, O_RDWR); if (fd < 0) { goto done; } if (drmGetDevice(fd, &dev) == 0) { if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { // Make sure that drm_name is the path to the render node, which is // what wlEglGetPlatformDisplayExport checks for. if (strcmp(protocols->drm_name, dev->nodes[DRM_NODE_RENDER]) != 0) { free(protocols->drm_name); protocols->drm_name = strdup(dev->nodes[DRM_NODE_RENDER]); if (protocols->drm_name == NULL) { goto done; } } } /* * Since we've already called drmGetDevice anyway, if this is a PCI * device, then check if the vendor ID is for NVIDIA. If this is a * Tegra device, though, then it won't be a PCI device, so we'll need * to call drmGetVesion and look at the driver name instead. */ if (dev->bustype == DRM_BUS_PCI && dev->deviceinfo.pci->vendor_id == 0x10de) { result = EGL_TRUE; } } if (!result) { version = drmGetVersion(fd); if (version != NULL && version->name != NULL) { if (strcmp(version->name, "nvidia-drm") == 0 || strcmp(version->name, "tegra-udrm") == 0 || strcmp(version->name, "tegra") == 0) { result = EGL_TRUE; } } } done: if (version != NULL) { drmFreeVersion(version); } if (dev != NULL) { drmFreeDevice(&dev); } if (fd >= 0) { close(fd); } return result; } EGLDisplay wlEglGetPlatformDisplayExport(void *data, EGLenum platform, void *nativeDpy, const EGLAttrib *attribs) { WlEglPlatformData *pData = (WlEglPlatformData *)data; WlEglDisplay *display = NULL; WlServerProtocols protocols = {}; EGLint numDevices = 0; int i = 0; EGLDeviceEXT *eglDeviceList = NULL; EGLDeviceEXT eglDevice = NULL; EGLint err = EGL_SUCCESS; EGLBoolean useInitRefCount = EGL_FALSE; const char *primeRenderOffloadStr; EGLDeviceEXT serverDevice = EGL_NO_DEVICE_EXT; EGLDeviceEXT requestedDevice = EGL_NO_DEVICE_EXT; EGLBoolean usePrimeRenderOffload = EGL_FALSE; EGLBoolean isServerNV; const char *drmName = NULL; if (platform != EGL_PLATFORM_WAYLAND_EXT) { wlEglSetError(data, EGL_BAD_PARAMETER); return EGL_NO_DISPLAY; } /* Check the attribute list. Any attributes are likely to require some * special handling, so reject anything we don't recognize. */ if (attribs) { for (i = 0; attribs[i] != EGL_NONE; i += 2) { if (attribs[i] == EGL_TRACK_REFERENCES_KHR) { if (attribs[i + 1] == EGL_TRUE || attribs[i + 1] == EGL_FALSE) { useInitRefCount = (EGLBoolean) attribs[i + 1]; } else { wlEglSetError(data, EGL_BAD_ATTRIBUTE); return EGL_NO_DISPLAY; } } else if (attribs[i] == EGL_DEVICE_EXT) { requestedDevice = (EGLDeviceEXT) attribs[i + 1]; if (requestedDevice == EGL_NO_DEVICE_EXT) { wlEglSetError(data, EGL_BAD_DEVICE_EXT); return EGL_NO_DISPLAY; } } else { wlEglSetError(data, EGL_BAD_ATTRIBUTE); return EGL_NO_DISPLAY; } } } wlExternalApiLock(); /* First, check if we've got an existing display that matches. */ wl_list_for_each(display, &wlEglDisplayList, link) { if ((display->nativeDpy == nativeDpy || (!nativeDpy && display->ownNativeDpy)) && display->useInitRefCount == useInitRefCount && display->requestedDevice == requestedDevice) { wlExternalApiUnlock(); return (EGLDisplay)display; } } display = calloc(1, sizeof(*display)); if (!display) { err = EGL_BAD_ALLOC; goto fail; } display->data = pData; display->nativeDpy = nativeDpy; display->useInitRefCount = useInitRefCount; display->requestedDevice = requestedDevice; /* If default display is requested, create a new Wayland display connection * and its corresponding internal EGLDisplay. Otherwise, check for existing * associated EGLDisplay for the given Wayland display and if it doesn't * exist, create a new one */ if (!display->nativeDpy) { display->nativeDpy = wl_display_connect(NULL); if (!display->nativeDpy) { err = EGL_BAD_ALLOC; goto fail; } display->ownNativeDpy = EGL_TRUE; wl_display_dispatch_pending(display->nativeDpy); } primeRenderOffloadStr = getenv("__NV_PRIME_RENDER_OFFLOAD"); if (primeRenderOffloadStr && !strcmp(primeRenderOffloadStr, "1")) { usePrimeRenderOffload = EGL_TRUE; } /* * This is where we check the supported protocols on the compositor, * and bind to wl_drm to get the device name. * protocols.drm_name will be allocated here if using wl_drm */ if (!getServerProtocolsInfo(display->nativeDpy, &protocols)) { err = EGL_BAD_ALLOC; goto fail; } // Check if the server is running on an NVIDIA device. This will also make // sure that the device node that we're looking at is a render node, // regardless of which node the server sends back. isServerNV = checkNvidiaDrmDevice(&protocols); if (!usePrimeRenderOffload && requestedDevice == EGL_NO_DEVICE_EXT) { /* * We're not configured to use any sort of GPU offloading, so we only * support this display if the server is running on an NVIDIA GPU. Do * this early, before we call eglQueryDevicesEXT. eglQueryDevicesEXT * might have to power on the GPU's, which can be very slow. */ if (!isServerNV) { err = EGL_SUCCESS; goto fail; } } if (!protocols.hasEglStream && !protocols.hasDmaBuf) { goto fail; } /* Get the number of devices available */ if (!pData->egl.queryDevices(-1, NULL, &numDevices) || numDevices == 0) { goto fail; } eglDeviceList = calloc(numDevices, sizeof(*eglDeviceList)); if (!eglDeviceList) { goto fail; } /* * Now we need to find an EGLDevice. If __NV_PRIME_RENDER_OFFLOAD=1, we will use the * first NVIDIA GPU returned by eglQueryDevices. Otherwise, if wl_drm is in use, we will * try to find one that matches the device the compositor is using. We know that device * is an nvidia device since we just checked that above. */ if (!pData->egl.queryDevices(numDevices, eglDeviceList, &numDevices) || numDevices == 0) { goto fail; } // Try to find the device that the compositor is running on. if (protocols.drm_name) { for (i = 0; i < numDevices; i++) { EGLDeviceEXT tmpDev = eglDeviceList[i]; /* * To check against the wl_drm name, we need to check if we can use * the drm extension */ const char *dev_exts = display->data->egl.queryDeviceString(tmpDev, EGL_EXTENSIONS); if (dev_exts && wlEglFindExtension("EGL_EXT_device_drm_render_node", dev_exts)) { const char *dev_name = display->data->egl.queryDeviceString(tmpDev, EGL_DRM_RENDER_NODE_FILE_EXT); if (dev_name) { /* * At this point we have gotten the name from wl_drm, gotten * the drm node from the EGLDevice. If they match, then * this is the final device to use, since it is the compositor's * device. */ if (strcmp(dev_name, protocols.drm_name) == 0) { serverDevice = tmpDev; break; } } } } } // By default, use whatever device the server is using. eglDevice = serverDevice; if (requestedDevice != EGL_NO_DEVICE_EXT) { // If the app requested a specific device, then use it. // Make sure that the requested device is a valid EGLDeviceEXT handle. EGLBoolean found = EGL_FALSE; for (i = 0; i < numDevices; i++) { if (eglDeviceList[i] == requestedDevice) { found = EGL_TRUE; break; } } if (found) { if (serverDevice != EGL_NO_DEVICE_EXT && serverDevice != requestedDevice) { /* * Don't allow NV -> NV offloading, because it doesn't * currently work in Weston or Mutter. Weston may try to use * the images as scanout buffers (which doesn't work), and * Mutter doesn't correctly handle external-only images. * * Without proper compositor support, This could still work if * the client either does a blit between devices into something * that the compositor can consume, or reads back the image * into an SHM buffer. */ err = EGL_BAD_MATCH; goto fail; } eglDevice = requestedDevice; } else if (!usePrimeRenderOffload) { /* * The EGL_DEVICE_EXT attribute doesn't match any NVIDIA device, * but it might match a non-NV device. If the user requested GPU * offloading, then we'll pick an NVIDIA device below. Otherwise, * fail here so that another driver can handle it. * * We'll generate an EGL_BAD_MATCH error in this case -- * technically, it should be EGL_BAD_DEVICE_EXT if the device is * not valid, or EGL_BAD_MATCH if the device is valid but we can't * use it. We have no way to know if this is a valid device from * Mesa, though, but assume that it is so that a non-buggy * application can get useful feedback. */ err = EGL_BAD_MATCH; goto fail; } } if (eglDevice == EGL_NO_DEVICE_EXT && usePrimeRenderOffload) { /* * If __NV_PRIME_RENDER_OFFLOAD is set, then use an NVIDIA device. It * doesn't matter which one. */ eglDevice = eglDeviceList[0]; } if (eglDevice == EGL_NO_DEVICE_EXT) { // If we couldn't find a device to render on, then fail. goto fail; } if (eglDevice != serverDevice) { /* * If we're rendering with a different device than the compositor is * using, then we'll need to use the PRIME offloading path. */ display->primeRenderOffload = EGL_TRUE; } display->devDpy = wlGetInternalDisplay(pData, eglDevice); if (display->devDpy == NULL) { goto fail; } if (!wlEglInitializeMutex(&display->mutex)) { goto fail; } display->refCount = 1; WL_LIST_INIT(&display->wlEglSurfaceList); /* Get the DRM device in use */ drmName = display->data->egl.queryDeviceString(display->devDpy->eglDevice, EGL_DRM_DEVICE_FILE_EXT); if (!drmName) { goto fail; } display->drmFd = open(drmName, O_RDWR | O_CLOEXEC); if (display->drmFd < 0) { goto fail; } // The newly created WlEglDisplay has been set up properly, insert it // in wlEglDisplayList. wl_list_insert(&wlEglDisplayList, &display->link); free(eglDeviceList); if (protocols.drm_name) { free(protocols.drm_name); } wlExternalApiUnlock(); return display; fail: wlExternalApiUnlock(); free(eglDeviceList); free(protocols.drm_name); if (display && display->ownNativeDpy) { wl_display_disconnect(display->nativeDpy); } free(display); if (err != EGL_SUCCESS) { wlEglSetError(data, err); } return EGL_NO_DISPLAY; } static void wlEglCheckDriverSyncSupport(WlEglDisplay *display) { EGLSyncKHR eglSync = EGL_NO_SYNC_KHR; int syncFd = -1; EGLDisplay dpy = display->devDpy->eglDisplay; EGLint attribs[5]; uint32_t tmpSyncobj; const char *disableExplicitSyncStr = getenv("__NV_DISABLE_EXPLICIT_SYNC"); /* * Don't enable explicit sync if requested by the user or if we do not have * the necessary EGL extensions. */ if ((disableExplicitSyncStr && !strcmp(disableExplicitSyncStr, "1")) || !display->supports_native_fence_sync) { return; } /* make a dummy fd to pass in */ if (drmSyncobjCreate(display->drmFd, 0, &tmpSyncobj) != 0) { return; } if (drmSyncobjHandleToFD(display->drmFd, tmpSyncobj, &syncFd)) { goto destroy; } /* * This call is supposed to fail if the driver is new enough to support * Explicit Sync. Since we don't have an easy way to detect the driver * version number at the moment, we check for some error conditions added * as part of the EGL driver support. Here we check that specifying a valid * fd and a sync object status returns EGL_BAD_ATTRIBUTE. */ attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID; attribs[1] = syncFd; attribs[2] = EGL_SYNC_STATUS; attribs[3] = EGL_SIGNALED; attribs[4] = EGL_NONE; eglSync = display->data->egl.createSync(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); /* If the call failed then the driver version is recent enough */ if (eglSync == EGL_NO_SYNC_KHR && display->data->egl.getError() == EGL_BAD_ATTRIBUTE) { display->supports_explicit_sync = true; } destroy: if (eglSync != EGL_NO_SYNC_KHR) { display->data->egl.destroySync(dpy, eglSync); } drmSyncobjDestroy(display->drmFd, tmpSyncobj); } EGLBoolean wlEglInitializeHook(EGLDisplay dpy, EGLint *major, EGLint *minor) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; struct wl_display *wrapper = NULL; EGLint err = EGL_SUCCESS; int ret = 0; const char *dev_exts = NULL; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); data = display->data; if (display->initCount > 0) { // This display has already been initialized. if (major) { *major = display->devDpy->major; } if (minor) { *minor = display->devDpy->minor; } if (display->useInitRefCount) { display->initCount++; } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_TRUE; } if (!wlInternalInitialize(display->devDpy)) { pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } dev_exts = display->data->egl.queryString(display->devDpy->eglDisplay, EGL_EXTENSIONS); if (dev_exts && wlEglFindExtension("EGL_ANDROID_native_fence_sync", dev_exts)) { display->supports_native_fence_sync = true; } /* Check if we support explicit sync */ wlEglCheckDriverSyncSupport(display); // Set the initCount to 1. If something goes wrong, then terminateDisplay // will clean up and set it back to zero. display->initCount = 1; display->wlEventQueue = wl_display_create_queue(display->nativeDpy);; if (display->wlEventQueue == NULL) { err = EGL_BAD_ALLOC; goto fail; } wrapper = wl_proxy_create_wrapper(display->nativeDpy); wl_proxy_set_queue((struct wl_proxy *)wrapper, display->wlEventQueue); /* Listen to wl_registry events and make a roundtrip in order to find the * wl_eglstream_display and/or zwp_linux_dmabuf_v1 global object */ display->wlRegistry = wl_display_get_registry(wrapper); wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ ret = wl_registry_add_listener(display->wlRegistry, ®istry_listener, display); if (ret == 0) { ret = wl_display_roundtrip_queue(display->nativeDpy, display->wlEventQueue); } if (ret < 0) { err = EGL_BAD_ALLOC; goto fail; } if (display->wlStreamDpy) { /* Listen to wl_eglstream_display events */ ret = wl_eglstream_display_add_listener(display->wlStreamDpy, &eglstream_display_listener, display); } else if (display->wlDmaBuf) { ret = zwp_linux_dmabuf_v1_add_listener(display->wlDmaBuf, &dmabuf_listener, display); if (ret == 0 && display->dmaBufProtocolVersion >= 4) { /* Since the compositor supports it, opt into surface format feedback */ display->defaultFeedback.wlDmaBufFeedback = zwp_linux_dmabuf_v1_get_default_feedback(display->wlDmaBuf); if (display->defaultFeedback.wlDmaBufFeedback) { ret = WlEglRegisterFeedback(&display->defaultFeedback); } } } if (ret < 0 || !(display->wlStreamDpy || display->wlDmaBuf)) { /* This library requires either the EGLStream or dma-buf protocols to * present content to the Wayland compositor. */ err = EGL_BAD_ALLOC; goto fail; } /* * Make another roundtrip so we catch any bind-related event (e.g. server capabilities) */ ret = wl_display_roundtrip_queue(display->nativeDpy, display->wlEventQueue); if (ret < 0) { err = EGL_BAD_ALLOC; goto fail; } /* We haven't created any surfaces yet, so no need to reallocate. */ display->defaultFeedback.unprocessedFeedback = false; if (major != NULL) { *major = display->devDpy->major; } if (minor != NULL) { *minor = display->devDpy->minor; } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_TRUE; fail: terminateDisplay(display, EGL_FALSE); if (err != EGL_SUCCESS) { wlEglSetError(data, err); } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } EGLBoolean wlEglIsWlEglDisplay(WlEglDisplay *display) { WlEglDisplay *dpy; wl_list_for_each(dpy, &wlEglDisplayList, link) { if (dpy == display) { return EGL_TRUE; } } return EGL_FALSE; } WlEglDisplay *wlEglAcquireDisplay(EGLDisplay dpy) { WlEglDisplay *display = (WlEglDisplay *)dpy; wlExternalApiLock(); if (wlEglIsWlEglDisplay(display)) { ++display->refCount; } else { display = NULL; } wlExternalApiUnlock(); return display; } static void wlEglUnrefDisplay(WlEglDisplay *display) { if (--display->refCount == 0) { wlEglMutexDestroy(&display->mutex); close(display->drmFd); free(display); } } void wlEglReleaseDisplay(WlEglDisplay *display) { wlExternalApiLock(); wlEglUnrefDisplay(display); wlExternalApiUnlock(); } EGLBoolean wlEglChooseConfigHook(EGLDisplay dpy, EGLint const *attribs, EGLConfig *configs, EGLint configSize, EGLint *numConfig) { WlEglDisplay *display = (WlEglDisplay *)dpy; WlEglPlatformData *data = display->data; EGLint *attribs2 = NULL; EGLint nAttribs = 0; EGLint nTotalAttribs = 0; EGLBoolean surfType = EGL_FALSE; EGLint err = EGL_SUCCESS; EGLBoolean ret; /* Save the internal EGLDisplay handle, as it's needed by the actual * eglChooseConfig() call */ dpy = display->devDpy->eglDisplay; /* Calculate number of attributes in attribs */ if (attribs) { while (attribs[nAttribs] != EGL_NONE) { surfType = surfType || (attribs[nAttribs] == EGL_SURFACE_TYPE); nAttribs += 2; } } /* If not SURFACE_TYPE provided, we need convert the default WINDOW_BIT to a * default EGL_STREAM_BIT */ nTotalAttribs += (surfType ? nAttribs : (nAttribs + 2)); /* Make attributes list copy */ attribs2 = (EGLint *)malloc((nTotalAttribs + 1) * sizeof(*attribs2)); if (!attribs2) { err = EGL_BAD_ALLOC; goto done; } if (nAttribs > 0) { memcpy(attribs2, attribs, nAttribs * sizeof(*attribs2)); } attribs2[nTotalAttribs] = EGL_NONE; /* Replace all WINDOW_BITs by EGL_STREAM_BITs */ if (surfType) { nAttribs = 0; while (attribs2[nAttribs] != EGL_NONE) { if ((attribs2[nAttribs] == EGL_SURFACE_TYPE) && (attribs2[nAttribs + 1] != EGL_DONT_CARE) && (attribs2[nAttribs + 1] & EGL_WINDOW_BIT)) { attribs2[nAttribs + 1] &= ~EGL_WINDOW_BIT; attribs2[nAttribs + 1] |= EGL_STREAM_BIT_KHR; } nAttribs += 2; } } else { attribs2[nTotalAttribs - 2] = EGL_SURFACE_TYPE; attribs2[nTotalAttribs - 1] = EGL_STREAM_BIT_KHR; } /* Actual eglChooseConfig() call */ ret = data->egl.chooseConfig(dpy, attribs2, configs, configSize, numConfig); done: /* Cleanup */ free(attribs2); if (err != EGL_SUCCESS) { wlEglSetError(data, err); return EGL_FALSE; } return ret; } EGLBoolean wlEglGetConfigAttribHook(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) { WlEglDisplay *display = (WlEglDisplay *)dpy; WlEglPlatformData *data = display->data; EGLBoolean ret = EGL_FALSE; /* Save the internal EGLDisplay handle, as it's needed by the actual * eglGetConfigAttrib() call */ dpy = display->devDpy->eglDisplay; ret = data->egl.getConfigAttrib(dpy, config, attribute, value); if (ret && (attribute == EGL_SURFACE_TYPE)) { /* We only support window configurations through EGLStreams */ if (*value & EGL_STREAM_BIT_KHR) { *value |= EGL_WINDOW_BIT; } else { *value &= ~EGL_WINDOW_BIT; } } return ret; } EGLBoolean wlEglQueryDisplayAttribHook(EGLDisplay dpy, EGLint name, EGLAttrib *value) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; EGLBoolean ret = EGL_TRUE; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); data = display->data; if (value == NULL) { wlEglSetError(data, EGL_BAD_PARAMETER); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } if (display->initCount == 0) { wlEglSetError(data, EGL_NOT_INITIALIZED); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } switch (name) { case EGL_DEVICE_EXT: *value = (EGLAttrib) display->devDpy->eglDevice; break; case EGL_TRACK_REFERENCES_KHR: *value = (EGLAttrib) display->useInitRefCount; break; default: ret = data->egl.queryDisplayAttrib(display->devDpy->eglDisplay, name, value); break; } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return ret; } EGLBoolean wlEglDestroyAllDisplays(WlEglPlatformData *data) { WlEglDisplay *display, *next; EGLBoolean res = EGL_TRUE; wlExternalApiLock(); wl_list_for_each_safe(display, next, &wlEglDisplayList, link) { if (display->data == data) { pthread_mutex_lock(&display->mutex); res = terminateDisplay(display, EGL_TRUE) && res; if (display->ownNativeDpy) { wl_display_disconnect(display->nativeDpy); } display->devDpy = NULL; pthread_mutex_unlock(&display->mutex); wl_list_remove(&display->link); /* Unref the external display */ wlEglUnrefDisplay(display); } } wlFreeAllInternalDisplays(data); wlExternalApiUnlock(); return res; } const char* wlEglQueryStringExport(void *data, EGLDisplay dpy, EGLExtPlatformString name) { WlEglPlatformData *pData = (WlEglPlatformData *)data; EGLBoolean isEGL15 = (pData->egl.major > 1) || ((pData->egl.major == 1) && (pData->egl.minor >= 5)); const char *res = NULL; switch (name) { case EGL_EXT_PLATFORM_PLATFORM_CLIENT_EXTENSIONS: res = isEGL15 ? "EGL_KHR_platform_wayland EGL_EXT_platform_wayland EGL_EXT_explicit_device" : "EGL_EXT_platform_wayland"; break; case EGL_EXT_PLATFORM_DISPLAY_EXTENSIONS: if (dpy == EGL_NO_DISPLAY) { /* This should return all client extensions, which for now is * equivalent to EXTERNAL_PLATFORM_CLIENT_EXTENSIONS */ res = isEGL15 ? "EGL_KHR_platform_wayland EGL_EXT_platform_wayland EGL_EXT_explicit_device" : "EGL_EXT_platform_wayland"; } else { /* * Check whether the given display supports EGLStream * extensions. For Wayland support over EGLStreams, at least the * following extensions must be supported by the underlying * driver: * * - EGL_KHR_stream * - EGL_KHR_stream_producer_eglsurface * - EGL_KHR_stream_cross_process_fd * * For Wayland support via dma-buf, at least the following * extensions must be supported by the underlying driver: * * - EGL_KHR_stream * - EGL_KHR_stream_producer_eglsurface * - EGL_NV_stream_consumer_eglimage * - EGL_MESA_image_dma_buf_export */ const char *exts = pData->egl.queryString(dpy, EGL_EXTENSIONS); if (wlEglFindExtension("EGL_KHR_stream", exts) && wlEglFindExtension("EGL_KHR_stream_producer_eglsurface", exts)) { if (wlEglFindExtension("EGL_KHR_stream_cross_process_fd", exts)) { res = "EGL_EXT_present_opaque EGL_WL_bind_wayland_display " "EGL_WL_wayland_eglstream"; } else if (wlEglFindExtension("EGL_NV_stream_consumer_eglimage", exts) && wlEglFindExtension("EGL_MESA_image_dma_buf_export", exts)) { res = "EGL_EXT_present_opaque EGL_WL_bind_wayland_display"; } } } break; default: break; } return res; } egl-wayland-1.1.18/src/wayland-eglhandle.c000066400000000000000000000167361474153032600203430ustar00rootroot00000000000000/* * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-eglhandle.h" #include "wayland-egldisplay.h" #include "wayland-eglsurface-internal.h" #include "wayland-thread.h" #include #include #include WlEglPlatformData* wlEglCreatePlatformData(int apiMajor, int apiMinor, const EGLExtDriver *driver) { const char *exts = NULL; WlEglPlatformData *res = NULL; assert((driver != NULL) && (driver->getProcAddress != NULL)); /* Allocate platform data and fetch EGL functions */ res = calloc(1, sizeof(WlEglPlatformData)); if (res == NULL) { return NULL; } wl_list_init(&res->deviceDpyList); /* Cache the EGL driver version */ #if EGL_EXTERNAL_PLATFORM_HAS(DRIVER_VERSION) if (EGL_EXTERNAL_PLATFORM_SUPPORTS(apiMajor, apiMinor, DRIVER_VERSION)) { res->egl.major = driver->major; res->egl.minor = driver->minor; } #endif /* Fetch all required driver functions */ #define GET_PROC(_FIELD_, _NAME_) \ do { \ res->egl._FIELD_ = driver->getProcAddress(#_NAME_); \ if (res->egl._FIELD_ == NULL) { \ goto fail; \ } \ } while (0) /* Core and basic stream functionality */ GET_PROC(queryString, eglQueryString); GET_PROC(queryDevices, eglQueryDevicesEXT); /* TODO: use eglGetPlatformDisplay instead of eglGetPlatformDisplayEXT if EGL 1.5 is available */ GET_PROC(getPlatformDisplay, eglGetPlatformDisplayEXT); GET_PROC(initialize, eglInitialize); GET_PROC(terminate, eglTerminate); GET_PROC(chooseConfig, eglChooseConfig); GET_PROC(getConfigAttrib, eglGetConfigAttrib); GET_PROC(querySurface, eglQuerySurface); GET_PROC(getCurrentContext, eglGetCurrentContext); GET_PROC(getCurrentSurface, eglGetCurrentSurface); GET_PROC(makeCurrent, eglMakeCurrent); GET_PROC(createStream, eglCreateStreamKHR); GET_PROC(createStreamFromFD, eglCreateStreamFromFileDescriptorKHR); GET_PROC(createStreamAttrib, eglCreateStreamAttribNV); GET_PROC(getStreamFileDescriptor, eglGetStreamFileDescriptorKHR); GET_PROC(createStreamProducerSurface, eglCreateStreamProducerSurfaceKHR); GET_PROC(createPbufferSurface, eglCreatePbufferSurface); GET_PROC(destroyStream, eglDestroyStreamKHR); GET_PROC(destroySurface, eglDestroySurface); GET_PROC(swapBuffers, eglSwapBuffers); GET_PROC(swapBuffersWithDamage, eglSwapBuffersWithDamageKHR); GET_PROC(swapInterval, eglSwapInterval); GET_PROC(getError, eglGetError); GET_PROC(releaseThread, eglReleaseThread); /* From EGL_EXT_device_query, used by the wayland-drm implementation */ GET_PROC(queryDisplayAttrib, eglQueryDisplayAttribEXT); GET_PROC(queryDeviceString, eglQueryDeviceStringEXT); #undef GET_PROC /* Fetch all optional driver functions */ #define GET_PROC(_FIELD_, _NAME_) \ res->egl._FIELD_ = driver->getProcAddress(#_NAME_) /* Used by damage thread */ GET_PROC(queryStream, eglQueryStreamKHR); GET_PROC(queryStreamu64, eglQueryStreamu64KHR); GET_PROC(createStreamSync, eglCreateStreamSyncNV); GET_PROC(clientWaitSync, eglClientWaitSyncKHR); GET_PROC(signalSync, eglSignalSyncKHR); GET_PROC(destroySync, eglDestroySyncKHR); GET_PROC(createSync, eglCreateSyncKHR); GET_PROC(dupNativeFenceFD, eglDupNativeFenceFDANDROID); /* Stream flush */ GET_PROC(streamFlush, eglStreamFlushNV); /* EGLImage Stream consumer and dependencies */ GET_PROC(streamImageConsumerConnect, eglStreamImageConsumerConnectNV); GET_PROC(streamAcquireImage, eglStreamAcquireImageNV); GET_PROC(streamReleaseImage, eglStreamReleaseImageNV); GET_PROC(queryStreamConsumerEvent, eglQueryStreamConsumerEventNV); GET_PROC(exportDMABUFImage, eglExportDMABUFImageMESA); GET_PROC(exportDMABUFImageQuery, eglExportDMABUFImageQueryMESA); GET_PROC(createImage, eglCreateImageKHR); GET_PROC(destroyImage, eglDestroyImageKHR); #undef GET_PROC /* Check for required EGL client extensions */ exts = res->egl.queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); if (exts == NULL) { goto fail; } /* * Note EGL_EXT_platform_device implies support for EGL_EXT_device_base, * which is equivalent to the combination of EGL_EXT_device_query and * EGL_EXT_device_enumeration. The wayland-drm implementation assumes * EGL_EXT_device_query is supported based on this check. */ if (!wlEglFindExtension("EGL_EXT_platform_base", exts) || !wlEglFindExtension("EGL_EXT_platform_device", exts)) { goto fail; } res->supportsDisplayReference = wlEglFindExtension("EGL_KHR_display_reference", exts); /* Cache driver imports */ res->callbacks.setError = driver->setError; res->callbacks.streamSwapInterval = driver->streamSwapInterval; return res; fail: free(res); return NULL; } void wlEglDestroyPlatformData(WlEglPlatformData *data) { free(data); } void* wlEglGetInternalHandleExport(EGLDisplay dpy, EGLenum type, void *handle) { WlEglDisplay *display; if (type == EGL_OBJECT_DISPLAY_KHR) { display = wlEglAcquireDisplay(handle); if (display) { handle = (void *)display->devDpy->eglDisplay; wlEglReleaseDisplay(display); } } else if (type == EGL_OBJECT_SURFACE_KHR) { display = wlEglAcquireDisplay(dpy); if (display) { pthread_mutex_lock(&display->mutex); if (wlEglIsWlEglSurfaceForDisplay(display, (WlEglSurface *)handle)) { handle = (void *)(((WlEglSurface *)handle)->ctx.eglSurface); } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(dpy); } } return handle; } egl-wayland-1.1.18/src/wayland-eglstream-server.c000066400000000000000000000322741474153032600217020ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wayland-eglstream-server.h" #include "wayland-eglstream-server-protocol.h" #include "wayland-eglstream.h" #include "wayland-drm.h" #include "wayland-eglswap.h" #include "wayland-eglutils.h" #include "wayland-thread.h" #define MASK(_VAL_) (1 << (_VAL_)) static struct wl_list wlStreamDpyList = WL_LIST_INITIALIZER(&wlStreamDpyList); static void destroy_wl_eglstream_resource(struct wl_resource *resource) { struct wl_eglstream *wlStream = wl_resource_get_user_data(resource); if (wlStream->handle >= 0) { close(wlStream->handle); } free(wlStream); } static void destroy_wl_eglstream(struct wl_client *client, struct wl_resource *resource) { (void) client; wl_resource_destroy(resource); } static void handle_create_stream(struct wl_client *client, struct wl_resource *resource, uint32_t id, int32_t width, int32_t height, int handle, int handle_type, struct wl_array *attribs) { struct wl_eglstream_display *wlStreamDpy = wl_resource_get_user_data(resource); struct wl_eglstream *wlStream; struct sockaddr_in sockAddr; char sockAddrStr[NI_MAXHOST]; intptr_t *attr; int mask = 0; enum wl_eglstream_error err; wlStream = calloc(1, sizeof *wlStream); if (wlStream == NULL) { err = WL_EGLSTREAM_ERROR_BAD_ALLOC; goto error_create_stream; } wlStream->wlStreamDpy = wlStreamDpy; wlStream->eglStream = EGL_NO_STREAM_KHR; wlStream->width = width; wlStream->height = height; wlStream->handle = -1; wlStream->yInverted = EGL_FALSE; memset(&sockAddr, 0, sizeof(sockAddr)); switch (handle_type) { case WL_EGLSTREAM_HANDLE_TYPE_FD: wlStream->handle = handle; wlStream->fromFd = EGL_TRUE; break; case WL_EGLSTREAM_HANDLE_TYPE_INET: sockAddr.sin_family = AF_INET; wlStream->isInet = EGL_TRUE; /* Close the given dummy fd */ close(handle); break; case WL_EGLSTREAM_HANDLE_TYPE_SOCKET: wlStream->handle = handle; break; default: err = WL_EGLSTREAM_ERROR_BAD_HANDLE; goto error_create_stream; } wl_array_for_each(attr, attribs) { switch (attr[0]) { case WL_EGLSTREAM_ATTRIB_INET_ADDR: /* INET_ADDR should only be set once */ if (mask & MASK(WL_EGLSTREAM_ATTRIB_INET_ADDR)) { err = WL_EGLSTREAM_ERROR_BAD_ATTRIBS; goto error_create_stream; } sockAddr.sin_addr.s_addr = htonl((int)attr[1]); mask |= MASK(WL_EGLSTREAM_ATTRIB_INET_ADDR); break; case WL_EGLSTREAM_ATTRIB_INET_PORT: /* INET_PORT should only be set once */ if (mask & MASK(WL_EGLSTREAM_ATTRIB_INET_PORT)) { err = WL_EGLSTREAM_ERROR_BAD_ATTRIBS; goto error_create_stream; } sockAddr.sin_port = htons((int)attr[1]); mask |= MASK(WL_EGLSTREAM_ATTRIB_INET_PORT); break; case WL_EGLSTREAM_ATTRIB_Y_INVERTED: /* Y_INVERTED should only be set once */ if (mask & MASK(WL_EGLSTREAM_ATTRIB_Y_INVERTED)) { err = WL_EGLSTREAM_ERROR_BAD_ATTRIBS; goto error_create_stream; } wlStream->yInverted = (EGLBoolean)attr[1]; mask |= MASK(WL_EGLSTREAM_ATTRIB_Y_INVERTED); break; default: assert(!"Unknown attribute"); break; } /* Attribs processed in pairs */ attr++; } if (wlStream->isInet) { /* Both address and port should have been set */ if (mask != (MASK(WL_EGLSTREAM_ATTRIB_INET_ADDR) | MASK(WL_EGLSTREAM_ATTRIB_INET_PORT))) { err = WL_EGLSTREAM_ERROR_BAD_ATTRIBS; goto error_create_stream; } wlStream->handle = socket(AF_INET, SOCK_STREAM, 0); if (wlStream->handle == -1) { err = WL_EGLSTREAM_ERROR_BAD_ALLOC; goto error_create_stream; } if (connect(wlStream->handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)) < 0) { err = WL_EGLSTREAM_ERROR_BAD_ADDRESS; goto error_create_stream; } } wlStream->resource = wl_resource_create(client, &wl_buffer_interface, 1, id); if (!wlStream->resource) { err = WL_EGLSTREAM_ERROR_BAD_ALLOC; goto error_create_stream; } wl_resource_set_implementation( wlStream->resource, (void (**)(void))&wlStreamDpy->wl_eglstream_interface, wlStream, destroy_wl_eglstream_resource); return; error_create_stream: switch (err) { case WL_EGLSTREAM_ERROR_BAD_ALLOC: wl_resource_post_no_memory(resource); break; case WL_EGLSTREAM_ERROR_BAD_HANDLE: wl_resource_post_error(resource, err, "Invalid or unknown handle"); break; case WL_EGLSTREAM_ERROR_BAD_ATTRIBS: wl_resource_post_error(resource, err, "Malformed attributes list"); break; case WL_EGLSTREAM_ERROR_BAD_ADDRESS: wl_resource_post_error(resource, err, "Unable to connect to %s:%d.", (getnameinfo((struct sockaddr *)&sockAddr, sizeof(sockAddr), sockAddrStr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) ? "" : sockAddrStr), ntohs(sockAddr.sin_port)); break; default: assert(!"Unknown error code"); break; } if (wlStream) { if (wlStream->isInet && wlStream->handle >= 0) { close(wlStream->handle); } free(wlStream); } } static void handle_swap_interval(struct wl_client *client, struct wl_resource *displayResource, struct wl_resource *streamResource, int interval) { struct wl_eglstream_display *wlStreamDpy = wl_resource_get_user_data(displayResource); struct wl_eglstream *wlStream = wl_eglstream_display_get_stream(wlStreamDpy, streamResource); (void) client; if (wlEglStreamSwapIntervalCallback(wlStreamDpy->data, wlStream->eglStream, &interval) == EGL_BAD_MATCH) { wl_eglstream_display_send_swapinterval_override(displayResource, interval, streamResource); } } static const struct wl_eglstream_display_interface wl_eglstream_display_interface_impl = { handle_create_stream, handle_swap_interval, }; static void wl_eglstream_display_global_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_eglstream_display *wlStreamDpy = NULL; struct wl_resource *resource = NULL; wlStreamDpy = (struct wl_eglstream_display *)data; resource = wl_resource_create(client, &wl_eglstream_display_interface, version, id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &wl_eglstream_display_interface_impl, data, NULL); wl_eglstream_display_send_caps(resource, wlStreamDpy->supported_caps); } EGLBoolean wl_eglstream_display_bind(WlEglPlatformData *data, struct wl_display *wlDisplay, EGLDisplay eglDisplay, const char *exts, const char *dev_name) { struct wl_eglstream_display *wlStreamDpy = NULL; char *env = NULL; /* Check whether there's an EGLDisplay already bound to the given * wl_display */ if (wl_eglstream_display_get(eglDisplay) != NULL) { return EGL_FALSE; } wlStreamDpy = calloc(1, sizeof(*wlStreamDpy)); if (!wlStreamDpy) { return EGL_FALSE; } wlStreamDpy->data = data; wlStreamDpy->wlDisplay = wlDisplay; wlStreamDpy->eglDisplay = eglDisplay; wlStreamDpy->caps_override = 0; #define CACHE_EXT(_PREFIX_, _NAME_) \ wlStreamDpy->exts._NAME_ = \ !!wlEglFindExtension("EGL_" #_PREFIX_ "_" #_NAME_, exts) CACHE_EXT(NV, stream_attrib); CACHE_EXT(KHR, stream_cross_process_fd); CACHE_EXT(NV, stream_remote); CACHE_EXT(NV, stream_socket); CACHE_EXT(NV, stream_socket_inet); CACHE_EXT(NV, stream_socket_unix); CACHE_EXT(NV, stream_origin); #undef CACHE_EXT /* Advertise server capabilities */ if (wlStreamDpy->exts.stream_cross_process_fd) { wlStreamDpy->supported_caps |= WL_EGLSTREAM_DISPLAY_CAP_STREAM_FD; } if (wlStreamDpy->exts.stream_attrib && wlStreamDpy->exts.stream_remote && wlStreamDpy->exts.stream_socket) { if (wlStreamDpy->exts.stream_socket_inet) { wlStreamDpy->supported_caps |= WL_EGLSTREAM_DISPLAY_CAP_STREAM_INET; } if (wlStreamDpy->exts.stream_socket_unix) { wlStreamDpy->supported_caps |= WL_EGLSTREAM_DISPLAY_CAP_STREAM_SOCKET; } } env = getenv("WL_EGLSTREAM_CAP_OVERRIDE"); if (env) { int serverCapOverride = atoi(env); wlStreamDpy->caps_override = (wlStreamDpy->supported_caps & serverCapOverride) != wlStreamDpy->supported_caps; wlStreamDpy->supported_caps &= serverCapOverride; } wlStreamDpy->wl_eglstream_interface.destroy = destroy_wl_eglstream; wlStreamDpy->global = wl_global_create(wlDisplay, &wl_eglstream_display_interface, 1, wlStreamDpy, wl_eglstream_display_global_bind); /* Failure is not fatal */ wl_drm_display_bind(wlDisplay, wlStreamDpy, dev_name); wl_list_insert(&wlStreamDpyList, &wlStreamDpy->link); return EGL_TRUE; } void wl_eglstream_display_unbind(struct wl_eglstream_display *wlStreamDpy) { wl_drm_display_unbind(wlStreamDpy); wl_global_destroy(wlStreamDpy->global); wl_list_remove(&wlStreamDpy->link); free(wlStreamDpy); } struct wl_eglstream_display* wl_eglstream_display_get(EGLDisplay eglDisplay) { struct wl_eglstream_display *wlDisplay; wl_list_for_each(wlDisplay, &wlStreamDpyList, link) { if (wlDisplay->eglDisplay == eglDisplay) { return wlDisplay; } } return NULL; } struct wl_eglstream* wl_eglstream_display_get_stream(struct wl_eglstream_display *wlStreamDpy, struct wl_resource *resource) { if (resource == NULL) { return NULL; } if (wl_resource_instance_of(resource, &wl_buffer_interface, &wlStreamDpy->wl_eglstream_interface)) { return wl_resource_get_user_data(resource); } else { return NULL; } } egl-wayland-1.1.18/src/wayland-eglstream.c000066400000000000000000000211011474153032600203610ustar00rootroot00000000000000/* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-eglstream.h" #include "wayland-eglstream-server.h" #include "wayland-thread.h" #include "wayland-eglhandle.h" #include "wayland-egldisplay.h" #include "wayland-eglutils.h" #include "wayland-egl-ext.h" #include #include #include #define WL_EGL_CONN_WAIT_USECS 1e3 /* 1 msec */ #define WL_EGL_CONN_TIMEOUT_USECS 1e6 /* 1 sec */ EGLStreamKHR wlEglCreateStreamAttribHook(EGLDisplay dpy, const EGLAttrib *attribs) { WlEglPlatformData *data = NULL; EGLStreamKHR stream = EGL_NO_STREAM_KHR; struct wl_eglstream_display *wlStreamDpy = NULL; struct wl_resource *resource = NULL; struct wl_eglstream *wlStream = NULL; int nAttribs = 0; int idx = 0; int fd = -1; EGLint err = EGL_SUCCESS; /* Parse attribute list and count internal attributes */ if (attribs) { while (attribs[idx] != EGL_NONE) { if (attribs[idx] == EGL_WAYLAND_EGLSTREAM_WL) { if (resource != NULL) { err = EGL_BAD_MATCH; break; } resource = (struct wl_resource *)attribs[idx + 1]; if (resource == NULL) { err = EGL_BAD_ACCESS; break; } } else { /* Internal attribute */ nAttribs++; } idx += 2; } } if ((err == EGL_SUCCESS) && (resource == NULL)) { /* No EGL_WAYLAND_EGLSTREAM_WL attribute provided, which means dpy is * external. Forward this call to the underlying driver as there's * nothing to do here */ WlEglDisplay *display = (WlEglDisplay *)dpy; return display->data->egl.createStreamAttrib(display->devDpy->eglDisplay, attribs); } /* Otherwise, we must create a server-side stream */ wlExternalApiLock(); wlStreamDpy = wl_eglstream_display_get(dpy); if (wlStreamDpy == NULL) { err = EGL_BAD_ACCESS; } else { data = wlStreamDpy->data; } if (err != EGL_SUCCESS) { goto fail; } wlStream = wl_eglstream_display_get_stream(wlStreamDpy, resource); if (wlStream == NULL) { err = EGL_BAD_ACCESS; goto fail; } if (wlStream->eglStream != EGL_NO_STREAM_KHR || wlStream->handle == -1) { err = EGL_BAD_STREAM_KHR; goto fail; } if (wlStream->fromFd) { /* Check for EGL_KHR_stream_cross_process_fd support */ if (!wlStreamDpy->exts.stream_cross_process_fd) { err = EGL_BAD_ACCESS; goto fail; } /* eglCreateStreamFromFileDescriptorKHR from * EGL_KHR_stream_cross_process_fd does not take attributes. Thus, only * EGL_WAYLAND_EGLSTREAM_WL should have been specified and processed * above. caps_override is an exception to this, since the wayland * compositor calling into this function wouldn't be aware of an * override in place */ if (nAttribs != 0 && !wlStreamDpy->caps_override) { err = EGL_BAD_ATTRIBUTE; goto fail; } fd = wlStream->handle; stream = data->egl.createStreamFromFD(dpy, wlStream->handle); /* Clean up */ close(fd); wlStream->handle = -1; } #if defined(EGL_NV_stream_attrib) && \ defined(EGL_NV_stream_remote) && \ defined(EGL_NV_stream_socket) else { EGLAttrib *attribs2 = NULL; /* Check for required extensions support */ if (!wlStreamDpy->exts.stream_attrib || !wlStreamDpy->exts.stream_remote || !wlStreamDpy->exts.stream_socket || (!wlStreamDpy->exts.stream_socket_inet && !wlStreamDpy->exts.stream_socket_unix)) { err = EGL_BAD_ACCESS; goto fail; } /* If not inet connection supported, wlStream should not be inet */ if (!wlStreamDpy->exts.stream_socket_inet && wlStream->isInet) { err = EGL_BAD_ACCESS; goto fail; } /* Create attributes array to pass down to the actual EGL stream * creation function */ attribs2 = (EGLAttrib *)malloc((2*(nAttribs + 5) + 1)*sizeof(*attribs2)); nAttribs = 0; attribs2[nAttribs++] = EGL_STREAM_TYPE_NV; attribs2[nAttribs++] = EGL_STREAM_CROSS_PROCESS_NV; attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_NV; attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_SOCKET_NV; attribs2[nAttribs++] = EGL_STREAM_ENDPOINT_NV; attribs2[nAttribs++] = EGL_STREAM_CONSUMER_NV; attribs2[nAttribs++] = EGL_SOCKET_TYPE_NV; attribs2[nAttribs++] = (wlStream->isInet ? EGL_SOCKET_TYPE_INET_NV : EGL_SOCKET_TYPE_UNIX_NV); attribs2[nAttribs++] = EGL_SOCKET_HANDLE_NV; attribs2[nAttribs++] = (EGLAttrib)wlStream->handle; /* Include internal attributes given by the application */ while (attribs && attribs[0] != EGL_NONE) { switch (attribs[0]) { /* Filter out external attributes */ case EGL_WAYLAND_EGLSTREAM_WL: break; /* EGL_NV_stream_remote attributes shouldn't be set by the * application */ case EGL_STREAM_TYPE_NV: case EGL_STREAM_PROTOCOL_NV: case EGL_STREAM_ENDPOINT_NV: case EGL_SOCKET_TYPE_NV: case EGL_SOCKET_HANDLE_NV: free(attribs2); err = EGL_BAD_ATTRIBUTE; goto fail; /* Everything else is fine and will be handled by EGL */ default: attribs2[nAttribs++] = attribs[0]; attribs2[nAttribs++] = attribs[1]; } attribs += 2; } attribs2[nAttribs] = EGL_NONE; stream = data->egl.createStreamAttrib(dpy, attribs2); /* Clean up */ free(attribs2); if (stream != EGL_NO_STREAM_KHR) { /* Wait for the stream to establish connection with the producer's * side */ uint32_t timeout = WL_EGL_CONN_TIMEOUT_USECS; EGLint state = EGL_STREAM_STATE_INITIALIZING_NV; do { usleep(WL_EGL_CONN_WAIT_USECS); timeout -= WL_EGL_CONN_WAIT_USECS; if (!data->egl.queryStream(dpy, stream, EGL_STREAM_STATE_KHR, &state)) { break; } } while ((state == EGL_STREAM_STATE_INITIALIZING_NV) && (timeout > 0)); if (state == EGL_STREAM_STATE_INITIALIZING_NV) { data->egl.destroyStream(dpy, stream); stream = EGL_NO_STREAM_KHR; } } } #endif if (stream == EGL_NO_STREAM_KHR) { err = EGL_BAD_ACCESS; goto fail; } wlStream->eglStream = stream; wlStream->handle = -1; wlExternalApiUnlock(); return stream; fail: wlExternalApiUnlock(); wlEglSetError(data, err); return EGL_NO_STREAM_KHR; } egl-wayland-1.1.18/src/wayland-eglsurface.c000066400000000000000000003117511474153032600205330ustar00rootroot00000000000000/* * Copyright (c) 2014-2024, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-eglsurface-internal.h" #include "wayland-eglstream-client-protocol.h" #include "wayland-eglstream-controller-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "linux-drm-syncobj-v1-client-protocol.h" #include "wayland-eglstream-server.h" #include "wayland-thread.h" #include "wayland-eglutils.h" #include "wayland-egl-ext.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE 3 #define MAX_IMAGES 4 /* The swapchain image count */ enum BufferReleaseThreadEvents { BUFFER_RELEASE_THREAD_EVENT_TERMINATE, }; enum BufferReleasePipeEndpoints { BUFFER_RELEASE_PIPE_READ = 0, BUFFER_RELEASE_PIPE_WRITE = 1 }; static WlEglStreamImage * pop_acquired_image(WlEglSurface *surface); static void remove_surface_image(WlEglDisplay *display, WlEglSurface *surface, EGLImageKHR eglImage); static EGLBoolean validateSurfaceAttrib(EGLAttrib attrib, EGLAttrib value); static EGLint assignWlEglSurfaceAttribs(WlEglSurface *surface, const EGLAttrib *attribs); EGLBoolean wlEglIsWlEglSurfaceForDisplay(WlEglDisplay *display, WlEglSurface *surface) { WlEglSurface *surf; wl_list_for_each(surf, &display->wlEglSurfaceList, link) { if (surf == surface) { return EGL_TRUE; } } return EGL_FALSE; } EGLBoolean wlEglIsWaylandWindowValid(struct wl_egl_window *window) { struct wl_surface *surface = NULL; if (!window || !wlEglMemoryIsReadable(window, sizeof (*window))) { return EGL_FALSE; } surface = (struct wl_surface *)window->version; if (!wlEglMemoryIsReadable(surface, sizeof (void *))) { surface = window->surface; if (!wlEglMemoryIsReadable(surface, sizeof (void *))) { return EGL_FALSE; } } return wlEglCheckInterfaceType((struct wl_object *)surface, "wl_surface"); } static void wayland_throttleCallback(void *data, struct wl_callback *callback, uint32_t time) { WlEglSurface *surface = (WlEglSurface *)data; (void) time; if (surface->throttleCallback != NULL) { wl_callback_destroy(callback); surface->throttleCallback = NULL; } } static const struct wl_callback_listener throttle_listener = { wayland_throttleCallback }; void wlEglCreateFrameSync(WlEglSurface *surface) { struct wl_surface *wrapper = NULL; assert(surface->wlEventQueue); if (surface->swapInterval > 0) { wrapper = wl_proxy_create_wrapper(surface->wlSurface); wl_proxy_set_queue((struct wl_proxy *)wrapper, surface->wlEventQueue); surface->throttleCallback = wl_surface_frame(wrapper); wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ if (wl_callback_add_listener(surface->throttleCallback, &throttle_listener, surface) == -1) { return; } } } EGLint wlEglWaitFrameSync(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; struct wl_event_queue *queue = surface->wlEventQueue; int ret = 0; assert(queue || surface->throttleCallback == NULL); while (ret != -1 && surface->throttleCallback != NULL) { ret = wl_display_dispatch_queue(display->nativeDpy, queue); } return EGL_SUCCESS; } static bool syncobj_import_fd_to_current_point(WlEglDisplay *display, WlEglSurface *surface, int syncFd) { bool ret = false; uint32_t tmpSyncobj; /* Import our syncfd at a new release point */ if (drmSyncobjCreate(display->drmFd, 0, &tmpSyncobj) != 0) { return false; } if (drmSyncobjImportSyncFile(display->drmFd, tmpSyncobj, syncFd) != 0) { goto end; } if (drmSyncobjTransfer(display->drmFd, surface->drmSyncobjHandle, surface->syncPoint, tmpSyncobj, 0, 0) != 0) { goto end; } ret = true; end: drmSyncobjDestroy(display->drmFd, tmpSyncobj); return ret; } static bool send_explicit_sync_points (WlEglDisplay *display, WlEglSurface *surface, WlEglStreamImage *image) { WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; int syncFd, err; uint64_t acquireSyncPoint; /* Ignore this unless we are using Explicit Sync */ if (!surface->wlSyncobjSurf) { return true; } /* --------------- Get acquire sync fd -------------- */ syncFd = data->egl.dupNativeFenceFD(dpy, image->acquireSync); if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { return false; } /* Clean up our acquire sync object now that we are done with it */ data->egl.destroySync(dpy, image->acquireSync); image->acquireSync = EGL_NO_SYNC_KHR; err = syncobj_import_fd_to_current_point(display, surface, syncFd); close(syncFd); if (!err) { return false; } acquireSyncPoint = surface->syncPoint++; /* --------------- Get release EGLSyncKHR -------------- */ /* Increment to a new sync point here in the image. */ image->releasePoint++; image->releasePending = true; /* --------------- Send sync points -------------- */ /* Now notify the compositor of our next acquire point */ wp_linux_drm_syncobj_surface_v1_set_acquire_point(surface->wlSyncobjSurf, surface->wlAcquireTimeline, acquireSyncPoint >> 32, acquireSyncPoint & 0xffffffff); /* Now notify the compositor of our next release point */ wp_linux_drm_syncobj_surface_v1_set_release_point(surface->wlSyncobjSurf, image->wlReleaseTimeline, image->releasePoint >> 32, image->releasePoint & 0xffffffff); return true; } EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface, struct wl_event_queue *queue, EGLint *rects, EGLint n_rects) { struct wl_display *wlDpy = surface->wlEglDpy->nativeDpy; EGLint i; if (surface->ctx.wlStreamResource) { /* Attach same buffer to indicate new content for the surface is * made available by the client */ wl_surface_attach(surface->wlSurface, surface->ctx.wlStreamResource, surface->dx, surface->dy); } else { WlEglStreamImage *image; if (wlEglHandleImageStreamEvents(surface) != EGL_SUCCESS) { return EGL_FALSE; } image = pop_acquired_image(surface); if (!image) { return EGL_FALSE; } surface->ctx.currentBuffer = image->buffer; image->attached = EGL_TRUE; /* * Send our explicit sync acquire and release points. This needs to be done * as part of the surface attach as it is a protocol error to specify these * points without attaching a buffer in the same commit. * * Perform this before wl_surface_attach in case there is an error importing * the syncfd at the current timeline point. If this errors out after the * attach has happened then we are stuck with a protocol error from not * specifying the timeline sync points. */ if (!send_explicit_sync_points(surface->wlEglDpy, surface, image)) { return EGL_FALSE; } wl_surface_attach(surface->wlSurface, surface->ctx.currentBuffer, surface->dx, surface->dy); } if (n_rects > 0 && (wl_proxy_get_version((struct wl_proxy *)surface->wlSurface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)) { for (i = 0; i < n_rects; i++) { const EGLint *rect = &rects[i * 4]; // Coordinate systems are flipped between eglSwapBuffersWithDamage // and wl_surface_damage_buffer, so invert Y values. int inv_y = surface->height - (rect[1] + rect[3]); wl_surface_damage_buffer(surface->wlSurface, rect[0], inv_y, rect[2], rect[3]); } } else { wl_surface_damage(surface->wlSurface, 0, 0, INT32_MAX, INT32_MAX); } wl_surface_commit(surface->wlSurface); surface->ctx.isAttached = EGL_TRUE; return (wl_display_roundtrip_queue(wlDpy, queue) >= 0) ? EGL_TRUE : EGL_FALSE; } static void* damage_thread(void *args) { WlEglSurface *surface = (WlEglSurface*)args; WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; struct wl_event_queue *queue = wl_display_create_queue( display->nativeDpy); int ok = (queue != NULL); EGLint state; while (ok) { // Unsignal sync and check latest frame and stream state // Done if any functions fail or stream has disconnected. ok = data->egl.signalSync(display->devDpy->eglDisplay, surface->ctx.damageThreadSync, EGL_UNSIGNALED_KHR) && data->egl.queryStreamu64(display->devDpy->eglDisplay, surface->ctx.eglStream, EGL_PRODUCER_FRAME_KHR, &surface->ctx.framesFinished) && data->egl.queryStream(display->devDpy->eglDisplay, surface->ctx.eglStream, EGL_STREAM_STATE_KHR, &state) && (state != EGL_STREAM_STATE_DISCONNECTED_KHR); // If flush has been requested, trigger shutdown once // last produced frame has been processed. if (surface->ctx.damageThreadFlush) { if (surface->ctx.framesProcessed == surface->ctx.framesProduced) { surface->ctx.damageThreadShutdown = 1; } } // If shutdown has been requested, we're done if (surface->ctx.damageThreadShutdown) { ok = 0; } // We only expect a valid wlEglWin to be set when using // a surface created with EGL_KHR_platform_wayland. if(!wlEglIsWaylandDisplay(display->nativeDpy) || (surface->isSurfaceProducer && !wlEglIsWaylandWindowValid(surface->wlEglWin))) { ok = 0; } // If not done, keep handling frames if (ok) { pthread_mutex_lock(&surface->mutexFrameSync); // If there's an unprocessed frame ready, send damage event if (surface->ctx.framesFinished != surface->ctx.framesProcessed) { if (display->devDpy->exts.stream_flush) { data->egl.streamFlush(display->devDpy->eglDisplay, surface->ctx.eglStream); } wlEglCreateFrameSync(surface); ok = wlEglSendDamageEvent(surface, queue, NULL, 0); surface->ctx.framesProcessed++; pthread_cond_signal(&surface->condFrameSync); pthread_mutex_unlock(&surface->mutexFrameSync); } // Otherwise, wait for sync to trigger else { pthread_mutex_unlock(&surface->mutexFrameSync); ok = (EGL_CONDITION_SATISFIED_KHR == data->egl.clientWaitSync(display->devDpy->eglDisplay, surface->ctx.damageThreadSync, 0, EGL_FOREVER_KHR)); } } } wl_event_queue_destroy(queue); data->egl.releaseThread(); return NULL; } static EGLint setup_wl_eglstream_damage_thread(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; int ret; surface->ctx.damageThreadFlush = 0; surface->ctx.damageThreadShutdown = 0; surface->ctx.framesProduced = 0; surface->ctx.framesFinished = 0; surface->ctx.framesProcessed = 0; surface->ctx.damageThreadSync = data->egl.createStreamSync(display->devDpy->eglDisplay, surface->ctx.eglStream, EGL_SYNC_NEW_FRAME_NV, NULL); if (surface->ctx.damageThreadSync == EGL_NO_SYNC_KHR) { return data->egl.getError(); } ret = pthread_create(&surface->ctx.damageThreadId, NULL, damage_thread, (void*)surface); if (ret != 0) { return EGL_BAD_ALLOC; } return EGL_SUCCESS; } static void finish_wl_eglstream_damage_thread(WlEglSurface *surface, WlEglSurfaceCtx *ctx, int immediate) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; if (ctx->damageThreadSync != EGL_NO_SYNC_KHR) { if (immediate) { ctx->damageThreadShutdown = 1; } else { ctx->damageThreadFlush = 1; } data->egl.signalSync(display->devDpy->eglDisplay, ctx->damageThreadSync, EGL_SIGNALED_KHR); if (ctx->damageThreadId != (pthread_t)0) { pthread_join(ctx->damageThreadId, NULL); ctx->damageThreadId = (pthread_t)0; } data->egl.destroySync(display->devDpy->eglDisplay, ctx->damageThreadSync); ctx->damageThreadSync = EGL_NO_SYNC_KHR; } } static void* buffer_release_thread(void *args) { WlEglSurface *surface = (WlEglSurface*)args; WlEglDisplay *display = surface->wlEglDpy; struct wl_display *wlDpy = display->nativeDpy; struct wl_event_queue *queue = surface->wlBufferEventQueue; struct pollfd pfds[2]; const int fd = wl_display_get_fd(wlDpy); int res; uint8_t cmd; while (1) { /* First clear out any previously-read events on the queue */ wl_display_dispatch_queue_pending(wlDpy, queue); /* * Now block until more events are present on the wire, then * read them. Also, watch for messages from the app thread. */ if ((wl_display_prepare_read_queue(wlDpy, queue) < 0)) { if (errno == EAGAIN) { continue; } else { return NULL; } } memset(&pfds, 0, sizeof(pfds)); pfds[0].fd = fd; pfds[0].events = POLLIN; pfds[1].fd = surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_READ]; pfds[1].events = POLLIN; res = poll(&pfds[0], sizeof(pfds) / sizeof(pfds[0]), 1000); if (res <= 0) { wl_display_cancel_read(wlDpy); continue; } if (pfds[1].revents & POLLIN) { if (read(pfds[1].fd, &cmd, sizeof(cmd)) != sizeof(cmd)) { /* Reading an event from the app side failed. Bail. */ wl_display_cancel_read(wlDpy); return NULL; } switch (cmd) { default: /* * Unknown command from the app side. Treat it like a * termination request. * * fall through. */ case BUFFER_RELEASE_THREAD_EVENT_TERMINATE: wl_display_cancel_read(wlDpy); return NULL; } } if (pfds[0].revents & POLLIN) { if (wl_display_read_events(wlDpy) < 0) { return NULL; } } else { wl_display_cancel_read(wlDpy); } } return NULL; } static EGLint setup_wl_buffer_release_thread(WlEglSurface *surface) { int ret; if (pipe(surface->bufferReleaseThreadPipe)) { return EGL_BAD_ALLOC; } surface->wlBufferEventQueue = wl_display_create_queue(surface->wlEglDpy->nativeDpy); ret = pthread_create(&surface->bufferReleaseThreadId, NULL, buffer_release_thread, (void*)surface); if (ret != 0) { close(surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_WRITE]); surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_WRITE] = -1; close(surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_READ]); surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_READ] = -1; wl_event_queue_destroy(surface->wlBufferEventQueue); surface->wlBufferEventQueue = NULL; return EGL_BAD_ALLOC; } return EGL_SUCCESS; } static void finish_wl_buffer_release_thread(WlEglSurface *surface) { uint8_t cmd = BUFFER_RELEASE_THREAD_EVENT_TERMINATE; if (surface->wlBufferEventQueue) { if (write(surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_WRITE], &cmd, sizeof(cmd)) != sizeof(cmd)) { /* The thread is not going to terminate gracefully. */ pthread_cancel(surface->bufferReleaseThreadId); } pthread_join(surface->bufferReleaseThreadId, NULL); surface->bufferReleaseThreadId = (pthread_t)0; close(surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_WRITE]); surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_WRITE] = -1; close(surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_READ]); surface->bufferReleaseThreadPipe[BUFFER_RELEASE_PIPE_READ] = -1; wl_event_queue_destroy(surface->wlBufferEventQueue); surface->wlBufferEventQueue = NULL; } } static void destroy_stream_image(WlEglDisplay *display, WlEglSurface *surface, WlEglStreamImage *image) { WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; /* Must be called with surface->ctx.streamImagesMutex already locked */ if (surface->ctx.currentBuffer == image->buffer) { surface->ctx.currentBuffer = NULL; } if (!surface->wlSyncobjSurf && image->attached) { // This is used for delaying the destruction of images only when // explicit-sync is not in use to prevent the buffer release thread // from accessing images after they are deallocated. image->destructionPending = EGL_TRUE; return; } assert(image->eglImage != EGL_NO_IMAGE_KHR); data->egl.destroyImage(dpy, image->eglImage); if (image->buffer) { wl_buffer_destroy(image->buffer); } if (image->wlReleaseTimeline) { wp_linux_drm_syncobj_timeline_v1_destroy(image->wlReleaseTimeline); drmSyncobjDestroy(display->drmFd, image->drmSyncobjHandle); if (image->acquireSync != EGL_NO_SYNC_KHR) { data->egl.destroySync(dpy, image->acquireSync); } } wl_list_remove(&image->acquiredLink); wl_list_remove(&image->link); free(image); } static void destroy_surface_context(WlEglSurface *surface, WlEglSurfaceCtx *ctx) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; EGLSurface surf = ctx->eglSurface; EGLStreamKHR stream = ctx->eglStream; void *resource = ctx->wlStreamResource; finish_wl_eglstream_damage_thread(surface, ctx, 1); ctx->eglSurface = EGL_NO_SURFACE; ctx->eglStream = EGL_NO_STREAM_KHR; ctx->wlStreamResource = NULL; if (surf != EGL_NO_SURFACE) { data->egl.destroySurface(dpy, surf); } if (surface->ctx.isOffscreen) { return; } if (stream != EGL_NO_STREAM_KHR) { data->egl.destroyStream(dpy, stream); ctx->eglStream = EGL_NO_STREAM_KHR; } if (!resource) { // streamImages list is not valid when wlStreamResource is in use. WlEglStreamImage *image, *next; pthread_mutex_lock(&surface->ctx.streamImagesMutex); // Destroy all images. If there are attached images, this will mark // them for destruction. Following buffer release event will destroy // them. wl_list_for_each_safe(image, next, &ctx->streamImages, link) { destroy_stream_image(display, surface, image); } pthread_mutex_unlock(&surface->ctx.streamImagesMutex); } else { wl_buffer_destroy(resource); } } static void discard_surface_context(WlEglSurface *surface) { /* If the surface context is marked as attached, it means the compositor * might still be using the resources because some content was actually * displayed. In that case, defer its destruction until we make sure the * compositor doesn't need it anymore (i.e. upon stream release); * otherwise, we can just destroy it right away */ if (surface->ctx.isAttached && surface->ctx.wlStreamResource) { WlEglSurfaceCtx *ctx = malloc(sizeof(WlEglSurfaceCtx)); if (ctx) { memcpy(ctx, &surface->ctx, sizeof(*ctx)); wl_list_insert(&surface->oldCtxList, &ctx->link); } } else { destroy_surface_context(surface, &surface->ctx); } } static void wl_buffer_release(void *data, struct wl_buffer *buffer) { WlEglSurface *surface = (WlEglSurface*)data; WlEglSurfaceCtx *ctx; /* Look for the surface context for the given buffer and destroy it */ wl_list_for_each(ctx, &surface->oldCtxList, link) { if (ctx->wlStreamResource == buffer) { destroy_surface_context(surface, ctx); wl_list_remove(&ctx->link); free(ctx); break; } } } static struct wl_buffer_listener wl_buffer_listener = { wl_buffer_release }; static void * create_wl_eglstream(WlEglSurface *surface, int32_t handle, int32_t type, struct wl_array *attribs) { WlEglDisplay *display = surface->wlEglDpy; struct wl_egl_window *window = surface->wlEglWin; struct wl_eglstream_display *wrapper = NULL; struct wl_buffer *buffer = NULL; int32_t width; int32_t height; if (!display->wlStreamDpy) { return NULL; } if (surface->isSurfaceProducer) { assert(window); width = window->width; height = window->height; } else { width = surface->width; height = surface->height; } wrapper = wl_proxy_create_wrapper(display->wlStreamDpy); wl_proxy_set_queue((struct wl_proxy *)wrapper, surface->wlEventQueue); buffer = wl_eglstream_display_create_stream(wrapper, width, height, handle, type, attribs); wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ if (!buffer) { return NULL; } if (wl_buffer_add_listener(buffer, &wl_buffer_listener, surface) == -1) { wl_buffer_destroy(buffer); return NULL; } return buffer; } static EGLint create_surface_stream_fd(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; int handle = EGL_NO_FILE_DESCRIPTOR_KHR; struct wl_array wlAttribs; EGLint eglAttribs[] = { EGL_STREAM_FIFO_LENGTH_KHR, surface->fifoLength, EGL_NONE, EGL_NONE, EGL_NONE }; EGLint err = EGL_SUCCESS; /* We don't have any mechanism to check whether the compositor is going to * use this surface for composition or not when using cross_process_fd, so * just enable FIFO_SYNCHRONOUS if the extensions are supported */ if (display->devDpy->exts.stream_fifo_synchronous && display->devDpy->exts.stream_sync && surface->fifoLength > 0) { eglAttribs[2] = EGL_STREAM_FIFO_SYNCHRONOUS_NV; eglAttribs[3] = EGL_TRUE; } /* First, create the EGLStream */ surface->ctx.eglStream = data->egl.createStream(display->devDpy->eglDisplay, eglAttribs); if (surface->ctx.eglStream == EGL_NO_STREAM_KHR) { err = data->egl.getError(); goto fail; } handle = data->egl.getStreamFileDescriptor(display->devDpy->eglDisplay, surface->ctx.eglStream); if (handle == EGL_NO_FILE_DESCRIPTOR_KHR) { err = data->egl.getError(); goto fail; } /* Finally, create the wl_eglstream */ wl_array_init(&wlAttribs); /* Empty attributes list */ surface->ctx.wlStreamResource = create_wl_eglstream(surface, handle, WL_EGLSTREAM_HANDLE_TYPE_FD, &wlAttribs); if (!surface->ctx.wlStreamResource) { err = EGL_BAD_ALLOC; goto fail; } /* Clean-up */ close(handle); return EGL_SUCCESS; fail: destroy_surface_context(surface, &surface->ctx); if (handle >= 0) { close(handle); } return err; } #ifdef EGL_NV_stream_remote static void* acceptOneConnection(void *args) { int *socket = (int *)args; int serverSocket = *socket; /* Accept only one connection and store the client socket that will be used * to create the EGLStream */ *socket = accept(serverSocket, NULL, NULL); close(serverSocket); return NULL; } static EGLint startInetHandshake(pthread_t *thread, int *clientSocket, int *port) { struct sockaddr_in addr; unsigned int addrLen; EGLint err = EGL_SUCCESS; int ret; /* Create a server socket that will listen to all IPs and pick a random * port. Then, only one connection will be accepted. We can use clientSocket * to temporary store the server socket */ *clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (*clientSocket == -1) { err = EGL_BAD_ALLOC; goto fail; } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; /* Accept connections to all IPs */ addr.sin_port = htons(0); /* Get a random port */ if (bind(*clientSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { err = EGL_BAD_ALLOC; goto fail; } if (listen(*clientSocket, 1) < 0) { err = EGL_BAD_ALLOC; goto fail; } /* Return host byte ordered port for sending through wayland protocol. * Wayland will convert back to wire format before sending. */ addrLen = sizeof(addr); ret = getsockname(*clientSocket, (struct sockaddr *)&addr, &addrLen); if (ret != 0) { err = EGL_BAD_ALLOC; goto fail; } *port = ntohs(addr.sin_port); /* Start a new thread that will accept one connection only. It will store * the new client socket in . */ ret = pthread_create(thread, NULL, acceptOneConnection, clientSocket); if (ret != 0) { err = EGL_BAD_ALLOC; goto fail; } return EGL_SUCCESS; fail: if (*clientSocket >= 0) { close(*clientSocket); *clientSocket = -1; } return err; } static EGLint finishInetHandshake(pthread_t thread, int *socket) { int ret = pthread_join(thread, NULL); if (ret != 0 || *socket == -1) { return EGL_BAD_ALLOC; } return EGL_SUCCESS; } static EGLint create_surface_stream_remote(WlEglSurface *surface, EGLBoolean useInet) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; struct wl_array wlAttribs; intptr_t *wlAttribsData; EGLint eglAttribs[] = { EGL_STREAM_TYPE_NV, EGL_STREAM_CROSS_PROCESS_NV, EGL_STREAM_ENDPOINT_NV, EGL_STREAM_PRODUCER_NV, EGL_STREAM_PROTOCOL_NV, EGL_STREAM_PROTOCOL_SOCKET_NV, EGL_SOCKET_TYPE_NV, EGL_DONT_CARE, EGL_SOCKET_HANDLE_NV, -1, EGL_NONE, EGL_NONE, EGL_NONE, }; pthread_t thread; int socket[2]; int port; EGLint err = EGL_SUCCESS; int ret; wl_array_init(&wlAttribs); /* Empty attributes list */ if (useInet) { /* Start inet handshaking and get the socket and selected port */ err = startInetHandshake(&thread, &socket[0], &port); if (err != EGL_SUCCESS) { goto fail; } /* Fill the wlAttribs array with the connection data. */ if (!(wlAttribsData = (intptr_t *)wl_array_add(&wlAttribs, 4*sizeof(intptr_t)))) { err = EGL_BAD_ALLOC; goto fail; } /* Get host byte ordered address for sending through wayland protocol. * Wayland will convert back to wire format before sending. Assume a * local INET connection until cross partition wayland support is added. */ wlAttribsData[0] = WL_EGLSTREAM_ATTRIB_INET_ADDR; wlAttribsData[1] = (intptr_t)INADDR_LOOPBACK; wlAttribsData[2] = WL_EGLSTREAM_ATTRIB_INET_PORT; wlAttribsData[3] = (intptr_t)port; if (wlAttribsData[1] == -1) { err = EGL_BAD_ALLOC; goto fail; } /* Create a dummy fd to be feed into wayland. The fd will never be used, * but wayland will abort if an invaild fd is given. */ socket[1] = open("/dev/null", O_RDONLY); if (socket[1] == -1) { err = EGL_BAD_ALLOC; goto fail; } } else { /* Create a new socket pair for both EGLStream endpoints */ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, socket); if (ret != 0) { err = EGL_BAD_ALLOC; goto fail; } } if (!(wlAttribsData = (intptr_t *)wl_array_add(&wlAttribs, 2*sizeof(intptr_t)))) { err = EGL_BAD_ALLOC; goto fail; } /* If Vulkan, default Y_INVERTED to 'true'. Otherwise, assume OpenGL * orientation (image origin at the lower left corner), which aligns with * what a wayland compositor would consider 'non-y-inverted' */ wlAttribsData[0] = WL_EGLSTREAM_ATTRIB_Y_INVERTED; wlAttribsData[1] = (intptr_t)(surface->isSurfaceProducer ? EGL_FALSE : EGL_TRUE); surface->ctx.wlStreamResource = create_wl_eglstream(surface, socket[1], (useInet ? WL_EGLSTREAM_HANDLE_TYPE_INET : WL_EGLSTREAM_HANDLE_TYPE_SOCKET), &wlAttribs); if (!surface->ctx.wlStreamResource) { err = EGL_BAD_ALLOC; goto fail; } /* Need a roundtrip for the consumer's endpoint to be created before the * producer's */ if (wl_display_roundtrip_queue(display->nativeDpy, surface->wlEventQueue) < 0) { err = EGL_BAD_ALLOC; goto fail; } if (useInet) { /* Wait for the inet handshake to finish */ err = finishInetHandshake(thread, &socket[0]); if (err != EGL_SUCCESS) { goto fail; } } /* Finally, create the EGLStream * * EGL_SOCKET_TYPE_NV is in eglAttribs[6] * EGL_SOCKET_HANDLE_NV is in eglAttribs[8] */ eglAttribs[7] = (useInet ? EGL_SOCKET_TYPE_INET_NV : EGL_SOCKET_TYPE_UNIX_NV); eglAttribs[9] = socket[0]; if (!surface->isSurfaceProducer && display->wlStreamCtlVer >= WL_EGLSTREAM_CONTROLLER_ATTACH_EGLSTREAM_CONSUMER_ATTRIB_SINCE) { eglAttribs[10] = EGL_STREAM_FIFO_LENGTH_KHR; eglAttribs[11] = surface->fifoLength; } surface->ctx.eglStream = data->egl.createStream(display->devDpy->eglDisplay, eglAttribs); if (surface->ctx.eglStream == EGL_NO_STREAM_KHR) { err = data->egl.getError(); goto fail; } /* Close server socket on the client side */ if (socket[1] >= 0) { close(socket[1]); } wl_array_release(&wlAttribs); return EGL_SUCCESS; fail: destroy_surface_context(surface, &surface->ctx); wl_array_release(&wlAttribs); if (socket[0] >= 0) { close(socket[0]); } if (!useInet && socket[1] >= 0) { close(socket[1]); } return err; } #endif static void stream_local_buffer_release_callback(void *ptr, struct wl_buffer *buffer) { WlEglStreamImage *image = (WlEglStreamImage*)ptr; /* Safe: image->surface pointer value is const once image is initialized */ WlEglSurface *surface = image->surface; WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; pthread_mutex_lock(&surface->ctx.streamImagesMutex); (void)buffer; /* In case assert() compiles to nothing */ assert(image->buffer == NULL || image->buffer == buffer); image->attached = EGL_FALSE; if (image->destructionPending) { /* * If there was an attempt to destroy the image while its buffer was * attached, the buffer destruction was deferred. Clean it up now. */ destroy_stream_image(display, surface, image); } else { /* * Release our image back to the stream if explicit sync is not in use * * If explicit sync was used, then wl_buffer.release means nothing. We * will instead have already marked this image for release contingent * on the release sync getting signaled. This callback doesn't even fire * in that scenario. */ assert(image->eglImage != EGL_NO_IMAGE_KHR); data->egl.streamReleaseImage(display->devDpy->eglDisplay, surface->ctx.eglStream, image->eglImage, EGL_NO_SYNC_KHR); } pthread_mutex_unlock(&surface->ctx.streamImagesMutex); } static const struct wl_buffer_listener stream_local_buffer_listener = { stream_local_buffer_release_callback, }; /* * Export a syncfd from the timeline at the specified point and make an * EGLSyncKHR out of it. We can then pass this eglsync to releaseImageNV and * it will wait for the release point to signal before releasing the image back * to the screen. */ static EGLSyncKHR get_release_sync(WlEglDisplay *display, WlEglStreamImage *image) { EGLDisplay dpy = display->devDpy->eglDisplay; WlEglPlatformData *data = display->data; EGLSyncKHR eglSync = EGL_NO_SYNC_KHR; int syncFd = -1; uint32_t tmpSyncobj; EGLint attribs[3]; /* Import our acquire syncfd at a new acquire point */ if (drmSyncobjCreate(display->drmFd, 0, &tmpSyncobj) != 0) { return EGL_NO_SYNC_KHR; } if (drmSyncobjTransfer(display->drmFd, tmpSyncobj, 0, image->drmSyncobjHandle, image->releasePoint, 0) != 0) { goto destroy; } if (drmSyncobjExportSyncFile(display->drmFd, tmpSyncobj, &syncFd) != 0) { goto destroy; } attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID; attribs[1] = syncFd; attribs[2] = EGL_NONE; eglSync = data->egl.createSync(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); destroy: drmSyncobjDestroy(display->drmFd, tmpSyncobj); return eglSync; } /* * We have committed a frame, and if we are using explicit sync * we will have registered a release point with the compositor. * The release point's fence didn't exist then, so we should check * for any available fences that we should trigger releasing * images back into the stream with. * * This will block if no available buffers have been released. */ EGLBoolean wlEglSurfaceCheckReleasePoints(WlEglDisplay *display, WlEglSurface *surface) { WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; EGLSyncKHR releaseSync = EGL_NO_SYNC_KHR; WlEglStreamImage *image = NULL; WlEglStreamImage *streamImages[MAX_IMAGES]; uint32_t syncobjs[MAX_IMAGES]; uint64_t syncPoints[MAX_IMAGES]; uint32_t firstSignaled, numSyncPoints = 0; int64_t timeout; EGLBoolean ret = EGL_FALSE; if (!surface->wlSyncobjSurf) { return EGL_TRUE; } pthread_mutex_lock(&surface->ctx.streamImagesMutex); /* record each release point we are waiting on */ wl_list_for_each(image, &surface->ctx.streamImages, link) { if (image->releasePending) { if (numSyncPoints >= MAX_IMAGES) { assert(!"The number of the pending sync points is more \ than the expected size of the swapchain"); break; } streamImages[numSyncPoints] = image; syncobjs[numSyncPoints] = image->drmSyncobjHandle; syncPoints[numSyncPoints] = image->releasePoint; numSyncPoints++; } } if (numSyncPoints == 0) { goto end; } /* * Wait for at least one release point to have a fence. We need to block here * since the streams internal code expects to have at least one buffer placed * back on the release (internally called returns) queue. * * We only wait indefinitely when all but one buffers are pending. There are * four total buffers: * 1. One owned by the driver (as per above) * 2. One we just committed and sent to the compositor * 3. One owned by compositor, queued for scanout * 4. One owned by compositor, in process of releasing * * Not all compositors will hold 3 and 4 indefinitely, although Kwin does * at certain times. */ timeout = numSyncPoints >= 3 ? INT64_MAX : 0; /* * The Linux docs say that DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE should be * used to wait for a fence to appear without waiting on the fence itself. * Note that there are some bugs with older kernels where this may not * signal correctly. */ if (drmSyncobjTimelineWait(display->drmFd, syncobjs, syncPoints, numSyncPoints, timeout, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE, &firstSignaled) != 0) { /* A timeout is the only type of error we expect here */ #ifdef ETIME assert(errno == ETIME); #endif goto end; } image = streamImages[firstSignaled]; /* Try to get a release point for the first available buffer. */ releaseSync = get_release_sync(display, image); if (releaseSync == EGL_NO_SYNC_KHR) { goto end; } /* * Pass our newly created release EGLSyncKHR to our eglstream, it * will wait for it to signal before it releases the image back to * the stream. Note that wl_buffer.release means nothing with * linux-drm-syncobj-v1. */ ret = data->egl.streamReleaseImage(display->devDpy->eglDisplay, surface->ctx.eglStream, image->eglImage, releaseSync); /* releaseImage makes a copy, so we destroy ours here */ data->egl.destroySync(dpy, releaseSync); /* * If we succesfully released the image, Clear our release point so we * don't repeat this. */ if (ret == EGL_TRUE) { image->releasePending = false; } end: pthread_mutex_unlock(&surface->ctx.streamImagesMutex); return ret; } static EGLint acquire_surface_image(WlEglDisplay *display, WlEglSurface *surface) { WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; EGLImageKHR eglImage; WlEglStreamImage *image = NULL; struct zwp_linux_dmabuf_v1 *wrapper = NULL; struct zwp_linux_buffer_params_v1 *params; EGLuint64KHR modifier; int format; int planes; EGLint stride; EGLint offset; int fd; EGLSyncKHR acquireSync = EGL_NO_SYNC_KHR; EGLBoolean found = EGL_FALSE; const EGLint attribs[] = { EGL_SYNC_NATIVE_FENCE_FD_ANDROID, EGL_NO_NATIVE_FENCE_FD_ANDROID, EGL_SYNC_STATUS, EGL_SIGNALED, EGL_NONE, }; if (surface->wlSyncobjSurf) { /* * don't flush before acquireImage, we have to pass it in signaled. * * acquireImage will reset this, causing the fd to populate. */ acquireSync = data->egl.createSync(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (acquireSync == EGL_NO_SYNC_KHR) { return EGL_BAD_SURFACE; } } if (!data->egl.streamAcquireImage(dpy, surface->ctx.eglStream, &eglImage, acquireSync)) { goto fail_destroy_sync; } pthread_mutex_lock(&surface->ctx.streamImagesMutex); // Locate the corresponding WlEglStreamImage wl_list_for_each(image, &surface->ctx.streamImages, link) { if (image->eglImage == eglImage) { found = EGL_TRUE; break; } } if (!found) { goto fail_release; } image->acquireSync = acquireSync; if (!image->buffer) { if (!data->egl.exportDMABUFImageQuery(dpy, eglImage, &format, &planes, &modifier)) { goto fail_release; } assert(planes == 1); /* XXX support planar formats */ if (!data->egl.exportDMABUFImage(dpy, eglImage, &fd, &stride, &offset)) { goto fail_release; } wrapper = wl_proxy_create_wrapper(display->wlDmaBuf); wl_proxy_set_queue((struct wl_proxy *)wrapper, surface->wlBufferEventQueue); params = zwp_linux_dmabuf_v1_create_params(wrapper); zwp_linux_buffer_params_v1_add(params, fd, 0, /* XXX support planar formats */ offset, stride, modifier >> 32, modifier & 0xffffffff); /* * Before sending the format, check if we are ignoring alpha due to a * surface attribute. */ if (surface->presentOpaque) { /* * We are ignoring alpha, so we need to find a DRM_FORMAT_* that is equivalent to * the current format, but ignores the alpha. i.e. RGBA -> RGBX * * There is also only one format with alpha that we expose on wayland: ARGB8888. If * the format does not match this, silently ignore it as the app must be mistakenly * using EGL_PRESENT_OPAQUE_EXT on an already opaque surface. */ if (format == DRM_FORMAT_ARGB8888) { format = DRM_FORMAT_XRGB8888; } } image->buffer = zwp_linux_buffer_params_v1_create_immed(params, surface->width, surface->height, format, 0); zwp_linux_buffer_params_v1_destroy(params); wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ close(fd); if (!image->buffer) { goto fail_release; } if (!surface->wlSyncobjSurf && wl_buffer_add_listener(image->buffer, &stream_local_buffer_listener, image) == -1) { wl_buffer_destroy(image->buffer); image->buffer = NULL; goto fail_release; } } /* Add image to the end of the acquired images list */ wl_list_insert(surface->ctx.acquiredImages.prev, &image->acquiredLink); pthread_mutex_unlock(&surface->ctx.streamImagesMutex); return EGL_SUCCESS; fail_release: /* Release the image back to the stream */ data->egl.streamReleaseImage(dpy, surface->ctx.eglStream, eglImage, EGL_NO_SYNC_KHR); if (acquireSync != EGL_NO_SYNC_KHR) { data->egl.destroySync(dpy, acquireSync); if (found) { assert(image->acquireSync == acquireSync); image->acquireSync = EGL_NO_SYNC_KHR; } } /* Release the image lock */ pthread_mutex_unlock(&surface->ctx.streamImagesMutex); return EGL_BAD_SURFACE; fail_destroy_sync: if (acquireSync != EGL_NO_SYNC_KHR) { data->egl.destroySync(dpy, acquireSync); } return EGL_BAD_SURFACE; } static void remove_surface_image(WlEglDisplay *display, WlEglSurface *surface, EGLImageKHR eglImage) { WlEglStreamImage *image; pthread_mutex_lock(&surface->ctx.streamImagesMutex); wl_list_for_each(image, &surface->ctx.streamImages, link) { /* Safe only because the iteration is aborted by a break statement */ if (image->eglImage == eglImage) { destroy_stream_image(display, surface, image); break; } } pthread_mutex_unlock(&surface->ctx.streamImagesMutex); } static WlEglStreamImage * pop_acquired_image(WlEglSurface *surface) { WlEglStreamImage *image; wl_list_for_each(image, &surface->ctx.acquiredImages, acquiredLink) { /* Safe only because the iteration is aborted by returning */ wl_list_remove(&image->acquiredLink); wl_list_init(&image->acquiredLink); return image; } /* List is empty. Return NULL. */ return NULL; } static int create_syncobj_timeline(WlEglDisplay *display, uint32_t *drmSyncobjHandleOut) { int ret; /* Create a DRM timeline and share it with the compositor */ if (drmSyncobjCreate(display->drmFd, 0, drmSyncobjHandleOut)) { return -1; } if (drmSyncobjHandleToFD(display->drmFd, *drmSyncobjHandleOut, &ret)) { return -1; } return ret; } static EGLint init_surface_image(WlEglDisplay *display, WlEglSurface *surface, WlEglStreamImage *image) { WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; int drmSyncobjFd = -1; image->eglImage = data->egl.createImage(dpy, EGL_NO_CONTEXT, EGL_STREAM_CONSUMER_IMAGE_NV, (EGLClientBuffer)surface->ctx.eglStream, NULL); if (image->eglImage == EGL_NO_IMAGE_KHR) { return EGL_BAD_ALLOC; } /* * Create a per-stream image release timeline. * * This is needed since we will be the ones signaling acquire points. If the acquire points * are on the same timeline as the release points then they will accidentally signal all * pending release points. */ if (surface->wlSyncobjSurf) { drmSyncobjFd = create_syncobj_timeline(display, &image->drmSyncobjHandle); if (drmSyncobjFd < 0) { goto fail; } image->acquireSync = EGL_NO_SYNC_KHR; /* Get a DRM timeline wl object */ image->wlReleaseTimeline = wp_linux_drm_syncobj_manager_v1_import_timeline(display->wlDrmSyncobj, drmSyncobjFd); if (!image->wlReleaseTimeline) { goto fail; } close(drmSyncobjFd); } image->surface = surface; wl_list_init(&image->acquiredLink); return EGL_SUCCESS; fail: if (image->drmSyncobjHandle) { drmSyncobjDestroy(display->drmFd, image->drmSyncobjHandle); } if (drmSyncobjFd > 0) { close(drmSyncobjFd); } data->egl.destroyImage(dpy, image->eglImage); return EGL_BAD_ALLOC; } static EGLint add_surface_image(WlEglDisplay *display, WlEglSurface *surface) { WlEglStreamImage* const image = calloc(1, sizeof(*image)); EGLint ret; if (!image) { return EGL_BAD_ALLOC; } ret = init_surface_image(display, surface, image); if (ret != EGL_SUCCESS) { free(image); return ret; } pthread_mutex_lock(&surface->ctx.streamImagesMutex); wl_list_insert(&surface->ctx.streamImages, &image->link); pthread_mutex_unlock(&surface->ctx.streamImagesMutex); return EGL_SUCCESS; } EGLint wlEglHandleImageStreamEvents(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; EGLDisplay dpy = display->devDpy->eglDisplay; WlEglPlatformData *data = display->data; EGLAttrib aux; EGLenum event; EGLint err = EGL_SUCCESS; EGLTime timeout = surface->wlSyncobjSurf ? EGL_FOREVER : 0; if (surface->ctx.wlStreamResource) { /* Not a local stream */ return err; } while (1) { /* * With explicit sync we should block here and not return until we have * acquired a new image. The stream will not release the image until * the release point we handed to the compositor signals. */ err = data->egl.queryStreamConsumerEvent(dpy, surface->ctx.eglStream, timeout, &event, &aux); if (err == EGL_TRUE) { err = EGL_SUCCESS; } else if (err == EGL_TIMEOUT_EXPIRED) { err = EGL_SUCCESS; break; } else if (err == EGL_FALSE) { /* XXX Pick the right error code */ err = EGL_BAD_SURFACE; break; } else { break; } switch (event) { case EGL_STREAM_IMAGE_AVAILABLE_NV: err = acquire_surface_image(display, surface); /* Clear our timeout so we exit after all events are handled */ timeout = 0; break; case EGL_STREAM_IMAGE_ADD_NV: err = add_surface_image(display, surface); break; case EGL_STREAM_IMAGE_REMOVE_NV: remove_surface_image(display, surface, (EGLImageKHR)aux); break; default: assert(!"Unhandled EGLImage stream consumer event"); } } return err; } static WlEglDmaBufFormatSet * WlEglGetFormatSetForDev(WlEglDmaBufFeedback *feedback, dev_t dev, uint32_t format) { /* find the dev_t in our feedback's list of tranches */ for (int i = 0; i < (int)feedback->numTranches; i++) { if (feedback->tranches[i].drmDev == dev) { /* check if this tranche contains our format */ WlEglDmaBufFormatSet *formatSet = &feedback->tranches[i].formatSet; for (int j = 0; j < (int)formatSet->numFormats; ++j) { if (formatSet->dmaBufFormats[j].format == format) { return formatSet; } } } } return NULL; } /* * TODO: Remove this once an EGL extension exists to query this information. * * This is taken from the GBM library, it looks at the config's sizes and * hardcodes the equivalent DRM format. Eventually we want to be able to query * this directly from the EGLConfig, but since that extension is in the works * we don't want to hold up wayland improvements. This function will be used * until the extension is available. */ static uint32_t ConfigToDrmFourCC(WlEglDisplay* display, EGLConfig config) { EGLDisplay dpy = display->devDpy->eglDisplay; EGLint r, g, b, a; EGLBoolean ret = EGL_TRUE; ret &= display->data->egl.getConfigAttrib(dpy, config, EGL_RED_SIZE, &r); ret &= display->data->egl.getConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); ret &= display->data->egl.getConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); ret &= display->data->egl.getConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); if (!ret) { /* * The only reason this could fail is some internal error in the * platform library code or if the application terminated the display * in another thread while this code was running. In either case, * behave as if there is no DRM fourcc format associated with this * config. */ return 0; /* DRM_FORMAT_INVALID */ } /* Handles configs with up to 255 bits per component */ assert(a < 256 && g < 256 && b < 256 && a < 256); #define PACK_CONFIG(r_, g_, b_, a_) \ (((r_) << 24ULL) | ((g_) << 16ULL) | ((b_) << 8ULL) | (a_)) switch (PACK_CONFIG(r, g, b, a)) { case PACK_CONFIG(8, 8, 8, 0): return DRM_FORMAT_XRGB8888; case PACK_CONFIG(8, 8, 8, 8): return DRM_FORMAT_ARGB8888; case PACK_CONFIG(5, 6, 5, 0): return DRM_FORMAT_RGB565; case PACK_CONFIG(10, 10, 10, 0): return DRM_FORMAT_XRGB2101010; case PACK_CONFIG(10, 10, 10, 2): return DRM_FORMAT_ARGB2101010; default: return 0; /* DRM_FORMAT_INVALID */ } } static EGLint create_surface_stream_local(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; EGLDisplay dpy = display->devDpy->eglDisplay; EGLint eglAttribs[] = { EGL_STREAM_FIFO_LENGTH_KHR, surface->fifoLength, EGL_NONE, EGL_NONE, EGL_NONE }; EGLint err = EGL_SUCCESS; EGLint numModifiers = 0; EGLuint64KHR *modifiers = NULL; uint32_t format; WlEglDmaBufFormatSet *formatSet = NULL; WlEglDmaBufFeedback *feedback = NULL; /* * Vulkan surfaces will not have an eglConfig set. We will need to address them * separately. */ if (surface->eglConfig) { /* * Find the format we are using * For now we use the hardcoded calculation in ConfigToDrmFourCC (which is from * the gbm code). In the future we will have an EGL extension to get this from * a config, and we will switch to that. */ format = ConfigToDrmFourCC(display, surface->eglConfig); if (!format) { err = EGL_BAD_ACCESS; goto fail; } /* Get our format set, if we have feedback it will be the device's format set */ if (display->dmaBufProtocolVersion < 4) { formatSet = &display->formatSet; } else { /* * If the surface has a per-surface feedback object, then use the modifiers * from that. Otherwise use the default feedback. */ if (surface->feedback.wlDmaBufFeedback) { feedback = &surface->feedback; } else { feedback = &display->defaultFeedback; } formatSet = WlEglGetFormatSetForDev(feedback, display->devDpy->dev, format); if (!formatSet) { /* try again and see if there is a matching tranche for the render node */ formatSet = WlEglGetFormatSetForDev(feedback, display->devDpy->renderNode, format); } /* * If we could not find any modifiers for this device, and if we are * in a prime setup, use the main device's format set. This will allow * us to check if the main device supports the linear modifier. */ if (!formatSet && display->primeRenderOffload) { formatSet = WlEglGetFormatSetForDev(feedback, feedback->mainDev, format); } } /* grab the modifier array */ if (formatSet) { for (int i = 0; i < (int)formatSet->numFormats; i++) { if (formatSet->dmaBufFormats[i].format == format) { modifiers = formatSet->dmaBufFormats[i].modifiers; numModifiers = formatSet->dmaBufFormats[i].numModifiers; break; } } } } /* We don't have any mechanism to check whether the compositor is going to * use this surface for composition or not when using local streams, so * just enable FIFO_SYNCHRONOUS if the extensions are supported. * Note the use of this mechanism makes the optional sync parameter * passed to eglStreamAcquireImageNV() redundant, so that mechanism is not * used in this library. */ if (display->devDpy->exts.stream_fifo_synchronous && display->devDpy->exts.stream_sync && surface->fifoLength > 0) { eglAttribs[2] = EGL_STREAM_FIFO_SYNCHRONOUS_NV; eglAttribs[3] = EGL_TRUE; } /* First, create the EGLStream */ surface->ctx.eglStream = data->egl.createStream(dpy, eglAttribs); if (surface->ctx.eglStream == EGL_NO_STREAM_KHR) { err = data->egl.getError(); goto fail; } /* Now create the local EGLImage consumer */ if (!data->egl.streamImageConsumerConnect(dpy, surface->ctx.eglStream, numModifiers, modifiers, NULL)) { err = data->egl.getError(); goto fail; } wl_list_init(&surface->ctx.acquiredImages); /* * Don't enable the buffer release thread when explicit sync is in use. * In explicit sync we don't care about the delivery of release events, we * only pay attention to the release points. */ if (!surface->wlBufferEventQueue && !surface->wlSyncobjSurf) { /* * Local stream contexts need a private wayland queue used by a separate * thread that can process buffer release events even the application * thread is blocking in the EGL library for stream frame acquisition * while swapping the producer surface. The thread and queue are owned * by the surface because they need to outlive the context when resizing * the surface while a buffer is still attached, but they are * initialized lazily here to avoid incuring the cost of an extra unused * thread and two pipe file descriptors per surface when they are not * needed. */ if (EGL_SUCCESS != setup_wl_buffer_release_thread(surface)) { goto fail; } } return EGL_SUCCESS; fail: destroy_surface_context(surface, &surface->ctx); return err; } static EGLint create_surface_stream(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; EGLint err = EGL_BAD_ACCESS; /* Try all supported EGLStream creation methods until one of them succeeds. * More efficient connection schemes should be given a higher priority. If * there is more than one method giving the same efficiency, the more * versatile/configurable one would be preferred: * * 1. Local stream + dma-buf * 2. Cross-process unix sockets * 3. Cross-process FD * 4. Cross-process inet sockets */ #ifdef EGL_NV_stream_consumer_eglimage if ((err != EGL_SUCCESS) && display->devDpy->exts.stream_consumer_eglimage && display->devDpy->exts.image_dma_buf_export && display->wlDmaBuf) { err = create_surface_stream_local(surface); } #endif #ifdef EGL_NV_stream_remote if ((err != EGL_SUCCESS) && display->caps.stream_socket && display->devDpy->exts.stream_remote) { err = create_surface_stream_remote(surface, EGL_FALSE); } #endif if ((err != EGL_SUCCESS) && display->caps.stream_fd && display->devDpy->exts.stream_cross_process_fd) { err = create_surface_stream_fd(surface); } #ifdef EGL_NV_stream_remote if ((err != EGL_SUCCESS) && display->caps.stream_inet && display->devDpy->exts.stream_remote) { err = create_surface_stream_remote(surface, EGL_TRUE); } #endif return err; } static EGLint create_surface_context(WlEglSurface *surface) { WlEglDisplay *display = surface->wlEglDpy; WlEglPlatformData *data = display->data; struct wl_egl_window *window = surface->wlEglWin; int winWidth = 0; int winHeight = 0; int winDx = 0; int winDy = 0; EGLint synchronous = EGL_FALSE; EGLint err = EGL_SUCCESS; struct wl_array wlAttribs; intptr_t *wlAttribsData; assert(surface->ctx.eglSurface == EGL_NO_SURFACE); if (surface->isSurfaceProducer) { winWidth = window->width; winHeight = window->height; winDx = window->dx; winDy = window->dy; /* Width and height are the first and second attributes respectively */ surface->attribs[1] = winWidth; surface->attribs[3] = winHeight; } else { winWidth = surface->width; winHeight = surface->height; winDx = surface->dx; winDy = surface->dy; } /* First, create the underlying wl_eglstream and EGLStream */ err = create_surface_stream(surface); if (err != EGL_SUCCESS) { goto fail; } /* If the stream has a server component, attach the wl_eglstream so the * compositor connects a consumer to the EGLStream */ if (surface->ctx.wlStreamResource) { if (display->wlStreamCtl != NULL) { if (display->wlStreamCtlVer >= WL_EGLSTREAM_CONTROLLER_ATTACH_EGLSTREAM_CONSUMER_ATTRIB_SINCE) { wl_array_init(&wlAttribs); if (!wl_array_add(&wlAttribs, 2 * sizeof(intptr_t))) { wl_array_release(&wlAttribs); err = EGL_BAD_ALLOC; goto fail; } wlAttribsData = (intptr_t *)wlAttribs.data; wlAttribsData[0] = WL_EGLSTREAM_CONTROLLER_ATTRIB_PRESENT_MODE; if (surface->isSurfaceProducer) { wlAttribsData[1] = WL_EGLSTREAM_CONTROLLER_PRESENT_MODE_DONT_CARE; } else if (surface->fifoLength > 0) { if (!wl_array_add(&wlAttribs, 2 * sizeof(intptr_t))) { wl_array_release(&wlAttribs); err = EGL_BAD_ALLOC; goto fail; } wlAttribsData = (intptr_t *)wlAttribs.data; wlAttribsData[1] = WL_EGLSTREAM_CONTROLLER_PRESENT_MODE_FIFO; wlAttribsData[2] = WL_EGLSTREAM_CONTROLLER_ATTRIB_FIFO_LENGTH; wlAttribsData[3] = surface->fifoLength; } else { wlAttribsData[1] = WL_EGLSTREAM_CONTROLLER_PRESENT_MODE_MAILBOX; } wl_eglstream_controller_attach_eglstream_consumer_attribs(display->wlStreamCtl, surface->wlSurface, surface->ctx.wlStreamResource, &wlAttribs); wl_array_release(&wlAttribs); } else { wl_eglstream_controller_attach_eglstream_consumer(display->wlStreamCtl, surface->wlSurface, surface->ctx.wlStreamResource); } } else { wl_surface_attach(surface->wlSurface, surface->ctx.wlStreamResource, winDx, winDy); wl_surface_commit(surface->wlSurface); /* Since we are using the legacy method of overloading wl_surface_attach * in order to create the server-side EGLStream here, the compositor * will actually take this as a new buffer. We mark it as 'attached' * because whenever a new wl_surface_attach request is issued, the * compositor will emit back a wl_buffer_release event, and we will * destroy the context then. */ surface->ctx.isAttached = EGL_TRUE; } if (wl_display_roundtrip_queue(display->nativeDpy, surface->wlEventQueue) < 0) { err = EGL_BAD_ALLOC; goto fail; } } if (surface->isSurfaceProducer) { /* Finally, create the surface producer */ surface->ctx.eglSurface = data->egl.createStreamProducerSurface(display->devDpy->eglDisplay, surface->eglConfig, surface->ctx.eglStream, surface->attribs); if (surface->ctx.eglSurface == EGL_NO_SURFACE) { err = data->egl.getError(); goto fail; } wl_display_flush(display->nativeDpy); } /* Check whether we should use a damage thread */ surface->ctx.useDamageThread = !surface->wlSyncobjSurf && display->devDpy->exts.stream_fifo_synchronous && display->devDpy->exts.stream_sync && data->egl.queryStream(display->devDpy->eglDisplay, surface->ctx.eglStream, EGL_STREAM_FIFO_SYNCHRONOUS_NV, &synchronous) && (synchronous == EGL_TRUE); if (surface->ctx.useDamageThread) { err = setup_wl_eglstream_damage_thread(surface); if (err != EGL_SUCCESS) { goto fail; } } /* Cache current window size and displacement for future checks */ if (surface->isSurfaceProducer) { surface->width = winWidth; surface->height = winHeight; surface->dx = winDx; surface->dy = winDy; window->attached_width = winWidth; window->attached_height = winHeight; } return EGL_SUCCESS; fail: destroy_surface_context(surface, &surface->ctx); finish_wl_buffer_release_thread(surface); return err; } WL_EXPORT EGLStreamKHR wlEglGetSurfaceStreamExport(WlEglSurface *surface) { if (!surface) return EGL_NO_STREAM_KHR; return surface->ctx.eglStream; } WL_EXPORT int wlEglWaitAllPresentationFeedbacksExport(WlEglSurface *surface) { int numberOfPresentEvents = 0; WlEglDisplay *display = wlEglAcquireDisplay((WlEglDisplay *)surface->wlEglDpy); pthread_mutex_lock(&surface->mutexLock); // Destroy all presentation feedback objects in flight if (display->wpPresentation) { assert(surface->landedPresentFeedbackCount == 0); while (surface->inFlightPresentFeedbackCount > 0) { const int ret = wl_display_dispatch_queue(display->nativeDpy, surface->presentFeedbackQueue); if (ret < 0) { pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return ret; } } } assert(surface->inFlightPresentFeedbackCount == 0); numberOfPresentEvents = surface->landedPresentFeedbackCount; surface->landedPresentFeedbackCount = 0; pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return numberOfPresentEvents; } WL_EXPORT int wlEglProcessPresentationFeedbacksExport(WlEglSurface *surface) { int numberOfPresentEvents = 0; WlEglDisplay *display = wlEglAcquireDisplay((WlEglDisplay *)surface->wlEglDpy); pthread_mutex_lock(&surface->mutexLock); if (display->wpPresentation) { int ret = 0; assert(surface->landedPresentFeedbackCount == 0); ret = wl_display_dispatch_queue_pending(display->nativeDpy, surface->presentFeedbackQueue); if (ret < 0) { pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return ret; } } numberOfPresentEvents = surface->landedPresentFeedbackCount; surface->landedPresentFeedbackCount = 0; assert(surface->inFlightPresentFeedbackCount >= 0); pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return numberOfPresentEvents; } WL_EXPORT WlEglSurface *wlEglCreateSurfaceExport(EGLDisplay dpy, int width, int height, struct wl_surface *native_surface, int fifo_length) { WlEglDisplay *display = (WlEglDisplay *)wlEglAcquireDisplay(dpy); WlEglSurface *surface = NULL; if (!display) { return NULL; } pthread_mutex_lock(&display->mutex); surface = calloc(1, sizeof (*surface)); if (!surface) { goto fail; } surface->wlEglDpy = display; surface->width = width; surface->height = height; surface->wlSurface = native_surface; surface->fifoLength = fifo_length; surface->swapInterval = fifo_length > 0 ? 1 : 0; // Create per surface wayland queue surface->wlEventQueue = wl_display_create_queue(display->nativeDpy); // Create an event queue for presentation time feedback events if // the presentation time protocol exists if (display->wpPresentation) { surface->presentFeedbackQueue = wl_display_create_queue(display->nativeDpy); } surface->refCount = 1; if (!wlEglInitializeMutex(&surface->mutexLock)) { goto fail; } if (!wlEglInitializeMutex(&surface->mutexFrameSync)) { pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } if (pthread_cond_init(&surface->condFrameSync, NULL)) { pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } if (create_surface_context(surface) != EGL_SUCCESS) { wl_event_queue_destroy(surface->wlEventQueue); if (surface->presentFeedbackQueue) { wl_event_queue_destroy(surface->presentFeedbackQueue); } goto fail; } wl_list_insert(&display->wlEglSurfaceList, &surface->link); wl_list_init(&surface->oldCtxList); if (surface->ctx.wlStreamResource) { /* Set client's pendingSwapIntervalUpdate for updating client's * swapinterval */ surface->pendingSwapIntervalUpdate = EGL_TRUE; } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return surface; fail: pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); free(surface); return NULL; } WL_EXPORT WlEglSurface *wlEglCreateSurfaceExport2(EGLDisplay dpy, int width, int height, struct wl_surface *native_surface, int fifo_length, int (*present_update_callback)(void*, uint64_t, int), const EGLAttrib *attribs) { WlEglSurface* const surface = wlEglCreateSurfaceExport(dpy, width, height, native_surface, fifo_length); if (!surface) { return NULL; } surface->present_update_callback = present_update_callback; if (assignWlEglSurfaceAttribs(surface, attribs) != EGL_SUCCESS) { wlEglDestroySurfaceHook(dpy, surface); return NULL; } return surface; } void wlEglReallocSurface(WlEglDisplay *display, WlEglPlatformData *pData, WlEglSurface *surface) { EGLint err = EGL_SUCCESS; // If a damage thread is in use, wait for it to finish processing all // pending frames finish_wl_eglstream_damage_thread(surface, &surface->ctx, 0); discard_surface_context(surface); surface->isResized = EGL_FALSE; surface->ctx.wlStreamResource = NULL; surface->ctx.isAttached = EGL_FALSE; surface->ctx.eglSurface = EGL_NO_SURFACE; surface->ctx.eglStream = EGL_NO_STREAM_KHR; surface->ctx.damageThreadSync = EGL_NO_SYNC_KHR; surface->ctx.damageThreadId = (pthread_t)0; surface->feedback.unprocessedFeedback = false; display->defaultFeedback.unprocessedFeedback = false; err = create_surface_context(surface); if (err == EGL_SUCCESS) { /* This looks like a no-op, but we've replaced the surface's internal * handle with a new surface, so we need to make it current again. */ pData->egl.makeCurrent(display, pData->egl.getCurrentSurface(EGL_DRAW), pData->egl.getCurrentSurface(EGL_READ), pData->egl.getCurrentContext()); if (surface->ctx.wlStreamResource) { /* Set client's pendingSwapIntervalUpdate for updating client's * swapinterval */ surface->pendingSwapIntervalUpdate = EGL_TRUE; } } } static void resize_callback(struct wl_egl_window *window, void *data) { WlEglDisplay *display = NULL; WlEglPlatformData *pData; WlEglSurface *surface = (WlEglSurface *)data; if (!window || !surface) { return; } display = surface->wlEglDpy; if (!wlEglIsWaylandDisplay(display->nativeDpy) || !wlEglIsWaylandWindowValid(surface->wlEglWin)) { return; } pData = display->data; pthread_mutex_lock(&surface->mutexLock); /* Resize stream only if window geometry has changed */ if ((surface->width != window->width) || (surface->height != window->height) || (surface->dx != window->dx) || (surface->dy != window->dy)) { if (surface == pData->egl.getCurrentSurface(EGL_DRAW) || surface == pData->egl.getCurrentSurface(EGL_READ)) { wlEglReallocSurface(display, pData, surface); } else { surface->isResized = EGL_TRUE; } } pthread_mutex_unlock(&surface->mutexLock); } static EGLBoolean validateSurfaceAttrib(EGLAttrib attrib, EGLAttrib value) { switch (attrib) { /* Window-only attributes will be ignored, but we still need to make sure a * valid value is given */ case EGL_RENDER_BUFFER: return (value == EGL_BACK_BUFFER) ? EGL_TRUE : EGL_FALSE; case EGL_POST_SUB_BUFFER_SUPPORTED_NV: return (value == EGL_TRUE || value == EGL_FALSE) ? EGL_TRUE : EGL_FALSE; /* EGL_WIDTH and EGL_HEIGHT shouldn't be specified */ case EGL_WIDTH: case EGL_HEIGHT: return EGL_FALSE; case EGL_PRESENT_OPAQUE_EXT: return (value == EGL_TRUE || value == EGL_FALSE) ? EGL_TRUE : EGL_FALSE; /* If attribute is supported/unsupported for both EGL_WINDOW_BIT and * EGL_STREAM_BIT_KHR, then that will be handled inside the actual * eglCreateStreamProducerSurfaceKHR() */ default: return EGL_TRUE; } } static EGLint assignWlEglSurfaceAttribs(WlEglSurface *surface, const EGLAttrib *attribs) { EGLint *int_attribs = NULL; unsigned int nAttribs = 2; // At least width and height int i; if (attribs) { for (i = 0; attribs[i] != EGL_NONE; i += 2) { if (!validateSurfaceAttrib(attribs[i], attribs[i + 1])) { return EGL_BAD_ATTRIBUTE; } /* Filter out window-only attributes */ if ((attribs[i] != EGL_RENDER_BUFFER) && (attribs[i] != EGL_POST_SUB_BUFFER_SUPPORTED_NV)) { nAttribs++; } } } int_attribs = (EGLint *)malloc(((nAttribs * 2) + 1) * sizeof(*int_attribs)); if (!int_attribs) { return EGL_BAD_ALLOC; } nAttribs = 0; int_attribs[nAttribs++] = EGL_WIDTH; // width at offset 0 int_attribs[nAttribs++] = 0; int_attribs[nAttribs++] = EGL_HEIGHT; // height at offset 2 int_attribs[nAttribs++] = 0; if (attribs) { for (i = 0; attribs[i] != EGL_NONE; i += 2) { if (attribs[i] == EGL_PRESENT_OPAQUE_EXT) { surface->presentOpaque = attribs[i + 1]; continue; } if ((attribs[i] != EGL_RENDER_BUFFER) && (attribs[i] != EGL_POST_SUB_BUFFER_SUPPORTED_NV)) { int_attribs[nAttribs++] = (EGLint)attribs[i]; int_attribs[nAttribs++] = (EGLint)attribs[i + 1]; } } } int_attribs[nAttribs] = EGL_NONE; surface->attribs = int_attribs; return EGL_SUCCESS; } EGLBoolean wlEglQuerySurfaceHook(EGLDisplay dpy, EGLSurface eglSurface, EGLint attribute, EGLint *value) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; WlEglSurface *surface = (WlEglSurface *)eglSurface; EGLint ret = EGL_FALSE; EGLint err = EGL_SUCCESS; if (!display) { return EGL_FALSE; } data = display->data; if (!wlEglIsWlEglSurfaceForDisplay(display, surface)) { err = EGL_BAD_SURFACE; wlEglSetError(data, err); goto done; } if (attribute == EGL_PRESENT_OPAQUE_EXT) { *value = surface->presentOpaque; ret = EGL_TRUE; goto done; } dpy = display->devDpy->eglDisplay; ret = data->egl.querySurface(dpy, surface->ctx.eglSurface, attribute, value); done: wlEglReleaseDisplay(display); return ret; } EGLBoolean wlEglSurfaceRef(WlEglDisplay *display, WlEglSurface *surface) { if (!wlEglIsWlEglSurfaceForDisplay(display, surface) || surface->wlEglDpy->initCount == 0) { return EGL_FALSE; } surface->refCount++; return EGL_TRUE; } void wlEglSurfaceUnref(WlEglSurface *surface) { surface->refCount--; if (surface->refCount > 0) { return; } wlEglMutexDestroy(&surface->mutexLock); wlEglMutexDestroy(&surface->ctx.streamImagesMutex); if (!surface->ctx.isOffscreen) { wlEglMutexDestroy(&surface->mutexFrameSync); pthread_cond_destroy(&surface->condFrameSync); } free(surface); return; } static EGLBoolean wlEglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) { WlEglDisplay *display = (WlEglDisplay*)dpy; WlEglSurface *surface = (WlEglSurface*)eglSurface; if (!wlEglIsWlEglSurfaceForDisplay(display, surface) || display != surface->wlEglDpy) { return EGL_FALSE; } wl_list_remove(&surface->link); surface->isDestroyed = EGL_TRUE; // Acquire WlEglSurface lock. pthread_mutex_lock(&surface->mutexLock); destroy_surface_context(surface, &surface->ctx); if (!surface->ctx.isOffscreen) { WlEglSurfaceCtx *ctx; WlEglSurfaceCtx *next; // We only expect a valid wlEglWin to be set when using // a surface created with EGL_KHR_platform_wayland. if (wlEglIsWaylandDisplay(display->nativeDpy) && wlEglIsWaylandWindowValid(surface->wlEglWin)) { surface->wlEglWin->driver_private = NULL; surface->wlEglWin->resize_callback = NULL; if (surface->wlEglWinVer >= WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE) { surface->wlEglWin->destroy_window_callback = NULL; } } wl_list_for_each_safe(ctx, next, &surface->oldCtxList, link) { destroy_surface_context(surface, ctx); wl_list_remove(&ctx->link); free(ctx); } free(surface->attribs); } wlEglDestroyFeedback(&surface->feedback); if (surface->wlSyncobjSurf) { wp_linux_drm_syncobj_surface_v1_destroy(surface->wlSyncobjSurf); wp_linux_drm_syncobj_timeline_v1_destroy(surface->wlAcquireTimeline); } if (surface->presentFeedbackQueue != NULL) { wl_event_queue_destroy(surface->presentFeedbackQueue); surface->presentFeedbackQueue = NULL; } if (surface->throttleCallback != NULL) { wl_callback_destroy(surface->throttleCallback); surface->throttleCallback = NULL; } /* all proxies using the queue must be destroyed first! */ if (surface->wlEventQueue != NULL) { wl_event_queue_destroy(surface->wlEventQueue); surface->wlEventQueue = NULL; } if (surface->wlBufferEventQueue) { /* * If explicit sync is in use, the stream images are destroyed when * destroy_surface_context() is called above. */ WlEglStreamImage *image; WlEglStreamImage *nextImage; pthread_mutex_lock(&surface->ctx.streamImagesMutex); /* * Destroy any attached buffers to ensure no further buffer release * events are delivered after the buffer release queue and thread are * torn down. * * Do not destroy the WlEglStreamImages here because they may be * in-flight and as soon as we unlock streamImagesMutex, the buffer * release thread may access them. */ wl_list_for_each(image, &surface->ctx.streamImages, link) { if (image->buffer) { assert(image->attached); wl_buffer_destroy(image->buffer); image->buffer = NULL; image->attached = EGL_FALSE; } } pthread_mutex_unlock(&surface->ctx.streamImagesMutex); finish_wl_buffer_release_thread(surface); /* * If there are remaining images in the streamImages list, destroy them * here safely since the buffer release thread is no longer running. */ wl_list_for_each_safe(image, nextImage, &surface->ctx.streamImages, link) { destroy_stream_image(display, surface, image); } } assert(wl_list_empty(&surface->ctx.streamImages)); // Release WlEglSurface lock. pthread_mutex_unlock(&surface->mutexLock); wlEglSurfaceUnref(eglSurface); return EGL_TRUE; } static void destroy_callback(void *data) { WlEglSurface *surface = (WlEglSurface*)data; WlEglDisplay *display = surface->wlEglDpy; pthread_mutex_lock(&display->mutex); if (!surface || surface->wlEglDpy->initCount == 0) { pthread_mutex_unlock(&display->mutex); return; } wlEglDestroySurface((EGLDisplay)surface->wlEglDpy, (EGLSurface)surface); pthread_mutex_unlock(&display->mutex); } static void getWlEglWindowVersionAndSurface(struct wl_egl_window *window, long int *version, struct wl_surface **surface) { /* * Given that wl_egl_window wasn't always a versioned struct, and that * 'window->version' replaced 'window->surface', we must check whether * 'window->version' is actually a valid pointer. If it is, we are dealing * with a wl_egl_window from an old implementation of libwayland-egl.so */ *version = window->version; *surface = window->surface; if (wlEglMemoryIsReadable((void *)window->version, sizeof (void *))) { *version = 0; *surface = (struct wl_surface *)(window->version); } } static bool wlEglInitializeSurfaceCommon(WlEglDisplay *display, WlEglSurface *surface, EGLConfig config) { surface->wlEglDpy = display; surface->eglConfig = config; surface->syncPoint = 1; surface->refCount = 1; surface->isDestroyed = EGL_FALSE; wl_list_init(&surface->ctx.streamImages); wl_list_init(&surface->oldCtxList); return wlEglInitializeMutex(&surface->ctx.streamImagesMutex); } EGLSurface wlEglCreatePlatformWindowSurfaceHook(EGLDisplay dpy, EGLConfig config, void *nativeWin, const EGLAttrib *attribs) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; WlEglSurface *surface = NULL; WlEglSurface *existingSurf = NULL; struct wl_egl_window *window = (struct wl_egl_window *)nativeWin; struct wl_surface *wsurf = NULL; long int wver = 0; EGLBoolean res = EGL_FALSE; EGLint err = EGL_SUCCESS; EGLint surfType; int drmSyncobjFd = -1; if (!display) { return EGL_NO_SURFACE; } pthread_mutex_lock(&display->mutex); data = display->data; if (display->initCount == 0) { err = EGL_NOT_INITIALIZED; goto fail; } dpy = display->devDpy->eglDisplay; if (!wlEglIsWaylandWindowValid(window)) { err = EGL_BAD_NATIVE_WINDOW; goto fail; } // Check for existing associated surface if (window->driver_private != NULL) { err = EGL_BAD_ALLOC; goto fail; } getWlEglWindowVersionAndSurface(window, &wver, &wsurf); if (wsurf == NULL) { err = EGL_BAD_ALLOC; goto fail; } // Make sure that we don't have any existing EGLSurfaces for this // wl_surface. The driver_private check above isn't sufficient for this: If // the app calls wl_egl_window_create more than once on the same // wl_surface, then it would get multiple wl_egl_window structs. wl_list_for_each(existingSurf, &display->wlEglSurfaceList, link) { if (existingSurf->wlSurface == wsurf) { err = EGL_BAD_ALLOC; goto fail; } } res = data->egl.getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfType); if (!res || !(surfType & EGL_STREAM_BIT_KHR)) { err = EGL_BAD_CONFIG; goto fail; } if (!display->devDpy->exts.stream || (!display->devDpy->exts.stream_cross_process_fd && !display->devDpy->exts.stream_remote) || !display->devDpy->exts.stream_producer_eglsurface) { err = EGL_BAD_ALLOC; goto fail; } surface = calloc(1, sizeof(*surface)); if (!surface) { err = EGL_BAD_ALLOC; goto fail; } if (!wlEglInitializeSurfaceCommon(display, surface, config)) { err = EGL_BAD_ALLOC; goto fail; } if (!wlEglInitializeMutex(&surface->mutexLock)) { err = EGL_BAD_ALLOC; goto fail; } if (!wlEglInitializeMutex(&surface->mutexFrameSync)) { err = EGL_BAD_ALLOC; goto fail; } if (pthread_cond_init(&surface->condFrameSync, NULL)) { err = EGL_BAD_ALLOC; goto fail; } surface->wlEglWin = window; surface->ctx.eglStream = EGL_NO_STREAM_KHR; surface->ctx.eglSurface = EGL_NO_SURFACE; surface->ctx.isOffscreen = EGL_FALSE; surface->isSurfaceProducer = EGL_TRUE; // FIFO_LENGTH == 1 to set FIFO mode, FIFO_LENGTH == 0 to set MAILBOX mode // We set two here however to bump the "swapchain" count to 4 on Wayland. // This is done to better match what Mesa does, as apparently 4 is the // expectation on wayland. // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6249#note_1328923 // // The problem users are running into is that we always have to advance to // a new buffer (in PresentCore) because the driver always expects to be // incremented to the next valid buffer as part of swapbuffers. So // currently it seems one of the three images will always be owned by the // driver (either the buffer currently/just rendered to, or the one we just // advanced to for future rendering) // // So the three buffers are used up by: // 1. One buffer owned by the driver // 2. One buffer that just got committed and shared with the compositor // 3. One buffer owned by the compositor, pending a release // // For whatever reason Kwin is holding onto 2 and 3 indefinitely when the // dock gets hidden, and we hold onto 1 and try waiting for one of the // other two to become free. We need a fourth to allow us to continue feeding // the driver . surface->fifoLength = (display->devDpy->exts.stream_fifo_synchronous && display->devDpy->exts.stream_sync) ? 2 : 0; // Create per surface wayland queue surface->wlEventQueue = wl_display_create_queue(display->nativeDpy); surface->wlEglWinVer = wver; surface->wlSurface = wsurf; err = assignWlEglSurfaceAttribs(surface, attribs); if (err != EGL_SUCCESS) { goto fail; } /* * If the compositor supports it, then we can request a dmabuf feedback * object for this surface. This will let the compositor give us per-surface * hints about which modifiers to use. */ if (display->dmaBufProtocolVersion >= 4) { struct zwp_linux_dmabuf_v1 *wrapper = wl_proxy_create_wrapper(display->wlDmaBuf); wl_proxy_set_queue((struct wl_proxy *)wrapper, surface->wlEventQueue); surface->feedback.wlDmaBufFeedback = zwp_linux_dmabuf_v1_get_surface_feedback(wrapper, surface->wlSurface); wl_proxy_wrapper_destroy(wrapper); if (!surface->feedback.wlDmaBufFeedback || WlEglRegisterFeedback(&surface->feedback)) { err = EGL_BAD_ALLOC; goto fail; } /* Do a roundtrip to get the tranches before calling create_surface_context */ if (wl_display_roundtrip_queue(display->nativeDpy, surface->wlEventQueue) < 0) { err = EGL_BAD_ALLOC; goto fail; } /* We haven't allocated our surface yet, so we can clear this flag. */ surface->feedback.unprocessedFeedback = false; } if (display->wlDrmSyncobj) { /* Create a DRM timeline and share it with the compositor */ drmSyncobjFd = create_syncobj_timeline(display, &surface->drmSyncobjHandle); if (drmSyncobjFd < 0) { goto fail; } /* Get a per-surface explicit sync object, share our DRM syncobj with the compositor */ surface->wlSyncobjSurf = wp_linux_drm_syncobj_manager_v1_get_surface(display->wlDrmSyncobj, surface->wlSurface); surface->wlAcquireTimeline = wp_linux_drm_syncobj_manager_v1_import_timeline(display->wlDrmSyncobj, drmSyncobjFd); close(drmSyncobjFd); drmSyncobjFd = -1; if (!surface->wlSyncobjSurf || !surface->wlAcquireTimeline) { err = EGL_BAD_ALLOC; goto fail; } } err = create_surface_context(surface); if (err != EGL_SUCCESS) { goto fail; } surface->swapInterval = 1; // Default swap interval is 1 if (surface->ctx.wlStreamResource) { /* Set client's pendingSwapIntervalUpdate for updating client's * swapinterval */ surface->pendingSwapIntervalUpdate = EGL_TRUE; } window->driver_private = surface; window->resize_callback = resize_callback; if (surface->wlEglWinVer >= WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE) { window->destroy_window_callback = destroy_callback; } wl_list_insert(&display->wlEglSurfaceList, &surface->link); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return surface; fail: if (drmSyncobjFd > 0) { close(drmSyncobjFd); } if (surface) { if (surface->drmSyncobjHandle) { drmSyncobjDestroy(display->drmFd, surface->drmSyncobjHandle); } wlEglDestroySurface(display, surface); } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); wlEglSetError(data, err); return EGL_NO_SURFACE; } EGLSurface wlEglCreatePlatformPixmapSurfaceHook(EGLDisplay dpy, EGLConfig config, void *nativePixmap, const EGLAttrib *attribs) { WlEglDisplay *display = (WlEglDisplay*)dpy; (void) config; (void) nativePixmap; (void) attribs; /* Wayland does not support pixmap types. See EGL_EXT_platform_wayland. */ wlEglSetError(display->data, EGL_BAD_PARAMETER); return EGL_NO_SURFACE; } EGLSurface wlEglCreatePbufferSurfaceHook(EGLDisplay dpy, EGLConfig config, const EGLint *attribs) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; WlEglSurface *surface = NULL; EGLSurface surf = EGL_NO_SURFACE; EGLint err = EGL_SUCCESS; if (!display) { return EGL_NO_SURFACE; } pthread_mutex_lock(&display->mutex); data = display->data; /* Nothing really special needs to be done. Just fall back to the driver's * Pbuffer surface creation function */ dpy = display->devDpy->eglDisplay; surf = data->egl.createPbufferSurface(dpy, config, attribs); if (surf == EGL_NO_SURFACE) { goto fail; } surface = calloc(1, sizeof(*surface)); if (!surface) { err = EGL_BAD_ALLOC; goto fail; } if (!wlEglInitializeSurfaceCommon(display, surface, config)) { err = EGL_BAD_ALLOC; goto fail; } surface->ctx.eglSurface = surf; surface->ctx.isOffscreen = EGL_TRUE; wl_list_insert(&display->wlEglSurfaceList, &surface->link); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return surface; fail: pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); if (err != EGL_SUCCESS) { wlEglSetError(data, err); } return EGL_NO_SURFACE; } EGLSurface wlEglCreateStreamProducerSurfaceHook(EGLDisplay dpy, EGLConfig config, EGLStreamKHR stream, const EGLint *attribs) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); WlEglPlatformData *data = NULL; WlEglSurface *surface = NULL; EGLSurface surf = EGL_NO_SURFACE; EGLint err = EGL_SUCCESS; if (!display) { return EGL_NO_SURFACE; } pthread_mutex_lock(&display->mutex); data = display->data; /* Nothing really special needs to be done. Just fall back to the driver's * stream producer surface creation function */ dpy = display->devDpy->eglDisplay; surf = data->egl.createStreamProducerSurface(dpy, config, stream, attribs); if (surf == EGL_NO_SURFACE) { goto fail; } surface = calloc(1, sizeof(*surface)); if (!surface) { err = EGL_BAD_ALLOC; goto fail; } if (!wlEglInitializeMutex(&surface->mutexLock)) { err = EGL_BAD_ALLOC; goto fail; } surface->wlEglDpy = display; surface->eglConfig = config; surface->ctx.eglSurface = surf; surface->ctx.isOffscreen = EGL_TRUE; surface->refCount = 1; surface->isDestroyed = EGL_FALSE; wl_list_init(&surface->oldCtxList); wl_list_insert(&display->wlEglSurfaceList, &surface->link); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return surface; fail: pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); if (err != EGL_SUCCESS) { wlEglSetError(data, err); } return EGL_NO_SURFACE; } EGLBoolean wlEglDestroySurfaceHook(EGLDisplay dpy, EGLSurface eglSurface) { WlEglDisplay *display = wlEglAcquireDisplay(dpy); EGLint ret = EGL_FALSE; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); if (display->initCount == 0) { wlEglSetError(display->data, EGL_NOT_INITIALIZED); goto done; } ret = wlEglDestroySurface(dpy, eglSurface); if (!ret) { wlEglSetError(display->data, EGL_BAD_SURFACE); } done: pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return ret; } EGLBoolean wlEglDestroyAllSurfaces(WlEglDisplay *display) { WlEglSurface *surface, *next; EGLBoolean res = EGL_TRUE; wl_list_for_each_safe(surface, next, &display->wlEglSurfaceList, link) { if (surface->wlEglDpy == display) { res = wlEglDestroySurface(display, surface) && res; } } return res; } EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy, void *nativeResource, EGLint attribute, int *value) { struct wl_eglstream_display *wlStreamDpy = NULL; struct wl_eglstream *wlStream = NULL; EGLBoolean res = EGL_FALSE; EGLint originY; wlExternalApiLock(); wlStreamDpy = wl_eglstream_display_get(dpy); if (!wlStreamDpy) { goto done; } wlStream = wl_eglstream_display_get_stream( wlStreamDpy, (struct wl_resource *)nativeResource); if(!wlStream) { goto done; } switch (attribute) { case EGL_WIDTH: *value = (int)wlStream->width; res = EGL_TRUE; goto done; case EGL_HEIGHT: *value = (int)wlStream->height; res = EGL_TRUE; goto done; case EGL_WAYLAND_Y_INVERTED_WL: if (wlStreamDpy->exts.stream_origin && wlStreamDpy->data->egl.queryStream(wlStreamDpy->eglDisplay, wlStream->eglStream, EGL_STREAM_FRAME_ORIGIN_Y_NV, &originY)) { /* If we have an image with origin at the top, the wayland * compositor will consider it as y-inverted */ *value = (int)((originY == EGL_TOP_NV) ? EGL_TRUE : EGL_FALSE); } else { /* No mechanism found to query frame orientation. Set to * stream's default value.*/ *value = (int)wlStream->yInverted; } res = EGL_TRUE; goto done; } done: wlExternalApiUnlock(); return res; } egl-wayland-1.1.18/src/wayland-eglswap.c000066400000000000000000000363261474153032600200570ustar00rootroot00000000000000/* * Copyright (c) 2014-2022, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include "wayland-eglswap.h" #include "wayland-eglstream-client-protocol.h" #include "presentation-time-client-protocol.h" #include "wayland-thread.h" #include "wayland-egldisplay.h" #include "wayland-eglsurface-internal.h" #include "wayland-eglhandle.h" #include "wayland-eglutils.h" #include #include #include enum PresentationStatus { KICKED_OFF = 0, LANDED = 1 }; struct EventItem { uint64_t capturedId; void *presentInfo; WlEglSurface *surface; }; EGLBoolean wlEglSwapBuffersHook(EGLDisplay eglDisplay, EGLSurface eglSurface) { return wlEglSwapBuffersWithDamageHook(eglDisplay, eglSurface, NULL, 0); } EGLBoolean wlEglSwapBuffersWithDamageHook(EGLDisplay eglDisplay, EGLSurface eglSurface, EGLint *rects, EGLint n_rects) { WlEglDisplay *display = wlEglAcquireDisplay(eglDisplay); WlEglPlatformData *data = NULL; WlEglSurface *surface = NULL; EGLStreamKHR eglStream = EGL_NO_STREAM_KHR; EGLBoolean isOffscreen = EGL_FALSE; EGLBoolean res; EGLint err; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); data = display->data; if (display->initCount == 0) { err = EGL_NOT_INITIALIZED; goto fail; } if (!wlEglSurfaceRef(display, eglSurface)) { err = EGL_BAD_SURFACE; goto fail; } surface = eglSurface; if (surface->pendingSwapIntervalUpdate == EGL_TRUE) { /* Send request from client to override swapinterval value based on * server's swapinterval for overlay compositing */ assert(surface->ctx.wlStreamResource); wl_eglstream_display_swap_interval(display->wlStreamDpy, surface->ctx.wlStreamResource, surface->swapInterval); /* For receiving any event in case of override */ if (wl_display_roundtrip_queue(display->nativeDpy, display->wlEventQueue) < 0) { err = EGL_BAD_ALLOC; goto fail; } surface->pendingSwapIntervalUpdate = EGL_FALSE; } pthread_mutex_unlock(&display->mutex); // Acquire wlEglSurface lock. pthread_mutex_lock(&surface->mutexLock); if (surface->isDestroyed) { err = EGL_BAD_SURFACE; goto fail_locked; } isOffscreen = surface->ctx.isOffscreen; if (!isOffscreen) { if (!wlEglIsWaylandWindowValid(surface->wlEglWin)) { err = EGL_BAD_NATIVE_WINDOW; goto fail_locked; } if (surface->ctx.useDamageThread) { pthread_mutex_lock(&surface->mutexFrameSync); // Wait for damage thread to submit the // previous frame and generate frame sync while (surface->ctx.framesProduced != surface->ctx.framesProcessed) { pthread_cond_wait(&surface->condFrameSync, &surface->mutexFrameSync); } pthread_mutex_unlock(&surface->mutexFrameSync); } wlEglWaitFrameSync(surface); } /* Save the internal EGLDisplay, EGLSurface and EGLStream handles, as * they are needed by the eglSwapBuffers() and streamFlush calls below */ eglDisplay = display->devDpy->eglDisplay; eglSurface = surface->ctx.eglSurface; eglStream = surface->ctx.eglStream; /* eglSwapBuffers() is a blocking call. We must release the lock so other * threads using the external platform are allowed to progress. */ if (rects) { res = data->egl.swapBuffersWithDamage(eglDisplay, eglSurface, rects, n_rects); } else { res = data->egl.swapBuffers(eglDisplay, eglSurface); } if (isOffscreen) { goto done; } if (display->devDpy->exts.stream_flush) { data->egl.streamFlush(eglDisplay, eglStream); } if (res) { if (surface->ctx.useDamageThread) { surface->ctx.framesProduced++; } else { wlEglCreateFrameSync(surface); res = wlEglSendDamageEvent(surface, surface->wlEventQueue, rects, n_rects); wlEglSurfaceCheckReleasePoints(display, surface); } } /* Resize stream if window geometry or available modifiers have changed */ if (surface->isResized || surface->feedback.unprocessedFeedback || display->defaultFeedback.unprocessedFeedback) { wlEglReallocSurface(display, data, surface); } done: // Release wlEglSurface lock. pthread_mutex_unlock(&surface->mutexLock); /* reacquire display lock */ pthread_mutex_lock(&display->mutex); wlEglSurfaceUnref(surface); pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return res; fail_locked: pthread_mutex_unlock(&surface->mutexLock); /* reacquire display lock */ pthread_mutex_lock(&display->mutex); fail: if (surface != NULL) { wlEglSurfaceUnref(surface); } pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); wlEglSetError(data, err); return EGL_FALSE; } EGLBoolean wlEglSwapIntervalHook(EGLDisplay eglDisplay, EGLint interval) { WlEglDisplay *display = wlEglAcquireDisplay(eglDisplay); WlEglPlatformData *data = NULL; WlEglSurface *surface = NULL; EGLBoolean ret = EGL_TRUE; EGLint state; if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); data = display->data; if (display->initCount == 0) { wlEglSetError(data, EGL_NOT_INITIALIZED); ret = EGL_FALSE; goto done; } /* Save the internal EGLDisplay handle, as it's needed by the actual * eglSwapInterval() call */ eglDisplay = display->devDpy->eglDisplay; pthread_mutex_unlock(&display->mutex); if (!(data->egl.swapInterval(eglDisplay, interval))) { wlEglReleaseDisplay(display); return EGL_FALSE; } surface = (WlEglSurface *)data->egl.getCurrentSurface(EGL_DRAW); pthread_mutex_lock(&display->mutex); /* Check this is a valid wayland EGL surface */ if (display->initCount == 0 || !wlEglIsWlEglSurfaceForDisplay(display, surface) || (surface->swapInterval == interval) || (surface->ctx.eglStream == EGL_NO_STREAM_KHR)) { goto done; } /* Cache interval value so we can reset it upon surface reattach */ surface->swapInterval = interval; if (surface->ctx.wlStreamResource && data->egl.queryStream(display->devDpy->eglDisplay, surface->ctx.eglStream, EGL_STREAM_STATE_KHR, &state) && state != EGL_STREAM_STATE_DISCONNECTED_KHR) { /* Set client's pendingSwapIntervalUpdate for updating client's * swapinterval if the compositor supports wl_eglstream_display * and the surface has a valid server-side stream */ surface->pendingSwapIntervalUpdate = EGL_TRUE; } done: pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return ret; } WL_EXPORT EGLBoolean wlEglPrePresentExport(WlEglSurface *surface) { WlEglDisplay *display = wlEglAcquireDisplay((WlEglDisplay *)surface->wlEglDpy); if (!display) { return EGL_FALSE; } pthread_mutex_lock(&display->mutex); if (surface->pendingSwapIntervalUpdate == EGL_TRUE) { /* Send request from client to override swapinterval value based on * server's swapinterval for overlay compositing */ wl_eglstream_display_swap_interval(display->wlStreamDpy, surface->ctx.wlStreamResource, surface->swapInterval); /* For receiving any event in case of override */ if (wl_display_roundtrip_queue(display->nativeDpy, display->wlEventQueue) < 0) { pthread_mutex_unlock(&display->mutex); wlEglReleaseDisplay(display); return EGL_FALSE; } surface->pendingSwapIntervalUpdate = EGL_FALSE; } pthread_mutex_unlock(&display->mutex); // Acquire wlEglSurface lock. pthread_mutex_lock(&surface->mutexLock); if (surface->ctx.useDamageThread) { pthread_mutex_lock(&surface->mutexFrameSync); // Wait for damage thread to submit the // previous frame and generate frame sync while (surface->ctx.framesProduced != surface->ctx.framesProcessed) { pthread_cond_wait(&surface->condFrameSync, &surface->mutexFrameSync); } pthread_mutex_unlock(&surface->mutexFrameSync); } wlEglWaitFrameSync(surface); // Release wlEglSurface lock. pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return EGL_TRUE; } WL_EXPORT EGLBoolean wlEglPostPresentExport(WlEglSurface *surface) { return wlEglPostPresentExport2(surface, 0, NULL); } static void present_feedback_sync_output(void *data, struct wp_presentation_feedback *feedback, struct wl_output *output) { // This function is intentionally left blank. sync_output events // precede the presented events when wl_output is bound. // // The information provided by this function is not needed at the // moment. (void) data; (void) feedback; (void) output; } static void present_feedback_discarded(void *data, struct wp_presentation_feedback *feedback) { struct EventItem *eventItem = data; WlEglSurface *surface = eventItem->surface; // If the following condition is not true, it means that this presentInfo // was overwritten. The presentInfo that it was pointing to when this // request was created is not the same as the one it is pointing to right // now. Status of the previous presentInfo is not relevant. if (surface->present_update_callback(eventItem->presentInfo, eventItem->capturedId, LANDED)) { surface->landedPresentFeedbackCount++; } surface->inFlightPresentFeedbackCount--; free(eventItem); (void) feedback; } static void present_feedback_presented(void *data, struct wp_presentation_feedback *feedback, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh, uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) { // For now, whatever the outcome of the presentation is, the same // operations are performed as a result present_feedback_discarded(data, feedback); (void) tv_sec_hi; (void) tv_sec_lo; (void) tv_nsec; (void) refresh; (void) seq_hi; (void) seq_lo; (void) tv_sec_hi; (void) flags; } static const struct wp_presentation_feedback_listener present_feedback_listener = { present_feedback_sync_output, present_feedback_presented, present_feedback_discarded }; WL_EXPORT EGLBoolean wlEglPostPresentExport2(WlEglSurface *surface, uint64_t presentId, void *presentInfo) { WlEglDisplay *display = wlEglAcquireDisplay((WlEglDisplay *)surface->wlEglDpy); WlEglPlatformData *data = NULL; EGLBoolean res = EGL_TRUE; if (!display) { return EGL_FALSE; } data = display->data; // Acquire wlEglSurface lock. pthread_mutex_lock(&surface->mutexLock); if (display->devDpy->exts.stream_flush) { data->egl.streamFlush((EGLDisplay) display, surface->ctx.eglStream); } if (presentInfo) { assert(surface->present_update_callback != NULL); if (display->wpPresentation) { struct wp_presentation_feedback *presentationFeedback = NULL; struct wp_presentation *wrapper = wl_proxy_create_wrapper(display->wpPresentation); struct EventItem *eventItem = malloc(sizeof(struct EventItem)); eventItem->capturedId = presentId; eventItem->presentInfo = presentInfo; eventItem->surface = surface; wl_proxy_set_queue((struct wl_proxy *)wrapper, surface->presentFeedbackQueue); presentationFeedback = wp_presentation_feedback(wrapper, surface->wlSurface); wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ if (wp_presentation_feedback_add_listener(presentationFeedback, &present_feedback_listener, eventItem) == -1) { pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return EGL_FALSE; } surface->present_update_callback(presentInfo, presentId, KICKED_OFF); surface->inFlightPresentFeedbackCount++; } else { // If the presentation feedback protocol is not supported by the compositor, // there is not much we can do to get this information. surface->present_update_callback(presentInfo, presentId, LANDED); surface->landedPresentFeedbackCount++; } } if (surface->ctx.useDamageThread) { surface->ctx.framesProduced++; } else { wlEglCreateFrameSync(surface); res = wlEglSendDamageEvent(surface, surface->wlEventQueue, NULL, 0); } // Release wlEglSurface lock. pthread_mutex_unlock(&surface->mutexLock); wlEglReleaseDisplay(display); return res; } EGLint wlEglStreamSwapIntervalCallback(WlEglPlatformData *data, EGLStreamKHR stream, EGLint *interval) { EGLint res = EGL_SUCCESS; if (data->callbacks.streamSwapInterval) { res = data->callbacks.streamSwapInterval(stream, interval); } return res; } egl-wayland-1.1.18/src/wayland-eglutils.c000066400000000000000000000071161474153032600202400ustar00rootroot00000000000000/* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "wayland-eglutils.h" #include "wayland-thread.h" #include "wayland-eglhandle.h" #include #include #include #include #include #include #include EGLBoolean wlEglFindExtension(const char *extension, const char *extensions) { const char *start; const char *where, *terminator; start = extensions; for (;;) { where = strstr(start, extension); if (!where) { break; } terminator = where + strlen(extension); if (where == start || *(where - 1) == ' ') { if (*terminator == ' ' || *terminator == '\0') { return EGL_TRUE; } } start = terminator; } return EGL_FALSE; } EGLBoolean wlEglMemoryIsReadable(const void *p, size_t len) { int fds[2], result = -1; if (pipe(fds) == -1) { return EGL_FALSE; } if (fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1) { goto done; } /* write will fail with EFAULT if the provided buffer is outside * our accessible address space. */ result = write(fds[1], p, len); assert(result != -1 || errno == EFAULT); done: close(fds[0]); close(fds[1]); return result != -1; } EGLBoolean wlEglCheckInterfaceType(struct wl_object *obj, const char *ifname) { /* The first member of a wl_object is a pointer to its wl_interface, */ struct wl_interface *interface = *(void **)obj; /* Check if the memory for the wl_interface struct, and the * interface name, are safe to read. */ int len = strlen(ifname); if (!wlEglMemoryIsReadable(interface, sizeof (*interface)) || !wlEglMemoryIsReadable(interface->name, len + 1)) { return EGL_FALSE; } return !strcmp(interface->name, ifname); } void wlEglSetErrorCallback(WlEglPlatformData *data, EGLint error, const char *file, int line) { if (data && data->callbacks.setError) { const char *defaultMsg = "Wayland external platform error"; if (file != NULL) { char msg[256]; if (snprintf(msg, 256, "%s:%d: %s", file, line, defaultMsg) > 0) { data->callbacks.setError(error, EGL_DEBUG_MSG_ERROR_KHR, msg); return; } } data->callbacks.setError(error, EGL_DEBUG_MSG_ERROR_KHR, defaultMsg); } } egl-wayland-1.1.18/src/wayland-external-exports.c000066400000000000000000000117471474153032600217410ustar00rootroot00000000000000/* * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #include #include "wayland-external-exports.h" #include "wayland-egldisplay.h" #include "wayland-eglstream.h" #include "wayland-eglsurface-internal.h" #include "wayland-eglswap.h" #include "wayland-eglutils.h" #include "wayland-eglhandle.h" #include #include typedef struct WlEglHookRec { const char *name; void *func; } WlEglHook; static const WlEglHook wlEglHooksMap[] = { /* Keep names in ascending order */ { "eglBindWaylandDisplayWL", wlEglBindDisplaysHook }, { "eglChooseConfig", wlEglChooseConfigHook }, { "eglCreatePbufferSurface", wlEglCreatePbufferSurfaceHook }, { "eglCreatePlatformPixmapSurface", wlEglCreatePlatformPixmapSurfaceHook }, { "eglCreatePlatformWindowSurface", wlEglCreatePlatformWindowSurfaceHook }, { "eglCreateStreamAttribNV", wlEglCreateStreamAttribHook }, { "eglCreateStreamProducerSurfaceKHR", wlEglCreateStreamProducerSurfaceHook }, { "eglDestroySurface", wlEglDestroySurfaceHook }, { "eglGetConfigAttrib", wlEglGetConfigAttribHook }, { "eglInitialize", wlEglInitializeHook }, { "eglQueryDisplayAttribEXT", wlEglQueryDisplayAttribHook }, { "eglQueryDisplayAttribKHR", wlEglQueryDisplayAttribHook }, { "eglQuerySurface", wlEglQuerySurfaceHook }, { "eglQueryWaylandBufferWL", wlEglQueryNativeResourceHook }, { "eglSwapBuffers", wlEglSwapBuffersHook }, { "eglSwapBuffersWithDamageKHR", wlEglSwapBuffersWithDamageHook }, { "eglSwapInterval", wlEglSwapIntervalHook }, { "eglTerminate", wlEglTerminateHook }, { "eglUnbindWaylandDisplayWL", wlEglUnbindDisplaysHook }, }; static int hookCmp(const void *elemA, const void *elemB) { const char *key = (const char *)elemA; const WlEglHook *hook = (const WlEglHook *)elemB; return strcmp(key, hook->name); } static void* wlEglGetHookAddressExport(void *data, const char *name) { WlEglHook *hook; (void) data; hook = (WlEglHook *)bsearch((const void *)name, (const void *)wlEglHooksMap, sizeof(wlEglHooksMap)/sizeof(WlEglHook), sizeof(WlEglHook), hookCmp); if (hook) { return hook->func; } return NULL; } static EGLBoolean wlEglUnloadPlatformExport(void *data) { EGLBoolean res; res = wlEglDestroyAllDisplays((WlEglPlatformData *)data); wlEglDestroyPlatformData((WlEglPlatformData *)data); return res; } EGLBoolean loadEGLExternalPlatform(int major, int minor, const EGLExtDriver *driver, EGLExtPlatform *platform) { if (!platform || !EGL_EXTERNAL_PLATFORM_VERSION_CMP(major, minor, WAYLAND_EXTERNAL_VERSION_MAJOR, WAYLAND_EXTERNAL_VERSION_MINOR)) { return EGL_FALSE; } platform->version.major = WAYLAND_EXTERNAL_VERSION_MAJOR; platform->version.minor = WAYLAND_EXTERNAL_VERSION_MINOR; platform->version.micro = WAYLAND_EXTERNAL_VERSION_MICRO; platform->platform = EGL_PLATFORM_WAYLAND_EXT; platform->data = (void *)wlEglCreatePlatformData(major, minor, driver); if (platform->data == NULL) { return EGL_FALSE; } platform->exports.unloadEGLExternalPlatform = wlEglUnloadPlatformExport; platform->exports.getHookAddress = wlEglGetHookAddressExport; platform->exports.isValidNativeDisplay = wlEglIsValidNativeDisplayExport; platform->exports.getPlatformDisplay = wlEglGetPlatformDisplayExport; platform->exports.queryString = wlEglQueryStringExport; platform->exports.getInternalHandle = wlEglGetInternalHandleExport; return EGL_TRUE; } egl-wayland-1.1.18/src/wayland-thread.c000066400000000000000000000067431474153032600176640ustar00rootroot00000000000000/* * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ /* To include PTHREAD_MUTEX_ERRORCHECK */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include "wayland-thread.h" #include "wayland-egldisplay.h" #include #include #if defined(__QNX__) #define WL_EGL_ATTRIBUTE_DESTRUCTOR #define WL_EGL_ATEXIT(func) atexit(func) #else #define WL_EGL_ATTRIBUTE_DESTRUCTOR __attribute__((destructor)) #define WL_EGL_ATEXIT(func) 0 #endif static pthread_mutex_t wlMutex; static pthread_once_t wlMutexOnceControl = PTHREAD_ONCE_INIT; static int wlMutexInitialized = 0; static void wlExternalApiInitializeLock(void) { pthread_mutexattr_t attr; if (pthread_mutexattr_init(&attr)) { assert(!"failed to initialize pthread attribute mutex"); return; } if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) { assert(!"failed to set pthread attribute mutex errorcheck"); goto fail; } if (pthread_mutex_init(&wlMutex, &attr)) { assert(!"failed to initialize pthread mutex"); goto fail; } wlMutexInitialized = 1; fail: if (pthread_mutexattr_destroy(&attr)) { assert(!"failed to destroy pthread attribute mutex"); } } void wlExternalApiDestroyLock(void) { if (!wlMutexInitialized || pthread_mutex_destroy(&wlMutex)) { assert(!"failed to destroy pthread mutex"); } } int wlExternalApiLock(void) { if (pthread_once(&wlMutexOnceControl, wlExternalApiInitializeLock)) { assert(!"pthread once failed"); return -1; } if (!wlMutexInitialized || pthread_mutex_lock(&wlMutex)) { assert(!"failed to lock pthread mutex"); return -1; } return 0; } int wlExternalApiUnlock(void) { if (!wlMutexInitialized || pthread_mutex_unlock(&wlMutex)) { assert(!"failed to unlock pthread mutex"); return -1; } return 0; } bool wlEglInitializeMutex(pthread_mutex_t *mutex) { pthread_mutexattr_t attr; bool ret = true; if (pthread_mutexattr_init(&attr)) { return false; } if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) { ret = false; goto done; } if (pthread_mutex_init(mutex, &attr)) { ret = false; goto done; } done: pthread_mutexattr_destroy(&attr); return ret; } void wlEglMutexDestroy(pthread_mutex_t *mutex) { pthread_mutex_destroy(mutex); } egl-wayland-1.1.18/wayland-drm/000077500000000000000000000000001474153032600162325ustar00rootroot00000000000000egl-wayland-1.1.18/wayland-drm/meson.build000066400000000000000000000016651474153032600204040ustar00rootroot00000000000000foreach output_type: ['client-header', 'server-header', 'public-code'] if output_type == 'client-header' output_file = 'wayland-drm-client-protocol.h' elif output_type == 'server-header' output_file = 'wayland-drm-server-protocol.h' else output_file = 'wayland-drm-protocol.c' if wl_scanner.version().version_compare('< 1.14.91') output_type = 'code' elif generated_public_protocols.contains(proto) output_type = 'public-code' endif endif var_name = output_file.underscorify() target = custom_target( '@0@'.format(output_file), command: [prog_scanner, output_type, '@INPUT@', '@OUTPUT@'], input: 'wayland-drm.xml', output: output_file, ) set_variable(var_name, target) endforeach install_data( 'wayland-drm.xml', install_dir : join_paths(get_option('datadir'), meson.project_name()) ) egl-wayland-1.1.18/wayland-drm/wayland-drm.xml000066400000000000000000000174471474153032600212100ustar00rootroot00000000000000 Copyright © 2008-2011 Kristian Høgsberg Copyright © 2010-2011 Intel Corporation Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that\n the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. Bitmask of capabilities. egl-wayland-1.1.18/wayland-egl/000077500000000000000000000000001474153032600162175ustar00rootroot00000000000000egl-wayland-1.1.18/wayland-egl/wayland-egl-ext.h000066400000000000000000000112471474153032600213770ustar00rootroot00000000000000/* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * * 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 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. */ #ifndef WAYLAND_EGL_EXT_H #define WAYLAND_EGL_EXT_H #ifndef EGL_WL_bind_wayland_display #define EGL_WL_bind_wayland_display 1 #define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC #define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC #define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC struct wl_display; struct wl_resource; #define EGL_WAYLAND_BUFFER_WL 0x31D5 #define EGL_WAYLAND_PLANE_WL 0x31D6 #define EGL_TEXTURE_Y_U_V_WL 0x31D7 #define EGL_TEXTURE_Y_UV_WL 0x31D8 #define EGL_TEXTURE_Y_XUXV_WL 0x31D9 #define EGL_TEXTURE_EXTERNAL_WL 0x31DA #define EGL_WAYLAND_Y_INVERTED_WL 0x31DB typedef EGLBoolean (EGLAPIENTRYP PFNEGLBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display); typedef EGLBoolean (EGLAPIENTRYP PFNEGLUNBINDWAYLANDDISPLAYWLPROC) (EGLDisplay dpy, struct wl_display *display); typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWLPROC) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglBindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display); EGLAPI EGLBoolean EGLAPIENTRY eglUnbindWaylandDisplayWL (EGLDisplay dpy, struct wl_display *display); EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); #endif #endif /* EGL_WL_bind_wayland_display */ #ifndef EGL_WL_wayland_eglstream #define EGL_WL_wayland_eglstream 1 #define EGL_WAYLAND_EGLSTREAM_WL 0x334B #endif /* EGL_WL_wayland_eglstream */ #ifndef EGL_NV_stream_fifo_synchronous #define EGL_NV_stream_fifo_synchronous 1 #define EGL_STREAM_FIFO_SYNCHRONOUS_NV 0x3336 #endif /* EGL_NV_stream_fifo_synchronous */ #ifndef EGL_NV_stream_flush #define EGL_NV_stream_flush 1 typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMFLUSHNVPROC) (EGLDisplay dpy, EGLStreamKHR stream); #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglStreamFlushNV (EGLDisplay dpy, EGLStreamKHR stream); #endif #endif /*EGL_NV_stream_flush*/ /* Deprecated. Use EGL_KHR_stream_attrib */ #ifndef EGL_NV_stream_attrib #define EGL_NV_stream_attrib 1 #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamAttribNV(EGLDisplay dpy, const EGLAttrib *attrib_list); #endif typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMATTRIBNVPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list); #endif /* EGL_NV_stream_attrib */ #ifndef EGL_KHR_display_reference #define EGL_KHR_display_reference 1 #define EGL_TRACK_REFERENCES_KHR 0x3352 typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBKHRPROC) (EGLDisplay dpy, EGLint name, EGLAttrib *value); #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribKHR (EGLDisplay dpy, EGLint name, EGLAttrib *value); #endif #endif /* EGL_KHR_display_reference */ #ifndef EGL_NV_stream_origin #define EGL_NV_stream_origin 1 #define EGL_STREAM_FRAME_ORIGIN_X_NV 0x3366 #define EGL_STREAM_FRAME_ORIGIN_Y_NV 0x3367 #define EGL_STREAM_FRAME_MAJOR_AXIS_NV 0x3368 #define EGL_CONSUMER_AUTO_ORIENTATION_NV 0x3369 #define EGL_PRODUCER_AUTO_ORIENTATION_NV 0x336A #define EGL_LEFT_NV 0x336B #define EGL_RIGHT_NV 0x336C #define EGL_TOP_NV 0x336D #define EGL_BOTTOM_NV 0x336E #define EGL_X_AXIS_NV 0x336F #define EGL_Y_AXIS_NV 0x3370 #endif /* EGL_NV_stream_origin */ #endif egl-wayland-1.1.18/wayland-eglstream-protocols.pc.in000066400000000000000000000003111474153032600224010ustar00rootroot00000000000000prefix=@prefix@ datarootdir=@datarootdir@ pkgdatadir=@datadir@/@PACKAGE@ Name: wayland-eglstream-protocols Description: Nvidia Wayland EGLStream XML protocol files Version: @WAYLAND_EXTERNAL_VERSION@ egl-wayland-1.1.18/wayland-eglstream.pc.in000066400000000000000000000006171474153032600203700ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: wayland-eglstream Description: Nvidia Wayland EGLStream compositor helper libraries Version: @WAYLAND_EXTERNAL_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lnvidia-egl-wayland Requires: eglexternalplatform >= @EGL_EXTERNAL_PLATFORM_MIN_VERSION@ eglexternalplatform < @EGL_EXTERNAL_PLATFORM_MAX_VERSION@ egl-wayland-1.1.18/wayland-eglstream/000077500000000000000000000000001474153032600174335ustar00rootroot00000000000000egl-wayland-1.1.18/wayland-eglstream/.gitignore000066400000000000000000000001141474153032600214170ustar00rootroot00000000000000# All source files in this directory are autogenerated, ignore them *.c *.h egl-wayland-1.1.18/wayland-eglstream/meson.build000066400000000000000000000024311474153032600215750ustar00rootroot00000000000000generated_private_protocols = [ 'wayland-eglstream', ] generated_public_protocols = [ 'wayland-eglstream-controller', ] foreach proto : generated_private_protocols + generated_public_protocols foreach output_type: ['client-header', 'server-header', 'private-code'] if output_type == 'client-header' output_file = '@0@-client-protocol.h'.format(proto) elif output_type == 'server-header' output_file = '@0@-server-protocol.h'.format(proto) else output_file = '@0@-protocol.c'.format(proto) if wl_scanner.version().version_compare('< 1.14.91') output_type = 'code' elif generated_public_protocols.contains(proto) output_type = 'public-code' endif endif var_name = output_file.underscorify() target = custom_target( '@0@'.format(output_file), command: [prog_scanner, output_type, '@INPUT@', '@OUTPUT@'], input: '@0@.xml'.format(proto), output: output_file, ) set_variable(var_name, target) endforeach endforeach install_data( 'wayland-eglstream.xml', 'wayland-eglstream-controller.xml', install_dir : join_paths(get_option('datadir'), meson.project_name()) ) egl-wayland-1.1.18/wayland-eglstream/wayland-eglstream-controller.xml000066400000000000000000000110361474153032600257570ustar00rootroot00000000000000 Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved. 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 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. - dont_care: Using this enum will tell the server to make its own decisions regarding present mode. - fifo: Tells the server to use a fifo present mode. The decision to use fifo synchronous is left up to the server. - mailbox: Tells the server to use a mailbox present mode. - present_mode: Must be one of wl_eglstream_controller_present_mode. Tells the server the desired present mode that should be used. - fifo_length: Only valid when the present_mode attrib is provided and its value is specified as fifo. Tells the server the desired fifo length to be used when the desired present_mode is fifo. Creates the corresponding server side EGLStream from the given wl_buffer and attaches a consumer to it. Creates the corresponding server side EGLStream from the given wl_buffer and attaches a consumer to it using the given attributes. It contains key-value pairs compatible with intptr_t type. A key must be one of wl_eglstream_controller_attrib enumeration values. What a value represents is attribute-specific. egl-wayland-1.1.18/wayland-eglstream/wayland-eglstream.xml000066400000000000000000000213711474153032600236010ustar00rootroot00000000000000 Copyright (c) 2014-2019, NVIDIA CORPORATION. All rights reserved. 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 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. - fd: The given handle represents a file descriptor, and the EGLStream connection must be done as described in EGL_KHR_stream_cross_process_fd - inet: The EGLStream connection must be done using an inet address and port as described in EGL_NV_stream_socket. The given handle can be ignored, but both inet address and port must be given as attributes. - socket: The given handle represents a unix socket, and the EGLStream connection must be done as described in EGL_NV_stream_socket. - inet_addr: The given attribute encodes an IPv4 address of a client socket. Both IPv4 address and port must be set at the same time. - inet_port: The given attribute encodes a port of a client socket. Both IPv4 address and port must be set at the same time. - y_inverted: The given attribute encodes the default value for a stream's image inversion relative to wayland protocol convention. Vulkan apps will be set to 'true', while OpenGL apps will be set to 'false'. NOTE: EGL_NV_stream_origin is the authorative source of truth regarding a stream's frame orientation and should be queried for an accurate value. The given attribute is a 'best guess' fallback mechanism which should only be used when a query to EGL_NV_stream_origin fails. This enum values should be used as bit masks. - stream_fd: The server supports EGLStream connections as described in EGL_KHR_stream_cross_process_fd - stream_inet: The server supports EGLStream inet connections as described in EGL_NV_stream_socket. - stream_socket: The server supports EGLStream unix socket connections as described in EGL_NV_stream_socket. The capabilities event is sent out at wl_eglstream_display binding time. It allows the server to advertise what features it supports so clients may know what is safe to be used. The swapinterval_override event is sent out whenever a client requests a swapinterval setting through swap_interval() and there is an override in place that will make such request to be ignored. The swapinterval_override event will provide the override value so that the client is made aware of it. Create a wl_buffer corresponding to given handle. The attributes list may be used to define additional EGLStream connection data (e.g inet address/port). The server can create its EGLStream handle using the information encoded in the wl_buffer. It contains key-value pairs compatible with intptr_t type. A key must be one of wl_eglstream_display_attrib enumeration values. What a value represents is attribute-specific. Set the swap interval for the consumer of the given EGLStream. The swap interval is silently clamped to the valid range on the server side.