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