mirror of
https://gitlab.gnome.org/GNOME/libmks.git
synced 2024-12-22 13:45:21 +00:00
640587ed0f
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
264 lines
8.0 KiB
C
264 lines
8.0 KiB
C
/*
|
|
* mks-dmabuf-paintable.c
|
|
*
|
|
* Copyright 2023 Christian Hergert <chergert@redhat.com>
|
|
* Copyright 2023 Bilal Elmoussaoui <belmouss@redhat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation; either version 2.1 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "mks-dmabuf-paintable-private.h"
|
|
#include "mks-gl-context-private.h"
|
|
|
|
/*
|
|
* MksDmabufPaintable is a GdkPaintable that gets created the first time
|
|
* `ScanoutDMABUF` is called.
|
|
*
|
|
* The scanout data is then stored until we receive a `UpdateDMABUF` call
|
|
* so we can pass the damage region to `GdkGLTextureBuilder`.
|
|
*/
|
|
|
|
typedef struct _MksDmabufTextureData
|
|
{
|
|
GdkGLContext *gl_context;
|
|
GLuint texture_id;
|
|
} MksDmabufTextureData;
|
|
|
|
struct _MksDmabufPaintable
|
|
{
|
|
GObject parent_instance;
|
|
GdkTexture *texture;
|
|
GdkGLTextureBuilder *builder;
|
|
guint width;
|
|
guint height;
|
|
};
|
|
|
|
static MksDmabufTextureData *
|
|
mks_dmabuf_texture_data_new (GdkGLContext *gl_context,
|
|
GLuint texture_id)
|
|
{
|
|
MksDmabufTextureData *texture_data;
|
|
|
|
g_assert (GDK_IS_GL_CONTEXT (gl_context));
|
|
g_assert (texture_id > 0);
|
|
|
|
texture_data = g_new0 (MksDmabufTextureData, 1);
|
|
texture_data->gl_context = g_object_ref (gl_context);
|
|
texture_data->texture_id = texture_id;
|
|
|
|
return texture_data;
|
|
}
|
|
|
|
static void
|
|
mks_dmabuf_texture_data_free (gpointer data)
|
|
{
|
|
MksDmabufTextureData *texture_data = data;
|
|
|
|
gdk_gl_context_make_current (texture_data->gl_context);
|
|
glDeleteTextures (1, &texture_data->texture_id);
|
|
|
|
texture_data->texture_id = 0;
|
|
g_clear_object (&texture_data->gl_context);
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
mks_dmabuf_paintable_class_init (MksDmabufPaintableClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->dispose = mks_dmabuf_paintable_dispose;
|
|
}
|
|
|
|
static void
|
|
mks_dmabuf_paintable_init (MksDmabufPaintable *self)
|
|
{
|
|
}
|
|
|
|
gboolean
|
|
mks_dmabuf_paintable_import (MksDmabufPaintable *self,
|
|
GdkGLContext *gl_context,
|
|
MksDmabufScanoutData *data,
|
|
cairo_region_t *region,
|
|
GError **error)
|
|
{
|
|
cairo_region_t *accumulated_damages;
|
|
cairo_region_t *previous_region;
|
|
GLuint texture_id;
|
|
guint zero = 0;
|
|
|
|
g_return_val_if_fail (MKS_IS_DMABUF_PAINTABLE (self), FALSE);
|
|
g_return_val_if_fail (!gl_context || GDK_IS_GL_CONTEXT (gl_context), FALSE);
|
|
|
|
if (data->dmabuf_fd < 0)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_ARGUMENT,
|
|
"invalid dmabuf_fd (%d)",
|
|
data->dmabuf_fd);
|
|
return FALSE;
|
|
}
|
|
|
|
if (data->width == 0 || data->height == 0 || data->stride == 0)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_ARGUMENT,
|
|
"invalid width/height/stride (%u/%u/%u)",
|
|
data->width, data->height, data->stride);
|
|
return FALSE;
|
|
}
|
|
|
|
if (self->width != data->width || self->height != data->height)
|
|
{
|
|
self->width = data->width;
|
|
self->height = data->height;
|
|
gdk_paintable_invalidate_size (GDK_PAINTABLE (self));
|
|
}
|
|
|
|
if (!(texture_id = mks_gl_context_import_dmabuf (gl_context,
|
|
data->fourcc, data->width, data->height,
|
|
1, &data->dmabuf_fd, &data->stride, &zero,
|
|
&data->modifier)))
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_FAILED,
|
|
"Failed to import dmabuf into GL texture");
|
|
return FALSE;
|
|
}
|
|
|
|
accumulated_damages = cairo_region_create ();
|
|
|
|
if (region != NULL)
|
|
cairo_region_union (accumulated_damages, region);
|
|
|
|
if (self->builder != NULL)
|
|
{
|
|
previous_region = gdk_gl_texture_builder_get_update_region (self->builder);
|
|
if (previous_region != NULL)
|
|
cairo_region_union (accumulated_damages, previous_region);
|
|
}
|
|
|
|
g_clear_object (&self->builder);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
MksDmabufPaintable *
|
|
mks_dmabuf_paintable_new (void)
|
|
{
|
|
g_autoptr(MksDmabufPaintable) self = NULL;
|
|
|
|
self = g_object_new (MKS_TYPE_DMABUF_PAINTABLE, NULL);
|
|
self->width = 0;
|
|
self->height = 0;
|
|
return g_steal_pointer (&self);
|
|
}
|