1
0
Fork 0

onscreen/native: Use EGLSyncs instead of cogl_framebuffer_finish

cogl_framebuffer_finish can result in a CPU-side stall because it waits for
the primary GPU to flush and execute all commands that were queued before
that. By using a GPU-side EGLSync we can let the primary GPU inform us when
it is done with the queued commands instead. We then create another EGLSync
on the secondary GPU using the same fd so the primary GPU effectively
signals the secondary GPU when it is done rendering, causing the latter
to wait for the former before copying part of the frames it needs for
monitors attached to it directly.

This solves the corruption that cogl_framebuffer_finish also solved, but
without needing a CPU-side stall.

Signed-off-by: Mingi Sung <sungmg@saltyming.net>
This commit is contained in:
Gert-dev 2024-09-12 22:02:31 +02:00 committed by Mingi Sung
parent 1444e82cd8
commit 2cdcc4b9cc
Signed by: sungmg
GPG key ID: 41BAFD6FFD8036C5
3 changed files with 200 additions and 2 deletions

View file

@ -44,6 +44,11 @@ struct _MetaEgl
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
PFNEGLCREATESYNCPROC eglCreateSync;
PFNEGLDESTROYSYNCPROC eglDestroySync;
PFNEGLWAITSYNCPROC eglWaitSync;
PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID;
PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplayWL;
PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBufferWL;
@ -1162,6 +1167,90 @@ meta_egl_query_display_attrib (MetaEgl *egl,
return TRUE;
}
gboolean
meta_egl_create_sync (MetaEgl *egl,
EGLDisplay display,
EGLenum type,
const EGLAttrib *attrib_list,
EGLSync *egl_sync,
GError **error)
{
if (!is_egl_proc_valid (egl->eglCreateSync, error))
return FALSE;
EGLSync sync;
sync = egl->eglCreateSync (display, type, attrib_list);
if (sync == EGL_NO_SYNC)
{
set_egl_error (error);
return FALSE;
}
*egl_sync = sync;
return TRUE;
}
gboolean
meta_egl_destroy_sync (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
GError **error)
{
if (!is_egl_proc_valid (egl->eglDestroySync, error))
return FALSE;
if (!egl->eglDestroySync (display, sync))
{
set_egl_error (error);
return FALSE;
}
return TRUE;
}
gboolean
meta_egl_wait_sync (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
EGLint flags,
GError **error)
{
if (!is_egl_proc_valid (egl->eglWaitSync, error))
return FALSE;
if (!egl->eglWaitSync (display, sync, flags))
{
set_egl_error (error);
return FALSE;
}
return TRUE;
}
EGLint
meta_egl_duplicate_native_fence_fd (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
GError **error)
{
if (!is_egl_proc_valid (egl->eglDupNativeFenceFDANDROID, error))
return EGL_NO_NATIVE_FENCE_FD_ANDROID;
EGLint fd = EGL_NO_NATIVE_FENCE_FD_ANDROID;
fd = egl->eglDupNativeFenceFDANDROID (display, sync);
if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
set_egl_error (error);
}
return fd;
}
#define GET_EGL_PROC_ADDR(proc) \
egl->proc = (void *) eglGetProcAddress (#proc);
@ -1175,6 +1264,11 @@ meta_egl_constructed (GObject *object)
GET_EGL_PROC_ADDR (eglCreateImageKHR);
GET_EGL_PROC_ADDR (eglDestroyImageKHR);
GET_EGL_PROC_ADDR (eglCreateSync);
GET_EGL_PROC_ADDR (eglDestroySync);
GET_EGL_PROC_ADDR (eglWaitSync);
GET_EGL_PROC_ADDR (eglDupNativeFenceFDANDROID);
GET_EGL_PROC_ADDR (eglBindWaylandDisplayWL);
GET_EGL_PROC_ADDR (eglQueryWaylandBufferWL);

View file

@ -276,3 +276,26 @@ gboolean meta_egl_query_display_attrib (MetaEgl *egl,
EGLint attribute,
EGLAttrib *value,
GError **error);
gboolean meta_egl_create_sync (MetaEgl *egl,
EGLDisplay display,
EGLenum type,
const EGLAttrib *attrib_list,
EGLSync *egl_sync,
GError **error);
gboolean meta_egl_destroy_sync (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
GError **error);
gboolean meta_egl_wait_sync (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
EGLint flags,
GError **error);
EGLint meta_egl_duplicate_native_fence_fd (MetaEgl *egl,
EGLDisplay display,
EGLSync sync,
GError **error);

View file

@ -29,6 +29,7 @@
#include "backends/native/meta-onscreen-native.h"
#include <glib/gstdio.h>
#include <drm_fourcc.h>
#include "backends/meta-egl-ext.h"
@ -851,19 +852,51 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
CoglContext *cogl_context = cogl_framebuffer_get_context (framebuffer);
CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys;
MetaRenderDevice *render_device;
EGLDisplay egl_display;
EGLDisplay egl_display = NULL;
gboolean use_modifiers;
MetaDeviceFile *device_file;
MetaDrmBufferFlags flags;
MetaDrmBufferGbm *buffer_gbm = NULL;
struct gbm_bo *bo;
EGLSync primary_gpu_egl_sync = EGL_NO_SYNC;
EGLSync secondary_gpu_egl_sync = EGL_NO_SYNC;
g_autofd int primary_gpu_sync_fence = EGL_NO_NATIVE_FENCE_FD_ANDROID;
COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu,
"copy_shared_framebuffer_gpu()");
if (renderer_gpu_data->secondary.needs_explicit_sync)
cogl_framebuffer_finish (COGL_FRAMEBUFFER (onscreen));
{
if (!meta_egl_create_sync (egl,
cogl_renderer_egl->edpy,
EGL_SYNC_NATIVE_FENCE_ANDROID,
NULL,
&primary_gpu_egl_sync,
error))
{
g_prefix_error (error, "Failed to create EGLSync on primary GPU: ");
return NULL;
}
// According to the EGL_KHR_fence_sync specification we must ensure
// the fence command is flushed in this context to be able to await it
// in another (secondary GPU context) or we risk waiting indefinitely.
cogl_framebuffer_flush (COGL_FRAMEBUFFER (onscreen));
primary_gpu_sync_fence =
meta_egl_duplicate_native_fence_fd (egl,
cogl_renderer_egl->edpy,
primary_gpu_egl_sync,
error);
if (primary_gpu_sync_fence == EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
g_prefix_error (error, "Failed to duplicate EGLSync FD on primary GPU: ");
goto done;
}
}
render_device = renderer_gpu_data->render_device;
egl_display = meta_render_device_get_egl_display (render_device);
@ -879,6 +912,40 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
goto done;
}
if (primary_gpu_sync_fence != EGL_NO_NATIVE_FENCE_FD_ANDROID)
{
EGLAttrib attribs[3];
attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID;
attribs[1] = primary_gpu_sync_fence;
attribs[2] = EGL_NONE;
if (!meta_egl_create_sync (egl,
egl_display,
EGL_SYNC_NATIVE_FENCE_ANDROID,
attribs,
&secondary_gpu_egl_sync,
error))
{
g_prefix_error (error, "Failed to create EGLSync on secondary GPU: ");
goto done;
}
// eglCreateSync takes ownership of an existing fd that is passed, so
// don't try to clean it up twice.
primary_gpu_sync_fence = EGL_NO_NATIVE_FENCE_FD_ANDROID;
if (!meta_egl_wait_sync (egl,
egl_display,
secondary_gpu_egl_sync,
0,
error))
{
g_prefix_error (error, "Failed to wait for EGLSync on secondary GPU: ");
goto done;
}
}
buffer_gbm = META_DRM_BUFFER_GBM (primary_gpu_fb);
bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
if (!meta_renderer_native_gles3_blit_shared_bo (egl,
@ -928,6 +995,20 @@ copy_shared_framebuffer_gpu (CoglOnscreen *onscreen,
done:
_cogl_winsys_egl_ensure_current (cogl_display);
if (primary_gpu_egl_sync != EGL_NO_SYNC &&
!meta_egl_destroy_sync (egl,
cogl_renderer_egl->edpy,
primary_gpu_egl_sync,
error))
g_prefix_error (error, "Failed to destroy primary GPU EGLSync: ");
if (secondary_gpu_egl_sync != EGL_NO_SYNC &&
!meta_egl_destroy_sync (egl,
egl_display,
secondary_gpu_egl_sync,
error))
g_prefix_error (error, "Failed to destroy secondary GPU EGLSync: ");
return buffer_gbm ? META_DRM_BUFFER (buffer_gbm) : NULL;
}