Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/types/scene/wlr_scene.c (160.8K)
1 #include <assert.h>
2 #include <math.h>
3 #include <pixman.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <time.h>
9 #include <wlr/backend.h>
10 #include <wlr/render/swapchain.h>
11 #include <wlr/render/drm_syncobj.h>
12 #include <wlr/render/wlr_renderer.h>
13 #include <wlr/types/wlr_color_management_v1.h>
14 #include <wlr/types/wlr_compositor.h>
15 #include <wlr/types/wlr_damage_ring.h>
16 #include <wlr/types/wlr_gamma_control_v1.h>
17 #include <wlr/types/wlr_linux_dmabuf_v1.h>
18 #include <wlr/types/wlr_presentation_time.h>
19 #include <wlr/util/log.h>
20 #include <wlr/util/region.h>
21 #include <wlr/util/transform.h>
22
23 static bool cce_scene_blur_debug(void);
24
25 #include "render/color.h"
26 #include "render/tracy.h"
27 #include "scenefx/render/fx_renderer/fx_offscreen_buffers.h"
28 #include "scenefx/render/pass.h"
29 #include "scenefx/types/fx/blur_data.h"
30 #include "scenefx/types/fx/clipped_region.h"
31 #include "scenefx/types/wlr_scene.h"
32 #include "types/fx/clipped_region.h"
33 #include "types/wlr_output.h"
34 #include "types/wlr_scene.h"
35 #include "util/array.h"
36 #include "util/env.h"
37 #include "util/time.h"
38
39 #include <wlr/config.h>
40
41 #if WLR_HAS_XWAYLAND
42 #include <wlr/xwayland/xwayland.h>
43 #endif
44
45 #define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30
46 #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
47
48 #define CURSOR_HISTORY_SIZE 16
49 // Only this many of the most recent footprints are damage: a stale cursor
50 // can only be baked into a buffer the swapchain still hands back, and a
51 // wlroots swapchain holds at most WLR_SWAPCHAIN_CAP (4) — so twice that is
52 // a margin, not a guess. Until 2026-10-06 all 16 counted, forever: the
53 // trail of a moving cursor was re-rendered (and re-blurred) every frame, and
54 // after it stopped, every unrelated frame still pulled in the old trail.
55 #define CURSOR_HISTORY_DAMAGE 8
56 static struct {
57 int x, y, w, h;
58 bool valid;
59 // The footprint is in this output's coordinates.
60 struct wlr_output *output;
61 } g_cursor_history[CURSOR_HISTORY_SIZE];
62 static int g_cursor_history_index = 0;
63
64 struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) {
65 assert(node->type == WLR_SCENE_NODE_TREE);
66 struct wlr_scene_tree *tree = wl_container_of(node, tree, node);
67 return tree;
68 }
69
70 struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) {
71 assert(node->type == WLR_SCENE_NODE_RECT);
72 struct wlr_scene_rect *rect = wl_container_of(node, rect, node);
73 return rect;
74 }
75
76 struct wlr_scene_optimized_blur *wlr_scene_optimized_blur_from_node(
77 struct wlr_scene_node *node) {
78 assert(node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR);
79 struct wlr_scene_optimized_blur *blur_node =
80 wl_container_of(node, blur_node, node);
81 return blur_node;
82 }
83
84 struct wlr_scene_buffer *wlr_scene_buffer_from_node(
85 struct wlr_scene_node *node) {
86 assert(node->type == WLR_SCENE_NODE_BUFFER);
87 struct wlr_scene_buffer *buffer = wl_container_of(node, buffer, node);
88 return buffer;
89 }
90
91 struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node) {
92 assert(node->type == WLR_SCENE_NODE_SHADOW);
93 struct wlr_scene_shadow *shadow = wl_container_of(node, shadow, node);
94 return shadow;
95 }
96
97 struct wlr_scene_frame *wlr_scene_frame_from_node(struct wlr_scene_node *node) {
98 assert(node->type == WLR_SCENE_NODE_FRAME);
99 struct wlr_scene_frame *frame = wl_container_of(node, frame, node);
100 return frame;
101 }
102
103 struct wlr_scene_bevel *wlr_scene_bevel_from_node(struct wlr_scene_node *node) {
104 assert(node->type == WLR_SCENE_NODE_BEVEL);
105 struct wlr_scene_bevel *bevel = wl_container_of(node, bevel, node);
106 return bevel;
107 }
108
109 struct wlr_scene_droplet *wlr_scene_droplet_from_node(struct wlr_scene_node *node) {
110 assert(node->type == WLR_SCENE_NODE_DROPLET);
111 struct wlr_scene_droplet *droplet = wl_container_of(node, droplet, node);
112 return droplet;
113 }
114
115 struct wlr_scene_blur *wlr_scene_blur_from_node(struct wlr_scene_node *node) {
116 assert(node->type == WLR_SCENE_NODE_BLUR);
117 struct wlr_scene_blur *blur = wl_container_of(node, blur, node);
118 return blur;
119 }
120
121 struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) {
122 struct wlr_scene_tree *tree;
123 if (node->type == WLR_SCENE_NODE_TREE) {
124 tree = wlr_scene_tree_from_node(node);
125 } else {
126 tree = node->parent;
127 }
128
129 while (tree->node.parent != NULL) {
130 tree = tree->node.parent;
131 }
132 struct wlr_scene *scene = wl_container_of(tree, scene, tree);
133 return scene;
134 }
135
136 static void scene_node_init(struct wlr_scene_node *node,
137 enum wlr_scene_node_type type, struct wlr_scene_tree *parent) {
138 *node = (struct wlr_scene_node){
139 .type = type,
140 .parent = parent,
141 .enabled = true,
142 };
143
144 wl_list_init(&node->link);
145
146 wl_signal_init(&node->events.destroy);
147 pixman_region32_init(&node->visible);
148
149 if (parent != NULL) {
150 wl_list_insert(parent->children.prev, &node->link);
151 }
152
153 wlr_addon_set_init(&node->addons);
154 }
155
156 struct highlight_region {
157 pixman_region32_t region;
158 struct timespec when;
159 struct wl_list link;
160 };
161
162 static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
163 struct wlr_buffer *buffer);
164 static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer,
165 struct wlr_texture *texture);
166
167 void wlr_scene_node_destroy(struct wlr_scene_node *node) {
168 if (node == NULL) {
169 return;
170 }
171
172 // We want to call the destroy listeners before we do anything else
173 // in case the destroy signal would like to remove children before they
174 // are recursively destroyed.
175 wl_signal_emit_mutable(&node->events.destroy, NULL);
176 wlr_addon_set_finish(&node->addons);
177
178 wlr_scene_node_set_enabled(node, false);
179
180 struct wlr_scene *scene = scene_node_get_root(node);
181 if (node->type == WLR_SCENE_NODE_BUFFER) {
182 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
183
184 uint64_t active = scene_buffer->active_outputs;
185 if (active) {
186 struct wlr_scene_output *scene_output;
187 wl_list_for_each(scene_output, &scene->outputs, link) {
188 if (active & (1ull << scene_output->index)) {
189 wl_signal_emit_mutable(&scene_buffer->events.output_leave,
190 scene_output);
191 }
192 }
193 }
194
195 scene_buffer_set_buffer(scene_buffer, NULL);
196 scene_buffer_set_texture(scene_buffer, NULL);
197 pixman_region32_fini(&scene_buffer->opaque_region);
198 wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
199 linked_node_destroy(&scene_buffer->blur);
200
201 assert(wl_list_empty(&scene_buffer->events.output_leave.listener_list));
202 assert(wl_list_empty(&scene_buffer->events.output_enter.listener_list));
203 assert(wl_list_empty(&scene_buffer->events.outputs_update.listener_list));
204 assert(wl_list_empty(&scene_buffer->events.output_sample.listener_list));
205 assert(wl_list_empty(&scene_buffer->events.frame_done.listener_list));
206 } else if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
207 pixman_region32_fini(&wlr_scene_optimized_blur_from_node(node)->baked_region);
208 pixman_region32_fini(&wlr_scene_optimized_blur_from_node(node)->edge_region);
209 } else if (node->type == WLR_SCENE_NODE_TREE) {
210 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
211
212 if (scene_tree == &scene->tree) {
213 assert(!node->parent);
214 struct wlr_scene_output *scene_output, *scene_output_tmp;
215 wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) {
216 wlr_scene_output_destroy(scene_output);
217 }
218
219 wl_list_remove(&scene->linux_dmabuf_v1_destroy.link);
220 wl_list_remove(&scene->gamma_control_manager_v1_destroy.link);
221 wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link);
222 } else {
223 assert(node->parent);
224 }
225
226 struct wlr_scene_node *child, *child_tmp;
227 wl_list_for_each_safe(child, child_tmp,
228 &scene_tree->children, link) {
229 wlr_scene_node_destroy(child);
230 }
231 } else if (node->type == WLR_SCENE_NODE_BLUR) {
232 struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
233 linked_node_destroy(&blur->transparency_mask_source);
234 }
235
236 assert(wl_list_empty(&node->events.destroy.listener_list));
237
238 wl_list_remove(&node->link);
239 pixman_region32_fini(&node->visible);
240 free(node);
241 }
242
243 static void scene_tree_init(struct wlr_scene_tree *tree,
244 struct wlr_scene_tree *parent) {
245 *tree = (struct wlr_scene_tree){0};
246 scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent);
247 wl_list_init(&tree->children);
248 }
249
250 struct wlr_scene *wlr_scene_create(void) {
251 struct wlr_scene *scene = calloc(1, sizeof(*scene));
252 if (scene == NULL) {
253 return NULL;
254 }
255
256 scene_tree_init(&scene->tree, NULL);
257
258 wl_list_init(&scene->outputs);
259 wl_list_init(&scene->linux_dmabuf_v1_destroy.link);
260 wl_list_init(&scene->gamma_control_manager_v1_destroy.link);
261 wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link);
262
263 scene->restack_xwayland_surfaces = true;
264
265 const char *debug_damage_options[] = {
266 "none",
267 "rerender",
268 "highlight",
269 NULL
270 };
271
272 scene->debug_damage_option = env_parse_switch("WLR_SCENE_DEBUG_DAMAGE", debug_damage_options);
273 scene->direct_scanout = !env_parse_bool("WLR_SCENE_DISABLE_DIRECT_SCANOUT");
274 scene->calculate_visibility = !env_parse_bool("WLR_SCENE_DISABLE_VISIBILITY");
275 scene->highlight_transparent_region = env_parse_bool("WLR_SCENE_HIGHLIGHT_TRANSPARENT_REGION");
276
277 scene->blur_data = blur_data_get_default();
278
279 return scene;
280 }
281
282 struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) {
283 assert(parent);
284
285 struct wlr_scene_tree *tree = calloc(1, sizeof(*tree));
286 if (tree == NULL) {
287 return NULL;
288 }
289
290 scene_tree_init(tree, parent);
291 return tree;
292 }
293
294 typedef bool (*scene_node_box_iterator_func_t)(struct wlr_scene_node *node,
295 int sx, int sy, void *data);
296
297 static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
298 scene_node_box_iterator_func_t iterator, void *user_data, int lx, int ly) {
299 if (!node->enabled) {
300 return false;
301 }
302
303 switch (node->type) {
304 case WLR_SCENE_NODE_TREE:;
305 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
306 struct wlr_scene_node *child;
307 wl_list_for_each_reverse(child, &scene_tree->children, link) {
308 if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) {
309 return true;
310 }
311 }
312 break;
313 case WLR_SCENE_NODE_RECT:
314 case WLR_SCENE_NODE_BUFFER:
315 case WLR_SCENE_NODE_SHADOW:
316 case WLR_SCENE_NODE_BEVEL:
317 case WLR_SCENE_NODE_FRAME:
318 case WLR_SCENE_NODE_DROPLET:
319 case WLR_SCENE_NODE_OPTIMIZED_BLUR:
320 case WLR_SCENE_NODE_BLUR:;
321 struct wlr_box node_box = { .x = lx, .y = ly };
322 scene_node_get_size(node, &node_box.width, &node_box.height);
323
324 if (wlr_box_intersection(&node_box, &node_box, box) &&
325 iterator(node, lx, ly, user_data)) {
326 return true;
327 }
328 break;
329 }
330
331 return false;
332 }
333
334 static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
335 scene_node_box_iterator_func_t iterator, void *user_data) {
336 int x, y;
337 wlr_scene_node_coords(node, &x, &y);
338
339 return _scene_nodes_in_box(node, box, iterator, user_data, x, y);
340 }
341
342 static pixman_region32_t create_corner_location_region(struct fx_corner_radii corners, int x, int y, int width, int height) {
343 pixman_region32_t corner_region;
344 pixman_region32_init(&corner_region);
345 if (corners.top_left) {
346 pixman_region32_union_rect(&corner_region, &corner_region, x, y, corners.top_left, corners.top_left);
347 }
348
349 if (corners.top_right) {
350 pixman_region32_union_rect(&corner_region, &corner_region, x + (width - corners.top_right), y,
351 corners.top_right, corners.top_right);
352 }
353
354 if (corners.bottom_left) {
355 pixman_region32_union_rect(&corner_region, &corner_region, x, y + (height - corners.bottom_left),
356 corners.bottom_left, corners.bottom_left);
357 }
358
359 if (corners.bottom_right) {
360 pixman_region32_union_rect(&corner_region, &corner_region,
361 x + (width - corners.bottom_right), y + (height - corners.bottom_right),
362 corners.bottom_right, corners.bottom_right);
363 }
364
365 return corner_region;
366 }
367
368 static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
369 pixman_region32_t *opaque) {
370 int width, height;
371 scene_node_get_size(node, &width, &height);
372
373 if (node->type == WLR_SCENE_NODE_RECT) {
374 struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
375 if (scene_rect->color[3] != 1 || scene_rect->fade_inset > 0) {
376 return;
377 }
378
379 pixman_region32_fini(opaque);
380 pixman_region32_init_rect(opaque, x, y, width, height);
381
382 // subtract corners from opaque region
383 if (!fx_corner_radii_is_empty(&scene_rect->corners)) {
384 pixman_region32_t corners = create_corner_location_region(scene_rect->corners, x, y, width, height);
385 pixman_region32_subtract(opaque, opaque, &corners);
386 pixman_region32_fini(&corners);
387 }
388
389 // subtract clipped area from opaque region
390 if (!wlr_box_empty(&scene_rect->clipped_region.area)) {
391 struct wlr_box *clipped = &scene_rect->clipped_region.area;
392 pixman_region32_t clipped_region;
393 pixman_region32_init_rect(&clipped_region, clipped->x + x, clipped->y + y,
394 clipped->width, clipped->height);
395 pixman_region32_subtract(opaque, opaque, &clipped_region);
396 pixman_region32_fini(&clipped_region);
397 }
398 return;
399 } else if (node->type == WLR_SCENE_NODE_BUFFER) {
400 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
401
402 if (!scene_buffer->buffer) {
403 return;
404 }
405
406 if (scene_buffer->opacity != 1) {
407 return;
408 }
409
410 if (!scene_buffer->buffer_is_opaque) {
411 pixman_region32_copy(opaque, &scene_buffer->opaque_region);
412 pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height);
413 pixman_region32_translate(opaque, x, y);
414 } else {
415 pixman_region32_fini(opaque);
416 pixman_region32_init_rect(opaque, x, y, width, height);
417 }
418
419 // subtract the corners from the opaque region
420 if (!fx_corner_radii_is_empty(&scene_buffer->corners)) {
421 pixman_region32_t corners = create_corner_location_region(scene_buffer->corners, x, y, width, height);
422 pixman_region32_subtract(opaque, opaque, &corners);
423 pixman_region32_fini(&corners);
424 }
425
426 return;
427 } else if (node->type == WLR_SCENE_NODE_SHADOW) {
428 // TODO: test & handle case of blur sigma = 0 and color[3] = 1?
429 return;
430 } else if (node->type == WLR_SCENE_NODE_BEVEL
431 || node->type == WLR_SCENE_NODE_FRAME
432 || node->type == WLR_SCENE_NODE_DROPLET
433 || node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR
434 || node->type == WLR_SCENE_NODE_BLUR) {
435 // Always transparent (the droplet is opaque inside its silhouette
436 // but never box-shaped, so it must not occlude as a box).
437 return;
438 }
439
440 pixman_region32_fini(opaque);
441 pixman_region32_init_rect(opaque, x, y, width, height);
442 }
443
444 struct scene_update_data {
445 struct wlr_scene_node *updated_node;
446 pixman_region32_t *visible;
447 const pixman_region32_t *update_region;
448 struct wlr_box update_box;
449 struct wl_list *outputs;
450 bool calculate_visibility;
451 bool restack_xwayland_surfaces;
452
453 #if WLR_HAS_XWAYLAND
454 struct wlr_xwayland_surface *restack_above;
455 #endif
456 bool blur_frozen;
457 bool content_update;
458 };
459
460 static uint32_t region_area(const pixman_region32_t *region) {
461 uint32_t area = 0;
462
463 int nrects;
464 pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects);
465 for (int i = 0; i < nrects; ++i) {
466 area += (rects[i].x2 - rects[i].x1) * (rects[i].y2 - rects[i].y1);
467 }
468
469 return area;
470 }
471
472 static void scale_region(pixman_region32_t *region, float scale, bool round_up) {
473 wlr_region_scale(region, region, scale);
474
475 if (round_up && floor(scale) != scale) {
476 wlr_region_expand(region, region, 1);
477 }
478 }
479
480 struct render_data {
481 enum wl_output_transform transform;
482 float scale;
483 struct wlr_box logical;
484 int trans_width, trans_height;
485
486 struct wlr_scene_output *output;
487
488 struct wlr_render_pass *render_pass;
489 pixman_region32_t damage;
490 };
491
492 static void logical_to_buffer_coords(pixman_region32_t *region, const struct render_data *data,
493 bool round_up) {
494 enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
495 scale_region(region, data->scale, round_up);
496 wlr_region_transform(region, region, transform, data->trans_width, data->trans_height);
497 }
498
499 static void output_to_buffer_coords(pixman_region32_t *damage, struct wlr_output *output) {
500 int width, height;
501 wlr_output_transformed_resolution(output, &width, &height);
502
503 wlr_region_transform(damage, damage,
504 wlr_output_transform_invert(output->transform), width, height);
505 }
506
507 static int scale_length(int length, int offset, float scale) {
508 return round((offset + length) * scale) - round(offset * scale);
509 }
510
511 static void scale_box(struct wlr_box *box, float scale) {
512 box->width = scale_length(box->width, box->x, scale);
513 box->height = scale_length(box->height, box->y, scale);
514 box->x = round(box->x * scale);
515 box->y = round(box->y * scale);
516 }
517
518 static void transform_output_box(struct wlr_box *box, const struct render_data *data) {
519 enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
520 scale_box(box, data->scale);
521 wlr_box_transform(box, box, transform, data->trans_width, data->trans_height);
522 }
523
524 /* Whether a node renders with the scene's desk sub-pixel offset: any
525 * ancestor is one of the scene's desk trees. */
526 static bool scene_node_has_desk_offset(struct wlr_scene *scene, struct wlr_scene_node *node) {
527 for (struct wlr_scene_tree *t = node->parent; t != NULL; t = t->node.parent) {
528 for (int i = 0; i < WLR_SCENE_DESK_TREES; i++) {
529 if (scene->desk_trees[i] == t) {
530 return true;
531 }
532 }
533 }
534 return false;
535 }
536
537 /* The desk offset in an output's device px (each 0 or negative). Only a
538 * NORMAL transform is shifted: on a rotated output the vector would need
539 * rotating too, and no cce output is rotated. */
540 static void scene_desk_offset_px(struct wlr_scene *scene, float scale,
541 enum wl_output_transform transform, int *dx, int *dy) {
542 *dx = 0;
543 *dy = 0;
544 if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
545 return;
546 }
547 *dx = (int)lround(scene->desk_sub_x * scale);
548 *dy = (int)lround(scene->desk_sub_y * scale);
549 }
550
551 static void scene_output_damage(struct wlr_scene_output *scene_output,
552 const pixman_region32_t *damage) {
553 struct wlr_output *output = scene_output->output;
554
555 pixman_region32_t clipped;
556 pixman_region32_init(&clipped);
557 pixman_region32_intersect_rect(&clipped, damage, 0, 0, output->width, output->height);
558
559 // A desk shifted by the sub-pixel offset draws its nodes up to one
560 // device px away from where their layout boxes say; widen the damage
561 // by that shift so the drawn pixels are always inside it.
562 {
563 int dx, dy;
564 scene_desk_offset_px(scene_output->scene, output->scale, output->transform, &dx, &dy);
565 if (dx != 0 || dy != 0) {
566 pixman_region32_t shifted;
567 pixman_region32_init(&shifted);
568 pixman_region32_copy(&shifted, &clipped);
569 pixman_region32_translate(&shifted, dx, dy);
570 pixman_region32_union(&clipped, &clipped, &shifted);
571 pixman_region32_intersect_rect(&clipped, &clipped, 0, 0, output->width, output->height);
572 pixman_region32_fini(&shifted);
573 }
574 }
575
576 if (!pixman_region32_empty(&clipped)) {
577 wlr_output_schedule_frame(scene_output->output);
578 wlr_damage_ring_add(&scene_output->damage_ring, &clipped);
579
580 pixman_region32_union(&scene_output->pending_commit_damage,
581 &scene_output->pending_commit_damage, &clipped);
582 }
583
584 pixman_region32_fini(&clipped);
585 }
586
587 static void scene_output_damage_whole(struct wlr_scene_output *scene_output) {
588 struct wlr_output *output = scene_output->output;
589
590 pixman_region32_t damage;
591 pixman_region32_init_rect(&damage, 0, 0, output->width, output->height);
592 scene_output_damage(scene_output, &damage);
593 pixman_region32_fini(&damage);
594 }
595
596 static void scene_damage_outputs(struct wlr_scene *scene, const pixman_region32_t *damage) {
597 if (pixman_region32_empty(damage)) {
598 return;
599 }
600
601 struct wlr_scene_output *scene_output;
602 wl_list_for_each(scene_output, &scene->outputs, link) {
603 pixman_region32_t output_damage;
604 pixman_region32_init(&output_damage);
605 pixman_region32_copy(&output_damage, damage);
606 pixman_region32_translate(&output_damage,
607 -scene_output->x, -scene_output->y);
608 scale_region(&output_damage, scene_output->output->scale, true);
609 output_to_buffer_coords(&output_damage, scene_output->output);
610 scene_output_damage(scene_output, &output_damage);
611 pixman_region32_fini(&output_damage);
612 }
613 }
614
615 static void update_node_update_outputs(struct wlr_scene_node *node,
616 struct wl_list *outputs, struct wlr_scene_output *ignore,
617 struct wlr_scene_output *force) {
618 if (node->type != WLR_SCENE_NODE_BUFFER) {
619 return;
620 }
621
622 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
623
624 uint32_t largest_overlap = 0;
625 struct wlr_scene_output *old_primary_output = scene_buffer->primary_output;
626 scene_buffer->primary_output = NULL;
627
628 size_t count = 0;
629 uint64_t active_outputs = 0;
630
631 if (!pixman_region32_empty(&node->visible)) {
632 uint32_t visible_area = region_area(&node->visible);
633
634 // let's update the outputs in two steps:
635 // - the primary outputs
636 // - the enter/leave signals
637 // This ensures that the enter/leave signals can rely on the primary output
638 // to have a reasonable value. Otherwise, they may get a value that's in
639 // the middle of a calculation.
640 struct wlr_scene_output *scene_output;
641 wl_list_for_each(scene_output, outputs, link) {
642 if (scene_output == ignore) {
643 continue;
644 }
645
646 if (!scene_output->output->enabled) {
647 continue;
648 }
649
650 struct wlr_box output_box = {
651 .x = scene_output->x,
652 .y = scene_output->y,
653 };
654 wlr_output_effective_resolution(scene_output->output,
655 &output_box.width, &output_box.height);
656
657 pixman_region32_t intersection;
658 pixman_region32_init(&intersection);
659 pixman_region32_intersect_rect(&intersection, &node->visible,
660 output_box.x, output_box.y, output_box.width, output_box.height);
661 uint32_t overlap = region_area(&intersection);
662 pixman_region32_fini(&intersection);
663
664 // If the overlap accounts for less than 10% of the visible node area,
665 // ignore this output
666 if (overlap >= 0.1 * visible_area) {
667 if (overlap >= largest_overlap) {
668 largest_overlap = overlap;
669 scene_buffer->primary_output = scene_output;
670 }
671
672 active_outputs |= 1ull << scene_output->index;
673 count++;
674 }
675 }
676 }
677
678 // NOT cleared on a primary_output change (cce): clearing it defeats the
679 // duplicate-suppression memcmp in scene_buffer_send_dmabuf_feedback, and
680 // the options it compares already carry everything a change here could
681 // affect — main_renderer and scanout_primary_output. Composition feedback
682 // is {renderer, NULL} whichever output is primary, so clearing only ever
683 // forced a byte-identical resend.
684 //
685 // Each resend costs the client a format-table fd, and Mesa parks those on
686 // its private WSI queues until the next acquire/present — so an idle,
687 // damage-driven window banks one per resend and never drains. Measured on
688 // a live session before this change: one enter+exit of the overview added
689 // 67 fds to every long-lived client at once, against a 1024 soft limit.
690
691 uint64_t old_active = scene_buffer->active_outputs;
692 scene_buffer->active_outputs = active_outputs;
693
694 struct wlr_scene_output *scene_output;
695 wl_list_for_each(scene_output, outputs, link) {
696 uint64_t mask = 1ull << scene_output->index;
697 bool intersects = active_outputs & mask;
698 bool intersects_before = old_active & mask;
699
700 if (intersects && !intersects_before) {
701 wl_signal_emit_mutable(&scene_buffer->events.output_enter, scene_output);
702 } else if (!intersects && intersects_before) {
703 wl_signal_emit_mutable(&scene_buffer->events.output_leave, scene_output);
704 }
705 }
706
707 // if there are active outputs on this node, we should always have a primary
708 // output
709 assert(!scene_buffer->active_outputs || scene_buffer->primary_output);
710
711 // Skip output update event if nothing was updated
712 if (old_active == active_outputs &&
713 (!force || ((1ull << force->index) & ~active_outputs)) &&
714 old_primary_output == scene_buffer->primary_output) {
715 return;
716 }
717
718 struct wlr_scene_output *outputs_array[64];
719 struct wlr_scene_outputs_update_event event = {
720 .active = outputs_array,
721 .size = count,
722 };
723
724 size_t i = 0;
725 wl_list_for_each(scene_output, outputs, link) {
726 if (~active_outputs & (1ull << scene_output->index)) {
727 continue;
728 }
729
730 assert(i < count);
731 outputs_array[i++] = scene_output;
732 }
733
734 wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event);
735 }
736
737 #if WLR_HAS_XWAYLAND
738 static struct wlr_xwayland_surface *scene_node_try_get_managed_xwayland_surface(
739 struct wlr_scene_node *node) {
740 if (node->type != WLR_SCENE_NODE_BUFFER) {
741 return NULL;
742 }
743
744 struct wlr_scene_buffer *buffer_node = wlr_scene_buffer_from_node(node);
745 struct wlr_scene_surface *surface_node = wlr_scene_surface_try_from_buffer(buffer_node);
746 if (!surface_node) {
747 return NULL;
748 }
749
750 struct wlr_xwayland_surface *xwayland_surface =
751 wlr_xwayland_surface_try_from_wlr_surface(surface_node->surface);
752 if (!xwayland_surface || xwayland_surface->override_redirect) {
753 return NULL;
754 }
755
756 return xwayland_surface;
757 }
758
759 static void restack_xwayland_surface(struct wlr_scene_node *node,
760 struct wlr_box *box, struct scene_update_data *data) {
761 struct wlr_xwayland_surface *xwayland_surface =
762 scene_node_try_get_managed_xwayland_surface(node);
763 if (!xwayland_surface) {
764 return;
765 }
766
767 // ensure this node is entirely inside the update region. If not, we can't
768 // restack this node since we're not considering the whole thing.
769 if (wlr_box_contains_box(&data->update_box, box)) {
770 if (data->restack_above) {
771 wlr_xwayland_surface_restack(xwayland_surface, data->restack_above, XCB_STACK_MODE_BELOW);
772 } else {
773 wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_ABOVE);
774 }
775 }
776
777 data->restack_above = xwayland_surface;
778 }
779 #endif
780
781 static bool cce_scene_blur_debug(void) {
782 static int enabled = -1;
783 if (enabled < 0) {
784 const char *v = getenv("CCE_BLUR_DEBUG");
785 enabled = v && *v && strcmp(v, "0") != 0;
786 }
787 return enabled;
788 }
789
790 static bool scene_node_is_below(struct wlr_scene_node *a, struct wlr_scene_node *b) {
791 if (a == b) {
792 return false;
793 }
794
795 struct wlr_scene_node *chain_a[64];
796 struct wlr_scene_node *chain_b[64];
797 int len_a = 0;
798 int len_b = 0;
799
800 struct wlr_scene_node *curr = a;
801 while (curr && len_a < 64) {
802 chain_a[len_a++] = curr;
803 curr = curr->parent ? &curr->parent->node : NULL;
804 }
805
806 curr = b;
807 while (curr && len_b < 64) {
808 chain_b[len_b++] = curr;
809 curr = curr->parent ? &curr->parent->node : NULL;
810 }
811
812 int idx_a = len_a - 1;
813 int idx_b = len_b - 1;
814
815 if (chain_a[idx_a] != chain_b[idx_b]) {
816 return false;
817 }
818
819 while (idx_a >= 0 && idx_b >= 0 && chain_a[idx_a] == chain_b[idx_b]) {
820 idx_a--;
821 idx_b--;
822 }
823
824 if (idx_a < 0) {
825 return true;
826 }
827 if (idx_b < 0) {
828 return false;
829 }
830
831 struct wlr_scene_tree *lca = wlr_scene_tree_from_node(chain_a[idx_a + 1]);
832 struct wlr_scene_node *child;
833 wl_list_for_each(child, &lca->children, link) {
834 if (child == chain_a[idx_a]) {
835 return true;
836 }
837 if (child == chain_b[idx_b]) {
838 return false;
839 }
840 }
841
842 return false;
843 }
844
845 static bool scene_node_update_iterator(struct wlr_scene_node *node,
846 int lx, int ly, void *_data) {
847 struct scene_update_data *data = _data;
848
849 if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
850 struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
851 // A frozen scene ignores nodes MOVING below (the backdrop travelling
852 // with a pan); content changing below still re-bakes.
853 if (data->updated_node && (!data->blur_frozen || data->content_update) &&
854 scene_node_is_below(data->updated_node, node)) {
855 if (data->blur_frozen && cce_scene_blur_debug()) {
856 wlr_log(WLR_INFO, "[scenefx] frozen: content dirty from node type %d (%dx%d) below opt %dx%d",
857 data->updated_node->type, data->update_box.width, data->update_box.height,
858 scene_blur->width, scene_blur->height);
859 }
860 scene_blur->dirty = true;
861 }
862 if (scene_blur->dirty) {
863 // Restore the visible region back to default, without any opaque
864 // regions. This ensures that all nodes below are fully re-rendered
865 // and not culled by above nodes.
866 pixman_region32_clear(data->visible);
867 pixman_region32_copy(data->visible, data->update_region);
868 }
869 }
870
871 struct wlr_box box = { .x = lx, .y = ly };
872 scene_node_get_size(node, &box.width, &box.height);
873
874 pixman_region32_subtract(&node->visible, &node->visible, data->update_region);
875 pixman_region32_union(&node->visible, &node->visible, data->visible);
876 pixman_region32_intersect_rect(&node->visible, &node->visible,
877 lx, ly, box.width, box.height);
878
879 if (data->calculate_visibility) {
880 pixman_region32_t opaque;
881 pixman_region32_init(&opaque);
882 scene_node_opaque_region(node, lx, ly, &opaque);
883 pixman_region32_subtract(data->visible, data->visible, &opaque);
884 pixman_region32_fini(&opaque);
885 }
886
887 update_node_update_outputs(node, data->outputs, NULL, NULL);
888 #if WLR_HAS_XWAYLAND
889 if (data->restack_xwayland_surfaces) {
890 restack_xwayland_surface(node, &box, data);
891 }
892 #endif
893
894 return false;
895 }
896
897 static void scene_node_visibility(struct wlr_scene_node *node,
898 pixman_region32_t *visible) {
899 if (!node->enabled) {
900 return;
901 }
902
903 if (node->type == WLR_SCENE_NODE_TREE) {
904 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
905 struct wlr_scene_node *child;
906 wl_list_for_each(child, &scene_tree->children, link) {
907 scene_node_visibility(child, visible);
908 }
909 return;
910 }
911
912 pixman_region32_union(visible, visible, &node->visible);
913 }
914
915 static void scene_node_bounds(struct wlr_scene_node *node,
916 int x, int y, pixman_region32_t *visible) {
917 if (!node->enabled) {
918 return;
919 }
920
921 if (node->type == WLR_SCENE_NODE_TREE) {
922 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
923 struct wlr_scene_node *child;
924 wl_list_for_each(child, &scene_tree->children, link) {
925 scene_node_bounds(child, x + child->x, y + child->y, visible);
926 }
927 return;
928 }
929
930 int width, height;
931 scene_node_get_size(node, &width, &height);
932 pixman_region32_union_rect(visible, visible, x, y, width, height);
933 }
934
935 static void scene_update_region(struct wlr_scene *scene,
936 struct wlr_scene_node *updated_node,
937 const pixman_region32_t *update_region, bool content_update) {
938 pixman_region32_t visible;
939 pixman_region32_init(&visible);
940 pixman_region32_copy(&visible, update_region);
941
942 struct pixman_box32 *region_box = pixman_region32_extents(update_region);
943 struct scene_update_data data = {
944 .updated_node = updated_node,
945 .visible = &visible,
946 .update_region = update_region,
947 .update_box = {
948 .x = region_box->x1,
949 .y = region_box->y1,
950 .width = region_box->x2 - region_box->x1,
951 .height = region_box->y2 - region_box->y1,
952 },
953 .outputs = &scene->outputs,
954 .calculate_visibility = scene->calculate_visibility,
955 .restack_xwayland_surfaces = scene->restack_xwayland_surfaces,
956 .blur_frozen = scene->blur_frozen,
957 .content_update = content_update,
958 };
959
960 // update node visibility and output enter/leave events
961 scene_nodes_in_box(&scene->tree.node, &data.update_box, scene_node_update_iterator, &data);
962
963 pixman_region32_fini(&visible);
964 }
965
966 static void scene_node_cleanup_when_disabled(struct wlr_scene_node *node,
967 bool xwayland_restack, struct wl_list *outputs) {
968 if (node->type == WLR_SCENE_NODE_TREE) {
969 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
970 struct wlr_scene_node *child;
971 wl_list_for_each(child, &scene_tree->children, link) {
972 if (!child->enabled) {
973 continue;
974 }
975
976 scene_node_cleanup_when_disabled(child, xwayland_restack, outputs);
977 }
978 return;
979 }
980
981 pixman_region32_clear(&node->visible);
982 update_node_update_outputs(node, outputs, NULL, NULL);
983
984 #if WLR_HAS_XWAYLAND
985 if (xwayland_restack) {
986 struct wlr_xwayland_surface *xwayland_surface =
987 scene_node_try_get_managed_xwayland_surface(node);
988 if (!xwayland_surface) {
989 return;
990 }
991
992 wlr_xwayland_surface_restack(xwayland_surface, NULL, XCB_STACK_MODE_BELOW);
993 }
994 #endif
995 }
996
997 /**
998 * Updates the nodes visibility, xwayland restacking, send leave/enter events
999 * and damages the screen. The damage region is used to not only damage the
1000 * screen, but to direct the update logic to only update certain parts of the
1001 * screen and not the whole thing. If a NULL damage is given, the damage is
1002 * assumed to be the previous nodes visibility.
1003 *
1004 * Currently, the only usage for an explicit damage is for update scenarios where
1005 * the scene node might be enabled/disabled. If the scene node is disabled, the
1006 * update logic will ignore the node. This is normally desirable as most update
1007 * scenarios like updating the color or whatever. However, it's not what we want
1008 * when disabling the node. Note that reparenting the node could lead to the node
1009 * being reparented to a disabled super tree.
1010 */
1011 static void scene_node_update(struct wlr_scene_node *node,
1012 pixman_region32_t *damage) {
1013 struct wlr_scene *scene = scene_node_get_root(node);
1014 // Explicit damage means the node's CONTENT changed (a buffer commit, a
1015 // disable); NULL means a property or position change.
1016 bool content_update = damage != NULL;
1017
1018 int x, y;
1019 if (!wlr_scene_node_coords(node, &x, &y)) {
1020 // We assume explicit damage on a disabled tree means the node was just
1021 // disabled.
1022 if (damage) {
1023 scene_node_cleanup_when_disabled(node, scene->restack_xwayland_surfaces, &scene->outputs);
1024
1025 scene_update_region(scene, node, damage, content_update);
1026 scene_damage_outputs(scene, damage);
1027 pixman_region32_fini(damage);
1028 }
1029
1030 return;
1031 }
1032
1033 pixman_region32_t visible;
1034 if (!damage) {
1035 pixman_region32_init(&visible);
1036 scene_node_visibility(node, &visible);
1037 damage = &visible;
1038 }
1039
1040 pixman_region32_t update_region;
1041 pixman_region32_init(&update_region);
1042 pixman_region32_copy(&update_region, damage);
1043 scene_node_bounds(node, x, y, &update_region);
1044
1045 scene_update_region(scene, node, &update_region, content_update);
1046 pixman_region32_fini(&update_region);
1047
1048 scene_node_visibility(node, damage);
1049 scene_damage_outputs(scene, damage);
1050 pixman_region32_fini(damage);
1051 }
1052
1053 struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
1054 int width, int height, const float color[static 4]) {
1055 assert(parent);
1056 assert(width >= 0 && height >= 0);
1057
1058 struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect));
1059 if (scene_rect == NULL) {
1060 return NULL;
1061 }
1062 scene_node_init(&scene_rect->node, WLR_SCENE_NODE_RECT, parent);
1063
1064 scene_rect->width = width;
1065 scene_rect->height = height;
1066 memcpy(scene_rect->color, color, sizeof(scene_rect->color));
1067 scene_rect->corners = (struct fx_corner_radii){0};
1068 scene_rect->accepts_input = true;
1069 scene_rect->clipped_region = clipped_region_get_default();
1070
1071 scene_node_update(&scene_rect->node, NULL);
1072
1073 return scene_rect;
1074 }
1075
1076 void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) {
1077 if (rect->width == width && rect->height == height) {
1078 return;
1079 }
1080
1081 assert(width >= 0 && height >= 0);
1082
1083 scene_node_update(&rect->node, NULL);
1084 rect->width = width;
1085 rect->height = height;
1086 scene_node_update(&rect->node, NULL);
1087 }
1088
1089 void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) {
1090 if (memcmp(rect->color, color, sizeof(rect->color)) == 0) {
1091 return;
1092 }
1093
1094 memcpy(rect->color, color, sizeof(rect->color));
1095 scene_node_update(&rect->node, NULL);
1096 }
1097
1098 void wlr_scene_rect_set_fade_inset(struct wlr_scene_rect *rect, int fade_inset) {
1099 if (rect->fade_inset == fade_inset) {
1100 return;
1101 }
1102
1103 rect->fade_inset = fade_inset;
1104 scene_node_update(&rect->node, NULL);
1105 }
1106
1107 static void scene_buffer_handle_buffer_release(struct wl_listener *listener,
1108 void *data) {
1109 struct wlr_scene_buffer *scene_buffer =
1110 wl_container_of(listener, scene_buffer, buffer_release);
1111
1112 scene_buffer->buffer = NULL;
1113 wl_list_remove(&scene_buffer->buffer_release.link);
1114 wl_list_init(&scene_buffer->buffer_release.link);
1115 }
1116
1117 static void scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
1118 struct wlr_buffer *buffer) {
1119 wl_list_remove(&scene_buffer->buffer_release.link);
1120 wl_list_init(&scene_buffer->buffer_release.link);
1121 if (scene_buffer->own_buffer) {
1122 wlr_buffer_unlock(scene_buffer->buffer);
1123 }
1124 scene_buffer->buffer = NULL;
1125 scene_buffer->own_buffer = false;
1126 scene_buffer->buffer_width = scene_buffer->buffer_height = 0;
1127 scene_buffer->buffer_is_opaque = false;
1128
1129 if (!buffer) {
1130 return;
1131 }
1132
1133 scene_buffer->own_buffer = true;
1134 scene_buffer->buffer = wlr_buffer_lock(buffer);
1135 scene_buffer->buffer_width = buffer->width;
1136 scene_buffer->buffer_height = buffer->height;
1137 scene_buffer->buffer_is_opaque = wlr_buffer_is_opaque(buffer);
1138
1139 scene_buffer->buffer_release.notify = scene_buffer_handle_buffer_release;
1140 wl_signal_add(&buffer->events.release, &scene_buffer->buffer_release);
1141 }
1142
1143 static void scene_buffer_handle_renderer_destroy(struct wl_listener *listener,
1144 void *data) {
1145 struct wlr_scene_buffer *scene_buffer = wl_container_of(listener, scene_buffer, renderer_destroy);
1146 scene_buffer_set_texture(scene_buffer, NULL);
1147 }
1148
1149 static void scene_buffer_set_texture(struct wlr_scene_buffer *scene_buffer,
1150 struct wlr_texture *texture) {
1151 wl_list_remove(&scene_buffer->renderer_destroy.link);
1152 wlr_texture_destroy(scene_buffer->texture);
1153 scene_buffer->texture = texture;
1154
1155 if (texture != NULL) {
1156 scene_buffer->renderer_destroy.notify = scene_buffer_handle_renderer_destroy;
1157 wl_signal_add(&texture->renderer->events.destroy, &scene_buffer->renderer_destroy);
1158 } else {
1159 wl_list_init(&scene_buffer->renderer_destroy.link);
1160 }
1161 }
1162
1163 static void scene_buffer_set_wait_timeline(struct wlr_scene_buffer *scene_buffer,
1164 struct wlr_drm_syncobj_timeline *timeline, uint64_t point) {
1165 wlr_drm_syncobj_timeline_unref(scene_buffer->wait_timeline);
1166 if (timeline != NULL) {
1167 scene_buffer->wait_timeline = wlr_drm_syncobj_timeline_ref(timeline);
1168 scene_buffer->wait_point = point;
1169 } else {
1170 scene_buffer->wait_timeline = NULL;
1171 scene_buffer->wait_point = 0;
1172 }
1173 }
1174
1175 inline void wlr_scene_rect_set_corner_radius(struct wlr_scene_rect *rect, int corner_radius) {
1176 wlr_scene_rect_set_corner_radii(rect, corner_radii_all(corner_radius));
1177 }
1178
1179 void wlr_scene_rect_set_corner_radii(struct wlr_scene_rect *rect, struct fx_corner_radii corners) {
1180 if (fx_corner_radii_eq(rect->corners, corners)) {
1181 return;
1182 }
1183
1184 rect->corners = corners;
1185 scene_node_update(&rect->node, NULL);
1186 }
1187
1188 void wlr_scene_rect_set_clipped_region(struct wlr_scene_rect *rect,
1189 struct clipped_region clipped_region) {
1190 if (fx_corner_radii_eq(rect->clipped_region.corners, clipped_region.corners) &&
1191 wlr_box_equal(&rect->clipped_region.area, &clipped_region.area)) {
1192 return;
1193 }
1194
1195 rect->clipped_region = clipped_region;
1196 scene_node_update(&rect->node, NULL);
1197 }
1198
1199 struct wlr_scene_shadow *wlr_scene_shadow_create(struct wlr_scene_tree *parent,
1200 int width, int height, int corner_radius, float blur_sigma,
1201 const float color [static 4]) {
1202 struct wlr_scene_shadow *scene_shadow = calloc(1, sizeof(*scene_shadow));
1203 if (scene_shadow == NULL) {
1204 return NULL;
1205 }
1206 assert(parent);
1207 scene_node_init(&scene_shadow->node, WLR_SCENE_NODE_SHADOW, parent);
1208
1209 scene_shadow->width = width;
1210 scene_shadow->height = height;
1211 scene_shadow->corner_radius = corner_radius;
1212 scene_shadow->blur_sigma = blur_sigma;
1213 memcpy(scene_shadow->color, color, sizeof(scene_shadow->color));
1214 scene_shadow->clipped_region = clipped_region_get_default();
1215
1216 scene_node_update(&scene_shadow->node, NULL);
1217
1218 return scene_shadow;
1219 }
1220
1221 struct wlr_scene_frame *wlr_scene_frame_create(struct wlr_scene_tree *parent,
1222 int width, int height, int corner_radius, const float color[static 4]) {
1223 struct wlr_scene_frame *scene_frame = calloc(1, sizeof(*scene_frame));
1224 if (scene_frame == NULL) {
1225 return NULL;
1226 }
1227 scene_node_init(&scene_frame->node, WLR_SCENE_NODE_FRAME, parent);
1228
1229 scene_frame->width = width;
1230 scene_frame->height = height;
1231 scene_frame->corner_radius = corner_radius;
1232 // Shape defaults to a plain even ring; the compositor sets the real
1233 // profile every frame through wlr_scene_frame_set_shape.
1234 scene_frame->band = 0.0f;
1235 scene_frame->band_min = 0.0f;
1236 scene_frame->corner_len = 0.0f;
1237 scene_frame->gap = 0.0f;
1238 scene_frame->hovered = -1.0f;
1239 scene_frame->swell_curve = 1.0f;
1240 scene_frame->bulge = 0.0f;
1241 scene_frame->buttons = 0.0f;
1242 memcpy(scene_frame->color, color, sizeof(scene_frame->color));
1243 memcpy(scene_frame->hover_color, color, sizeof(scene_frame->hover_color));
1244
1245 scene_node_update(&scene_frame->node, NULL);
1246
1247 return scene_frame;
1248 }
1249
1250 void wlr_scene_frame_set_size(struct wlr_scene_frame *frame, int width, int height) {
1251 if (frame->width == width && frame->height == height) {
1252 return;
1253 }
1254 frame->width = width;
1255 frame->height = height;
1256 scene_node_update(&frame->node, NULL);
1257 }
1258
1259 void wlr_scene_frame_set_corner_radius(struct wlr_scene_frame *frame, int radius) {
1260 if (frame->corner_radius == radius) {
1261 return;
1262 }
1263 frame->corner_radius = radius;
1264 scene_node_update(&frame->node, NULL);
1265 }
1266
1267 void wlr_scene_frame_set_shape(struct wlr_scene_frame *frame, float band,
1268 float band_min, float corner_len, float gap, float swell_curve, float bulge) {
1269 if (frame->band == band && frame->band_min == band_min
1270 && frame->corner_len == corner_len && frame->gap == gap
1271 && frame->swell_curve == swell_curve && frame->bulge == bulge) {
1272 return;
1273 }
1274 frame->swell_curve = swell_curve;
1275 frame->bulge = bulge;
1276 frame->band = band;
1277 frame->band_min = band_min;
1278 frame->corner_len = corner_len;
1279 frame->gap = gap;
1280 scene_node_update(&frame->node, NULL);
1281 }
1282
1283 void wlr_scene_frame_set_exclusion(struct wlr_scene_frame *frame,
1284 const float rect[static 4]) {
1285 if (memcmp(frame->exclusion, rect, sizeof(frame->exclusion)) == 0) {
1286 return;
1287 }
1288 memcpy(frame->exclusion, rect, sizeof(frame->exclusion));
1289 scene_node_update(&frame->node, NULL);
1290 }
1291
1292 void wlr_scene_frame_set_buttons(struct wlr_scene_frame *frame, float buttons) {
1293 if (frame->buttons == buttons) {
1294 return;
1295 }
1296 frame->buttons = buttons;
1297 scene_node_update(&frame->node, NULL);
1298 }
1299
1300 void wlr_scene_frame_set_color(struct wlr_scene_frame *frame, const float color[static 4]) {
1301 if (memcmp(frame->color, color, sizeof(frame->color)) == 0) {
1302 return;
1303 }
1304 memcpy(frame->color, color, sizeof(frame->color));
1305 scene_node_update(&frame->node, NULL);
1306 }
1307
1308 void wlr_scene_frame_set_hover(struct wlr_scene_frame *frame, float hovered,
1309 const float color[static 4]) {
1310 if (frame->hovered == hovered
1311 && memcmp(frame->hover_color, color, sizeof(frame->hover_color)) == 0) {
1312 return;
1313 }
1314 frame->hovered = hovered;
1315 memcpy(frame->hover_color, color, sizeof(frame->hover_color));
1316 scene_node_update(&frame->node, NULL);
1317 }
1318
1319 struct wlr_scene_bevel *wlr_scene_bevel_create(struct wlr_scene_tree *parent,
1320 int width, int height, int corner_radius, float thickness,
1321 const float color[static 4]) {
1322 struct wlr_scene_bevel *scene_bevel = calloc(1, sizeof(*scene_bevel));
1323 if (scene_bevel == NULL) {
1324 return NULL;
1325 }
1326 assert(parent);
1327 scene_node_init(&scene_bevel->node, WLR_SCENE_NODE_BEVEL, parent);
1328
1329 scene_bevel->width = width;
1330 scene_bevel->height = height;
1331 scene_bevel->corner_radius = corner_radius;
1332 scene_bevel->thickness = thickness;
1333 // Default light: top-left, matching the DE's shadow convention.
1334 scene_bevel->light_dir[0] = -0.7071f;
1335 scene_bevel->light_dir[1] = -0.7071f;
1336 scene_bevel->light_intensity = 1.0f;
1337 scene_bevel->shade_intensity = 1.0f;
1338 scene_bevel->shoulder = 0.5f;
1339 memcpy(scene_bevel->color, color, sizeof(scene_bevel->color));
1340
1341 scene_node_update(&scene_bevel->node, NULL);
1342
1343 return scene_bevel;
1344 }
1345
1346 void wlr_scene_bevel_set_size(struct wlr_scene_bevel *bevel, int width, int height) {
1347 if (bevel->width == width && bevel->height == height) {
1348 return;
1349 }
1350 bevel->width = width;
1351 bevel->height = height;
1352 scene_node_update(&bevel->node, NULL);
1353 }
1354
1355 void wlr_scene_bevel_set_corner_radius(struct wlr_scene_bevel *bevel, int radius) {
1356 if (bevel->corner_radius == radius) {
1357 return;
1358 }
1359 bevel->corner_radius = radius;
1360 scene_node_update(&bevel->node, NULL);
1361 }
1362
1363 void wlr_scene_bevel_set_thickness(struct wlr_scene_bevel *bevel, float thickness) {
1364 if (bevel->thickness == thickness) {
1365 return;
1366 }
1367 bevel->thickness = thickness;
1368 scene_node_update(&bevel->node, NULL);
1369 }
1370
1371 void wlr_scene_bevel_set_light(struct wlr_scene_bevel *bevel, float dir_x, float dir_y,
1372 float light_intensity, float shade_intensity) {
1373 if (bevel->light_dir[0] == dir_x && bevel->light_dir[1] == dir_y
1374 && bevel->light_intensity == light_intensity
1375 && bevel->shade_intensity == shade_intensity) {
1376 return;
1377 }
1378 bevel->light_dir[0] = dir_x;
1379 bevel->light_dir[1] = dir_y;
1380 bevel->light_intensity = light_intensity;
1381 bevel->shade_intensity = shade_intensity;
1382 scene_node_update(&bevel->node, NULL);
1383 }
1384
1385 void wlr_scene_bevel_set_shoulder(struct wlr_scene_bevel *bevel, float shoulder) {
1386 if (bevel->shoulder == shoulder) {
1387 return;
1388 }
1389 bevel->shoulder = shoulder;
1390 scene_node_update(&bevel->node, NULL);
1391 }
1392
1393 struct wlr_scene_droplet *wlr_scene_droplet_create(struct wlr_scene_tree *parent,
1394 int width, int height) {
1395 struct wlr_scene_droplet *droplet = calloc(1, sizeof(*droplet));
1396 if (droplet == NULL) {
1397 return NULL;
1398 }
1399 assert(parent);
1400 scene_node_init(&droplet->node, WLR_SCENE_NODE_DROPLET, parent);
1401
1402 droplet->width = width;
1403 droplet->height = height;
1404 droplet->curve = 2.0f;
1405
1406 scene_node_update(&droplet->node, NULL);
1407
1408 return droplet;
1409 }
1410
1411 void wlr_scene_droplet_set_size(struct wlr_scene_droplet *droplet, int width, int height) {
1412 if (droplet->width == width && droplet->height == height) {
1413 return;
1414 }
1415 droplet->width = width;
1416 droplet->height = height;
1417 scene_node_update(&droplet->node, NULL);
1418 }
1419
1420 void wlr_scene_droplet_set_silhouette(struct wlr_scene_droplet *droplet,
1421 float attach_r, float sheet_r, float bow_rise, float blend_k, float curve) {
1422 if (droplet->attach_r == attach_r && droplet->sheet_r == sheet_r
1423 && droplet->bow_rise == bow_rise && droplet->blend_k == blend_k
1424 && droplet->curve == curve) {
1425 return;
1426 }
1427 droplet->attach_r = attach_r;
1428 droplet->sheet_r = sheet_r;
1429 droplet->bow_rise = bow_rise;
1430 droplet->blend_k = blend_k;
1431 droplet->curve = curve;
1432 scene_node_update(&droplet->node, NULL);
1433 }
1434
1435 void wlr_scene_droplet_set_lens(struct wlr_scene_droplet *droplet,
1436 float band_px, float refr, float ghost) {
1437 if (droplet->band_px == band_px && droplet->refr == refr
1438 && droplet->ghost == ghost) {
1439 return;
1440 }
1441 droplet->band_px = band_px;
1442 droplet->refr = refr;
1443 droplet->ghost = ghost;
1444 scene_node_update(&droplet->node, NULL);
1445 }
1446
1447 void wlr_scene_droplet_set_compress(struct wlr_scene_droplet *droplet,
1448 float ceil, float knee, bool invert) {
1449 if (droplet->compress_ceil == ceil && droplet->compress_knee == knee
1450 && droplet->compress_invert == invert) {
1451 return;
1452 }
1453 droplet->compress_ceil = ceil;
1454 droplet->compress_knee = knee;
1455 droplet->compress_invert = invert;
1456 scene_node_update(&droplet->node, NULL);
1457 }
1458
1459 void wlr_scene_bevel_set_color(struct wlr_scene_bevel *bevel, const float color[static 4]) {
1460 if (memcmp(bevel->color, color, sizeof(bevel->color)) == 0) {
1461 return;
1462 }
1463 memcpy(bevel->color, color, sizeof(bevel->color));
1464 scene_node_update(&bevel->node, NULL);
1465 }
1466
1467 void wlr_scene_bevel_set_focus(struct wlr_scene_bevel *bevel, float focus, float sharpness,
1468 const float color[static 3]) {
1469 if (bevel->focus == focus && bevel->focus_sharpness == sharpness &&
1470 memcmp(bevel->focus_color, color, sizeof(bevel->focus_color)) == 0) {
1471 return;
1472 }
1473 bevel->focus = focus;
1474 bevel->focus_sharpness = sharpness;
1475 memcpy(bevel->focus_color, color, sizeof(bevel->focus_color));
1476 scene_node_update(&bevel->node, NULL);
1477 }
1478
1479 void wlr_scene_shadow_set_size(struct wlr_scene_shadow *shadow, int width, int height) {
1480 if (shadow->width == width && shadow->height == height) {
1481 return;
1482 }
1483
1484 shadow->width = width;
1485 shadow->height = height;
1486 scene_node_update(&shadow->node, NULL);
1487 }
1488
1489 void wlr_scene_shadow_set_corner_radius(struct wlr_scene_shadow *shadow, int corner_radius) {
1490 if (shadow->corner_radius == corner_radius) {
1491 return;
1492 }
1493
1494 shadow->corner_radius = corner_radius;
1495 scene_node_update(&shadow->node, NULL);
1496 }
1497
1498 void wlr_scene_shadow_set_blur_sigma(struct wlr_scene_shadow *shadow, float blur_sigma) {
1499 if (shadow->blur_sigma == blur_sigma) {
1500 return;
1501 }
1502
1503 shadow->blur_sigma = blur_sigma;
1504 scene_node_update(&shadow->node, NULL);
1505 }
1506
1507 void wlr_scene_shadow_set_color(struct wlr_scene_shadow *shadow, const float color[static 4]) {
1508 if (memcmp(shadow->color, color, sizeof(shadow->color)) == 0) {
1509 return;
1510 }
1511
1512 memcpy(shadow->color, color, sizeof(shadow->color));
1513 scene_node_update(&shadow->node, NULL);
1514 }
1515
1516 void wlr_scene_shadow_set_clipped_region(struct wlr_scene_shadow *shadow,
1517 struct clipped_region clipped_region) {
1518 if (fx_corner_radii_eq(shadow->clipped_region.corners, clipped_region.corners) &&
1519 wlr_box_equal(&shadow->clipped_region.area, &clipped_region.area)) {
1520 return;
1521 }
1522
1523 shadow->clipped_region = clipped_region;
1524 scene_node_update(&shadow->node, NULL);
1525 }
1526
1527 static void mark_all_optimized_blur_nodes_dirty(struct wlr_scene_node *node) {
1528 if (node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
1529 struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
1530 wlr_scene_optimized_blur_mark_dirty(scene_blur);
1531 } else if (node->type == WLR_SCENE_NODE_TREE) {
1532 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
1533 struct wlr_scene_node *child;
1534 wl_list_for_each(child, &scene_tree->children, link) {
1535 mark_all_optimized_blur_nodes_dirty(child);
1536 }
1537 }
1538 }
1539
1540 struct wlr_scene_blur *wlr_scene_blur_create(struct wlr_scene_tree *parent,
1541 int width, int height) {
1542 struct wlr_scene_blur *blur = calloc(1, sizeof(*blur));
1543 if (blur == NULL) {
1544 return NULL;
1545 }
1546 assert(parent);
1547 scene_node_init(&blur->node, WLR_SCENE_NODE_BLUR, parent);
1548
1549 blur->alpha = 1.0f;
1550 blur->strength = 1.0f;
1551 blur->clipped_region = (struct clipped_region){0};
1552 blur->corners = corner_radii_all(0);
1553 blur->should_only_blur_bottom_layer = false;
1554 blur->transparency_mask_source = linked_node_init();
1555 blur->width = width;
1556 blur->height = height;
1557
1558 scene_node_update(&blur->node, NULL);
1559
1560 return blur;
1561 }
1562
1563 void wlr_scene_blur_set_size(struct wlr_scene_blur *blur, int width, int height) {
1564 if (blur->width == width && blur->height == height) {
1565 return;
1566 }
1567
1568 blur->width = width;
1569 blur->height = height;
1570
1571 scene_node_update(&blur->node, NULL);
1572 }
1573
1574 inline void wlr_scene_blur_set_corner_radius(struct wlr_scene_blur *blur, int corner_radius) {
1575 wlr_scene_blur_set_corner_radii(blur, corner_radii_all(corner_radius));
1576 }
1577
1578 void wlr_scene_blur_set_corner_radii(struct wlr_scene_blur *blur, struct fx_corner_radii corners) {
1579 if (fx_corner_radii_eq(blur->corners, corners)) {
1580 return;
1581 }
1582
1583 blur->corners = corners;
1584 scene_node_update(&blur->node, NULL);
1585 }
1586
1587 void wlr_scene_blur_set_should_only_blur_bottom_layer(struct wlr_scene_blur *blur,
1588 bool should_only_blur_bottom_layer) {
1589 if (blur->should_only_blur_bottom_layer == should_only_blur_bottom_layer) {
1590 return;
1591 }
1592
1593 blur->should_only_blur_bottom_layer = should_only_blur_bottom_layer;
1594 scene_node_update(&blur->node, NULL);
1595 }
1596
1597 void wlr_scene_blur_set_transparency_mask_source(struct wlr_scene_blur *blur,
1598 struct wlr_scene_buffer *source) {
1599 if (source == NULL && blur->transparency_mask_source.link == NULL) {
1600 return;
1601 }
1602
1603 if (source != NULL && linked_nodes_are_linked(&blur->transparency_mask_source, &source->blur)) {
1604 return;
1605 }
1606
1607 linked_node_destroy(&blur->transparency_mask_source);
1608
1609 if (source != NULL) {
1610 linked_node_destroy(&source->blur);
1611 linked_node_init_link(&blur->transparency_mask_source, &source->blur);
1612 }
1613
1614 scene_node_update(&blur->node, NULL);
1615 }
1616
1617 struct wlr_scene_buffer *wlr_scene_blur_get_transparency_mask_source(
1618 struct wlr_scene_blur *blur) {
1619 struct linked_node *node = linked_nodes_get_sibling(&blur->transparency_mask_source);
1620 if (node == NULL) {
1621 return NULL;
1622 }
1623
1624 struct wlr_scene_buffer *output = wl_container_of(node, output, blur);
1625 return output;
1626 }
1627
1628 void wlr_scene_blur_set_alpha(struct wlr_scene_blur *blur, float alpha) {
1629 if (blur->alpha == alpha) {
1630 return;
1631 }
1632
1633 blur->alpha = alpha;
1634 scene_node_update(&blur->node, NULL);
1635 }
1636
1637 void wlr_scene_blur_set_strength(struct wlr_scene_blur *blur, float strength) {
1638 if (blur->strength == strength) {
1639 return;
1640 }
1641
1642 blur->strength = strength;
1643 scene_node_update(&blur->node, NULL);
1644 }
1645
1646 void wlr_scene_blur_set_compress(struct wlr_scene_blur *blur,
1647 float ceil, float knee, bool invert) {
1648 if (blur->compress_ceil == ceil && blur->compress_knee == knee
1649 && blur->compress_invert == invert) {
1650 return;
1651 }
1652 blur->compress_ceil = ceil;
1653 blur->compress_knee = knee;
1654 blur->compress_invert = invert;
1655 scene_node_update(&blur->node, NULL);
1656 }
1657
1658 void wlr_scene_blur_set_clipped_region(struct wlr_scene_blur *blur,
1659 struct clipped_region clipped_region) {
1660 if (fx_corner_radii_eq(blur->clipped_region.corners, clipped_region.corners) &&
1661 wlr_box_equal(&blur->clipped_region.area, &clipped_region.area)) {
1662 return;
1663 }
1664
1665 blur->clipped_region = clipped_region;
1666 scene_node_update(&blur->node, NULL);
1667 }
1668
1669 void wlr_scene_set_blur_data(struct wlr_scene *scene, int num_passes,
1670 int radius, float noise, float brightness, float contrast, float saturation) {
1671 struct blur_data *buff_data = &scene->blur_data;
1672 if (buff_data->num_passes == num_passes
1673 && buff_data->radius == radius
1674 && buff_data->noise == noise
1675 && buff_data->brightness == brightness
1676 && buff_data->contrast == contrast
1677 && buff_data->saturation == saturation) {
1678 return;
1679 }
1680
1681 buff_data->num_passes = num_passes;
1682 buff_data->radius = radius;
1683 buff_data->noise = noise;
1684 buff_data->brightness = brightness;
1685 buff_data->contrast = contrast;
1686 buff_data->saturation = saturation;
1687
1688 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1689 scene_node_update(&scene->tree.node, NULL);
1690 }
1691
1692 void wlr_scene_set_blur_num_passes(struct wlr_scene *scene, int num_passes) {
1693 struct blur_data *buff_data = &scene->blur_data;
1694 if (buff_data->num_passes == num_passes) {
1695 return;
1696 }
1697 buff_data->num_passes = num_passes;
1698 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1699 scene_node_update(&scene->tree.node, NULL);
1700 }
1701
1702 void wlr_scene_set_blur_radius(struct wlr_scene *scene, int radius) {
1703 struct blur_data *buff_data = &scene->blur_data;
1704 if (buff_data->radius == radius) {
1705 return;
1706 }
1707 buff_data->radius = radius;
1708 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1709 scene_node_update(&scene->tree.node, NULL);
1710 }
1711
1712 void wlr_scene_set_blur_noise(struct wlr_scene *scene, float noise) {
1713 struct blur_data *buff_data = &scene->blur_data;
1714 if (buff_data->noise == noise) {
1715 return;
1716 }
1717 buff_data->noise = noise;
1718 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1719 scene_node_update(&scene->tree.node, NULL);
1720 }
1721
1722 void wlr_scene_set_blur_brightness(struct wlr_scene *scene, float brightness) {
1723 struct blur_data *buff_data = &scene->blur_data;
1724 if (buff_data->brightness == brightness) {
1725 return;
1726 }
1727 buff_data->brightness = brightness;
1728 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1729 scene_node_update(&scene->tree.node, NULL);
1730 }
1731
1732 void wlr_scene_set_blur_contrast(struct wlr_scene *scene, float contrast) {
1733 struct blur_data *buff_data = &scene->blur_data;
1734 if (buff_data->contrast == contrast) {
1735 return;
1736 }
1737 buff_data->contrast = contrast;
1738 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1739 scene_node_update(&scene->tree.node, NULL);
1740 }
1741
1742 void wlr_scene_set_blur_saturation(struct wlr_scene *scene, float saturation) {
1743 struct blur_data *buff_data = &scene->blur_data;
1744 if (buff_data->saturation == saturation) {
1745 return;
1746 }
1747 buff_data->saturation = saturation;
1748 mark_all_optimized_blur_nodes_dirty(&scene->tree.node);
1749 scene_node_update(&scene->tree.node, NULL);
1750 }
1751
1752 struct wlr_scene_optimized_blur *wlr_scene_optimized_blur_create(
1753 struct wlr_scene_tree *parent, int width, int height) {
1754 struct wlr_scene_optimized_blur *scene_blur = calloc(1, sizeof(*scene_blur));
1755 if (scene_blur == NULL) {
1756 return NULL;
1757 }
1758 assert(parent);
1759 scene_node_init(&scene_blur->node, WLR_SCENE_NODE_OPTIMIZED_BLUR, parent);
1760
1761 scene_blur->width = width;
1762 scene_blur->height = height;
1763 pixman_region32_init(&scene_blur->baked_region);
1764 pixman_region32_init(&scene_blur->edge_region);
1765 // Start dirty so the first render pass bakes the cache; scene_entry_render
1766 // only re-bakes when dirty (re-baking on undamaged frames samples stale
1767 // pass->buffer content, ghosting whatever was composited above the node).
1768 scene_blur->dirty = true;
1769
1770 scene_node_update(&scene_blur->node, NULL);
1771
1772 return scene_blur;
1773 }
1774
1775 void wlr_scene_optimized_blur_set_size(struct wlr_scene_optimized_blur *blur_node,
1776 int width, int height) {
1777 assert(blur_node);
1778 if (blur_node->width == width && blur_node->height == height) {
1779 return;
1780 }
1781
1782 blur_node->width = width;
1783 blur_node->height = height;
1784
1785 // While the scene is frozen (a camera gesture in flight, see
1786 // wlr_scene.blur_frozen) a resize keeps its bake: the anchored render
1787 // path bakes only the strips a grown node newly exposes, and a shrunk
1788 // one needs nothing. Re-baking here made every frame of a zoom re-bake
1789 // every blur, which the freeze exists to avoid; the thaw re-bakes once.
1790 struct wlr_scene *scene = scene_node_get_root(&blur_node->node);
1791 if (scene != NULL && scene->blur_frozen && blur_node->baked) {
1792 scene_node_update(&blur_node->node, NULL);
1793 return;
1794 }
1795
1796 wlr_scene_optimized_blur_mark_dirty(blur_node);
1797 }
1798
1799 void wlr_scene_optimized_blur_mark_dirty(struct wlr_scene_optimized_blur *blur_node) {
1800 // Skip re-rendering the optimized blur if the blur node is disabled
1801 if (blur_node && !blur_node->node.enabled) {
1802 return;
1803 }
1804
1805 blur_node->dirty = true;
1806
1807 scene_node_update(&blur_node->node, NULL);
1808 }
1809
1810 struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
1811 struct wlr_buffer *buffer) {
1812 struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer));
1813 if (scene_buffer == NULL) {
1814 return NULL;
1815 }
1816 assert(parent);
1817 scene_node_init(&scene_buffer->node, WLR_SCENE_NODE_BUFFER, parent);
1818
1819 wl_signal_init(&scene_buffer->events.outputs_update);
1820 wl_signal_init(&scene_buffer->events.output_enter);
1821 wl_signal_init(&scene_buffer->events.output_leave);
1822 wl_signal_init(&scene_buffer->events.output_sample);
1823 wl_signal_init(&scene_buffer->events.frame_done);
1824
1825 pixman_region32_init(&scene_buffer->opaque_region);
1826 wl_list_init(&scene_buffer->buffer_release.link);
1827 wl_list_init(&scene_buffer->renderer_destroy.link);
1828 scene_buffer->opacity = 1;
1829
1830 scene_buffer->corners = corner_radii_none();
1831
1832 scene_buffer->blur = linked_node_init();
1833
1834 scene_buffer_set_buffer(scene_buffer, buffer);
1835 scene_node_update(&scene_buffer->node, NULL);
1836
1837 return scene_buffer;
1838 }
1839
1840 void wlr_scene_buffer_set_buffer_with_options(struct wlr_scene_buffer *scene_buffer,
1841 struct wlr_buffer *buffer, const struct wlr_scene_buffer_set_buffer_options *options) {
1842 const struct wlr_scene_buffer_set_buffer_options default_options = {0};
1843 if (options == NULL) {
1844 options = &default_options;
1845 }
1846
1847 // specifying a region for a NULL buffer doesn't make sense. We need to know
1848 // about the buffer to scale the buffer local coordinates down to scene
1849 // coordinates.
1850 assert(buffer || !options->damage);
1851
1852 bool mapped = buffer != NULL;
1853 bool prev_mapped = scene_buffer->buffer != NULL || scene_buffer->texture != NULL;
1854
1855 if (!mapped && !prev_mapped) {
1856 // unmapping already unmapped buffer - noop
1857 return;
1858 }
1859
1860 // if this node used to not be mapped or its previous displayed
1861 // buffer region will be different from what the new buffer would
1862 // produce we need to update the node.
1863 bool update = mapped != prev_mapped;
1864 if (buffer != NULL && scene_buffer->dst_width == 0 && scene_buffer->dst_height == 0) {
1865 update = update || scene_buffer->buffer_width != buffer->width ||
1866 scene_buffer->buffer_height != buffer->height;
1867 }
1868
1869 // If this is a buffer change, check if it's a single pixel buffer.
1870 // Cache that so we can still apply rendering optimisations even when
1871 // the original buffer has been freed after texture upload.
1872 if (buffer != scene_buffer->buffer) {
1873 scene_buffer->is_single_pixel_buffer = false;
1874 struct wlr_client_buffer *client_buffer = NULL;
1875 if (buffer != NULL) {
1876 client_buffer = wlr_client_buffer_get(buffer);
1877 }
1878 if (client_buffer != NULL && client_buffer->source != NULL) {
1879 struct wlr_single_pixel_buffer_v1 *single_pixel_buffer =
1880 wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source);
1881 if (single_pixel_buffer != NULL) {
1882 scene_buffer->is_single_pixel_buffer = true;
1883 scene_buffer->single_pixel_buffer_color[0] = single_pixel_buffer->r;
1884 scene_buffer->single_pixel_buffer_color[1] = single_pixel_buffer->g;
1885 scene_buffer->single_pixel_buffer_color[2] = single_pixel_buffer->b;
1886 scene_buffer->single_pixel_buffer_color[3] = single_pixel_buffer->a;
1887 }
1888 }
1889 }
1890
1891 scene_buffer_set_buffer(scene_buffer, buffer);
1892 scene_buffer_set_texture(scene_buffer, NULL);
1893 scene_buffer_set_wait_timeline(scene_buffer,
1894 options->wait_timeline, options->wait_point);
1895
1896 if (update) {
1897 scene_node_update(&scene_buffer->node, NULL);
1898 // updating the node will already damage the whole node for us. Return
1899 // early to not damage again
1900 return;
1901 }
1902
1903 int lx, ly;
1904 if (!wlr_scene_node_coords(&scene_buffer->node, &lx, &ly)) {
1905 return;
1906 }
1907
1908 pixman_region32_t fallback_damage;
1909 pixman_region32_init_rect(&fallback_damage, 0, 0, buffer->width, buffer->height);
1910 const pixman_region32_t *damage = options->damage;
1911 if (!damage) {
1912 damage = &fallback_damage;
1913 }
1914
1915 struct wlr_fbox box = scene_buffer->src_box;
1916 if (wlr_fbox_empty(&box)) {
1917 box.x = 0;
1918 box.y = 0;
1919 box.width = buffer->width;
1920 box.height = buffer->height;
1921 }
1922
1923 wlr_fbox_transform(&box, &box, scene_buffer->transform,
1924 buffer->width, buffer->height);
1925
1926 float scale_x, scale_y;
1927 if (scene_buffer->dst_width || scene_buffer->dst_height) {
1928 scale_x = scene_buffer->dst_width / box.width;
1929 scale_y = scene_buffer->dst_height / box.height;
1930 } else {
1931 scale_x = buffer->width / box.width;
1932 scale_y = buffer->height / box.height;
1933 }
1934
1935 pixman_region32_t trans_damage;
1936 pixman_region32_init(&trans_damage);
1937 wlr_region_transform(&trans_damage, damage,
1938 scene_buffer->transform, buffer->width, buffer->height);
1939 pixman_region32_intersect_rect(&trans_damage, &trans_damage,
1940 box.x, box.y, box.width, box.height);
1941 pixman_region32_translate(&trans_damage, -box.x, -box.y);
1942
1943 struct wlr_scene *scene = scene_node_get_root(&scene_buffer->node);
1944 struct wlr_scene_output *scene_output;
1945 wl_list_for_each(scene_output, &scene->outputs, link) {
1946 float output_scale = scene_output->output->scale;
1947 float output_scale_x = output_scale * scale_x;
1948 float output_scale_y = output_scale * scale_y;
1949 pixman_region32_t output_damage;
1950 pixman_region32_init(&output_damage);
1951 wlr_region_scale_xy(&output_damage, &trans_damage,
1952 output_scale_x, output_scale_y);
1953
1954 // One output pixel will match (buffer_scale_x)x(buffer_scale_y) buffer pixels.
1955 // If the buffer is upscaled on the given axis (output_scale_* > 1.0,
1956 // buffer_scale_* < 1.0), its contents will bleed into adjacent
1957 // (ceil(output_scale_* / 2)) output pixels because of linear filtering.
1958 // Additionally, if the buffer is downscaled (output_scale_* < 1.0,
1959 // buffer_scale_* > 1.0), and one output pixel matches a non-integer number of
1960 // buffer pixels, its contents will bleed into neighboring output pixels.
1961 // Handle both cases by computing buffer_scale_{x,y} and checking if they are
1962 // integer numbers; ceilf() is used to ensure that the distance is at least 1.
1963 float buffer_scale_x = 1.0f / output_scale_x;
1964 float buffer_scale_y = 1.0f / output_scale_y;
1965 int dist_x = floor(buffer_scale_x) != buffer_scale_x ?
1966 (int)ceilf(output_scale_x / 2.0f) : 0;
1967 int dist_y = floor(buffer_scale_y) != buffer_scale_y ?
1968 (int)ceilf(output_scale_y / 2.0f) : 0;
1969 // TODO: expand with per-axis distances
1970 wlr_region_expand(&output_damage, &output_damage,
1971 dist_x >= dist_y ? dist_x : dist_y);
1972
1973 pixman_region32_t cull_region;
1974 pixman_region32_init(&cull_region);
1975 pixman_region32_copy(&cull_region, &scene_buffer->node.visible);
1976 scale_region(&cull_region, output_scale, true);
1977 pixman_region32_translate(&cull_region, -lx * output_scale, -ly * output_scale);
1978 pixman_region32_intersect(&output_damage, &output_damage, &cull_region);
1979 pixman_region32_fini(&cull_region);
1980
1981 pixman_region32_translate(&output_damage,
1982 (int)round((lx - scene_output->x) * output_scale),
1983 (int)round((ly - scene_output->y) * output_scale));
1984 output_to_buffer_coords(&output_damage, scene_output->output);
1985 scene_output_damage(scene_output, &output_damage);
1986 pixman_region32_fini(&output_damage);
1987 }
1988
1989 pixman_region32_fini(&trans_damage);
1990 pixman_region32_fini(&fallback_damage);
1991 }
1992
1993 void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer,
1994 struct wlr_buffer *buffer, const pixman_region32_t *damage) {
1995 const struct wlr_scene_buffer_set_buffer_options options = {
1996 .damage = damage,
1997 };
1998 wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, &options);
1999 }
2000
2001 void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer,
2002 struct wlr_buffer *buffer) {
2003 wlr_scene_buffer_set_buffer_with_options(scene_buffer, buffer, NULL);
2004 }
2005
2006 void wlr_scene_buffer_set_geometry_pinned(struct wlr_scene_buffer *scene_buffer,
2007 bool pinned) {
2008 scene_buffer->geometry_pinned = pinned;
2009 }
2010
2011 void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer,
2012 const pixman_region32_t *region) {
2013 if (scene_buffer->geometry_pinned) {
2014 return;
2015 }
2016 wlr_scene_buffer_set_opaque_region_pinned(scene_buffer, region);
2017 }
2018
2019 void wlr_scene_buffer_set_opaque_region_pinned(struct wlr_scene_buffer *scene_buffer,
2020 const pixman_region32_t *region) {
2021 if (pixman_region32_equal(&scene_buffer->opaque_region, region)) {
2022 return;
2023 }
2024
2025 pixman_region32_copy(&scene_buffer->opaque_region, region);
2026
2027 int x, y;
2028 if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) {
2029 return;
2030 }
2031
2032 pixman_region32_t update_region;
2033 pixman_region32_init(&update_region);
2034 scene_node_bounds(&scene_buffer->node, x, y, &update_region);
2035 scene_update_region(scene_node_get_root(&scene_buffer->node), &scene_buffer->node, &update_region, true);
2036 pixman_region32_fini(&update_region);
2037 }
2038
2039 void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer,
2040 const struct wlr_fbox *box) {
2041 if (wlr_fbox_equal(&scene_buffer->src_box, box)) {
2042 return;
2043 }
2044
2045 if (box != NULL) {
2046 assert(box->x >= 0 && box->y >= 0 && box->width >= 0 && box->height >= 0);
2047 scene_buffer->src_box = *box;
2048 } else {
2049 scene_buffer->src_box = (struct wlr_fbox){0};
2050 }
2051
2052 scene_node_update(&scene_buffer->node, NULL);
2053 }
2054
2055 void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer,
2056 int width, int height) {
2057 if (scene_buffer->geometry_pinned) {
2058 return;
2059 }
2060 wlr_scene_buffer_set_dest_size_pinned(scene_buffer, width, height);
2061 }
2062
2063 void wlr_scene_buffer_set_dest_size_pinned(struct wlr_scene_buffer *scene_buffer,
2064 int width, int height) {
2065 if (scene_buffer->dst_width == width && scene_buffer->dst_height == height) {
2066 return;
2067 }
2068
2069 assert(width >= 0 && height >= 0);
2070 scene_node_update(&scene_buffer->node, NULL);
2071 scene_buffer->dst_width = width;
2072 scene_buffer->dst_height = height;
2073 scene_node_update(&scene_buffer->node, NULL);
2074 }
2075
2076 void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer,
2077 enum wl_output_transform transform) {
2078 if (scene_buffer->transform == transform) {
2079 return;
2080 }
2081
2082 scene_node_update(&scene_buffer->node, NULL);
2083 scene_buffer->transform = transform;
2084 scene_node_update(&scene_buffer->node, NULL);
2085 }
2086
2087 // Frame-callback delivery tracer. A demand-driven client cannot repaint while
2088 // it waits on a frame callback, so a surface silently skipped by the visible
2089 // gate below stalls until the next output frame that does reach it. Set
2090 // CCE_FRAME_DEBUG=1 to trace every buffer, or CCE_FRAME_DEBUG=WxH (the dst
2091 // size, e.g. 504x1032) to trace one window — the filtered form is what to use
2092 // while measuring, since tracing every buffer adds writes to the very thread
2093 // whose latency is under test.
2094 static bool cce_frame_debug_match(int dst_width, int dst_height) {
2095 static bool initialized = false;
2096 static bool enabled = false;
2097 static int want_w = -1, want_h = -1;
2098 if (!initialized) {
2099 const char *value = getenv("CCE_FRAME_DEBUG");
2100 if (value && *value) {
2101 enabled = true;
2102 // "WxH" matches one size exactly; a bare "W" matches on width only
2103 // (a window's height can change under it); anything else traces all.
2104 int parsed = sscanf(value, "%dx%d", &want_w, &want_h);
2105 if (parsed == 1) {
2106 want_h = -1;
2107 } else if (parsed != 2) {
2108 want_w = want_h = -1;
2109 }
2110 }
2111 initialized = true;
2112 }
2113 if (!enabled) {
2114 return false;
2115 }
2116 if (want_w < 0) {
2117 return true;
2118 }
2119 return dst_width == want_w && (want_h < 0 || dst_height == want_h);
2120 }
2121
2122 // Same time base as cce-ui's CCE_PRESENT_DEBUG and cce-system-interface's
2123 // CCE_HOVER_DEBUG (epoch ms mod 100000) so the three logs interleave directly.
2124 static int64_t cce_now_ms(void) {
2125 struct timespec ts;
2126 clock_gettime(CLOCK_REALTIME, &ts);
2127 return ((int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000) % 100000;
2128 }
2129
2130 void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
2131 struct wlr_scene_frame_done_event *event) {
2132 bool visible = !pixman_region32_empty(&scene_buffer->node.visible);
2133 if (cce_frame_debug_match(scene_buffer->dst_width, scene_buffer->dst_height)) {
2134 pixman_box32_t *extents = pixman_region32_extents(&scene_buffer->node.visible);
2135 // listeners: is anything wired to turn this signal into a client callback?
2136 // (wlr_scene_surface registers one; zero means the emit goes nowhere.)
2137 // committed_cb / pending_cb: does the surface actually hold a frame callback
2138 // for wlr_surface_send_frame_done to fire? A request stuck in `pending` means
2139 // the client asked but the compositor has not applied that commit yet — which
2140 // separates "compositor drops it" from "client's request never landed".
2141 int listeners = wl_list_length(&scene_buffer->events.frame_done.listener_list);
2142 int committed_cb = -1, pending_cb = -1;
2143 struct wlr_scene_surface *scene_surface =
2144 wlr_scene_surface_try_from_buffer(scene_buffer);
2145 if (scene_surface != NULL && scene_surface->surface != NULL) {
2146 committed_cb = wl_list_length(&scene_surface->surface->current.frame_callback_list);
2147 pending_cb = wl_list_length(&scene_surface->surface->pending.frame_callback_list);
2148 }
2149 wlr_log(WLR_INFO, "[cce-frame] t=%lld send_frame_done dst=%dx%d sent=%d listeners=%d committed_cb=%d pending_cb=%d visible_extents=(%d,%d %dx%d)",
2150 (long long)cce_now_ms(), scene_buffer->dst_width, scene_buffer->dst_height,
2151 visible, listeners, committed_cb, pending_cb,
2152 extents->x1, extents->y1,
2153 extents->x2 - extents->x1, extents->y2 - extents->y1);
2154 }
2155 if (visible) {
2156 wl_signal_emit_mutable(&scene_buffer->events.frame_done, event);
2157 }
2158 }
2159
2160 void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer,
2161 float opacity) {
2162 if (scene_buffer->opacity == opacity) {
2163 return;
2164 }
2165
2166 assert(opacity >= 0 && opacity <= 1);
2167 scene_buffer->opacity = opacity;
2168 scene_node_update(&scene_buffer->node, NULL);
2169 }
2170
2171 void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer,
2172 enum wlr_scale_filter_mode filter_mode) {
2173 if (scene_buffer->filter_mode == filter_mode) {
2174 return;
2175 }
2176
2177 scene_buffer->filter_mode = filter_mode;
2178 scene_node_update(&scene_buffer->node, NULL);
2179 }
2180
2181 void wlr_scene_buffer_set_transfer_function(struct wlr_scene_buffer *scene_buffer,
2182 enum wlr_color_transfer_function transfer_function) {
2183 if (scene_buffer->transfer_function == transfer_function) {
2184 return;
2185 }
2186
2187 scene_buffer->transfer_function = transfer_function;
2188 scene_node_update(&scene_buffer->node, NULL);
2189 }
2190
2191 void wlr_scene_buffer_set_primaries(struct wlr_scene_buffer *scene_buffer,
2192 enum wlr_color_named_primaries primaries) {
2193 if (scene_buffer->primaries == primaries) {
2194 return;
2195 }
2196
2197 scene_buffer->primaries = primaries;
2198 scene_node_update(&scene_buffer->node, NULL);
2199 }
2200
2201 void wlr_scene_buffer_set_color_encoding(struct wlr_scene_buffer *scene_buffer,
2202 enum wlr_color_encoding color_encoding) {
2203 if (scene_buffer->color_encoding == color_encoding) {
2204 return;
2205 }
2206
2207 scene_buffer->color_encoding = color_encoding;
2208 scene_node_update(&scene_buffer->node, NULL);
2209 }
2210
2211 void wlr_scene_buffer_set_color_range(struct wlr_scene_buffer *scene_buffer,
2212 enum wlr_color_range color_range) {
2213 if (scene_buffer->color_range == color_range) {
2214 return;
2215 }
2216
2217 scene_buffer->color_range = color_range;
2218 scene_node_update(&scene_buffer->node, NULL);
2219 }
2220
2221 inline void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer,
2222 int radii) {
2223 wlr_scene_buffer_set_corner_radii(scene_buffer, corner_radii_all(radii));
2224 }
2225
2226 void wlr_scene_buffer_set_corner_radii(struct wlr_scene_buffer *scene_buffer,
2227 struct fx_corner_radii corner_radii) {
2228 if (fx_corner_radii_eq(scene_buffer->corners, corner_radii)) {
2229 return;
2230 }
2231
2232 scene_buffer->corners = corner_radii;
2233 scene_node_update(&scene_buffer->node, NULL);
2234 }
2235
2236 static struct wlr_texture *scene_buffer_get_texture(
2237 struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) {
2238 if (scene_buffer->buffer == NULL || scene_buffer->texture != NULL) {
2239 return scene_buffer->texture;
2240 }
2241
2242 struct wlr_client_buffer *client_buffer =
2243 wlr_client_buffer_get(scene_buffer->buffer);
2244 if (client_buffer != NULL) {
2245 return client_buffer->texture;
2246 }
2247
2248 struct wlr_texture *texture =
2249 wlr_texture_from_buffer(renderer, scene_buffer->buffer);
2250 if (texture != NULL && scene_buffer->own_buffer) {
2251 scene_buffer->own_buffer = false;
2252 wlr_buffer_unlock(scene_buffer->buffer);
2253 }
2254 scene_buffer_set_texture(scene_buffer, texture);
2255 return texture;
2256 }
2257
2258 void scene_node_get_size(struct wlr_scene_node *node, int *width, int *height) {
2259 *width = 0;
2260 *height = 0;
2261
2262 switch (node->type) {
2263 case WLR_SCENE_NODE_TREE:
2264 return;
2265 case WLR_SCENE_NODE_RECT:;
2266 struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
2267 *width = scene_rect->width;
2268 *height = scene_rect->height;
2269 break;
2270 case WLR_SCENE_NODE_BUFFER:;
2271 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
2272 if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) {
2273 *width = scene_buffer->dst_width;
2274 *height = scene_buffer->dst_height;
2275 } else {
2276 *width = scene_buffer->buffer_width;
2277 *height = scene_buffer->buffer_height;
2278 wlr_output_transform_coords(scene_buffer->transform, width, height);
2279 }
2280 break;
2281 case WLR_SCENE_NODE_SHADOW:;
2282 struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
2283 *width = scene_shadow->width;
2284 *height = scene_shadow->height;
2285 break;
2286 case WLR_SCENE_NODE_BEVEL:;
2287 struct wlr_scene_bevel *scene_bevel = wlr_scene_bevel_from_node(node);
2288 *width = scene_bevel->width;
2289 *height = scene_bevel->height;
2290 break;
2291 case WLR_SCENE_NODE_FRAME:;
2292 struct wlr_scene_frame *scene_frame_sz = wlr_scene_frame_from_node(node);
2293 *width = scene_frame_sz->width;
2294 *height = scene_frame_sz->height;
2295 break;
2296 case WLR_SCENE_NODE_DROPLET:;
2297 struct wlr_scene_droplet *scene_droplet = wlr_scene_droplet_from_node(node);
2298 *width = scene_droplet->width;
2299 *height = scene_droplet->height;
2300 break;
2301 case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
2302 struct wlr_scene_optimized_blur *scene_blur =
2303 wlr_scene_optimized_blur_from_node(node);
2304 *width = scene_blur->width;
2305 *height = scene_blur->height;
2306 break;
2307 case WLR_SCENE_NODE_BLUR:;
2308 struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
2309 *width = blur->width;
2310 *height = blur->height;
2311 break;
2312 }
2313 }
2314
2315 void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) {
2316 if (node->enabled == enabled) {
2317 return;
2318 }
2319
2320 int x, y;
2321 pixman_region32_t visible;
2322 pixman_region32_init(&visible);
2323 if (wlr_scene_node_coords(node, &x, &y)) {
2324 scene_node_visibility(node, &visible);
2325 }
2326
2327 node->enabled = enabled;
2328
2329 scene_node_update(node, &visible);
2330 }
2331
2332 void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
2333 if (node->x == x && node->y == y) {
2334 return;
2335 }
2336
2337 scene_node_update(node, NULL);
2338 node->x = x;
2339 node->y = y;
2340 scene_node_update(node, NULL);
2341 }
2342
2343 void wlr_scene_node_place_above(struct wlr_scene_node *node,
2344 struct wlr_scene_node *sibling) {
2345 assert(node != sibling);
2346 assert(node->parent == sibling->parent);
2347
2348 if (node->link.prev == &sibling->link) {
2349 return;
2350 }
2351
2352 wl_list_remove(&node->link);
2353 wl_list_insert(&sibling->link, &node->link);
2354 scene_node_update(node, NULL);
2355 }
2356
2357 void wlr_scene_node_place_below(struct wlr_scene_node *node,
2358 struct wlr_scene_node *sibling) {
2359 assert(node != sibling);
2360 assert(node->parent == sibling->parent);
2361
2362 if (node->link.next == &sibling->link) {
2363 return;
2364 }
2365
2366 wl_list_remove(&node->link);
2367 wl_list_insert(sibling->link.prev, &node->link);
2368 scene_node_update(node, NULL);
2369 }
2370
2371 void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) {
2372 struct wlr_scene_node *current_top = wl_container_of(
2373 node->parent->children.prev, current_top, link);
2374 if (node == current_top) {
2375 return;
2376 }
2377 wlr_scene_node_place_above(node, current_top);
2378 }
2379
2380 void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) {
2381 struct wlr_scene_node *current_bottom = wl_container_of(
2382 node->parent->children.next, current_bottom, link);
2383 if (node == current_bottom) {
2384 return;
2385 }
2386 wlr_scene_node_place_below(node, current_bottom);
2387 }
2388
2389 void wlr_scene_node_reparent(struct wlr_scene_node *node,
2390 struct wlr_scene_tree *new_parent) {
2391 assert(new_parent != NULL);
2392
2393 if (node->parent == new_parent) {
2394 return;
2395 }
2396
2397 /* Ensure that a node cannot become its own ancestor */
2398 for (struct wlr_scene_tree *ancestor = new_parent; ancestor != NULL;
2399 ancestor = ancestor->node.parent) {
2400 assert(&ancestor->node != node);
2401 }
2402
2403 int x, y;
2404 pixman_region32_t visible;
2405 pixman_region32_init(&visible);
2406 if (wlr_scene_node_coords(node, &x, &y)) {
2407 scene_node_visibility(node, &visible);
2408 }
2409
2410 wl_list_remove(&node->link);
2411 node->parent = new_parent;
2412 wl_list_insert(new_parent->children.prev, &node->link);
2413 scene_node_update(node, &visible);
2414 }
2415
2416 bool wlr_scene_node_coords(struct wlr_scene_node *node,
2417 int *lx_ptr, int *ly_ptr) {
2418 assert(node);
2419
2420 int lx = 0, ly = 0;
2421 bool enabled = true;
2422 while (true) {
2423 lx += node->x;
2424 ly += node->y;
2425 enabled = enabled && node->enabled;
2426 if (node->parent == NULL) {
2427 break;
2428 }
2429
2430 node = &node->parent->node;
2431 }
2432
2433 *lx_ptr = lx;
2434 *ly_ptr = ly;
2435 return enabled;
2436 }
2437
2438 static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node,
2439 int lx, int ly, wlr_scene_buffer_iterator_func_t user_iterator,
2440 void *user_data) {
2441 if (!node->enabled) {
2442 return;
2443 }
2444
2445 lx += node->x;
2446 ly += node->y;
2447
2448 if (node->type == WLR_SCENE_NODE_BUFFER) {
2449 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
2450 user_iterator(scene_buffer, lx, ly, user_data);
2451 } else if (node->type == WLR_SCENE_NODE_TREE) {
2452 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
2453 struct wlr_scene_node *child;
2454 wl_list_for_each(child, &scene_tree->children, link) {
2455 scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data);
2456 }
2457 }
2458 }
2459
2460 void wlr_scene_node_for_each_buffer(struct wlr_scene_node *node,
2461 wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) {
2462 scene_node_for_each_scene_buffer(node, 0, 0, user_iterator, user_data);
2463 }
2464
2465 struct node_at_data {
2466 double lx, ly;
2467 double rx, ry;
2468 struct wlr_scene_node *node;
2469 };
2470
2471 static bool scene_node_at_iterator(struct wlr_scene_node *node,
2472 int lx, int ly, void *data) {
2473 struct node_at_data *at_data = data;
2474
2475 double rx = at_data->lx - lx;
2476 double ry = at_data->ly - ly;
2477
2478 if (node->type == WLR_SCENE_NODE_BUFFER) {
2479 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
2480
2481 // A dest-scaled surface buffer occupies dst_width x dst_height in
2482 // layout space while its input region (and the coordinates its
2483 // client expects) live in SURFACE-LOCAL logical space. Map the
2484 // node-local point between the two before the region check, or a
2485 // buffer resting at a display ratio != 1 (the desktop grid's
2486 // patch, whenever the camera zoom sits off the pow2 quantization)
2487 // hit-tests and reports coordinates displaced by exactly that
2488 // ratio — which is how a click on a desktop item read as
2489 // background, and how the press position disagreed with the drag
2490 // deltas hard enough to fling the item across the canvas.
2491 // Buffers displayed at their natural size (every window at rest)
2492 // scale by exactly 1 here and are untouched.
2493 //
2494 // The ratio is between dst and the DISPLAYED region of the
2495 // surface, which is the whole surface only while no clip is set.
2496 // A clipped surface (wlr_scene_subsurface_tree_set_clip — how a
2497 // CSD toplevel is cropped to its xdg geometry, shedding the
2498 // client-side shadow margins) shows just the clip, at dst ==
2499 // clip size, so it is a crop at ratio 1, not a scale — yet its
2500 // surface size still differs from dst by the margins. Measuring
2501 // against the whole surface inflated every pointer coordinate on
2502 // such a window by (surface / geometry): a GTK3 dialog 718px
2503 // tall cropped to 666 received clicks 8% below the cursor, more
2504 // the further down, and the bottom band was dead because the
2505 // inflated point fell outside the surface. The clip's origin is
2506 // added by the surface callback below; only the extent matters
2507 // here, clamped to the surface as the commit path clamps it.
2508 if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) {
2509 struct wlr_scene_surface *scene_surface =
2510 wlr_scene_surface_try_from_buffer(scene_buffer);
2511 if (scene_surface != NULL && scene_surface->surface != NULL) {
2512 int sw = scene_surface->surface->current.width;
2513 int sh = scene_surface->surface->current.height;
2514 const struct wlr_box *clip = &scene_surface->clip;
2515 if (!wlr_box_empty(clip)) {
2516 if (clip->width < sw - clip->x) {
2517 sw = clip->width;
2518 } else {
2519 sw -= clip->x;
2520 }
2521 if (clip->height < sh - clip->y) {
2522 sh = clip->height;
2523 } else {
2524 sh -= clip->y;
2525 }
2526 }
2527 if (sw > 0 && sh > 0 && (sw != scene_buffer->dst_width ||
2528 sh != scene_buffer->dst_height)) {
2529 rx = rx * sw / scene_buffer->dst_width;
2530 ry = ry * sh / scene_buffer->dst_height;
2531 }
2532 }
2533 }
2534
2535 if (scene_buffer->point_accepts_input &&
2536 !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) {
2537 return false;
2538 }
2539 } else if (node->type == WLR_SCENE_NODE_RECT) {
2540 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
2541 if (!rect->accepts_input) {
2542 return false;
2543 } else if (!wlr_box_empty(&rect->clipped_region.area)
2544 && wlr_box_contains_point(&rect->clipped_region.area, rx, ry)) {
2545 // Inside clipped region
2546 return false;
2547 }
2548 } else if (node->type == WLR_SCENE_NODE_SHADOW
2549 || node->type == WLR_SCENE_NODE_BEVEL
2550 || node->type == WLR_SCENE_NODE_FRAME
2551 || node->type == WLR_SCENE_NODE_DROPLET
2552 || node->type == WLR_SCENE_NODE_OPTIMIZED_BLUR
2553 || node->type == WLR_SCENE_NODE_BLUR) {
2554 // Disable interaction
2555 return false;
2556 }
2557
2558 at_data->rx = rx;
2559 at_data->ry = ry;
2560 at_data->node = node;
2561 return true;
2562 }
2563
2564 struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node,
2565 double lx, double ly, double *nx, double *ny) {
2566 struct wlr_box box = {
2567 .x = floor(lx),
2568 .y = floor(ly),
2569 .width = 1,
2570 .height = 1
2571 };
2572
2573 struct node_at_data data = {
2574 .lx = lx,
2575 .ly = ly
2576 };
2577
2578 if (scene_nodes_in_box(node, &box, scene_node_at_iterator, &data)) {
2579 if (nx) {
2580 *nx = data.rx;
2581 }
2582 if (ny) {
2583 *ny = data.ry;
2584 }
2585 return data.node;
2586 }
2587
2588 return NULL;
2589 }
2590
2591 struct render_list_entry {
2592 struct wlr_scene_node *node;
2593 bool highlight_transparent_region;
2594 int x, y;
2595 };
2596
2597 static float get_luminance_multiplier(const struct wlr_color_luminances *src_lum,
2598 const struct wlr_color_luminances *dst_lum) {
2599 return (dst_lum->reference / src_lum->reference) * (src_lum->max / dst_lum->max);
2600 }
2601
2602 /* Bake the content below the node within one screen-space layout rect into
2603 * the cache, landing at `shift` (buffer px added to the screen buffer
2604 * coordinates: the node's anchor plus the cache margin). */
2605 static bool optimized_blur_bake_rect(struct fx_gles_render_pass *fx_pass,
2606 struct wlr_scene *scene, const struct render_data *data,
2607 const struct wlr_box *layout_rect, int shift_x, int shift_y) {
2608 struct wlr_box buf = {
2609 .x = (int)round((layout_rect->x - data->logical.x) * data->scale),
2610 .y = (int)round((layout_rect->y - data->logical.y) * data->scale),
2611 .width = (int)round(layout_rect->width * data->scale),
2612 .height = (int)round(layout_rect->height * data->scale),
2613 };
2614 if (buf.width <= 0 || buf.height <= 0) {
2615 return true;
2616 }
2617 const float opacity = 1.0f;
2618 struct fx_render_blur_pass_options blur_options = {
2619 .tex_options = {
2620 .base = {
2621 .transform = WL_OUTPUT_TRANSFORM_NORMAL,
2622 .alpha = &opacity,
2623 .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
2624 .dst_box = buf,
2625 },
2626 .clip_box = &buf,
2627 .discard_transparent = false,
2628 .clipped_region = {0}
2629 },
2630 .blur_data = &scene->blur_data,
2631 .blur_strength = 1.0f,
2632 .cache_shift_x = shift_x,
2633 .cache_shift_y = shift_y,
2634 };
2635 return fx_render_pass_add_optimized_blur(fx_pass, &blur_options);
2636 }
2637
2638 /* Record a just-baked rect (anchor space) in the node's region. What lay
2639 * within the blur's reach of an output edge — anything outside `trusted`,
2640 * the output inset by that reach, in the same space — sampled clamped edge
2641 * pixels in place of the backdrop beyond, so it also goes into
2642 * `edge_region` to be re-baked once it is clear of the edge. */
2643 static void optimized_blur_note_baked(struct wlr_scene_optimized_blur *ob,
2644 const pixman_box32_t *rect, const struct wlr_box *trusted) {
2645 pixman_region32_t guessed;
2646 pixman_region32_init_rect(&guessed, rect->x1, rect->y1,
2647 rect->x2 - rect->x1, rect->y2 - rect->y1);
2648 pixman_region32_union(&ob->baked_region, &ob->baked_region, &guessed);
2649 pixman_region32_subtract(&ob->edge_region, &ob->edge_region, &guessed);
2650 if (trusted->width > 0 && trusted->height > 0) {
2651 pixman_region32_t keep;
2652 pixman_region32_init_rect(&keep, trusted->x, trusted->y,
2653 trusted->width, trusted->height);
2654 pixman_region32_subtract(&guessed, &guessed, &keep);
2655 pixman_region32_fini(&keep);
2656 }
2657 pixman_region32_union(&ob->edge_region, &ob->edge_region, &guessed);
2658 pixman_region32_fini(&guessed);
2659 }
2660
2661 /* The optimized node's per-frame work: a full bake (anchored where the node
2662 * is now) when dirty, never baked, baked elsewhere, or travelled past the
2663 * cache margin; otherwise only the strips the node now shows that its bake
2664 * does not cover, landed at its existing anchor. */
2665 static void optimized_blur_render(struct wlr_scene *scene,
2666 struct wlr_scene_optimized_blur *ob, struct wlr_scene_node *node,
2667 struct fx_gles_render_pass *fx_pass, const struct render_data *data,
2668 struct wlr_box dst_box) {
2669 struct fx_offscreen_buffers *fbos = fx_pass->fx_offscreen_buffers;
2670 if (fbos == NULL) {
2671 return;
2672 }
2673 const int mx = fbos->cache_margin_x, my = fbos->cache_margin_y;
2674 const bool normal = data->transform == WL_OUTPUT_TRANSFORM_NORMAL;
2675 int cx, cy;
2676 if (!wlr_scene_node_coords(node, &cx, &cy)) {
2677 return;
2678 }
2679 struct wlr_box cur = { .x = cx, .y = cy, .width = ob->width, .height = ob->height };
2680 struct wlr_box vis;
2681 if (!wlr_box_intersection(&vis, &cur, &data->logical)) {
2682 return;
2683 }
2684
2685 // The output inset by the blur's sampling reach (layout px): a pixel
2686 // baked outside it sampled past the output's edge.
2687 const int reach = (int)ceil(blur_data_calc_size(&scene->blur_data) / data->scale);
2688 const struct wlr_box trusted = {
2689 .x = data->logical.x + reach,
2690 .y = data->logical.y + reach,
2691 .width = data->logical.width - 2 * reach,
2692 .height = data->logical.height - 2 * reach,
2693 };
2694
2695 bool full = ob->dirty || !ob->baked || ob->baked_output != data->output || !normal;
2696 int dx = 0, dy = 0; // anchor-space shift, layout px: anchor - current
2697 if (!full) {
2698 dx = ob->baked_x - cx;
2699 dy = ob->baked_y - cy;
2700 double mlx = mx / data->scale, mly = my / data->scale;
2701 if (vis.x + dx < data->logical.x - mlx || vis.y + dy < data->logical.y - mly ||
2702 vis.x + dx + vis.width > data->logical.x + data->logical.width + mlx ||
2703 vis.y + dy + vis.height > data->logical.y + data->logical.height + mly) {
2704 full = true;
2705 }
2706 }
2707
2708 if (full) {
2709 if (cce_scene_blur_debug()) {
2710 wlr_log(WLR_INFO, "[scenefx] optimized bake full %dx%d dirty=%d baked=%d",
2711 vis.width, vis.height, ob->dirty, ob->baked);
2712 }
2713 bool ok;
2714 if (normal) {
2715 ok = optimized_blur_bake_rect(fx_pass, scene, data, &vis, mx, my);
2716 } else {
2717 // Transformed output: the whole node in its buffer-space box,
2718 // in place (no anchoring on rotated outputs).
2719 const float opacity = 1.0f;
2720 enum wl_output_transform transform = wlr_output_transform_invert(data->transform);
2721 transform = wlr_output_transform_compose(transform, data->transform);
2722 struct fx_render_blur_pass_options blur_options = {
2723 .tex_options = {
2724 .base = {
2725 .transform = transform,
2726 .alpha = &opacity,
2727 .blend_mode = WLR_RENDER_BLEND_MODE_NONE,
2728 .dst_box = dst_box,
2729 },
2730 .clip_box = &dst_box,
2731 .discard_transparent = false,
2732 .clipped_region = {0}
2733 },
2734 .blur_data = &scene->blur_data,
2735 .blur_strength = 1.0f,
2736 .cache_shift_x = mx,
2737 .cache_shift_y = my,
2738 };
2739 ok = fx_render_pass_add_optimized_blur(fx_pass, &blur_options);
2740 }
2741 if (!ok) {
2742 return;
2743 }
2744 ob->dirty = false;
2745 ob->baked = true;
2746 ob->baked_x = cx;
2747 ob->baked_y = cy;
2748 ob->baked_output = data->output;
2749 pixman_region32_clear(&ob->baked_region);
2750 pixman_region32_clear(&ob->edge_region);
2751 const pixman_box32_t all = { vis.x, vis.y, vis.x + vis.width, vis.y + vis.height };
2752 optimized_blur_note_baked(ob, &all, &trusted);
2753 return;
2754 }
2755
2756 // Anchored: what the node shows now, in anchor space, minus what the
2757 // cache already holds for it — plus what it holds only as an edge guess
2758 // that has since moved clear of every edge (a pan carrying a window
2759 // that hung off the output inward: without this, the band along the
2760 // old edge kept its clamped blur, a seam of smeared backdrop).
2761 const struct wlr_box trusted_a = {
2762 .x = trusted.x + dx, .y = trusted.y + dy,
2763 .width = trusted.width, .height = trusted.height,
2764 };
2765 pixman_region32_t need, uncovered, stale;
2766 pixman_region32_init_rect(&need, vis.x + dx, vis.y + dy, vis.width, vis.height);
2767 pixman_region32_init(&uncovered);
2768 pixman_region32_subtract(&uncovered, &need, &ob->baked_region);
2769 pixman_region32_init(&stale);
2770 if (trusted.width > 0 && trusted.height > 0) {
2771 pixman_region32_intersect_rect(&stale, &ob->edge_region,
2772 trusted_a.x, trusted_a.y, trusted_a.width, trusted_a.height);
2773 pixman_region32_intersect(&stale, &stale, &need);
2774 if (cce_scene_blur_debug() && pixman_region32_not_empty(&stale)) {
2775 const pixman_box32_t *e = pixman_region32_extents(&stale);
2776 wlr_log(WLR_INFO, "[scenefx] optimized re-bake edge guess %dx%d at (%d, %d), node travel (%d, %d)",
2777 e->x2 - e->x1, e->y2 - e->y1, e->x1, e->y1, -dx, -dy);
2778 }
2779 pixman_region32_union(&uncovered, &uncovered, &stale);
2780 }
2781 pixman_region32_fini(&stale);
2782 int n = 0;
2783 const pixman_box32_t *rects = pixman_region32_rectangles(&uncovered, &n);
2784 const int shift_x = (int)round(dx * data->scale) + mx;
2785 const int shift_y = (int)round(dy * data->scale) + my;
2786 for (int i = 0; i < n; i++) {
2787 // The strip in screen space (where its content is being composited
2788 // this frame); it lands in the cache at the node's anchor.
2789 struct wlr_box r = {
2790 .x = rects[i].x1 - dx, .y = rects[i].y1 - dy,
2791 .width = rects[i].x2 - rects[i].x1, .height = rects[i].y2 - rects[i].y1,
2792 };
2793 if (optimized_blur_bake_rect(fx_pass, scene, data, &r, shift_x, shift_y)) {
2794 optimized_blur_note_baked(ob, &rects[i], &trusted_a);
2795 }
2796 }
2797 pixman_region32_fini(&need);
2798 pixman_region32_fini(&uncovered);
2799 }
2800
2801 static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) {
2802 struct wlr_scene_node *node = entry->node;
2803 struct fx_gles_render_pass *fx_pass = fx_get_render_pass(data->render_pass);
2804
2805 // Desk nodes draw shifted by the sub-pixel offset (device px); every
2806 // box and region derived from the node's layout position below gets
2807 // the same shift, so the node stays self-consistent and only its
2808 // placement against unshifted layers (bars, fullscreen) is off by it.
2809 int desk_dx = 0, desk_dy = 0;
2810 if (scene_node_has_desk_offset(data->output->scene, node)) {
2811 scene_desk_offset_px(data->output->scene, data->scale, data->transform, &desk_dx, &desk_dy);
2812 }
2813
2814 pixman_region32_t render_region;
2815 pixman_region32_init(&render_region);
2816 pixman_region32_copy(&render_region, &node->visible);
2817 pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y);
2818 logical_to_buffer_coords(&render_region, data, true);
2819 pixman_region32_translate(&render_region, desk_dx, desk_dy);
2820 pixman_region32_intersect(&render_region, &render_region, &data->damage);
2821 if (pixman_region32_empty(&render_region)) {
2822 pixman_region32_fini(&render_region);
2823 return;
2824 }
2825
2826 // CCE_BLUR_DEBUG: why is this blur node rendering — its box, the slice
2827 // inside this frame's damage, and the damage extents.
2828 if (node->type == WLR_SCENE_NODE_BLUR && cce_scene_blur_debug()) {
2829 const pixman_box32_t *v = pixman_region32_extents(&node->visible);
2830 const pixman_box32_t *r = pixman_region32_extents(&render_region);
2831 const pixman_box32_t *d = pixman_region32_extents(&data->damage);
2832 wlr_log(WLR_INFO, "[scenefx] blur entry visible=(%d,%d)-(%d,%d) render=(%d,%d)-(%d,%d) damage=(%d,%d)-(%d,%d) rects=%d",
2833 v->x1, v->y1, v->x2, v->y2, r->x1, r->y1, r->x2, r->y2,
2834 d->x1, d->y1, d->x2, d->y2, pixman_region32_n_rects(&data->damage));
2835 }
2836
2837 int x = entry->x - data->logical.x;
2838 int y = entry->y - data->logical.y;
2839
2840 struct wlr_box dst_box = {
2841 .x = x,
2842 .y = y,
2843 };
2844 scene_node_get_size(node, &dst_box.width, &dst_box.height);
2845 transform_output_box(&dst_box, data);
2846 dst_box.x += desk_dx;
2847 dst_box.y += desk_dy;
2848
2849 pixman_region32_t opaque;
2850 pixman_region32_init(&opaque);
2851 scene_node_opaque_region(node, x, y, &opaque);
2852 logical_to_buffer_coords(&opaque, data, false);
2853 pixman_region32_translate(&opaque, desk_dx, desk_dy);
2854 pixman_region32_subtract(&opaque, &render_region, &opaque);
2855
2856 enum wl_output_transform node_transform =
2857 wlr_output_transform_compose(WL_OUTPUT_TRANSFORM_NORMAL, data->transform);
2858
2859 struct wlr_scene *scene = data->output->scene;
2860 switch (node->type) {
2861 case WLR_SCENE_NODE_TREE:
2862 assert(false);
2863 break;
2864 case WLR_SCENE_NODE_RECT:;
2865 struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node);
2866 struct fx_corner_radii rect_corners = scene_rect->corners;
2867
2868 fx_corner_radii_transform(node_transform, &rect_corners);
2869
2870 struct wlr_box rect_clipped_region_box = scene_rect->clipped_region.area;
2871 struct fx_corner_radii rect_clipped_corners = scene_rect->clipped_region.corners;
2872
2873 // Node relative -> Root relative
2874 rect_clipped_region_box.x += x;
2875 rect_clipped_region_box.y += y;
2876
2877 transform_output_box(&rect_clipped_region_box, data);
2878 rect_clipped_region_box.x += desk_dx;
2879 rect_clipped_region_box.y += desk_dy;
2880 fx_corner_radii_transform(node_transform, &rect_clipped_corners);
2881
2882 struct fx_render_rect_options rect_options = {
2883 .base = {
2884 .box = dst_box,
2885 .color = {
2886 .r = scene_rect->color[0],
2887 .g = scene_rect->color[1],
2888 .b = scene_rect->color[2],
2889 .a = scene_rect->color[3],
2890 },
2891 .clip = &render_region,
2892 .blend_mode = (scene_rect->color[3] == 1.0f && fx_corner_radii_is_empty(&rect_corners) && scene_rect->fade_inset == 0) ?
2893 WLR_RENDER_BLEND_MODE_NONE : WLR_RENDER_BLEND_MODE_PREMULTIPLIED,
2894 },
2895 .clipped_region = {
2896 .area = rect_clipped_region_box,
2897 .corners = fx_corner_radii_scale(rect_clipped_corners, data->scale),
2898 },
2899 };
2900
2901 // TODO: Use the base wlr_render_pass_add_rect as a fast-path in the future
2902 if (!fx_corner_radii_is_empty(&rect_corners) || scene_rect->fade_inset > 0) {
2903 // fade_inset is wire-encoded as inset_px * 1000 + fade_mode:
2904 // scale only the inset part — multiplying the whole encoding
2905 // also multiplied the mode digit (quadratic became gaussian on
2906 // a 2x output).
2907 int fade_enc = scene_rect->fade_inset;
2908 int fade_px = (int)((float)(fade_enc / 1000) * data->scale + 0.5f);
2909 struct fx_render_rounded_rect_options rounded_rect_options = {
2910 .base = rect_options.base,
2911 .corners = fx_corner_radii_scale(rect_corners, data->scale),
2912 .clipped_region = rect_options.clipped_region,
2913 .fade_inset = fade_px * 1000 + fade_enc % 1000,
2914 };
2915 fx_render_pass_add_rounded_rect(fx_pass, &rounded_rect_options);
2916 } else {
2917 fx_render_pass_add_rect(fx_pass, &rect_options);
2918 }
2919 break;
2920 case WLR_SCENE_NODE_BUFFER:;
2921 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
2922 struct fx_corner_radii buffer_corners = scene_buffer->corners;
2923
2924 if (scene_buffer->is_single_pixel_buffer) {
2925 // TODO: Render blur/rounded corners/etc here:
2926
2927 // Render the buffer as a rect, this is likely to be more efficient
2928 wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
2929 .box = dst_box,
2930 .color = {
2931 .r = (float)scene_buffer->single_pixel_buffer_color[0] / (float)UINT32_MAX,
2932 .g = (float)scene_buffer->single_pixel_buffer_color[1] / (float)UINT32_MAX,
2933 .b = (float)scene_buffer->single_pixel_buffer_color[2] / (float)UINT32_MAX,
2934 .a = (float)scene_buffer->single_pixel_buffer_color[3] /
2935 (float)UINT32_MAX * scene_buffer->opacity,
2936 },
2937 .clip = &render_region,
2938 });
2939 break;
2940 }
2941
2942 struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer,
2943 data->output->output->renderer);
2944 if (texture == NULL) {
2945 scene_output_damage(data->output, &render_region);
2946 break;
2947 }
2948
2949 enum wl_output_transform transform =
2950 wlr_output_transform_invert(scene_buffer->transform);
2951 transform = wlr_output_transform_compose(transform, data->transform);
2952 fx_corner_radii_transform(transform, &buffer_corners);
2953
2954 struct wlr_color_primaries primaries = {0};
2955 if (scene_buffer->primaries != 0) {
2956 wlr_color_primaries_from_named(&primaries, scene_buffer->primaries);
2957 }
2958
2959 struct wlr_color_luminances src_lum, srgb_lum;
2960 wlr_color_transfer_function_get_default_luminance(
2961 scene_buffer->transfer_function, &src_lum);
2962 wlr_color_transfer_function_get_default_luminance(
2963 WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
2964 float luminance_multiplier = get_luminance_multiplier(&src_lum, &srgb_lum);
2965
2966 struct fx_render_texture_options tex_options = {
2967 .base = (struct wlr_render_texture_options){
2968 .texture = texture,
2969 .src_box = scene_buffer->src_box,
2970 .dst_box = dst_box,
2971 .transform = transform,
2972 .clip = &render_region, // Render with the smaller region, clipping CSD
2973 .alpha = &scene_buffer->opacity,
2974 .filter_mode = scene_buffer->filter_mode,
2975 .blend_mode = !data->output->scene->calculate_visibility ||
2976 !pixman_region32_empty(&opaque) ?
2977 WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE,
2978 .transfer_function = scene_buffer->transfer_function,
2979 .primaries = scene_buffer->primaries != 0 ? &primaries : NULL,
2980 .color_encoding = scene_buffer->color_encoding,
2981 .color_range = scene_buffer->color_range,
2982 .luminance_multiplier = &luminance_multiplier,
2983 .wait_timeline = scene_buffer->wait_timeline,
2984 .wait_point = scene_buffer->wait_point,
2985 },
2986 .clip_box = &dst_box,
2987 .corners = fx_corner_radii_scale(buffer_corners, data->scale),
2988 .clipped_region = {0},
2989 };
2990
2991 // TODO: Use the base wlr_render_pass_add_texture as a fast-path in the future
2992 fx_render_pass_add_texture(fx_pass, &tex_options);
2993
2994 struct wlr_scene_output_sample_event sample_event = {
2995 .output = data->output,
2996 .direct_scanout = false,
2997 .release_timeline = data->output->in_timeline,
2998 .release_point = data->output->in_point,
2999 };
3000 wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event);
3001
3002 if (entry->highlight_transparent_region) {
3003 wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){
3004 .box = dst_box,
3005 .color = { .r = 0, .g = 0.3, .b = 0, .a = 0.3 },
3006 .clip = &opaque,
3007 });
3008 }
3009 break;
3010 case WLR_SCENE_NODE_SHADOW:;
3011 struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node);
3012
3013 struct wlr_box shadow_clipped_region_box = scene_shadow->clipped_region.area;
3014 struct fx_corner_radii shadow_clipped_corners = scene_shadow->clipped_region.corners;
3015
3016 // Node relative -> Root relative
3017 shadow_clipped_region_box.x += x;
3018 shadow_clipped_region_box.y += y;
3019
3020 transform_output_box(&shadow_clipped_region_box, data);
3021 shadow_clipped_region_box.x += desk_dx;
3022 shadow_clipped_region_box.y += desk_dy;
3023 fx_corner_radii_transform(node_transform, &shadow_clipped_corners);
3024
3025 struct fx_render_box_shadow_options shadow_options = {
3026 .box = dst_box,
3027 .clipped_region = {
3028 .area = shadow_clipped_region_box,
3029 .corners = fx_corner_radii_scale(shadow_clipped_corners, data->scale),
3030 },
3031 .blur_sigma = scene_shadow->blur_sigma,
3032 .corner_radius = scene_shadow->corner_radius * data->scale,
3033 .color = {
3034 .r = scene_shadow->color[0],
3035 .g = scene_shadow->color[1],
3036 .b = scene_shadow->color[2],
3037 .a = scene_shadow->color[3],
3038 },
3039 .clip = &render_region,
3040 };
3041 fx_render_pass_add_box_shadow(fx_pass, &shadow_options);
3042 break;
3043 case WLR_SCENE_NODE_FRAME:;
3044 struct wlr_scene_frame *scene_frame = wlr_scene_frame_from_node(node);
3045
3046 struct fx_render_frame_options frame_options = {
3047 .box = dst_box,
3048 /* Lengths are node-local logical px, like the bevel's
3049 * thickness and the exclusion rect below; the shader works
3050 * in buffer px, so every one takes the output scale. Left
3051 * unscaled, the handles drew at half size on a scale-2
3052 * output — and smaller than the compositor's hit zone,
3053 * which is sized in logical px. */
3054 .corner_radius = scene_frame->corner_radius * data->scale,
3055 .band = scene_frame->band * data->scale,
3056 .band_min = scene_frame->band_min * data->scale,
3057 .corner_len = scene_frame->corner_len * data->scale,
3058 .gap = scene_frame->gap * data->scale,
3059 .hovered = scene_frame->hovered,
3060 .swell_curve = scene_frame->swell_curve,
3061 .bulge = scene_frame->bulge * data->scale,
3062 .buttons = scene_frame->buttons,
3063 .exclusion = {
3064 scene_frame->exclusion[0] * data->scale,
3065 scene_frame->exclusion[1] * data->scale,
3066 scene_frame->exclusion[2] * data->scale,
3067 scene_frame->exclusion[3] * data->scale,
3068 },
3069 .hover_color = {
3070 scene_frame->hover_color[0],
3071 scene_frame->hover_color[1],
3072 scene_frame->hover_color[2],
3073 scene_frame->hover_color[3],
3074 },
3075 .color = {
3076 .r = scene_frame->color[0],
3077 .g = scene_frame->color[1],
3078 .b = scene_frame->color[2],
3079 .a = scene_frame->color[3],
3080 },
3081 .clip = &render_region,
3082 };
3083 fx_render_pass_add_frame(fx_pass, &frame_options);
3084 break;
3085 case WLR_SCENE_NODE_BEVEL:;
3086 struct wlr_scene_bevel *scene_bevel = wlr_scene_bevel_from_node(node);
3087
3088 // The light direction is authored in screen space, so it has to
3089 // follow the output transform along with the box it lights.
3090 float bevel_dir_x = scene_bevel->light_dir[0];
3091 float bevel_dir_y = scene_bevel->light_dir[1];
3092 switch (node_transform) {
3093 case WL_OUTPUT_TRANSFORM_90:
3094 bevel_dir_x = scene_bevel->light_dir[1];
3095 bevel_dir_y = -scene_bevel->light_dir[0];
3096 break;
3097 case WL_OUTPUT_TRANSFORM_180:
3098 bevel_dir_x = -scene_bevel->light_dir[0];
3099 bevel_dir_y = -scene_bevel->light_dir[1];
3100 break;
3101 case WL_OUTPUT_TRANSFORM_270:
3102 bevel_dir_x = -scene_bevel->light_dir[1];
3103 bevel_dir_y = scene_bevel->light_dir[0];
3104 break;
3105 default:
3106 break;
3107 }
3108
3109 struct fx_render_bevel_options bevel_options = {
3110 .box = dst_box,
3111 .corner_radius = scene_bevel->corner_radius * data->scale,
3112 .thickness = scene_bevel->thickness * data->scale,
3113 .light_dir = { bevel_dir_x, bevel_dir_y },
3114 .light_intensity = scene_bevel->light_intensity,
3115 .shade_intensity = scene_bevel->shade_intensity,
3116 .shoulder = scene_bevel->shoulder,
3117 .focus = scene_bevel->focus,
3118 .focus_color = {
3119 scene_bevel->focus_color[0],
3120 scene_bevel->focus_color[1],
3121 scene_bevel->focus_color[2],
3122 },
3123 .focus_sharpness = scene_bevel->focus_sharpness,
3124 .color = {
3125 .r = scene_bevel->color[0],
3126 .g = scene_bevel->color[1],
3127 .b = scene_bevel->color[2],
3128 .a = scene_bevel->color[3],
3129 },
3130 .clip = &render_region,
3131 };
3132 fx_render_pass_add_bevel(fx_pass, &bevel_options);
3133 break;
3134 case WLR_SCENE_NODE_DROPLET:;
3135 struct wlr_scene_droplet *scene_droplet = wlr_scene_droplet_from_node(node);
3136
3137 struct fx_render_droplet_options droplet_options = {
3138 .box = dst_box,
3139 .attach_r = scene_droplet->attach_r * data->scale,
3140 .sheet_r = scene_droplet->sheet_r * data->scale,
3141 .bow_rise = scene_droplet->bow_rise * data->scale,
3142 .blend_k = scene_droplet->blend_k * data->scale,
3143 .curve = scene_droplet->curve,
3144 .band_px = scene_droplet->band_px * data->scale,
3145 .refr = scene_droplet->refr * data->scale,
3146 .ghost = scene_droplet->ghost,
3147 .compress = {
3148 .ceil = scene_droplet->compress_ceil,
3149 .knee = scene_droplet->compress_knee,
3150 .invert = scene_droplet->compress_invert,
3151 },
3152 .clip = &render_region,
3153 };
3154 fx_render_pass_add_droplet(fx_pass, &droplet_options);
3155 break;
3156 case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
3157 struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
3158 // Bake (or top up) this node's cache entry. The dirty gate on a full
3159 // bake is load-bearing: mark_dirty damages the node's whole box, so
3160 // pass->buffer holds fresh below-node content when the bake samples
3161 // it; strips are baked only where the node newly shows content, in
3162 // the frame that composites it.
3163 if (fx_pass->has_blur && is_scene_blur_enabled(&scene->blur_data)) {
3164 optimized_blur_render(scene, scene_blur, node, fx_pass, data, dst_box);
3165 }
3166 break;
3167 case WLR_SCENE_NODE_BLUR:;
3168 struct wlr_scene_blur *blur = wlr_scene_blur_from_node(node);
3169
3170 struct wlr_texture *tex = NULL;
3171 struct wlr_scene_buffer *mask = wlr_scene_blur_get_transparency_mask_source(blur);
3172
3173 enum wl_output_transform mask_transform = WL_OUTPUT_TRANSFORM_NORMAL;
3174
3175 struct wlr_fbox mask_src_box = {0};
3176 if (mask != NULL) {
3177 tex = scene_buffer_get_texture(mask, data->output->output->renderer);
3178 mask_transform = wlr_output_transform_invert(mask->transform);
3179 mask_transform = wlr_output_transform_compose(mask_transform, data->transform);
3180 mask_src_box = mask->src_box;
3181
3182 // Map the blur box through the mask buffer's own geometry instead
3183 // of stretching the whole mask texture over the blur rect: a mask
3184 // buffer LARGER than the blur box (a client overflow rim — the
3185 // surface extends transparently past the xdg geometry the blur is
3186 // sized to) would otherwise squeeze its transparent rim INTO the
3187 // box, and ignore_transparent then kills the blur in a rim-scaled
3188 // band at the window's right/bottom. Sample exactly the sub-rect
3189 // of the mask that overlaps the blur box, 1:1 in layout space.
3190 // Congruent boxes (the common case) skip this and keep the
3191 // previous full-texture mapping byte-for-byte.
3192 if (tex != NULL) {
3193 int mask_x, mask_y, blur_x, blur_y;
3194 if (wlr_scene_node_coords(&mask->node, &mask_x, &mask_y) &&
3195 wlr_scene_node_coords(node, &blur_x, &blur_y)) {
3196 int mask_w = 0, mask_h = 0;
3197 scene_node_get_size(&mask->node, &mask_w, &mask_h);
3198 if (mask_w > 0 && mask_h > 0 &&
3199 (mask_w != blur->width || mask_h != blur->height ||
3200 mask_x != blur_x || mask_y != blur_y)) {
3201 struct wlr_fbox full = mask_src_box;
3202 if (full.width <= 0 || full.height <= 0) {
3203 full = (struct wlr_fbox){
3204 .x = 0,
3205 .y = 0,
3206 .width = tex->width,
3207 .height = tex->height,
3208 };
3209 }
3210 double sx = full.width / mask_w;
3211 double sy = full.height / mask_h;
3212 mask_src_box = (struct wlr_fbox){
3213 .x = full.x + (blur_x - mask_x) * sx,
3214 .y = full.y + (blur_y - mask_y) * sy,
3215 .width = blur->width * sx,
3216 .height = blur->height * sy,
3217 };
3218 }
3219 }
3220 }
3221 }
3222
3223 struct fx_corner_radii blur_corners = blur->corners;
3224 fx_corner_radii_transform(node_transform, &blur_corners);
3225
3226 // Sample the shared cache at this node's own bake: the sibling
3227 // optimized node's anchor (its coordinates at its last full bake)
3228 // plus the cache margin. The optimized node, rendered just before
3229 // this one, has already baked whatever the node newly shows.
3230 int cache_mx = 0, cache_my = 0;
3231 if (fx_pass->fx_offscreen_buffers) {
3232 cache_mx = fx_pass->fx_offscreen_buffers->cache_margin_x;
3233 cache_my = fx_pass->fx_offscreen_buffers->cache_margin_y;
3234 }
3235 int shift_x = cache_mx, shift_y = cache_my;
3236 if (blur->should_only_blur_bottom_layer &&
3237 data->transform == WL_OUTPUT_TRANSFORM_NORMAL && node->parent) {
3238 struct wlr_scene_node *sib;
3239 wl_list_for_each(sib, &node->parent->children, link) {
3240 if (sib->type != WLR_SCENE_NODE_OPTIMIZED_BLUR) {
3241 continue;
3242 }
3243 struct wlr_scene_optimized_blur *opt = wlr_scene_optimized_blur_from_node(sib);
3244 int cur_x, cur_y;
3245 if (opt->baked && opt->baked_output == data->output &&
3246 wlr_scene_node_coords(sib, &cur_x, &cur_y)) {
3247 shift_x += (int)round((opt->baked_x - cur_x) * data->scale);
3248 shift_y += (int)round((opt->baked_y - cur_y) * data->scale);
3249 }
3250 break;
3251 }
3252 }
3253
3254 struct fx_render_blur_pass_options blur_options = {
3255 .tex_options = {
3256 .base = (struct wlr_render_texture_options) {
3257 .texture = tex,
3258 .src_box = mask_src_box,
3259 .dst_box = dst_box,
3260 .transform = mask_transform,
3261 .clip = &render_region,
3262 .alpha = &blur->alpha,
3263 .filter_mode = WLR_SCALE_FILTER_BILINEAR,
3264 .blend_mode = WLR_RENDER_BLEND_MODE_PREMULTIPLIED,
3265 },
3266 .clip_box = &dst_box,
3267 .corners = fx_corner_radii_scale(blur_corners, data->scale),
3268 .discard_transparent = false,
3269 .compress = {
3270 .ceil = blur->compress_ceil,
3271 .knee = blur->compress_knee,
3272 .invert = blur->compress_invert,
3273 },
3274 },
3275 .use_optimized_blur = blur->should_only_blur_bottom_layer,
3276 .blur_data = &scene->blur_data,
3277 .ignore_transparent = mask != NULL,
3278 .blur_strength = blur->strength,
3279 // Frozen cache: sample at this node's own delta since the freeze
3280 // (layout px -> buffer px), so it reads its own bake. Only
3281 // meaningful for an untransformed output; a rotated one gets
3282 // the unshifted cache.
3283 .cache_shift_x = shift_x,
3284 .cache_shift_y = shift_y,
3285 };
3286 fx_render_pass_add_blur(fx_pass, &blur_options);
3287 break;
3288 }
3289
3290 pixman_region32_fini(&opaque);
3291 pixman_region32_fini(&render_region);
3292 }
3293
3294 static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener,
3295 void *data) {
3296 struct wlr_scene *scene =
3297 wl_container_of(listener, scene, linux_dmabuf_v1_destroy);
3298 wl_list_remove(&scene->linux_dmabuf_v1_destroy.link);
3299 wl_list_init(&scene->linux_dmabuf_v1_destroy.link);
3300 scene->linux_dmabuf_v1 = NULL;
3301 }
3302
3303 void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene,
3304 struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) {
3305 assert(scene->linux_dmabuf_v1 == NULL);
3306 scene->linux_dmabuf_v1 = linux_dmabuf_v1;
3307 scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy;
3308 wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy);
3309 }
3310
3311 static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener *listener,
3312 void *data) {
3313 const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
3314 struct wlr_scene *scene =
3315 wl_container_of(listener, scene, gamma_control_manager_v1_set_gamma);
3316 struct wlr_scene_output *output = wlr_scene_get_scene_output(scene, event->output);
3317 if (!output) {
3318 // this scene might not own this output.
3319 return;
3320 }
3321
3322 output->gamma_lut_changed = true;
3323 output->gamma_lut = event->control;
3324 wlr_color_transform_unref(output->gamma_lut_color_transform);
3325 output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control);
3326 wlr_output_schedule_frame(output->output);
3327 }
3328
3329 static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *listener,
3330 void *data) {
3331 struct wlr_scene *scene =
3332 wl_container_of(listener, scene, gamma_control_manager_v1_destroy);
3333 wl_list_remove(&scene->gamma_control_manager_v1_destroy.link);
3334 wl_list_init(&scene->gamma_control_manager_v1_destroy.link);
3335 wl_list_remove(&scene->gamma_control_manager_v1_set_gamma.link);
3336 wl_list_init(&scene->gamma_control_manager_v1_set_gamma.link);
3337 scene->gamma_control_manager_v1 = NULL;
3338
3339 struct wlr_scene_output *output;
3340 wl_list_for_each(output, &scene->outputs, link) {
3341 output->gamma_lut_changed = false;
3342 output->gamma_lut = NULL;
3343 wlr_color_transform_unref(output->gamma_lut_color_transform);
3344 output->gamma_lut_color_transform = NULL;
3345 }
3346 }
3347
3348 void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene,
3349 struct wlr_gamma_control_manager_v1 *gamma_control) {
3350 assert(scene->gamma_control_manager_v1 == NULL);
3351 scene->gamma_control_manager_v1 = gamma_control;
3352
3353 scene->gamma_control_manager_v1_destroy.notify =
3354 scene_handle_gamma_control_manager_v1_destroy;
3355 wl_signal_add(&gamma_control->events.destroy, &scene->gamma_control_manager_v1_destroy);
3356 scene->gamma_control_manager_v1_set_gamma.notify =
3357 scene_handle_gamma_control_manager_v1_set_gamma;
3358 wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma);
3359 }
3360
3361 static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) {
3362 struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy);
3363 wl_list_remove(&scene->color_manager_v1_destroy.link);
3364 wl_list_init(&scene->color_manager_v1_destroy.link);
3365 scene->color_manager_v1 = NULL;
3366 }
3367
3368 void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) {
3369 assert(scene->color_manager_v1 == NULL);
3370 scene->color_manager_v1 = manager;
3371
3372 scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy;
3373 wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy);
3374 }
3375
3376 static void scene_output_handle_destroy(struct wlr_addon *addon) {
3377 struct wlr_scene_output *scene_output =
3378 wl_container_of(addon, scene_output, addon);
3379 wlr_scene_output_destroy(scene_output);
3380 }
3381
3382 static const struct wlr_addon_interface output_addon_impl = {
3383 .name = "wlr_scene_output",
3384 .destroy = scene_output_handle_destroy,
3385 };
3386
3387 static void scene_node_output_update(struct wlr_scene_node *node,
3388 struct wl_list *outputs, struct wlr_scene_output *ignore,
3389 struct wlr_scene_output *force) {
3390 if (node->type == WLR_SCENE_NODE_TREE) {
3391 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
3392 struct wlr_scene_node *child;
3393 wl_list_for_each(child, &scene_tree->children, link) {
3394 scene_node_output_update(child, outputs, ignore, force);
3395 }
3396 return;
3397 }
3398
3399 update_node_update_outputs(node, outputs, ignore, force);
3400 }
3401
3402 static void scene_output_update_geometry(struct wlr_scene_output *scene_output,
3403 bool force_update) {
3404 scene_output_damage_whole(scene_output);
3405
3406 scene_node_output_update(&scene_output->scene->tree.node,
3407 &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL);
3408 }
3409
3410 static void scene_output_handle_commit(struct wl_listener *listener, void *data) {
3411 struct wlr_scene_output *scene_output = wl_container_of(listener,
3412 scene_output, output_commit);
3413 struct wlr_output_event_commit *event = data;
3414 const struct wlr_output_state *state = event->state;
3415
3416 // if the output has been committed with a certain damage, we know that region
3417 // will be acknowledged by the backend so we don't need to keep track of it
3418 // anymore
3419 if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
3420 if (state->committed & WLR_OUTPUT_STATE_DAMAGE) {
3421 pixman_region32_subtract(&scene_output->pending_commit_damage,
3422 &scene_output->pending_commit_damage, &state->damage);
3423 } else {
3424 pixman_region32_fini(&scene_output->pending_commit_damage);
3425 pixman_region32_init(&scene_output->pending_commit_damage);
3426 }
3427 }
3428
3429 bool force_update = state->committed & (
3430 WLR_OUTPUT_STATE_TRANSFORM |
3431 WLR_OUTPUT_STATE_SCALE |
3432 WLR_OUTPUT_STATE_SUBPIXEL);
3433
3434 if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE |
3435 WLR_OUTPUT_STATE_ENABLED)) {
3436 scene_output_update_geometry(scene_output, force_update);
3437 }
3438
3439 if (scene_output->scene->debug_damage_option == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT &&
3440 !wl_list_empty(&scene_output->damage_highlight_regions)) {
3441 wlr_output_schedule_frame(scene_output->output);
3442 }
3443
3444 TRACY_WHEN_CONNECTED({
3445 // Queue a new frame as soon as possible when profiling
3446 wlr_output_schedule_frame(scene_output->output);
3447 })
3448
3449 // Next time the output is enabled, try to re-apply the gamma LUT
3450 if (scene_output->scene->gamma_control_manager_v1 &&
3451 (state->committed & WLR_OUTPUT_STATE_ENABLED) &&
3452 !scene_output->output->enabled) {
3453 scene_output->gamma_lut_changed = true;
3454 }
3455 }
3456
3457 static void scene_output_handle_damage(struct wl_listener *listener, void *data) {
3458 struct wlr_scene_output *scene_output = wl_container_of(listener,
3459 scene_output, output_damage);
3460 struct wlr_output *output = scene_output->output;
3461 struct wlr_output_event_damage *event = data;
3462
3463 int width, height;
3464 wlr_output_transformed_resolution(output, &width, &height);
3465
3466 pixman_region32_t damage;
3467 pixman_region32_init(&damage);
3468 pixman_region32_copy(&damage, event->damage);
3469 wlr_region_transform(&damage, &damage,
3470 wlr_output_transform_invert(output->transform), width, height);
3471 scene_output_damage(scene_output, &damage);
3472 pixman_region32_fini(&damage);
3473 }
3474
3475 static void scene_output_handle_needs_frame(struct wl_listener *listener, void *data) {
3476 struct wlr_scene_output *scene_output = wl_container_of(listener,
3477 scene_output, output_needs_frame);
3478 wlr_output_schedule_frame(scene_output->output);
3479 }
3480
3481 struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
3482 struct wlr_output *output) {
3483 struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output));
3484 if (scene_output == NULL) {
3485 return NULL;
3486 }
3487
3488 scene_output->output = output;
3489 scene_output->scene = scene;
3490 wlr_addon_init(&scene_output->addon, &output->addons, scene, &output_addon_impl);
3491
3492 wlr_damage_ring_init(&scene_output->damage_ring);
3493 pixman_region32_init(&scene_output->pending_commit_damage);
3494 wl_list_init(&scene_output->damage_highlight_regions);
3495
3496 int prev_output_index = -1;
3497 struct wl_list *prev_output_link = &scene->outputs;
3498
3499 struct wlr_scene_output *current_output;
3500 wl_list_for_each(current_output, &scene->outputs, link) {
3501 if (prev_output_index + 1 != current_output->index) {
3502 break;
3503 }
3504
3505 prev_output_index = current_output->index;
3506 prev_output_link = ¤t_output->link;
3507 }
3508
3509 int drm_fd = wlr_backend_get_drm_fd(output->backend);
3510 if (drm_fd >= 0 && output->backend->features.timeline &&
3511 output->renderer != NULL && output->renderer->features.timeline) {
3512 scene_output->in_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
3513 scene_output->out_timeline = wlr_drm_syncobj_timeline_create(drm_fd);
3514 if (scene_output->in_timeline == NULL || scene_output->out_timeline == NULL) {
3515 wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
3516 wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
3517 return NULL;
3518 }
3519 }
3520
3521 scene_output->index = prev_output_index + 1;
3522 assert(scene_output->index < 64);
3523 wl_list_insert(prev_output_link, &scene_output->link);
3524
3525 wl_signal_init(&scene_output->events.destroy);
3526
3527 scene_output->output_commit.notify = scene_output_handle_commit;
3528 wl_signal_add(&output->events.commit, &scene_output->output_commit);
3529
3530 scene_output->output_damage.notify = scene_output_handle_damage;
3531 wl_signal_add(&output->events.damage, &scene_output->output_damage);
3532
3533 scene_output->output_needs_frame.notify = scene_output_handle_needs_frame;
3534 wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame);
3535
3536 scene_output_update_geometry(scene_output, false);
3537
3538 return scene_output;
3539 }
3540
3541 static void highlight_region_destroy(struct highlight_region *damage) {
3542 wl_list_remove(&damage->link);
3543 pixman_region32_fini(&damage->region);
3544 free(damage);
3545 }
3546
3547 void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
3548 if (scene_output == NULL) {
3549 return;
3550 }
3551
3552 wl_signal_emit_mutable(&scene_output->events.destroy, NULL);
3553
3554 scene_node_output_update(&scene_output->scene->tree.node,
3555 &scene_output->scene->outputs, scene_output, NULL);
3556
3557 assert(wl_list_empty(&scene_output->events.destroy.listener_list));
3558
3559 struct highlight_region *damage, *tmp_damage;
3560 wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) {
3561 highlight_region_destroy(damage);
3562 }
3563
3564 wlr_addon_finish(&scene_output->addon);
3565 wlr_damage_ring_finish(&scene_output->damage_ring);
3566 pixman_region32_fini(&scene_output->pending_commit_damage);
3567 wl_list_remove(&scene_output->link);
3568 wl_list_remove(&scene_output->output_commit.link);
3569 wl_list_remove(&scene_output->output_damage.link);
3570 wl_list_remove(&scene_output->output_needs_frame.link);
3571 if (scene_output->in_timeline != NULL) {
3572 wlr_drm_syncobj_timeline_signal(scene_output->in_timeline, UINT64_MAX);
3573 wlr_drm_syncobj_timeline_unref(scene_output->in_timeline);
3574 }
3575 if (scene_output->out_timeline != NULL) {
3576 wlr_drm_syncobj_timeline_signal(scene_output->out_timeline, UINT64_MAX);
3577 wlr_drm_syncobj_timeline_unref(scene_output->out_timeline);
3578 }
3579 wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
3580 wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
3581 wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
3582 wlr_color_transform_unref(scene_output->combined_color_transform);
3583 wl_array_release(&scene_output->render_list);
3584 free(scene_output);
3585 }
3586
3587 struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene,
3588 struct wlr_output *output) {
3589 struct wlr_addon *addon =
3590 wlr_addon_find(&output->addons, scene, &output_addon_impl);
3591 if (addon == NULL) {
3592 return NULL;
3593 }
3594 struct wlr_scene_output *scene_output =
3595 wl_container_of(addon, scene_output, addon);
3596 return scene_output;
3597 }
3598
3599 void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
3600 int lx, int ly) {
3601 if (scene_output->x == lx && scene_output->y == ly) {
3602 return;
3603 }
3604
3605 scene_output->x = lx;
3606 scene_output->y = ly;
3607
3608 scene_output_update_geometry(scene_output, false);
3609 }
3610
3611 static bool scene_node_invisible(struct wlr_scene_node *node) {
3612 if (node->type == WLR_SCENE_NODE_TREE) {
3613 return true;
3614 } else if (node->type == WLR_SCENE_NODE_RECT) {
3615 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
3616 // TODO: Check if clipped region covers whole rect?
3617 return rect->color[3] == 0.f;
3618 } else if (node->type == WLR_SCENE_NODE_BUFFER) {
3619 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
3620
3621 return buffer->buffer == NULL && buffer->texture == NULL;
3622 } else if (node->type == WLR_SCENE_NODE_SHADOW) {
3623 struct wlr_scene_shadow *shadow = wlr_scene_shadow_from_node(node);
3624
3625 return shadow->color[3] == 0.f;
3626 } else if (node->type == WLR_SCENE_NODE_BEVEL) {
3627 struct wlr_scene_bevel *bevel = wlr_scene_bevel_from_node(node);
3628
3629 return bevel->color[3] == 0.f || bevel->thickness <= 0.f;
3630 } else if (node->type == WLR_SCENE_NODE_FRAME) {
3631 struct wlr_scene_frame *frame = wlr_scene_frame_from_node(node);
3632
3633 return frame->color[3] == 0.f || frame->band <= 0.f;
3634 }
3635
3636 return false;
3637 }
3638
3639 struct render_list_constructor_data {
3640 struct wlr_box box;
3641 struct wl_array *render_list;
3642 bool calculate_visibility;
3643 bool highlight_transparent_region;
3644 bool fractional_scale;
3645 };
3646
3647 static bool scene_buffer_is_black_opaque(struct wlr_scene_buffer *scene_buffer) {
3648 return scene_buffer->is_single_pixel_buffer &&
3649 scene_buffer->single_pixel_buffer_color[0] == 0 &&
3650 scene_buffer->single_pixel_buffer_color[1] == 0 &&
3651 scene_buffer->single_pixel_buffer_color[2] == 0 &&
3652 scene_buffer->single_pixel_buffer_color[3] == UINT32_MAX &&
3653 scene_buffer->opacity == 1.0 &&
3654 fx_corner_radii_is_empty(&scene_buffer->corners);
3655 }
3656
3657 static bool scene_rect_is_black_opaque(struct wlr_scene_rect *scene_rect) {
3658 return scene_rect->color[0] == 0.f &&
3659 scene_rect->color[1] == 0.f &&
3660 scene_rect->color[2] == 0.f &&
3661 scene_rect->color[3] == 1.f &&
3662 scene_rect->fade_inset == 0 &&
3663 fx_corner_radii_is_empty(&scene_rect->corners) &&
3664 fx_corner_radii_is_empty(&scene_rect->clipped_region.corners) &&
3665 wlr_box_empty(&scene_rect->clipped_region.area);
3666 }
3667
3668 static bool construct_render_list_iterator(struct wlr_scene_node *node,
3669 int lx, int ly, void *_data) {
3670 struct render_list_constructor_data *data = _data;
3671
3672 if (scene_node_invisible(node)) {
3673 return false;
3674 }
3675
3676 // While rendering, the background should always be black. If we see a
3677 // black rect, we can ignore rendering everything under the rect, and
3678 // unless fractional scale is used even the rect itself (to avoid running
3679 // into issues regarding damage region expansion).
3680 if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility &&
3681 (!data->fractional_scale || data->render_list->size == 0)) {
3682 struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node);
3683
3684 if (scene_rect_is_black_opaque(rect)) {
3685 return false;
3686 }
3687 }
3688
3689 // Apply the same special-case to black opaque single-pixel buffers
3690 if (node->type == WLR_SCENE_NODE_BUFFER && data->calculate_visibility &&
3691 (!data->fractional_scale || data->render_list->size == 0)) {
3692 struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
3693
3694 if (scene_buffer_is_black_opaque(scene_buffer)) {
3695 return false;
3696 }
3697 }
3698
3699 pixman_region32_t intersection;
3700 pixman_region32_init(&intersection);
3701 pixman_region32_intersect_rect(&intersection, &node->visible,
3702 data->box.x, data->box.y,
3703 data->box.width, data->box.height);
3704 if (pixman_region32_empty(&intersection)) {
3705 pixman_region32_fini(&intersection);
3706 return false;
3707 }
3708
3709 pixman_region32_fini(&intersection);
3710
3711 struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry));
3712 if (!entry) {
3713 return false;
3714 }
3715
3716 *entry = (struct render_list_entry){
3717 .node = node,
3718 .x = lx,
3719 .y = ly,
3720 .highlight_transparent_region = data->highlight_transparent_region,
3721 };
3722
3723 return false;
3724 }
3725
3726 static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
3727 struct wlr_scene_buffer *scene_buffer,
3728 const struct wlr_linux_dmabuf_feedback_v1_init_options *options) {
3729 if (!scene->linux_dmabuf_v1) {
3730 return;
3731 }
3732
3733 struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer);
3734 if (!surface) {
3735 return;
3736 }
3737
3738 // compare to the previous options so that we don't send
3739 // duplicate feedback events.
3740 if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) {
3741 return;
3742 }
3743
3744 scene_buffer->prev_feedback_options = *options;
3745
3746 // One line per feedback that actually goes out (the memcmp above having
3747 // found a real change). Each carries an fd to the client, so a burst here
3748 // is the fd-exhaustion signature: `grep -c 'dmabuf feedback sent'`.
3749 wlr_log(WLR_DEBUG, "dmabuf feedback sent (scanout_output=%p)",
3750 (void *)options->scanout_primary_output);
3751
3752 struct wlr_linux_dmabuf_feedback_v1 feedback = {0};
3753 if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) {
3754 return;
3755 }
3756
3757 enum wl_output_transform preferred_buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
3758 if (options->scanout_primary_output != NULL) {
3759 preferred_buffer_transform = options->scanout_primary_output->transform;
3760 }
3761
3762 // TODO: also send wl_surface.preferred_buffer_transform when running with
3763 // pure software rendering
3764 wlr_surface_set_preferred_buffer_transform(surface->surface, preferred_buffer_transform);
3765 wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1,
3766 surface->surface, &feedback);
3767
3768 wlr_linux_dmabuf_feedback_v1_finish(&feedback);
3769 }
3770
3771 static bool color_management_is_scanout_allowed(const struct wlr_output_image_description *img_desc,
3772 const struct wlr_scene_buffer *buffer) {
3773 // Disallow scanout if the output has colorimetry information but buffer
3774 // doesn't; allow it only if the output also lacks it.
3775 if (buffer->transfer_function == 0 && buffer->primaries == 0) {
3776 return img_desc == NULL;
3777 }
3778
3779 // If the output has colorimetry information, the buffer must match it for
3780 // direct scanout to be allowed.
3781 if (img_desc != NULL) {
3782 return img_desc->transfer_function == buffer->transfer_function &&
3783 img_desc->primaries == buffer->primaries;
3784 }
3785 // If the output doesn't have colorimetry image description set, we can only
3786 // scan out buffers with default colorimetry (gamma2.2 transfer and sRGB
3787 // primaries) used in wlroots.
3788 return buffer->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 &&
3789 buffer->primaries == WLR_COLOR_NAMED_PRIMARIES_SRGB;
3790 }
3791
3792 enum scene_direct_scanout_result {
3793 // This scene node is not a candidate for scanout
3794 SCANOUT_INELIGIBLE,
3795
3796 // This scene node is a candidate for scanout, but is currently
3797 // incompatible due to e.g. buffer mismatch, and if possible we'd like to
3798 // resolve this incompatibility.
3799 SCANOUT_CANDIDATE,
3800
3801 // Scanout is successful.
3802 SCANOUT_SUCCESS,
3803 };
3804
3805 static enum scene_direct_scanout_result scene_entry_try_direct_scanout(
3806 struct render_list_entry *entry, struct wlr_output_state *state,
3807 const struct render_data *data) {
3808 struct wlr_scene_output *scene_output = data->output;
3809 struct wlr_scene_node *node = entry->node;
3810
3811 if (!scene_output->scene->direct_scanout) {
3812 return SCANOUT_INELIGIBLE;
3813 }
3814
3815 if (node->type != WLR_SCENE_NODE_BUFFER) {
3816 return SCANOUT_INELIGIBLE;
3817 }
3818
3819 // A desk node drawn with a sub-pixel shift is not where its buffer
3820 // would be scanned out.
3821 {
3822 int dx, dy;
3823 scene_desk_offset_px(scene_output->scene, data->scale, data->transform, &dx, &dy);
3824 if ((dx != 0 || dy != 0) && scene_node_has_desk_offset(scene_output->scene, node)) {
3825 return SCANOUT_INELIGIBLE;
3826 }
3827 }
3828
3829 if (state->committed & (WLR_OUTPUT_STATE_MODE |
3830 WLR_OUTPUT_STATE_ENABLED |
3831 WLR_OUTPUT_STATE_RENDER_FORMAT)) {
3832 // Legacy DRM will explode if we try to modeset with a direct scanout buffer
3833 return SCANOUT_INELIGIBLE;
3834 }
3835
3836 if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) {
3837 return SCANOUT_INELIGIBLE;
3838 }
3839
3840 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
3841 if (buffer->buffer == NULL) {
3842 return SCANOUT_INELIGIBLE;
3843 }
3844
3845 // The native size of the buffer after any transform is applied
3846 int default_width = buffer->buffer->width;
3847 int default_height = buffer->buffer->height;
3848 wlr_output_transform_coords(buffer->transform, &default_width, &default_height);
3849 struct wlr_fbox default_box = {
3850 .width = default_width,
3851 .height = default_height,
3852 };
3853
3854 if (buffer->transform != data->transform) {
3855 return SCANOUT_INELIGIBLE;
3856 }
3857
3858 const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state);
3859 if (!color_management_is_scanout_allowed(img_desc, buffer)) {
3860 return SCANOUT_INELIGIBLE;
3861 }
3862
3863 bool is_color_repr_none = buffer->color_encoding == WLR_COLOR_ENCODING_NONE &&
3864 buffer->color_range == WLR_COLOR_RANGE_NONE;
3865 bool is_color_repr_identity_full = buffer->color_encoding == WLR_COLOR_ENCODING_IDENTITY &&
3866 buffer->color_range == WLR_COLOR_RANGE_FULL;
3867
3868 if (!(is_color_repr_none || is_color_repr_identity_full)) {
3869 return SCANOUT_INELIGIBLE;
3870 }
3871
3872 // We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled
3873 // on a frame-by-frame basis, we wait for a few frames to send the new format recommendations.
3874 // Maybe we should only send feedback in this case if tests fail.
3875 if (scene_output->dmabuf_feedback_debounce >= DMABUF_FEEDBACK_DEBOUNCE_FRAMES
3876 && buffer->primary_output == scene_output) {
3877 struct wlr_linux_dmabuf_feedback_v1_init_options options = {
3878 .main_renderer = scene_output->output->renderer,
3879 .scanout_primary_output = scene_output->output,
3880 };
3881
3882 scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
3883 }
3884
3885 struct wlr_output_state pending;
3886 wlr_output_state_init(&pending);
3887 if (!wlr_output_state_copy(&pending, state)) {
3888 return SCANOUT_CANDIDATE;
3889 }
3890
3891 if (!wlr_fbox_empty(&buffer->src_box) &&
3892 !wlr_fbox_equal(&buffer->src_box, &default_box)) {
3893 pending.buffer_src_box = buffer->src_box;
3894 }
3895
3896 // Translate the position from scene coordinates to output coordinates
3897 pending.buffer_dst_box.x = entry->x - scene_output->x;
3898 pending.buffer_dst_box.y = entry->y - scene_output->y;
3899
3900 scene_node_get_size(node, &pending.buffer_dst_box.width, &pending.buffer_dst_box.height);
3901 transform_output_box(&pending.buffer_dst_box, data);
3902
3903 struct wlr_buffer *wlr_buffer = buffer->buffer;
3904 struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(wlr_buffer);
3905 if (client_buffer != NULL && client_buffer->source != NULL && client_buffer->source->n_locks > 0) {
3906 wlr_buffer = client_buffer->source;
3907 }
3908
3909 wlr_output_state_set_buffer(&pending, wlr_buffer);
3910 if (buffer->wait_timeline != NULL) {
3911 wlr_output_state_set_wait_timeline(&pending, buffer->wait_timeline, buffer->wait_point);
3912 }
3913
3914 if (scene_output->out_timeline) {
3915 scene_output->out_point++;
3916 wlr_output_state_set_signal_timeline(&pending, scene_output->out_timeline, scene_output->out_point);
3917 }
3918
3919 if (!wlr_output_test_state(scene_output->output, &pending)) {
3920 wlr_output_state_finish(&pending);
3921 return SCANOUT_CANDIDATE;
3922 }
3923
3924 wlr_output_state_copy(state, &pending);
3925 wlr_output_state_finish(&pending);
3926
3927 struct wlr_scene_output_sample_event sample_event = {
3928 .output = scene_output,
3929 .direct_scanout = true,
3930 .release_timeline = data->output->out_timeline,
3931 .release_point = data->output->out_point,
3932 };
3933 wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event);
3934 return SCANOUT_SUCCESS;
3935 }
3936
3937 bool wlr_scene_output_needs_frame(struct wlr_scene_output *scene_output) {
3938 return scene_output->output->needs_frame ||
3939 !pixman_region32_empty(&scene_output->pending_commit_damage) ||
3940 scene_output->gamma_lut_changed;
3941 }
3942
3943 static bool should_blur_node_extend_damage(struct wlr_scene_node *node,
3944 struct fx_gles_render_pass *fx_pass, struct blur_data *blur_data) {
3945 switch (node->type) {
3946 case WLR_SCENE_NODE_BLUR:;
3947 struct wlr_scene_blur *blur_node = wlr_scene_blur_from_node(node);
3948 // No artifact prevention needed when the whole blur is already
3949 // rendered
3950 if (blur_node->should_only_blur_bottom_layer && blur_node->strength == 1.0) {
3951 fx_pass->has_blur = true;
3952 return false;
3953 }
3954 // Apply the blur strength to avoid rendering more blur than
3955 // what's needed
3956 if (blur_node->strength < 1.0f) {
3957 *blur_data = blur_data_apply_strength(blur_data, blur_node->strength);
3958 }
3959 break;
3960 case WLR_SCENE_NODE_OPTIMIZED_BLUR:;
3961 struct wlr_scene_optimized_blur *optimized_node =
3962 wlr_scene_optimized_blur_from_node(node);
3963 if (!optimized_node->dirty) {
3964 return false;
3965 }
3966 break;
3967 case WLR_SCENE_NODE_DROPLET:;
3968 // The droplet lens samples the frame-so-far around itself, so it has
3969 // the same stale-sample problem the blur comment below describes:
3970 // partial damage under or NEAR the drop leaves neighboring pixels
3971 // holding last frame's final composite (including the drop's own
3972 // output), and re-rendering only the damaged sliver bakes those into
3973 // part of the lens while the rest keeps the old frame — which is
3974 // exactly the visible/invisible flashing on segments whose text
3975 // ticks (clock, battery). Whole-node re-render, independent of the
3976 // scene blur config.
3977 struct wlr_scene_droplet *droplet_node = wlr_scene_droplet_from_node(node);
3978 if (droplet_node->refr <= 0.0f && droplet_node->ghost <= 0.0f) {
3979 return false;
3980 }
3981 return true;
3982 default:
3983 return false;
3984 }
3985
3986 return is_scene_blur_enabled(blur_data);
3987 }
3988
3989 static bool apply_blur_region(struct wlr_scene_node *node, struct blur_data *blur_data,
3990 struct render_data *render_data, struct wlr_output_state *output_state,
3991 const pixman_region32_t *original_damage, pixman_region32_t *blur_padding_region) {
3992 bool should_compensate_blur = false;
3993 int sample_size = blur_data_calc_size(blur_data);
3994 if (node->type == WLR_SCENE_NODE_DROPLET) {
3995 // The droplet's sampling reach: rim refraction plus the inverted
3996 // ghost's span (0.35 x the box about its center) — the same margin
3997 // fx_render_pass_add_droplet copies. Node fields are logical px.
3998 struct wlr_scene_droplet *droplet = wlr_scene_droplet_from_node(node);
3999 int dim = droplet->width > droplet->height ? droplet->width : droplet->height;
4000 sample_size = (int)((droplet->refr + 1.0f + 0.35f * (float)dim)
4001 * render_data->scale) + 1;
4002 }
4003
4004 pixman_region32_t node_visible_region;
4005 pixman_region32_init(&node_visible_region);
4006 pixman_region32_copy(&node_visible_region, &node->visible);
4007 pixman_region32_translate(&node_visible_region, -render_data->output->x, -render_data->output->y);
4008 logical_to_buffer_coords(&node_visible_region, render_data, false);
4009
4010 pixman_region32_t expanded_damage;
4011 pixman_region32_init(&expanded_damage);
4012 wlr_region_expand(&expanded_damage, original_damage, sample_size);
4013
4014 pixman_region32_t intersection;
4015 pixman_region32_init(&intersection);
4016 pixman_region32_intersect(&intersection, &expanded_damage, &node_visible_region);
4017 // pixman_region32_intersect's return value reports allocation success,
4018 // not a non-empty result — testing it here made EVERY blur node count as
4019 // touched by EVERY frame's damage, so all of them re-blurred (and pulled
4020 // their whole box into the damage) whenever anything on screen changed:
4021 // one animating window cost a full re-blur of every status segment and
4022 // translucent window each frame. Only a node the expanded damage
4023 // actually reaches has stale samples to worry about.
4024 if (pixman_region32_not_empty(&intersection)) {
4025 should_compensate_blur = true;
4026 if (cce_scene_blur_debug()) {
4027 const pixman_box32_t *nv = pixman_region32_extents(&node_visible_region);
4028 const pixman_box32_t *od = pixman_region32_extents(original_damage);
4029 wlr_log(WLR_INFO, "[scenefx] blur_region: type=%d sample=%d node=(%d,%d)-(%d,%d) orig=(%d,%d)-(%d,%d)",
4030 node->type, sample_size, nv->x1, nv->y1, nv->x2, nv->y2,
4031 od->x1, od->y1, od->x2, od->y2);
4032 }
4033
4034 // Re-render the node's entire blur region, not just the damaged
4035 // sliver: the blur samples the framebuffer up to sample_size beyond
4036 // the damage, where pixels still hold the previous frame's final
4037 // composited content — including this node's own content drawn above
4038 // the blur. Re-blurring only the sliver bakes those stale samples
4039 // into part of the backdrop while neighboring pixels keep last
4040 // frame's blur, which shimmers whenever damage sweeps underneath
4041 // (e.g. grid lines during a desktop pan). Re-blurring the whole node
4042 // computes every pixel from the same freshly rendered below-content.
4043 pixman_region32_union(&render_data->damage, &render_data->damage, &node_visible_region);
4044 // Also make sure that the backend also knows about the new
4045 // damage. Very important
4046 output_state->committed |= WLR_OUTPUT_STATE_DAMAGE;
4047 pixman_region32_union(&output_state->damage, &output_state->damage, &node_visible_region);
4048
4049 // Expand it once more to get the blur padding region
4050 // which is key for artifact removal :)
4051 wlr_region_expand(&intersection, &node_visible_region, sample_size);
4052 // Don't re-add already added rectangles
4053 pixman_region32_subtract(&intersection, &intersection, blur_padding_region);
4054 pixman_region32_union(blur_padding_region, blur_padding_region, &intersection);
4055 }
4056
4057 pixman_region32_fini(&intersection);
4058 pixman_region32_fini(&expanded_damage);
4059 pixman_region32_fini(&node_visible_region);
4060 return should_compensate_blur;
4061 }
4062
4063 bool wlr_scene_output_commit(struct wlr_scene_output *scene_output,
4064 const struct wlr_scene_output_state_options *options) {
4065 if (!wlr_scene_output_needs_frame(scene_output)) {
4066 return true;
4067 }
4068
4069 bool ok = false;
4070 struct wlr_output_state state;
4071 wlr_output_state_init(&state);
4072 if (!wlr_scene_output_build_state(scene_output, &state, options)) {
4073 goto out;
4074 }
4075
4076 ok = wlr_output_commit_state(scene_output->output, &state);
4077 if (!ok) {
4078 goto out;
4079 }
4080
4081 out:
4082 wlr_output_state_finish(&state);
4083 return ok;
4084 }
4085
4086 static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_output,
4087 struct wlr_output_state *state) {
4088 if (!scene_output->gamma_lut_changed) {
4089 return;
4090 }
4091
4092 struct wlr_output_state gamma_pending = {0};
4093 if (!wlr_output_state_copy(&gamma_pending, state)) {
4094 return;
4095 }
4096
4097 wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform);
4098 scene_output->gamma_lut_changed = false;
4099
4100 if (!wlr_output_test_state(scene_output->output, &gamma_pending)) {
4101 wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut);
4102
4103 scene_output->gamma_lut = NULL;
4104 wlr_color_transform_unref(scene_output->gamma_lut_color_transform);
4105 scene_output->gamma_lut_color_transform = NULL;
4106 wlr_output_state_finish(&gamma_pending);
4107 return;
4108 }
4109
4110 wlr_output_state_copy(state, &gamma_pending);
4111 wlr_output_state_finish(&gamma_pending);
4112 }
4113
4114 static bool scene_output_combine_color_transforms(
4115 struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied,
4116 const struct wlr_output_image_description *img_desc, bool render_gamma_lut) {
4117 bool result = false;
4118 struct wlr_color_transform *color_matrix = NULL;
4119 struct wlr_color_transform *inv_eotf = NULL;
4120 struct wlr_color_transform *user_gamma = NULL;
4121
4122 if (img_desc != NULL) {
4123 assert(supplied == NULL);
4124 struct wlr_color_primaries primaries_srgb;
4125 wlr_color_primaries_from_named(&primaries_srgb, WLR_COLOR_NAMED_PRIMARIES_SRGB);
4126 struct wlr_color_primaries primaries;
4127 wlr_color_primaries_from_named(&primaries, img_desc->primaries);
4128 float matrix[9];
4129 wlr_color_primaries_transform_absolute_colorimetric(&primaries_srgb, &primaries, matrix);
4130
4131 struct wlr_color_luminances srgb_lum, dst_lum;
4132 wlr_color_transfer_function_get_default_luminance(
4133 WLR_COLOR_TRANSFER_FUNCTION_SRGB, &srgb_lum);
4134 wlr_color_transfer_function_get_default_luminance(img_desc->transfer_function, &dst_lum);
4135 float luminance_multiplier = get_luminance_multiplier(&srgb_lum, &dst_lum);
4136 for (int i = 0; i < 9; ++i) {
4137 matrix[i] *= luminance_multiplier;
4138 }
4139
4140 color_matrix = wlr_color_transform_init_matrix(matrix);
4141 inv_eotf = wlr_color_transform_init_linear_to_inverse_eotf(img_desc->transfer_function);
4142 if (color_matrix == NULL || inv_eotf == NULL) {
4143 goto cleanup_transforms;
4144 }
4145 } else if (supplied != NULL) {
4146 inv_eotf = wlr_color_transform_ref(supplied);
4147 } else {
4148 inv_eotf = wlr_color_transform_init_linear_to_inverse_eotf(
4149 WLR_COLOR_TRANSFER_FUNCTION_GAMMA22);
4150 if (inv_eotf == NULL) {
4151 goto cleanup_transforms;
4152 }
4153 }
4154
4155 struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform;
4156 if (gamma_lut != NULL && render_gamma_lut) {
4157 user_gamma = wlr_color_transform_ref(gamma_lut);
4158 }
4159
4160 struct wlr_color_transform *combined;
4161 struct wlr_color_transform *transforms[] = {
4162 color_matrix,
4163 inv_eotf,
4164 user_gamma,
4165 };
4166 const size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]);
4167 if (!color_transform_compose(&combined, transforms, transforms_len)) {
4168 goto cleanup_transforms;
4169 }
4170
4171 wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform);
4172 scene_output->prev_gamma_lut_color_transform = gamma_lut ? wlr_color_transform_ref(gamma_lut) : NULL;
4173 wlr_color_transform_unref(scene_output->prev_supplied_color_transform);
4174 scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL;
4175 wlr_color_transform_unref(scene_output->combined_color_transform);
4176 scene_output->combined_color_transform = combined;
4177
4178 result = true;
4179
4180 cleanup_transforms:
4181 wlr_color_transform_unref(color_matrix);
4182 wlr_color_transform_unref(inv_eotf);
4183 wlr_color_transform_unref(user_gamma);
4184 return result;
4185 }
4186
4187 bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
4188 struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) {
4189 struct wlr_scene_output_state_options default_options = {0};
4190 if (!options) {
4191 options = &default_options;
4192 }
4193 struct wlr_scene_timer *timer = options->timer;
4194 struct timespec start_time;
4195 if (timer) {
4196 clock_gettime(CLOCK_MONOTONIC, &start_time);
4197 wlr_scene_timer_finish(timer);
4198 *timer = (struct wlr_scene_timer){0};
4199 }
4200
4201 if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) {
4202 // if the state is being disabled, do nothing.
4203 return true;
4204 }
4205
4206 struct wlr_output *output = scene_output->output;
4207 enum wlr_scene_debug_damage_option debug_damage =
4208 scene_output->scene->debug_damage_option;
4209
4210 bool render_gamma_lut = false;
4211 if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) {
4212 if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) {
4213 scene_output_damage_whole(scene_output);
4214 }
4215 if (scene_output->gamma_lut_color_transform != NULL) {
4216 render_gamma_lut = true;
4217 }
4218 }
4219
4220 struct render_data render_data = {
4221 .transform = output->transform,
4222 .scale = output->scale,
4223 .logical = { .x = scene_output->x, .y = scene_output->y },
4224 .output = scene_output,
4225 };
4226
4227 int resolution_width, resolution_height;
4228 output_pending_resolution(output, state,
4229 &resolution_width, &resolution_height);
4230
4231 if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) {
4232 if (render_data.transform != state->transform) {
4233 scene_output_damage_whole(scene_output);
4234 }
4235
4236 render_data.transform = state->transform;
4237 }
4238
4239 if (state->committed & WLR_OUTPUT_STATE_SCALE) {
4240 if (render_data.scale != state->scale) {
4241 scene_output_damage_whole(scene_output);
4242 }
4243
4244 render_data.scale = state->scale;
4245 }
4246
4247 render_data.trans_width = resolution_width;
4248 render_data.trans_height = resolution_height;
4249 wlr_output_transform_coords(render_data.transform,
4250 &render_data.trans_width, &render_data.trans_height);
4251
4252 render_data.logical.width = render_data.trans_width / render_data.scale;
4253 render_data.logical.height = render_data.trans_height / render_data.scale;
4254
4255 struct render_list_constructor_data list_con = {
4256 .box = render_data.logical,
4257 .render_list = &scene_output->render_list,
4258 .calculate_visibility = scene_output->scene->calculate_visibility,
4259 .highlight_transparent_region = scene_output->scene->highlight_transparent_region,
4260 .fractional_scale = floor(render_data.scale) != render_data.scale,
4261 };
4262
4263 list_con.render_list->size = 0;
4264 scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box,
4265 construct_render_list_iterator, &list_con);
4266 array_realloc(list_con.render_list, list_con.render_list->size);
4267
4268 struct render_list_entry *list_data = list_con.render_list->data;
4269 int list_len = list_con.render_list->size / sizeof(*list_data);
4270
4271 if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) {
4272 scene_output_damage_whole(scene_output);
4273 }
4274
4275 struct timespec now;
4276 if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
4277 struct wl_list *regions = &scene_output->damage_highlight_regions;
4278 clock_gettime(CLOCK_MONOTONIC, &now);
4279
4280 // add the current frame's damage if there is damage
4281 if (!pixman_region32_empty(&scene_output->damage_ring.current)) {
4282 struct highlight_region *current_damage = calloc(1, sizeof(*current_damage));
4283 if (current_damage) {
4284 pixman_region32_init(¤t_damage->region);
4285 pixman_region32_copy(¤t_damage->region,
4286 &scene_output->damage_ring.current);
4287 current_damage->when = now;
4288 wl_list_insert(regions, ¤t_damage->link);
4289 }
4290 }
4291
4292 pixman_region32_t acc_damage;
4293 pixman_region32_init(&acc_damage);
4294 struct highlight_region *damage, *tmp_damage;
4295 wl_list_for_each_safe(damage, tmp_damage, regions, link) {
4296 // remove overlapping damage regions
4297 pixman_region32_subtract(&damage->region, &damage->region, &acc_damage);
4298 pixman_region32_union(&acc_damage, &acc_damage, &damage->region);
4299
4300 // if this damage is too old or has nothing in it, get rid of it
4301 struct timespec time_diff;
4302 timespec_sub(&time_diff, &now, &damage->when);
4303 if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME ||
4304 pixman_region32_empty(&damage->region)) {
4305 highlight_region_destroy(damage);
4306 }
4307 }
4308
4309 scene_output_damage(scene_output, &acc_damage);
4310 pixman_region32_fini(&acc_damage);
4311 }
4312
4313 wlr_output_state_set_damage(state, &scene_output->pending_commit_damage);
4314
4315 // We only want to try direct scanout if:
4316 // - There is only one entry in the render list
4317 // - There are no color transforms that need to be applied
4318 // - Damage highlight debugging is not enabled
4319 enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE;
4320 if (options->color_transform == NULL && !render_gamma_lut && list_len == 1
4321 && debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
4322 scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data);
4323 }
4324
4325 if (scanout_result == SCANOUT_INELIGIBLE) {
4326 if (scene_output->dmabuf_feedback_debounce > 0) {
4327 // We cannot scan out, so count down towards sending composition dmabuf feedback
4328 scene_output->dmabuf_feedback_debounce--;
4329 }
4330 } else if (scene_output->dmabuf_feedback_debounce < DMABUF_FEEDBACK_DEBOUNCE_FRAMES) {
4331 // We either want to scan out or successfully scanned out, so count up towards sending
4332 // scanout dmabuf feedback
4333 scene_output->dmabuf_feedback_debounce++;
4334 }
4335
4336 bool scanout = scanout_result == SCANOUT_SUCCESS;
4337 if (scene_output->prev_scanout != scanout) {
4338 scene_output->prev_scanout = scanout;
4339 wlr_log(WLR_DEBUG, "Direct scan-out %s",
4340 scanout ? "enabled" : "disabled");
4341 }
4342
4343 if (scanout) {
4344 scene_output_state_attempt_gamma(scene_output, state);
4345
4346 if (timer) {
4347 struct timespec end_time, duration;
4348 clock_gettime(CLOCK_MONOTONIC, &end_time);
4349 timespec_sub(&duration, &end_time, &start_time);
4350 timer->pre_render_duration = timespec_to_nsec(&duration);
4351 }
4352
4353 TRACY_MARK_FRAME;
4354 return true;
4355 }
4356
4357 struct wlr_swapchain *swapchain = options->swapchain;
4358 if (!swapchain) {
4359 if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) {
4360 TRACY_MARK_FRAME;
4361 return false;
4362 }
4363
4364 swapchain = output->swapchain;
4365 }
4366
4367 struct wlr_buffer *buffer = wlr_swapchain_acquire(swapchain);
4368 if (buffer == NULL) {
4369 TRACY_MARK_FRAME;
4370 return false;
4371 }
4372
4373 assert(buffer->width == resolution_width && buffer->height == resolution_height);
4374
4375 if (timer) {
4376 timer->render_timer = wlr_render_timer_create(output->renderer);
4377
4378 struct timespec end_time, duration;
4379 clock_gettime(CLOCK_MONOTONIC, &end_time);
4380 timespec_sub(&duration, &end_time, &start_time);
4381 timer->pre_render_duration = timespec_to_nsec(&duration);
4382 }
4383
4384 if ((render_gamma_lut
4385 && scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform)
4386 || scene_output->prev_supplied_color_transform != options->color_transform
4387 || (state->committed & WLR_OUTPUT_STATE_IMAGE_DESCRIPTION)) {
4388 const struct wlr_output_image_description *output_description =
4389 output_pending_image_description(output, state);
4390 if (!scene_output_combine_color_transforms(scene_output, options->color_transform,
4391 output_description, render_gamma_lut)) {
4392 wlr_buffer_unlock(buffer);
4393 return false;
4394 }
4395 }
4396
4397 scene_output->in_point++;
4398 struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer,
4399 &(struct wlr_buffer_pass_options){
4400 .timer = timer ? timer->render_timer : NULL,
4401 .color_transform = scene_output->combined_color_transform,
4402 .signal_timeline = scene_output->in_timeline,
4403 .signal_point = scene_output->in_point,
4404 });
4405 if (render_pass == NULL) {
4406 wlr_buffer_unlock(buffer);
4407
4408 TRACY_MARK_FRAME;
4409 return false;
4410 }
4411
4412 render_data.render_pass = render_pass;
4413
4414 pixman_region32_init(&render_data.damage);
4415 wlr_damage_ring_rotate_buffer(&scene_output->damage_ring, buffer,
4416 &render_data.damage);
4417
4418 struct fx_gles_render_pass *fx_pass = fx_get_render_pass(render_pass);
4419 bool should_compensate_blur = false;
4420 if (fx_render_pass_init_offscreen_buffers(render_pass, output)
4421 && pixman_region32_not_empty(&render_data.damage)) {
4422 // Software cursors are baked into the reused buffer, so their recent
4423 // footprints must be treated as damage before the blur compensation
4424 // geometry is computed: inside the damage they get re-blurred with
4425 // full sampling context, and the padding restore (which excludes
4426 // damage) can never paste a stale cursor back. The live cursor is
4427 // redrawn on top afterwards since
4428 // wlr_output_add_software_cursors_to_render_pass() clips to this
4429 // damage.
4430 if (is_scene_blur_enabled(&scene_output->scene->blur_data)) {
4431 struct wlr_output_cursor *cursor;
4432 wl_list_for_each(cursor, &output->cursors, link) {
4433 if (!cursor->enabled || !cursor->visible) {
4434 continue;
4435 }
4436 double scale = output->scale;
4437 g_cursor_history[g_cursor_history_index].x =
4438 (cursor->x - cursor->hotspot_x) * scale;
4439 g_cursor_history[g_cursor_history_index].y =
4440 (cursor->y - cursor->hotspot_y) * scale;
4441 g_cursor_history[g_cursor_history_index].w = cursor->width;
4442 g_cursor_history[g_cursor_history_index].h = cursor->height;
4443 g_cursor_history[g_cursor_history_index].valid = true;
4444 g_cursor_history[g_cursor_history_index].output = output;
4445 g_cursor_history_index =
4446 (g_cursor_history_index + 1) % CURSOR_HISTORY_SIZE;
4447 }
4448
4449 // Newest first, this output's only, the most recent few.
4450 for (int k = 1, taken = 0; k <= CURSOR_HISTORY_SIZE && taken < CURSOR_HISTORY_DAMAGE; k++) {
4451 int i = (g_cursor_history_index - k + CURSOR_HISTORY_SIZE) % CURSOR_HISTORY_SIZE;
4452 if (!g_cursor_history[i].valid || g_cursor_history[i].output != output) {
4453 continue;
4454 }
4455 taken++;
4456 pixman_region32_union_rect(&render_data.damage, &render_data.damage,
4457 g_cursor_history[i].x - 16,
4458 g_cursor_history[i].y - 16,
4459 g_cursor_history[i].w + 32,
4460 g_cursor_history[i].h + 32);
4461 pixman_region32_union_rect(&state->damage, &state->damage,
4462 g_cursor_history[i].x - 16,
4463 g_cursor_history[i].y - 16,
4464 g_cursor_history[i].w + 32,
4465 g_cursor_history[i].h + 32);
4466 state->committed |= WLR_OUTPUT_STATE_DAMAGE;
4467 }
4468 pixman_region32_intersect_rect(&render_data.damage, &render_data.damage,
4469 0, 0, output->width, output->height);
4470 }
4471
4472 // Blur artifact prevention
4473 // Note: Supports individual blur node blur_data
4474 pixman_region32_t original_damage;
4475 pixman_region32_init(&original_damage);
4476 pixman_region32_copy(&original_damage, &render_data.damage);
4477
4478 // Only compensate for blur artifacts when the damage doesn't cover
4479 // the whole output. Test actual coverage, not extents: scattered
4480 // damage (e.g. every grid cell rect moving during a desktop pan)
4481 // has full-output extents while being mostly holes, and skipping
4482 // compensation then leaves each blur node re-blurring only slivers
4483 // against stale framebuffer content.
4484 pixman_box32_t full_output_box = {
4485 .x1 = 0, .y1 = 0,
4486 .x2 = output->width, .y2 = output->height,
4487 };
4488 const bool full_damage = pixman_region32_contains_rectangle(
4489 &original_damage, &full_output_box) == PIXMAN_REGION_IN;
4490
4491 // The extra region we copy and paste onto the framebuffer after render
4492 // for artifact removal
4493 pixman_region32_t blur_padding_region;
4494 pixman_region32_init(&blur_padding_region);
4495
4496 // Check if the original damage expanded by the to-be-rendered blur
4497 // nodes sampling size intersects with said nodes visible region. The
4498 // intersection gets expanded once more which becomes the region to be
4499 // copied before render and pasted onto the framebuffer after render
4500 // (the region where artifacts will be visible). This method fixes blur
4501 // nodes not updating properly when nearby, non-blurred nodes get
4502 // damaged while avoiding to re-render the whole nodes blur region. It
4503 // also allows for node-individual blur_data if needed in the future.
4504 for (int i = list_len - 1; i >= 0; i--) {
4505 struct render_list_entry *entry = &list_data[i];
4506 struct wlr_scene_node *node = entry->node;
4507 struct blur_data blur_data = scene_output->scene->blur_data;
4508 if (!should_blur_node_extend_damage(node, fx_pass, &blur_data)) {
4509 continue;
4510 }
4511
4512 // End early. No need to compensate for blur if the whole output
4513 // is damaged anyway, but still make sure to let the renderer know that
4514 // there's blur nodes to render.
4515 if (full_damage) {
4516 should_compensate_blur = false;
4517 fx_pass->has_blur = true;
4518 break;
4519 }
4520
4521 if (apply_blur_region(node, &blur_data, &render_data, state,
4522 &original_damage, &blur_padding_region)) {
4523 should_compensate_blur = true;
4524 fx_pass->has_blur = true;
4525 }
4526 }
4527
4528 // Expand the damage to compensate for blur
4529 if (should_compensate_blur) {
4530 // Capture the padding pixels around the blur where artifacts will be drawn
4531 pixman_region32_subtract(&fx_pass->blur_padding_region,
4532 &blur_padding_region, &render_data.damage);
4533 // Make sure that the padding and damage doesn't exceed the output bounds
4534 pixman_region32_intersect_rect(&fx_pass->blur_padding_region, &fx_pass->blur_padding_region,
4535 0, 0, output->width, output->height);
4536
4537 // Combine with the render damage (we need to redraw the padding area as well)
4538 pixman_region32_union(&render_data.damage,
4539 &render_data.damage, &fx_pass->blur_padding_region);
4540 pixman_region32_intersect_rect(&render_data.damage, &render_data.damage,
4541 0, 0, output->width, output->height);
4542
4543 // Copy the surrounding content where the blur would display artifacts
4544 // and draw it above the artifacts. Otherwise The old rendered
4545 // content would be included into the new blur. This means that
4546 // content like a high z-index toplevel would be included into the
4547 // blur of a toplevel with a low z-index.
4548 fx_render_pass_read_to_buffer(fx_pass, &fx_pass->blur_padding_region,
4549 fx_pass->fx_offscreen_buffers->blur_saved_pixels_buffer, fx_pass->buffer);
4550 }
4551
4552 pixman_region32_fini(&blur_padding_region);
4553 pixman_region32_fini(&original_damage);
4554 }
4555
4556 pixman_region32_t background;
4557 pixman_region32_init(&background);
4558 pixman_region32_copy(&background, &render_data.damage);
4559
4560 // Cull areas of the background that are occluded by opaque regions of
4561 // scene nodes above. Those scene nodes will just render atop having us
4562 // never see the background.
4563 if (scene_output->scene->calculate_visibility) {
4564 for (int i = list_len - 1; i >= 0; i--) {
4565 struct render_list_entry *entry = &list_data[i];
4566
4567 // We must only cull opaque regions that are visible by the node.
4568 // The node's visibility will have the knowledge of a black rect
4569 // that may have been omitted from the render list via the black
4570 // rect optimization. In order to ensure we don't cull background
4571 // rendering in that black rect region, consider the node's visibility.
4572 pixman_region32_t opaque;
4573 pixman_region32_init(&opaque);
4574 scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque);
4575 pixman_region32_intersect(&opaque, &opaque, &entry->node->visible);
4576
4577 pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y);
4578 logical_to_buffer_coords(&opaque, &render_data, false);
4579 pixman_region32_subtract(&background, &background, &opaque);
4580 pixman_region32_fini(&opaque);
4581 }
4582
4583 if (floor(render_data.scale) != render_data.scale) {
4584 wlr_region_expand(&background, &background, 1);
4585
4586 // reintersect with the damage because we never want to render
4587 // outside of the damage region
4588 pixman_region32_intersect(&background, &background, &render_data.damage);
4589 }
4590 }
4591
4592 wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){
4593 .box = { .width = buffer->width, .height = buffer->height },
4594 .color = { .r = 0, .g = 0, .b = 0, .a = 1 },
4595 .clip = &background,
4596 });
4597 pixman_region32_fini(&background);
4598
4599 for (int i = list_len - 1; i >= 0; i--) {
4600 struct render_list_entry *entry = &list_data[i];
4601 scene_entry_render(entry, &render_data);
4602
4603 if (entry->node->type == WLR_SCENE_NODE_BUFFER) {
4604 struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
4605
4606 // Direct scanout counts up to DMABUF_FEEDBACK_DEBOUNCE_FRAMES before sending new dmabuf
4607 // feedback, and on composition we wait until it hits zero again. If we knew that an
4608 // entry could never be a scanout candidate, we could send feedback to it
4609 // unconditionally without debounce, but for now it is all or nothing
4610 if (scene_output->dmabuf_feedback_debounce == 0 && buffer->primary_output == scene_output) {
4611 struct wlr_linux_dmabuf_feedback_v1_init_options options = {
4612 .main_renderer = output->renderer,
4613 .scanout_primary_output = NULL,
4614 };
4615
4616 scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
4617 }
4618 }
4619 }
4620
4621 if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
4622 struct highlight_region *damage;
4623 wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) {
4624 struct timespec time_diff;
4625 timespec_sub(&time_diff, &now, &damage->when);
4626 int64_t time_diff_ms = timespec_to_msec(&time_diff);
4627 float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME;
4628
4629 wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){
4630 .box = { .width = buffer->width, .height = buffer->height },
4631 .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 },
4632 .clip = &damage->region,
4633 });
4634 }
4635 }
4636
4637 if (should_compensate_blur) {
4638 // Render the saved pixels over the blur artifacts
4639 fx_render_pass_read_to_buffer(fx_pass, &fx_pass->blur_padding_region,
4640 fx_pass->buffer, fx_pass->fx_offscreen_buffers->blur_saved_pixels_buffer);
4641 }
4642
4643 wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage);
4644
4645 pixman_region32_fini(&render_data.damage);
4646
4647 if (!wlr_render_pass_submit(render_pass)) {
4648 wlr_buffer_unlock(buffer);
4649
4650 // if we failed to render the buffer, it will have undefined contents
4651 // Trash the damage ring
4652 wlr_damage_ring_add_whole(&scene_output->damage_ring);
4653
4654 TRACY_MARK_FRAME;
4655 return false;
4656 }
4657
4658 wlr_output_state_set_buffer(state, buffer);
4659 wlr_buffer_unlock(buffer);
4660
4661 if (scene_output->in_timeline != NULL) {
4662 wlr_output_state_set_wait_timeline(state, scene_output->in_timeline,
4663 scene_output->in_point);
4664 scene_output->out_point++;
4665 wlr_output_state_set_signal_timeline(state, scene_output->out_timeline,
4666 scene_output->out_point);
4667 }
4668
4669 if (!render_gamma_lut) {
4670 scene_output_state_attempt_gamma(scene_output, state);
4671 }
4672
4673 TRACY_MARK_FRAME;
4674 return true;
4675 }
4676
4677 int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) {
4678 int64_t pre_render = timer->pre_render_duration;
4679 if (!timer->render_timer) {
4680 return pre_render;
4681 }
4682 int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer);
4683 return render != -1 ? pre_render + render : -1;
4684 }
4685
4686 void wlr_scene_timer_finish(struct wlr_scene_timer *timer) {
4687 if (timer->render_timer) {
4688 wlr_render_timer_destroy(timer->render_timer);
4689 }
4690 }
4691
4692 static void scene_node_send_frame_done(struct wlr_scene_node *node,
4693 struct wlr_scene_output *scene_output, struct timespec *now) {
4694 if (!node->enabled) {
4695 return;
4696 }
4697
4698 if (node->type == WLR_SCENE_NODE_BUFFER) {
4699 struct wlr_scene_buffer *scene_buffer =
4700 wlr_scene_buffer_from_node(node);
4701 struct wlr_scene_frame_done_event event = {
4702 .output = scene_output,
4703 .when = *now,
4704 };
4705 wlr_scene_buffer_send_frame_done(scene_buffer, &event);
4706 } else if (node->type == WLR_SCENE_NODE_TREE) {
4707 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
4708 struct wlr_scene_node *child;
4709 wl_list_for_each(child, &scene_tree->children, link) {
4710 scene_node_send_frame_done(child, scene_output, now);
4711 }
4712 }
4713 }
4714
4715 void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output,
4716 struct timespec *now) {
4717 scene_node_send_frame_done(&scene_output->scene->tree.node,
4718 scene_output, now);
4719 }
4720
4721 static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box,
4722 struct wlr_scene_node *node, int lx, int ly,
4723 wlr_scene_buffer_iterator_func_t user_iterator, void *user_data) {
4724 if (!node->enabled) {
4725 return;
4726 }
4727
4728 lx += node->x;
4729 ly += node->y;
4730
4731 if (node->type == WLR_SCENE_NODE_BUFFER) {
4732 struct wlr_box node_box = { .x = lx, .y = ly };
4733 scene_node_get_size(node, &node_box.width, &node_box.height);
4734
4735 struct wlr_box intersection;
4736 if (wlr_box_intersection(&intersection, output_box, &node_box)) {
4737 struct wlr_scene_buffer *scene_buffer =
4738 wlr_scene_buffer_from_node(node);
4739 user_iterator(scene_buffer, lx, ly, user_data);
4740 }
4741 } else if (node->type == WLR_SCENE_NODE_TREE) {
4742 struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node);
4743 struct wlr_scene_node *child;
4744 wl_list_for_each(child, &scene_tree->children, link) {
4745 scene_output_for_each_scene_buffer(output_box, child, lx, ly,
4746 user_iterator, user_data);
4747 }
4748 }
4749 }
4750
4751 void wlr_scene_output_for_each_buffer(struct wlr_scene_output *scene_output,
4752 wlr_scene_buffer_iterator_func_t iterator, void *user_data) {
4753 struct wlr_box box = { .x = scene_output->x, .y = scene_output->y };
4754 wlr_output_effective_resolution(scene_output->output,
4755 &box.width, &box.height);
4756 scene_output_for_each_scene_buffer(&box, &scene_output->scene->tree.node, 0, 0,
4757 iterator, user_data);
4758 }