[gnome-remote-desktop] egl-thread: Also allow bound dma-bufs to be mapped



commit 7de1d378f2ad39dc73d972f7675c1b320df10545
Author: Pascal Nowack <Pascal Nowack gmx de>
Date:   Thu Jan 13 17:53:59 2022 +0100

    egl-thread: Also allow bound dma-bufs to be mapped
    
    Currently, hardware acceleration using CUDA can only be used, when
    dma-buf support is disabled, as mapping pixel buffer objects created
    from dma-buf textures is not implemented yet.
    In order to be able to make use of damage detection on the GPU, the EGL
    thread needs to map the pixel buffer object for a dma-buf texture.
    When NVENC support is unavailable, then the dma-buf framebuffer also
    needs to be downloaded.
    In the case of CUDA is unavailable, but dma-bufs are still supported,
    gnome-remote-desktop needs to download the dma-buf framebuffer.
    
    To support all these cases with dma-bufs, extend the current download
    API accordingly.
    When the dst_data is NULL, don't download the framebuffer from the GPU.
    When a GrdEglThreadImportIface instance was provided to the download
    function, import the dma-buf with the help of the provided functions in
    the iface.
    This allows later to directly use the dma-buf content for the use in an
    external API like CUDA without additional GPU <-> CPU transfer.

 src/grd-egl-thread.c          | 138 +++++++++++++++++++++++++++++++++---------
 src/grd-egl-thread.h          |  40 +++++++-----
 src/grd-rdp-pipewire-stream.c |  53 ++++++++++++++++
 src/grd-vnc-pipewire-stream.c |   2 +
 tests/egl-thread-test.c       |   2 +
 5 files changed, 192 insertions(+), 43 deletions(-)
---
diff --git a/src/grd-egl-thread.c b/src/grd-egl-thread.c
index 0e3995e4..e4fdcf93 100644
--- a/src/grd-egl-thread.c
+++ b/src/grd-egl-thread.c
@@ -119,6 +119,14 @@ typedef struct _GrdEglTaskDownload
 {
   GrdEglTask base;
 
+  GLuint pbo;
+  uint32_t pbo_height;
+  uint32_t pbo_stride;
+
+  GrdEglThreadImportIface iface;
+  gpointer import_user_data;
+  GDestroyNotify import_destroy_notify;
+
   uint8_t *dst_data;
   int dst_row_width;
 
@@ -404,6 +412,9 @@ grd_egl_task_download_free (GrdEglTask *task_base)
   g_free (task->offsets);
   g_free (task->modifiers);
 
+  if (task->import_destroy_notify)
+    task->import_destroy_notify (task->import_user_data);
+
   grd_egl_task_free (task_base);
 }
 
@@ -699,18 +710,29 @@ create_dmabuf_image (GrdEglThread   *egl_thread,
 }
 
 static gboolean
-bind_egl_image (GrdEglThread       *egl_thread,
-                EGLImageKHR         egl_image,
-                GrdEglTaskDownload *task,
-                GLuint             *tex,
-                GLuint             *fbo)
+bind_egl_image (GrdEglThread *egl_thread,
+                EGLImageKHR   egl_image,
+                int           dst_row_width,
+                GLuint       *tex,
+                GLuint       *fbo)
 {
+  GLenum error;
+
   glGenTextures (1, tex);
   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   glBindTexture (GL_TEXTURE_2D, *tex);
-  glPixelStorei (GL_PACK_ROW_LENGTH, task->dst_row_width);
+  glPixelStorei (GL_PACK_ROW_LENGTH, dst_row_width);
   glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, egl_image);
+  error = glGetError ();
+  if (error != GL_NO_ERROR)
+    {
+      g_warning ("[EGL Thread] Failed to bind DMA buf texture: %u", error);
+      return FALSE;
+    }
+
+  if (!fbo)
+    return TRUE;
 
   glGenFramebuffers (1, fbo);
   glBindFramebuffer (GL_FRAMEBUFFER, *fbo);
@@ -727,11 +749,13 @@ bind_egl_image (GrdEglThread       *egl_thread,
 }
 
 static void
-read_pixels (GrdEglTaskDownload *task)
+read_pixels (uint8_t      *dst_data,
+             unsigned int  width,
+             unsigned int  height)
 {
-  glReadPixels (0, 0, task->width, task->height,
+  glReadPixels (0, 0, width, height,
                 GL_BGRA, GL_UNSIGNED_BYTE,
-                task->dst_data);
+                dst_data);
 }
 
 static void
@@ -742,13 +766,30 @@ download_in_impl (gpointer data,
   GrdEglTaskDownload *task = user_data;
   EGLImageKHR egl_image;
   gboolean success = FALSE;
+  uint32_t buffer_size;
   GLuint tex = 0;
   GLuint fbo = 0;
 
-  eglMakeCurrent (egl_thread->impl.egl_display,
-                  EGL_NO_SURFACE,
-                  EGL_NO_SURFACE,
-                  egl_thread->impl.egl_context);
+  buffer_size = task->pbo_stride * task->pbo_height * sizeof (uint8_t);
+  if (task->iface.allocate && !task->pbo)
+    {
+      GLuint pbo = 0;
+
+      glGenBuffers (1, &pbo);
+      glBindBuffer (GL_PIXEL_PACK_BUFFER, pbo);
+      glBufferData (GL_PIXEL_PACK_BUFFER, buffer_size, NULL, GL_DYNAMIC_DRAW);
+      glBindBuffer (GL_PIXEL_PACK_BUFFER, 0);
+
+      if (!task->iface.allocate (task->import_user_data, pbo))
+        {
+          g_warning ("[EGL Thread] Failed to allocate GL resources");
+          glDeleteBuffers (1, &pbo);
+          goto out;
+        }
+
+      g_debug ("[EGL Thread] Allocating GL resources was successful");
+      task->pbo = pbo;
+    }
 
   egl_image =
     create_dmabuf_image (egl_thread,
@@ -763,14 +804,39 @@ download_in_impl (gpointer data,
   if (egl_image == EGL_NO_IMAGE)
     goto out;
 
-  if (!bind_egl_image (egl_thread, egl_image, task, &tex, &fbo))
+  if (!bind_egl_image (egl_thread, egl_image, task->dst_row_width, &tex,
+                       task->dst_data ? &fbo : NULL))
     goto out;
 
-  read_pixels (task);
+  if (task->iface.realize)
+    {
+      GLenum error;
+
+      glBindBuffer (GL_PIXEL_PACK_BUFFER, task->pbo);
+      glBufferData (GL_PIXEL_PACK_BUFFER, buffer_size, NULL, GL_DYNAMIC_DRAW);
+      glReadPixels (0, 0, task->width, task->height,
+                    GL_BGRA, GL_UNSIGNED_BYTE, NULL);
+      error = glGetError ();
+      if (error != GL_NO_ERROR)
+        {
+          g_warning ("[EGL Thread] Failed to update buffer data: %u", error);
+          goto out;
+        }
+
+      glBindBuffer (GL_PIXEL_PACK_BUFFER, 0);
+
+      if (!task->iface.realize (task->import_user_data))
+        goto out;
+    }
+
+  if (task->dst_data)
+    read_pixels (task->dst_data, task->width, task->height);
 
   success = TRUE;
 
 out:
+  glBindBuffer (GL_PIXEL_PACK_BUFFER, 0);
+
   if (fbo)
     {
       glBindFramebuffer (GL_FRAMEBUFFER, 0);
@@ -906,25 +972,39 @@ run_custom_task_in_impl (gpointer data,
 }
 
 void
-grd_egl_thread_download (GrdEglThread         *egl_thread,
-                         uint8_t              *dst_data,
-                         int                   dst_row_width,
-                         uint32_t              format,
-                         unsigned int          width,
-                         unsigned int          height,
-                         uint32_t              n_planes,
-                         const int            *fds,
-                         const uint32_t       *strides,
-                         const uint32_t       *offsets,
-                         const uint64_t       *modifiers,
-                         GrdEglThreadCallback  callback,
-                         gpointer              user_data,
-                         GDestroyNotify        destroy)
+grd_egl_thread_download (GrdEglThread                  *egl_thread,
+                         uint32_t                       pbo,
+                         uint32_t                       pbo_height,
+                         uint32_t                       pbo_stride,
+                         const GrdEglThreadImportIface *iface,
+                         gpointer                       import_user_data,
+                         GDestroyNotify                 import_destroy_notify,
+                         uint8_t                       *dst_data,
+                         int                            dst_row_width,
+                         uint32_t                       format,
+                         unsigned int                   width,
+                         unsigned int                   height,
+                         uint32_t                       n_planes,
+                         const int                     *fds,
+                         const uint32_t                *strides,
+                         const uint32_t                *offsets,
+                         const uint64_t                *modifiers,
+                         GrdEglThreadCallback           callback,
+                         gpointer                       user_data,
+                         GDestroyNotify                 destroy)
 {
   GrdEglTaskDownload *task;
 
   task = g_new0 (GrdEglTaskDownload, 1);
 
+  task->pbo = pbo;
+  task->pbo_height = pbo_height;
+  task->pbo_stride = pbo_stride;
+
+  task->iface = iface ? *iface : (GrdEglThreadImportIface) {};
+  task->import_user_data = import_user_data;
+  task->import_destroy_notify = import_destroy_notify;
+
   task->dst_data = dst_data;
   task->dst_row_width = dst_row_width;
 
diff --git a/src/grd-egl-thread.h b/src/grd-egl-thread.h
index 860f0296..93fa891c 100644
--- a/src/grd-egl-thread.h
+++ b/src/grd-egl-thread.h
@@ -33,24 +33,36 @@ typedef gboolean (* GrdEglThreadAllocBufferFunc) (gpointer user_data,
                                                   uint32_t pbo);
 typedef void (* GrdEglThreadDeallocBufferFunc) (gpointer user_data);
 
+typedef struct _GrdEglThreadImportIface
+{
+  GrdEglThreadAllocBufferFunc allocate;
+  GrdEglThreadCustomFunc realize;
+} GrdEglThreadImportIface;
+
 GrdEglThread * grd_egl_thread_new (GError **error);
 
 void grd_egl_thread_free (GrdEglThread *egl_thread);
 
-void grd_egl_thread_download (GrdEglThread         *egl_thread,
-                              uint8_t              *dst_data,
-                              int                   dst_row_width,
-                              uint32_t              format,
-                              unsigned int          width,
-                              unsigned int          height,
-                              uint32_t              n_planes,
-                              const int            *fds,
-                              const uint32_t       *strides,
-                              const uint32_t       *offsets,
-                              const uint64_t       *modifiers,
-                              GrdEglThreadCallback  callback,
-                              gpointer              user_data,
-                              GDestroyNotify        destroy);
+void grd_egl_thread_download (GrdEglThread                  *egl_thread,
+                              uint32_t                       pbo,
+                              uint32_t                       pbo_height,
+                              uint32_t                       pbo_stride,
+                              const GrdEglThreadImportIface *iface,
+                              gpointer                       import_user_data,
+                              GDestroyNotify                 import_destroy_notify,
+                              uint8_t                       *dst_data,
+                              int                            dst_row_width,
+                              uint32_t                       format,
+                              unsigned int                   width,
+                              unsigned int                   height,
+                              uint32_t                       n_planes,
+                              const int                     *fds,
+                              const uint32_t                *strides,
+                              const uint32_t                *offsets,
+                              const uint64_t                *modifiers,
+                              GrdEglThreadCallback           callback,
+                              gpointer                       user_data,
+                              GDestroyNotify                 destroy);
 
 void grd_egl_thread_allocate (GrdEglThread                *egl_thread,
                               uint32_t                     height,
diff --git a/src/grd-rdp-pipewire-stream.c b/src/grd-rdp-pipewire-stream.c
index d40a522a..93e9dea3 100644
--- a/src/grd-rdp-pipewire-stream.c
+++ b/src/grd-rdp-pipewire-stream.c
@@ -95,6 +95,14 @@ typedef struct
   GrdRdpBuffer *rdp_buffer;
 } RealizeBufferData;
 
+typedef struct
+{
+  AllocateBufferData allocate;
+  RealizeBufferData realize;
+
+  GrdRdpBuffer *rdp_buffer;
+} ImportBufferData;
+
 struct _GrdRdpPipeWireStream
 {
   GObject parent;
@@ -502,6 +510,15 @@ cuda_allocate_buffer (gpointer user_data,
   return success;
 }
 
+static gboolean
+allocate_buffer (gpointer user_data,
+                 uint32_t pbo)
+{
+  ImportBufferData *data = user_data;
+
+  return cuda_allocate_buffer (&data->allocate, pbo);
+}
+
 static gboolean
 cuda_map_resource (gpointer user_data)
 {
@@ -516,6 +533,14 @@ cuda_map_resource (gpointer user_data)
                                                rdp_buffer->cuda_stream);
 }
 
+static gboolean
+realize_buffer (gpointer user_data)
+{
+  ImportBufferData *data = user_data;
+
+  return cuda_map_resource (&data->realize);
+}
+
 static void
 on_framebuffer_ready (gboolean success,
                       gpointer user_data)
@@ -678,6 +703,10 @@ process_buffer (GrdRdpPipeWireStream     *stream,
       GrdSession *session = GRD_SESSION (stream->session_rdp);
       GrdContext *context = grd_session_get_context (session);
       GrdEglThread *egl_thread = grd_context_get_egl_thread (context);
+      GrdHwAccelNvidia *hwaccel_nvidia = stream->rdp_surface->hwaccel_nvidia;
+      GrdEglThreadImportIface iface;
+      ImportBufferData *import_buffer_data = NULL;
+      GrdRdpBuffer *rdp_buffer;
       int row_width;
       int *fds;
       uint32_t *offsets;
@@ -695,6 +724,7 @@ process_buffer (GrdRdpPipeWireStream     *stream,
           callback (stream, g_steal_pointer (&frame), FALSE, user_data);
           return;
         }
+      rdp_buffer = frame->buffer;
 
       row_width = dst_stride / bpp;
 
@@ -713,7 +743,30 @@ process_buffer (GrdRdpPipeWireStream     *stream,
         }
       dst_data = frame->buffer->local_data;
 
+      if (hwaccel_nvidia)
+        {
+          unmap_cuda_resources (egl_thread, hwaccel_nvidia, rdp_buffer);
+
+          iface.allocate = allocate_buffer;
+          iface.realize = realize_buffer;
+
+          import_buffer_data = g_new0 (ImportBufferData, 1);
+          import_buffer_data->allocate.hwaccel_nvidia = hwaccel_nvidia;
+          import_buffer_data->allocate.rdp_buffer = rdp_buffer;
+
+          import_buffer_data->realize.hwaccel_nvidia = hwaccel_nvidia;
+          import_buffer_data->realize.rdp_buffer = rdp_buffer;
+
+          import_buffer_data->rdp_buffer = rdp_buffer;
+        }
+
       grd_egl_thread_download (egl_thread,
+                               rdp_buffer->pbo,
+                               height,
+                               dst_stride,
+                               hwaccel_nvidia ? &iface : NULL,
+                               import_buffer_data,
+                               g_free,
                                dst_data,
                                row_width,
                                drm_format,
diff --git a/src/grd-vnc-pipewire-stream.c b/src/grd-vnc-pipewire-stream.c
index f4bbc9f7..e4caabe8 100644
--- a/src/grd-vnc-pipewire-stream.c
+++ b/src/grd-vnc-pipewire-stream.c
@@ -496,6 +496,8 @@ process_buffer (GrdVncPipeWireStream     *stream,
 
       frame->data = dst_data;
       grd_egl_thread_download (egl_thread,
+                               0, 0, 0,
+                               NULL, NULL, NULL,
                                dst_data,
                                row_width,
                                drm_format,
diff --git a/tests/egl-thread-test.c b/tests/egl-thread-test.c
index 34a654cd..c834a51e 100644
--- a/tests/egl-thread-test.c
+++ b/tests/egl-thread-test.c
@@ -308,6 +308,8 @@ run_dma_buf_tests (struct gbm_device *gbm_device,
     g_malloc0 (dma_buf->height * dma_buf->read_back_stride);
 
   grd_egl_thread_download (egl_thread,
+                           0, 0, 0,
+                           NULL, NULL, NULL,
                            dma_buf->read_back_data,
                            dst_row_width,
                            dma_buf->format,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]