From 4163f34fbdbc0516085ba562e0013487b408c7ab Mon Sep 17 00:00:00 2001
From: Brendan King <Brendan.King@imgtec.com>
Date: Tue, 9 Mar 2021 17:15:30 +0000
Subject: [PATCH] dri: preserve the original FD for driver use.

If an application uses a different GPU from the default, allow the
file descriptor (FD) for that original GPU/display to be preserved
for use by drivers. Drivers may wish to use the original FD to
allocate shared surfaces, to ensure the surface properties are
compatible with the original GPU/display (e.g. for X11 or Wayland).

This feature is only available on platforms that choose to support
it, by implementing the new getDisplayFD function in the DRI image,
and DRI2 loader extensions.

If the feature is available, drivers can obtain the original FD
by calling the getDisplayFD function in the relevant loader extension.
Drivers should check the FD is valid before use (i.e. not -1). If
the FD is valid, it may be equal to the current GPU FD if a different
GPU is not being used. The FD is owned by the platform, not the
driver, and the platform is responsible for closing it.

The feature is currently supported by the Wayland, and DRI3 based
X11 EGL and GLX platforms.

---
 include/GL/internal/dri_interface.h      | 26 +++++++++++++++++--
 src/egl/drivers/dri2/egl_dri2.c          | 10 +++++++
 src/egl/drivers/dri2/egl_dri2.h          |  1 +
 src/egl/drivers/dri2/platform_wayland.c  | 31 ++++++++++++++++++++--
 src/egl/drivers/dri2/platform_x11.c      |  3 +++
 src/egl/drivers/dri2/platform_x11_dri3.c | 27 ++++++++++++++++++-
 src/glx/dri3_glx.c                       | 33 ++++++++++++++++++++++--
 src/glx/dri3_priv.h                      |  1 +
 8 files changed, 125 insertions(+), 7 deletions(-)

diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
index 888a117..2fb440f 100644
--- a/include/GL/internal/dri_interface.h
+++ b/include/GL/internal/dri_interface.h
@@ -1146,7 +1146,7 @@ struct __DRIbufferRec {
 };
 
 #define __DRI_DRI2_LOADER "DRI_DRI2Loader"
-#define __DRI_DRI2_LOADER_VERSION 5
+#define __DRI_DRI2_LOADER_VERSION 6
 
 enum dri_loader_cap {
    /* Whether the loader handles RGBA channel ordering correctly. If not,
@@ -1227,6 +1227,17 @@ struct __DRIdri2LoaderExtensionRec {
      * \since 5
      */
     void (*destroyLoaderImageState)(void *loaderPrivate);
+
+    /**
+     * Get the display FD
+     *
+     * Get the FD of the display device.
+     *
+     * \param loaderPrivate The last parameter of createNewScreen or
+     *                      createNewScreen2.
+     * \since 6
+     */
+     int (*getDisplayFD)(void *loaderPrivate);
 };
 
 /**
@@ -2131,7 +2142,7 @@ struct __DRIimageList {
 };
 
 #define __DRI_IMAGE_LOADER "DRI_IMAGE_LOADER"
-#define __DRI_IMAGE_LOADER_VERSION 4
+#define __DRI_IMAGE_LOADER_VERSION 5
 
 struct __DRIimageLoaderExtensionRec {
     __DRIextension base;
@@ -2199,6 +2210,17 @@ struct __DRIimageLoaderExtensionRec {
      * \since 4
      */
     void (*destroyLoaderImageState)(void *loaderPrivate);
+
+    /**
+     * Get the display FD
+     *
+     * Get the FD of the display device.
+     *
+     * \param loaderPrivate The last parameter of createNewScreen or
+     *                      createNewScreen2.
+     * \since 5
+     */
+     int (*getDisplayFD)(void *loaderPrivate);
 };
 
 /**
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index c4a49ca..1df53ef 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -1367,6 +1367,16 @@ dri2_display_destroy(_EGLDisplay *disp)
       break;
    }
 
+   switch (disp->Platform) {
+   case _EGL_PLATFORM_WAYLAND:
+   case _EGL_PLATFORM_X11:
+      if (dri2_dpy->fd_dpy >= 0 && dri2_dpy->fd_dpy != dri2_dpy->fd)
+         close(dri2_dpy->fd_dpy);
+      break;
+   default:
+      break;
+   }
+
    if (dri2_dpy->fd >= 0)
       close(dri2_dpy->fd);
 
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index f8792ea..22e27b4 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -231,6 +231,7 @@ struct dri2_egl_display
    const __DRIconfigOptionsExtension *configOptions;
    const __DRImutableRenderBufferDriverExtension *mutable_render_buffer;
    int                       fd;
+   int                       fd_dpy;
 
    /* dri2_initialize/dri2_terminate increment/decrement this count, so does
     * dri2_make_current (tracks if there are active contexts/surfaces). */
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 06272d4..b393e05 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -44,6 +44,7 @@
 #include "loader.h"
 #include "util/u_vector.h"
 #include "util/anon_file.h"
+#include "util/os_file.h"
 #include "eglglobals.h"
 
 #include <wayland-egl-backend.h>
@@ -973,21 +974,32 @@ dri2_wl_get_capability(void *loaderPrivate, enum dri_loader_cap cap)
    }
 }
 
+static int
+dri2_wl_get_display_fd(void *loaderPrivate)
+{
+   _EGLDisplay *disp = loaderPrivate;
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+   return dri2_dpy->fd_dpy;
+}
+
 static const __DRIdri2LoaderExtension dri2_loader_extension = {
-   .base = { __DRI_DRI2_LOADER, 4 },
+   .base = { __DRI_DRI2_LOADER, 6 },
 
    .getBuffers           = dri2_wl_get_buffers,
    .flushFrontBuffer     = dri2_wl_flush_front_buffer,
    .getBuffersWithFormat = dri2_wl_get_buffers_with_format,
    .getCapability        = dri2_wl_get_capability,
+   .getDisplayFD         = dri2_wl_get_display_fd,
 };
 
 static const __DRIimageLoaderExtension image_loader_extension = {
-   .base = { __DRI_IMAGE_LOADER, 2 },
+   .base = { __DRI_IMAGE_LOADER, 5 },
 
    .getBuffers          = image_get_buffers,
    .flushFrontBuffer    = dri2_wl_flush_front_buffer,
    .getCapability       = dri2_wl_get_capability,
+   .getDisplayFD        = dri2_wl_get_display_fd,
 };
 
 static void
@@ -1640,12 +1652,14 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
 {
    _EGLDevice *dev;
    struct dri2_egl_display *dri2_dpy;
+   int fd_old;
 
    dri2_dpy = calloc(1, sizeof *dri2_dpy);
    if (!dri2_dpy)
       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
 
    dri2_dpy->fd = -1;
+   dri2_dpy->fd_dpy = -1;
    disp->DriverData = (void *) dri2_dpy;
    if (disp->PlatformDisplay == NULL) {
       dri2_dpy->wl_dpy = wl_display_connect(NULL);
@@ -1690,8 +1704,20 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
        (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated))
       goto cleanup;
 
+   fd_old = dri2_dpy->fd;
+   dri2_dpy->fd_dpy = os_dupfd_cloexec(dri2_dpy->fd);
    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd,
                                                &dri2_dpy->is_different_gpu);
+   if (dri2_dpy->fd == fd_old) {
+      if (dri2_dpy->fd_dpy != -1)
+         close(dri2_dpy->fd_dpy);
+
+      dri2_dpy->fd_dpy = dri2_dpy->fd;
+   } else if (dri2_dpy->fd_dpy == -1) {
+      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to dup display FD");
+      goto cleanup;
+   }
+
    dev = _eglAddDevice(dri2_dpy->fd, false);
    if (!dev) {
       _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to find EGLDevice");
@@ -2236,6 +2262,7 @@ dri2_initialize_wayland_swrast(_EGLDisplay *disp)
       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
 
    dri2_dpy->fd = -1;
+   dri2_dpy->fd_dpy = -1;
    disp->DriverData = (void *) dri2_dpy;
    if (disp->PlatformDisplay == NULL) {
       dri2_dpy->wl_dpy = wl_display_connect(NULL);
diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c
index 5ffdf13..5cf3ce2 100644
--- a/src/egl/drivers/dri2/platform_x11.c
+++ b/src/egl/drivers/dri2/platform_x11.c
@@ -1277,6 +1277,7 @@ dri2_initialize_x11_swrast(_EGLDisplay *disp)
       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
 
    dri2_dpy->fd = -1;
+   dri2_dpy->fd_dpy = -1;
    if (!dri2_get_xcb_connection(disp, dri2_dpy))
       goto cleanup;
 
@@ -1364,6 +1365,7 @@ dri2_initialize_x11_dri3(_EGLDisplay *disp)
       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
 
    dri2_dpy->fd = -1;
+   dri2_dpy->fd_dpy = -1;
    if (!dri2_get_xcb_connection(disp, dri2_dpy))
       goto cleanup;
 
@@ -1472,6 +1474,7 @@ dri2_initialize_x11_dri2(_EGLDisplay *disp)
       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
 
    dri2_dpy->fd = -1;
+   dri2_dpy->fd_dpy = -1;
    if (!dri2_get_xcb_connection(disp, dri2_dpy))
       goto cleanup;
 
diff --git a/src/egl/drivers/dri2/platform_x11_dri3.c b/src/egl/drivers/dri2/platform_x11_dri3.c
index e117105..0babf9f 100644
--- a/src/egl/drivers/dri2/platform_x11_dri3.c
+++ b/src/egl/drivers/dri2/platform_x11_dri3.c
@@ -32,6 +32,7 @@
 
 #include <xf86drm.h>
 #include "util/macros.h"
+#include "util/os_file.h"
 
 #include "egl_dri2.h"
 #include "platform_x11_dri3.h"
@@ -414,11 +415,21 @@ dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
       _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
 }
 
+static int
+dri3_get_display_fd(void *loaderPrivate)
+{
+   _EGLDisplay *disp = loaderPrivate;
+   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+   return dri2_dpy->fd_dpy;
+}
+
 const __DRIimageLoaderExtension dri3_image_loader_extension = {
-   .base = { __DRI_IMAGE_LOADER, 1 },
+   .base = { __DRI_IMAGE_LOADER, 5 },
 
    .getBuffers          = loader_dri3_get_buffers,
    .flushFrontBuffer    = dri3_flush_front_buffer,
+   .getDisplayFD        = dri3_get_display_fd,
 };
 
 static EGLBoolean
@@ -537,6 +548,7 @@ dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
    xcb_xfixes_query_version_cookie_t xfixes_query_cookie;
    xcb_generic_error_t *error;
    const xcb_query_extension_reply_t *extension;
+   int fd_old;
 
    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
@@ -616,12 +628,25 @@ dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
       return EGL_FALSE;
    }
 
+   fd_old = dri2_dpy->fd;
+   dri2_dpy->fd_dpy = os_dupfd_cloexec(dri2_dpy->fd);
    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
+   if (dri2_dpy->fd == fd_old) {
+      if (dri2_dpy->fd_dpy != -1)
+         close(dri2_dpy->fd_dpy);
+
+      dri2_dpy->fd_dpy = dri2_dpy->fd;
+   } else if (dri2_dpy->fd_dpy == -1) {
+      _eglLog(_EGL_WARNING, "DRI3: failed to dup display FD");
+      close(dri2_dpy->fd);
+      return EGL_FALSE;
+   }
 
    dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
    if (!dri2_dpy->driver_name) {
       _eglLog(_EGL_WARNING, "DRI3: No driver found");
       close(dri2_dpy->fd);
+      close(dri2_dpy->fd_dpy);
       return EGL_FALSE;
    }
 
diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
index db1b079..1ed6b60 100644
--- a/src/glx/dri3_glx.c
+++ b/src/glx/dri3_glx.c
@@ -77,6 +77,7 @@
 #include "dri3_priv.h"
 #include "loader.h"
 #include "dri2.h"
+#include "util/os_file.h"
 
 static struct dri3_drawable *
 loader_drawable_to_dri3_drawable(struct loader_dri3_drawable *draw) {
@@ -529,6 +530,14 @@ dri3_flush_swap_buffers(__DRIdrawable *driDrawable, void *loaderPrivate)
    loader_dri3_swapbuffer_barrier(draw);
 }
 
+static int
+dri3_get_display_fd(void *loaderPrivate)
+{
+   struct dri3_screen *psc = (struct dri3_screen *)loaderPrivate;
+
+   return psc->fd_dpy;
+}
+
 static void
 dri_set_background_context(void *loaderPrivate)
 {
@@ -548,11 +557,12 @@ dri_is_thread_safe(void *loaderPrivate)
 /* The image loader extension record for DRI3
  */
 static const __DRIimageLoaderExtension imageLoaderExtension = {
-   .base = { __DRI_IMAGE_LOADER, 3 },
+   .base = { __DRI_IMAGE_LOADER, 5 },
 
    .getBuffers          = loader_dri3_get_buffers,
    .flushFrontBuffer    = dri3_flush_front_buffer,
    .flushSwapBuffers    = dri3_flush_swap_buffers,
+   .getDisplayFD        = dri3_get_display_fd,
 };
 
 const __DRIuseInvalidateExtension dri3UseInvalidate = {
@@ -618,6 +628,10 @@ dri3_destroy_screen(struct glx_screen *base)
    loader_dri3_close_screen(psc->driScreen);
    (*psc->core->destroyScreen) (psc->driScreen);
    driDestroyConfigs(psc->driver_configs);
+
+   if (psc->fd_dpy != psc->fd)
+      close(psc->fd_dpy);
+
    close(psc->fd);
    free(psc);
 }
@@ -842,8 +856,9 @@ dri3_create_screen(int screen, struct glx_display * priv)
    struct dri3_screen *psc;
    __GLXDRIscreen *psp;
    struct glx_config *configs = NULL, *visuals = NULL;
-   char *driverName, *driverNameDisplayGPU, *tmp;
+   char *driverName = NULL, *driverNameDisplayGPU, *tmp;
    int i;
+   int fd_old;
 
    psc = calloc(1, sizeof *psc);
    if (psc == NULL)
@@ -851,6 +866,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
 
    psc->fd = -1;
    psc->fd_display_gpu = -1;
+   psc->fd_dpy = -1;
 
    if (!glx_screen_init(&psc->base, screen, priv)) {
       free(psc);
@@ -871,12 +887,23 @@ dri3_create_screen(int screen, struct glx_display * priv)
       return NULL;
    }
 
+   fd_old = psc->fd;
+   psc->fd_dpy = os_dupfd_cloexec(psc->fd);
    psc->fd_display_gpu = fcntl(psc->fd, F_DUPFD_CLOEXEC, 3);
    psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
    if (!psc->is_different_gpu) {
       close(psc->fd_display_gpu);
       psc->fd_display_gpu = -1;
    }
+   if (psc->fd == fd_old) {
+      if (psc->fd_dpy != -1)
+         close(psc->fd_dpy);
+
+      psc->fd_dpy = psc->fd;
+   } else if (psc->fd_dpy == -1) {
+         ErrorMessageF("Unable to dup the display FD");
+         goto handle_error;
+   }
 
    driverName = loader_get_driver_for_fd(psc->fd);
    if (!driverName) {
@@ -1049,6 +1076,8 @@ handle_error:
    if (psc->driScreenDisplayGPU)
        psc->core->destroyScreen(psc->driScreenDisplayGPU);
    psc->driScreenDisplayGPU = NULL;
+   if (psc->fd_dpy >= 0 && psc->fd_dpy != psc->fd)
+      close(psc->fd_dpy);
    if (psc->fd >= 0)
       close(psc->fd);
    if (psc->fd_display_gpu >= 0)
diff --git a/src/glx/dri3_priv.h b/src/glx/dri3_priv.h
index c0e833c..b3dccf2 100644
--- a/src/glx/dri3_priv.h
+++ b/src/glx/dri3_priv.h
@@ -107,6 +107,7 @@ struct dri3_screen {
 
    void *driver;
    int fd;
+   int fd_dpy;
    bool is_different_gpu;
 
    /* fd for display GPU in case of prime */
