From 6f9ff3394422040209c8d130bdcf921308c5f8ba Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Sat, 5 Apr 2025 20:32:25 +0300 Subject: [PATCH 01/12] scene: use fractional positions Signed-off-by: Loukas Agorgianitis --- include/wlr/types/wlr_scene.h | 12 +++---- test/bench_scene.c | 2 +- types/ext_image_capture_source_v1/scene.c | 12 +++---- types/scene/surface.c | 2 +- types/scene/wlr_scene.c | 44 +++++++++++------------ 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index f6f97cfea..629744496 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -50,7 +50,7 @@ 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); + struct wlr_scene_buffer *buffer, double sx, double sy, void *user_data); enum wlr_scene_node_type { WLR_SCENE_NODE_TREE, @@ -66,7 +66,7 @@ struct wlr_scene_node { struct wl_list link; // wlr_scene_tree.children bool enabled; - int x, y; // relative to parent + double x, y; // relative to parent struct { struct wl_signal destroy; @@ -228,7 +228,7 @@ struct wlr_scene_output { struct wlr_damage_ring damage_ring; - int x, y; + double x, y; struct { struct wl_signal destroy; @@ -301,7 +301,7 @@ 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); +void wlr_scene_node_set_position(struct wlr_scene_node *node, double x, double y); /** * Move the node right above the specified sibling. * Asserts that node and sibling are distinct and share the same parent. @@ -332,7 +332,7 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, * * 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); +bool wlr_scene_node_coords(struct wlr_scene_node *node, double *lx, double *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 @@ -591,7 +591,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output); * Set the output's position in the scene-graph. */ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, - int lx, int ly); + double lx, double ly); struct wlr_scene_output_state_options { struct wlr_scene_timer *timer; diff --git a/test/bench_scene.c b/test/bench_scene.c index 6d6b270f0..89cfff265 100644 --- a/test/bench_scene.c +++ b/test/bench_scene.c @@ -104,7 +104,7 @@ static void bench_scene_node_at(struct wlr_scene *scene, struct tree_spec *spec) } static void noop_iterator(struct wlr_scene_buffer *buffer, - int sx, int sy, void *user_data) { + double sx, double sy, void *user_data) { (void)buffer; (void)sx; (void)sy; diff --git a/types/ext_image_capture_source_v1/scene.c b/types/ext_image_capture_source_v1/scene.c index 99d34e012..d5db8f276 100644 --- a/types/ext_image_capture_source_v1/scene.c +++ b/types/ext_image_capture_source_v1/scene.c @@ -34,7 +34,7 @@ struct scene_node_source_frame_event { static size_t last_output_num = 0; -static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box, int lx, int ly) { +static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_fbox *box, double lx, double ly) { switch (node->type) { case WLR_SCENE_NODE_TREE:; struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); @@ -45,7 +45,7 @@ static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box break; case WLR_SCENE_NODE_RECT: case WLR_SCENE_NODE_BUFFER:; - struct wlr_box node_box = { .x = lx, .y = ly }; + struct wlr_fbox node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); if (node_box.x < box->x) { @@ -64,9 +64,9 @@ static void _get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box } } -static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box *box) { - *box = (struct wlr_box){ .x = INT_MAX, .y = INT_MAX }; - int lx = 0, ly = 0; +static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_fbox *box) { + *box = (struct wlr_fbox){ .x = INT_MAX, .y = INT_MAX }; + double lx = 0, ly = 0; wlr_scene_node_coords(node, &lx, &ly); _get_scene_node_extents(node, box, lx, ly); } @@ -74,7 +74,7 @@ static void get_scene_node_extents(struct wlr_scene_node *node, struct wlr_box * static void source_render(struct scene_node_source *source) { struct wlr_scene_output *scene_output = source->scene_output; - struct wlr_box extents; + struct wlr_fbox extents; get_scene_node_extents(source->node, &extents); if (extents.width == 0 || extents.height == 0) { diff --git a/types/scene/surface.c b/types/scene/surface.c index e6ea1333a..414620687 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -359,7 +359,7 @@ static void handle_scene_surface_surface_commit( // schedule the frame however if the node is enabled and there is an // output intersecting, otherwise the frame done events would never reach // the surface anyway. - int lx, ly; + double lx, ly; bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); if (!wl_list_empty(&surface->surface->current.frame_callback_list) && diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7231422e8..7b5ff28e1 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -202,10 +202,10 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { } typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, - int sx, int sy, void *data); + double sx, double 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) { + scene_node_box_iterator_func_t iterator, void *user_data, double lx, double ly) { if (!node->enabled) { return false; } @@ -237,13 +237,13 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box 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; + double 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, +static void scene_node_opaque_region(struct wlr_scene_node *node, double x, double y, pixman_region32_t *opaque) { int width, height; scene_node_get_size(node, &width, &height); @@ -545,7 +545,7 @@ static void restack_xwayland_surface(struct wlr_scene_node *node, #endif static bool scene_node_update_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { + double lx, double ly, void *_data) { struct scene_update_data *data = _data; struct wlr_box box = { .x = lx, .y = ly }; @@ -593,7 +593,7 @@ static void scene_node_visibility(struct wlr_scene_node *node, } static void scene_node_bounds(struct wlr_scene_node *node, - int x, int y, pixman_region32_t *visible) { + double x, double y, pixman_region32_t *visible) { if (!node->enabled) { return; } @@ -688,7 +688,7 @@ 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; + double x, y; if (!wlr_scene_node_coords(node, &x, &y)) { // We assume explicit damage on a disabled tree means the node was just // disabled. @@ -919,7 +919,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf return; } - int lx, ly; + double lx, ly; if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) { return; } @@ -1030,7 +1030,7 @@ void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, pixman_region32_copy(&scene_buffer->opaque_region, region); - int x, y; + double x, y; if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { return; } @@ -1201,7 +1201,7 @@ void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { return; } - int x, y; + double x, y; pixman_region32_t visible; pixman_region32_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { @@ -1213,7 +1213,7 @@ void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { scene_node_update(node, &visible); } -void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { +void wlr_scene_node_set_position(struct wlr_scene_node *node, double x, double y) { if (node->x == x && node->y == y) { return; } @@ -1283,7 +1283,7 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, assert(&ancestor->node != node); } - int x, y; + double x, y; pixman_region32_t visible; pixman_region32_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { @@ -1297,10 +1297,10 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, } bool wlr_scene_node_coords(struct wlr_scene_node *node, - int *lx_ptr, int *ly_ptr) { + double *lx_ptr, double *ly_ptr) { assert(node); - int lx = 0, ly = 0; + double lx = 0, ly = 0; bool enabled = true; while (true) { lx += node->x; @@ -1319,7 +1319,7 @@ bool wlr_scene_node_coords(struct wlr_scene_node *node, } 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, + double lx, double ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { if (!node->enabled) { return; @@ -1352,7 +1352,7 @@ struct node_at_data { }; static bool scene_node_at_iterator(struct wlr_scene_node *node, - int lx, int ly, void *data) { + double lx, double ly, void *data) { struct node_at_data *at_data = data; double rx = at_data->lx - lx; @@ -1403,7 +1403,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, struct render_list_entry { struct wlr_scene_node *node; bool highlight_transparent_region; - int x, y; + double x, y; }; static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum, @@ -1425,8 +1425,8 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren return; } - int x = entry->x - data->logical.x; - int y = entry->y - data->logical.y; + double x = entry->x - data->logical.x; + double y = entry->y - data->logical.y; struct wlr_box dst_box = { .x = x, @@ -1845,7 +1845,7 @@ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, } void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, - int lx, int ly) { + double lx, double ly) { if (scene_output->x == lx && scene_output->y == ly) { return; } @@ -1890,7 +1890,7 @@ static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) } static bool construct_render_list_iterator(struct wlr_scene_node *node, - int lx, int ly, void *_data) { + double lx, double ly, void *_data) { struct render_list_constructor_data *data = _data; if (scene_node_invisible(node)) { @@ -2656,7 +2656,7 @@ void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, } static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, - struct wlr_scene_node *node, int lx, int ly, + struct wlr_scene_node *node, double lx, double ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { if (!node->enabled) { return; From d05f14f38bd5dcd456f1d81b5695feee8ac6bd5a Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 14 Apr 2025 11:33:10 +0200 Subject: [PATCH 02/12] scene: use fractional sizes for rects Signed-off-by: Loukas Agorgianitis --- include/wlr/types/wlr_scene.h | 6 +++--- types/scene/wlr_scene.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 629744496..a7fecb38f 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -141,7 +141,7 @@ struct wlr_scene_surface { /** A scene-graph node displaying a solid-colored rectangle */ struct wlr_scene_rect { struct wlr_scene_node node; - int width, height; + double width, height; float color[4]; }; @@ -453,12 +453,12 @@ void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface, * 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]); + double width, double 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); +void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, double width, double height); /** * Change the color of an existing rectangle node. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 7b5ff28e1..b9da6252b 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -724,7 +724,7 @@ static void scene_node_update(struct wlr_scene_node *node, } struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, - int width, int height, const float color[static 4]) { + double width, double height, const float color[static 4]) { assert(parent); assert(width >= 0 && height >= 0); @@ -743,7 +743,7 @@ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, return scene_rect; } -void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) { +void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, double width, double height) { if (rect->width == width && rect->height == height) { return; } From 807373594ec9654fbaff246ee51d05123ebd7449 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Fri, 27 Mar 2026 21:28:18 +0200 Subject: [PATCH 03/12] util/box: introduce wlr_fbox_intersects Signed-off-by: Loukas Agorgianitis --- include/wlr/util/box.h | 7 +++++++ util/box.c | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/include/wlr/util/box.h b/include/wlr/util/box.h index f6809e0c3..543447a5d 100644 --- a/include/wlr/util/box.h +++ b/include/wlr/util/box.h @@ -114,6 +114,13 @@ void wlr_fbox_transform(struct wlr_fbox *dest, const struct wlr_fbox *box, */ bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b); +/** + * Checks whether two boxes intersect. + * + * Returns false if either box is empty. + */ +bool wlr_fbox_intersects(const struct wlr_fbox *a, const struct wlr_fbox *b); + /** * Returns true if the two boxes are equal, false otherwise. */ diff --git a/util/box.c b/util/box.c index 62d405488..7221a710e 100644 --- a/util/box.c +++ b/util/box.c @@ -111,6 +111,15 @@ bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b) { a->y < b->y + b->height && b->y < a->y + a->height; } +bool wlr_fbox_intersects(const struct wlr_fbox *a, const struct wlr_fbox *b) { + if (wlr_fbox_empty(a) || wlr_fbox_empty(b)) { + return false; + } + + return a->x < b->x + b->width && b->x < a->x + a->width && + a->y < b->y + b->height && b->y < a->y + a->height; +} + void wlr_box_transform(struct wlr_box *dest, const struct wlr_box *box, enum wl_output_transform transform, int width, int height) { struct wlr_box src = {0}; From d26884920e05d4fc444caef02edb69f511b6fcd5 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 14 Apr 2025 11:38:47 +0200 Subject: [PATCH 04/12] util/box: introduce wlr_fbox_intersection Signed-off-by: Loukas Agorgianitis --- include/wlr/util/box.h | 8 ++++++++ util/box.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/wlr/util/box.h b/include/wlr/util/box.h index 543447a5d..7da693e5c 100644 --- a/include/wlr/util/box.h +++ b/include/wlr/util/box.h @@ -61,6 +61,14 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, const struct wlr_box *box_b); +/** + * Gives the intersecting box between two struct wlr_fbox. + * + * Returns an empty box if the provided boxes don't intersect. + */ +bool wlr_fbox_intersection(struct wlr_fbox *dest, const struct wlr_fbox *box_a, + const struct wlr_fbox *box_b); + /** * Verifies if a point is contained within the bounds of a given struct wlr_box. * diff --git a/util/box.c b/util/box.c index 7221a710e..34834d340 100644 --- a/util/box.c +++ b/util/box.c @@ -82,6 +82,34 @@ bool wlr_box_intersection(struct wlr_box *dest, const struct wlr_box *box_a, return true; } +bool wlr_fbox_intersection(struct wlr_fbox *dest, const struct wlr_fbox *box_a, + const struct wlr_fbox *box_b) { + bool a_empty = wlr_fbox_empty(box_a); + bool b_empty = wlr_fbox_empty(box_b); + + if (a_empty || b_empty) { + *dest = (struct wlr_fbox){0}; + return false; + } + + double x1 = fmax(box_a->x, box_b->x); + double y1 = fmax(box_a->y, box_b->y); + double x2 = fmin(box_a->x + box_a->width, box_b->x + box_b->width); + double y2 = fmin(box_a->y + box_a->height, box_b->y + box_b->height); + + dest->x = x1; + dest->y = y1; + dest->width = x2 - x1; + dest->height = y2 - y1; + + if (wlr_fbox_empty(dest)) { + *dest = (struct wlr_fbox){0}; + return false; + } + + return true; +} + bool wlr_box_contains_point(const struct wlr_box *box, double x, double y) { if (wlr_box_empty(box)) { return false; From c7d6629d16095c940469beed25257c5778c7124a Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 14 Apr 2025 11:42:06 +0200 Subject: [PATCH 05/12] util/box: introduce wlr_fbox_contains_box Signed-off-by: Loukas Agorgianitis --- include/wlr/util/box.h | 8 ++++++++ util/box.c | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/wlr/util/box.h b/include/wlr/util/box.h index 7da693e5c..277d3dd23 100644 --- a/include/wlr/util/box.h +++ b/include/wlr/util/box.h @@ -87,6 +87,14 @@ bool wlr_box_contains_point(const struct wlr_box *box, double x, double y); */ bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *smaller); +/** + * Verifies that a box is fully contained within another box. + * + * Returns true if the "smaller" box is fully contained within the "bigger" box. + * If either of the boxes are empty, false is returned. + */ +bool wlr_fbox_contains_box(const struct wlr_fbox *bigger, const struct wlr_fbox *smaller); + /** * Checks whether a box is empty or not. * diff --git a/util/box.c b/util/box.c index 34834d340..30caedcd4 100644 --- a/util/box.c +++ b/util/box.c @@ -130,6 +130,17 @@ bool wlr_box_contains_box(const struct wlr_box *bigger, const struct wlr_box *sm smaller->y + smaller->height <= bigger->y + bigger->height; } +bool wlr_fbox_contains_box(const struct wlr_fbox *bigger, const struct wlr_fbox *smaller) { + if (wlr_fbox_empty(bigger) || wlr_fbox_empty(smaller)) { + return false; + } + + return smaller->x >= bigger->x && + smaller->x + smaller->width <= bigger->x + bigger->width && + smaller->y >= bigger->y && + smaller->y + smaller->height <= bigger->y + bigger->height; +} + bool wlr_box_intersects(const struct wlr_box *a, const struct wlr_box *b) { if (wlr_box_empty(a) || wlr_box_empty(b)) { return false; From 41d9e1086b175ddb79597cc0b2144805f15d10c2 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Fri, 25 Apr 2025 20:32:52 +0300 Subject: [PATCH 06/12] scene: use fractional sizes Signed-off-by: Loukas Agorgianitis --- include/types/wlr_scene.h | 2 +- include/wlr/types/wlr_scene.h | 4 +- types/scene/wlr_scene.c | 89 ++++++++++++++++++----------------- 3 files changed, 50 insertions(+), 45 deletions(-) diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h index c4b40cdd0..c72b3dd96 100644 --- a/include/types/wlr_scene.h +++ b/include/types/wlr_scene.h @@ -5,7 +5,7 @@ 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_node_get_size(struct wlr_scene_node *node, double *width, double *height); void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip); diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index a7fecb38f..5548d4ec5 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -188,7 +188,7 @@ struct wlr_scene_buffer { float opacity; enum wlr_scale_filter_mode filter_mode; struct wlr_fbox src_box; - int dst_width, dst_height; + double dst_width, dst_height; enum wl_output_transform transform; pixman_region32_t opaque_region; enum wlr_color_transfer_function transfer_function; @@ -538,7 +538,7 @@ void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, * destination size is zero. */ void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, - int width, int height); + double width, double height); /** * Set a transform which will be applied to the buffer. diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index b9da6252b..1f0472105 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -204,7 +204,7 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node, double sx, double sy, void *data); -static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, +static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_fbox *box, scene_node_box_iterator_func_t iterator, void *user_data, double lx, double ly) { if (!node->enabled) { return false; @@ -222,10 +222,10 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box break; case WLR_SCENE_NODE_RECT: case WLR_SCENE_NODE_BUFFER:; - struct wlr_box node_box = { .x = lx, .y = ly }; + struct wlr_fbox node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (wlr_box_intersects(&node_box, box) && + if (wlr_fbox_intersects(&node_box, box) && iterator(node, lx, ly, user_data)) { return true; } @@ -235,7 +235,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box return false; } -static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, +static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_fbox *box, scene_node_box_iterator_func_t iterator, void *user_data) { double x, y; wlr_scene_node_coords(node, &x, &y); @@ -245,7 +245,7 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, static void scene_node_opaque_region(struct wlr_scene_node *node, double x, double y, pixman_region32_t *opaque) { - int width, height; + double width, height; scene_node_get_size(node, &width, &height); if (node->type == WLR_SCENE_NODE_RECT) { @@ -279,7 +279,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, double x, doub struct scene_update_data { pixman_region32_t *visible; const pixman_region32_t *update_region; - struct wlr_box update_box; + struct wlr_fbox update_box; struct wl_list *outputs; bool calculate_visibility; bool restack_xwayland_surfaces; @@ -312,7 +312,7 @@ 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; + struct wlr_fbox logical; int trans_width, trans_height; struct wlr_scene_output *output; @@ -336,20 +336,20 @@ static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output wlr_output_transform_invert(output->transform), width, height); } -static int scale_length(int length, int offset, float scale) { +static int scale_length(double length, double 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 scale_box(struct wlr_box *box, struct wlr_fbox *src, float scale) { + box->width = scale_length(src->width, src->x, scale); + box->height = scale_length(src->height, src->y, scale); + box->x = round(src->x * scale); + box->y = round(src->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, struct wlr_fbox *src, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - scale_box(box, data->scale); + scale_box(box, src, data->scale); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } @@ -523,7 +523,7 @@ static struct wlr_xwayland_surface *scene_node_try_get_managed_xwayland_surface( } static void restack_xwayland_surface(struct wlr_scene_node *node, - struct wlr_box *box, struct scene_update_data *data) { + struct wlr_fbox *box, struct scene_update_data *data) { struct wlr_xwayland_surface *xwayland_surface = scene_node_try_get_managed_xwayland_surface(node); if (!xwayland_surface) { @@ -532,7 +532,7 @@ static void restack_xwayland_surface(struct wlr_scene_node *node, // 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 (wlr_fbox_contains_box(&data->update_box, box)) { if (data->restack_above) { wlr_xwayland_surface_restack(xwayland_surface, data->restack_above, XCB_STACK_MODE_BELOW); } else { @@ -548,7 +548,7 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, double lx, double ly, void *_data) { struct scene_update_data *data = _data; - struct wlr_box box = { .x = lx, .y = ly }; + struct wlr_fbox box = { .x = lx, .y = ly }; scene_node_get_size(node, &box.width, &box.height); pixman_region32_subtract(&node->visible, &node->visible, data->update_region); @@ -607,7 +607,7 @@ static void scene_node_bounds(struct wlr_scene_node *node, return; } - int width, height; + double width, height; scene_node_get_size(node, &width, &height); pixman_region32_union_rect(visible, visible, x, y, width, height); } @@ -1059,7 +1059,7 @@ void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, } void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer, - int width, int height) { + double width, double height) { if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) { return; } @@ -1170,7 +1170,7 @@ static struct wlr_texture *scene_buffer_get_texture( return texture; } -void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { +void scene_node_get_size(struct wlr_scene_node *node, double *width, double *height) { *width = 0; *height = 0; @@ -1188,9 +1188,10 @@ void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) { *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); + int w = scene_buffer->buffer_width, h = scene_buffer->buffer_height; + wlr_output_transform_coords(scene_buffer->transform, &w, &h); + *width = w; + *height = h; } break; } @@ -1375,9 +1376,9 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node, 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), + struct wlr_fbox box = { + .x = lx, + .y = ly, .width = 1, .height = 1 }; @@ -1428,12 +1429,13 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren double x = entry->x - data->logical.x; double y = entry->y - data->logical.y; - struct wlr_box dst_box = { + struct wlr_fbox node_box = { .x = x, .y = y, }; - scene_node_get_size(node, &dst_box.width, &dst_box.height); - transform_output_box(&dst_box, data); + scene_node_get_size(node, &node_box.width, &node_box.height); + struct wlr_box dst_box = {0}; + transform_output_box(&dst_box, &node_box, data); pixman_region32_t opaque; pixman_region32_init(&opaque); @@ -1873,7 +1875,7 @@ static bool scene_node_invisible(struct wlr_scene_node *node) { } struct render_list_constructor_data { - struct wlr_box box; + struct wlr_fbox box; struct wl_array *render_list; bool calculate_visibility; bool highlight_transparent_region; @@ -2094,11 +2096,12 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( } // Translate the position from scene coordinates to output coordinates - 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); - transform_output_box(&pending.buffer_dst_box, data); + struct wlr_fbox node_box = { + .x = entry->x - scene_output->x, + .y = entry->y - scene_output->y + }; + scene_node_get_size(node, &node_box.width, &node_box.height); + transform_output_box(&pending.buffer_dst_box, &node_box, data); struct wlr_buffer *wlr_buffer = buffer->buffer; struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(wlr_buffer); @@ -2655,7 +2658,7 @@ void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, scene_output, now); } -static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, +static void scene_output_for_each_scene_buffer(const struct wlr_fbox *output_box, struct wlr_scene_node *node, double lx, double ly, wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) { if (!node->enabled) { @@ -2666,10 +2669,10 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, ly += node->y; if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_box node_box = { .x = lx, .y = ly }; + struct wlr_fbox 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)) { + if (wlr_fbox_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); @@ -2686,9 +2689,11 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, 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); + struct wlr_fbox box = { .x = scene_output->x, .y = scene_output->y }; + int width, height; + wlr_output_effective_resolution(scene_output->output, &width, &height); + box.width = width; + box.height = height; scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0, iterator, user_data); } From a5d5a50e5cdee4e3d6e87eab795cef1b4ded019a Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 16 Jun 2025 14:36:34 +0300 Subject: [PATCH 07/12] build: bump minimum required pixman version to 0.46.2 Signed-off-by: Loukas Agorgianitis --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 6d31bdc41..70c464aa6 100644 --- a/meson.build +++ b/meson.build @@ -116,7 +116,7 @@ xkbcommon = dependency('xkbcommon', ], ) pixman = dependency('pixman-1', - version: '>=0.43.0', + version: '>=0.46.2', fallback: 'pixman', default_options: ['werror=false'], ) From ad6730ee757890ff7f7f1eb3f708d1dee2579497 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 28 Apr 2025 17:49:36 +0300 Subject: [PATCH 08/12] util/region: add fractional region helpers Signed-off-by: Loukas Agorgianitis --- include/wlr/util/region.h | 25 ++++++++++++++++++++ util/region.c | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/include/wlr/util/region.h b/include/wlr/util/region.h index ccd926fd1..592cb81ef 100644 --- a/include/wlr/util/region.h +++ b/include/wlr/util/region.h @@ -44,6 +44,31 @@ void wlr_region_scale(pixman_region32_t *dst, const pixman_region32_t *src, void wlr_region_scale_xy(pixman_region32_t *dst, const pixman_region32_t *src, float scale_x, float scale_y); +/** + * Scale a fractional region by the specified factor. + * + * The resulting coordinates are rounded to nearest integer. + * + * Also see wlr_region_scalef_xy(). + */ +void wlr_region_scalef(pixman_region32_t *dst, const pixman_region64f_t *src, + float scale); + +/** + * Scale a region by the specified factors. + * + * The X and Y coordinates are scaled separately by scale_x and scale_y. + * + * The resulting coordinates are rounded to nearest integer. + */ +void wlr_region_scalef_xy(pixman_region32_t *dst, const pixman_region64f_t *src, + float scale_x, float scale_y); + +/** + * Copy an integer region into a fractional region. + */ +void wlr_region64f_copy_from_region32(pixman_region64f_t *dst, const pixman_region32_t *src); + /** * Applies a transform to a region inside a box of size `width` x `height`. */ diff --git a/util/region.c b/util/region.c index b74c55e54..bf95c4fb5 100644 --- a/util/region.c +++ b/util/region.c @@ -36,6 +36,54 @@ void wlr_region_scale_xy(pixman_region32_t *dst, const pixman_region32_t *src, free(dst_rects); } +void wlr_region_scalef(pixman_region32_t *dst, const pixman_region64f_t *src, + float scale) { + wlr_region_scalef_xy(dst, src, scale, scale); +} + +void wlr_region_scalef_xy(pixman_region32_t *dst, const pixman_region64f_t *src, + float scale_x, float scale_y) { + int nrects; + const pixman_box64f_t *src_rects = pixman_region64f_rectangles(src, &nrects); + + pixman_box32_t *dst_rects = malloc(nrects * sizeof(pixman_box32_t)); + if (dst_rects == NULL) { + return; + } + + for (int i = 0; i < nrects; ++i) { + dst_rects[i].x1 = round(src_rects[i].x1 * scale_x); + dst_rects[i].x2 = round(src_rects[i].x2 * scale_x); + dst_rects[i].y1 = round(src_rects[i].y1 * scale_y); + dst_rects[i].y2 = round(src_rects[i].y2 * scale_y); + } + + pixman_region32_fini(dst); + pixman_region32_init_rects(dst, dst_rects, nrects); + free(dst_rects); +} + +void wlr_region64f_copy_from_region32(pixman_region64f_t *dst, const pixman_region32_t *src) { + int nrects; + const pixman_box32_t *src_rects = pixman_region32_rectangles(src, &nrects); + + pixman_box64f_t *dst_rects = malloc(nrects * sizeof(pixman_box64f_t)); + if (dst_rects == NULL) { + return; + } + + for (int i = 0; i < nrects; ++i) { + dst_rects[i].x1 = src_rects[i].x1; + dst_rects[i].x2 = src_rects[i].x2; + dst_rects[i].y1 = src_rects[i].y1; + dst_rects[i].y2 = src_rects[i].y2; + } + + pixman_region64f_fini(dst); + pixman_region64f_init_rects(dst, dst_rects, nrects); + free(dst_rects); +} + void wlr_region_transform(pixman_region32_t *dst, const pixman_region32_t *src, enum wl_output_transform transform, int width, int height) { if (transform == WL_OUTPUT_TRANSFORM_NORMAL) { From 46e10e7d6bbda91a2b552f884c2052cab3a7077d Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 28 Apr 2025 17:47:53 +0300 Subject: [PATCH 09/12] scene: use fractional regions Signed-off-by: Loukas Agorgianitis --- include/wlr/types/wlr_scene.h | 6 +- types/scene/surface.c | 10 +- types/scene/wlr_scene.c | 202 +++++++++++++++++++--------------- 3 files changed, 123 insertions(+), 95 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 5548d4ec5..aa662577a 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -77,7 +77,7 @@ struct wlr_scene_node { struct wlr_addon_set addons; struct { - pixman_region32_t visible; + pixman_region64f_t visible; } WLR_PRIVATE; }; @@ -190,7 +190,7 @@ struct wlr_scene_buffer { struct wlr_fbox src_box; double dst_width, dst_height; enum wl_output_transform transform; - pixman_region32_t opaque_region; + pixman_region64f_t opaque_region; enum wlr_color_transfer_function transfer_function; enum wlr_color_named_primaries primaries; enum wlr_color_encoding color_encoding; @@ -519,7 +519,7 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf * 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); + const pixman_region64f_t *region); /** * Set the source rectangle describing the region of the buffer which will be diff --git a/types/scene/surface.c b/types/scene/surface.c index 414620687..f7646d367 100644 --- a/types/scene/surface.c +++ b/types/scene/surface.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "types/wlr_scene.h" static double get_surface_preferred_buffer_scale(struct wlr_surface *surface) { @@ -182,7 +183,7 @@ static void handle_scene_buffer_frame_done( void wlr_scene_surface_send_frame_done(struct wlr_scene_surface *scene_surface, const struct timespec *when) { - if (!pixman_region32_empty(&scene_surface->buffer->node.visible)) { + if (!pixman_region64f_empty(&scene_surface->buffer->node.visible)) { wlr_surface_send_frame_done(scene_surface->surface, when); } } @@ -298,7 +299,11 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { } } - wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); + pixman_region64f_t opaquef; + pixman_region64f_init(&opaquef); + wlr_region64f_copy_from_region32(&opaquef, &opaque); + + wlr_scene_buffer_set_opaque_region(scene_buffer, &opaquef); wlr_scene_buffer_set_source_box(scene_buffer, &src_box); wlr_scene_buffer_set_dest_size(scene_buffer, width, height); wlr_scene_buffer_set_transform(scene_buffer, state->transform); @@ -343,6 +348,7 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { wlr_scene_buffer_set_buffer(scene_buffer, NULL); } + pixman_region64f_fini(&opaquef); pixman_region32_fini(&opaque); } diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 1f0472105..2f913cd42 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -76,7 +76,7 @@ static void scene_node_init(struct wlr_scene_node *node, wl_list_init(&node->link); wl_signal_init(&node->events.destroy); - pixman_region32_init(&node->visible); + pixman_region64f_init(&node->visible); if (parent != NULL) { wl_list_insert(parent->children.prev, &node->link); @@ -115,7 +115,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { scene_buffer_set_buffer(scene_buffer, NULL); scene_buffer_set_texture(scene_buffer, NULL); - pixman_region32_fini(&scene_buffer->opaque_region); + pixman_region64f_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)); @@ -148,7 +148,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { assert(wl_list_empty(&node->events.destroy.listener_list)); wl_list_remove(&node->link); - pixman_region32_fini(&node->visible); + pixman_region64f_fini(&node->visible); free(node); } @@ -244,7 +244,7 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_fbox *box } static void scene_node_opaque_region(struct wlr_scene_node *node, double x, double y, - pixman_region32_t *opaque) { + pixman_region64f_t *opaque) { double width, height; scene_node_get_size(node, &width, &height); @@ -265,20 +265,20 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, double x, doub } 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); + pixman_region64f_copy(opaque, &scene_buffer->opaque_region); + pixman_region64f_intersect_rectf(opaque, opaque, 0, 0, width, height); + pixman_region64f_translatef(opaque, x, y); return; } } - pixman_region32_fini(opaque); - pixman_region32_init_rect(opaque, x, y, width, height); + pixman_region64f_fini(opaque); + pixman_region64f_init_rectf(opaque, x, y, width, height); } struct scene_update_data { - pixman_region32_t *visible; - const pixman_region32_t *update_region; + pixman_region64f_t *visible; + const pixman_region64f_t *update_region; struct wlr_fbox update_box; struct wl_list *outputs; bool calculate_visibility; @@ -289,11 +289,11 @@ struct scene_update_data { #endif }; -static uint32_t region_area(const pixman_region32_t *region) { - uint32_t area = 0; +static double region_area(const pixman_region64f_t *region) { + double area = 0; int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + pixman_box64f_t *rects = pixman_region64f_rectangles(region, &nrects); for (int i = 0; i < nrects; ++i) { area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1); } @@ -301,8 +301,8 @@ static uint32_t region_area(const pixman_region32_t *region) { return area; } -static void scale_region(pixman_region32_t *region, float scale, bool round_up) { - wlr_region_scale(region, region, scale); +static void scale_region(pixman_region32_t *region, pixman_region64f_t *src, float scale, bool round_up) { + wlr_region_scalef(region, src, scale); if (round_up && floor(scale) != scale) { wlr_region_expand(region, region, 1); @@ -321,10 +321,10 @@ struct render_data { pixman_region32_t damage; }; -static void logical_to_buffer_coords(pixman_region32_t *region, const struct render_data *data, - bool round_up) { +static void logical_to_buffer_coords(pixman_region32_t *region, pixman_region64f_t *src, + const struct render_data *data, bool round_up) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - scale_region(region, data->scale, round_up); + scale_region(region, src, data->scale, round_up); wlr_region_transform(region, region, transform, data->trans_width, data->trans_height); } @@ -381,19 +381,22 @@ 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)) { +static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region64f_t *damage) { + if (pixman_region64f_empty(damage)) { return; } struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { + pixman_region64f_t logical_output_damage; + pixman_region64f_init(&logical_output_damage); + pixman_region64f_copy(&logical_output_damage, damage); + pixman_region64f_translatef(&logical_output_damage, + -scene_output->x, -scene_output->y); 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); + scale_region(&output_damage, &logical_output_damage, scene_output->output->scale, true); + pixman_region64f_fini(&logical_output_damage); output_to_buffer_coords(&output_damage, scene_output->output); scene_output_damage(scene_output, &output_damage); pixman_region32_fini(&output_damage); @@ -409,15 +412,15 @@ static void update_node_update_outputs(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); - uint32_t largest_overlap = 0; + double 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); + if (!pixman_region64f_empty(&node->visible)) { + double visible_area = region_area(&node->visible); // let's update the outputs in two steps: // - the primary outputs @@ -435,19 +438,22 @@ static void update_node_update_outputs(struct wlr_scene_node *node, continue; } - struct wlr_box output_box = { + struct wlr_fbox output_box = { .x = scene_output->x, .y = scene_output->y, }; + int output_width, output_height; wlr_output_effective_resolution(scene_output->output, - &output_box.width, &output_box.height); + &output_width, &output_height); + output_box.width = output_width; + output_box.height = output_height; - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, + pixman_region64f_t intersection; + pixman_region64f_init(&intersection); + pixman_region64f_intersect_rectf(&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); + pixman_region64f_fini(&intersection); // If the overlap accounts for less than 10% of the visible node area, // ignore this output @@ -551,17 +557,17 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, struct wlr_fbox 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, + pixman_region64f_subtract(&node->visible, &node->visible, data->update_region); + pixman_region64f_union(&node->visible, &node->visible, data->visible); + pixman_region64f_intersect_rectf(&node->visible, &node->visible, lx, ly, box.width, box.height); if (data->calculate_visibility) { - pixman_region32_t opaque; - pixman_region32_init(&opaque); + pixman_region64f_t opaque; + pixman_region64f_init(&opaque); scene_node_opaque_region(node, lx, ly, &opaque); - pixman_region32_subtract(data->visible, data->visible, &opaque); - pixman_region32_fini(&opaque); + pixman_region64f_subtract(data->visible, data->visible, &opaque); + pixman_region64f_fini(&opaque); } update_node_update_outputs(node, data->outputs, NULL, NULL); @@ -575,7 +581,7 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, } static void scene_node_visibility(struct wlr_scene_node *node, - pixman_region32_t *visible) { + pixman_region64f_t *visible) { if (!node->enabled) { return; } @@ -589,11 +595,11 @@ static void scene_node_visibility(struct wlr_scene_node *node, return; } - pixman_region32_union(visible, visible, &node->visible); + pixman_region64f_union(visible, visible, &node->visible); } static void scene_node_bounds(struct wlr_scene_node *node, - double x, double y, pixman_region32_t *visible) { + double x, double y, pixman_region64f_t *visible) { if (!node->enabled) { return; } @@ -609,16 +615,16 @@ static void scene_node_bounds(struct wlr_scene_node *node, double width, height; scene_node_get_size(node, &width, &height); - pixman_region32_union_rect(visible, visible, x, y, width, height); + pixman_region64f_union_rectf(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); + const pixman_region64f_t *update_region) { + pixman_region64f_t visible; + pixman_region64f_init(&visible); + pixman_region64f_copy(&visible, update_region); - struct pixman_box32 *region_box = pixman_region32_extents(update_region); + struct pixman_box64f *region_box = pixman_region64f_extents(update_region); struct scene_update_data data = { .visible = &visible, .update_region = update_region, @@ -636,7 +642,7 @@ static void scene_update_region(struct wlr_scene *scene, // 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); + pixman_region64f_fini(&visible); } static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, @@ -654,7 +660,7 @@ static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, return; } - pixman_region32_clear(&node->visible); + pixman_region64f_clear(&node->visible); update_node_update_outputs(node, outputs, NULL, NULL); #if WLR_HAS_XWAYLAND @@ -685,7 +691,7 @@ static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node, * being reparented to a disabled super tree. */ static void scene_node_update(struct wlr_scene_node *node, - pixman_region32_t *damage) { + pixman_region64f_t *damage) { struct wlr_scene *scene = scene_node_get_root(node); double x, y; @@ -697,30 +703,30 @@ static void scene_node_update(struct wlr_scene_node *node, scene_update_region(scene, damage); scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); + pixman_region64f_fini(damage); } return; } - pixman_region32_t visible; + pixman_region64f_t visible; if (!damage) { - pixman_region32_init(&visible); + pixman_region64f_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); + pixman_region64f_t update_region; + pixman_region64f_init(&update_region); + pixman_region64f_copy(&update_region, damage); scene_node_bounds(node, x, y, &update_region); scene_update_region(scene, &update_region); - pixman_region32_fini(&update_region); + pixman_region64f_fini(&update_region); scene_node_visibility(node, damage); scene_damage_outputs(scene, damage); - pixman_region32_fini(damage); + pixman_region64f_fini(damage); } struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, @@ -845,7 +851,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); - pixman_region32_init(&scene_buffer->opaque_region); + pixman_region64f_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; @@ -989,10 +995,13 @@ void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buf wlr_region_expand(&output_damage, &output_damage, dist_x >= dist_y ? dist_x : dist_y); + pixman_region64f_t logical_cull_region; + pixman_region64f_init(&logical_cull_region); + pixman_region64f_copy(&logical_cull_region, &scene_buffer->node.visible); 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); + scale_region(&cull_region, &logical_cull_region, output_scale, true); + pixman_region64f_fini(&logical_cull_region); 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); @@ -1023,23 +1032,23 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, } 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)) { + const pixman_region64f_t *region) { + if (pixman_region64f_equal(&scene_buffer->opaque_region, region)) { return; } - pixman_region32_copy(&scene_buffer->opaque_region, region); + pixman_region64f_copy(&scene_buffer->opaque_region, region); double x, y; if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { return; } - pixman_region32_t update_region; - pixman_region32_init(&update_region); + pixman_region64f_t update_region; + pixman_region64f_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); + pixman_region64f_fini(&update_region); } void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, @@ -1082,7 +1091,7 @@ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer, 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)) { + if (!pixman_region64f_empty(&scene_buffer->node.visible)) { wl_signal_emit_mutable(&scene_buffer->events.frame_done, event); } } @@ -1203,8 +1212,8 @@ void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) { } double x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); + pixman_region64f_t visible; + pixman_region64f_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { scene_node_visibility(node, &visible); } @@ -1285,8 +1294,8 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, } double x, y; - pixman_region32_t visible; - pixman_region32_init(&visible); + pixman_region64f_t visible; + pixman_region64f_init(&visible); if (wlr_scene_node_coords(node, &x, &y)) { scene_node_visibility(node, &visible); } @@ -1415,11 +1424,16 @@ static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { struct wlr_scene_node *node = entry->node; + pixman_region64f_t logical_render_region; + pixman_region64f_init(&logical_render_region); + pixman_region64f_copy(&logical_render_region, &node->visible); + pixman_region64f_translatef(&logical_render_region, -data->logical.x, -data->logical.y); + 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); + logical_to_buffer_coords(&render_region, &logical_render_region, data, true); + pixman_region64f_fini(&logical_render_region); + pixman_region32_intersect(&render_region, &render_region, &data->damage); if (pixman_region32_empty(&render_region)) { pixman_region32_fini(&render_region); @@ -1437,10 +1451,14 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren struct wlr_box dst_box = {0}; transform_output_box(&dst_box, &node_box, data); + pixman_region64f_t logical_opaque; + pixman_region64f_init(&logical_opaque); + scene_node_opaque_region(node, x, y, &logical_opaque); + pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(node, x, y, &opaque); - logical_to_buffer_coords(&opaque, data, false); + logical_to_buffer_coords(&opaque, &logical_opaque, data, false); + pixman_region64f_fini(&logical_opaque); pixman_region32_subtract(&opaque, &render_region, &opaque); switch (node->type) { @@ -1923,17 +1941,17 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, } } - pixman_region32_t intersection; - pixman_region32_init(&intersection); - pixman_region32_intersect_rect(&intersection, &node->visible, + pixman_region64f_t intersection; + pixman_region64f_init(&intersection); + pixman_region64f_intersect_rectf(&intersection, &node->visible, data->box.x, data->box.y, data->box.width, data->box.height); - if (pixman_region32_empty(&intersection)) { - pixman_region32_fini(&intersection); + if (pixman_region64f_empty(&intersection)) { + pixman_region64f_fini(&intersection); return false; } - pixman_region32_fini(&intersection); + pixman_region64f_fini(&intersection); struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); if (!entry) { @@ -2519,13 +2537,17 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, // that may have been omitted from the render list via the black // rect optimization. In order to ensure we don't cull background // rendering in that black rect region, consider the node's visibility. + pixman_region64f_t logical_opaque; + pixman_region64f_init(&logical_opaque); + scene_node_opaque_region(entry->node, entry->x, entry->y, &logical_opaque); + pixman_region64f_intersect(&logical_opaque, &logical_opaque, &entry->node->visible); + pixman_region64f_translatef(&logical_opaque, -scene_output->x, -scene_output->y); + pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); - pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); + logical_to_buffer_coords(&opaque, &logical_opaque, &render_data, false); + pixman_region64f_fini(&logical_opaque); - pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - logical_to_buffer_coords(&opaque, &render_data, false); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } From fa8a954b67708f4c20aaad15bdec5750021e6513 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 28 Apr 2025 17:51:55 +0300 Subject: [PATCH 10/12] scene: use position independent scaling Signed-off-by: Loukas Agorgianitis --- types/scene/wlr_scene.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 2f913cd42..30224346a 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -336,20 +336,12 @@ static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output wlr_output_transform_invert(output->transform), width, height); } -static int scale_length(double length, double offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); -} - -static void scale_box(struct wlr_box *box, struct wlr_fbox *src, float scale) { - box->width = scale_length(src->width, src->x, scale); - box->height = scale_length(src->height, src->y, scale); - box->x = round(src->x * scale); - box->y = round(src->y * scale); -} - static void transform_output_box(struct wlr_box *box, struct wlr_fbox *src, const struct render_data *data) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - scale_box(box, src, data->scale); + box->width = round(src->width * data->scale); + box->height = round(src->height * data->scale); + box->x = round(src->x * data->scale); + box->y = round(src->y * data->scale); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); } From 4118768a75d86064aa5ee5860b0cccbdebea4230 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Thu, 26 Jun 2025 02:19:14 +0300 Subject: [PATCH 11/12] scene: move node active outputs detection logic to dedicated function Signed-off-by: Loukas Agorgianitis --- types/scene/wlr_scene.c | 59 +++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 30224346a..581b04f0e 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -395,31 +395,13 @@ static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region64f } } -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); - +static void visible_region_active_outputs(struct wl_list *outputs, struct wlr_scene_output *ignore, pixman_region64f_t *visible, + uint64_t *active_outputs, size_t *count, struct wlr_scene_output **primary_output) { double 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_region64f_empty(visible)) { + double visible_area = region_area(visible); - if (!pixman_region64f_empty(&node->visible)) { - double 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) { @@ -442,7 +424,7 @@ static void update_node_update_outputs(struct wlr_scene_node *node, pixman_region64f_t intersection; pixman_region64f_init(&intersection); - pixman_region64f_intersect_rectf(&intersection, &node->visible, + pixman_region64f_intersect_rectf(&intersection, visible, output_box.x, output_box.y, output_box.width, output_box.height); uint32_t overlap = region_area(&intersection); pixman_region64f_fini(&intersection); @@ -452,15 +434,40 @@ static void update_node_update_outputs(struct wlr_scene_node *node, if (overlap >= 0.1 * visible_area) { if (overlap >= largest_overlap) { largest_overlap = overlap; - scene_buffer->primary_output = scene_output; + *primary_output = scene_output; } - active_outputs |= 1ull << scene_output->index; - count++; + (*active_outputs) |= 1ull << scene_output->index; + (*count)++; } } } +} +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); + struct wlr_scene_output *old_primary_output = scene_buffer->primary_output; + scene_buffer->primary_output = NULL; + + // 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. + size_t count = 0; + uint64_t active_outputs = 0; + struct wlr_scene_output *primary_output = NULL; + visible_region_active_outputs(outputs, ignore, &node->visible, + &active_outputs, &count, &primary_output); + + scene_buffer->primary_output = primary_output; if (old_primary_output != scene_buffer->primary_output) { scene_buffer->prev_feedback_options = (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; From 3265b228f8361cd0ec4adc9e601832263675d669 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Thu, 26 Jun 2025 03:05:41 +0300 Subject: [PATCH 12/12] scene: use stretch scaling when node is between outputs with different scales Signed-off-by: Loukas Agorgianitis --- include/wlr/types/wlr_scene.h | 1 + types/scene/wlr_scene.c | 75 ++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index aa662577a..afb4a7d89 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -72,6 +72,7 @@ struct wlr_scene_node { struct wl_signal destroy; } events; + bool scaling_group; void *data; struct wlr_addon_set addons; diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 581b04f0e..f8de38e9e 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -336,10 +336,20 @@ static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output wlr_output_transform_invert(output->transform), width, height); } -static void transform_output_box(struct wlr_box *box, struct wlr_fbox *src, const struct render_data *data) { +static int scale_length(double length, double offset, float scale) { + return round((offset + length) * scale) - round(offset * scale); +} + +static void transform_output_box(struct wlr_box *box, struct wlr_fbox *src, + const struct render_data *data, bool scale_stretch) { enum wl_output_transform transform = wlr_output_transform_invert(data->transform); - box->width = round(src->width * data->scale); - box->height = round(src->height * data->scale); + if (scale_stretch) { + box->width = scale_length(src->width, src->x, data->scale); + box->height = scale_length(src->height, src->y, data->scale); + } else { + box->width = round(src->width * data->scale); + box->height = round(src->height * data->scale); + } box->x = round(src->x * data->scale); box->y = round(src->y * data->scale); wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); @@ -1411,6 +1421,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, struct render_list_entry { struct wlr_scene_node *node; + bool scale_stretch; bool highlight_transparent_region; double x, y; }; @@ -1448,7 +1459,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren }; scene_node_get_size(node, &node_box.width, &node_box.height); struct wlr_box dst_box = {0}; - transform_output_box(&dst_box, &node_box, data); + transform_output_box(&dst_box, &node_box, data, entry->scale_stretch); pixman_region64f_t logical_opaque; pixman_region64f_init(&logical_opaque); @@ -1894,6 +1905,7 @@ static bool scene_node_invisible(struct wlr_scene_node *node) { struct render_list_constructor_data { struct wlr_fbox box; struct wl_array *render_list; + struct wlr_scene_output *scene_output; bool calculate_visibility; bool highlight_transparent_region; bool fractional_scale; @@ -1908,6 +1920,21 @@ static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) scene_buffer->opacity == 1.0; } +static struct wlr_scene_node *scene_node_scaling_group(struct wlr_scene_node *node) { + struct wlr_scene_node *scaling_group = node; + while (true) { + if (node->scaling_group) { + scaling_group = node; + break; + } + if (node->parent == NULL) { + break; + } + node = &node->parent->node; + } + return scaling_group; +} + static bool construct_render_list_iterator(struct wlr_scene_node *node, double lx, double ly, void *_data) { struct render_list_constructor_data *data = _data; @@ -1952,6 +1979,29 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, pixman_region64f_fini(&intersection); + bool scale_stretch = false; + if (data->scene_output != NULL) { + struct wlr_scene_node *scaling_group = scene_node_scaling_group(node); + + double x, y; + wlr_scene_node_coords(scaling_group, &x, &y); + + pixman_region64f_t visible; + pixman_region64f_init(&visible); + scene_node_bounds(scaling_group, x, y, &visible); + + uint64_t active_outputs; + size_t count; + struct wlr_scene_output *primary_output = NULL; + visible_region_active_outputs(&data->scene_output->scene->outputs, NULL, &visible, + &active_outputs, &count, &primary_output); + pixman_region64f_fini(&visible); + + scale_stretch = primary_output != NULL && + data->scene_output != primary_output && + data->scene_output->output->scale != primary_output->output->scale; + } + struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); if (!entry) { return false; @@ -1961,6 +2011,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, .node = node, .x = lx, .y = ly, + .scale_stretch = scale_stretch, .highlight_transparent_region = data->highlight_transparent_region, }; @@ -2118,7 +2169,7 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( .y = entry->y - scene_output->y }; scene_node_get_size(node, &node_box.width, &node_box.height); - transform_output_box(&pending.buffer_dst_box, &node_box, data); + transform_output_box(&pending.buffer_dst_box, &node_box, data, false); struct wlr_buffer *wlr_buffer = buffer->buffer; struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(wlr_buffer); @@ -2295,6 +2346,16 @@ cleanup_transforms: return result; } +static bool scene_outputs_have_multiple_scale_factors(struct wlr_scene *scene, float scale) { + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + if (scene_output->output->enabled && scene_output->output->scale != scale) { + return true; + } + } + return false; +} + bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { struct wlr_scene_output_state_options default_options = {0}; @@ -2363,9 +2424,13 @@ 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; + bool multiple_output_scales = + scene_outputs_have_multiple_scale_factors(scene_output->scene, render_data.scale); + struct render_list_constructor_data list_con = { .box = render_data.logical, .render_list = &scene_output->render_list, + .scene_output = multiple_output_scales ? scene_output : NULL, .calculate_visibility = scene_output->scene->calculate_visibility, .highlight_transparent_region = scene_output->scene->highlight_transparent_region, .fractional_scale = floor(render_data.scale) != render_data.scale,