wayland/dma-buf: Add basic support for DMA buffer feedback

This includes sending the default tranche, but so far only sends the
same for every surface feedback requested. Scanout tranche will be added
later.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1959>
This commit is contained in:
Jonas Ådahl 2021-08-05 15:10:47 +02:00 committed by Marge Bot
parent fc358e1306
commit 7acecb1c72
3 changed files with 360 additions and 11 deletions

View File

@ -42,7 +42,7 @@ gudev_req = '>= 232'
# wayland version requirements
wayland_server_req = '>= 1.18'
wayland_protocols_req = '>= 1.23'
wayland_protocols_req = '>= 1.24'
# native backend version requirements
libinput_req = '>= 1.19.0'

View File

@ -40,6 +40,9 @@
#include "wayland/meta-wayland-dma-buf.h"
#include <drm_fourcc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "backends/meta-backend-private.h"
#include "backends/meta-egl-ext.h"
@ -66,17 +69,41 @@
#define META_WAYLAND_DMA_BUF_MAX_FDS 4
/* Compatible with zwp_linux_dmabuf_feedback_v1.tranche_flags */
typedef enum _MetaWaylandDmaBufTrancheFlags
{
META_WAYLAND_DMA_BUF_TRANCHE_FLAG_NONE = 0,
META_WAYLAND_DMA_BUF_TRANCHE_FLAG_SCANOUT = 1,
} MetaWaylandDmaBufTrancheFlags;
typedef struct _MetaWaylandDmaBufFormat
{
uint32_t drm_format;
uint64_t drm_modifier;
uint16_t table_index;
} MetaWaylandDmaBufFormat;
typedef struct _MetaWaylandDmaBufTranche
{
dev_t target_device_id;
GArray *formats;
MetaWaylandDmaBufTrancheFlags flags;
} MetaWaylandDmaBufTranche;
typedef struct _MetaWaylandDmaBufFeedback
{
dev_t main_device_id;
GList *tranches;
} MetaWaylandDmaBufFeedback;
struct _MetaWaylandDmaBufManager
{
MetaWaylandCompositor *compositor;
dev_t main_device_id;
GArray *formats;
MetaAnonymousFile *format_table_file;
MetaWaylandDmaBufFeedback *default_feedback;
};
struct _MetaWaylandDmaBufBuffer
@ -95,6 +122,118 @@ struct _MetaWaylandDmaBufBuffer
G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT);
static MetaWaylandDmaBufTranche *
meta_wayland_dma_buf_tranche_new (dev_t device_id,
GArray *formats,
MetaWaylandDmaBufTrancheFlags flags)
{
MetaWaylandDmaBufTranche *tranche;
tranche = g_new0 (MetaWaylandDmaBufTranche, 1);
tranche->target_device_id = device_id;
tranche->formats = g_array_copy (formats);
tranche->flags = flags;
return tranche;
}
static void
meta_wayland_dma_buf_tranche_free (MetaWaylandDmaBufTranche *tranche)
{
g_clear_pointer (&tranche->formats, g_array_unref);
g_free (tranche);
}
static void
meta_wayland_dma_buf_tranche_send (MetaWaylandDmaBufTranche *tranche,
struct wl_resource *resource)
{
struct wl_array target_device_buf;
dev_t *device_id_ptr;
struct wl_array formats_array;
unsigned int i;
wl_array_init (&target_device_buf);
device_id_ptr = wl_array_add (&target_device_buf, sizeof (*device_id_ptr));
*device_id_ptr = tranche->target_device_id;
zwp_linux_dmabuf_feedback_v1_send_tranche_target_device (resource,
&target_device_buf);
wl_array_release (&target_device_buf);
zwp_linux_dmabuf_feedback_v1_send_tranche_flags (resource, tranche->flags);
wl_array_init (&formats_array);
for (i = 0; i < tranche->formats->len; i++)
{
MetaWaylandDmaBufFormat *format =
&g_array_index (tranche->formats,
MetaWaylandDmaBufFormat,
i);
uint16_t *format_index_ptr;
format_index_ptr = wl_array_add (&formats_array,
sizeof (*format_index_ptr));
*format_index_ptr = format->table_index;
}
zwp_linux_dmabuf_feedback_v1_send_tranche_formats (resource, &formats_array);
wl_array_release (&formats_array);
zwp_linux_dmabuf_feedback_v1_send_tranche_done (resource);
}
static void
meta_wayland_dma_buf_feedback_send (MetaWaylandDmaBufFeedback *feedback,
MetaWaylandDmaBufManager *dma_buf_manager,
struct wl_resource *resource)
{
size_t size;
int fd;
struct wl_array main_device_buf;
dev_t *device_id_ptr;
fd = meta_anonymous_file_open_fd (dma_buf_manager->format_table_file,
META_ANONYMOUS_FILE_MAPMODE_PRIVATE);
size = meta_anonymous_file_size (dma_buf_manager->format_table_file);
zwp_linux_dmabuf_feedback_v1_send_format_table (resource, fd, size);
meta_anonymous_file_close_fd (fd);
wl_array_init (&main_device_buf);
device_id_ptr = wl_array_add (&main_device_buf, sizeof (*device_id_ptr));
*device_id_ptr = feedback->main_device_id;
zwp_linux_dmabuf_feedback_v1_send_main_device (resource, &main_device_buf);
g_list_foreach (feedback->tranches,
(GFunc) meta_wayland_dma_buf_tranche_send,
resource);
zwp_linux_dmabuf_feedback_v1_send_done (resource);
}
static void
meta_wayland_dma_buf_feedback_add_tranche (MetaWaylandDmaBufFeedback *feedback,
MetaWaylandDmaBufTranche *tranche)
{
feedback->tranches = g_list_append (feedback->tranches, tranche);
}
static MetaWaylandDmaBufFeedback *
meta_wayland_dma_buf_feedback_new (dev_t device_id)
{
MetaWaylandDmaBufFeedback *feedback;
feedback = g_new0 (MetaWaylandDmaBufFeedback, 1);
feedback->main_device_id = device_id;
return feedback;
}
static void
meta_wayland_dma_buf_feedback_free (MetaWaylandDmaBufFeedback *feedback)
{
g_clear_list (&feedback->tranches,
(GDestroyNotify) meta_wayland_dma_buf_tranche_free);
g_free (feedback);
}
static gboolean
meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer,
GError **error)
@ -630,10 +769,80 @@ dma_buf_handle_create_buffer_params (struct wl_client *client,
buffer_params_destructor);
}
static void
feedback_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct zwp_linux_dmabuf_feedback_v1_interface feedback_implementation =
{
feedback_destroy,
};
static void
feedback_destructor (struct wl_resource *resource)
{
}
static void
dma_buf_handle_get_default_feedback (struct wl_client *client,
struct wl_resource *dma_buf_resource,
uint32_t feedback_id)
{
MetaWaylandDmaBufManager *dma_buf_manager =
wl_resource_get_user_data (dma_buf_resource);
struct wl_resource *feedback_resource;
feedback_resource =
wl_resource_create (client,
&zwp_linux_dmabuf_feedback_v1_interface,
wl_resource_get_version (dma_buf_resource),
feedback_id);
wl_resource_set_implementation (feedback_resource,
&feedback_implementation,
NULL,
feedback_destructor);
meta_wayland_dma_buf_feedback_send (dma_buf_manager->default_feedback,
dma_buf_manager,
feedback_resource);
}
static void
dma_buf_handle_get_surface_feedback (struct wl_client *client,
struct wl_resource *dma_buf_resource,
uint32_t feedback_id,
struct wl_resource *surface_resource)
{
MetaWaylandDmaBufManager *dma_buf_manager =
wl_resource_get_user_data (dma_buf_resource);
struct wl_resource *feedback_resource;
feedback_resource =
wl_resource_create (client,
&zwp_linux_dmabuf_feedback_v1_interface,
wl_resource_get_version (dma_buf_resource),
feedback_id);
wl_resource_set_implementation (feedback_resource,
&feedback_implementation,
NULL,
feedback_destructor);
meta_wayland_dma_buf_feedback_send (dma_buf_manager->default_feedback,
dma_buf_manager,
feedback_resource);
}
static const struct zwp_linux_dmabuf_v1_interface dma_buf_implementation =
{
dma_buf_handle_destroy,
dma_buf_handle_create_buffer_params,
dma_buf_handle_get_default_feedback,
dma_buf_handle_get_surface_feedback,
};
static gboolean
@ -661,6 +870,9 @@ static void
send_modifiers (struct wl_resource *resource,
MetaWaylandDmaBufFormat *format)
{
g_assert (wl_resource_get_version (resource) <
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION);
zwp_linux_dmabuf_v1_send_format (resource, format->drm_format);
if (wl_resource_get_version (resource) <
@ -681,22 +893,25 @@ dma_buf_bind (struct wl_client *client,
{
MetaWaylandDmaBufManager *dma_buf_manager = user_data;
struct wl_resource *resource;
unsigned int i;
resource = wl_resource_create (client, &zwp_linux_dmabuf_v1_interface,
version, id);
wl_resource_set_implementation (resource, &dma_buf_implementation,
dma_buf_manager, NULL);
for (i = 0; i < dma_buf_manager->formats->len; i++)
if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION)
{
MetaWaylandDmaBufFormat *format =
&g_array_index (dma_buf_manager->formats,
MetaWaylandDmaBufFormat,
i);
unsigned int i;
send_modifiers (resource, format);
for (i = 0; i < dma_buf_manager->formats->len; i++)
{
MetaWaylandDmaBufFormat *format =
&g_array_index (dma_buf_manager->formats,
MetaWaylandDmaBufFormat,
i);
send_modifiers (resource, format);
}
}
}
@ -741,6 +956,7 @@ add_format (MetaWaylandDmaBufManager *dma_buf_manager,
format = (MetaWaylandDmaBufFormat) {
.drm_format = drm_format,
.drm_modifier = modifiers[i],
.table_index = dma_buf_manager->formats->len,
};
g_array_append_val (dma_buf_manager->formats, format);
}
@ -751,10 +967,53 @@ add_fallback:
format = (MetaWaylandDmaBufFormat) {
.drm_format = drm_format,
.drm_modifier = DRM_FORMAT_MOD_INVALID,
.table_index = dma_buf_manager->formats->len,
};
g_array_append_val (dma_buf_manager->formats, format);
}
/*
* This is the structure the data is expected to have in the shared memory file
* shared with clients, according to the Wayland Linux DMA buffer protocol.
* It's structured as 16 bytes (128 bits) per entry, where each entry consists
* of the following:
*
* [ 32 bit format ][ 32 bit padding ][ 64 bit modifier ]
*/
typedef struct _MetaMetaWaylanDmaBdufFormatEntry
{
uint32_t drm_format;
uint32_t unused_padding;
uint64_t drm_modifier;
} MetaWaylandDmaBufFormatEntry;
G_STATIC_ASSERT (sizeof (MetaWaylandDmaBufFormatEntry) == 16);
G_STATIC_ASSERT (offsetof (MetaWaylandDmaBufFormatEntry, drm_format) == 0);
G_STATIC_ASSERT (offsetof (MetaWaylandDmaBufFormatEntry, drm_modifier) == 8);
static void
init_format_table (MetaWaylandDmaBufManager *dma_buf_manager)
{
g_autofree MetaWaylandDmaBufFormatEntry *format_table = NULL;
size_t size;
int i;
size = sizeof (MetaWaylandDmaBufFormatEntry) * dma_buf_manager->formats->len;
format_table = g_malloc0 (size);
for (i = 0; i < dma_buf_manager->formats->len; i++)
{
MetaWaylandDmaBufFormat *format =
&g_array_index (dma_buf_manager->formats, MetaWaylandDmaBufFormat, i);
format_table[i].drm_format = format->drm_format;
format_table[i].drm_modifier = format->drm_modifier;
}
dma_buf_manager->format_table_file =
meta_anonymous_file_new (size, (uint8_t *) format_table);
}
static void
init_formats (MetaWaylandDmaBufManager *dma_buf_manager,
EGLDisplay egl_display)
@ -775,6 +1034,25 @@ init_formats (MetaWaylandDmaBufManager *dma_buf_manager,
add_format (dma_buf_manager, egl_display, DRM_FORMAT_XBGR16161616F);
add_format (dma_buf_manager, egl_display, DRM_FORMAT_XRGB16161616F);
add_format (dma_buf_manager, egl_display, DRM_FORMAT_ARGB16161616F);
init_format_table (dma_buf_manager);
}
static void
init_default_feedback (MetaWaylandDmaBufManager *dma_buf_manager)
{
MetaWaylandDmaBufTrancheFlags flags;
MetaWaylandDmaBufTranche *tranche;
dma_buf_manager->default_feedback =
meta_wayland_dma_buf_feedback_new (dma_buf_manager->main_device_id);
flags = META_WAYLAND_DMA_BUF_TRANCHE_FLAG_NONE;
tranche = meta_wayland_dma_buf_tranche_new (dma_buf_manager->main_device_id,
dma_buf_manager->formats,
flags);
meta_wayland_dma_buf_feedback_add_tranche (dma_buf_manager->default_feedback,
tranche);
}
/**
@ -794,8 +1072,14 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor *compositor,
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context);
dev_t device_id = 0;
int protocol_version;
EGLDeviceEXT egl_device;
EGLAttrib attrib;
g_autoptr (GError) local_error = NULL;
g_autofree MetaWaylandDmaBufManager *dma_buf_manager = NULL;
const char *device_path = NULL;
struct stat device_stat;
g_assert (backend && egl && clutter_backend && cogl_context && egl_display);
@ -808,11 +1092,71 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor *compositor,
return NULL;
}
if (!meta_egl_query_display_attrib (egl, egl_display,
EGL_DEVICE_EXT, &attrib,
&local_error))
{
g_warning ("Failed to query EGL device from primary EGL display: %s",
local_error->message);
protocol_version = 3;
goto initialize;
}
egl_device = (EGLDeviceEXT) attrib;
if (meta_egl_egl_device_has_extensions (egl, egl_device, NULL,
"EGL_EXT_device_drm_render_node",
NULL))
{
device_path = meta_egl_query_device_string (egl, egl_device,
EGL_DRM_RENDER_NODE_FILE_EXT,
&local_error);
}
else if (meta_egl_egl_device_has_extensions (egl, egl_device, NULL,
"EGL_EXT_device_drm",
NULL))
{
device_path = meta_egl_query_device_string (egl, egl_device,
EGL_DRM_DEVICE_FILE_EXT,
&local_error);
}
else
{
meta_topic (META_DEBUG_WAYLAND,
"Only advertising zwp_linux_dmabuf_v1 interface version 3 "
"support, missing 'EGL_EXT_device_drm' and "
"'EGL_EXT_device_drm_render_node'");
protocol_version = 3;
goto initialize;
}
if (!device_path)
{
g_warning ("Failed to query EGL device path: %s",
local_error->message);
protocol_version = 3;
goto initialize;
}
if (stat (device_path, &device_stat) != 0)
{
g_warning ("Failed to fetch device file ID for '%s': %s",
device_path,
g_strerror (errno));
protocol_version = 3;
goto initialize;
}
device_id = device_stat.st_rdev;
protocol_version = 4;
initialize:
dma_buf_manager = g_new0 (MetaWaylandDmaBufManager, 1);
if (!wl_global_create (compositor->wayland_display,
&zwp_linux_dmabuf_v1_interface,
META_ZWP_LINUX_DMABUF_V1_VERSION,
protocol_version,
dma_buf_manager,
dma_buf_bind))
{
@ -822,8 +1166,10 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor *compositor,
}
dma_buf_manager->compositor = compositor;
dma_buf_manager->main_device_id = device_id;
init_formats (dma_buf_manager, egl_display);
init_default_feedback (dma_buf_manager);
return g_steal_pointer (&dma_buf_manager);
}
@ -831,7 +1177,11 @@ meta_wayland_dma_buf_manager_new (MetaWaylandCompositor *compositor,
void
meta_wayland_dma_buf_manager_free (MetaWaylandDmaBufManager *dma_buf_manager)
{
g_clear_pointer (&dma_buf_manager->format_table_file,
meta_anonymous_file_free);
g_clear_pointer (&dma_buf_manager->formats, g_array_unref);
g_clear_pointer (&dma_buf_manager->default_feedback,
meta_wayland_dma_buf_feedback_free);
g_free (dma_buf_manager);
}

View File

@ -48,7 +48,6 @@
#define META_ZWP_POINTER_GESTURES_V1_VERSION 3
#define META_ZXDG_EXPORTER_V1_VERSION 1
#define META_ZXDG_IMPORTER_V1_VERSION 1
#define META_ZWP_LINUX_DMABUF_V1_VERSION 3
#define META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION 1
#define META_ZXDG_OUTPUT_V1_VERSION 3
#define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1