libmks/lib/mks-dmabuf-paintable.c
Bilal Elmoussaoui c259212be5 dmabuf: Properly propagate damage area
The current approach makes use of
- A tiled rendering to work around gdk_texture_diff only doing pointer
comparaison
- Assumes that we would only recieve a scanout cmd followed by multiple
flush ones

In reality, with virtio-gpu at least, the scanout cmd is always
submitted followed by a flush one containing the damaged region.

With the assumption currently made, we end up creating a new paintable
for every scanout cmd causing a full redraw instead
of only redrawing the damaged areas.
Isntead we create the paintable once and call import whenever
we receive a flush cmd (UpdateDMABUF) so we can properly
pass the damage area when creating a GdkGLTexture making
the tiled rendering no longer needed.
2023-05-17 13:08:37 -07:00

254 lines
7.5 KiB
C

/* mks-dmabuf-paintable.c
*
* Copyright 2023 Christian Hergert <chergert@redhat.com>
* Copyright 2023 Bilal Elmoussaoui <belmouss@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 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: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "mks-dmabuf-paintable"
#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`.
*/
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)
{
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))
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_atomic_rc_box_new0 (MksDmabufTextureData);
texture_data->gl_context = g_object_ref (gl_context);
texture_data->texture_id = texture_id;
return texture_data;
}
static MksDmabufTextureData *
mks_dmabuf_texture_data_ref (MksDmabufTextureData *texture_data)
{
return g_atomic_rc_box_acquire (texture_data);
}
static void
mks_dmabuf_texture_data_finalize (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);
}
static void
mks_dmabuf_texture_data_unref (MksDmabufTextureData *texture_data)
{
g_atomic_rc_box_release_full (texture_data, mks_dmabuf_texture_data_finalize);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MksDmabufTextureData, mks_dmabuf_texture_data_unref)
static void
mks_dmabuf_paintable_dispose (GObject *object)
{
MksDmabufPaintable *self = (MksDmabufPaintable *)object;
g_clear_object (&self->texture);
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)
{
g_autoptr(MksDmabufTextureData) texture_data = NULL;
GLuint texture_id;
g_autoptr(GdkGLTextureBuilder) builder = NULL;
guint zero = 0;
g_return_val_if_fail (MKS_IS_DMABUF_PAINTABLE (self), 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;
}
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);
if (region)
{
gdk_gl_texture_builder_set_update_region (builder, region);
gdk_gl_texture_builder_set_update_texture (builder, self->texture);
}
texture_data = mks_dmabuf_texture_data_new (gl_context, texture_id);
g_clear_object (&self->texture);
self->texture = gdk_gl_texture_builder_build (builder,
(GDestroyNotify) mks_dmabuf_texture_data_unref,
mks_dmabuf_texture_data_ref (texture_data)
);
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);
}