aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--types/scene/wlr_scene.c311
1 files changed, 189 insertions, 122 deletions
diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c
index f4815ece..498fa878 100644
--- a/types/scene/wlr_scene.c
+++ b/types/scene/wlr_scene.c
@@ -67,8 +67,6 @@ static void scene_node_init(struct wlr_scene_node *node,
wlr_addon_set_init(&node->addons);
}
-static void scene_node_damage_whole(struct wlr_scene_node *node);
-
struct highlight_region {
pixman_region32_t region;
struct timespec when;
@@ -241,14 +239,44 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box,
return _scene_nodes_in_box(node, box, iterator, user_data, x, y);
}
-// This function must be called whenever the coordinates/dimensions of a scene
-// buffer or scene output change. It is not necessary to call when a scene
-// buffer's node is enabled/disabled or obscured by other nodes.
-static void scene_buffer_update_outputs(struct wlr_scene_buffer *scene_buffer,
- int lx, int ly, struct wlr_scene *scene,
- struct wlr_scene_output *ignore) {
- struct wlr_box buffer_box = { .x = lx, .y = ly };
- scene_node_get_size(&scene_buffer->node, &buffer_box.width, &buffer_box.height);
+struct scene_update_data {
+ pixman_region32_t *visible;
+ pixman_region32_t *update_region;
+ struct wl_list *outputs;
+};
+
+static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) {
+ if (!pixman_region32_not_empty(damage)) {
+ return;
+ }
+
+ struct wlr_scene_output *scene_output;
+ wl_list_for_each(scene_output, &scene->outputs, link) {
+ pixman_region32_t output_damage;
+ pixman_region32_init(&output_damage);
+ pixman_region32_copy(&output_damage, damage);
+ pixman_region32_translate(&output_damage,
+ -scene_output->x, -scene_output->y);
+ wlr_region_scale(&output_damage, &output_damage,
+ scene_output->output->scale);
+ if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) {
+ wlr_output_schedule_frame(scene_output->output);
+ }
+ pixman_region32_fini(&output_damage);
+ }
+}
+
+static void update_node_update_outputs(struct wlr_scene_node *node,
+ struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ if (node->type != WLR_SCENE_NODE_BUFFER) {
+ return;
+ }
+
+ struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
+
+ struct wlr_box buffer_box;
+ wlr_scene_node_coords(node, &buffer_box.x, &buffer_box.y);
+ scene_node_get_size(node, &buffer_box.width, &buffer_box.height);
int largest_overlap = 0;
scene_buffer->primary_output = NULL;
@@ -262,7 +290,7 @@ static void scene_buffer_update_outputs(struct wlr_scene_buffer *scene_buffer,
// to have a reasonable value. Otherwise, they may get a value that's in
// the middle of a calculation.
struct wlr_scene_output *scene_output;
- wl_list_for_each(scene_output, &scene->outputs, link) {
+ wl_list_for_each(scene_output, outputs, link) {
if (scene_output == ignore) {
continue;
}
@@ -291,7 +319,7 @@ static void scene_buffer_update_outputs(struct wlr_scene_buffer *scene_buffer,
uint64_t old_active = scene_buffer->active_outputs;
scene_buffer->active_outputs = active_outputs;
- wl_list_for_each(scene_output, &scene->outputs, link) {
+ wl_list_for_each(scene_output, outputs, link) {
uint64_t mask = 1ull << scene_output->index;
bool intersects = active_outputs & mask;
bool intersects_before = old_active & mask;
@@ -304,29 +332,118 @@ static void scene_buffer_update_outputs(struct wlr_scene_buffer *scene_buffer,
}
}
-static void _scene_node_update_outputs(struct wlr_scene_node *node,
- int lx, int ly, struct wlr_scene *scene,
- struct wlr_scene_output *ignore) {
- if (node->type == WLR_SCENE_NODE_BUFFER) {
- struct wlr_scene_buffer *scene_buffer =
- wlr_scene_buffer_from_node(node);
- scene_buffer_update_outputs(scene_buffer, lx, ly, scene, ignore);
- } else if (node->type == WLR_SCENE_NODE_TREE) {
+static bool scene_node_update_iterator(struct wlr_scene_node *node,
+ int lx, int ly, void *_data) {
+ struct scene_update_data *data = _data;
+
+ struct wlr_box box = { .x = lx, .y = ly };
+ scene_node_get_size(node, &box.width, &box.height);
+
+ pixman_region32_fini(&node->visible);
+ pixman_region32_init_rect(&node->visible, lx, ly, box.width, box.height);
+
+ update_node_update_outputs(node, data->outputs, NULL);
+
+ return false;
+}
+
+static void scene_node_visibility(struct wlr_scene_node *node,
+ pixman_region32_t *visible) {
+ if (!node->enabled) {
+ return;
+ }
+
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_visibility(child, visible);
+ }
+ return;
+ }
+
+ pixman_region32_union(visible, visible, &node->visible);
+}
+
+static void scene_node_bounds(struct wlr_scene_node *node,
+ int x, int y, pixman_region32_t *visible) {
+ if (!node->enabled) {
+ return;
+ }
+
+ if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &scene_tree->children, link) {
- _scene_node_update_outputs(child, lx + child->x,
- ly + child->y, scene, ignore);
+ scene_node_bounds(child, x + child->x, y + child->y, visible);
}
+ return;
}
+
+ int width, height;
+ scene_node_get_size(node, &width, &height);
+ pixman_region32_union_rect(visible, visible, x, y, width, height);
+}
+
+static void scene_update_region(struct wlr_scene *scene,
+ pixman_region32_t *update_region) {
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ pixman_region32_copy(&visible, update_region);
+
+ struct scene_update_data data = {
+ .visible = &visible,
+ .update_region = update_region,
+ .outputs = &scene->outputs,
+ };
+
+ struct pixman_box32 *region_box = pixman_region32_extents(update_region);
+ struct wlr_box box = {
+ .x = region_box->x1,
+ .y = region_box->y1,
+ .width = region_box->x2 - region_box->x1,
+ .height = region_box->y2 - region_box->y1,
+ };
+
+ // update node visibility and output enter/leave events
+ scene_nodes_in_box(&scene->tree.node, &box, scene_node_update_iterator, &data);
+
+ pixman_region32_fini(&visible);
}
-static void scene_node_update_outputs(struct wlr_scene_node *node,
- struct wlr_scene_output *ignore) {
+static void scene_node_update(struct wlr_scene_node *node,
+ pixman_region32_t *damage) {
struct wlr_scene *scene = scene_node_get_root(node);
- int lx, ly;
- wlr_scene_node_coords(node, &lx, &ly);
- _scene_node_update_outputs(node, lx, ly, scene, ignore);
+
+ int x, y;
+ if (!wlr_scene_node_coords(node, &x, &y)) {
+ if (damage) {
+ scene_update_region(scene, damage);
+ scene_damage_outputs(scene, damage);
+ pixman_region32_fini(damage);
+ }
+
+ return;
+ }
+
+ pixman_region32_t visible;
+ if (!damage) {
+ pixman_region32_init(&visible);
+ scene_node_visibility(node, &visible);
+ damage = &visible;
+ }
+
+ pixman_region32_t update_region;
+ pixman_region32_init(&update_region);
+ pixman_region32_copy(&update_region, damage);
+ scene_node_bounds(node, x, y, &update_region);
+
+ scene_update_region(scene, &update_region);
+ pixman_region32_fini(&update_region);
+
+ scene_node_visibility(node, damage);
+ scene_damage_outputs(scene, damage);
+ pixman_region32_fini(damage);
}
struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
@@ -343,7 +460,7 @@ struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent,
scene_rect->height = height;
memcpy(scene_rect->color, color, sizeof(scene_rect->color));
- scene_node_damage_whole(&scene_rect->node);
+ scene_node_update(&scene_rect->node, NULL);
return scene_rect;
}
@@ -353,10 +470,9 @@ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height)
return;
}
- scene_node_damage_whole(&rect->node);
rect->width = width;
rect->height = height;
- scene_node_damage_whole(&rect->node);
+ scene_node_update(&rect->node, NULL);
}
void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]) {
@@ -365,7 +481,7 @@ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[sta
}
memcpy(rect->color, color, sizeof(rect->color));
- scene_node_damage_whole(&rect->node);
+ scene_node_update(&rect->node, NULL);
}
struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
@@ -386,9 +502,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent,
wl_signal_init(&scene_buffer->events.output_present);
wl_signal_init(&scene_buffer->events.frame_done);
- scene_node_damage_whole(&scene_buffer->node);
-
- scene_node_update_outputs(&scene_buffer->node, NULL);
+ scene_node_update(&scene_buffer->node, NULL);
return scene_buffer;
}
@@ -401,10 +515,6 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff
assert(buffer || !damage);
if (buffer != scene_buffer->buffer) {
- if (!damage) {
- scene_node_damage_whole(&scene_buffer->node);
- }
-
wlr_texture_destroy(scene_buffer->texture);
scene_buffer->texture = NULL;
wlr_buffer_unlock(scene_buffer->buffer);
@@ -415,10 +525,8 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff
scene_buffer->buffer = NULL;
}
- scene_node_update_outputs(&scene_buffer->node, NULL);
-
if (!damage) {
- scene_node_damage_whole(&scene_buffer->node);
+ scene_node_update(&scene_buffer->node, NULL);
}
}
@@ -500,7 +608,7 @@ void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer,
memset(cur, 0, sizeof(*cur));
}
- scene_node_damage_whole(&scene_buffer->node);
+ scene_node_update(&scene_buffer->node, NULL);
}
void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer,
@@ -509,12 +617,9 @@ void wlr_scene_buffer_set_dest_size(struct wlr_scene_buffer *scene_buffer,
return;
}
- scene_node_damage_whole(&scene_buffer->node);
scene_buffer->dst_width = width;
scene_buffer->dst_height = height;
- scene_node_damage_whole(&scene_buffer->node);
-
- scene_node_update_outputs(&scene_buffer->node, NULL);
+ scene_node_update(&scene_buffer->node, NULL);
}
void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer,
@@ -523,11 +628,8 @@ void wlr_scene_buffer_set_transform(struct wlr_scene_buffer *scene_buffer,
return;
}
- scene_node_damage_whole(&scene_buffer->node);
scene_buffer->transform = transform;
- scene_node_damage_whole(&scene_buffer->node);
-
- scene_node_update_outputs(&scene_buffer->node, NULL);
+ scene_node_update(&scene_buffer->node, NULL);
}
void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer,
@@ -594,67 +696,21 @@ static void scale_box(struct wlr_box *box, float scale) {
box->y = round(box->y * scale);
}
-static void _scene_node_damage_whole(struct wlr_scene_node *node,
- struct wlr_scene *scene, int lx, int ly) {
- if (!node->enabled) {
- return;
- }
-
- if (node->type == WLR_SCENE_NODE_TREE) {
- struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
- struct wlr_scene_node *child;
- wl_list_for_each(child, &scene_tree->children, link) {
- _scene_node_damage_whole(child, scene,
- lx + child->x, ly + child->y);
- }
- }
-
- int width, height;
- scene_node_get_size(node, &width, &height);
-
- pixman_region32_fini(&node->visible);
- pixman_region32_init_rect(&node->visible, lx, ly, width, height);
-
- struct wlr_scene_output *scene_output;
- wl_list_for_each(scene_output, &scene->outputs, link) {
- struct wlr_box box = {
- .x = lx - scene_output->x,
- .y = ly - scene_output->y,
- .width = width,
- .height = height,
- };
-
- scale_box(&box, scene_output->output->scale);
-
- if (wlr_damage_ring_add_box(&scene_output->damage_ring, &box)) {
- wlr_output_schedule_frame(scene_output->output);
- }
- }
-}
-
-static void scene_node_damage_whole(struct wlr_scene_node *node) {
- struct wlr_scene *scene = scene_node_get_root(node);
- if (wl_list_empty(&scene->outputs)) {
- return;
- }
-
- int lx, ly;
- if (!wlr_scene_node_coords(node, &lx, &ly)) {
- return;
- }
-
- _scene_node_damage_whole(node, scene, lx, ly);
-}
-
void wlr_scene_node_set_enabled(struct wlr_scene_node *node, bool enabled) {
if (node->enabled == enabled) {
return;
}
- // One of these damage_whole() calls will short-circuit and be a no-op
- scene_node_damage_whole(node);
+ int x, y;
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ if (wlr_scene_node_coords(node, &x, &y)) {
+ scene_node_visibility(node, &visible);
+ }
+
node->enabled = enabled;
- scene_node_damage_whole(node);
+
+ scene_node_update(node, &visible);
}
void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
@@ -662,12 +718,9 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) {
return;
}
- scene_node_damage_whole(node);
node->x = x;
node->y = y;
- scene_node_damage_whole(node);
-
- scene_node_update_outputs(node, NULL);
+ scene_node_update(node, NULL);
}
void wlr_scene_node_place_above(struct wlr_scene_node *node,
@@ -681,9 +734,7 @@ void wlr_scene_node_place_above(struct wlr_scene_node *node,
wl_list_remove(&node->link);
wl_list_insert(&sibling->link, &node->link);
-
- scene_node_damage_whole(node);
- scene_node_damage_whole(sibling);
+ scene_node_update(node, NULL);
}
void wlr_scene_node_place_below(struct wlr_scene_node *node,
@@ -697,9 +748,7 @@ void wlr_scene_node_place_below(struct wlr_scene_node *node,
wl_list_remove(&node->link);
wl_list_insert(sibling->link.prev, &node->link);
-
- scene_node_damage_whole(node);
- scene_node_damage_whole(sibling);
+ scene_node_update(node, NULL);
}
void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) {
@@ -734,15 +783,17 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node,
assert(&ancestor->node != node);
}
- scene_node_damage_whole(node);
+ int x, y;
+ pixman_region32_t visible;
+ pixman_region32_init(&visible);
+ if (wlr_scene_node_coords(node, &x, &y)) {
+ scene_node_visibility(node, &visible);
+ }
wl_list_remove(&node->link);
node->parent = new_parent;
wl_list_insert(new_parent->children.prev, &node->link);
-
- scene_node_damage_whole(node);
-
- scene_node_update_outputs(node, NULL);
+ scene_node_update(node, &visible);
}
bool wlr_scene_node_coords(struct wlr_scene_node *node,
@@ -1026,13 +1077,28 @@ static const struct wlr_addon_interface output_addon_impl = {
.destroy = scene_output_handle_destroy,
};
+static void scene_node_output_update(struct wlr_scene_node *node,
+ struct wl_list *outputs, struct wlr_scene_output *ignore) {
+ if (node->type == WLR_SCENE_NODE_TREE) {
+ struct wlr_scene_tree *scene_tree = scene_tree_from_node(node);
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &scene_tree->children, link) {
+ scene_node_output_update(child, outputs, ignore);
+ }
+ return;
+ }
+
+ update_node_update_outputs(node, outputs, ignore);
+}
+
static void scene_output_update_geometry(struct wlr_scene_output *scene_output) {
int width, height;
wlr_output_transformed_resolution(scene_output->output, &width, &height);
wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height);
wlr_output_schedule_frame(scene_output->output);
- scene_node_update_outputs(&scene_output->scene->tree.node, NULL);
+ scene_node_output_update(&scene_output->scene->tree.node,
+ &scene_output->scene->outputs, NULL);
}
static void scene_output_handle_commit(struct wl_listener *listener, void *data) {
@@ -1131,7 +1197,8 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wlr_signal_emit_safe(&scene_output->events.destroy, NULL);
- scene_node_update_outputs(&scene_output->scene->tree.node, scene_output);
+ scene_node_output_update(&scene_output->scene->tree.node,
+ &scene_output->scene->outputs, scene_output);
struct highlight_region *damage, *tmp_damage;
wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) {