aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wlr/types/wlr_linux_dmabuf_v1.h15
-rw-r--r--protocol/meson.build2
-rw-r--r--types/wlr_linux_dmabuf_v1.c271
3 files changed, 283 insertions, 5 deletions
diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h
index b4eb8675..bbbb2772 100644
--- a/include/wlr/types/wlr_linux_dmabuf_v1.h
+++ b/include/wlr/types/wlr_linux_dmabuf_v1.h
@@ -10,6 +10,7 @@
#define WLR_TYPES_WLR_LINUX_DMABUF_H
#include <stdint.h>
+#include <sys/stat.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/render/dmabuf.h>
@@ -38,6 +39,18 @@ bool wlr_dmabuf_v1_resource_is_buffer(struct wl_resource *buffer_resource);
struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_from_buffer_resource(
struct wl_resource *buffer_resource);
+struct wlr_linux_dmabuf_feedback_v1 {
+ dev_t main_device;
+ size_t tranches_len;
+ const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches;
+};
+
+struct wlr_linux_dmabuf_feedback_v1_tranche {
+ dev_t target_device;
+ uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags
+ const struct wlr_drm_format_set *formats;
+};
+
/* the protocol interface */
struct wlr_linux_dmabuf_v1 {
struct wl_global *global;
@@ -49,6 +62,8 @@ struct wlr_linux_dmabuf_v1 {
// private state
+ struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback;
+
struct wl_listener display_destroy;
struct wl_listener renderer_destroy;
};
diff --git a/protocol/meson.build b/protocol/meson.build
index 8d8b2502..3e34f788 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -1,5 +1,5 @@
wayland_protos = dependency('wayland-protocols',
- version: '>=1.23',
+ version: '>=1.24',
fallback: ['wayland-protocols', 'wayland_protocols'],
default_options: ['tests=false'],
)
diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c
index b91fb59d..325b5362 100644
--- a/types/wlr_linux_dmabuf_v1.c
+++ b/types/wlr_linux_dmabuf_v1.c
@@ -2,17 +2,17 @@
#include <assert.h>
#include <drm_fourcc.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <unistd.h>
-#include <wayland-server-core.h>
-#include <wlr/render/drm_format_set.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/util/log.h>
#include "linux-dmabuf-unstable-v1-protocol.h"
#include "render/drm_format_set.h"
#include "util/signal.h"
+#include "util/shm.h"
-#define LINUX_DMABUF_VERSION 3
+#define LINUX_DMABUF_VERSION 4
struct wlr_linux_buffer_params_v1 {
struct wl_resource *resource;
@@ -21,6 +21,32 @@ struct wlr_linux_buffer_params_v1 {
bool has_modifier;
};
+struct wlr_linux_dmabuf_feedback_v1_compiled_tranche {
+ dev_t target_device;
+ uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags
+ struct wl_array indices; // uint16_t
+};
+
+struct wlr_linux_dmabuf_feedback_v1_compiled {
+ dev_t main_device;
+ int table_fd;
+ size_t table_size;
+
+ size_t tranches_len;
+ struct wlr_linux_dmabuf_feedback_v1_compiled_tranche tranches[];
+};
+
+struct wlr_linux_dmabuf_feedback_v1_table_entry {
+ uint32_t format;
+ uint32_t pad; // unused
+ uint64_t modifier;
+};
+
+// TODO: switch back to static_assert once this fix propagates in stable trees:
+// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255290
+_Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16,
+ "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed");
+
static void buffer_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
@@ -415,6 +441,228 @@ static void linux_dmabuf_create_params(struct wl_client *client,
&buffer_params_impl, params, params_handle_resource_destroy);
}
+static void linux_dmabuf_feedback_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct zwp_linux_dmabuf_feedback_v1_interface
+ linux_dmabuf_feedback_impl = {
+ .destroy = linux_dmabuf_feedback_destroy,
+};
+
+static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile(
+ const struct wlr_linux_dmabuf_feedback_v1 *feedback) {
+ assert(feedback->tranches_len > 0);
+
+ // Require the last tranche to be the fallback tranche and contain all
+ // formats/modifiers
+ const struct wlr_linux_dmabuf_feedback_v1_tranche *fallback_tranche =
+ &feedback->tranches[feedback->tranches_len - 1];
+
+ size_t table_len = 0;
+ for (size_t i = 0; i < fallback_tranche->formats->len; i++) {
+ const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i];
+ table_len += 1 + fmt->len;
+ }
+ assert(table_len > 0);
+
+ size_t table_size =
+ table_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry);
+ int rw_fd, ro_fd;
+ if (!allocate_shm_file_pair(table_size, &rw_fd, &ro_fd)) {
+ wlr_log(WLR_ERROR, "Failed to allocate shm file for format table");
+ return NULL;
+ }
+
+ struct wlr_linux_dmabuf_feedback_v1_table_entry *table =
+ mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, rw_fd, 0);
+ if (table == MAP_FAILED) {
+ wlr_log_errno(WLR_ERROR, "mmap failed");
+ close(rw_fd);
+ close(ro_fd);
+ return NULL;
+ }
+
+ close(rw_fd);
+
+ size_t n = 0;
+ for (size_t i = 0; i < fallback_tranche->formats->len; i++) {
+ const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i];
+
+ table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){
+ .format = fmt->format,
+ .modifier = DRM_FORMAT_MOD_INVALID,
+ };
+ n++;
+
+ for (size_t k = 0; k < fmt->len; k++) {
+ table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){
+ .format = fmt->format,
+ .modifier = fmt->modifiers[k],
+ };
+ n++;
+ }
+ }
+ assert(n == table_len);
+
+ munmap(table, table_size);
+
+ struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = calloc(1,
+ sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled) +
+ feedback->tranches_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled_tranche));
+ if (compiled == NULL) {
+ close(ro_fd);
+ return NULL;
+ }
+
+ compiled->main_device = feedback->main_device;
+ compiled->tranches_len = feedback->tranches_len;
+ compiled->table_fd = ro_fd;
+ compiled->table_size = table_size;
+
+ // Build the indices lists for all but the last (fallback) tranches
+ for (size_t i = 0; i < feedback->tranches_len - 1; i++) {
+ assert(false); // TODO: unimplemented
+ }
+
+ struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *fallback_compiled_tranche =
+ &compiled->tranches[compiled->tranches_len - 1];
+ fallback_compiled_tranche->target_device = fallback_tranche->target_device;
+ fallback_compiled_tranche->flags = fallback_tranche->flags;
+
+ // Build the indices list for the last (fallback) tranche
+ wl_array_init(&fallback_compiled_tranche->indices);
+ if (!wl_array_add(&fallback_compiled_tranche->indices,
+ table_len * sizeof(uint16_t))) {
+ wlr_log(WLR_ERROR, "Failed to allocate fallback tranche indices array");
+ goto error_compiled;
+ }
+
+ n = 0;
+ uint16_t *index_ptr;
+ wl_array_for_each(index_ptr, &fallback_compiled_tranche->indices) {
+ *index_ptr = n;
+ n++;
+ }
+
+ return compiled;
+
+error_compiled:
+ close(compiled->table_fd);
+ free(compiled);
+ return NULL;
+}
+
+static void compiled_feedback_destroy(
+ struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) {
+ for (size_t i = 0; i < feedback->tranches_len; i++) {
+ wl_array_release(&feedback->tranches[i].indices);
+ }
+ close(feedback->table_fd);
+ free(feedback);
+}
+
+static bool feedback_tranche_init_with_renderer(
+ struct wlr_linux_dmabuf_feedback_v1_tranche *tranche,
+ struct wlr_renderer *renderer) {
+ memset(tranche, 0, sizeof(*tranche));
+
+ int drm_fd = wlr_renderer_get_drm_fd(renderer);
+ if (drm_fd < 0) {
+ wlr_log(WLR_ERROR, "Failed to get DRM FD from renderer");
+ return false;
+ }
+
+ struct stat stat;
+ if (fstat(drm_fd, &stat) != 0) {
+ wlr_log_errno(WLR_ERROR, "fstat failed");
+ return false;
+ }
+ tranche->target_device = stat.st_rdev;
+
+ tranche->formats = wlr_renderer_get_dmabuf_texture_formats(renderer);
+ if (tranche->formats == NULL) {
+ wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats");
+ return false;
+ }
+
+ return true;
+}
+
+static struct wlr_linux_dmabuf_feedback_v1_compiled *compile_default_feedback(
+ struct wlr_renderer *renderer) {
+ struct wlr_linux_dmabuf_feedback_v1_tranche tranche = {0};
+ if (!feedback_tranche_init_with_renderer(&tranche, renderer)) {
+ return NULL;
+ }
+
+ const struct wlr_linux_dmabuf_feedback_v1 feedback = {
+ .main_device = tranche.target_device,
+ .tranches = &tranche,
+ .tranches_len = 1,
+ };
+
+ return feedback_compile(&feedback);
+}
+
+static void feedback_tranche_send(
+ const struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *tranche,
+ struct wl_resource *resource) {
+ struct wl_array dev_array = {
+ .size = sizeof(tranche->target_device),
+ .data = (void *)&tranche->target_device,
+ };
+ zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(resource, &dev_array);
+ zwp_linux_dmabuf_feedback_v1_send_tranche_flags(resource, tranche->flags);
+ zwp_linux_dmabuf_feedback_v1_send_tranche_formats(resource,
+ (struct wl_array *)&tranche->indices);
+ zwp_linux_dmabuf_feedback_v1_send_tranche_done(resource);
+}
+
+static void feedback_send(const struct wlr_linux_dmabuf_feedback_v1_compiled *feedback,
+ struct wl_resource *resource) {
+ struct wl_array dev_array = {
+ .size = sizeof(feedback->main_device),
+ .data = (void *)&feedback->main_device,
+ };
+ zwp_linux_dmabuf_feedback_v1_send_main_device(resource, &dev_array);
+
+ zwp_linux_dmabuf_feedback_v1_send_format_table(resource,
+ feedback->table_fd, feedback->table_size);
+
+ for (size_t i = 0; i < feedback->tranches_len; i++) {
+ feedback_tranche_send(&feedback->tranches[i], resource);
+ }
+
+ zwp_linux_dmabuf_feedback_v1_send_done(resource);
+}
+
+static void linux_dmabuf_get_default_feedback(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id) {
+ struct wlr_linux_dmabuf_v1 *linux_dmabuf =
+ linux_dmabuf_from_resource(resource);
+
+ uint32_t version = wl_resource_get_version(resource);
+ struct wl_resource *feedback_resource = wl_resource_create(client,
+ &zwp_linux_dmabuf_feedback_v1_interface, version, id);
+ if (feedback_resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl,
+ NULL, NULL);
+
+ feedback_send(linux_dmabuf->default_feedback, feedback_resource);
+}
+
+static void linux_dmabuf_get_surface_feedback(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *surface_resource) {
+ // TODO: implement per-surface feedback
+ linux_dmabuf_get_default_feedback(client, resource, id);
+}
+
static void linux_dmabuf_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
@@ -423,6 +671,8 @@ static void linux_dmabuf_destroy(struct wl_client *client,
static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = {
.destroy = linux_dmabuf_destroy,
.create_params = linux_dmabuf_create_params,
+ .get_default_feedback = linux_dmabuf_get_default_feedback,
+ .get_surface_feedback = linux_dmabuf_get_surface_feedback,
};
static void linux_dmabuf_send_modifiers(struct wl_resource *resource,
@@ -477,12 +727,17 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data,
}
wl_resource_set_implementation(resource, &linux_dmabuf_impl,
linux_dmabuf, NULL);
- linux_dmabuf_send_formats(linux_dmabuf, resource);
+
+ if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
+ linux_dmabuf_send_formats(linux_dmabuf, resource);
+ }
}
static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) {
wlr_signal_emit_safe(&linux_dmabuf->events.destroy, linux_dmabuf);
+ compiled_feedback_destroy(linux_dmabuf->default_feedback);
+
wl_list_remove(&linux_dmabuf->display_destroy.link);
wl_list_remove(&linux_dmabuf->renderer_destroy.link);
@@ -523,6 +778,14 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa
return NULL;
}
+ linux_dmabuf->default_feedback = compile_default_feedback(renderer);
+ if (linux_dmabuf->default_feedback == NULL) {
+ wlr_log(WLR_ERROR, "Failed to init default linux-dmabuf feedback");
+ wl_global_destroy(linux_dmabuf->global);
+ free(linux_dmabuf);
+ return NULL;
+ }
+
linux_dmabuf->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy);