From 3ef9f9128306cac549a99a3e844b3e55f9381279 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 20 Feb 2023 16:54:52 -0500 Subject: [PATCH] wlr_scene: Add dmabuf_feedback helper --- include/wlr/types/wlr_scene.h | 13 ++++++ types/scene/wlr_scene.c | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 19924090f..b6f0d2175 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -38,6 +38,7 @@ struct wlr_scene_node; struct wlr_scene_buffer; struct wlr_presentation; +struct wlr_linux_dmabuf_v1; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( struct wlr_scene_buffer *buffer, int sx, int sy); @@ -95,10 +96,12 @@ struct wlr_scene { // May be NULL struct wlr_presentation *presentation; + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; // private state struct wl_listener presentation_destroy; + struct wl_listener linux_dmabuf_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -169,6 +172,7 @@ struct wlr_scene_buffer { int dst_width, dst_height; enum wl_output_transform transform; pixman_region32_t opaque_region; + struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; }; /** A viewport for an output in the scene-graph */ @@ -287,6 +291,15 @@ struct wlr_scene *wlr_scene_create(void); void wlr_scene_set_presentation(struct wlr_scene *scene, struct wlr_presentation *presentation); +/** + * Handles linux_dmabuf_v1 feedback for all surfaces in the scene. + * + * Asserts that a struct wlr_linux_dmabuf_v1 hasn't already been set for the scene. + */ +void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1); + + /** * Add a node displaying nothing but its children. */ diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 9b9224b19..45a004291 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } wl_list_remove(&scene->presentation_destroy.link); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); } else { assert(node->parent); } @@ -153,6 +155,7 @@ struct wlr_scene *wlr_scene_create(void) { wl_list_init(&scene->outputs); wl_list_init(&scene->presentation_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); const char *debug_damage_options[] = { "none", @@ -307,6 +310,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint32_t largest_overlap = 0; + struct wlr_scene_output *old_primary_output = scene_buffer->primary_output; scene_buffer->primary_output = NULL; size_t count = 0; @@ -354,6 +358,11 @@ static void update_node_update_outputs(struct wlr_scene_node *node, pixman_region32_fini(&intersection); } + if (old_primary_output != scene_buffer->primary_output) { + memset(&scene_buffer->prev_feedback_options, 0, + sizeof(scene_buffer->prev_feedback_options)); + } + uint64_t old_active = scene_buffer->active_outputs; scene_buffer->active_outputs = active_outputs; @@ -1171,6 +1180,23 @@ void wlr_scene_set_presentation(struct wlr_scene *scene, wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); } +static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, linux_dmabuf_v1_destroy); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); + scene->linux_dmabuf_v1 = NULL; +} + +void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) { + assert(scene->linux_dmabuf_v1 == NULL); + scene->linux_dmabuf_v1 = linux_dmabuf_v1; + scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy; + wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1414,6 +1440,37 @@ static void get_frame_damage(struct wlr_scene_output *scene_output, pixman_regio transform, tr_width, tr_height); } +static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, + struct wlr_scene_buffer *scene_buffer, + const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { + if (!scene->linux_dmabuf_v1) { + return; + } + + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (!surface) { + return; + } + + // compare to the previous options so that we don't send + // duplicate feedback events. + if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) { + return; + } + + scene_buffer->prev_feedback_options = *options; + + struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; + if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) { + return; + } + + wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1, + surface->surface, &feedback); + + wlr_linux_dmabuf_feedback_v1_finish(&feedback); +} + static bool scene_buffer_can_consider_direct_scanout(struct wlr_scene_buffer *buffer, const struct wlr_scene_output *scene_output) { struct wlr_scene_node *node = &buffer->node; @@ -1517,6 +1574,8 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); struct wlr_scene_node **list_data = list_con.render_list->data; + bool sent_direct_scanout_feedback = false; + // if there is only one thing to render let's see if that thing can be // directly scanned out bool scanout = false; @@ -1527,6 +1586,16 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); if (scene_buffer_can_consider_direct_scanout(buffer, scene_output)) { + if (buffer->primary_output == scene_output) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = output->renderer, + .scanout_primary_output = output, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + sent_direct_scanout_feedback = true; + } + scanout = scene_buffer_try_direct_scanout(buffer, scene_output); } } @@ -1667,6 +1736,19 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { for (int i = list_len - 1; i >= 0; i--) { struct wlr_scene_node *node = list_data[i]; scene_node_render(node, scene_output, &damage); + + if (node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + + if (buffer->primary_output == scene_output && !sent_direct_scanout_feedback) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = output->renderer, + .scanout_primary_output = NULL, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + } + } } wlr_renderer_scissor(renderer, NULL);