[gnome-remote-desktop] egl-thread: Also allow bound dma-bufs to be mapped
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-remote-desktop] egl-thread: Also allow bound dma-bufs to be mapped
- Date: Thu, 3 Mar 2022 14:23:09 +0000 (UTC)
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]