From 640587ed0f4ef1d45497ae5add3b57388b26c34d Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Tue, 15 Aug 2023 10:56:10 +0200 Subject: [PATCH] dmabuf-paintable: Build the texture when snapshot is called Always creating the texture even if the app is not being displayed (minimized / different virtual monitor) or if the GdkFrameClock drops a frame we end up doing a comparison with a very old frame causing full redraws in certain cases or even artifacts in others. Instead, we only build the texture once snapshot is called and accumulate the damage area until that happens to avoid updating the wrong area --- lib/mks-dmabuf-paintable.c | 160 +++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 68 deletions(-) diff --git a/lib/mks-dmabuf-paintable.c b/lib/mks-dmabuf-paintable.c index 5131ffe..b77d9cc 100644 --- a/lib/mks-dmabuf-paintable.c +++ b/lib/mks-dmabuf-paintable.c @@ -35,67 +35,20 @@ * so we can pass the damage region to `GdkGLTextureBuilder`. */ -struct _MksDmabufPaintable -{ - GObject parent_instance; - GdkTexture *texture; - guint width; - guint height; -}; - typedef struct _MksDmabufTextureData { GdkGLContext *gl_context; GLuint texture_id; } MksDmabufTextureData; -static int -mks_dmabuf_paintable_get_intrinsic_width (GdkPaintable *paintable) +struct _MksDmabufPaintable { - return MKS_DMABUF_PAINTABLE (paintable)->width; -} - -static int -mks_dmabuf_paintable_get_intrinsic_height (GdkPaintable *paintable) -{ - return MKS_DMABUF_PAINTABLE (paintable)->height; -} - -static double -mks_dmabuf_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) -{ - MksDmabufPaintable *self = MKS_DMABUF_PAINTABLE (paintable); - - return (double)self->width / (double)self->height; -} - -static void -mks_dmabuf_paintable_snapshot (GdkPaintable *paintable, - GdkSnapshot *snapshot, - double width, - double height) -{ - MksDmabufPaintable *self = (MksDmabufPaintable *)paintable; - graphene_rect_t area; - - g_assert (MKS_IS_DMABUF_PAINTABLE (self)); - g_assert (GDK_IS_SNAPSHOT (snapshot)); - - area = GRAPHENE_RECT_INIT (0, 0, width, height); - gtk_snapshot_append_texture (snapshot, self->texture, &area); -} - -static void -paintable_iface_init (GdkPaintableInterface *iface) -{ - iface->get_intrinsic_width = mks_dmabuf_paintable_get_intrinsic_width; - iface->get_intrinsic_height = mks_dmabuf_paintable_get_intrinsic_height; - iface->get_intrinsic_aspect_ratio = mks_dmabuf_paintable_get_intrinsic_aspect_ratio; - iface->snapshot = mks_dmabuf_paintable_snapshot; -} - -G_DEFINE_FINAL_TYPE_WITH_CODE (MksDmabufPaintable, mks_dmabuf_paintable, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, paintable_iface_init)) + GObject parent_instance; + GdkTexture *texture; + GdkGLTextureBuilder *builder; + guint width; + guint height; +}; static MksDmabufTextureData * mks_dmabuf_texture_data_new (GdkGLContext *gl_context, @@ -127,12 +80,76 @@ mks_dmabuf_texture_data_free (gpointer data) g_free (texture_data); } +static int +mks_dmabuf_paintable_get_intrinsic_width (GdkPaintable *paintable) +{ + return MKS_DMABUF_PAINTABLE (paintable)->width; +} + +static int +mks_dmabuf_paintable_get_intrinsic_height (GdkPaintable *paintable) +{ + return MKS_DMABUF_PAINTABLE (paintable)->height; +} + +static double +mks_dmabuf_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) +{ + MksDmabufPaintable *self = MKS_DMABUF_PAINTABLE (paintable); + + return (double)self->width / (double)self->height; +} + +static void +mks_dmabuf_paintable_snapshot (GdkPaintable *paintable, + GdkSnapshot *snapshot, + double width, + double height) +{ + MksDmabufPaintable *self = (MksDmabufPaintable *)paintable; + g_autoptr(GdkTexture) texture = NULL; + GdkGLContext *gl_context; + GLuint texture_id; + graphene_rect_t area; + + g_assert (MKS_IS_DMABUF_PAINTABLE (self)); + g_assert (GDK_IS_SNAPSHOT (snapshot)); + + texture_id = gdk_gl_texture_builder_get_id (self->builder); + gl_context = gdk_gl_texture_builder_get_context (self->builder); + + gdk_gl_texture_builder_set_update_texture (self->builder, self->texture); + texture = gdk_gl_texture_builder_build (self->builder, + mks_dmabuf_texture_data_free, + mks_dmabuf_texture_data_new (gl_context, + texture_id)); + // Clear up the update region to not union it with the next UpdateDMABuf call + gdk_gl_texture_builder_set_update_region (self->builder, NULL); + g_set_object (&self->texture, texture); + + area = GRAPHENE_RECT_INIT (0, 0, width, height); + gtk_snapshot_append_texture (snapshot, self->texture, &area); +} + +static void +paintable_iface_init (GdkPaintableInterface *iface) +{ + iface->get_intrinsic_width = mks_dmabuf_paintable_get_intrinsic_width; + iface->get_intrinsic_height = mks_dmabuf_paintable_get_intrinsic_height; + iface->get_intrinsic_aspect_ratio = mks_dmabuf_paintable_get_intrinsic_aspect_ratio; + iface->snapshot = mks_dmabuf_paintable_snapshot; +} + +G_DEFINE_FINAL_TYPE_WITH_CODE (MksDmabufPaintable, mks_dmabuf_paintable, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, paintable_iface_init)) + static void mks_dmabuf_paintable_dispose (GObject *object) { MksDmabufPaintable *self = (MksDmabufPaintable *)object; g_clear_object (&self->texture); + g_clear_object (&self->builder); G_OBJECT_CLASS (mks_dmabuf_paintable_parent_class)->dispose (object); } @@ -157,8 +174,8 @@ mks_dmabuf_paintable_import (MksDmabufPaintable *self, cairo_region_t *region, GError **error) { - g_autoptr(GdkGLTextureBuilder) builder = NULL; - g_autoptr(GdkTexture) texture = NULL; + cairo_region_t *accumulated_damages; + cairo_region_t *previous_region; GLuint texture_id; guint zero = 0; @@ -204,25 +221,32 @@ mks_dmabuf_paintable_import (MksDmabufPaintable *self, return FALSE; } - builder = gdk_gl_texture_builder_new (); - gdk_gl_texture_builder_set_id (builder, texture_id); - gdk_gl_texture_builder_set_width (builder, self->width); - gdk_gl_texture_builder_set_height (builder, self->height); - gdk_gl_texture_builder_set_context (builder, gl_context); + accumulated_damages = cairo_region_create (); if (region != NULL) + cairo_region_union (accumulated_damages, region); + + if (self->builder != NULL) { - gdk_gl_texture_builder_set_update_region (builder, region); - gdk_gl_texture_builder_set_update_texture (builder, self->texture); + previous_region = gdk_gl_texture_builder_get_update_region (self->builder); + if (previous_region != NULL) + cairo_region_union (accumulated_damages, previous_region); } - texture = gdk_gl_texture_builder_build (builder, - mks_dmabuf_texture_data_free, - mks_dmabuf_texture_data_new (gl_context, texture_id)); + g_clear_object (&self->builder); - g_set_object (&self->texture, texture); + self->builder = gdk_gl_texture_builder_new (); + gdk_gl_texture_builder_set_width (self->builder, self->width); + gdk_gl_texture_builder_set_height (self->builder, self->height); + gdk_gl_texture_builder_set_context (self->builder, gl_context); + gdk_gl_texture_builder_set_id (self->builder, texture_id); + + if (cairo_region_num_rectangles (accumulated_damages) > 0) + gdk_gl_texture_builder_set_update_region (self->builder, + accumulated_damages); + + g_clear_pointer (&accumulated_damages, cairo_region_destroy); gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); - return TRUE; }