From 04501f8d8b0925638f8a5e1fe3db98da2a8424d5 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Sun, 26 Apr 2026 21:55:22 +0800 Subject: [PATCH] types/scene: split wlr_scene into modular components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the scene graph implementation by splitting wlr_scene.c into multiple focused units: buffer, node, rect and tree. Introduce corresponding public headers for each component. This reduces the size and complexity of the core scene file, improves code organization and makes individual parts of the scene graph easier to maintain and extend. As part of this refactor, drop unnecessary pointer indirection when passing scene trees (e.g. &scene->tree → scene->tree) and update all call sites accordingly. No functional changes intended. --- examples/cairo-buffer.c | 2 +- examples/embedded.c | 2 +- examples/scene-graph.c | 5 +- include/types/wlr_scene.h | 4 - include/wlr/types/wlr_scene.h | 326 +--- include/wlr/types/wlr_scene_buffer.h | 209 +++ include/wlr/types/wlr_scene_node.h | 184 +++ include/wlr/types/wlr_scene_rect.h | 40 + include/wlr/types/wlr_scene_tree.h | 32 + test/bench_scene.c | 9 +- tinywl/tinywl.c | 8 +- types/ext_image_capture_source_v1/scene.c | 62 +- types/meson.build | 4 + types/scene/output_layout.c | 2 +- types/scene/subsurface_tree.c | 6 +- types/scene/surface.c | 2 +- types/scene/wlr_scene.c | 1661 +-------------------- types/scene/wlr_scene_buffer.c | 1190 +++++++++++++++ types/scene/wlr_scene_node.c | 505 +++++++ types/scene/wlr_scene_rect.c | 364 +++++ types/scene/wlr_scene_tree.c | 352 +++++ 21 files changed, 2970 insertions(+), 1999 deletions(-) create mode 100644 include/wlr/types/wlr_scene_buffer.h create mode 100644 include/wlr/types/wlr_scene_node.h create mode 100644 include/wlr/types/wlr_scene_rect.h create mode 100644 include/wlr/types/wlr_scene_tree.h create mode 100644 types/scene/wlr_scene_buffer.c create mode 100644 types/scene/wlr_scene_node.c create mode 100644 types/scene/wlr_scene_rect.c create mode 100644 types/scene/wlr_scene_tree.c diff --git a/examples/cairo-buffer.c b/examples/cairo-buffer.c index 6392b2ac5..e68b7c539 100644 --- a/examples/cairo-buffer.c +++ b/examples/cairo-buffer.c @@ -178,7 +178,7 @@ int main(void) { /* End drawing */ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( - &server.scene->tree, &buffer->base); + server.scene->tree, &buffer->base); if (!scene_buffer) { wl_display_destroy(server.display); return EXIT_FAILURE; diff --git a/examples/embedded.c b/examples/embedded.c index f0f661198..47737ed6f 100644 --- a/examples/embedded.c +++ b/examples/embedded.c @@ -153,7 +153,7 @@ static void handle_new_surface(struct wl_listener *listener, void *data) { surface->destroy.notify = surface_handle_destroy; wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); - wlr_scene_surface_create(&scene->tree, wlr_surface); + wlr_scene_surface_create(scene->tree, wlr_surface); } static void init_egl(struct wl_display *display) { diff --git a/examples/scene-graph.c b/examples/scene-graph.c index 421986534..369db0fc4 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -131,12 +132,12 @@ static void server_handle_new_surface(struct wl_listener *listener, wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); /* Border dimensions will be set in surface.commit handler */ - surface->border = wlr_scene_rect_create(&server->scene->tree, + surface->border = wlr_scene_rect_create(server->scene->tree, 0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 }); wlr_scene_node_set_position(&surface->border->node, pos, pos); surface->scene_surface = - wlr_scene_surface_create(&server->scene->tree, wlr_surface); + wlr_scene_surface_create(server->scene->tree, wlr_surface); wlr_scene_node_set_position(&surface->scene_surface->buffer->node, pos + border_width, pos + border_width); diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h index c4b40cdd0..ef8cb6a81 100644 --- a/include/types/wlr_scene.h +++ b/include/types/wlr_scene.h @@ -3,10 +3,6 @@ #include -struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node); - -void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height); - void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip); #endif diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index f6f97cfea..5b3d3e0c6 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -46,57 +48,18 @@ struct wlr_gamma_control_manager_v1; struct wlr_color_manager_v1; struct wlr_output_state; -typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( - struct wlr_scene_buffer *buffer, double *sx, double *sy); - typedef void (*wlr_scene_buffer_iterator_func_t)( struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data); -enum wlr_scene_node_type { - WLR_SCENE_NODE_TREE, - WLR_SCENE_NODE_RECT, - WLR_SCENE_NODE_BUFFER, -}; - -/** A node is an object in the scene. */ -struct wlr_scene_node { - enum wlr_scene_node_type type; - struct wlr_scene_tree *parent; - - struct wl_list link; // wlr_scene_tree.children - - bool enabled; - int x, y; // relative to parent - - struct { - struct wl_signal destroy; - } events; - - void *data; - - struct wlr_addon_set addons; - - struct { - pixman_region32_t visible; - } WLR_PRIVATE; -}; - enum wlr_scene_debug_damage_option { WLR_SCENE_DEBUG_DAMAGE_NONE, WLR_SCENE_DEBUG_DAMAGE_RERENDER, WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT }; -/** A sub-tree in the scene-graph. */ -struct wlr_scene_tree { - struct wlr_scene_node node; - - struct wl_list children; // wlr_scene_node.link -}; - /** The root scene-graph node. */ struct wlr_scene { - struct wlr_scene_tree tree; + struct wlr_scene_tree *tree; struct wl_list outputs; // wlr_scene_output.link @@ -138,13 +101,6 @@ struct wlr_scene_surface { } WLR_PRIVATE; }; -/** A scene-graph node displaying a solid-colored rectangle */ -struct wlr_scene_rect { - struct wlr_scene_node node; - int width, height; - float color[4]; -}; - struct wlr_scene_outputs_update_event { struct wlr_scene_output **active; size_t size; @@ -157,68 +113,6 @@ struct wlr_scene_output_sample_event { uint64_t release_point; }; -struct wlr_scene_frame_done_event { - struct wlr_scene_output *output; - struct timespec when; -}; - -/** A scene-graph node displaying a buffer */ -struct wlr_scene_buffer { - struct wlr_scene_node node; - - // May be NULL - struct wlr_buffer *buffer; - - struct { - struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event - struct wl_signal output_sample; // struct wlr_scene_output_sample_event - struct wl_signal frame_done; // struct wlr_scene_frame_done_event - } events; - - // May be NULL - wlr_scene_buffer_point_accepts_input_func_t point_accepts_input; - - /** - * The output that the largest area of this buffer is displayed on. - * This may be NULL if the buffer is not currently displayed on any - * outputs. - */ - struct wlr_scene_output *primary_output; - - float opacity; - enum wlr_scale_filter_mode filter_mode; - struct wlr_fbox src_box; - int dst_width, dst_height; - enum wl_output_transform transform; - pixman_region32_t opaque_region; - enum wlr_color_transfer_function transfer_function; - enum wlr_color_named_primaries primaries; - enum wlr_color_encoding color_encoding; - enum wlr_color_range color_range; - - struct { - uint64_t active_outputs; - struct wlr_texture *texture; - struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; - - bool own_buffer; - int buffer_width, buffer_height; - bool buffer_is_opaque; - - struct wlr_drm_syncobj_timeline *wait_timeline; - uint64_t wait_point; - - struct wl_listener buffer_release; - struct wl_listener renderer_destroy; - - // True if the underlying buffer is a wlr_single_pixel_buffer_v1 - bool is_single_pixel_buffer; - // If is_single_pixel_buffer is set, contains the color of the buffer - // as {R, G, B, A} where the max value of each component is UINT32_MAX - uint32_t single_pixel_buffer_color[4]; - } WLR_PRIVATE; -}; - /** A viewport for an output in the scene-graph */ struct wlr_scene_output { struct wlr_output *output; @@ -289,66 +183,6 @@ struct wlr_scene_layer_surface_v1 { } WLR_PRIVATE; }; -/** - * Immediately destroy the scene-graph node. - */ -void wlr_scene_node_destroy(struct wlr_scene_node *node); -/** - * Enable or disable this node. If a node is disabled, all of its children are - * implicitly disabled as well. - */ -void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled); -/** - * Set the position of the node relative to its parent. - */ -void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y); -/** - * Move the node right above the specified sibling. - * Asserts that node and sibling are distinct and share the same parent. - */ -void wlr_scene_node_place_above(struct wlr_scene_node *node, - struct wlr_scene_node *sibling); -/** - * Move the node right below the specified sibling. - * Asserts that node and sibling are distinct and share the same parent. - */ -void wlr_scene_node_place_below(struct wlr_scene_node *node, - struct wlr_scene_node *sibling); -/** - * Move the node above all of its sibling nodes. - */ -void wlr_scene_node_raise_to_top(struct wlr_scene_node *node); -/** - * Move the node below all of its sibling nodes. - */ -void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node); -/** - * Move the node to another location in the tree. - */ -void wlr_scene_node_reparent(struct wlr_scene_node *node, - struct wlr_scene_tree *new_parent); -/** - * Get the node's layout-local coordinates. - * - * True is returned if the node and all of its ancestors are enabled. - */ -bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx, int *ly); -/** - * Call `iterator` on each buffer in the scene-graph, with the buffer's - * position in layout coordinates. The function is called from root to leaves - * (in rendering order). - */ -void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, - wlr_scene_buffer_iterator_func_t iterator, void *user_data); -/** - * Find the topmost node in this scene-graph that contains the point at the - * given layout-local coordinates. (For surface nodes, this means accepting - * input events at that point.) Returns the node and coordinates relative to the - * returned node, or NULL if no node is found at that location. - */ -struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, - double lx, double ly, double *nx, double *ny); - /** * Create a new scene-graph. * @@ -357,6 +191,8 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, */ struct wlr_scene *wlr_scene_create(void); +void wlr_scene_destroy(struct wlr_scene *scene); + /** * Handles linux_dmabuf_v1 feedback for all surfaces in the scene. * @@ -381,11 +217,6 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, */ void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager); -/** - * Add a node displaying nothing but its children. - */ -struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); - /** * Add a node displaying a single surface to the scene-graph. * @@ -416,24 +247,6 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, struct wlr_surface *surface); -/** - * If this node represents a wlr_scene_buffer, that buffer will be returned. It - * is not legal to feed a node that does not represent a wlr_scene_buffer. - */ -struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node); - -/** - * If this node represents a wlr_scene_tree, that tree will be returned. It - * is not legal to feed a node that does not represent a wlr_scene_tree. - */ -struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node); - -/** - * If this node represents a wlr_scene_rect, that rect will be returned. It - * is not legal to feed a node that does not represent a wlr_scene_rect. - */ -struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node); - /** * If this buffer is backed by a surface, then the struct wlr_scene_surface is * returned. If not, NULL will be returned. @@ -447,135 +260,6 @@ struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface, const struct timespec *when); -/** - * Add a node displaying a solid-colored rectangle to the scene-graph. - * - * The color argument must be a premultiplied color value. - */ -struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, - int width, int height, const float color[static 4]); - -/** - * Change the width and height of an existing rectangle node. - */ -void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height); - -/** - * Change the color of an existing rectangle node. - * - * The color argument must be a premultiplied color value. - */ -void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]); - -/** - * Add a node displaying a buffer to the scene-graph. - * - * If the buffer is NULL, this node will not be displayed. - */ -struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, - struct wlr_buffer *buffer); - -/** - * Sets the buffer's backing buffer. - * - * If the buffer is NULL, the buffer node will not be displayed. - */ -void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer); - -/** - * Sets the buffer's backing buffer with a custom damage region. - * - * The damage region is in buffer-local coordinates. If the region is NULL, - * the whole buffer node will be damaged. - */ -void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, const pixman_region32_t *region); - -/** - * Options for wlr_scene_buffer_set_buffer_with_options(). - */ -struct wlr_scene_buffer_set_buffer_options { - // The damage region is in buffer-local coordinates. If the region is NULL, - // the whole buffer node will be damaged. - const pixman_region32_t *damage; - - // Wait for a timeline synchronization point before reading from the buffer. - struct wlr_drm_syncobj_timeline *wait_timeline; - uint64_t wait_point; -}; - -/** - * Sets the buffer's backing buffer. - * - * If the buffer is NULL, the buffer node will not be displayed. If options is - * NULL, empty options are used. - */ -void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options); - -/** - * Sets the buffer's opaque region. This is an optimization hint used to - * determine if buffers which reside under this one need to be rendered or not. - */ -void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - const pixman_region32_t *region); - -/** - * Set the source rectangle describing the region of the buffer which will be - * sampled to render this node. This allows cropping the buffer. - * - * If NULL, the whole buffer is sampled. By default, the source box is NULL. - */ -void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, - const struct wlr_fbox *box); - -/** - * Set the destination size describing the region of the scene-graph the buffer - * will be painted onto. This allows scaling the buffer. - * - * If zero, the destination size will be the buffer size. By default, the - * destination size is zero. - */ -void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, - int width, int height); - -/** - * Set a transform which will be applied to the buffer. - */ -void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, - enum wl_output_transform transform); - -/** -* Sets the opacity of this buffer -*/ -void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, - float opacity); - -/** -* Sets the filter mode to use when scaling the buffer -*/ -void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, - enum wlr_scale_filter_mode filter_mode); - -void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_transfer_function transfer_function); - -void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_named_primaries primaries); - -void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_encoding encoding); - -void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_range range); - -/** - * Calls the buffer's frame_done signal. - */ -void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, - struct wlr_scene_frame_done_event *event); - /** * Add a viewport for the specified output to the scene-graph. * diff --git a/include/wlr/types/wlr_scene_buffer.h b/include/wlr/types/wlr_scene_buffer.h new file mode 100644 index 000000000..0247c0051 --- /dev/null +++ b/include/wlr/types/wlr_scene_buffer.h @@ -0,0 +1,209 @@ +#ifndef WLR_TYPES_WLR_SCENE_BUFFER_H +#define WLR_TYPES_WLR_SCENE_BUFFER_H + +#include +#include +#include +#include +#include +#include +#include + +struct wlr_scene_buffer; + +typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( + struct wlr_scene_buffer *buffer, double *sx, double *sy); +typedef void (*wlr_scene_buffer_iterator_func_t)( + struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data); + +/** A scene-graph node displaying a buffer */ +struct wlr_scene_buffer { + struct wlr_scene_node node; + + // May be NULL + struct wlr_buffer *buffer; + + struct { + struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event + struct wl_signal output_enter; // struct wlr_scene_output + struct wl_signal output_leave; // struct wlr_scene_output + struct wl_signal output_sample; // struct wlr_scene_output_sample_event + struct wl_signal frame_done; // struct timespec + } events; + + // May be NULL + wlr_scene_buffer_point_accepts_input_func_t point_accepts_input; + + /** + * The output that the largest area of this buffer is displayed on. + * This may be NULL if the buffer is not currently displayed on any + * outputs. This is the output that should be used for frame callbacks, + * presentation feedback, etc. + */ + struct wlr_scene_output *primary_output; + + float opacity; + enum wlr_scale_filter_mode filter_mode; + struct wlr_fbox src_box; + int dst_width, dst_height; + enum wl_output_transform transform; + pixman_region32_t opaque_region; + enum wlr_color_transfer_function transfer_function; + enum wlr_color_named_primaries primaries; + enum wlr_color_encoding color_encoding; + enum wlr_color_range color_range; + + + struct { + uint64_t active_outputs; + struct wlr_texture *texture; + struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; + + bool own_buffer; + int buffer_width, buffer_height; + bool buffer_is_opaque; + + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; + + struct wl_listener buffer_release; + struct wl_listener renderer_destroy; + + // True if the underlying buffer is a wlr_single_pixel_buffer_v1 + bool is_single_pixel_buffer; + // If is_single_pixel_buffer is set, contains the color of the buffer + // as {R, G, B, A} where the max value of each component is UINT32_MAX + uint32_t single_pixel_buffer_color[4]; + } WLR_PRIVATE; +}; + +/** + * Add a node displaying a buffer to the scene-graph. + * + * If the buffer is NULL, this node will not be displayed. + */ +struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, + struct wlr_buffer *buffer); + +/** + * Sets the buffer's backing buffer. + * + * If the buffer is NULL, the buffer node will not be displayed. + */ +void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer); + +/** + * Sets the buffer's backing buffer with a custom damage region. + * + * The damage region is in buffer-local coordinates. If the region is NULL, + * the whole buffer node will be damaged. + */ +void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const pixman_region32_t *region); + +/** + * Options for wlr_scene_buffer_set_buffer_with_options(). + */ +struct wlr_scene_buffer_set_buffer_options { + // The damage region is in buffer-local coordinates. If the region is NULL, + // the whole buffer node will be damaged. + const pixman_region32_t *damage; + + // Wait for a timeline synchronization point before reading from the buffer. + struct wlr_drm_syncobj_timeline *wait_timeline; + uint64_t wait_point; +}; + +/** + * Sets the buffer's backing buffer. + * + * If the buffer is NULL, the buffer node will not be displayed. If options is + * NULL, empty options are used. + */ +void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options); + +/** + * Sets the buffer's opaque region. This is an optimization hint used to + * determine if buffers which reside under this one need to be rendered or not. + */ +void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, + const pixman_region32_t *region); + +/** + * Set the source rectangle describing the region of the buffer which will be + * sampled to render this node. This allows cropping the buffer. + * + * If NULL, the whole buffer is sampled. By default, the source box is NULL. + */ +void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, + const struct wlr_fbox *box); + +/** + * Set the destination size describing the region of the scene-graph the buffer + * will be painted onto. This allows scaling the buffer. + * + * If zero, the destination size will be the buffer size. By default, the + * destination size is zero. + */ +void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, + int width, int height); + +/** + * Set a transform which will be applied to the buffer. + */ +void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, + enum wl_output_transform transform); + +/** +* Sets the opacity of this buffer +*/ +void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, + float opacity); + +/** +* Sets the filter mode to use when scaling the buffer +*/ +void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, + enum wlr_scale_filter_mode filter_mode); + +struct wlr_scene_frame_done_event { + struct wlr_scene_output *output; + struct timespec when; +}; + +/** + * Calls the buffer's frame_done signal. + */ +void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, + struct wlr_scene_frame_done_event *event); + +bool wlr_scene_node_is_buffer(const struct wlr_scene_node *node); + +/** + * If this node represents a wlr_scene_buffer, that buffer will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_buffer. + */ +struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node); + +void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_transfer_function transfer_function); + +void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_named_primaries primaries); + +void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_encoding encoding); + +void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_range range); + +/** + * Call `iterator` on each buffer in the scene-graph, with the buffer's + * position in layout coordinates. The function is called from root to leaves + * (in rendering order). + */ +void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, + wlr_scene_buffer_iterator_func_t iterator, void *user_data); +#endif // WLR_TYPES_WLR_SCENE_BUFFER_H diff --git a/include/wlr/types/wlr_scene_node.h b/include/wlr/types/wlr_scene_node.h new file mode 100644 index 000000000..51a533616 --- /dev/null +++ b/include/wlr/types/wlr_scene_node.h @@ -0,0 +1,184 @@ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_WLR_SCENE_NODE_H +#define WLR_TYPES_WLR_SCENE_NODE_H + +#include +#include +#include +#include +#include + +#include + +#if WLR_HAS_XWAYLAND +#include +#endif + +struct wlr_scene_node; +struct wlr_scene_output; +struct wlr_render_list_entry; +struct wlr_render_data; +struct wlr_scene; +struct wlr_scene_buffer; +struct wlr_linux_dmabuf_feedback_v1_init_options; + +typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, + int sx, int sy, void *data); + +struct wlr_scene_update_data { + pixman_region32_t *visible; + const pixman_region32_t *update_region; + struct wlr_box update_box; + struct wl_list *outputs; + bool calculate_visibility; + bool restack_xwayland_surfaces; + +#if WLR_HAS_XWAYLAND + struct wlr_xwayland_surface *restack_above; +#endif +}; + +struct wlr_render_data { + enum wl_output_transform transform; + float scale; + struct wlr_box logical; + int trans_width, trans_height; + + struct wlr_scene_output *output; + + struct wlr_render_pass *render_pass; + pixman_region32_t damage; +}; + +struct wlr_render_list_constructor_data { + struct wlr_box box; + struct wl_array *render_list; + bool calculate_visibility; + bool highlight_transparent_region; + bool fractional_scale; +}; + +struct wlr_render_list_entry { + struct wlr_scene_node *node; + bool highlight_transparent_region; + int x, y; +}; + +struct wlr_scene_node_impl { + void (*destroy)(struct wlr_scene_node *node); + void (*set_enabled)(struct wlr_scene_node *node, bool enabled); + void (*set_position)(struct wlr_scene_node *node, int x, int y); + void (*bounds)(struct wlr_scene_node *node, + int x, int y, pixman_region32_t *visible); + void (*get_size)(struct wlr_scene_node *node, + int *width, int *height); + bool (*coords)(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr); + struct wlr_scene_node *(*at)(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny); + bool (*in_box)(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data); + void (*opaque_region)(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque); + void (*update_outputs)(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force); + void (*update)(struct wlr_scene_node *node, + pixman_region32_t *damage); + void (*visibility)(struct wlr_scene_node *node, + pixman_region32_t *visible); + void (*frame_done)(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now); + bool (*invisible)(struct wlr_scene_node *node); + bool (*construct_render_list_iterator)(struct wlr_scene_node *node, + int lx, int ly, void *_data); + void (*render)(struct wlr_render_list_entry *entry, const struct wlr_render_data *data); + void (*dmabuf_feedback)(struct wlr_render_list_entry *entry, + struct wlr_scene_output *scene_output); + void (*get_extents)(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max); + struct wl_list *(*get_children)(struct wlr_scene_node *node); + void (*restack_xwayland_surface)(struct wlr_scene_node *node, + struct wlr_box *box, struct wlr_scene_update_data *data); + void (*cleanup_when_disabled)(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs); +}; + +struct wlr_scene_node { + const struct wlr_scene_node_impl *impl; + + struct wlr_scene_tree *parent; + struct wlr_scene *scene; + + struct wl_list link; // wlr_scene_tree.children + + bool enabled; + int x, y; // relative to parent + + struct { + struct wl_signal destroy; + } events; + + void *data; + + struct wlr_addon_set addons; + + struct { + pixman_region32_t visible; + } WLR_PRIVATE; + + struct wl_list children; // wlr_scene_node.link +}; + +void wlr_scene_node_init(struct wlr_scene_node *node, + const struct wlr_scene_node_impl *impl, struct wlr_scene_tree *parent); +void wlr_scene_node_destroy(struct wlr_scene_node *node); + +void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled); +void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y); +void wlr_scene_node_place_above(struct wlr_scene_node *node, + struct wlr_scene_node *sibling); +void wlr_scene_node_place_below(struct wlr_scene_node *node, + struct wlr_scene_node *sibling); +void wlr_scene_node_raise_to_top(struct wlr_scene_node *node); +void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node); +void wlr_scene_node_reparent(struct wlr_scene_node *node, + struct wlr_scene_tree *new_parent); +void wlr_scene_node_bounds(struct wlr_scene_node *node, + int x, int y, pixman_region32_t *visible); +void wlr_scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height); +bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr); +struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny); +bool wlr_scene_node_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data); +void wlr_scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque); +void wlr_scene_node_update_outputs(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force); +void wlr_scene_node_update(struct wlr_scene_node *node, + pixman_region32_t *damage); +void wlr_scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible); +void wlr_scene_node_send_frame_done(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now); +bool wlr_scene_node_invisible(struct wlr_scene_node *node); +bool wlr_scene_node_construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data); +void wlr_scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data); +void wlr_scene_node_dmabuf_feedback(struct wlr_render_list_entry *entry, + struct wlr_scene_output *scene_output); +void wlr_scene_node_restack_xwayland_surface_below(struct wlr_scene_node *node); +void wlr_scene_node_get_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max); +struct wl_list *wlr_scene_node_get_children(struct wlr_scene_node *node); +void wlr_scene_node_restack_xwayland_surface(struct wlr_scene_node *node, + struct wlr_box *box, struct wlr_scene_update_data *data); +void wlr_scene_node_cleanup_when_disabled(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs); + +#endif diff --git a/include/wlr/types/wlr_scene_rect.h b/include/wlr/types/wlr_scene_rect.h new file mode 100644 index 000000000..ad2349420 --- /dev/null +++ b/include/wlr/types/wlr_scene_rect.h @@ -0,0 +1,40 @@ +#ifndef WLR_TYPES_WLR_SCENE_RECT_H +#define WLR_TYPES_WLR_SCENE_RECT_H + +#include + +struct wlr_scene_rect { + struct wlr_scene_node node; + int width, height; + float color[4]; +}; + +/** + * Add a node displaying a solid-colored rectangle to the scene-graph. + * + * The color argument must be a premultiplied color value. + */ +struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, + int width, int height, const float color[static 4]); + +/** + * Change the width and height of an existing rectangle node. + */ +void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height); + +/** + * Change the color of an existing rectangle node. + * + * The color argument must be a premultiplied color value. + */ +void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]); + +bool wlr_scene_node_is_rect(const struct wlr_scene_node *node); + +/** + * If this node represents a wlr_scene_rect, that rect will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_rect. + */ +struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node); + +#endif diff --git a/include/wlr/types/wlr_scene_tree.h b/include/wlr/types/wlr_scene_tree.h new file mode 100644 index 000000000..b474ac776 --- /dev/null +++ b/include/wlr/types/wlr_scene_tree.h @@ -0,0 +1,32 @@ +#ifndef WLR_TYPES_WLR_SCENE_TREE_H +#define WLR_TYPES_WLR_SCENE_TREE_H + +#include + +#include + +#include + +struct wlr_scene; + +struct wlr_scene_tree { + struct wlr_scene_node node; + + struct wl_list children; // wlr_scene_node.link +}; + +/** + * Add a node displaying nothing but its children. + */ +struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); +struct wlr_scene_tree *wlr_root_scene_tree_create(struct wlr_scene *scene); + +bool wlr_scene_node_is_tree(const struct wlr_scene_node *node); + +/** + * If this node represents a wlr_scene_tree, that tree will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_tree. + */ +struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node); + +#endif // WLR_TYPES_WLR_SCENE_TREE_H diff --git a/test/bench_scene.c b/test/bench_scene.c index 6d6b270f0..16533a350 100644 --- a/test/bench_scene.c +++ b/test/bench_scene.c @@ -1,6 +1,7 @@ #include #include #include +#include struct tree_spec { // Parameters for the tree we'll construct @@ -62,7 +63,7 @@ static bool build_tree(struct wlr_scene_tree *parent, struct tree_spec *spec, static bool bench_create_tree(struct wlr_scene *scene, struct tree_spec *spec) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); - if (!build_tree(&scene->tree, spec, 0, 0, 0)) { + if (!build_tree(scene->tree, spec, 0, 0, 0)) { fprintf(stderr, "build_tree failed\n"); return false; } @@ -90,7 +91,7 @@ static void bench_scene_node_at(struct wlr_scene *scene, struct tree_spec *spec) double ly = (double)(i * 53 % spec->max_y); double nx, ny; struct wlr_scene_node *node = - wlr_scene_node_at(&scene->tree.node, lx, ly, &nx, &ny); + wlr_scene_node_at(&scene->tree->node, lx, ly, &nx, &ny); if (node != NULL) { hits++; } @@ -119,7 +120,7 @@ static void bench_scene_node_for_each_buffer(struct wlr_scene *scene, struct tre clock_gettime(CLOCK_MONOTONIC, &start); for (int i = 0; i < iters; i++) { - wlr_scene_node_for_each_buffer(&scene->tree.node, + wlr_scene_node_for_each_buffer(&scene->tree->node, noop_iterator, &hits); } clock_gettime(CLOCK_MONOTONIC, &end); @@ -149,6 +150,6 @@ int main(void) { bench_scene_node_at(scene, &spec); bench_scene_node_for_each_buffer(scene, &spec); - wlr_scene_node_destroy(&scene->tree.node); + wlr_scene_destroy(scene); return 0; } diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index fe242e1a9..b1bd39bd4 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -364,8 +364,8 @@ static struct tinywl_toplevel *desktop_toplevel_at( * We only care about surface nodes as we are specifically looking for a * surface in the surface tree of a tinywl_toplevel. */ struct wlr_scene_node *node = wlr_scene_node_at( - &server->scene->tree.node, lx, ly, sx, sy); - if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { + &server->scene->tree->node, lx, ly, sx, sy); + if (node == NULL || !wlr_scene_node_is_buffer(node)) { return NULL; } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); @@ -810,7 +810,7 @@ static void server_new_xdg_toplevel(struct wl_listener *listener, void *data) { toplevel->server = server; toplevel->xdg_toplevel = xdg_toplevel; toplevel->scene_tree = - wlr_scene_xdg_surface_create(&toplevel->server->scene->tree, xdg_toplevel->base); + wlr_scene_xdg_surface_create(toplevel->server->scene->tree, xdg_toplevel->base); toplevel->scene_tree->node.data = toplevel; xdg_toplevel->base->data = toplevel->scene_tree; @@ -1090,7 +1090,7 @@ int main(int argc, char *argv[]) { wl_list_remove(&server.new_output.link); - wlr_scene_node_destroy(&server.scene->tree.node); + wlr_scene_destroy(server.scene); wlr_xcursor_manager_destroy(server.cursor_mgr); wlr_cursor_destroy(server.cursor); wlr_allocator_destroy(server.allocator); diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 7d4b8928a..51b4af008 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -34,43 +34,43 @@ struct scene_node_source_frame_event { static size_t last_output_num = 0; -static void _get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, - int *x_min, int *y_min, int *x_max, int *y_max) { - switch (node->type) { - case WLR_SCENE_NODE_TREE:; - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - _get_scene_node_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max); - } - break; - case WLR_SCENE_NODE_RECT: - case WLR_SCENE_NODE_BUFFER:; - struct wlr_box node_box = { .x = lx, .y = ly }; - scene_node_get_size(node, &node_box.width, &node_box.height); +// static void _get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, +// int *x_min, int *y_min, int *x_max, int *y_max) { +// switch (node->type) { +// case WLR_SCENE_NODE_TREE:; +// struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); +// struct wlr_scene_node *child; +// wl_list_for_each(child, &scene_tree->children, link) { +// _get_scene_node_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max); +// } +// break; +// case WLR_SCENE_NODE_RECT: +// case WLR_SCENE_NODE_BUFFER:; +// struct wlr_box node_box = { .x = lx, .y = ly }; +// scene_node_get_size(node, &node_box.width, &node_box.height); - if (node_box.x < *x_min) { - *x_min = node_box.x; - } - if (node_box.y < *y_min) { - *y_min = node_box.y; - } - if (node_box.x + node_box.width > *x_max) { - *x_max = node_box.x + node_box.width; - } - if (node_box.y + node_box.height > *y_max) { - *y_max = node_box.y + node_box.height; - } - break; - } -} +// if (node_box.x < *x_min) { +// *x_min = node_box.x; +// } +// if (node_box.y < *y_min) { +// *y_min = node_box.y; +// } +// if (node_box.x + node_box.width > *x_max) { +// *x_max = node_box.x + node_box.width; +// } +// if (node_box.y + node_box.height > *y_max) { +// *y_max = node_box.y + node_box.height; +// } +// break; +// } +// } static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) { int lx = 0, ly = 0; wlr_scene_node_coords(node, &lx, &ly); *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX }; int x_max = INT_MIN, y_max = INT_MIN; - _get_scene_node_extents(node, lx, ly, &box->x, &box->y, &x_max, &y_max); + wlr_scene_node_get_extents(node, lx, ly, &box->x, &box->y, &x_max, &y_max); box->width = x_max - box->x; box->height = y_max - box->y; } @@ -321,7 +321,7 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_w wlr_output_init_render(&source->output, allocator, renderer); - struct wlr_scene *scene = scene_node_get_root(node); + struct wlr_scene *scene = node->scene; source->scene_output = wlr_scene_output_create(scene, &source->output); source->node_destroy.notify = source_handle_node_destroy; diff --git a/types/meson.build b/types/meson.build index bc3d32cd9..b0a373b4a 100644 --- a/types/meson.build +++ b/types/meson.build @@ -19,6 +19,10 @@ wlr_files += files( 'scene/output_layout.c', 'scene/xdg_shell.c', 'scene/layer_shell_v1.c', + 'scene/wlr_scene_node.c', + 'scene/wlr_scene_tree.c', + 'scene/wlr_scene_buffer.c', + 'scene/wlr_scene_rect.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c index 88185904e..2a784a024 100644 --- a/types/scene/output_layout.c +++ b/types/scene/output_layout.c @@ -135,7 +135,7 @@ struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene wl_signal_add(&output_layout->events.change, &sol->layout_change); sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy; - wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy); + wl_signal_add(&scene->tree->node.events.destroy, &sol->scene_destroy); return sol; } diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index 4e0a3f999..261b7a068 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -327,7 +327,7 @@ static struct wlr_scene_subsurface_tree *get_subsurface_tree_from_node( static bool subsurface_tree_set_clip(struct wlr_scene_node *node, const struct wlr_box *clip) { - if (node->type != WLR_SCENE_NODE_TREE) { + if (wlr_scene_node_get_children(node) == NULL) { return false; } @@ -350,9 +350,9 @@ static bool subsurface_tree_set_clip(struct wlr_scene_node *node, subsurface_tree_reconfigure_clip(tree); } - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wl_list *childrens = wlr_scene_node_get_children(node); struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { + wl_list_for_each(child, childrens, link) { discovered_subsurface_tree |= subsurface_tree_set_clip(child, clip); } diff --git a/types/scene/surface.c b/types/scene/surface.c index a6958645c..53c03900f 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -95,7 +95,7 @@ static void handle_scene_buffer_outputs_update( struct wlr_scene_surface *surface = wl_container_of(listener, surface, outputs_update); struct wlr_scene_outputs_update_event *event = data; - struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); + struct wlr_scene *scene = surface->buffer->node.scene; // If the surface is no longer visible on any output, keep the last sent // preferred configuration to avoid unnecessary redraws diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7231422e8..e93ddc0be 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -31,149 +31,30 @@ #define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30 #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 -struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_TREE); - struct wlr_scene_tree *tree = wl_container_of(node, tree, node); - return tree; -} - -struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_RECT); - struct wlr_scene_rect *rect = wl_container_of(node, rect, node); - return rect; -} - -struct wlr_scene_buffer *wlr_scene_buffer_from_node( - struct wlr_scene_node *node) { - assert(node->type == WLR_SCENE_NODE_BUFFER); - struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node); - return buffer; -} - -struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { - struct wlr_scene_tree *tree; - if (node->type == WLR_SCENE_NODE_TREE) { - tree = wlr_scene_tree_from_node(node); - } else { - tree = node->parent; - } - - while (tree->node.parent != NULL) { - tree = tree->node.parent; - } - struct wlr_scene *scene = wl_container_of(tree, scene, tree); - return scene; -} - -static void scene_node_init(struct wlr_scene_node *node, - enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { - *node = (struct wlr_scene_node){ - .type = type, - .parent = parent, - .enabled = true, - }; - - wl_list_init(&node->link); - - wl_signal_init(&node->events.destroy); - pixman_region32_init(&node->visible); - - if (parent != NULL) { - wl_list_insert(parent->children.prev, &node->link); - } - - wlr_addon_set_init(&node->addons); -} - struct highlight_region { pixman_region32_t region; struct timespec when; struct wl_list link; }; -static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer); -static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, - struct wlr_texture *texture); - -void wlr_scene_node_destroy(struct wlr_scene_node *node) { - if (node == NULL) { - return; - } - - // We want to call the destroy listeners before we do anything else - // in case the destroy signal would like to remove children before they - // are recursively destroyed. - wl_signal_emit_mutable(&node->events.destroy, NULL); - wlr_addon_set_finish(&node->addons); - - wlr_scene_node_set_enabled(node, false); - - struct wlr_scene *scene = scene_node_get_root(node); - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - scene_buffer_set_buffer(scene_buffer, NULL); - scene_buffer_set_texture(scene_buffer, NULL); - pixman_region32_fini(&scene_buffer->opaque_region); - wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); - - assert(wl_list_empty(&scene_buffer->events.outputs_update.listener_list)); - assert(wl_list_empty(&scene_buffer->events.output_sample.listener_list)); - assert(wl_list_empty(&scene_buffer->events.frame_done.listener_list)); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - - if (scene_tree == &scene->tree) { - assert(!node->parent); - struct wlr_scene_output *scene_output, *scene_output_tmp; - wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { - wlr_scene_output_destroy(scene_output); - } - - wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); - wl_list_remove(&scene->gamma_control_manager_v1_destroy.link); - wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link); - } else { - assert(node->parent); - } - - struct wlr_scene_node *child, *child_tmp; - wl_list_for_each_safe(child, child_tmp, - &scene_tree->children, link) { - wlr_scene_node_destroy(child); - } - } - - assert(wl_list_empty(&node->events.destroy.listener_list)); - - wl_list_remove(&node->link); - pixman_region32_fini(&node->visible); - free(node); -} - -static void scene_tree_init(struct wlr_scene_tree *tree, - struct wlr_scene_tree *parent) { - *tree = (struct wlr_scene_tree){0}; - scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); - wl_list_init(&tree->children); -} - struct wlr_scene *wlr_scene_create(void) { struct wlr_scene *scene = calloc(1, sizeof(*scene)); if (scene == NULL) { return NULL; } - scene_tree_init(&scene->tree, NULL); + scene->tree = wlr_root_scene_tree_create(scene); + if (scene->tree == NULL) { + free(scene); + return NULL; + } + scene->tree->node.data = scene; wl_list_init(&scene->outputs); wl_list_init(&scene->linux_dmabuf_v1_destroy.link); wl_list_init(&scene->gamma_control_manager_v1_destroy.link); wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link); - scene->restack_xwayland_surfaces = true; - const char *debug_damage_options[] = { "none", "rerender", @@ -189,116 +70,13 @@ struct wlr_scene *wlr_scene_create(void) { return scene; } -struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { - assert(parent); - - struct wlr_scene_tree *tree = calloc(1, sizeof(*tree)); - if (tree == NULL) { - return NULL; +void wlr_scene_destroy(struct wlr_scene *scene) { + if (scene == NULL) { + return; } - scene_tree_init(tree, parent); - return tree; -} - -typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, - int sx, int sy, void *data); - -static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, - scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { - if (!node->enabled) { - return false; - } - - switch (node->type) { - case WLR_SCENE_NODE_TREE:; - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each_reverse(child, &scene_tree->children, link) { - if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { - return true; - } - } - break; - case WLR_SCENE_NODE_RECT: - case WLR_SCENE_NODE_BUFFER:; - struct wlr_box node_box = { .x = lx, .y = ly }; - scene_node_get_size(node, &node_box.width, &node_box.height); - - if (wlr_box_intersects(&node_box, box) && - iterator(node, lx, ly, user_data)) { - return true; - } - break; - } - - return false; -} - -static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, - scene_node_box_iterator_func_t iterator, void *user_data) { - int x, y; - wlr_scene_node_coords(node, &x, &y); - - return _scene_nodes_in_box(node, box, iterator, user_data, x, y); -} - -static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, - pixman_region32_t *opaque) { - int width, height; - scene_node_get_size(node, &width, &height); - - if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); - if (scene_rect->color[3] != 1) { - return; - } - } else if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (!scene_buffer->buffer) { - return; - } - - if (scene_buffer->opacity != 1) { - return; - } - - if (!scene_buffer->buffer_is_opaque) { - pixman_region32_copy(opaque, &scene_buffer->opaque_region); - pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); - pixman_region32_translate(opaque, x, y); - return; - } - } - - pixman_region32_fini(opaque); - pixman_region32_init_rect(opaque, x, y, width, height); -} - -struct scene_update_data { - pixman_region32_t *visible; - const pixman_region32_t *update_region; - struct wlr_box update_box; - struct wl_list *outputs; - bool calculate_visibility; - bool restack_xwayland_surfaces; - -#if WLR_HAS_XWAYLAND - struct wlr_xwayland_surface *restack_above; -#endif -}; - -static uint32_t region_area(const pixman_region32_t *region) { - uint32_t area = 0; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); - for (int i = 0; i < nrects; ++i) { - area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); - } - - return area; + wlr_scene_node_destroy(&scene->tree->node); + free(scene); } static void scale_region(pixman_region32_t *region, float scale, bool round_up) { @@ -309,33 +87,13 @@ static void scale_region(pixman_region32_t *region, float scale, bool round_up) } } -struct render_data { - enum wl_output_transform transform; - float scale; - struct wlr_box logical; - int trans_width, trans_height; - - struct wlr_scene_output *output; - - struct wlr_render_pass *render_pass; - pixman_region32_t damage; -}; - -static void logical_to_buffer_coords(pixman_region32_t *region, const struct render_data *data, +static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data, bool round_up) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); scale_region(region, data->scale, round_up); wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); } -static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) { - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - - wlr_region_transform(damage, damage, - wlr_output_transform_invert(output->transform), width, height); -} - static int scale_length(int length, int offset, float scale) { return round((offset + length) * scale) - round(offset * scale); } @@ -347,7 +105,7 @@ static void scale_box(struct wlr_box *box, float scale) { box->y = round(box->y * scale); } -static void transform_output_box(struct wlr_box *box, const struct render_data *data) { +static void transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); scale_box(box, data->scale); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); @@ -381,1169 +139,11 @@ static void scene_output_damage_whole(struct wlr_scene_output *scene_output) { pixman_region32_fini(&damage); } -static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region32_t *damage) { - if (pixman_region32_empty(damage)) { - return; - } - - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - pixman_region32_t output_damage; - pixman_region32_init(&output_damage); - pixman_region32_copy(&output_damage, damage); - pixman_region32_translate(&output_damage, - -scene_output->x, -scene_output->y); - scale_region(&output_damage, scene_output->output->scale, true); - output_to_buffer_coords(&output_damage, scene_output->output); - scene_output_damage(scene_output, &output_damage); - pixman_region32_fini(&output_damage); - } -} - -static void update_node_update_outputs(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore, - struct wlr_scene_output *force) { - if (node->type != WLR_SCENE_NODE_BUFFER) { - return; - } - - 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; - uint64_t active_outputs = 0; - - if (!pixman_region32_empty(&node->visible)) { - uint32_t visible_area = region_area(&node->visible); - - // let's update the outputs in two steps: - // - the primary outputs - // - the enter/leave signals - // This ensures that the enter/leave signals can rely on the primary output - // to have a reasonable value. Otherwise, they may get a value that's in - // the middle of a calculation. - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, outputs, link) { - if (scene_output == ignore) { - continue; - } - - if (!scene_output->output->enabled) { - continue; - } - - struct wlr_box output_box = { - .x = scene_output->x, - .y = scene_output->y, - }; - wlr_output_effective_resolution(scene_output->output, - &output_box.width, &output_box.height); - - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, - output_box.x, output_box.y, output_box.width, output_box.height); - uint32_t overlap = region_area(&intersection); - pixman_region32_fini(&intersection); - - // If the overlap accounts for less than 10% of the visible node area, - // ignore this output - if (overlap >= 0.1 * visible_area) { - if (overlap >= largest_overlap) { - largest_overlap = overlap; - scene_buffer->primary_output = scene_output; - } - - active_outputs |= 1ull << scene_output->index; - count++; - } - } - } - - if (old_primary_output != scene_buffer->primary_output) { - scene_buffer->prev_feedback_options = - (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; - } - - // if there are active outputs on this node, we should always have a primary - // output - assert(!active_outputs || scene_buffer->primary_output); - - // Skip output update event if nothing was updated - if (scene_buffer->active_outputs == active_outputs && - (!force || ((1ull << force->index) & ~active_outputs)) && - old_primary_output == scene_buffer->primary_output) { - return; - } - - struct wlr_scene_output *outputs_array[64]; - struct wlr_scene_outputs_update_event event = { - .active = outputs_array, - .size = count, - }; - - size_t i = 0; - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, outputs, link) { - if (~active_outputs & (1ull << scene_output->index)) { - continue; - } - - assert(i < count); - outputs_array[i++] = scene_output; - } - - scene_buffer->active_outputs = active_outputs; - wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); -} - -#if WLR_HAS_XWAYLAND -static struct wlr_xwayland_surface *scene_node_try_get_managed_xwayland_surface( - struct wlr_scene_node *node) { - if (node->type != WLR_SCENE_NODE_BUFFER) { - return NULL; - } - - struct wlr_scene_buffer *buffer_node = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface *surface_node = wlr_scene_surface_try_from_buffer(buffer_node); - if (!surface_node) { - return NULL; - } - - struct wlr_xwayland_surface *xwayland_surface = - wlr_xwayland_surface_try_from_wlr_surface(surface_node->surface); - if (!xwayland_surface || xwayland_surface->override_redirect) { - return NULL; - } - - return xwayland_surface; -} - -static void restack_xwayland_surface(struct wlr_scene_node *node, - struct wlr_box *box, struct scene_update_data *data) { - struct wlr_xwayland_surface *xwayland_surface = - scene_node_try_get_managed_xwayland_surface(node); - if (!xwayland_surface) { - return; - } - - // ensure this node is entirely inside the update region. If not, we can't - // restack this node since we're not considering the whole thing. - if (wlr_box_contains_box(&data->update_box, box)) { - if (data->restack_above) { - wlr_xwayland_surface_restack(xwayland_surface, data->restack_above, XCB_STACK_MODE_BELOW); - } else { - wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_ABOVE); - } - } - - data->restack_above = xwayland_surface; -} -#endif - -static bool scene_node_update_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { - struct scene_update_data *data = _data; - - struct wlr_box box = { .x = lx, .y = ly }; - scene_node_get_size(node, &box.width, &box.height); - - pixman_region32_subtract(&node->visible, &node->visible, data->update_region); - pixman_region32_union(&node->visible, &node->visible, data->visible); - pixman_region32_intersect_rect(&node->visible, &node->visible, - lx, ly, box.width, box.height); - - if (data->calculate_visibility) { - pixman_region32_t opaque; - pixman_region32_init(&opaque); - scene_node_opaque_region(node, lx, ly, &opaque); - pixman_region32_subtract(data->visible, data->visible, &opaque); - pixman_region32_fini(&opaque); - } - - update_node_update_outputs(node, data->outputs, NULL, NULL); -#if WLR_HAS_XWAYLAND - if (data->restack_xwayland_surfaces) { - restack_xwayland_surface(node, &box, data); - } -#endif - - return false; -} - -static void scene_node_visibility(struct wlr_scene_node *node, - pixman_region32_t *visible) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_visibility(child, visible); - } - return; - } - - pixman_region32_union(visible, visible, &node->visible); -} - -static void scene_node_bounds(struct wlr_scene_node *node, - int x, int y, pixman_region32_t *visible) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_bounds(child, x + child->x, y + child->y, visible); - } - return; - } - - int width, height; - scene_node_get_size(node, &width, &height); - pixman_region32_union_rect(visible, visible, x, y, width, height); -} - -static void scene_update_region(struct wlr_scene *scene, - const pixman_region32_t *update_region) { - pixman_region32_t visible; - pixman_region32_init(&visible); - pixman_region32_copy(&visible, update_region); - - struct pixman_box32 *region_box = pixman_region32_extents(update_region); - struct scene_update_data data = { - .visible = &visible, - .update_region = update_region, - .update_box = { - .x = region_box->x1, - .y = region_box->y1, - .width = region_box->x2 - region_box->x1, - .height = region_box->y2 - region_box->y1, - }, - .outputs = &scene->outputs, - .calculate_visibility = scene->calculate_visibility, - .restack_xwayland_surfaces = scene->restack_xwayland_surfaces, - }; - - // update node visibility and output enter/leave events - scene_nodes_in_box(&scene->tree.node, &data.update_box, scene_node_update_iterator, &data); - - pixman_region32_fini(&visible); -} - -static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, - bool xwayland_restack, struct wl_list *outputs) { - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - if (!child->enabled) { - continue; - } - - scene_node_cleanup_when_disabled(child, xwayland_restack, outputs); - } - return; - } - - pixman_region32_clear(&node->visible); - update_node_update_outputs(node, outputs, NULL, NULL); - -#if WLR_HAS_XWAYLAND - if (xwayland_restack) { - struct wlr_xwayland_surface *xwayland_surface = - scene_node_try_get_managed_xwayland_surface(node); - if (!xwayland_surface) { - return; - } - - wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW); - } -#endif -} - -/** - * Updates the nodes visibility, xwayland restacking, send leave/enter events - * and damages the screen. The damage region is used to not only damage the - * screen, but to direct the update logic to only update certain parts of the - * screen and not the whole thing. If a NULL damage is given, the damage is - * assumed to be the previous nodes visibility. - * - * Currently, the only usage for an explicit damage is for update scenarios where - * the scene node might be enabled/disabled. If the scene node is disabled, the - * update logic will ignore the node. This is normally desirable as most update - * scenarios like updating the color or whatever. However, it's not what we want - * when disabling the node. Note that reparenting the node could lead to the node - * being reparented to a disabled super tree. - */ -static void scene_node_update(struct wlr_scene_node *node, - pixman_region32_t *damage) { - struct wlr_scene *scene = scene_node_get_root(node); - - int x, y; - if (!wlr_scene_node_coords(node, &x, &y)) { - // We assume explicit damage on a disabled tree means the node was just - // disabled. - if (damage) { - scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs); - - scene_update_region(scene, damage); - scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); - } - - return; - } - - pixman_region32_t visible; - if (!damage) { - pixman_region32_init(&visible); - scene_node_visibility(node, &visible); - damage = &visible; - } - - pixman_region32_t update_region; - pixman_region32_init(&update_region); - pixman_region32_copy(&update_region, damage); - scene_node_bounds(node, x, y, &update_region); - - scene_update_region(scene, &update_region); - pixman_region32_fini(&update_region); - - scene_node_visibility(node, damage); - scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); -} - -struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, - int width, int height, const float color[static 4]) { - assert(parent); - assert(width >= 0 && height >= 0); - - struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); - if (scene_rect == NULL) { - return NULL; - } - scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent); - - scene_rect->width = width; - scene_rect->height = height; - memcpy(scene_rect->color, color, sizeof(scene_rect->color)); - - scene_node_update(&scene_rect->node, NULL); - - return scene_rect; -} - -void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { - if (rect->width == width && rect->height == height) { - return; - } - - assert(width >= 0 && height >= 0); - - rect->width = width; - rect->height = height; - scene_node_update(&rect->node, NULL); -} - -void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { - if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { - return; - } - - memcpy(rect->color, color, sizeof(rect->color)); - scene_node_update(&rect->node, NULL); -} - -static void scene_buffer_handle_buffer_release(struct wl_listener *listener, - void *data) { - struct wlr_scene_buffer *scene_buffer = - wl_container_of(listener, scene_buffer, buffer_release); - - scene_buffer->buffer = NULL; - wl_list_remove(&scene_buffer->buffer_release.link); - wl_list_init(&scene_buffer->buffer_release.link); -} - -static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer) { - wl_list_remove(&scene_buffer->buffer_release.link); - wl_list_init(&scene_buffer->buffer_release.link); - if (scene_buffer->own_buffer) { - wlr_buffer_unlock(scene_buffer->buffer); - } - scene_buffer->buffer = NULL; - scene_buffer->own_buffer = false; - scene_buffer->buffer_width = scene_buffer->buffer_height = 0; - scene_buffer->buffer_is_opaque = false; - - if (!buffer) { - return; - } - - scene_buffer->own_buffer = true; - scene_buffer->buffer = wlr_buffer_lock(buffer); - scene_buffer->buffer_width = buffer->width; - scene_buffer->buffer_height = buffer->height; - scene_buffer->buffer_is_opaque = wlr_buffer_is_opaque(buffer); - - scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; - wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); -} - -static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy); - scene_buffer_set_texture(scene_buffer, NULL); -} - -static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, - struct wlr_texture *texture) { - wl_list_remove(&scene_buffer->renderer_destroy.link); - wlr_texture_destroy(scene_buffer->texture); - scene_buffer->texture = texture; - - if (texture != NULL) { - scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy; - wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy); - } else { - wl_list_init(&scene_buffer->renderer_destroy.link); - } -} - -static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer, - struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { - wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); - if (timeline != NULL) { - scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); - scene_buffer->wait_point = point; - } else { - scene_buffer->wait_timeline = NULL; - scene_buffer->wait_point = 0; - } -} - -struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, - struct wlr_buffer *buffer) { - struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); - if (scene_buffer == NULL) { - return NULL; - } - assert(parent); - scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent); - - wl_signal_init(&scene_buffer->events.outputs_update); - wl_signal_init(&scene_buffer->events.output_sample); - wl_signal_init(&scene_buffer->events.frame_done); - - pixman_region32_init(&scene_buffer->opaque_region); - wl_list_init(&scene_buffer->buffer_release.link); - wl_list_init(&scene_buffer->renderer_destroy.link); - scene_buffer->opacity = 1; - - scene_buffer_set_buffer(scene_buffer, buffer); - scene_node_update(&scene_buffer->node, NULL); - - return scene_buffer; -} - -void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options) { - const struct wlr_scene_buffer_set_buffer_options default_options = {0}; - if (options == NULL) { - options = &default_options; - } - - // specifying a region for a NULL buffer doesn't make sense. We need to know - // about the buffer to scale the buffer local coordinates down to scene - // coordinates. - assert(buffer || !options->damage); - - bool mapped = buffer != NULL; - bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL; - - if (!mapped && !prev_mapped) { - // unmapping already unmapped buffer - noop - return; - } - - // if this node used to not be mapped or its previous displayed - // buffer region will be different from what the new buffer would - // produce we need to update the node. - bool update = mapped != prev_mapped; - if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { - update = update || scene_buffer->buffer_width != buffer->width || - scene_buffer->buffer_height != buffer->height; - } - - // If this is a buffer change, check if it's a single pixel buffer. - // Cache that so we can still apply rendering optimisations even when - // the original buffer has been freed after texture upload. - if (buffer != scene_buffer->buffer) { - scene_buffer->is_single_pixel_buffer = false; - struct wlr_client_buffer *client_buffer = NULL; - if (buffer != NULL) { - client_buffer = wlr_client_buffer_get(buffer); - } - if (client_buffer != NULL && client_buffer->source != NULL) { - struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = - wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); - if (single_pixel_buffer != NULL) { - scene_buffer->is_single_pixel_buffer = true; - scene_buffer->single_pixel_buffer_color[0] = single_pixel_buffer->r; - scene_buffer->single_pixel_buffer_color[1] = single_pixel_buffer->g; - scene_buffer->single_pixel_buffer_color[2] = single_pixel_buffer->b; - scene_buffer->single_pixel_buffer_color[3] = single_pixel_buffer->a; - } - } - } - - scene_buffer_set_buffer(scene_buffer, buffer); - scene_buffer_set_texture(scene_buffer, NULL); - scene_buffer_set_wait_timeline(scene_buffer, - options->wait_timeline, options->wait_point); - - if (update) { - scene_node_update(&scene_buffer->node, NULL); - // updating the node will already damage the whole node for us. Return - // early to not damage again - return; - } - - int lx, ly; - if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { - return; - } - - pixman_region32_t fallback_damage; - pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); - const pixman_region32_t *damage = options->damage; - if (!damage) { - damage = &fallback_damage; - } - - struct wlr_fbox box = scene_buffer->src_box; - if (wlr_fbox_empty(&box)) { - box.x = 0; - box.y = 0; - box.width = buffer->width; - box.height = buffer->height; - } - - wlr_fbox_transform(&box, &box, scene_buffer->transform, - buffer->width, buffer->height); - - float scale_x, scale_y; - if (scene_buffer->dst_width || scene_buffer->dst_height) { - scale_x = scene_buffer->dst_width / box.width; - scale_y = scene_buffer->dst_height / box.height; - } else { - scale_x = buffer->width / box.width; - scale_y = buffer->height / box.height; - } - - pixman_region32_t trans_damage; - pixman_region32_init(&trans_damage); - wlr_region_transform(&trans_damage, damage, - scene_buffer->transform, buffer->width, buffer->height); - pixman_region32_intersect_rect(&trans_damage, &trans_damage, - box.x, box.y, box.width, box.height); - pixman_region32_translate(&trans_damage, -box.x, -box.y); - - struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node); - struct wlr_scene_output *scene_output; - wl_list_for_each(scene_output, &scene->outputs, link) { - float output_scale = scene_output->output->scale; - float output_scale_x = output_scale * scale_x; - float output_scale_y = output_scale * scale_y; - pixman_region32_t output_damage; - pixman_region32_init(&output_damage); - wlr_region_scale_xy(&output_damage, &trans_damage, - output_scale_x, output_scale_y); - - // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels. - // If the buffer is upscaled on the given axis (output_scale_* > 1.0, - // buffer_scale_* < 1.0), its contents will bleed into adjacent - // (ceil(output_scale_* / 2)) output pixels because of linear filtering. - // Additionally, if the buffer is downscaled (output_scale_* < 1.0, - // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of - // buffer pixels, its contents will bleed into neighboring output pixels. - // Handle both cases by computing buffer_scale_{x,y} and checking if they are - // integer numbers; ceilf() is used to ensure that the distance is at least 1. - float buffer_scale_x = 1.0f / output_scale_x; - float buffer_scale_y = 1.0f / output_scale_y; - int dist_x = floor(buffer_scale_x) != buffer_scale_x ? - (int)ceilf(output_scale_x / 2.0f) : 0; - int dist_y = floor(buffer_scale_y) != buffer_scale_y ? - (int)ceilf(output_scale_y / 2.0f) : 0; - // TODO: expand with per-axis distances - wlr_region_expand(&output_damage, &output_damage, - dist_x >= dist_y ? dist_x : dist_y); - - pixman_region32_t cull_region; - pixman_region32_init(&cull_region); - pixman_region32_copy(&cull_region, &scene_buffer->node.visible); - scale_region(&cull_region, output_scale, true); - pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); - pixman_region32_intersect(&output_damage, &output_damage, &cull_region); - pixman_region32_fini(&cull_region); - - pixman_region32_translate(&output_damage, - (int)round((lx - scene_output->x) * output_scale), - (int)round((ly - scene_output->y) * output_scale)); - output_to_buffer_coords(&output_damage, scene_output->output); - scene_output_damage(scene_output, &output_damage); - pixman_region32_fini(&output_damage); - } - - pixman_region32_fini(&trans_damage); - pixman_region32_fini(&fallback_damage); -} - -void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, const pixman_region32_t *damage) { - const struct wlr_scene_buffer_set_buffer_options options = { - .damage = damage, - }; - wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, &options); -} - -void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer) { - wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, NULL); -} - -void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - const pixman_region32_t *region) { - if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { - return; - } - - pixman_region32_copy(&scene_buffer->opaque_region, region); - - int x, y; - if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { - return; - } - - pixman_region32_t update_region; - pixman_region32_init(&update_region); - scene_node_bounds(&scene_buffer->node, x, y, &update_region); - scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region); - pixman_region32_fini(&update_region); -} - -void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, - const struct wlr_fbox *box) { - if (wlr_fbox_equal(&scene_buffer->src_box, box)) { - return; - } - - if (box != NULL) { - assert(box->x >= 0 && box->y >= 0 && box->width >= 0 && box->height >= 0); - scene_buffer->src_box = *box; - } else { - scene_buffer->src_box = (struct wlr_fbox){0}; - } - - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, - int width, int height) { - if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { - return; - } - - assert(width >= 0 && height >= 0); - scene_buffer->dst_width = width; - scene_buffer->dst_height = height; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, - enum wl_output_transform transform) { - if (scene_buffer->transform == transform) { - return; - } - - scene_buffer->transform = transform; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, - struct wlr_scene_frame_done_event *event) { - if (!pixman_region32_empty(&scene_buffer->node.visible)) { - wl_signal_emit_mutable(&scene_buffer->events.frame_done, event); - } -} - -void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, - float opacity) { - if (scene_buffer->opacity == opacity) { - return; - } - - assert(opacity >= 0 && opacity <= 1); - scene_buffer->opacity = opacity; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, - enum wlr_scale_filter_mode filter_mode) { - if (scene_buffer->filter_mode == filter_mode) { - return; - } - - scene_buffer->filter_mode = filter_mode; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_transfer_function transfer_function) { - if (scene_buffer->transfer_function == transfer_function) { - return; - } - - scene_buffer->transfer_function = transfer_function; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_named_primaries primaries) { - if (scene_buffer->primaries == primaries) { - return; - } - - scene_buffer->primaries = primaries; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_encoding color_encoding) { - if (scene_buffer->color_encoding == color_encoding) { - return; - } - - scene_buffer->color_encoding = color_encoding; - scene_node_update(&scene_buffer->node, NULL); -} - -void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, - enum wlr_color_range color_range) { - if (scene_buffer->color_range == color_range) { - return; - } - - scene_buffer->color_range = color_range; - scene_node_update(&scene_buffer->node, NULL); -} - -static struct wlr_texture *scene_buffer_get_texture( - struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { - if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { - return scene_buffer->texture; - } - - struct wlr_client_buffer *client_buffer = - wlr_client_buffer_get(scene_buffer->buffer); - if (client_buffer != NULL) { - return client_buffer->texture; - } - - struct wlr_texture *texture = - wlr_texture_from_buffer(renderer, scene_buffer->buffer); - if (texture != NULL && scene_buffer->own_buffer) { - scene_buffer->own_buffer = false; - wlr_buffer_unlock(scene_buffer->buffer); - } - scene_buffer_set_texture(scene_buffer, texture); - return texture; -} - -void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { - *width = 0; - *height = 0; - - switch (node->type) { - case WLR_SCENE_NODE_TREE: - return; - case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); - *width = scene_rect->width; - *height = scene_rect->height; - break; - case WLR_SCENE_NODE_BUFFER:; - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { - *width = scene_buffer->dst_width; - *height = scene_buffer->dst_height; - } else { - *width = scene_buffer->buffer_width; - *height = scene_buffer->buffer_height; - wlr_output_transform_coords(scene_buffer->transform, width, height); - } - break; - } -} - -void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { - if (node->enabled == enabled) { - return; - } - - int x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); - if (wlr_scene_node_coords(node, &x, &y)) { - scene_node_visibility(node, &visible); - } - - node->enabled = enabled; - - scene_node_update(node, &visible); -} - -void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { - if (node->x == x && node->y == y) { - return; - } - - node->x = x; - node->y = y; - scene_node_update(node, NULL); -} - -void wlr_scene_node_place_above(struct wlr_scene_node *node, - struct wlr_scene_node *sibling) { - assert(node != sibling); - assert(node->parent == sibling->parent); - - if (node->link.prev == &sibling->link) { - return; - } - - wl_list_remove(&node->link); - wl_list_insert(&sibling->link, &node->link); - scene_node_update(node, NULL); -} - -void wlr_scene_node_place_below(struct wlr_scene_node *node, - struct wlr_scene_node *sibling) { - assert(node != sibling); - assert(node->parent == sibling->parent); - - if (node->link.next == &sibling->link) { - return; - } - - wl_list_remove(&node->link); - wl_list_insert(sibling->link.prev, &node->link); - scene_node_update(node, NULL); -} - -void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { - struct wlr_scene_node *current_top = wl_container_of( - node->parent->children.prev, current_top, link); - if (node == current_top) { - return; - } - wlr_scene_node_place_above(node, current_top); -} - -void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { - struct wlr_scene_node *current_bottom = wl_container_of( - node->parent->children.next, current_bottom, link); - if (node == current_bottom) { - return; - } - wlr_scene_node_place_below(node, current_bottom); -} - -void wlr_scene_node_reparent(struct wlr_scene_node *node, - struct wlr_scene_tree *new_parent) { - assert(new_parent != NULL); - - if (node->parent == new_parent) { - return; - } - - /* Ensure that a node cannot become its own ancestor */ - for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL; - ancestor = ancestor->node.parent) { - assert(&ancestor->node != node); - } - - int x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); - if (wlr_scene_node_coords(node, &x, &y)) { - scene_node_visibility(node, &visible); - } - - wl_list_remove(&node->link); - node->parent = new_parent; - wl_list_insert(new_parent->children.prev, &node->link); - scene_node_update(node, &visible); -} - -bool wlr_scene_node_coords(struct wlr_scene_node *node, - int *lx_ptr, int *ly_ptr) { - assert(node); - - int lx = 0, ly = 0; - bool enabled = true; - while (true) { - lx += node->x; - ly += node->y; - enabled = enabled && node->enabled; - if (node->parent == NULL) { - break; - } - - node = &node->parent->node; - } - - *lx_ptr = lx; - *ly_ptr = ly; - return enabled; -} - -static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, - int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, - void *user_data) { - if (!node->enabled) { - return; - } - - lx += node->x; - ly += node->y; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - user_iterator(scene_buffer, lx, ly, user_data); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); - } - } -} - -void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, - wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { - scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data); -} - -struct node_at_data { - double lx, ly; - double rx, ry; - struct wlr_scene_node *node; -}; - -static bool scene_node_at_iterator(struct wlr_scene_node *node, - int lx, int ly, void *data) { - struct node_at_data *at_data = data; - - double rx = at_data->lx - lx; - double ry = at_data->ly - ly; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (scene_buffer->point_accepts_input && - !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { - return false; - } - } - - at_data->rx = rx; - at_data->ry = ry; - at_data->node = node; - return true; -} - -struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, - double lx, double ly, double *nx, double *ny) { - struct wlr_box box = { - .x = floor(lx), - .y = floor(ly), - .width = 1, - .height = 1 - }; - - struct node_at_data data = { - .lx = lx, - .ly = ly - }; - - if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { - if (nx) { - *nx = data.rx; - } - if (ny) { - *ny = data.ry; - } - return data.node; - } - - return NULL; -} - -struct render_list_entry { - struct wlr_scene_node *node; - bool highlight_transparent_region; - int x, y; -}; - static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum, const struct wlr_color_luminances *dst_lum) { return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max); } -static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { - struct wlr_scene_node *node = entry->node; - - pixman_region32_t render_region; - pixman_region32_init(&render_region); - pixman_region32_copy(&render_region, &node->visible); - pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); - logical_to_buffer_coords(&render_region, data, true); - pixman_region32_intersect(&render_region, &render_region, &data->damage); - if (pixman_region32_empty(&render_region)) { - pixman_region32_fini(&render_region); - return; - } - - int x = entry->x - data->logical.x; - int y = entry->y - data->logical.y; - - struct wlr_box dst_box = { - .x = x, - .y = y, - }; - scene_node_get_size(node, &dst_box.width, &dst_box.height); - transform_output_box(&dst_box, data); - - pixman_region32_t opaque; - pixman_region32_init(&opaque); - scene_node_opaque_region(node, x, y, &opaque); - logical_to_buffer_coords(&opaque, data, false); - pixman_region32_subtract(&opaque, &render_region, &opaque); - - switch (node->type) { - case WLR_SCENE_NODE_TREE: - assert(false); - break; - case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); - - wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ - .box = dst_box, - .color = { - .r = scene_rect->color[0], - .g = scene_rect->color[1], - .b = scene_rect->color[2], - .a = scene_rect->color[3], - }, - .clip = &render_region, - }); - break; - case WLR_SCENE_NODE_BUFFER:; - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (scene_buffer->is_single_pixel_buffer) { - // Render the buffer as a rect, this is likely to be more efficient - wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ - .box = dst_box, - .color = { - .r = (float)scene_buffer->single_pixel_buffer_color[0] / (float)UINT32_MAX, - .g = (float)scene_buffer->single_pixel_buffer_color[1] / (float)UINT32_MAX, - .b = (float)scene_buffer->single_pixel_buffer_color[2] / (float)UINT32_MAX, - .a = (float)scene_buffer->single_pixel_buffer_color[3] / - (float)UINT32_MAX * scene_buffer->opacity, - }, - .clip = &render_region, - }); - break; - } - - struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, - data->output->output->renderer); - if (texture == NULL) { - scene_output_damage(data->output, &render_region); - break; - } - - enum wl_output_transform transform = - wlr_output_transform_invert(scene_buffer->transform); - transform = wlr_output_transform_compose(transform, data->transform); - - struct wlr_color_primaries primaries = {0}; - if (scene_buffer->primaries != 0) { - wlr_color_primaries_from_named(&primaries, scene_buffer->primaries); - } - - struct wlr_color_luminances src_lum, srgb_lum; - wlr_color_transfer_function_get_default_luminance( - scene_buffer->transfer_function, &src_lum); - wlr_color_transfer_function_get_default_luminance( - WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); - float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum); - - wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { - .texture = texture, - .src_box = scene_buffer->src_box, - .dst_box = dst_box, - .transform = transform, - .clip = &render_region, - .alpha = &scene_buffer->opacity, - .filter_mode = scene_buffer->filter_mode, - .blend_mode = !data->output->scene->calculate_visibility || - !pixman_region32_empty(&opaque) ? - WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, - .transfer_function = scene_buffer->transfer_function, - .primaries = scene_buffer->primaries != 0 ? &primaries : NULL, - .color_encoding = scene_buffer->color_encoding, - .color_range = scene_buffer->color_range, - .luminance_multiplier = &luminance_multiplier, - .wait_timeline = scene_buffer->wait_timeline, - .wait_point = scene_buffer->wait_point, - }); - - struct wlr_scene_output_sample_event sample_event = { - .output = data->output, - .direct_scanout = false, - .release_timeline = data->output->in_timeline, - .release_point = data->output->in_point, - }; - wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); - - if (entry->highlight_transparent_region) { - wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ - .box = dst_box, - .color = { .r = 0, .g = 0.3, .b = 0, .a = 0.3 }, - .clip = &opaque, - }); - } - - break; - } - - pixman_region32_fini(&opaque); - pixman_region32_fini(&render_region); -} - static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, void *data) { struct wlr_scene *scene = @@ -1637,26 +237,11 @@ static const struct wlr_addon_interface output_addon_impl = { .destroy = scene_output_handle_destroy, }; -static void scene_node_output_update(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore, - struct wlr_scene_output *force) { - if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_output_update(child, outputs, ignore, force); - } - return; - } - - update_node_update_outputs(node, outputs, ignore, force); -} - static void scene_output_update_geometry(struct wlr_scene_output *scene_output, bool force_update) { scene_output_damage_whole(scene_output); - scene_node_output_update(&scene_output->scene->tree.node, + wlr_scene_node_update_outputs(&scene_output->scene->tree->node, &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL); } @@ -1799,7 +384,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_signal_emit_mutable(&scene_output->events.destroy, NULL); - scene_node_output_update(&scene_output->scene->tree.node, + wlr_scene_node_update_outputs(&scene_output->scene->tree->node, &scene_output->scene->outputs, scene_output, NULL); assert(wl_list_empty(&scene_output->events.destroy.listener_list)); @@ -1844,6 +429,7 @@ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, return scene_output; } + void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly) { if (scene_output->x == lx && scene_output->y == ly) { @@ -1856,98 +442,6 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, scene_output_update_geometry(scene_output, false); } -static bool scene_node_invisible(struct wlr_scene_node *node) { - if (node->type == WLR_SCENE_NODE_TREE) { - return true; - } else if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); - - return rect->color[3] == 0.f; - } else if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - - return buffer->buffer == NULL && buffer->texture == NULL; - } - - return false; -} - -struct render_list_constructor_data { - struct wlr_box box; - struct wl_array *render_list; - bool calculate_visibility; - bool highlight_transparent_region; - bool fractional_scale; -}; - -static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) { - return scene_buffer->is_single_pixel_buffer && - scene_buffer->single_pixel_buffer_color[0] == 0 && - scene_buffer->single_pixel_buffer_color[1] == 0 && - scene_buffer->single_pixel_buffer_color[2] == 0 && - scene_buffer->single_pixel_buffer_color[3] == UINT32_MAX && - scene_buffer->opacity == 1.0; -} - -static bool construct_render_list_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { - struct render_list_constructor_data *data = _data; - - if (scene_node_invisible(node)) { - return false; - } - - // While rendering, the background should always be black. If we see a - // black rect, we can ignore rendering everything under the rect, and - // unless fractional scale is used even the rect itself (to avoid running - // into issues regarding damage region expansion). - if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility && - (!data->fractional_scale || data->render_list->size == 0)) { - struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); - float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; - - if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { - return false; - } - } - - // Apply the same special-case to black opaque single-pixel buffers - if (node->type == WLR_SCENE_NODE_BUFFER && data->calculate_visibility && - (!data->fractional_scale || data->render_list->size == 0)) { - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - - if (scene_buffer_is_black_opaque(scene_buffer)) { - return false; - } - } - - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, - data->box.x, data->box.y, - data->box.width, data->box.height); - if (pixman_region32_empty(&intersection)) { - pixman_region32_fini(&intersection); - return false; - } - - pixman_region32_fini(&intersection); - - struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); - if (!entry) { - return false; - } - - *entry = (struct render_list_entry){ - .node = node, - .x = lx, - .y = ly, - .highlight_transparent_region = data->highlight_transparent_region, - }; - - return false; -} - 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) { @@ -2022,8 +516,8 @@ enum scene_direct_scanout_result { }; static enum scene_direct_scanout_result scene_entry_try_direct_scanout( - struct render_list_entry *entry, struct wlr_output_state *state, - const struct render_data *data) { + struct wlr_render_list_entry *entry, struct wlr_output_state *state, + const struct wlr_render_data *data) { struct wlr_scene_output *scene_output = data->output; struct wlr_scene_node *node = entry->node; @@ -2031,7 +525,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( return SCANOUT_INELIGIBLE; } - if (node->type != WLR_SCENE_NODE_BUFFER) { + if (!wlr_scene_node_is_buffer(node)) { return SCANOUT_INELIGIBLE; } @@ -2097,7 +591,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( pending.buffer_dst_box.x = entry->x - scene_output->x; pending.buffer_dst_box.y = entry->y - scene_output->y; - scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height); + wlr_scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height); transform_output_box(&pending.buffer_dst_box, data); struct wlr_buffer *wlr_buffer = buffer->buffer; @@ -2111,7 +605,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point); } - if (scene_output->out_timeline) { + if (scene_output->out_timeline) { scene_output->out_point++; wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point); } @@ -2200,6 +694,7 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp wlr_output_state_copy(state, &gamma_pending); wlr_output_state_finish(&gamma_pending); + } static bool scene_output_combine_color_transforms( @@ -2308,7 +803,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } } - struct render_data render_data = { + struct wlr_render_data render_data = { .transform = output->transform, .scale = output->scale, .logical = { .x = scene_output->x, .y = scene_output->y }, @@ -2343,7 +838,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, render_data.logical.width = render_data.trans_width / render_data.scale; render_data.logical.height = render_data.trans_height / render_data.scale; - struct render_list_constructor_data list_con = { + struct wlr_render_list_constructor_data list_con = { .box = render_data.logical, .render_list = &scene_output->render_list, .calculate_visibility = scene_output->scene->calculate_visibility, @@ -2352,11 +847,11 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, }; list_con.render_list->size = 0; - scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, - construct_render_list_iterator, &list_con); + wlr_scene_node_nodes_in_box(&scene_output->scene->tree->node, &list_con.box, + wlr_scene_node_construct_render_list_iterator, &list_con); array_realloc(list_con.render_list, list_con.render_list->size); - struct render_list_entry *list_data = list_con.render_list->data; + struct wlr_render_list_entry *list_data = list_con.render_list->data; int list_len = list_con.render_list->size / sizeof(*list_data); if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { @@ -2384,7 +879,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_init(&acc_damage); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, regions, link) { - // remove overlapping damage regions + // remove overlaping damage regions pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); pixman_region32_union(&acc_damage, &acc_damage, &damage->region); @@ -2408,7 +903,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // - There are no color transforms that need to be applied // - Damage highlight debugging is not enabled enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE; - if (options->color_transform == NULL && !render_gamma_lut && list_len == 1 + if (options->color_transform == NULL && list_len == 1 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); } @@ -2509,7 +1004,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // never see the background. if (scene_output->scene->calculate_visibility) { for (int i = list_len - 1; i >= 0; i--) { - struct render_list_entry *entry = &list_data[i]; + struct wlr_render_list_entry *entry = &list_data[i]; // We must only cull opaque regions that are visible by the node. // The node's visibility will have the knowledge of a black rect @@ -2518,7 +1013,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // rendering in that black rect region, consider the node's visibility. pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); + wlr_scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); @@ -2544,25 +1039,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, pixman_region32_fini(&background); for (int i = list_len - 1; i >= 0; i--) { - struct render_list_entry *entry = &list_data[i]; - scene_entry_render(entry, &render_data); - - if (entry->node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); - - // Direct scanout counts up to DMABUF_FEEDBACK_DEBOUNCE_FRAMES before sending new dmabuf - // feedback, and on composition we wait until it hits zero again. If we knew that an - // entry could never be a scanout candidate, we could send feedback to it - // unconditionally without debounce, but for now it is all or nothing - if (scene_output->dmabuf_feedback_debounce == 0 && buffer->primary_output == scene_output) { - 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); - } - } + struct wlr_render_list_entry *entry = &list_data[i]; + wlr_scene_node_render(entry, &render_data); + wlr_scene_node_dmabuf_feedback(entry, scene_output); } if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { @@ -2611,84 +1090,14 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, return true; } -int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) { - int64_t pre_render = timer->pre_render_duration; - if (!timer->render_timer) { - return pre_render; - } - int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer); - return render != -1 ? pre_render + render : -1; -} - void wlr_scene_timer_finish(struct wlr_scene_timer *timer) { if (timer->render_timer) { wlr_render_timer_destroy(timer->render_timer); } } -static void scene_node_send_frame_done(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, struct timespec *now) { - if (!node->enabled) { - return; - } - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - struct wlr_scene_frame_done_event event = { - .output = scene_output, - .when = *now, - }; - wlr_scene_buffer_send_frame_done(scene_buffer, &event); - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_node_send_frame_done(child, scene_output, now); - } - } -} - void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, struct timespec *now) { - scene_node_send_frame_done(&scene_output->scene->tree.node, + wlr_scene_node_send_frame_done(&scene_output->scene->tree->node, scene_output, now); } - -static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, - struct wlr_scene_node *node, int lx, int ly, - wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { - if (!node->enabled) { - return; - } - - lx += node->x; - ly += node->y; - - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_box node_box = { .x = lx, .y = ly }; - scene_node_get_size(node, &node_box.width, &node_box.height); - - if (wlr_box_intersects(output_box, &node_box)) { - struct wlr_scene_buffer *scene_buffer = - wlr_scene_buffer_from_node(node); - user_iterator(scene_buffer, lx, ly, user_data); - } - } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); - struct wlr_scene_node *child; - wl_list_for_each(child, &scene_tree->children, link) { - scene_output_for_each_scene_buffer(output_box, child, lx, ly, - user_iterator, user_data); - } - } -} - -void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output, - wlr_scene_buffer_iterator_func_t iterator, void *user_data) { - struct wlr_box box = { .x = scene_output->x, .y = scene_output->y }; - wlr_output_effective_resolution(scene_output->output, - &box.width, &box.height); - scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0, - iterator, user_data); -} diff --git a/types/scene/wlr_scene_buffer.c b/types/scene/wlr_scene_buffer.c new file mode 100644 index 000000000..39eb3b3a1 --- /dev/null +++ b/types/scene/wlr_scene_buffer.c @@ -0,0 +1,1190 @@ +#include +#include +#include +#include +#include +#include +#include "types/wlr_output.h" +#include "types/wlr_scene.h" +#include "render/color.h" + +#include +#include + +#include +#include +#include + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible); +static void scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height); + +static void scene_buffer_handle_buffer_release(struct wl_listener *listener, + void *data) { + struct wlr_scene_buffer *scene_buffer = + wl_container_of(listener, scene_buffer, buffer_release); + + scene_buffer->buffer = NULL; + wl_list_remove(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->buffer_release.link); +} + +static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer) { + wl_list_remove(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->buffer_release.link); + if (scene_buffer->own_buffer) { + wlr_buffer_unlock(scene_buffer->buffer); + } + + scene_buffer->buffer = NULL; + scene_buffer->own_buffer = false; + scene_buffer->buffer_width = scene_buffer->buffer_height = 0; + scene_buffer->buffer_is_opaque = false; + + if (!buffer) { + return; + } + + scene_buffer->own_buffer = true; + scene_buffer->buffer = wlr_buffer_lock(buffer); + scene_buffer->buffer_width = buffer->width; + scene_buffer->buffer_height = buffer->height; + scene_buffer->buffer_is_opaque = wlr_buffer_is_opaque(buffer); + + scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release; + wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release); +} + +static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, + struct wlr_texture *texture); + +static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy); + scene_buffer_set_texture(scene_buffer, NULL); +} + +static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer, + struct wlr_texture *texture) { + wl_list_remove(&scene_buffer->renderer_destroy.link); + wlr_texture_destroy(scene_buffer->texture); + scene_buffer->texture = texture; + + if (texture != NULL) { + scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy; + wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy); + } else { + wl_list_init(&scene_buffer->renderer_destroy.link); + } +} + +static void scene_node_destroy(struct wlr_scene_node *node) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + scene_buffer_set_buffer(scene_buffer, NULL); + scene_buffer_set_texture(scene_buffer, NULL); + pixman_region32_fini(&scene_buffer->opaque_region); + wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); + + assert(wl_list_empty(&scene_buffer->events.outputs_update.listener_list)); + assert(wl_list_empty(&scene_buffer->events.output_sample.listener_list)); + assert(wl_list_empty(&scene_buffer->events.frame_done.listener_list)); + + free(scene_buffer); +} + +struct node_at_data { + double lx, ly; + double rx, ry; + struct wlr_scene_node *node; +}; + +static bool scene_node_at_iterator(struct wlr_scene_node *node, + int lx, int ly, void *data) { + struct node_at_data *at_data = data; + + double rx = at_data->lx - lx; + double ry = at_data->ly - ly; + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer->point_accepts_input && + !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { + return false; + } + + at_data->rx = rx; + at_data->ry = ry; + at_data->node = node; + return true; +} + +static struct wlr_scene_node *scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny) { + struct wlr_box box = { + .x = floor(lx), + .y = floor(ly), + .width = 1, + .height = 1 + }; + + struct node_at_data data = { + .lx = lx, + .ly = ly + }; + + if (wlr_scene_node_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { + if (nx) { + *nx = data.rx; + } + if (ny) { + *ny = data.ry; + } + return data.node; + } + + return NULL; +} + +static void scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { + *width = scene_buffer->dst_width; + *height = scene_buffer->dst_height; + } else { + *width = scene_buffer->buffer_width; + *height = scene_buffer->buffer_height; + wlr_output_transform_coords(scene_buffer->transform, width, height); + } +} + +static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { + if (!node->enabled) { + return false; + } + + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (wlr_box_intersection(&node_box, &node_box, box) && + iterator(node, lx, ly, user_data)) { + return true; + } + + return false; +} + +static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data) { + int x, y; + wlr_scene_node_coords(node, &x, &y); + + return _scene_nodes_in_box(node, box, iterator, user_data, x, y); +} + +static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque) { + int width, height; + scene_node_get_size(node, &width, &height); + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (!scene_buffer->buffer) { + return; + } + + if (scene_buffer->opacity != 1) { + return; + } + + if (!scene_buffer->buffer_is_opaque) { + pixman_region32_copy(opaque, &scene_buffer->opaque_region); + pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); + pixman_region32_translate(opaque, x, y); + return; + } + + pixman_region32_fini(opaque); + pixman_region32_init_rect(opaque, x, y, width, height); +} + +#if WLR_HAS_XWAYLAND +static struct wlr_xwayland_surface *scene_node_try_get_managed_xwayland_surface( + struct wlr_scene_node *node) { + struct wlr_scene_buffer *buffer_node = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *surface_node = wlr_scene_surface_try_from_buffer(buffer_node); + if (!surface_node) { + return NULL; + } + + struct wlr_xwayland_surface *xwayland_surface = + wlr_xwayland_surface_try_from_wlr_surface(surface_node->surface); + if (!xwayland_surface || xwayland_surface->override_redirect) { + return NULL; + } + + return xwayland_surface; +} + +static void restack_xwayland_surface(struct wlr_scene_node *node, + struct wlr_box *box, struct wlr_scene_update_data *data) { + struct wlr_xwayland_surface *xwayland_surface = + scene_node_try_get_managed_xwayland_surface(node); + if (!xwayland_surface) { + return; + } + + // ensure this node is entirely inside the update region. If not, we can't + // restack this node since we're not considering the whole thing. + if (wlr_box_contains_box(&data->update_box, box)) { + if (data->restack_above) { + wlr_xwayland_surface_restack(xwayland_surface, data->restack_above, XCB_STACK_MODE_BELOW); + } else { + wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_ABOVE); + } + } + + data->restack_above = xwayland_surface; +} +#endif + +static uint32_t region_area(pixman_region32_t *region) { + uint32_t area = 0; + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + for (int i = 0; i < nrects; ++i) { + area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); + } + + return area; +} + +static void update_node_update_outputs(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { + 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; + uint64_t active_outputs = 0; + + // let's update the outputs in two steps: + // - the primary outputs + // - the enter/leave signals + // This ensures that the enter/leave signals can rely on the primary output + // to have a reasonable value. Otherwise, they may get a value that's in + // the middle of a calculation. + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, outputs, link) { + if (scene_output == ignore) { + continue; + } + + if (!scene_output->output->enabled) { + continue; + } + + struct wlr_box output_box = { + .x = scene_output->x, + .y = scene_output->y, + }; + wlr_output_effective_resolution(scene_output->output, + &output_box.width, &output_box.height); + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + output_box.x, output_box.y, output_box.width, output_box.height); + + if (!pixman_region32_empty(&intersection)) { + uint32_t overlap = region_area(&intersection); + if (overlap >= largest_overlap) { + largest_overlap = overlap; + scene_buffer->primary_output = scene_output; + } + + active_outputs |= 1ull << scene_output->index; + count++; + } + + pixman_region32_fini(&intersection); + } + + if (old_primary_output != scene_buffer->primary_output) { + scene_buffer->prev_feedback_options = + (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; + } + + uint64_t old_active = scene_buffer->active_outputs; + scene_buffer->active_outputs = active_outputs; + + wl_list_for_each(scene_output, outputs, link) { + uint64_t mask = 1ull << scene_output->index; + bool intersects = active_outputs & mask; + bool intersects_before = old_active & mask; + + if (intersects && !intersects_before) { + wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output); + } else if (!intersects && intersects_before) { + wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output); + } + } + + // if there are active outputs on this node, we should always have a primary + // output + assert(!scene_buffer->active_outputs || scene_buffer->primary_output); + + // Skip output update event if nothing was updated + if (old_active == active_outputs && + (!force || ((1ull << force->index) & ~active_outputs)) && + old_primary_output == scene_buffer->primary_output) { + return; + } + + struct wlr_scene_output *outputs_array[64]; + struct wlr_scene_outputs_update_event event = { + .active = outputs_array, + .size = count, + }; + + size_t i = 0; + wl_list_for_each(scene_output, outputs, link) { + if (~active_outputs & (1ull << scene_output->index)) { + continue; + } + + assert(i < count); + outputs_array[i++] = scene_output; + } + + wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); +} + +static bool scene_node_update_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct wlr_scene_update_data *data = _data; + + struct wlr_box box = { .x = lx, .y = ly }; + scene_node_get_size(node, &box.width, &box.height); + + pixman_region32_subtract(&node->visible, &node->visible, data->update_region); + pixman_region32_union(&node->visible, &node->visible, data->visible); + pixman_region32_intersect_rect(&node->visible, &node->visible, + lx, ly, box.width, box.height); + + if (data->calculate_visibility) { + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, lx, ly, &opaque); + pixman_region32_subtract(data->visible, data->visible, &opaque); + pixman_region32_fini(&opaque); + } + + update_node_update_outputs(node, data->outputs, NULL, NULL); +#if WLR_HAS_XWAYLAND + restack_xwayland_surface(node, &box, data); +#endif + + return false; +} + +static void scale_region(pixman_region32_t *region, float scale, bool round_up) { + wlr_region_scale(region, region, scale); + + if (round_up && floor(scale) != scale) { + wlr_region_expand(region, region, 1); + } +} + +static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) { + int width, height; + wlr_output_transformed_resolution(output, &width, &height); + + wlr_region_transform(damage, damage, + wlr_output_transform_invert(output->transform), width, height); +} + +static void scene_output_damage(struct wlr_scene_output *scene_output, + const pixman_region32_t *damage) { + struct wlr_output *output = scene_output->output; + + pixman_region32_t clipped; + pixman_region32_init(&clipped); + pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height); + + if (!pixman_region32_empty(&clipped)) { + wlr_output_schedule_frame(scene_output->output); + wlr_damage_ring_add(&scene_output->damage_ring, &clipped); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &clipped); + } + + pixman_region32_fini(&clipped); +} + +static void scene_update_region(struct wlr_scene *scene, + pixman_region32_t *update_region) { + pixman_region32_t visible; + pixman_region32_init(&visible); + pixman_region32_copy(&visible, update_region); + + struct pixman_box32 *region_box = pixman_region32_extents(update_region); + struct wlr_scene_update_data data = { + .visible = &visible, + .update_region = update_region, + .update_box = { + .x = region_box->x1, + .y = region_box->y1, + .width = region_box->x2 - region_box->x1, + .height = region_box->y2 - region_box->y1, + }, + .outputs = &scene->outputs, + .calculate_visibility = scene->calculate_visibility, + }; + + // update node visibility and output enter/leave events + wlr_scene_node_nodes_in_box(&scene->tree->node, &data.update_box, scene_node_update_iterator, &data); + + pixman_region32_fini(&visible); +} + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + pixman_region32_union(visible, visible, &node->visible); +} + +static void scene_node_send_frame_done(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now) { + if (!node->enabled) { + return; + } + + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(node); + struct wlr_scene_frame_done_event event = { + .output = scene_output, + .when = *now, + }; + wlr_scene_buffer_send_frame_done(scene_buffer, &event); +} + +static bool scene_node_invisible(struct wlr_scene_node *node) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + + return buffer->buffer == NULL && buffer->texture == NULL; +} + +static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) { + return scene_buffer->is_single_pixel_buffer && + scene_buffer->single_pixel_buffer_color[0] == 0 && + scene_buffer->single_pixel_buffer_color[1] == 0 && + scene_buffer->single_pixel_buffer_color[2] == 0 && + scene_buffer->single_pixel_buffer_color[3] == UINT32_MAX && + scene_buffer->opacity == 1.0; +} + +static bool construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct wlr_render_list_constructor_data *data = _data; + + if (wlr_scene_node_invisible(node)) { + return false; + } + + // Apply the same special-case to black opaque single-pixel buffers + if (data->calculate_visibility && + (!data->fractional_scale || data->render_list->size == 0)) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer_is_black_opaque(scene_buffer)) { + return false; + } + } + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + data->box.x, data->box.y, + data->box.width, data->box.height); + if (pixman_region32_empty(&intersection)) { + pixman_region32_fini(&intersection); + return false; + } + + pixman_region32_fini(&intersection); + + struct wlr_render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); + if (entry == NULL) { + return false; + } + + *entry = (struct wlr_render_list_entry){ + .node = node, + .x = lx, + .y = ly, + .highlight_transparent_region = data->highlight_transparent_region, + }; + + return false; +} + +static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data, + bool round_up) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_region(region, data->scale, round_up); + wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); +} + +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void scale_box(struct wlr_box *box, float scale) { + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); +} + +static void transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_box(box, data->scale); + wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); +} + +static struct wlr_texture *scene_buffer_get_texture( + struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { + if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) { + return scene_buffer->texture; + } + + struct wlr_client_buffer *client_buffer = + wlr_client_buffer_get(scene_buffer->buffer); + if (client_buffer != NULL) { + return client_buffer->texture; + } + + struct wlr_texture *texture = + wlr_texture_from_buffer(renderer, scene_buffer->buffer); + if (texture != NULL && scene_buffer->own_buffer) { + scene_buffer->own_buffer = false; + wlr_buffer_unlock(scene_buffer->buffer); + } + scene_buffer_set_texture(scene_buffer, texture); + return texture; +} + +static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum, + const struct wlr_color_luminances *dst_lum) { + return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max); +} + +static void scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) { + struct wlr_scene_node *node = entry->node; + + pixman_region32_t render_region; + pixman_region32_init(&render_region); + pixman_region32_copy(&render_region, &node->visible); + pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); + logical_to_buffer_coords(&render_region, data, true); + pixman_region32_intersect(&render_region, &render_region, &data->damage); + if (pixman_region32_empty(&render_region)) { + pixman_region32_fini(&render_region); + return; + } + + int x = entry->x - data->logical.x; + int y = entry->y - data->logical.y; + + struct wlr_box dst_box = { + .x = x, + .y = y, + }; + scene_node_get_size(node, &dst_box.width, &dst_box.height); + transform_output_box(&dst_box, data); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, x, y, &opaque); + logical_to_buffer_coords(&opaque, data, false); + pixman_region32_subtract(&opaque, &render_region, &opaque); + + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + + if (scene_buffer->is_single_pixel_buffer) { + // Render the buffer as a rect, this is likely to be more efficient + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { + .r = (float)scene_buffer->single_pixel_buffer_color[0] / (float)UINT32_MAX, + .g = (float)scene_buffer->single_pixel_buffer_color[1] / (float)UINT32_MAX, + .b = (float)scene_buffer->single_pixel_buffer_color[2] / (float)UINT32_MAX, + .a = (float)scene_buffer->single_pixel_buffer_color[3] / + (float)UINT32_MAX * scene_buffer->opacity, + }, + .clip = &render_region, + }); + goto done; + } + + struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, + data->output->output->renderer); + if (texture == NULL) { + scene_output_damage(data->output, &render_region); + goto done; + } + + enum wl_output_transform transform = + wlr_output_transform_invert(scene_buffer->transform); + transform = wlr_output_transform_compose(transform, data->transform); + + struct wlr_color_primaries primaries = {0}; + if (scene_buffer->primaries != 0) { + wlr_color_primaries_from_named(&primaries, scene_buffer->primaries); + } + + struct wlr_color_luminances src_lum, srgb_lum; + wlr_color_transfer_function_get_default_luminance( + scene_buffer->transfer_function, &src_lum); + wlr_color_transfer_function_get_default_luminance( + WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum); + float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum); + + wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { + .texture = texture, + .src_box = scene_buffer->src_box, + .dst_box = dst_box, + .transform = transform, + .clip = &render_region, + .alpha = &scene_buffer->opacity, + .filter_mode = scene_buffer->filter_mode, + .blend_mode = !data->output->scene->calculate_visibility || + !pixman_region32_empty(&opaque) ? + WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, + .transfer_function = scene_buffer->transfer_function, + .primaries = scene_buffer->primaries != 0 ? &primaries : NULL, + .color_encoding = scene_buffer->color_encoding, + .color_range = scene_buffer->color_range, + .luminance_multiplier = &luminance_multiplier, + .wait_timeline = scene_buffer->wait_timeline, + .wait_point = scene_buffer->wait_point, + }); + + struct wlr_scene_output_sample_event sample_event = { + .output = data->output, + .direct_scanout = false, + .release_timeline = data->output->in_timeline, + .release_point = data->output->in_point, + }; + wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); + if (entry->highlight_transparent_region) { + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { .r = 0, .g = 0.3, .b = 0, .a = 0.3 }, + .clip = &opaque, + }); + } + + goto done; + +done: + pixman_region32_fini(&opaque); + pixman_region32_fini(&render_region); +} + +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 void node_scene_buffer_send_dmabuf_feedback(struct wlr_render_list_entry *entry, + struct wlr_scene_output *scene_output) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); + + // Direct scanout counts up to DMABUF_FEEDBACK_DEBOUNCE_FRAMES before sending new dmabuf + // feedback, and on composition we wait until it hits zero again. If we knew that an + // entry could never be a scanout candidate, we could send feedback to it + // unconditionally without debounce, but for now it is all or nothing + if (scene_output->dmabuf_feedback_debounce == 0 && buffer->primary_output == scene_output) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = scene_output->output->renderer, + .scanout_primary_output = NULL, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + } +} + +static void get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max) { + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (node_box.x < *x_min) { + *x_min = node_box.x; + } + if (node_box.y < *y_min) { + *y_min = node_box.y; + } + if (node_box.x + node_box.width > *x_max) { + *x_max = node_box.x + node_box.width; + } + if (node_box.y + node_box.height > *y_max) { + *y_max = node_box.y + node_box.height; + } +} + +static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs) { + pixman_region32_clear(&node->visible); + update_node_update_outputs(node, outputs, NULL, NULL); + +#if WLR_HAS_XWAYLAND + if (xwayland_restack) { + struct wlr_xwayland_surface *xwayland_surface = + scene_node_try_get_managed_xwayland_surface(node); + if (!xwayland_surface) { + return; + } + + wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW); + } +#endif +} + +static const struct wlr_scene_node_impl scene_node_impl = { + .destroy = scene_node_destroy, + .set_enabled = NULL, + .set_position = NULL, + .bounds = NULL, + .get_size = scene_node_get_size, + .coords = NULL, + .at = scene_node_at, + .in_box = scene_nodes_in_box, + .opaque_region = scene_node_opaque_region, + .update_outputs = update_node_update_outputs, + .update = NULL, + .visibility = scene_node_visibility, + .frame_done = scene_node_send_frame_done, + .invisible = scene_node_invisible, + .construct_render_list_iterator = construct_render_list_iterator, + .render = scene_node_render, + .dmabuf_feedback = node_scene_buffer_send_dmabuf_feedback, + .get_extents = get_scene_node_extents, + .get_children = NULL, +#if WLR_HAS_XWAYLAND + .restack_xwayland_surface = restack_xwayland_surface, +#else + .restack_xwayland_surface = NULL, +#endif + .cleanup_when_disabled = scene_node_cleanup_when_disabled, +}; + +static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer, + struct wlr_drm_syncobj_timeline *timeline, uint64_t point) { + wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline); + if (timeline != NULL) { + scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline); + scene_buffer->wait_point = point; + } else { + scene_buffer->wait_timeline = NULL; + scene_buffer->wait_point = 0; + } +} + +struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, + struct wlr_buffer *buffer) { + struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); + if (scene_buffer == NULL) { + return NULL; + } + assert(parent); + wlr_scene_node_init(&scene_buffer->node, &scene_node_impl, parent); + + wl_signal_init(&scene_buffer->events.outputs_update); + wl_signal_init(&scene_buffer->events.output_enter); + wl_signal_init(&scene_buffer->events.output_leave); + wl_signal_init(&scene_buffer->events.output_sample); + wl_signal_init(&scene_buffer->events.frame_done); + + pixman_region32_init(&scene_buffer->opaque_region); + wl_list_init(&scene_buffer->buffer_release.link); + wl_list_init(&scene_buffer->renderer_destroy.link); + scene_buffer->opacity = 1; + + scene_buffer_set_buffer(scene_buffer, buffer); + wlr_scene_node_update(&scene_buffer->node, NULL); + + return scene_buffer; +} + +void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer) { + wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, NULL); +} + +void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const pixman_region32_t *damage) { + const struct wlr_scene_buffer_set_buffer_options options = { + .damage = damage, + }; + wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, &options); +} + +void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer, + struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options) { + const struct wlr_scene_buffer_set_buffer_options default_options = {0}; + if (options == NULL) { + options = &default_options; + } + + // specifying a region for a NULL buffer doesn't make sense. We need to know + // about the buffer to scale the buffer local coordinates down to scene + // coordinates. + assert(buffer || !options->damage); + + bool mapped = buffer != NULL; + bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL; + + if (!mapped && !prev_mapped) { + // unmapping already unmapped buffer - noop + return; + } + + // if this node used to not be mapped or its previous displayed + // buffer region will be different from what the new buffer would + // produce we need to update the node. + bool update = mapped != prev_mapped; + if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) { + update = update || scene_buffer->buffer_width != buffer->width || + scene_buffer->buffer_height != buffer->height; + } + + // If this is a buffer change, check if it's a single pixel buffer. + // Cache that so we can still apply rendering optimisations even when + // the original buffer has been freed after texture upload. + if (buffer != scene_buffer->buffer) { + scene_buffer->is_single_pixel_buffer = false; + struct wlr_client_buffer *client_buffer = NULL; + if (buffer != NULL) { + client_buffer = wlr_client_buffer_get(buffer); + } + if (client_buffer != NULL && client_buffer->source != NULL) { + struct wlr_single_pixel_buffer_v1 *single_pixel_buffer = + wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); + if (single_pixel_buffer != NULL) { + scene_buffer->is_single_pixel_buffer = true; + scene_buffer->single_pixel_buffer_color[0] = single_pixel_buffer->r; + scene_buffer->single_pixel_buffer_color[1] = single_pixel_buffer->g; + scene_buffer->single_pixel_buffer_color[2] = single_pixel_buffer->b; + scene_buffer->single_pixel_buffer_color[3] = single_pixel_buffer->a; + } + } + } + + scene_buffer_set_buffer(scene_buffer, buffer); + scene_buffer_set_texture(scene_buffer, NULL); + scene_buffer_set_wait_timeline(scene_buffer, + options->wait_timeline, options->wait_point); + + if (update) { + wlr_scene_node_update(&scene_buffer->node, NULL); + // updating the node will already damage the whole node for us. Return + // early to not damage again + return; + } + + int lx, ly; + if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { + return; + } + + pixman_region32_t fallback_damage; + pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height); + const pixman_region32_t *damage = options->damage; + if (!damage) { + damage = &fallback_damage; + } + + struct wlr_fbox box = scene_buffer->src_box; + if (wlr_fbox_empty(&box)) { + box.x = 0; + box.y = 0; + box.width = buffer->width; + box.height = buffer->height; + } + + wlr_fbox_transform(&box, &box, scene_buffer->transform, + buffer->width, buffer->height); + + float scale_x, scale_y; + if (scene_buffer->dst_width || scene_buffer->dst_height) { + scale_x = scene_buffer->dst_width / box.width; + scale_y = scene_buffer->dst_height / box.height; + } else { + scale_x = buffer->width / box.width; + scale_y = buffer->height / box.height; + } + + pixman_region32_t trans_damage; + pixman_region32_init(&trans_damage); + wlr_region_transform(&trans_damage, damage, + scene_buffer->transform, buffer->width, buffer->height); + pixman_region32_intersect_rect(&trans_damage, &trans_damage, + box.x, box.y, box.width, box.height); + pixman_region32_translate(&trans_damage, -box.x, -box.y); + + struct wlr_scene *scene = scene_buffer->node.scene; + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + float output_scale = scene_output->output->scale; + float output_scale_x = output_scale * scale_x; + float output_scale_y = output_scale * scale_y; + pixman_region32_t output_damage; + pixman_region32_init(&output_damage); + wlr_region_scale_xy(&output_damage, &trans_damage, + output_scale_x, output_scale_y); + + // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels. + // If the buffer is upscaled on the given axis (output_scale_* > 1.0, + // buffer_scale_* < 1.0), its contents will bleed into adjacent + // (ceil(output_scale_* / 2)) output pixels because of linear filtering. + // Additionally, if the buffer is downscaled (output_scale_* < 1.0, + // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of + // buffer pixels, its contents will bleed into neighboring output pixels. + // Handle both cases by computing buffer_scale_{x,y} and checking if they are + // integer numbers; ceilf() is used to ensure that the distance is at least 1. + float buffer_scale_x = 1.0f / output_scale_x; + float buffer_scale_y = 1.0f / output_scale_y; + int dist_x = floor(buffer_scale_x) != buffer_scale_x ? + (int)ceilf(output_scale_x / 2.0f) : 0; + int dist_y = floor(buffer_scale_y) != buffer_scale_y ? + (int)ceilf(output_scale_y / 2.0f) : 0; + // TODO: expand with per-axis distances + wlr_region_expand(&output_damage, &output_damage, + dist_x >= dist_y ? dist_x : dist_y); + + pixman_region32_t cull_region; + pixman_region32_init(&cull_region); + pixman_region32_copy(&cull_region, &scene_buffer->node.visible); + scale_region(&cull_region, output_scale, true); + pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale); + pixman_region32_intersect(&output_damage, &output_damage, &cull_region); + pixman_region32_fini(&cull_region); + + pixman_region32_translate(&output_damage, + (int)round((lx - scene_output->x) * output_scale), + (int)round((ly - scene_output->y) * output_scale)); + output_to_buffer_coords(&output_damage, scene_output->output); + scene_output_damage(scene_output, &output_damage); + pixman_region32_fini(&output_damage); + } + + pixman_region32_fini(&trans_damage); + pixman_region32_fini(&fallback_damage); +} + +void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, + const pixman_region32_t *region) { + if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { + return; + } + + pixman_region32_copy(&scene_buffer->opaque_region, region); + + int x, y; + if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { + return; + } + + pixman_region32_t update_region; + pixman_region32_init(&update_region); + wlr_scene_node_bounds(&scene_buffer->node, x, y, &update_region); + scene_update_region(scene_buffer->node.scene, &update_region); + pixman_region32_fini(&update_region); +} + +void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, + const struct wlr_fbox *box) { + if (wlr_fbox_equal(&scene_buffer->src_box, box)) { + return; + } + + if (box != NULL) { + assert(box->x >= 0 && box->y >= 0 && box->width >= 0 && box->height >= 0); + scene_buffer->src_box = *box; + } else { + scene_buffer->src_box = (struct wlr_fbox){0}; + } + + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, + int width, int height) { + if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { + return; + } + + assert(width >= 0 && height >= 0); + scene_buffer->dst_width = width; + scene_buffer->dst_height = height; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, + enum wl_output_transform transform) { + if (scene_buffer->transform == transform) { + return; + } + + scene_buffer->transform = transform; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, + float opacity) { + if (scene_buffer->opacity == opacity) { + return; + } + + assert(opacity >= 0 && opacity <= 1); + scene_buffer->opacity = opacity; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, + enum wlr_scale_filter_mode filter_mode) { + if (scene_buffer->filter_mode == filter_mode) { + return; + } + + scene_buffer->filter_mode = filter_mode; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, + struct wlr_scene_frame_done_event *event) { + if (!pixman_region32_empty(&scene_buffer->node.visible)) { + wl_signal_emit_mutable(&scene_buffer->events.frame_done, event); + } +} + +bool wlr_scene_node_is_buffer(const struct wlr_scene_node *node) { + return node->impl == &scene_node_impl; +} + +struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node) { + assert(node->impl == &scene_node_impl); + + struct wlr_scene_buffer *scene_buffer = + wl_container_of(node, scene_buffer, node); + + return scene_buffer; +} + +void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_transfer_function transfer_function) { + if (scene_buffer->transfer_function == transfer_function) { + return; + } + + scene_buffer->transfer_function = transfer_function; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_named_primaries primaries) { + if (scene_buffer->primaries == primaries) { + return; + } + + scene_buffer->primaries = primaries; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_encoding color_encoding) { + if (scene_buffer->color_encoding == color_encoding) { + return; + } + + scene_buffer->color_encoding = color_encoding; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer, + enum wlr_color_range color_range) { + if (scene_buffer->color_range == color_range) { + return; + } + + scene_buffer->color_range = color_range; + wlr_scene_node_update(&scene_buffer->node, NULL); +} + +static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, + int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator, + void *user_data) { + if (!node->enabled) { + return; + } + + lx += node->x; + ly += node->y; + + if (wlr_scene_node_is_buffer(node)) { + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + user_iterator(scene_buffer, lx, ly, user_data); + } else if (wlr_scene_node_get_children(node) != NULL) { + struct wl_list *childrens = wlr_scene_node_get_children(node); + struct wlr_scene_node *child; + wl_list_for_each(child, childrens, link) { + scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); + } + } +} + +void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node, + wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { + scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data); +} diff --git a/types/scene/wlr_scene_node.c b/types/scene/wlr_scene_node.c new file mode 100644 index 000000000..c7bef15cf --- /dev/null +++ b/types/scene/wlr_scene_node.c @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include +#include +#include +#include "types/wlr_scene.h" +#include "types/wlr_output.h" + +#include +#include + +#include +#include + +#include + +void wlr_scene_node_init(struct wlr_scene_node *node, + const struct wlr_scene_node_impl *impl, + struct wlr_scene_tree *parent) { + assert(impl->destroy); + *node = (struct wlr_scene_node){ + .impl = impl, + .parent = parent, + .enabled = true, + }; + + wl_list_init(&node->link); + + wl_signal_init(&node->events.destroy); + pixman_region32_init(&node->visible); + + if (parent != NULL) { + wl_list_insert(parent->children.prev, &node->link); + node->scene = parent->node.scene; + } + + wlr_addon_set_init(&node->addons); +} + +void wlr_scene_node_destroy(struct wlr_scene_node *node) { + if (node == NULL) { + return; + } + + wl_signal_emit_mutable(&node->events.destroy, NULL); + assert(wl_list_empty(&node->events.destroy.listener_list)); + wlr_addon_set_finish(&node->addons); + + wlr_scene_node_set_enabled(node, false); + wl_list_remove(&node->link); + pixman_region32_fini(&node->visible); + if (node->impl->destroy != NULL) { + node->impl->destroy(node); + } +} + +void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { + if (node->impl->set_enabled == NULL) { + if (node->enabled == enabled) { + return; + } + + int x, y; + pixman_region32_t visible; + pixman_region32_init(&visible); + if (wlr_scene_node_coords(node, &x, &y)) { + wlr_scene_node_visibility(node, &visible); + } + + node->enabled = enabled; + wlr_scene_node_update(node, &visible); + return; + } + + node->impl->set_enabled(node, enabled); +} + +void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { + if (node->impl->set_position == NULL) { + if (node->x == x && node->y == y) { + return; + } + + node->x = x; + node->y = y; + wlr_scene_node_update(node, NULL); + return; + } + + node->impl->set_position(node, x, y); +} + +void wlr_scene_node_place_above(struct wlr_scene_node *node, + struct wlr_scene_node *sibling) { + assert(node != sibling); + assert(node->parent == sibling->parent); + + if (node->link.prev == &sibling->link) { + return; + } + + wl_list_remove(&node->link); + wl_list_insert(&sibling->link, &node->link); + wlr_scene_node_update(node, NULL); +} + +void wlr_scene_node_place_below(struct wlr_scene_node *node, + struct wlr_scene_node *sibling) { + assert(node != sibling); + assert(node->parent == sibling->parent); + + if (node->link.next == &sibling->link) { + return; + } + + wl_list_remove(&node->link); + wl_list_insert(sibling->link.prev, &node->link); + wlr_scene_node_update(node, NULL); +} + +void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { + struct wlr_scene_node *current_top = wl_container_of( + node->parent->children.prev, current_top, link); + if (node == current_top) { + return; + } + wlr_scene_node_place_above(node, current_top); +} + +void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { + struct wlr_scene_node *current_bottom = wl_container_of( + node->parent->children.next, current_bottom, link); + if (node == current_bottom) { + return; + } + wlr_scene_node_place_below(node, current_bottom); +} + +void wlr_scene_node_reparent(struct wlr_scene_node *node, + struct wlr_scene_tree *new_parent) { + assert(new_parent != NULL); + + if (node->parent == new_parent) { + return; + } + + /* Ensure that a node cannot become its own ancestor */ + for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL; + ancestor = ancestor->node.parent) { + assert(&ancestor->node != node); + } + + int x, y; + pixman_region32_t visible; + pixman_region32_init(&visible); + if (wlr_scene_node_coords(node, &x, &y)) { + wlr_scene_node_visibility(node, &visible); + } + + wl_list_remove(&node->link); + node->parent = new_parent; + wl_list_insert(new_parent->children.prev, &node->link); + wlr_scene_node_update(node, &visible); +} + +void wlr_scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height) { + if (node->impl->get_size == NULL) { + *width = 0; + *height = 0; + return; + } + + node->impl->get_size(node, width, height); +} + +void wlr_scene_node_bounds(struct wlr_scene_node *node, + int x, int y, pixman_region32_t *visible) { + if (node->impl->bounds == NULL) { + if (!node->enabled) { + return; + } + + int width, height; + wlr_scene_node_get_size(node, &width, &height); + pixman_region32_union_rect(visible, visible, x, y, width, height); + return; + } + + node->impl->bounds(node, x, y, visible); +} + +bool wlr_scene_node_coords(struct wlr_scene_node *node, int *lx_ptr, int *ly_ptr) { + if (node->impl->coords == NULL) { + int lx = 0, ly = 0; + bool enabled = true; + while (true) { + lx += node->x; + ly += node->y; + enabled = enabled && node->enabled; + if (node->parent == NULL) { + break; + } + + node = &node->parent->node; + } + + *lx_ptr = lx; + *ly_ptr = ly; + return enabled; + } + + return node->impl->coords(node, lx_ptr, ly_ptr); +} + +struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny) { + + if (node->impl->at == NULL) { + return NULL; + } + + return node->impl->at(node, lx, ly, nx, ny); +} + +bool wlr_scene_node_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data) { + if (node->impl->in_box == NULL) { + return false; + } + + return node->impl->in_box(node, box, iterator, user_data); +} + +void wlr_scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque) { + if (node->impl->opaque_region == NULL) { + int width, height; + wlr_scene_node_get_size(node, &width, &height); + pixman_region32_fini(opaque); + pixman_region32_init_rect(opaque, x, y, width, height); + return; + } + + return node->impl->opaque_region(node, x, y, opaque); +} + +void wlr_scene_node_update_outputs(struct wlr_scene_node *node, + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { + + if (node->impl->update_outputs == NULL) { + return; + } + + return node->impl->update_outputs(node, outputs, ignore, force); +} + +static void scale_region(pixman_region32_t *region, float scale, bool round_up) { + wlr_region_scale(region, region, scale); + + if (round_up && floor(scale) != scale) { + wlr_region_expand(region, region, 1); + } +} + +static bool scene_node_update_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct wlr_scene_update_data *data = _data; + + struct wlr_box box = { .x = lx, .y = ly }; + wlr_scene_node_get_size(node, &box.width, &box.height); + + pixman_region32_subtract(&node->visible, &node->visible, data->update_region); + pixman_region32_union(&node->visible, &node->visible, data->visible); + pixman_region32_intersect_rect(&node->visible, &node->visible, + lx, ly, box.width, box.height); + + if (data->calculate_visibility) { + pixman_region32_t opaque; + pixman_region32_init(&opaque); + wlr_scene_node_opaque_region(node, lx, ly, &opaque); + pixman_region32_subtract(data->visible, data->visible, &opaque); + pixman_region32_fini(&opaque); + } + + wlr_scene_node_update_outputs(node, data->outputs, NULL, NULL); +#if WLR_HAS_XWAYLAND + if (data->restack_xwayland_surfaces) { + wlr_scene_node_restack_xwayland_surface(node, &box, data); + } +#endif + + return false; +} + +static void scene_update_region(struct wlr_scene *scene, + const pixman_region32_t *update_region) { + pixman_region32_t visible; + pixman_region32_init(&visible); + pixman_region32_copy(&visible, update_region); + + struct pixman_box32 *region_box = pixman_region32_extents(update_region); + struct wlr_scene_update_data data = { + .visible = &visible, + .update_region = update_region, + .update_box = { + .x = region_box->x1, + .y = region_box->y1, + .width = region_box->x2 - region_box->x1, + .height = region_box->y2 - region_box->y1, + }, + .outputs = &scene->outputs, + .calculate_visibility = scene->calculate_visibility, + .restack_xwayland_surfaces = scene->restack_xwayland_surfaces, + }; + + // update node visibility and output enter/leave events + wlr_scene_node_nodes_in_box(&scene->tree->node, &data.update_box, scene_node_update_iterator, &data); + + pixman_region32_fini(&visible); +} + +static void scene_output_damage(struct wlr_scene_output *scene_output, + const pixman_region32_t *damage) { + struct wlr_output *output = scene_output->output; + + pixman_region32_t clipped; + pixman_region32_init(&clipped); + pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height); + + if (!pixman_region32_empty(&clipped)) { + wlr_output_schedule_frame(scene_output->output); + wlr_damage_ring_add(&scene_output->damage_ring, &clipped); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &clipped); + } + + pixman_region32_fini(&clipped); +} + +static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) { + int width, height; + wlr_output_transformed_resolution(output, &width, &height); + + wlr_region_transform(damage, damage, + wlr_output_transform_invert(output->transform), width, height); +} + +static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region32_t *damage) { + if (pixman_region32_empty(damage)) { + return; + } + + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + pixman_region32_t output_damage; + pixman_region32_init(&output_damage); + pixman_region32_copy(&output_damage, damage); + pixman_region32_translate(&output_damage, + -scene_output->x, -scene_output->y); + scale_region(&output_damage, scene_output->output->scale, true); + output_to_buffer_coords(&output_damage, scene_output->output); + scene_output_damage(scene_output, &output_damage); + pixman_region32_fini(&output_damage); + } +} + +void wlr_scene_node_update(struct wlr_scene_node *node, + pixman_region32_t *damage) { + if (node->impl->update == NULL) { + struct wlr_scene *scene = node->scene; + + int x, y; + if (!wlr_scene_node_coords(node, &x, &y)) { + // We assume explicit damage on a disabled tree means the node was just + // disabled. + if (damage) { + wlr_scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs); + + scene_update_region(scene, damage); + scene_damage_outputs(scene, damage); + pixman_region32_fini(damage); + } + + return; + } + + pixman_region32_t visible; + if (!damage) { + pixman_region32_init(&visible); + wlr_scene_node_visibility(node, &visible); + damage = &visible; + } + + pixman_region32_t update_region; + pixman_region32_init(&update_region); + pixman_region32_copy(&update_region, damage); + wlr_scene_node_bounds(node, x, y, &update_region); + + scene_update_region(scene, &update_region); + pixman_region32_fini(&update_region); + + wlr_scene_node_visibility(node, damage); + scene_damage_outputs(scene, damage); + pixman_region32_fini(damage); + return; + } + + node->impl->update(node, damage); +} + +void wlr_scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible) { + if (node->impl->visibility == NULL) { + return; + } + + node->impl->visibility(node, visible); +} + +void wlr_scene_node_send_frame_done(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now) { + if (!node->enabled) { + return; + } + + if (node->impl->frame_done == NULL) { + return; + } + + node->impl->frame_done(node, scene_output, now); +} + +bool wlr_scene_node_invisible(struct wlr_scene_node *node) { + if (node->impl->invisible == NULL) { + return false; + } + + return node->impl->invisible(node); +} + +bool wlr_scene_node_construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + if (node->impl->construct_render_list_iterator == NULL) { + return false; + } + + return node->impl->construct_render_list_iterator(node, lx, ly, _data); +} + +void wlr_scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) { + if (entry->node->impl->render == NULL) { + return; + } + + entry->node->impl->render(entry, data); +} + +void wlr_scene_node_dmabuf_feedback(struct wlr_render_list_entry *entry, + struct wlr_scene_output *scene_output) { + if (entry->node->impl->dmabuf_feedback == NULL) { + return; + } + + entry->node->impl->dmabuf_feedback(entry, scene_output); +} + +void wlr_scene_node_get_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max) { + if (node->impl->get_extents == NULL) { + return; + } + + node->impl->get_extents(node, lx, ly, x_min, y_min, x_max, y_max); +} + +struct wl_list *wlr_scene_node_get_children(struct wlr_scene_node *node) { + if (node->impl->get_children == NULL) { + return NULL; + } + + return node->impl->get_children(node); +} + +void wlr_scene_node_restack_xwayland_surface(struct wlr_scene_node *node, + struct wlr_box *box, struct wlr_scene_update_data *data) { + if (node->impl->restack_xwayland_surface == NULL) { + return; + } + + node->impl->restack_xwayland_surface(node, box, data); +} + +void wlr_scene_node_cleanup_when_disabled(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs) { + if (node->impl->cleanup_when_disabled == NULL) { + return; + } + + node->impl->cleanup_when_disabled(node, xwayland_restack, outputs); +} diff --git a/types/scene/wlr_scene_rect.c b/types/scene/wlr_scene_rect.c new file mode 100644 index 000000000..f2c7ad168 --- /dev/null +++ b/types/scene/wlr_scene_rect.c @@ -0,0 +1,364 @@ +#include +#include +#include "types/wlr_scene.h" +#include +#include +#include "types/wlr_output.h" +#include "types/wlr_scene.h" + +#include +#include + +#include +#include +#include + +#include + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible); +static void scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height); + +static void scene_node_destroy(struct wlr_scene_node *node) { + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + + free(scene_rect); +} + +struct node_at_data { + double lx, ly; + double rx, ry; + struct wlr_scene_node *node; +}; + +static bool scene_node_at_iterator(struct wlr_scene_node *node, + int lx, int ly, void *data) { + struct node_at_data *at_data = data; + + double rx = at_data->lx - lx; + double ry = at_data->ly - ly; + + at_data->rx = rx; + at_data->ry = ry; + at_data->node = node; + return true; +} + +static struct wlr_scene_node *scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny) { + struct wlr_box box = { + .x = floor(lx), + .y = floor(ly), + .width = 1, + .height = 1 + }; + + struct node_at_data data = { + .lx = lx, + .ly = ly + }; + + if (wlr_scene_node_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { + if (nx) { + *nx = data.rx; + } + if (ny) { + *ny = data.ry; + } + return data.node; + } + + return NULL; +} + +static void scene_node_get_size(struct wlr_scene_node *node, + int *width, int *height) { + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + *width = scene_rect->width; + *height = scene_rect->height; +} + +static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { + if (!node->enabled) { + return false; + } + + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (wlr_box_intersection(&node_box, &node_box, box) && + iterator(node, lx, ly, user_data)) { + return true; + } + + return false; +} + +static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data) { + int x, y; + wlr_scene_node_coords(node, &x, &y); + + return _scene_nodes_in_box(node, box, iterator, user_data, x, y); +} + +static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, + pixman_region32_t *opaque) { + int width, height; + scene_node_get_size(node, &width, &height); + + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + if (scene_rect->color[3] != 1) { + return; + } + + pixman_region32_fini(opaque); + pixman_region32_init_rect(opaque, x, y, width, height); +} + +static void scale_region(pixman_region32_t *region, float scale, bool round_up) { + wlr_region_scale(region, region, scale); + + if (round_up && floor(scale) != scale) { + wlr_region_expand(region, region, 1); + } +} + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + pixman_region32_union(visible, visible, &node->visible); +} + +static bool scene_node_invisible(struct wlr_scene_node *node) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + + return rect->color[3] == 0.f; +} + +static bool construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct wlr_render_list_constructor_data *data = _data; + + if (wlr_scene_node_invisible(node)) { + return false; + } + + // While rendering, the background should always be black. If we see a + // black rect, we can ignore rendering everything under the rect, and + // unless fractional scale is used even the rect itself (to avoid running + // into issues regarding damage region expansion). + if (data->calculate_visibility && + (!data->fractional_scale || data->render_list->size == 0)) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; + + if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { + return false; + } + } + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + data->box.x, data->box.y, + data->box.width, data->box.height); + if (pixman_region32_empty(&intersection)) { + pixman_region32_fini(&intersection); + return false; + } + + pixman_region32_fini(&intersection); + + struct wlr_render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); + if (entry == NULL) { + return false; + } + + *entry = (struct wlr_render_list_entry){ + .node = node, + .x = lx, + .y = ly, + .highlight_transparent_region = data->highlight_transparent_region, + }; + + return false; +} + +static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data, + bool round_up) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_region(region, data->scale, round_up); + wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); +} + +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void scale_box(struct wlr_box *box, float scale) { + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); +} + +static void transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_box(box, data->scale); + wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); +} + +static void scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) { + struct wlr_scene_node *node = entry->node; + + pixman_region32_t render_region; + pixman_region32_init(&render_region); + pixman_region32_copy(&render_region, &node->visible); + pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); + logical_to_buffer_coords(&render_region, data, true); + pixman_region32_intersect(&render_region, &render_region, &data->damage); + if (pixman_region32_empty(&render_region)) { + pixman_region32_fini(&render_region); + return; + } + + int x = entry->x - data->logical.x; + int y = entry->y - data->logical.y; + + struct wlr_box dst_box = { + .x = x, + .y = y, + }; + scene_node_get_size(node, &dst_box.width, &dst_box.height); + transform_output_box(&dst_box, data); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, x, y, &opaque); + logical_to_buffer_coords(&opaque, data, false); + pixman_region32_subtract(&opaque, &render_region, &opaque); + + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { + .r = scene_rect->color[0], + .g = scene_rect->color[1], + .b = scene_rect->color[2], + .a = scene_rect->color[3], + }, + .clip = &render_region, + }); + + pixman_region32_fini(&opaque); + pixman_region32_fini(&render_region); +} + +static void get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max) { + struct wlr_box node_box = { .x = lx, .y = ly }; + scene_node_get_size(node, &node_box.width, &node_box.height); + + if (node_box.x < *x_min) { + *x_min = node_box.x; + } + if (node_box.y < *y_min) { + *y_min = node_box.y; + } + if (node_box.x + node_box.width > *x_max) { + *x_max = node_box.x + node_box.width; + } + if (node_box.y + node_box.height > *y_max) { + *y_max = node_box.y + node_box.height; + } +} + +static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs) { + pixman_region32_clear(&node->visible); + wlr_scene_node_update_outputs(node, outputs, NULL, NULL); +} + +static const struct wlr_scene_node_impl scene_node_impl = { + .destroy = scene_node_destroy, + .set_enabled = NULL, + .set_position = NULL, + .bounds = NULL, + .get_size = scene_node_get_size, + .coords = NULL, + .at = scene_node_at, + .in_box = scene_nodes_in_box, + .opaque_region = scene_node_opaque_region, + .update_outputs = NULL, + .update = NULL, + .visibility = scene_node_visibility, + .frame_done = NULL, + .invisible = scene_node_invisible, + .construct_render_list_iterator = construct_render_list_iterator, + .render = scene_node_render, + .get_extents = get_scene_node_extents, + .get_children = NULL, + .restack_xwayland_surface = NULL, + .cleanup_when_disabled = scene_node_cleanup_when_disabled, +}; + +struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, + int width, int height, const float color[static 4]) { + assert(parent); + assert(width >= 0 && height >= 0); + + struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); + if (scene_rect == NULL) { + return NULL; + } + wlr_scene_node_init(&scene_rect->node, &scene_node_impl, parent); + + scene_rect->width = width; + scene_rect->height = height; + memcpy(scene_rect->color, color, sizeof(scene_rect->color)); + + wlr_scene_node_update(&scene_rect->node, NULL); + + return scene_rect; +} + +void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { + if (rect->width == width && rect->height == height) { + return; + } + + assert(width >= 0 && height >= 0); + + rect->width = width; + rect->height = height; + wlr_scene_node_update(&rect->node, NULL); +} + +void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) { + if (memcmp(rect->color, color, sizeof(rect->color)) == 0) { + return; + } + + memcpy(rect->color, color, sizeof(rect->color)); + wlr_scene_node_update(&rect->node, NULL); +} + +bool wlr_scene_node_is_rect(const struct wlr_scene_node *node) { + return node->impl == &scene_node_impl; +} + +struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) { + assert(node->impl == &scene_node_impl); + + struct wlr_scene_rect *scene_rect = + wl_container_of(node, scene_rect, node); + + return scene_rect; +} diff --git a/types/scene/wlr_scene_tree.c b/types/scene/wlr_scene_tree.c new file mode 100644 index 000000000..4c5178991 --- /dev/null +++ b/types/scene/wlr_scene_tree.c @@ -0,0 +1,352 @@ +#include +#include +#include +#include +#include +#include "types/wlr_output.h" +#include "types/wlr_scene.h" + +#include +#include + +#include +#include + +#include + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible); + +static void scene_node_destroy(struct wlr_scene_node *node) { + struct wlr_scene *scene = node->scene; + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + + if (scene_tree == scene->tree) { + assert(!node->parent); + struct wlr_scene_output *scene_output, *scene_output_tmp; + wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { + wlr_scene_output_destroy(scene_output); + } + + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + wl_list_remove(&scene->gamma_control_manager_v1_destroy.link); + wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link); + } else { + assert(node->parent); + } + + struct wlr_scene_node *child, *child_tmp; + wl_list_for_each_safe(child, child_tmp, + &scene_tree->children, link) { + wlr_scene_node_destroy(child); + } + + free(scene_tree); +} + +struct node_at_data { + double lx, ly; + double rx, ry; + struct wlr_scene_node *node; +}; + +static bool scene_node_at_iterator(struct wlr_scene_node *node, + int lx, int ly, void *data) { + struct node_at_data *at_data = data; + + double rx = at_data->lx - lx; + double ry = at_data->ly - ly; + + at_data->rx = rx; + at_data->ry = ry; + at_data->node = node; + return true; +} + +static struct wlr_scene_node *scene_node_at(struct wlr_scene_node *node, + double lx, double ly, double *nx, double *ny) { + struct wlr_box box = { + .x = floor(lx), + .y = floor(ly), + .width = 1, + .height = 1 + }; + + struct node_at_data data = { + .lx = lx, + .ly = ly + }; + + if (wlr_scene_node_nodes_in_box(node, &box, scene_node_at_iterator, &data)) { + if (nx) { + *nx = data.rx; + } + if (ny) { + *ny = data.ry; + } + return data.node; + } + + return NULL; +} + +static void scene_node_bounds(struct wlr_scene_node *node, + int x, int y, pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + wlr_scene_node_bounds(child, x + child->x, y + child->y, visible); + } +} + +static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) { + if (!node->enabled) { + return false; + } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each_reverse(child, &scene_tree->children, link) { + if (wlr_scene_node_nodes_in_box(child, box, iterator, user_data)) { + return true; + } + } + + return false; +} + +static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, + scene_node_box_iterator_func_t iterator, void *user_data) { + int x, y; + wlr_scene_node_coords(node, &x, &y); + + return _scene_nodes_in_box(node, box, iterator, user_data, x, y); +} + +static void scale_region(pixman_region32_t *region, float scale, bool round_up) { + wlr_region_scale(region, region, scale); + + if (round_up && floor(scale) != scale) { + wlr_region_expand(region, region, 1); + } +} + +static void scene_node_visibility(struct wlr_scene_node *node, + pixman_region32_t *visible) { + if (!node->enabled) { + return; + } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + wlr_scene_node_visibility(child, visible); + } +} + +static void scene_node_send_frame_done(struct wlr_scene_node *node, + struct wlr_scene_output *scene_output, struct timespec *now) { + if (!node->enabled) { + return; + } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + wlr_scene_node_send_frame_done(child, scene_output, now); + } +} + + +static bool scene_node_invisible(struct wlr_scene_node *node) { + return true; +} + +static bool construct_render_list_iterator(struct wlr_scene_node *node, + int lx, int ly, void *_data) { + struct wlr_render_list_constructor_data *data = _data; + + if (wlr_scene_node_invisible(node)) { + return false; + } + + pixman_region32_t intersection; + pixman_region32_init(&intersection); + pixman_region32_intersect_rect(&intersection, &node->visible, + data->box.x, data->box.y, + data->box.width, data->box.height); + if (pixman_region32_empty(&intersection)) { + pixman_region32_fini(&intersection); + return false; + } + + pixman_region32_fini(&intersection); + + struct wlr_render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); + if (entry == NULL) { + return false; + } + + *entry = (struct wlr_render_list_entry){ + .node = node, + .x = lx, + .y = ly, + .highlight_transparent_region = data->highlight_transparent_region, + }; + + return false; +} + +static void logical_to_buffer_coords(pixman_region32_t *region, const struct wlr_render_data *data, + bool round_up) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_region(region, data->scale, round_up); + wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); +} + +static int scale_length(int length, int offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void scale_box(struct wlr_box *box, float scale) { + box->width = scale_length(box->width, box->x, scale); + box->height = scale_length(box->height, box->y, scale); + box->x = round(box->x * scale); + box->y = round(box->y * scale); +} + +static void transform_output_box(struct wlr_box *box, const struct wlr_render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + scale_box(box, data->scale); + wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); +} + +static void scene_node_render(struct wlr_render_list_entry *entry, const struct wlr_render_data *data) { + struct wlr_scene_node *node = entry->node; + + pixman_region32_t render_region; + pixman_region32_init(&render_region); + pixman_region32_copy(&render_region, &node->visible); + pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); + logical_to_buffer_coords(&render_region, data, true); + pixman_region32_intersect(&render_region, &render_region, &data->damage); + if (pixman_region32_empty(&render_region)) { + pixman_region32_fini(&render_region); + return; + } + + int x = entry->x - data->logical.x; + int y = entry->y - data->logical.y; + + struct wlr_box dst_box = { + .x = x, + .y = y, + }; + wlr_scene_node_get_size(node, &dst_box.width, &dst_box.height); + transform_output_box(&dst_box, data); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + wlr_scene_node_opaque_region(node, x, y, &opaque); + logical_to_buffer_coords(&opaque, data, false); + pixman_region32_subtract(&opaque, &render_region, &opaque); + + assert(false); + + pixman_region32_fini(&opaque); + pixman_region32_fini(&render_region); +} + +static void get_scene_node_extents(struct wlr_scene_node *node, int lx, int ly, + int *x_min, int *y_min, int *x_max, int *y_max) { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + wlr_scene_node_get_extents(child, lx + child->x, ly + child->y, x_min, y_min, x_max, y_max); + } +} + +static struct wl_list *scene_node_get_children(struct wlr_scene_node *node) { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + return &scene_tree->children; +} + +static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, + bool xwayland_restack, struct wl_list *outputs) { + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *child; + wl_list_for_each(child, &scene_tree->children, link) { + if (!child->enabled) { + continue; + } + + wlr_scene_node_cleanup_when_disabled(child, xwayland_restack, outputs); + } + return; +} + +static const struct wlr_scene_node_impl scene_node_impl = { + .destroy = scene_node_destroy, + .set_enabled = NULL, + .set_position = NULL, + .bounds = scene_node_bounds, + .get_size = NULL, + .coords = NULL, + .at = scene_node_at, + .in_box = scene_nodes_in_box, + .opaque_region = NULL, + .update_outputs = NULL, + .update = NULL, + .visibility = scene_node_visibility, + .frame_done = scene_node_send_frame_done, + .invisible = scene_node_invisible, + .construct_render_list_iterator = construct_render_list_iterator, + .render = scene_node_render, + .get_extents = get_scene_node_extents, + .get_children = scene_node_get_children, + .restack_xwayland_surface = NULL, + .cleanup_when_disabled = scene_node_cleanup_when_disabled, +}; + +static struct wlr_scene_tree *scene_tree_create(struct wlr_scene_tree *parent) { + struct wlr_scene_tree *tree = calloc(1, sizeof(*tree)); + if (tree == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + wlr_scene_node_init(&tree->node, &scene_node_impl, parent); + wl_list_init(&tree->children); + return tree; +} + +struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { + assert(parent); + + return scene_tree_create(parent); +} + +struct wlr_scene_tree *wlr_root_scene_tree_create(struct wlr_scene *scene) { + struct wlr_scene_tree *tree = scene_tree_create(NULL); + tree->node.scene = scene; + + return tree; +} + +bool wlr_scene_node_is_tree(const struct wlr_scene_node *node) { + return node->impl == &scene_node_impl; +} + +struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { + assert(node->impl == &scene_node_impl); + + struct wlr_scene_tree *scene_tree = + wl_container_of(node, scene_tree, node); + + return scene_tree; +}