From f6a2126b14155fd89d661249dace6068fc10d76b Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Wed, 7 Apr 2021 11:19:39 -0700 Subject: [PATCH] glarea: Add support for explicit synchronization For glarea widgets, there is a need to determine when the previously submitted buffer has been consumed by the windowing system. This is particularly important if the buffer is backed up by a texture that is associated with a dma-buf. Upstream Wayland/Weston supports explicit sync protocol that makes it possible to make such a determination. Most of the code added in this MR is based on this Weston client: https://cgit.freedesktop.org/wayland/weston/tree/clients/simple-dmabuf-egl.c --- gdk/gdkdisplay.c | 17 +++++ gdk/gdkdisplayprivate.h | 4 + gdk/gdkglcontext.c | 14 ++++ gdk/gdkglcontext.h | 3 + gdk/wayland/gdkdisplay-wayland.c | 8 ++ gdk/wayland/gdkdisplay-wayland.h | 7 ++ gdk/wayland/gdkglcontext-wayland.c | 113 +++++++++++++++++++++++++++++ gdk/wayland/gdkglcontext-wayland.h | 1 + gdk/wayland/meson.build | 1 + gtk/gtkglarea.c | 93 ++++++++++++++++++++++++ gtk/gtkglarea.h | 8 ++ 11 files changed, 269 insertions(+) diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index b86ad959f24..7182f96b949 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -1623,3 +1623,20 @@ gdk_display_translate_key (GdkDisplay *display, level, consumed); } + +/** + * gdk_display_explicit_sync: + * @display: a `GdkDisplay` + * + * Waits until the windowing system has finished using the previously + * submitted buffer. This is most useful for Wayland based compositors that + * support the explicit sync prototol. On other windowing systems, this + * function will be a no-op. + */ +void +gdk_display_explicit_sync (GdkDisplay *display) +{ + g_return_if_fail (GDK_IS_DISPLAY (display)); + + GDK_DISPLAY_GET_CLASS (display)->explicit_sync (display); +} diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h index be4a0901ca2..3f7f2e168a5 100644 --- a/gdk/gdkdisplayprivate.h +++ b/gdk/gdkdisplayprivate.h @@ -106,6 +106,8 @@ struct _GdkDisplay guint composited : 1; guint input_shapes : 1; + gboolean use_explicit_sync; + GdkDebugFlags debug_flags; GList *seats; @@ -164,6 +166,7 @@ struct _GdkDisplayClass void (*opened) (GdkDisplay *display); void (*closed) (GdkDisplay *display, gboolean is_error); + void (*explicit_sync) (GdkDisplay *display); }; @@ -242,6 +245,7 @@ void _gdk_windowing_got_event (GdkDisplay *display, GList *event_link, GdkEvent *event, gulong serial); +void gdk_display_explicit_sync (GdkDisplay *display); G_END_DECLS diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 7c3c0a0a790..375bc1c247f 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -1258,3 +1258,17 @@ gdk_gl_context_use_es_bgra (GdkGLContext *context) return FALSE; } + +void +gdk_gl_context_explicit_sync (GdkGLContext *context, + gboolean use_explicit_sync) +{ + GdkDisplay *display; + + g_return_if_fail (GDK_IS_GL_CONTEXT (context)); + + display = gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)); + display->use_explicit_sync = use_explicit_sync; + gdk_display_explicit_sync (display); +} + diff --git a/gdk/gdkglcontext.h b/gdk/gdkglcontext.h index 10bac82e9bf..b20cc32d9ce 100644 --- a/gdk/gdkglcontext.h +++ b/gdk/gdkglcontext.h @@ -89,6 +89,9 @@ GDK_AVAILABLE_IN_ALL GdkGLContext * gdk_gl_context_get_current (void); GDK_AVAILABLE_IN_ALL void gdk_gl_context_clear_current (void); +GDK_AVAILABLE_IN_ALL +void gdk_gl_context_explicit_sync (GdkGLContext *context, + gboolean use_explicit_sync); G_END_DECLS diff --git a/gdk/wayland/gdkdisplay-wayland.c b/gdk/wayland/gdkdisplay-wayland.c index cc4583658b1..9761abe3ee4 100644 --- a/gdk/wayland/gdkdisplay-wayland.c +++ b/gdk/wayland/gdkdisplay-wayland.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "wm-button-layout-translation.h" @@ -576,6 +577,12 @@ gdk_registry_handle_global (void *data, wl_registry_bind (display_wayland->wl_registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); } + else if (strcmp(interface, "zwp_linux_explicit_synchronization_v1") == 0) + { + display_wayland->explicit_sync = + wl_registry_bind (display_wayland->wl_registry, id, + &zwp_linux_explicit_synchronization_v1_interface, 1); + } g_hash_table_insert (display_wayland->known_globals, GUINT_TO_POINTER (id), g_strdup (interface)); @@ -1048,6 +1055,7 @@ gdk_wayland_display_class_init (GdkWaylandDisplayClass *class) display_class->get_monitor_at_surface = gdk_wayland_display_get_monitor_at_surface; display_class->get_setting = gdk_wayland_display_get_setting; display_class->set_cursor_theme = gdk_wayland_display_set_cursor_theme; + display_class->explicit_sync = gdk_wayland_display_explicit_sync; } static void diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 886210ef3c4..3f2df1d81db 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -112,6 +112,9 @@ struct _GdkWaylandDisplay struct org_kde_kwin_server_decoration_manager *server_decoration_manager; struct zxdg_output_manager_v1 *xdg_output_manager; struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager; + struct zwp_linux_explicit_synchronization_v1 *explicit_sync; + struct zwp_linux_surface_synchronization_v1 *surface_sync; + struct zwp_linux_buffer_release_v1 *buffer_release; GList *async_roundtrips; @@ -148,6 +151,8 @@ struct _GdkWaylandDisplay gint64 last_bell_time_ms; + int release_fence_fd; + /* egl info */ EGLDisplay egl_display; int egl_major_version; @@ -158,6 +163,8 @@ struct _GdkWaylandDisplay guint have_egl_buffer_age : 1; guint have_egl_swap_buffers_with_damage : 1; guint have_egl_surfaceless_context : 1; + guint have_egl_khr_fence_sync : 1; + guint have_egl_android_native_fence_sync : 1; }; struct _GdkWaylandDisplayClass diff --git a/gdk/wayland/gdkglcontext-wayland.c b/gdk/wayland/gdkglcontext-wayland.c index 9e2572f5bea..f8d795ef237 100644 --- a/gdk/wayland/gdkglcontext-wayland.c +++ b/gdk/wayland/gdkglcontext-wayland.c @@ -34,6 +34,7 @@ #include "gdkprofilerprivate.h" #include "gdkintl.h" +#include /** * GdkWaylandGLContext: @@ -256,6 +257,80 @@ gdk_wayland_gl_context_get_damage (GdkGLContext *context) return GDK_GL_CONTEXT_CLASS (gdk_wayland_gl_context_parent_class)->get_damage (context); } +static void +buffer_fenced_release(void *data, + struct zwp_linux_buffer_release_v1 *release, + int32_t fence) +{ + GdkWaylandDisplay *display_wayland = data; + + g_assert(release == display_wayland->buffer_release); + g_assert(display_wayland->release_fence_fd == -1); + + display_wayland->release_fence_fd = fence; + zwp_linux_buffer_release_v1_destroy(display_wayland->buffer_release); + display_wayland->buffer_release = NULL; +} + +static void +buffer_immediate_release(void *data, + struct zwp_linux_buffer_release_v1 *release) +{ + GdkWaylandDisplay *display_wayland = data; + + g_assert(release == display_wayland->buffer_release); + g_assert(display_wayland->release_fence_fd == -1); + + zwp_linux_buffer_release_v1_destroy(display_wayland->buffer_release); + display_wayland->buffer_release = NULL; +} + +static int +create_egl_fence_fd(EGLDisplay dpy) +{ + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, + NULL); + int fd; + + g_assert(sync != EGL_NO_SYNC_KHR); + fd = eglDupNativeFenceFDANDROID(dpy, sync); + g_assert(fd >= 0); + + eglDestroySyncKHR(dpy, sync); + + return fd; +} + +static void +wait_for_buffer_release_fence(GdkWaylandDisplay *display_wayland) +{ + int ret; + EGLDisplay dpy = display_wayland->egl_display; + EGLint attrib_list[] = { + EGL_SYNC_NATIVE_FENCE_FD_ANDROID, display_wayland->release_fence_fd, + EGL_NONE, + }; + + EGLSyncKHR sync = eglCreateSyncKHR(dpy, + EGL_SYNC_NATIVE_FENCE_ANDROID, + attrib_list); + g_assert(sync); + display_wayland->release_fence_fd = -1; + + ret = eglClientWaitSyncKHR(dpy, sync, 0, + EGL_FOREVER_KHR); + g_assert(ret == EGL_TRUE); + + ret = eglDestroySyncKHR(dpy, sync); + g_assert(ret == EGL_TRUE); +} + +static const struct zwp_linux_buffer_release_v1_listener buffer_release_listener = { + buffer_fenced_release, + buffer_immediate_release, +}; + static void gdk_wayland_gl_context_end_frame (GdkDrawContext *draw_context, cairo_region_t *painted) @@ -279,6 +354,21 @@ gdk_wayland_gl_context_end_frame (GdkDrawContext *draw_context, gdk_wayland_surface_sync (surface); gdk_wayland_surface_request_frame (surface); + if (display->use_explicit_sync && display_wayland->surface_sync && + display_wayland->have_egl_khr_fence_sync && + display_wayland->have_egl_android_native_fence_sync) + { + int fence_fd = create_egl_fence_fd (display_wayland->egl_display); + zwp_linux_surface_synchronization_v1_set_acquire_fence (display_wayland->surface_sync, + fence_fd); + close(fence_fd); + + display_wayland->buffer_release = zwp_linux_surface_synchronization_v1_get_release( + display_wayland->surface_sync); + zwp_linux_buffer_release_v1_add_listener(display_wayland->buffer_release, + &buffer_release_listener, display_wayland); + } + gdk_profiler_add_mark (GDK_PROFILER_CURRENT_TIME, 0, "wayland", "swap buffers"); if (display_wayland->have_egl_swap_buffers_with_damage) { @@ -406,6 +496,12 @@ gdk_wayland_display_init_gl (GdkDisplay *display) display_wayland->have_egl_surfaceless_context = epoxy_has_egl_extension (dpy, "EGL_KHR_surfaceless_context"); + display_wayland->have_egl_khr_fence_sync = + epoxy_has_egl_extension (dpy, "EGL_KHR_fence_sync"); + + display_wayland->have_egl_android_native_fence_sync = + epoxy_has_egl_extension (dpy, "EGL_ANDROID_native_fence_sync"); + GDK_DISPLAY_NOTE (display, OPENGL, g_message ("EGL API version %d.%d found\n" " - Vendor: %s\n" @@ -508,6 +604,14 @@ gdk_wayland_surface_create_gl_context (GdkSurface *surface, context->egl_config = config; context->is_attached = attached; + if (!display_wayland->surface_sync) + { + display_wayland->surface_sync = zwp_linux_explicit_synchronization_v1_get_synchronization( + display_wayland->explicit_sync, + gdk_wayland_surface_get_wl_surface(surface)); + display_wayland->release_fence_fd = -1; + } + return GDK_GL_CONTEXT (context); } @@ -576,3 +680,12 @@ gdk_wayland_display_make_gl_context_current (GdkDisplay *display, return TRUE; } + +void +gdk_wayland_display_explicit_sync (GdkDisplay *display) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + + if (display_wayland->release_fence_fd >= 0) + wait_for_buffer_release_fence(display_wayland); +} diff --git a/gdk/wayland/gdkglcontext-wayland.h b/gdk/wayland/gdkglcontext-wayland.h index 48966b90ad1..40da8f4b799 100644 --- a/gdk/wayland/gdkglcontext-wayland.h +++ b/gdk/wayland/gdkglcontext-wayland.h @@ -52,6 +52,7 @@ GdkGLContext * gdk_wayland_surface_create_gl_context (GdkSurface GError **error); gboolean gdk_wayland_display_make_gl_context_current (GdkDisplay *display, GdkGLContext *context); +void gdk_wayland_display_explicit_sync (GdkDisplay *display); G_END_DECLS diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index 833945f6662..e773f4b065d 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -56,6 +56,7 @@ proto_sources = [ ['server-decoration', 'private' ], ['xdg-output', 'unstable', 'v1', ], ['idle-inhibit', 'unstable', 'v1', ], + ['linux-explicit-synchronization', 'unstable', 'v1', ], ] gdk_wayland_gen_headers = [] diff --git a/gtk/gtkglarea.c b/gtk/gtkglarea.c index 93c8a7a52b2..f099cf1bcf2 100644 --- a/gtk/gtkglarea.c +++ b/gtk/gtkglarea.c @@ -167,6 +167,7 @@ typedef struct { gboolean needs_render; gboolean auto_render; gboolean use_es; + gboolean use_explicit_sync; } GtkGLAreaPrivate; enum { @@ -176,6 +177,7 @@ enum { PROP_HAS_DEPTH_BUFFER, PROP_HAS_STENCIL_BUFFER, PROP_USE_ES, + PROP_USE_EXPLICIT_SYNC, PROP_AUTO_RENDER, @@ -225,6 +227,10 @@ gtk_gl_area_set_property (GObject *gobject, gtk_gl_area_set_use_es (self, g_value_get_boolean (value)); break; + case PROP_USE_EXPLICIT_SYNC: + gtk_gl_area_set_use_explicit_sync (self, g_value_get_boolean (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -260,6 +266,10 @@ gtk_gl_area_get_property (GObject *gobject, g_value_set_boolean (value, priv->use_es); break; + case PROP_USE_EXPLICIT_SYNC: + g_value_set_boolean (value, priv->use_explicit_sync); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } @@ -873,6 +883,21 @@ gtk_gl_area_class_init (GtkGLAreaClass *klass) G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + /** + * GtkGLArea:use-explicit_sync: (attributes org.gtk.Property.get=gtk_gl_area_get_use_explicit_sync org.gtk.Property.set=gtk_gl_area_set_use_explicit_sync) + * + * If set to %TRUE there will be a synchronization primitive/object created + * associated with previously submitted rendering commands that can be waited upon. + */ + obj_props[PROP_USE_EXPLICIT_SYNC] = + g_param_spec_boolean ("use-explicit-sync", + P_("Use Explicit Synchronization"), + P_("Whether a synch object needs to be created"), + FALSE, + GTK_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_EXPLICIT_NOTIFY); + gobject_class->set_property = gtk_gl_area_set_property; gobject_class->get_property = gtk_gl_area_get_property; gobject_class->notify = gtk_gl_area_notify; @@ -1351,3 +1376,71 @@ gtk_gl_area_make_current (GtkGLArea *area) if (priv->context != NULL) gdk_gl_context_make_current (priv->context); } + +/** + * gtk_gl_area_set_use_explicit_sync: (attributes org.gtk.Method.set_property=use-explicit-sync) + * @area: a `GtkGLArea` + * @use_explicit_sync: Whether a synch object needs to be created + * + * Sets whether the rendering associated with @area needs to be explicitly + * syncronized or not. + */ +void +gtk_gl_area_set_use_explicit_sync (GtkGLArea *area, + gboolean use_explicit_sync) +{ + GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_GL_AREA (area)); + g_return_if_fail (!gtk_widget_get_realized (GTK_WIDGET (area))); + + use_explicit_sync = !!use_explicit_sync; + + if (priv->use_explicit_sync != use_explicit_sync) + { + priv->use_explicit_sync = use_explicit_sync; + + g_object_notify_by_pspec (G_OBJECT (area), obj_props[PROP_USE_EXPLICIT_SYNC]); + } +} + +/** + * gtk_gl_area_get_use_explicit_sync: (attributes org.gtk.Method.get_property=use-explicit-sync) + * @area: a `GtkGLArea` + * + * Returns whether explicit synchronization is needed for `GtkGLArea. + * + * See [method@Gtk.GLArea.set_use_explicit_sync]. + * + * Returns: %TRUE if a sync object will be created for `GtkGLArea` + * and %FALSE otherwise + */ +gboolean +gtk_gl_area_get_use_explicit_sync (GtkGLArea *area) +{ + GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area); + + g_return_val_if_fail (GTK_IS_GL_AREA (area), FALSE); + + return priv->use_explicit_sync; +} + + +/** + * gtk_gl_area_explicit_sync: + * @area: a `GtkGLArea` + * + * Ensures that the previously submitted buffer is used up by the + * windowing system. + */ +void +gtk_gl_area_explicit_sync (GtkGLArea *area) +{ + GtkGLAreaPrivate *priv = gtk_gl_area_get_instance_private (area); + + g_return_if_fail (GTK_IS_GL_AREA (area)); + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (area))); + + if (priv->context != NULL) + gdk_gl_context_explicit_sync (priv->context, priv->use_explicit_sync); +} diff --git a/gtk/gtkglarea.h b/gtk/gtkglarea.h index b7bf7a6c095..8407b2fc825 100644 --- a/gtk/gtkglarea.h +++ b/gtk/gtkglarea.h @@ -122,6 +122,14 @@ void gtk_gl_area_set_error (GtkGLArea *area, GDK_AVAILABLE_IN_ALL GError * gtk_gl_area_get_error (GtkGLArea *area); +GDK_AVAILABLE_IN_ALL +void gtk_gl_area_set_use_explicit_sync (GtkGLArea *area, + gboolean use_explicit_sync); +GDK_AVAILABLE_IN_ALL +gboolean gtk_gl_area_get_use_explicit_sync (GtkGLArea *area); +GDK_AVAILABLE_IN_ALL +void gtk_gl_area_explicit_sync (GtkGLArea *area); + G_END_DECLS #endif /* __GTK_GL_AREA_H__ */ -- GitLab