diff --git a/lib/meson.build b/lib/meson.build index 70f861e..99e3b39 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -25,7 +25,9 @@ libmks_headers = [ libmks_private_sources = [ 'mks-cairo-framebuffer.c', + 'mks-dmabuf-paintable.c', 'mks-css.c', + 'mks-gl-context.c', 'mks-read-only-list-model.c', gnome.gdbus_codegen('mks-qemu', @@ -72,6 +74,7 @@ libmks_generated_sources = [ ] libmks_deps = [ + libepoxy_dep, libgio_dep, libgiounix_dep, libgtk_dep, diff --git a/lib/mks-dmabuf-paintable-private.h b/lib/mks-dmabuf-paintable-private.h new file mode 100644 index 0000000..ddf68fe --- /dev/null +++ b/lib/mks-dmabuf-paintable-private.h @@ -0,0 +1,46 @@ +/* mks-dmabuf-paintable-private.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define MKS_TYPE_DMABUF_PAINTABLE (mks_dmabuf_paintable_get_type()) + +G_DECLARE_FINAL_TYPE (MksDmabufPaintable, mks_dmabuf_paintable, MKS, DMABUF_PAINTABLE, GObject) + +MksDmabufPaintable *mks_dmabuf_paintable_new (GdkGLContext *gl_context, + int dmabuf_fd, + guint width, + guint height, + guint stride, + guint fourcc, + guint64 modifier, + gboolean y0_top, + GError **error); +void mks_dmabuf_paintable_invalidate (MksDmabufPaintable *self, + guint x, + guint y, + guint width, + guint height); + +G_END_DECLS diff --git a/lib/mks-dmabuf-paintable.c b/lib/mks-dmabuf-paintable.c new file mode 100644 index 0000000..2774664 --- /dev/null +++ b/lib/mks-dmabuf-paintable.c @@ -0,0 +1,298 @@ +/* mks-dmabuf-paintable.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "mks-dmabuf-paintable" + +#include "config.h" + +#include + +#include "mks-dmabuf-paintable-private.h" +#include "mks-gl-context-private.h" + +/* + * MksDmabufPaintable is a GdkPaintable for a single dmabuf_fd which is then + * imported into OpenGL. This has an advantage over just using a single + * GdkGLTexture for such a situation in that we can take advantage of how + * GskGLRenderer and GdkTexture work. + * + * First, by swapping between 2 GdkGLTextures, gsk_render_node_diff() + * will see a pointer difference and ensure the tile region is damaged. + * Since it is a dmabuf, we can already assume the contents are available + * to the GPU by layers beneath us. + */ + +#define TILE_WIDTH 128 +#define TILE_HEIGHT 128 + +struct _MksDmabufPaintable +{ + GObject parent_instance; + GdkTexture *textures[2]; + GArray *tiles; + guint width; + guint height; +}; + +typedef struct _MksDmabufTextureData +{ + GdkGLContext *gl_context; + GLuint texture_id; +} MksDmabufTextureData; + +typedef struct _MksDmabufTile +{ + graphene_rect_t area; + guint16 texture : 1; +} MksDmabufTile; + +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_assert (MKS_IS_DMABUF_PAINTABLE (self)); + g_assert (GDK_IS_SNAPSHOT (snapshot)); + + gtk_snapshot_save (snapshot); + gtk_snapshot_scale (snapshot, + width / (double)self->width, + height / (double)self->height); + + for (guint i = 0; i < self->tiles->len; i++) + { + MksDmabufTile *tile = &g_array_index (self->tiles, MksDmabufTile, i); + GdkTexture *texture = self->textures[tile->texture]; + + gtk_snapshot_append_texture (snapshot, texture, &tile->area); + } + + gtk_snapshot_restore (snapshot); +} + +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_pointer (&self->tiles, g_array_unref); + g_clear_object (&self->textures[0]); + g_clear_object (&self->textures[1]); + + 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) +{ + self->tiles = g_array_new (FALSE, FALSE, sizeof (MksDmabufTile)); +} + +MksDmabufPaintable * +mks_dmabuf_paintable_new (GdkGLContext *gl_context, + int dmabuf_fd, + guint width, + guint height, + guint stride, + guint fourcc, + guint64 modifier, + gboolean y0_top, + GError **error) +{ + g_autoptr(MksDmabufTextureData) texture_data = NULL; + g_autoptr(MksDmabufPaintable) self = NULL; + GLuint texture_id; + guint zero = 0; + + if (dmabuf_fd < 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "invalid dmabuf_fd (%d)", + dmabuf_fd); + return NULL; + } + + if (width == 0 || height == 0 || stride == 0) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "invalid width/height/stride (%u/%u/%u)", + width, height, stride); + return NULL; + } + + self = g_object_new (MKS_TYPE_DMABUF_PAINTABLE, NULL); + self->width = width; + self->height = height; + + if (!(texture_id = mks_gl_context_import_dmabuf (gl_context, + fourcc, width, height, + 1, &dmabuf_fd, &stride, &zero, &modifier))) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to import dmabuf into GL texture"); + return NULL; + } + + texture_data = mks_dmabuf_texture_data_new (gl_context, texture_id); + + self->textures[0] = gdk_gl_texture_new (gl_context, texture_id, width, height, + (GDestroyNotify) mks_dmabuf_texture_data_unref, + mks_dmabuf_texture_data_ref (texture_data)); + self->textures[1] = gdk_gl_texture_new (gl_context, texture_id, width, height, + (GDestroyNotify) mks_dmabuf_texture_data_unref, + mks_dmabuf_texture_data_ref (texture_data)); + + for (guint y = 0; y < height; y += TILE_HEIGHT) + { + guint tile_height = MIN (TILE_HEIGHT, height - y); + + for (guint x = 0; x < width; x += TILE_WIDTH) + { + MksDmabufTile tile; + guint tile_width = MIN (TILE_WIDTH, width - x); + + tile.area = GRAPHENE_RECT_INIT (x, y, tile_width, tile_height); + tile.texture = 0; + + g_array_append_val (self->tiles, tile); + } + } + + return g_steal_pointer (&self); +} + +void +mks_dmabuf_paintable_invalidate (MksDmabufPaintable *self, + guint x, + guint y, + guint width, + guint height) +{ + graphene_rect_t area; + + g_return_if_fail (MKS_IS_DMABUF_PAINTABLE (self)); + + if (width == 0 || height == 0) + return; + + area = GRAPHENE_RECT_INIT (x, y, width, height); + + for (guint i = 0; i < self->tiles->len; i++) + { + MksDmabufTile *tile = &g_array_index (self->tiles, MksDmabufTile, i); + G_GNUC_UNUSED graphene_rect_t res; + + if (graphene_rect_intersection (&area, &tile->area, &res)) + tile->texture = !tile->texture; + } + + gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); +} diff --git a/lib/mks-dmabuf-texture-private.h b/lib/mks-dmabuf-texture-private.h deleted file mode 100644 index 2615578..0000000 --- a/lib/mks-dmabuf-texture-private.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * mks-dmabuf-texture-private.h - * - * Copyright 2023 Christian Hergert - * - * 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 . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -GdkTexture *mks_dmabuf_texture_new (int dmabuf_fd, - guint width, - guint height, - guint stride, - guint fourcc, - guint64 modifier, - gboolean y0_top, - GError **error); - -G_END_DECLS diff --git a/lib/mks-dmabuf-texture.c b/lib/mks-dmabuf-texture.c deleted file mode 100644 index 9155df4..0000000 --- a/lib/mks-dmabuf-texture.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * mks-dmabuf-texture.c - * - * Copyright 2023 Christian Hergert - * - * 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 . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#include "config.h" - -#include "mks-dmabuf-texture-private.h" - -GdkTexture * -mks_dmabuf_texture_new (int dmabuf_fd, - guint width, - guint height, - guint stride, - guint fourcc, - guint64 modifier, - gboolean y0_top, - GError **error) -{ - if G_UNLIKELY (dmabuf_fd < 0) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid FD for DMA-BUF"); - return NULL; - } - - if G_UNLIKELY (width == 0 || height == 0 || stride == 0) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Invalid width/height/stride"); - return NULL; - } - - /* TODO: use dmabuf importing code here */ - - return NULL; -} diff --git a/lib/mks-gl-context-private.h b/lib/mks-gl-context-private.h new file mode 100644 index 0000000..0279dcc --- /dev/null +++ b/lib/mks-gl-context-private.h @@ -0,0 +1,37 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * 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 Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +GLuint mks_gl_context_import_dmabuf (GdkGLContext *context, + uint32_t format, + unsigned int width, + unsigned int height, + uint32_t n_planes, + const int *fds, + const uint32_t *strides, + const uint32_t *offsets, + const uint64_t *modifiers); + +G_END_DECLS diff --git a/lib/mks-gl-context.c b/lib/mks-gl-context.c new file mode 100644 index 0000000..46daa84 --- /dev/null +++ b/lib/mks-gl-context.c @@ -0,0 +1,174 @@ +/* + * Copyright 2021 Red Hat, Inc. + * + * 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 Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#ifdef GDK_WINDOWING_WAYLAND +# include +#endif + +#ifdef GDK_WINDOWING_X11 +# include +#endif + +#include "mks-gl-context-private.h" + +GLuint +mks_gl_context_import_dmabuf (GdkGLContext *context, + uint32_t format, + unsigned int width, + unsigned int height, + uint32_t n_planes, + const int *fds, + const uint32_t *strides, + const uint32_t *offsets, + const uint64_t *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; +} diff --git a/lib/mks-paintable.c b/lib/mks-paintable.c index fd4b995..c1f812a 100644 --- a/lib/mks-paintable.c +++ b/lib/mks-paintable.c @@ -28,24 +28,17 @@ #include #include "mks-cairo-framebuffer-private.h" +#include "mks-dmabuf-paintable-private.h" #include "mks-paintable-private.h" #include "mks-qemu.h" struct _MksPaintable { - GObject parent_instance; - - MksQemuListener *listener; - GDBusConnection *connection; - MksCairoFramebuffer *framebuffer; - - guint mode : 2; -}; - -enum { - MODE_INITIAL = 0, - MODE_FRAMEBUFFER, - MODE_DMABUF, + GObject parent_instance; + GdkGLContext *gl_context; + MksQemuListener *listener; + GDBusConnection *connection; + GdkPaintable *child; }; static cairo_format_t @@ -77,34 +70,25 @@ _pixman_format_to_cairo_format (guint pixman_format) static int mks_paintable_get_intrinsic_height (GdkPaintable *paintable) { - MksPaintable *self = MKS_PAINTABLE (paintable); + GdkPaintable *child = MKS_PAINTABLE (paintable)->child; - if (self->mode == MODE_FRAMEBUFFER) - return gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (self->framebuffer)); - - return 0; + return child ? gdk_paintable_get_intrinsic_height (child) : 0; } static int mks_paintable_get_intrinsic_width (GdkPaintable *paintable) { - MksPaintable *self = MKS_PAINTABLE (paintable); + GdkPaintable *child = MKS_PAINTABLE (paintable)->child; - if (self->mode == MODE_FRAMEBUFFER) - return gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (self->framebuffer)); - - return 0; + return child ? gdk_paintable_get_intrinsic_width (child) : 0; } static double mks_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable) { - MksPaintable *self = MKS_PAINTABLE (paintable); + GdkPaintable *child = MKS_PAINTABLE (paintable)->child; - if (self->mode == MODE_FRAMEBUFFER) - return gdk_paintable_get_intrinsic_aspect_ratio (GDK_PAINTABLE (self->framebuffer)); - - return .0; + return child ? gdk_paintable_get_intrinsic_aspect_ratio (child) : .0; } static void @@ -113,13 +97,10 @@ mks_paintable_snapshot (GdkPaintable *paintable, double width, double height) { - MksPaintable *self = (MksPaintable *)paintable; + GdkPaintable *child = MKS_PAINTABLE (paintable)->child; - g_assert (MKS_IS_PAINTABLE (self)); - g_assert (GDK_IS_SNAPSHOT (snapshot)); - - if (self->mode == MODE_FRAMEBUFFER) - gdk_paintable_snapshot (GDK_PAINTABLE (self->framebuffer), snapshot, width, height); + if (child != NULL) + gdk_paintable_snapshot (child, snapshot, width, height); } static void @@ -134,6 +115,23 @@ 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) { @@ -141,7 +139,8 @@ mks_paintable_dispose (GObject *object) g_clear_object (&self->connection); g_clear_object (&self->listener); - g_clear_object (&self->framebuffer); + g_clear_object (&self->child); + g_clear_object (&self->gl_context); G_OBJECT_CLASS (mks_paintable_parent_class)->dispose (object); } @@ -180,48 +179,46 @@ mks_paintable_invalidate_size_cb (MksPaintable *self, } static void -mks_paintable_set_framebuffer (MksPaintable *self, - MksCairoFramebuffer *framebuffer) +mks_paintable_set_child (MksPaintable *self, + GdkPaintable *child) { gboolean size_changed; g_assert (MKS_IS_PAINTABLE (self)); - g_assert (!framebuffer || MKS_IS_CAIRO_FRAMEBUFFER (framebuffer)); + g_assert (!child || GDK_IS_PAINTABLE (child)); - if (self->framebuffer == framebuffer) + if (self->child == child) return; - size_changed = self->framebuffer == NULL || - framebuffer == NULL || - mks_cairo_framebuffer_get_width (self->framebuffer) != mks_cairo_framebuffer_get_width (framebuffer) || - mks_cairo_framebuffer_get_height (self->framebuffer) != mks_cairo_framebuffer_get_height (framebuffer); + size_changed = self->child == NULL || + child == NULL || + gdk_paintable_get_intrinsic_width (self->child) != gdk_paintable_get_intrinsic_width (child) || + gdk_paintable_get_intrinsic_height (self->child) != gdk_paintable_get_intrinsic_height (child); - if (self->framebuffer != NULL) + if (self->child != NULL) { - g_signal_handlers_disconnect_by_func (self->framebuffer, - G_CALLBACK (gdk_paintable_invalidate_size), + g_signal_handlers_disconnect_by_func (self->child, + G_CALLBACK (mks_paintable_invalidate_size_cb), self); - g_signal_handlers_disconnect_by_func (self->framebuffer, - G_CALLBACK (gdk_paintable_invalidate_contents), + g_signal_handlers_disconnect_by_func (self->child, + G_CALLBACK (mks_paintable_invalidate_contents_cb), self); - g_clear_object (&self->framebuffer); - self->mode = MODE_INITIAL; + g_clear_object (&self->child); } - if (framebuffer != NULL) + if (child != NULL) { - self->framebuffer = g_object_ref (framebuffer); - g_signal_connect_object (self->framebuffer, + self->child = g_object_ref (child); + g_signal_connect_object (self->child, "invalidate-size", G_CALLBACK (mks_paintable_invalidate_size_cb), self, G_CONNECT_SWAPPED); - g_signal_connect_object (self->framebuffer, + g_signal_connect_object (self->child, "invalidate-contents", G_CALLBACK (mks_paintable_invalidate_contents_cb), self, G_CONNECT_SWAPPED); - self->mode = MODE_FRAMEBUFFER; } gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); @@ -230,24 +227,6 @@ mks_paintable_set_framebuffer (MksPaintable *self, gdk_paintable_invalidate_size (GDK_PAINTABLE (self)); } -static gboolean -mks_paintable_listener_update_dmabuf (MksPaintable *self, - GDBusMethodInvocation *invocation, - int x, - int y, - int width, - int height, - MksQemuListener *listener) -{ - g_assert (MKS_IS_PAINTABLE (self)); - g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); - g_assert (MKS_QEMU_IS_LISTENER (listener)); - - mks_qemu_listener_complete_update_dmabuf (listener, invocation); - - return TRUE; -} - static gboolean mks_paintable_listener_scanout_dmabuf (MksPaintable *self, GDBusMethodInvocation *invocation, @@ -260,12 +239,66 @@ mks_paintable_listener_scanout_dmabuf (MksPaintable *self, guint64 modifier, gboolean y0_top, MksQemuListener *listener) +{ + g_autoptr(MksDmabufPaintable) child = NULL; + g_autoptr(GError) error = NULL; + g_autofd int dmabuf_fd = -1; + GdkGLContext *gl_context; + guint handle; + + g_assert (MKS_IS_PAINTABLE (self)); + g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); + g_assert (MKS_QEMU_IS_LISTENER (listener)); + g_assert (g_variant_is_of_type (dmabuf, G_VARIANT_TYPE_HANDLE)); + + handle = g_variant_get_handle (dmabuf); + + if (handle >= g_unix_fd_list_get_length (unix_fd_list)) + { + g_dbus_method_invocation_return_error_literal (invocation, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid handle to DMA-BUF"); + return TRUE; + } + + if (-1 == (dmabuf_fd = g_unix_fd_list_get (unix_fd_list, handle, &error)) || + !(gl_context = mks_paintable_get_gl_context (self, &error)) || + !(child = mks_dmabuf_paintable_new (gl_context, + dmabuf_fd, + width, height, + stride, fourcc, + modifier, y0_top, + &error))) + { + g_dbus_method_invocation_return_gerror (invocation, error); + return TRUE; + } + + mks_paintable_set_child (self, GDK_PAINTABLE (child)); + + mks_qemu_listener_complete_scanout_dmabuf (listener, invocation, NULL); + + return TRUE; +} + +static gboolean +mks_paintable_listener_update_dmabuf (MksPaintable *self, + GDBusMethodInvocation *invocation, + int x, + int y, + int width, + int height, + MksQemuListener *listener) { g_assert (MKS_IS_PAINTABLE (self)); g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); g_assert (MKS_QEMU_IS_LISTENER (listener)); - mks_qemu_listener_complete_scanout_dmabuf (listener, invocation, NULL); + if (MKS_IS_DMABUF_PAINTABLE (self->child)) + mks_dmabuf_paintable_invalidate (MKS_DMABUF_PAINTABLE (self->child), x, y, width, height); + + mks_qemu_listener_complete_update_dmabuf (listener, invocation); return TRUE; } @@ -293,7 +326,7 @@ mks_paintable_listener_update (MksPaintable *self, g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); g_assert (MKS_QEMU_IS_LISTENER (listener)); - if (self->mode != MODE_FRAMEBUFFER || + if (!MKS_IS_CAIRO_FRAMEBUFFER (self->child) || !(format = _pixman_format_to_cairo_format (pixman_format))) { g_dbus_method_invocation_return_error_literal (invocation, @@ -328,19 +361,19 @@ mks_paintable_listener_update (MksPaintable *self, * * Generally this is seen at startup during EFI/BIOS. */ - if (x + width > mks_cairo_framebuffer_get_width (self->framebuffer) || - y + height > mks_cairo_framebuffer_get_height (self->framebuffer)) + if (x + width > gdk_paintable_get_intrinsic_width (self->child) || + y + height > gdk_paintable_get_intrinsic_height (self->child)) { - guint max_width = MAX (mks_cairo_framebuffer_get_width (self->framebuffer), x + width); - guint max_height = MAX (mks_cairo_framebuffer_get_height (self->framebuffer), y + height); + guint max_width = MAX (gdk_paintable_get_intrinsic_width (self->child), x + width); + guint max_height = MAX (gdk_paintable_get_intrinsic_height (self->child), y + height); g_autoptr(MksCairoFramebuffer) framebuffer = mks_cairo_framebuffer_new (format, max_width, max_height); - mks_cairo_framebuffer_copy_to (self->framebuffer, framebuffer); - mks_paintable_set_framebuffer (self, framebuffer); + mks_cairo_framebuffer_copy_to (MKS_CAIRO_FRAMEBUFFER (self->child), framebuffer); + mks_paintable_set_child (self, GDK_PAINTABLE (framebuffer)); } source = cairo_image_surface_create_for_data ((guint8 *)data, format, width, height, stride); - cr = mks_cairo_framebuffer_update (self->framebuffer, x, y, width, height); + cr = mks_cairo_framebuffer_update (MKS_CAIRO_FRAMEBUFFER (self->child), x, y, width, height); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_surface (cr, source, 0, 0); cairo_rectangle (cr, 0, 0, width, height); @@ -396,17 +429,18 @@ mks_paintable_listener_scanout (MksPaintable *self, return TRUE; } - if (self->framebuffer == NULL || - width != mks_cairo_framebuffer_get_width (self->framebuffer) || - height != mks_cairo_framebuffer_get_height (self->framebuffer)) + if (self->child == NULL || + !MKS_IS_CAIRO_FRAMEBUFFER (self->child) || + width != gdk_paintable_get_intrinsic_width (self->child) || + height != gdk_paintable_get_intrinsic_height (self->child)) { - g_autoptr(MksCairoFramebuffer) framebuffer = mks_cairo_framebuffer_new (format, width, height); + g_autoptr(MksCairoFramebuffer) child = mks_cairo_framebuffer_new (format, width, height); - mks_paintable_set_framebuffer (self, framebuffer); + mks_paintable_set_child (self, GDK_PAINTABLE (child)); } source = cairo_image_surface_create_for_data ((guint8 *)data, format, width, height, stride); - cr = mks_cairo_framebuffer_update (self->framebuffer, 0, 0, width, height); + cr = mks_cairo_framebuffer_update (MKS_CAIRO_FRAMEBUFFER (self->child), 0, 0, width, height); cairo_set_source_surface (cr, source, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_rectangle (cr, 0, 0, width, height); @@ -464,11 +498,8 @@ mks_paintable_listener_disable (MksPaintable *self, g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation)); g_assert (MKS_QEMU_IS_LISTENER (listener)); - if (self->mode == MODE_FRAMEBUFFER) - { - if (self->framebuffer != NULL) - mks_cairo_framebuffer_clear (self->framebuffer); - } + if (MKS_IS_CAIRO_FRAMEBUFFER (self->child)) + mks_cairo_framebuffer_clear (MKS_CAIRO_FRAMEBUFFER (self->child)); gdk_paintable_invalidate_contents (GDK_PAINTABLE (self)); diff --git a/meson.build b/meson.build index 01ea0cd..b5c8970 100644 --- a/meson.build +++ b/meson.build @@ -37,6 +37,7 @@ 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) @@ -154,4 +155,4 @@ add_project_link_arguments(project_link_args, language: 'c') subdir('lib') subdir('tools') -configure_file(output: 'config.h', configuration: config_h) \ No newline at end of file +configure_file(output: 'config.h', configuration: config_h)