diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1608f58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,81 @@ +# 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 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..252057b --- /dev/null +++ b/COPYING @@ -0,0 +1,19 @@ + 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. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..eca27af --- /dev/null +++ b/Makefile.am @@ -0,0 +1,97 @@ + +# 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 + +# Required library flags +libnvidia_egl_wayland_la_CFLAGS += \ + $(PTHREAD_CFLAGS) \ + $(EGL_EXTERNAL_PLATFORM_CFLAGS) \ + $(WAYLAND_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) \ + -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-external-exports.c + +libnvidia_egl_wayland_la_built_public_protocols = \ + wayland-eglstream/wayland-eglstream-controller-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 + +libnvidia_egl_wayland_la_built_server_headers = \ + wayland-eglstream/wayland-eglstream-server-protocol.h + +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) + +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_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_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 < $< > $@ diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c8987f --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +Wayland 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. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..b08bc83 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. +( + cd "$srcdir" && + autoreconf --force -v --install +) || exit +test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..e7d1661 --- /dev/null +++ b/configure.ac @@ -0,0 +1,106 @@ +AC_PREREQ([2.64]) + +m4_define([wayland_eglstream_major_version], [1]) +m4_define([wayland_eglstream_minor_version], [1]) +m4_define([wayland_eglstream_micro_version], [5]) +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_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"`]) + +# 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-external")]) +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]) + +# 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} + ]) diff --git a/include/wayland-egldevice.h b/include/wayland-egldevice.h new file mode 100644 index 0000000..2b436aa --- /dev/null +++ b/include/wayland-egldevice.h @@ -0,0 +1,85 @@ +/* + * 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" + +#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; + + 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; + } 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 diff --git a/include/wayland-egldisplay.h b/include/wayland-egldisplay.h new file mode 100644 index 0000000..ac42e7b --- /dev/null +++ b/include/wayland-egldisplay.h @@ -0,0 +1,127 @@ +/* + * 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 "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 + +typedef struct WlEglDisplayRec { + WlEglDeviceDpy *devDpy; + + EGLBoolean ownNativeDpy; + struct wl_display *nativeDpy; + + struct wl_registry *wlRegistry; + struct wl_eglstream_display *wlStreamDpy; + struct wl_eglstream_controller *wlStreamCtl; + unsigned int wlStreamCtlVer; + struct wl_event_queue *wlEventQueue; + struct { + unsigned int stream_fd : 1; + unsigned int stream_inet : 1; + unsigned int stream_socket : 1; + } caps; + + WlEglPlatformData *data; + + EGLBoolean useRefCount; + + /** + * The number of times that eglTerminate has to be called before the + * display is termianted. + * + * If \c useRefCount is true, then this is incremented each time + * eglInitialize is called, and decremented each time eglTerminate is + * called. + * + * If \c useRefCount is false, then this value is capped at 1. + * + * In all cases, the display is initialized if (initCount > 0). + */ + unsigned int initCount; + + struct wl_list link; +} 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; + +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); + +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 diff --git a/include/wayland-eglhandle.h b/include/wayland-eglhandle.h new file mode 100644 index 0000000..2697206 --- /dev/null +++ b/include/wayland-eglhandle.h @@ -0,0 +1,158 @@ +/* + * 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; + + 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; + + PFNEGLQUERYDISPLAYATTRIBKHRPROC queryDisplayAttrib; + + /* Used for fifo_synchronous support */ + PFNEGLQUERYSTREAMKHRPROC queryStream; + PFNEGLQUERYSTREAMU64KHRPROC queryStreamu64; + PFNEGLCREATESTREAMSYNCNVPROC createStreamSync; + PFNEGLCLIENTWAITSYNCKHRPROC clientWaitSync; + PFNEGLSIGNALSYNCKHRPROC signalSync; + PFNEGLDESTROYSYNCKHRPROC destroySync; + PFNEGLSTREAMFLUSHNVPROC streamFlush; + } 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 diff --git a/include/wayland-eglstream-server.h b/include/wayland-eglstream-server.h new file mode 100644 index 0000000..27ab863 --- /dev/null +++ b/include/wayland-eglstream-server.h @@ -0,0 +1,134 @@ +/* + * 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); + +/* + * 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; + + 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 diff --git a/include/wayland-eglstream.h b/include/wayland-eglstream.h new file mode 100644 index 0000000..e8486be --- /dev/null +++ b/include/wayland-eglstream.h @@ -0,0 +1,46 @@ +/* + * 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 diff --git a/include/wayland-eglsurface.h b/include/wayland-eglsurface.h new file mode 100644 index 0000000..38fcd85 --- /dev/null +++ b/include/wayland-eglsurface.h @@ -0,0 +1,142 @@ +/* + * 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_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 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; + + struct wl_list link; +} WlEglSurfaceCtx; + +typedef 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; + + struct wl_callback *throttleCallback; + struct wl_event_queue *wlEventQueue; + + 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; +} WlEglSurface; + +extern struct wl_list wlEglSurfaceList; + +WL_EXPORT +EGLBoolean wlEglInitializeSurfaceExport(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 wlEglIsWlEglSurface(WlEglSurface *wlEglSurface); + +EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy, + void *nativeResource, + EGLint attribute, + int *value); + +EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface, + struct wl_event_queue *queue); + +void wlEglCreateFrameSync(WlEglSurface *surface); +EGLint wlEglWaitFrameSync(WlEglSurface *surface); + +EGLBoolean wlEglSurfaceRef(WlEglSurface *surface); +void wlEglSurfaceUnref(WlEglSurface *surface); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/wayland-eglswap.h b/include/wayland-eglswap.h new file mode 100644 index 0000000..594f3ce --- /dev/null +++ b/include/wayland-eglswap.h @@ -0,0 +1,56 @@ +/* + * 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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/wayland-eglutils.h b/include/wayland-eglutils.h new file mode 100644 index 0000000..ae54fcb --- /dev/null +++ b/include/wayland-eglutils.h @@ -0,0 +1,71 @@ +/* + * 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_INIT +#define WL_LIST_INIT(head) { .prev = (head), .next = (head) } +#endif + +#if defined(__QNX__) +#define HAS_MINCORE 0 +#else +#define HAS_MINCORE 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +EGLBoolean wlEglFindExtension(const char *extension, const char *extensions); +#if HAS_MINCORE +EGLBoolean wlEglPointerIsDereferencable(void *p); +EGLBoolean wlEglCheckInterfaceType(struct wl_object *obj, const char *ifname); +#ifndef WL_CHECK_INTERFACE_TYPE +#define WL_CHECK_INTERFACE_TYPE(obj, ifname) \ + (wlEglCheckInterfaceType((struct wl_object *)(obj), #ifname) || \ + *(void **)(obj) == &ifname) +#endif +#endif +void wlEglSetErrorCallback(WlEglPlatformData *data, + EGLint err, + const char *file, + int line); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/wayland-external-exports.h b/include/wayland-external-exports.h new file mode 100644 index 0000000..a15c25b --- /dev/null +++ b/include/wayland-external-exports.h @@ -0,0 +1,70 @@ +/* + * 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 5 + + +#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 diff --git a/include/wayland-thread.h b/include/wayland-thread.h new file mode 100644 index 0000000..32769af --- /dev/null +++ b/include/wayland-thread.h @@ -0,0 +1,80 @@ +/* + * 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 diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..bd753b3 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# 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 diff --git a/m4/ax_check_enable_debug.m4 b/m4/ax_check_enable_debug.m4 new file mode 100644 index 0000000..f99d75f --- /dev/null +++ b/m4/ax_check_enable_debug.m4 @@ -0,0 +1,124 @@ +# =========================================================================== +# 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 +]) diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 0000000..e2d0d36 --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# 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 diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 new file mode 100644 index 0000000..6d400ed --- /dev/null +++ b/m4/ax_pthread.m4 @@ -0,0 +1,317 @@ +# =========================================================================== +# 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 diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..03abe73 --- /dev/null +++ b/meson.build @@ -0,0 +1,64 @@ +project('wayland-eglstream', 'c', + version : '1.1.5', + 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] + +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) + +generated_pc = [ + 'wayland-eglstream', + 'wayland-eglstream-protocols' +] + +foreach pc : generated_pc + configure_file( + input : '@0@.pc.in'.format(pc), + output : '@BASENAME@', + configuration : pkgconf, + install : true, + install_dir : join_paths(get_option('datadir'), 'pkgconfig') + ) +endforeach + +subdir('wayland-eglstream') +subdir('src') diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..0d77292 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,49 @@ +if 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_link_arguments('-Wl,-Bsymbolic', language : 'c') + +if cc.has_argument('-Wpedantic') + add_project_arguments('-Wno-pedantic', language : 'c') +endif + +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_eglstream_protocol_c, + wayland_eglstream_client_protocol_h, + wayland_eglstream_server_protocol_h, + wayland_eglstream_controller_protocol_c, + wayland_eglstream_controller_client_protocol_h, +] + +egl_wayland = library('nvidia-egl-wayland', + src, + dependencies : [ + eglexternalplatform, + wayland_server, + wayland_client, + wayland_egl_backend, + threads, + libdl, + ], + include_directories : inc, + version : meson.project_version(), + install : true, +) diff --git a/src/wayland-egldevice.c b/src/wayland-egldevice.c new file mode 100644 index 0000000..b24e7f3 --- /dev/null +++ b/src/wayland-egldevice.c @@ -0,0 +1,137 @@ +/* + * 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 "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; + + // 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) { + free(devDpy); + return NULL; + } + + wl_list_insert(&data->deviceDpyList, &devDpy->link); + return devDpy; +} + +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); + +#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; +} diff --git a/src/wayland-egldisplay.c b/src/wayland-egldisplay.c new file mode 100644 index 0000000..4a4f332 --- /dev/null +++ b/src/wayland-egldisplay.c @@ -0,0 +1,773 @@ +/* + * 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. + */ + +#include "wayland-egldisplay.h" +#include "wayland-eglstream-client-protocol.h" +#include "wayland-eglstream-controller-client-protocol.h" +#include "wayland-eglstream-server.h" +#include "wayland-thread.h" +#include "wayland-eglsurface.h" +#include "wayland-eglhandle.h" +#include "wayland-eglutils.h" +#include +#include +#include + +/* TODO: Make global display lists hang off platform data */ +static struct wl_list wlEglDisplayList = WL_LIST_INIT(&wlEglDisplayList); + +EGLBoolean wlEglIsWaylandDisplay(void *nativeDpy) +{ +#if HAS_MINCORE + if (!wlEglPointerIsDereferencable(nativeDpy)) { + return EGL_FALSE; + } + + return WL_CHECK_INTERFACE_TYPE(nativeDpy, wl_display_interface); +#else + (void)nativeDpy; + + /* we return EGL_TRUE in order to always assume a valid wayland + * display is given so that we bypass all the checks that would + * prevent any of the functions in this library to work + * otherwise. + */ + return EGL_TRUE; +#endif +} + +EGLBoolean wlEglIsValidNativeDisplayExport(void *data, void *nativeDpy) +{ + EGLBoolean checkDpy = EGL_TRUE; + char *val = getenv("EGL_PLATFORM"); + (void)data; + + if (val && !strcasecmp(val, "wayland")) { + return EGL_TRUE; + } + +#if !HAS_MINCORE + /* wlEglIsWaylandDisplay always returns true if mincore(2) + * is not available, hence we cannot ascertain whether the + * the nativeDpy is wayland. + * Note: this effectively forces applications to use + * eglGetPlatformDisplay() instead of eglGetDisplay(). + */ + checkDpy = EGL_FALSE; +#endif + + return (checkDpy ? wlEglIsWaylandDisplay(nativeDpy) : EGL_FALSE); +} + +EGLBoolean wlEglBindDisplaysHook(void *data, EGLDisplay dpy, void *nativeDpy) +{ + EGLBoolean res = EGL_FALSE; + + wlExternalApiLock(); + + res = wl_eglstream_display_bind((WlEglPlatformData *)data, + (struct wl_display *)nativeDpy, + dpy); + + 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 +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, + version); + } + if (strcmp(interface, "wl_eglstream_controller") == 0) { + display->wlStreamCtl = wl_registry_bind(registry, + name, + &wl_eglstream_controller_interface, + version); + display->wlStreamCtlVer = version; + } +} + +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 +registry_handle_global_check_eglstream( + void *data, + struct wl_registry *registry, + uint32_t name, + const char *interface, + uint32_t version) +{ + EGLBoolean *hasEglStream = (EGLBoolean *)data; + (void) registry; + (void) name; + (void) interface; + (void) version; + + if (strcmp(interface, "wl_eglstream_display") == 0) { + *hasEglStream = EGL_TRUE; + } +} + +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) +{ + WlEglSurface *surf = NULL; + (void) data; + (void) wlStreamDpy; + + wl_list_for_each(surf, &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(EGLDisplay dpy, EGLBoolean globalTeardown) +{ + WlEglDisplay *display = (WlEglDisplay *)dpy; + + 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) { + 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->wlEventQueue) { + wl_event_queue_destroy(display->wlEventQueue); + display->wlEventQueue = NULL; + } + } + + return EGL_TRUE; +} + +EGLBoolean wlEglTerminateHook(EGLDisplay dpy) +{ + EGLBoolean res; + + wlExternalApiLock(); + res = terminateDisplay(dpy, EGL_FALSE); + wlExternalApiUnlock(); + + return res; +} + +static EGLBoolean serverSupportsEglStream(struct wl_display *nativeDpy) +{ + struct wl_display *wrapper = NULL; + struct wl_registry *wlRegistry = NULL; + struct wl_event_queue *queue = wl_display_create_queue(nativeDpy); + int ret = 0; + EGLBoolean hasEglStream = EGL_FALSE; + const struct wl_registry_listener registryListener = { + registry_handle_global_check_eglstream, + registry_handle_global_remove + }; + + if (queue == NULL) { + return EGL_FALSE; + } + + wrapper = wl_proxy_create_wrapper(nativeDpy); + 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); + wl_proxy_wrapper_destroy(wrapper); /* Done with wrapper */ + ret = wl_registry_add_listener(wlRegistry, + ®istryListener, + &hasEglStream); + if (ret == 0) { + wl_display_roundtrip_queue(nativeDpy, queue); + } + + if (queue) { + wl_event_queue_destroy(queue); + } + if (wlRegistry) { + wl_registry_destroy(wlRegistry); + } + + return hasEglStream; +} + +EGLDisplay wlEglGetPlatformDisplayExport(void *data, + EGLenum platform, + void *nativeDpy, + const EGLAttrib *attribs) +{ + WlEglPlatformData *pData = (WlEglPlatformData *)data; + WlEglDisplay *display = NULL; + EGLint numDevices = 0; + int i = 0; + EGLDeviceEXT eglDevice = NULL; + EGLint err = EGL_SUCCESS; + EGLBoolean useRefCount = EGL_FALSE; + + 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) { + useRefCount = (EGLBoolean) attribs[i + 1]; + } else { + wlEglSetError(data, EGL_BAD_ATTRIBUTE); + 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->useRefCount == useRefCount) { + wlExternalApiUnlock(); + return (EGLDisplay)display; + } + } + + display = calloc(1, sizeof(*display)); + if (!display) { + wlExternalApiUnlock(); + err = EGL_BAD_ALLOC; + goto fail; + } + + display->data = pData; + + display->nativeDpy = nativeDpy; + display->useRefCount = useRefCount; + + /* 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) { + wlExternalApiUnlock(); + err = EGL_BAD_ALLOC; + goto fail; + } + + display->ownNativeDpy = EGL_TRUE; + wl_display_dispatch_pending(display->nativeDpy); + } + + if (!serverSupportsEglStream(display->nativeDpy)) { + wlExternalApiUnlock(); + goto fail; + } + + if (!pData->egl.queryDevices(1, &eglDevice, &numDevices) || numDevices == 0) { + wlExternalApiUnlock(); + goto fail; + } + display->devDpy = wlGetInternalDisplay(pData, eglDevice); + if (display->devDpy == NULL) { + wlExternalApiUnlock(); + goto fail; + } + + // The newly created WlEglDisplay has been set up properly, insert it + // in wlEglDisplayList. + wl_list_insert(&wlEglDisplayList, &display->link); + + wlExternalApiUnlock(); + return display; + +fail: + + if (display->ownNativeDpy) { + wl_display_disconnect(display->nativeDpy); + } + free(display); + + if (err != EGL_SUCCESS) { + wlEglSetError(data, err); + } + + return EGL_NO_DISPLAY; +} + +EGLBoolean wlEglInitializeHook(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + WlEglDisplay *display = (WlEglDisplay *)dpy; + WlEglPlatformData *data = display->data; + struct wl_display *wrapper = NULL; + EGLint err = EGL_SUCCESS; + int ret = 0; + + wlExternalApiLock(); + + if (display->initCount > 0) { + // This display has already been initialized. + if (major) { + *major = display->devDpy->major; + } + if (minor) { + *minor = display->devDpy->minor; + } + if (display->useRefCount) { + display->initCount++; + } + wlExternalApiUnlock(); + return EGL_TRUE; + } + + if (!wlInternalInitialize(display->devDpy)) { + wlExternalApiUnlock(); + return EGL_FALSE; + } + + // 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 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 || !display->wlStreamDpy) { + err = EGL_BAD_ALLOC; + goto fail; + } + + /* Listen to wl_eglstream_display events and make another roundtrip so we + * catch any bind-related event (e.g. server capabilities) + */ + ret = wl_eglstream_display_add_listener(display->wlStreamDpy, + &eglstream_display_listener, + display); + if (ret == 0) { + ret = wl_display_roundtrip_queue(display->nativeDpy, display->wlEventQueue); + } + if (ret < 0) { + err = EGL_BAD_ALLOC; + goto fail; + } + + if (major != NULL) { + *major = display->devDpy->major; + } + if (minor != NULL) { + *minor = display->devDpy->minor; + } + + wlExternalApiUnlock(); + return EGL_TRUE; + +fail: + terminateDisplay(display, EGL_FALSE); + if (err != EGL_SUCCESS) { + wlEglSetError(data, err); + } + wlExternalApiUnlock(); + 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; +} + +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 = (WlEglDisplay *) dpy; + WlEglPlatformData *data = display->data; + EGLBoolean ret = EGL_TRUE; + + if (value == NULL) { + wlEglSetError(data, EGL_BAD_PARAMETER); + return EGL_FALSE; + } + + wlExternalApiLock(); + + if (display->initCount == 0) { + wlEglSetError(data, EGL_NOT_INITIALIZED); + wlExternalApiUnlock(); + return EGL_FALSE; + } + + switch (name) { + case EGL_DEVICE_EXT: + *value = (EGLAttrib) display->devDpy->eglDevice; + break; + case EGL_TRACK_REFERENCES_KHR: + *value = (EGLAttrib) display->useRefCount; + break; + default: + ret = data->egl.queryDisplayAttrib(display->devDpy->eglDisplay, name, value); + break; + } + + wlExternalApiUnlock(); + 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) { + res = terminateDisplay((EGLDisplay)display, EGL_TRUE) && res; + if (display->ownNativeDpy) { + wl_display_disconnect(display->nativeDpy); + } + wl_list_remove(&display->link); + /* Destroy the external display */ + free(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_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_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 + */ + const char *exts = pData->egl.queryString(dpy, EGL_EXTENSIONS); + + if (wlEglFindExtension("EGL_KHR_stream", exts) && + wlEglFindExtension("EGL_KHR_stream_producer_eglsurface", exts) && + wlEglFindExtension("EGL_KHR_stream_cross_process_fd", exts)) { + res = "EGL_WL_bind_wayland_display " + "EGL_WL_wayland_eglstream"; + } + } + break; + + default: + break; + } + + return res; +} diff --git a/src/wayland-eglhandle.c b/src/wayland-eglhandle.c new file mode 100644 index 0000000..c97724b --- /dev/null +++ b/src/wayland-eglhandle.c @@ -0,0 +1,164 @@ +/* + * 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 "wayland-eglhandle.h" +#include "wayland-egldisplay.h" +#include "wayland-eglsurface.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(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); + +#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); + + /* Stream flush */ + GET_PROC(streamFlush, eglStreamFlushNV); + + GET_PROC(queryDisplayAttrib, eglQueryDisplayAttribKHR); + +#undef GET_PROC + + /* Check for required EGL client extensions */ + exts = res->egl.queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (exts == NULL) { + goto fail; + } + 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) +{ + wlExternalApiLock(); + + if ((type == EGL_OBJECT_DISPLAY_KHR) && + wlEglIsWlEglDisplay((WlEglDisplay *)handle)) { + handle = (void *)(((WlEglDisplay *)handle)->devDpy->eglDisplay); + } else if ((type == EGL_OBJECT_SURFACE_KHR) && + wlEglIsWlEglSurface((WlEglSurface *)handle)) { + WlEglSurface *surface = (WlEglSurface *)handle; + if (dpy == surface->wlEglDpy) { + handle = (void *)(surface->ctx.eglSurface); + } else { + handle = NULL; + } + } + + wlExternalApiUnlock(); + + return handle; +} diff --git a/src/wayland-eglstream-server.c b/src/wayland-eglstream-server.c new file mode 100644 index 0000000..c37f8e6 --- /dev/null +++ b/src/wayland-eglstream-server.c @@ -0,0 +1,398 @@ +/* + * 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-eglswap.h" +#include "wayland-eglutils.h" + +#define MASK(_VAL_) (1 << (_VAL_)) + +static struct wl_list wlStreamDpyList = WL_LIST_INIT(&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) +{ + struct wl_eglstream_display *wlStreamDpy = NULL; + const char *exts = 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; + + exts = data->egl.queryString(eglDisplay, EGL_EXTENSIONS); + +#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); + + wl_list_insert(&wlStreamDpyList, &wlStreamDpy->link); + + return EGL_TRUE; +} + +void +wl_eglstream_display_unbind(struct wl_eglstream_display *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; + } +} + diff --git a/src/wayland-eglstream.c b/src/wayland-eglstream.c new file mode 100644 index 0000000..fed6bd3 --- /dev/null +++ b/src/wayland-eglstream.c @@ -0,0 +1,248 @@ +/* + * 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); + } +#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; +} + diff --git a/src/wayland-eglsurface.c b/src/wayland-eglsurface.c new file mode 100644 index 0000000..156cb55 --- /dev/null +++ b/src/wayland-eglsurface.c @@ -0,0 +1,1544 @@ +/* + * 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-eglsurface.h" +#include "wayland-eglstream-client-protocol.h" +#include "wayland-eglstream-controller-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 + +#define WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE 3 + +struct wl_list wlEglSurfaceList = WL_LIST_INIT(&wlEglSurfaceList); + +EGLBoolean wlEglIsWlEglSurface(WlEglSurface *surface) +{ + WlEglSurface *surf; + + wl_list_for_each(surf, &wlEglSurfaceList, link) { + if (surf == surface) { + return EGL_TRUE; + } + } + + return EGL_FALSE; +} + +EGLBoolean wlEglIsWaylandWindowValid(struct wl_egl_window *window) +{ + struct wl_surface *surface = NULL; + +#if HAS_MINCORE + if (!window || !wlEglPointerIsDereferencable(window)) { + return EGL_FALSE; + } + + surface = (struct wl_surface *)window->version; + if (!wlEglPointerIsDereferencable(surface)) { + surface = window->surface; + if (!wlEglPointerIsDereferencable(surface)) { + return EGL_FALSE; + } + } + return WL_CHECK_INTERFACE_TYPE(surface, wl_surface_interface); +#else + /* + * Note that dereferencing an invalid surface pointer could mean an old + * version of libwayland-egl.so is loaded, which may not support version + * member in wl_egl_window struct. + */ + surface = window->surface; + + /* wl_surface is a wl_proxy, which is a wl_object. wl_objects's first + * element points to the interface type */ + return (((*(void **)surface)) == &wl_surface_interface); +#endif +} + +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; + } + + /* After a window resize, the compositor has to be + * updated with the new buffer's geometry. However, we + * won't have updated geometry information until the + * underlying buffer is attached. A surface attach + * may be deferred to a later time in some situations + * (e.g. FIFO_SYNCHRONOUS + damage thread). + * + * Therefore, the surface_commit done from here would + * use outdated geometry information if the buffer is + * not attached, which would make xdg-shell fail with + * error. To avoid this, skip the surface commit here + * if the surface attach is not yet done. */ + if (surface->ctx.isAttached) { + wl_surface_commit(surface->wlSurface); + } + } +} + +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; +} + +EGLBoolean +wlEglSendDamageEvent(WlEglSurface *surface, struct wl_event_queue *queue) +{ + /* 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); + wl_surface_damage(surface->wlSurface, 0, 0, + surface->width, surface->height); + wl_surface_commit(surface->wlSurface); + surface->ctx.isAttached = EGL_TRUE; + + return (wl_display_roundtrip_queue(surface->wlEglDpy->nativeDpy, + 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) { + + // 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); + } + ok = wlEglSendDamageEvent(surface, queue); + surface->ctx.framesProcessed++; + } + + // Otherwise, wait for sync to trigger + else { + 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 +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) { + 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) { + 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 (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 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. Cross-process unix sockets + * 2. Cross-process FD + * 3. Cross-process inet sockets + */ +#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; + } + + /* Then attach the wl_eglstream so the compositor connects a consumer to the + * EGLStream */ + 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 = + 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); + return err; +} + +EGLBoolean wlEglInitializeSurfaceExport(WlEglSurface *surface) +{ + WlEglDisplay *display = surface->wlEglDpy; + + wlExternalApiLock(); + // Create per surface wayland queue + surface->wlEventQueue = wl_display_create_queue(display->nativeDpy); + surface->refCount = 1; + + if (!wlEglInitializeMutex(&surface->mutexLock)) { + wlExternalApiUnlock(); + return EGL_FALSE; + } + + if (create_surface_context(surface) != EGL_SUCCESS) { + wl_event_queue_destroy(surface->wlEventQueue); + wlExternalApiUnlock(); + return EGL_FALSE; + } + + wl_list_insert(&wlEglSurfaceList, &surface->link); + wl_list_init(&surface->oldCtxList); + + /* Set client's pendingSwapIntervalUpdate for updating client's + * swapinterval + */ + surface->pendingSwapIntervalUpdate = EGL_TRUE; + + wlExternalApiUnlock(); + return EGL_TRUE; +} + +static void +resize_callback(struct wl_egl_window *window, void *data) +{ + WlEglDisplay *display = NULL; + WlEglPlatformData *pData = NULL; + WlEglSurface *surface = (WlEglSurface *)data; + EGLint err = EGL_SUCCESS; + + 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 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->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; + + err = create_surface_context(surface); + if (err == EGL_SUCCESS) { + pData->egl.makeCurrent(display, + surface, + surface, + pData->egl.getCurrentContext()); + + /* Set client's pendingSwapIntervalUpdate for updating client's + * swapinterval + */ + surface->pendingSwapIntervalUpdate = 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; + + /* 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_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 wlEglSurfaceRef(WlEglSurface *surface) +{ + + if (!wlEglIsWlEglSurface(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); + free(surface); + + return; +} + +static EGLBoolean wlEglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) +{ + WlEglDisplay *display = (WlEglDisplay*)dpy; + WlEglSurface *surface = (WlEglSurface*)eglSurface; + WlEglSurfaceCtx *ctx = NULL; + WlEglSurfaceCtx *next = NULL; + + if (!wlEglIsWlEglSurface(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) { + // 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->isSurfaceProducer))) { + + if (surface->wlEventQueue != NULL) { + wl_surface_attach(surface->wlSurface, NULL, 0, 0); + wl_surface_commit(surface->wlSurface); + + wl_display_roundtrip_queue(display->nativeDpy, + surface->wlEventQueue); + } + + if (surface->isSurfaceProducer) { + 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; + } + } + } + } + + if (surface->throttleCallback != NULL) { + wl_callback_destroy(surface->throttleCallback); + surface->throttleCallback = NULL; + } + if (surface->wlEventQueue != NULL) { + wl_event_queue_destroy(surface->wlEventQueue); + surface->wlEventQueue = NULL; + } + + if (!surface->ctx.isOffscreen) { + 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); + } + + // Release WlEglSurface lock. + pthread_mutex_unlock(&surface->mutexLock); + + wlEglSurfaceUnref(eglSurface); + + return EGL_TRUE; +} + +static void +destroy_callback(void *data) +{ + WlEglSurface *surface = (WlEglSurface*)data; + + wlExternalApiLock(); + + if (!surface || surface->wlEglDpy->initCount == 0) { + wlExternalApiUnlock(); + return; + } + + wlEglDestroySurface((EGLDisplay)surface->wlEglDpy, + (EGLSurface)surface); + wlExternalApiUnlock(); +} + +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 + * Note that this will be disabled if platfrom does not have + * mincore(2) to check whether a pointer is valid or not. + */ + + *version = window->version; + *surface = window->surface; + +#if HAS_MINCORE + if (wlEglPointerIsDereferencable((void *)(window->version))) { + *version = 0; + *surface = (struct wl_surface *)(window->version); + } +#endif + +} + +EGLSurface wlEglCreatePlatformWindowSurfaceHook(EGLDisplay dpy, + EGLConfig config, + void *nativeWin, + const EGLAttrib *attribs) +{ + WlEglDisplay *display = (WlEglDisplay*)dpy; + WlEglPlatformData *data = display->data; + WlEglSurface *surface = NULL; + struct wl_egl_window *window = (struct wl_egl_window *)nativeWin; + EGLBoolean res = EGL_FALSE; + EGLint err = EGL_SUCCESS; + EGLint surfType; + + wlExternalApiLock(); + + 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; + } + + 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 (!wlEglInitializeMutex(&surface->mutexLock)) { + err = EGL_BAD_ALLOC; + goto fail; + } + + surface->wlEglDpy = display; + surface->eglConfig = config; + 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; + surface->refCount = 1; + surface->isDestroyed = EGL_FALSE; + // FIFO_LENGTH == 1 to set FIFO mode, FIFO_LENGTH == 0 to set MAILBOX mode + surface->fifoLength = (display->devDpy->exts.stream_fifo_synchronous && + display->devDpy->exts.stream_sync) ? 1 : 0; + + // Create per surface wayland queue + surface->wlEventQueue = wl_display_create_queue(display->nativeDpy); + + getWlEglWindowVersionAndSurface(window, + &surface->wlEglWinVer, + &surface->wlSurface); + wl_list_init(&surface->oldCtxList); + + err = assignWlEglSurfaceAttribs(surface, attribs); + if (err != EGL_SUCCESS) { + goto fail; + } + + err = create_surface_context(surface); + if (err != EGL_SUCCESS) { + goto fail; + } + + surface->swapInterval = 1; // Default swap interval is 1 + + /* 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(&wlEglSurfaceList, &surface->link); + wlExternalApiUnlock(); + + return surface; + +fail: + if (surface) { + wlEglDestroySurface(display, surface); + } + + wlExternalApiUnlock(); + + 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 = (WlEglDisplay*)dpy; + WlEglPlatformData *data = display->data; + WlEglSurface *surface = NULL; + EGLSurface surf = EGL_NO_SURFACE; + EGLint err = EGL_SUCCESS; + + /* 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 (!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); + + wlExternalApiLock(); + wl_list_insert(&wlEglSurfaceList, &surface->link); + wlExternalApiUnlock(); + + return surface; + +fail: + if (err != EGL_SUCCESS) { + wlEglSetError(data, err); + } + return EGL_NO_SURFACE; +} + +EGLSurface wlEglCreateStreamProducerSurfaceHook(EGLDisplay dpy, + EGLConfig config, + EGLStreamKHR stream, + const EGLint *attribs) +{ + WlEglDisplay *display = (WlEglDisplay*)dpy; + WlEglPlatformData *data = display->data; + WlEglSurface *surface = NULL; + EGLSurface surf = EGL_NO_SURFACE; + EGLint err = EGL_SUCCESS; + + /* 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); + + wlExternalApiLock(); + wl_list_insert(&wlEglSurfaceList, &surface->link); + wlExternalApiUnlock(); + + return surface; + +fail: + if (err != EGL_SUCCESS) { + wlEglSetError(data, err); + } + return EGL_NO_SURFACE; +} + +EGLBoolean wlEglDestroySurfaceHook(EGLDisplay dpy, EGLSurface eglSurface) +{ + WlEglDisplay *display = (WlEglDisplay*)dpy; + EGLint ret = EGL_FALSE; + + wlExternalApiLock(); + + 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: + wlExternalApiUnlock(); + return ret; +} + + +EGLBoolean wlEglDestroyAllSurfaces(WlEglDisplay *display) +{ + WlEglSurface *surface, *next; + EGLBoolean res = EGL_TRUE; + + wl_list_for_each_safe(surface, next, &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; +} diff --git a/src/wayland-eglswap.c b/src/wayland-eglswap.c new file mode 100644 index 0000000..feab63d --- /dev/null +++ b/src/wayland-eglswap.c @@ -0,0 +1,272 @@ +/* + * 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-eglswap.h" +#include "wayland-eglstream-client-protocol.h" +#include "wayland-thread.h" +#include "wayland-egldisplay.h" +#include "wayland-eglsurface.h" +#include "wayland-eglhandle.h" +#include "wayland-eglutils.h" +#include +#include + +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 = (WlEglDisplay *)eglDisplay; + WlEglPlatformData *data = display->data; + WlEglSurface *surface = NULL; + EGLStreamKHR eglStream = EGL_NO_STREAM_KHR; + EGLBoolean isOffscreen = EGL_FALSE; + EGLBoolean res; + EGLint err; + + if (display->initCount == 0) { + err = EGL_NOT_INITIALIZED; + goto fail; + } + + //wlEglSurfaceRef() requires we acquire wlExternalApiLock. + wlExternalApiLock(); + if (!wlEglSurfaceRef(eglSurface)) { + wlExternalApiUnlock(); + 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 + */ + 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; + wlExternalApiUnlock(); + goto fail; + } + surface->pendingSwapIntervalUpdate = EGL_FALSE; + } + + wlExternalApiUnlock(); + + + // 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; + } + + 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 { + res = wlEglSendDamageEvent(surface, surface->wlEventQueue); + } + } + wlEglCreateFrameSync(surface); + +done: + // Release wlEglSurface lock. + pthread_mutex_unlock(&surface->mutexLock); + + //wlEglSurfaceUnRef() requires we acquire wlExternalApiLock. + wlExternalApiLock(); + wlEglSurfaceUnref(surface); + wlExternalApiUnlock(); + return res; + +fail_locked: + pthread_mutex_unlock(&surface->mutexLock); +fail: + if (surface != NULL) { + //wlEglSurfaceUnRef() requires we acquire wlExternalApiLock. + wlExternalApiLock(); + wlEglSurfaceUnref(surface); + wlExternalApiUnlock(); + } + wlEglSetError(data, err); + return EGL_FALSE; +} + +EGLBoolean wlEglSwapIntervalHook(EGLDisplay eglDisplay, EGLint interval) +{ + WlEglDisplay *display = (WlEglDisplay *)eglDisplay; + WlEglPlatformData *data = display->data; + WlEglSurface *surface = NULL; + EGLint state; + + /* Save the internal EGLDisplay handle, as it's needed by the actual + * eglSwapInterval() call */ + eglDisplay = display->devDpy->eglDisplay; + + if (!(data->egl.swapInterval(eglDisplay, interval))) { + return EGL_FALSE; + } + + surface = (WlEglSurface *)data->egl.getCurrentSurface(EGL_DRAW); + + wlExternalApiLock(); + + /* Check this is a valid wayland EGL surface (and stream) before sending the + * swap interval value to the consumer */ + if (!wlEglIsWlEglSurface(surface) || + (surface->swapInterval == interval) || + (surface->ctx.wlStreamResource == NULL) || + (surface->ctx.eglStream == EGL_NO_STREAM_KHR) || + (data->egl.queryStream(display->devDpy->eglDisplay, + surface->ctx.eglStream, + EGL_STREAM_STATE_KHR, + &state) == EGL_FALSE) || + (state == EGL_STREAM_STATE_DISCONNECTED_KHR)) + { + goto done; + } + + /* Cache interval value so we can reset it upon surface reattach */ + surface->swapInterval = interval; + + /* Set client's pendingSwapIntervalUpdate for updating client's + * swapinterval + */ + surface->pendingSwapIntervalUpdate = EGL_TRUE; + +done: + wlExternalApiUnlock(); + + return EGL_TRUE; +} + +EGLBoolean wlEglPrePresentExport(WlEglSurface *surface) { + + wlExternalApiLock(); + 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(surface->wlEglDpy->wlStreamDpy, + surface->ctx.wlStreamResource, + surface->swapInterval); + /* For receiving any event in case of override */ + if (wl_display_roundtrip_queue(surface->wlEglDpy->nativeDpy, + surface->wlEglDpy->wlEventQueue) < 0) { + wlExternalApiUnlock(); + return EGL_FALSE; + } + surface->pendingSwapIntervalUpdate = EGL_FALSE; + } + + wlExternalApiUnlock(); + + // Acquire wlEglSurface lock. + pthread_mutex_lock(&surface->mutexLock); + + wlEglWaitFrameSync(surface); + + // Release wlEglSurface lock. + pthread_mutex_unlock(&surface->mutexLock); + + return EGL_TRUE; +} + +EGLBoolean wlEglPostPresentExport(WlEglSurface *surface) { + WlEglDisplay *display = surface->wlEglDpy; + WlEglPlatformData *data = display->data; + EGLBoolean res = EGL_TRUE; + + // Acquire wlEglSurface lock. + pthread_mutex_lock(&surface->mutexLock); + + if (display->devDpy->exts.stream_flush) { + data->egl.streamFlush((EGLDisplay) display, surface->ctx.eglStream); + } + + + if (surface->ctx.useDamageThread) { + surface->ctx.framesProduced++; + } else { + res = wlEglSendDamageEvent(surface, surface->wlEventQueue); + } + + wlEglCreateFrameSync(surface); + + // Release wlEglSurface lock. + pthread_mutex_unlock(&surface->mutexLock); + + + 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; +} diff --git a/src/wayland-eglutils.c b/src/wayland-eglutils.c new file mode 100644 index 0000000..e9afff1 --- /dev/null +++ b/src/wayland-eglutils.c @@ -0,0 +1,160 @@ +/* + * 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 + +#if HAS_MINCORE +#include // Need dlsym() to load mincore() symbol dynamically. +#endif + +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; +} + +#if HAS_MINCORE +EGLBoolean wlEglPointerIsDereferencable(void *p) +{ + /* + * BSD and Solaris have slightly different prototypes for mincore, but + * they should be compatible with this. BSD uses: + * + * (const void *, size_t, char *) + * + * And Solaris uses: + * + * (caddr_t, size_t, char*) + * + * Which I believe are all ABI compatible with the Linux prototype used + * below for MINCOREPROC. + */ + typedef int (*MINCOREPROC)(void *, size_t, unsigned char *); + static MINCOREPROC pMinCore = NULL; + static EGLBoolean minCoreLoadAttempted = EGL_FALSE; + uintptr_t addr = (uintptr_t) p; + unsigned char unused; + const long page_size = getpagesize(); + + if (minCoreLoadAttempted == EGL_FALSE) { + minCoreLoadAttempted = EGL_TRUE; + + /* + * According to its manpage, mincore was introduced in Linux 2.3.99pre1 + * and glibc 2.2. The minimum glibc our driver supports is 2.0, so this + * mincore can not be linked in directly. It does however seem + * reasonable to assume that Wayland will not be run on glibc < 2.2. + * + * Attempt to load mincore from the currently available libraries. + * mincore comes from libc, which the EGL driver depends on, so it + * should always be loaded if our driver is running. + */ + pMinCore = (MINCOREPROC)dlsym(NULL, "mincore"); + } + + /* + * If the pointer can't be tested for safety, or is obviously unsafe, + * assume it can't be dereferenced. + */ + if (p == NULL || !pMinCore) { + return EGL_FALSE; + } + + /* align addr to page_size */ + addr &= ~(page_size - 1); + + /* + * mincore() returns 0 on success, and -1 on failure. The last parameter + * is a vector of bytes with one entry for each page queried. mincore + * returns page residency information in the first bit of each byte in the + * vector. + * + * Residency doesn't actually matter when determining whether a pointer is + * dereferenceable, so the output vector can be ignored. What matters is + * whether mincore succeeds. It will fail with ENOMEM if the range + * [addr, addr + length) is not mapped into the process, so all that needs + * to be checked there is whether the mincore call succeeds or not, as it + * can only succeed on dereferenceable memory ranges. + */ + return (pMinCore((void *) addr, page_size, &unused) >= 0); +} + +EGLBoolean wlEglCheckInterfaceType(struct wl_object *obj, const char *ifname) +{ + + Dl_info info; + /* + * The first member of a wl_object is a pointer to its wl_interface, + * so we can check if it corresponds to the given symbol name + */ + if (dladdr(*(void **)obj, &info) == 0) { + return EGL_FALSE; + } + return !strcmp(info.dli_sname, ifname); +} +#endif + +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); + } +} diff --git a/src/wayland-external-exports.c b/src/wayland-external-exports.c new file mode 100644 index 0000000..59a2ab1 --- /dev/null +++ b/src/wayland-external-exports.c @@ -0,0 +1,122 @@ +/* + * 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 +#include "wayland-external-exports.h" +#include "wayland-egldisplay.h" +#include "wayland-eglstream.h" +#include "wayland-eglsurface.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 }, + { "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_CHECK(major, 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; +} diff --git a/src/wayland-thread.c b/src/wayland-thread.c new file mode 100644 index 0000000..0ab122d --- /dev/null +++ b/src/wayland-thread.c @@ -0,0 +1,131 @@ +/* + * 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); +} diff --git a/wayland-egl/wayland-egl-ext.h b/wayland-egl/wayland-egl-ext.h new file mode 100644 index 0000000..a258bf4 --- /dev/null +++ b/wayland-egl/wayland-egl-ext.h @@ -0,0 +1,101 @@ +/* + * 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 diff --git a/wayland-eglstream-protocols.pc.in b/wayland-eglstream-protocols.pc.in new file mode 100644 index 0000000..775222f --- /dev/null +++ b/wayland-eglstream-protocols.pc.in @@ -0,0 +1,7 @@ +prefix=@prefix@ +datarootdir=@datarootdir@ +pkgdatadir=@datadir@/@PACKAGE@ + +Name: wayland-eglstream-protocols +Description: Nvidia Wayland EGLStream XML protocol files +Version: @WAYLAND_EXTERNAL_VERSION@ diff --git a/wayland-eglstream.pc.in b/wayland-eglstream.pc.in new file mode 100644 index 0000000..63953a2 --- /dev/null +++ b/wayland-eglstream.pc.in @@ -0,0 +1,11 @@ +prefix=@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@ diff --git a/wayland-eglstream/.gitignore b/wayland-eglstream/.gitignore new file mode 100644 index 0000000..914dabe --- /dev/null +++ b/wayland-eglstream/.gitignore @@ -0,0 +1,3 @@ +# All source files in this directory are autogenerated, ignore them +*.c +*.h diff --git a/wayland-eglstream/meson.build b/wayland-eglstream/meson.build new file mode 100644 index 0000000..a713900 --- /dev/null +++ b/wayland-eglstream/meson.build @@ -0,0 +1,40 @@ +generated_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()) +) diff --git a/wayland-eglstream/wayland-eglstream-controller.xml b/wayland-eglstream/wayland-eglstream-controller.xml new file mode 100644 index 0000000..2c3a635 --- /dev/null +++ b/wayland-eglstream/wayland-eglstream-controller.xml @@ -0,0 +1,87 @@ + + + + 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. + + + + + diff --git a/wayland-eglstream/wayland-eglstream.xml b/wayland-eglstream/wayland-eglstream.xml new file mode 100644 index 0000000..598e5ad --- /dev/null +++ b/wayland-eglstream/wayland-eglstream.xml @@ -0,0 +1,172 @@ + + + + + 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. + + + + + +