dmabuf paintable: Switch to GdkDmabufTexture

This commit is contained in:
Bilal Elmoussaoui 2023-10-02 16:51:17 +02:00
parent e295c48ccd
commit bdcd59fd6c
12 changed files with 50 additions and 325 deletions

View File

@ -13,7 +13,7 @@ tests:
variables:
MESON_FLAGS: "-Db_coverage=true -Ddocs=false -Dvapi=false -Dintrospection=disabled"
before_script:
- sudo dnf install -y git gtk4-devel libepoxy-devel meson gcc lcov wget
- sudo dnf install -y git gtk4-devel meson gcc lcov wget
qemu-system-x86 qemu-ui-dbus qemu-ui-opengl
dbus-x11 xorg-x11-server-Xvfb
script:
@ -46,7 +46,7 @@ reference:
variables:
MESON_FLAGS: "--buildtype=release -Ddocs=true -Dintrospection=enabled"
before_script:
- sudo dnf install -y git gtk4-devel libepoxy-devel meson gcc
- sudo dnf install -y git gtk4-devel meson gcc
gi-docgen gobject-introspection-devel vala
script:
- mkdir -p pfx/

View File

@ -45,7 +45,6 @@ libmks_private_sources = [
'mks-dmabuf-paintable.c',
'mks-display-picture.c',
'mks-css.c',
'mks-gl-context.c',
'mks-inhibitor.c',
'mks-read-only-list-model.c',
'mks-screen-resizer.c',
@ -120,7 +119,6 @@ libmks_private_generated_headers = [
]
libmks_deps = [
libepoxy_dep,
libgio_dep,
libgiounix_dep,
libgtk_dep,

View File

@ -145,6 +145,7 @@ mks_display_connect (MksDisplay *self,
mks_screen_resizer_set_screen (priv->resizer, screen);
mks_screen_attach (screen,
gtk_widget_get_display (GTK_WIDGET (self)),
NULL,
mks_display_attach_cb,
g_object_ref (self));

View File

@ -42,7 +42,7 @@ G_DECLARE_FINAL_TYPE (MksDmabufPaintable, mks_dmabuf_paintable, MKS, DMABUF_PAIN
MksDmabufPaintable *mks_dmabuf_paintable_new (void);
gboolean mks_dmabuf_paintable_import (MksDmabufPaintable *self,
GdkGLContext *gl_context,
GdkDisplay *display,
MksDmabufScanoutData *data,
cairo_region_t *region,
GError **error);

View File

@ -25,62 +25,25 @@
#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`.
* so we can pass the damage region to `GdkDmabufTextureBuilder`.
*/
typedef struct _MksDmabufTextureData
{
GdkGLContext *gl_context;
GLuint texture_id;
} MksDmabufTextureData;
struct _MksDmabufPaintable
{
GObject parent_instance;
GdkTexture *texture;
GdkGLTextureBuilder *builder;
GdkDmabufTextureBuilder *builder;
guint width;
guint height;
guint dmabuf_updated : 1;
};
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)
{
@ -112,8 +75,7 @@ mks_dmabuf_paintable_snapshot (GdkPaintable *paintable,
{
MksDmabufPaintable *self = (MksDmabufPaintable *)paintable;
g_autoptr(GdkTexture) texture = NULL;
GdkGLContext *gl_context;
GLuint texture_id;
g_autoptr(GError) error = NULL;
graphene_rect_t area;
g_assert (MKS_IS_DMABUF_PAINTABLE (self));
@ -122,21 +84,22 @@ mks_dmabuf_paintable_snapshot (GdkPaintable *paintable,
/**
* If the widget gets resized, snapshot would be called even
* if we didn't receive a new DMABufUpdate call.
* So only create a new GLTexture when that happens
* So only create a new DmabufTexture when that happens
*/
if (self->dmabuf_updated)
{
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));
gdk_dmabuf_texture_builder_set_update_texture (self->builder, self->texture);
texture = gdk_dmabuf_texture_builder_build (self->builder,
NULL, NULL, &error);
if (error != NULL)
{
g_warning ("Failed to build texture: %s", error->message);
return;
}
g_assert (texture != NULL);
// Clear up the update region to not union it with the next UpdateDMABuf call
gdk_gl_texture_builder_set_update_region (self->builder, NULL);
gdk_dmabuf_texture_builder_set_update_region (self->builder, NULL);
g_set_object (&self->texture, texture);
self->dmabuf_updated = FALSE;
}
@ -183,18 +146,16 @@ mks_dmabuf_paintable_init (MksDmabufPaintable *self)
gboolean
mks_dmabuf_paintable_import (MksDmabufPaintable *self,
GdkGLContext *gl_context,
GdkDisplay *display,
MksDmabufScanoutData *data,
cairo_region_t *region,
GError **error)
{
cairo_region_t *accumulated_damages;
cairo_region_t *previous_region;
GLuint texture_id;
guint zero = 0;
guint plane = 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)
{
@ -223,18 +184,6 @@ mks_dmabuf_paintable_import (MksDmabufPaintable *self,
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)
@ -242,22 +191,27 @@ mks_dmabuf_paintable_import (MksDmabufPaintable *self,
if (self->builder != NULL)
{
previous_region = gdk_gl_texture_builder_get_update_region (self->builder);
previous_region = gdk_dmabuf_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);
self->builder = gdk_dmabuf_texture_builder_new ();
gdk_dmabuf_texture_builder_set_modifier (self->builder, data->modifier);
gdk_dmabuf_texture_builder_set_stride (self->builder, plane, data->stride);
gdk_dmabuf_texture_builder_set_fourcc (self->builder, data->fourcc);
gdk_dmabuf_texture_builder_set_width (self->builder, data->width);
gdk_dmabuf_texture_builder_set_height (self->builder, data->height);
gdk_dmabuf_texture_builder_set_fd (self->builder, plane, data->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (self->builder, plane, 0);
gdk_dmabuf_texture_builder_set_display (self->builder, display);
gdk_dmabuf_texture_builder_set_n_planes (self->builder, 1);
if (cairo_region_num_rectangles (accumulated_damages) > 0)
gdk_gl_texture_builder_set_update_region (self->builder,
accumulated_damages);
gdk_dmabuf_texture_builder_set_update_region (self->builder,
accumulated_damages);
g_clear_pointer (&accumulated_damages, cairo_region_destroy);
self->dmabuf_updated = TRUE;

View File

@ -1,39 +0,0 @@
/*
* mks-gl-context-private.h
*
* Copyright 2023 Christian Hergert <chergert@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
*/
#pragma once
#include <epoxy/egl.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
GLuint mks_gl_context_import_dmabuf (GdkGLContext *context,
guint32 format,
guint width,
guint height,
guint32 n_planes,
const int *fds,
const guint32 *strides,
const guint32 *offsets,
const guint64 *modifiers);
G_END_DECLS

View File

@ -1,176 +0,0 @@
/*
* mks-gl-context.c
*
* Copyright 2023 Christian Hergert <chergert@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 <gdk/gdk.h>
#ifdef GDK_WINDOWING_WAYLAND
# include <gdk/wayland/gdkwayland.h>
#endif
#ifdef GDK_WINDOWING_X11
# include <gdk/x11/gdkx.h>
#endif
#include "mks-gl-context-private.h"
GLuint
mks_gl_context_import_dmabuf (GdkGLContext *context,
guint32 format,
guint width,
guint height,
guint32 n_planes,
const int *fds,
const guint32 *strides,
const guint32 *offsets,
const guint64 *modifiers)
{
GdkDisplay *display;
EGLDisplay egl_display;
EGLint attribs[2 * (3 + 4 * 5) + 1];
EGLImage image;
guint texture_id;
int i;
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), 0);
g_return_val_if_fail (0 < n_planes && n_planes <= 4, 0);
display = gdk_gl_context_get_display (context);
#ifdef GDK_WINDOWING_WAYLAND
if (GDK_IS_WAYLAND_DISPLAY (display))
egl_display = gdk_wayland_display_get_egl_display (display);
else
#endif
#ifdef GDK_WINDOWING_X11
if (GDK_IS_X11_DISPLAY (display))
egl_display = gdk_x11_display_get_egl_display (display);
else
#endif
egl_display = NULL;
if (egl_display == NULL)
{
g_warning ("Can't import dmabufs when not using EGL");
return 0;
}
i = 0;
attribs[i++] = EGL_WIDTH;
attribs[i++] = width;
attribs[i++] = EGL_HEIGHT;
attribs[i++] = height;
attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[i++] = format;
if (n_planes > 0)
{
attribs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT;
attribs[i++] = fds[0];
attribs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
attribs[i++] = offsets[0];
attribs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
attribs[i++] = strides[0];
if (modifiers)
{
attribs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
attribs[i++] = modifiers[0] & 0xFFFFFFFF;
attribs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
attribs[i++] = modifiers[0] >> 32;
}
}
if (n_planes > 1)
{
attribs[i++] = EGL_DMA_BUF_PLANE1_FD_EXT;
attribs[i++] = fds[1];
attribs[i++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
attribs[i++] = offsets[1];
attribs[i++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
attribs[i++] = strides[1];
if (modifiers)
{
attribs[i++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
attribs[i++] = modifiers[1] & 0xFFFFFFFF;
attribs[i++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
attribs[i++] = modifiers[1] >> 32;
}
}
if (n_planes > 2)
{
attribs[i++] = EGL_DMA_BUF_PLANE2_FD_EXT;
attribs[i++] = fds[2];
attribs[i++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
attribs[i++] = offsets[2];
attribs[i++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
attribs[i++] = strides[2];
if (modifiers)
{
attribs[i++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
attribs[i++] = modifiers[2] & 0xFFFFFFFF;
attribs[i++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
attribs[i++] = modifiers[2] >> 32;
}
}
if (n_planes > 3)
{
attribs[i++] = EGL_DMA_BUF_PLANE3_FD_EXT;
attribs[i++] = fds[3];
attribs[i++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
attribs[i++] = offsets[3];
attribs[i++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
attribs[i++] = strides[3];
if (modifiers)
{
attribs[i++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
attribs[i++] = modifiers[3] & 0xFFFFFFFF;
attribs[i++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
attribs[i++] = modifiers[3] >> 32;
}
}
attribs[i++] = EGL_NONE;
image = eglCreateImageKHR (egl_display,
EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT,
(EGLClientBuffer)NULL,
attribs);
if (image == EGL_NO_IMAGE)
{
g_warning ("Failed to create EGL image: %d\n", eglGetError ());
return 0;
}
gdk_gl_context_make_current (context);
glGenTextures (1, &texture_id);
glBindTexture (GL_TEXTURE_2D, texture_id);
glEGLImageTargetTexture2DOES (GL_TEXTURE_2D, image);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
eglDestroyImageKHR (egl_display, image);
return texture_id;
}

View File

@ -29,7 +29,8 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (MksPaintable, mks_paintable, MKS, PAINTABLE, GObject)
GdkPaintable *_mks_paintable_new (GCancellable *cancellable,
GdkPaintable *_mks_paintable_new (GdkDisplay *display,
GCancellable *cancellable,
int *peer_fd,
GError **error);
GdkCursor *_mks_paintable_get_cursor (MksPaintable *self);

View File

@ -39,9 +39,9 @@
struct _MksPaintable
{
GObject parent_instance;
GdkGLContext *gl_context;
MksQemuListener *listener;
GDBusConnection *connection;
GdkDisplay *display;
GdkPaintable *child;
GdkCursor *cursor;
MksDmabufScanoutData *scanout_data;
@ -155,23 +155,6 @@ paintable_iface_init (GdkPaintableInterface *iface)
G_DEFINE_FINAL_TYPE_WITH_CODE (MksPaintable, mks_paintable, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE, paintable_iface_init))
static GdkGLContext *
mks_paintable_get_gl_context (MksPaintable *self,
GError **error)
{
g_assert (MKS_IS_PAINTABLE (self));
if (self->gl_context == NULL)
{
GdkDisplay *display = gdk_display_get_default ();
if (!(self->gl_context = gdk_display_create_gl_context (display, error)))
return NULL;
}
return self->gl_context;
}
static void
mks_paintable_dispose (GObject *object)
{
@ -180,8 +163,8 @@ mks_paintable_dispose (GObject *object)
g_clear_object (&self->connection);
g_clear_object (&self->listener);
g_clear_object (&self->child);
g_clear_object (&self->gl_context);
g_clear_object (&self->cursor);
g_clear_object (&self->display);
G_OBJECT_CLASS (mks_paintable_parent_class)->dispose (object);
}
@ -396,7 +379,6 @@ mks_paintable_listener_update_dmabuf (MksPaintable *self,
{
cairo_region_t *region = NULL;
g_autoptr(GError) error = NULL;
GdkGLContext *gl_context;
g_assert (MKS_IS_PAINTABLE (self));
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
@ -409,9 +391,8 @@ mks_paintable_listener_update_dmabuf (MksPaintable *self,
y = self->scanout_data->height - y - height;
region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { x, y, width, height });
if (!(gl_context = mks_paintable_get_gl_context (self, &error)) ||
!mks_dmabuf_paintable_import (MKS_DMABUF_PAINTABLE (self->child),
gl_context,
if (!mks_dmabuf_paintable_import (MKS_DMABUF_PAINTABLE (self->child),
self->display,
self->scanout_data,
region,
&error))
@ -705,7 +686,8 @@ mks_paintable_connection_cb (GObject *object,
}
GdkPaintable *
_mks_paintable_new (GCancellable *cancellable,
_mks_paintable_new (GdkDisplay *display,
GCancellable *cancellable,
int *peer_fd,
GError **error)
{
@ -721,7 +703,7 @@ _mks_paintable_new (GCancellable *cancellable,
*peer_fd = -1;
self = g_object_new (MKS_TYPE_PAINTABLE, NULL);
self->display = g_object_ref (display);
/* Create a socketpair() to use for D-Bus P2P protocol. We will be receiving
* DMA-BUF FDs over this.
*/

View File

@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "mks-device-private.h"
#include "mks-enums.h"
@ -637,6 +638,7 @@ mks_screen_attach_cb (GObject *object,
*/
void
mks_screen_attach (MksScreen *self,
GdkDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@ -654,7 +656,7 @@ mks_screen_attach (MksScreen *self,
g_task_set_source_tag (task, mks_screen_attach);
if (!check_console (self, &error) ||
!(paintable = _mks_paintable_new (cancellable, &fd, &error)))
!(paintable = _mks_paintable_new (display, cancellable, &fd, &error)))
goto failure;
g_task_set_task_data (task, g_steal_pointer (&paintable), g_object_unref);
@ -714,6 +716,7 @@ mks_screen_attach_finish (MksScreen *self,
*/
GdkPaintable *
mks_screen_attach_sync (MksScreen *self,
GdkDisplay *display,
GCancellable *cancellable,
GError **error)
{
@ -725,7 +728,7 @@ mks_screen_attach_sync (MksScreen *self,
g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), NULL);
if (!check_console (self, error) ||
!(paintable = _mks_paintable_new (cancellable, &fd, error)))
!(paintable = _mks_paintable_new (display, cancellable, &fd, error)))
return NULL;
unix_fd_list = g_unix_fd_list_new_from_array (&fd, 1), fd = -1;

View File

@ -91,6 +91,7 @@ gboolean mks_screen_configure_sync (MksScreen *self,
GError **error);
MKS_AVAILABLE_IN_ALL
void mks_screen_attach (MksScreen *self,
GdkDisplay *display,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@ -100,6 +101,7 @@ GdkPaintable *mks_screen_attach_finish (MksScreen *self,
GError **error);
MKS_AVAILABLE_IN_ALL
GdkPaintable *mks_screen_attach_sync (MksScreen *self,
GdkDisplay *display,
GCancellable *cancellable,
GError **error);

View File

@ -23,7 +23,7 @@ add_project_arguments(['-I' + meson.project_build_root()], language: 'c')
# Check dependencies
glib_req_version = '2.75.0'
gtk_req_version = '4.11'
gtk_req_version = '4.13'
glib_req = '>= @0@'.format(glib_req_version)
gtk_req = '>= @0@'.format(gtk_req_version)
@ -40,7 +40,6 @@ if gtk_minor % 2 == 1
gtk_minor = gtk_minor + 1
endif
libepoxy_dep = dependency('epoxy')
libgio_dep = dependency('gio-2.0', version: glib_req)
libgiounix_dep = dependency('gio-unix-2.0', version: glib_req)
libgtk_dep = dependency('gtk4', version: gtk_req, fallback: ['gtk'])