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; +}