wlr_scene: Only consider visible parts of the node when culling background

Originally, I thought that we could safely subtract opaque regions
from the background even if the black rect optimization was kicking in.
This is wrong because a scene node that isn't fully occluded will still
appear in the render list even if its partially under a black rect. We
need to make sure that while culling the background, we only consider
opaque regions that are also visible. This will fix the black rect
optimization with the background.
This commit is contained in:
Alexander Orzechowski 2022-08-26 19:07:52 -04:00
parent 6a11256cd0
commit fa7d2cb8d6

View file

@ -221,8 +221,8 @@ 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); return _scene_nodes_in_box(node, box, iterator, user_data, x, y);
} }
static void scene_node_cull_hidden(struct wlr_scene_node *node, int x, int y, static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y,
pixman_region32_t *visible) { pixman_region32_t *opaque) {
if (node->type == WLR_SCENE_NODE_RECT) { if (node->type == WLR_SCENE_NODE_RECT) {
struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); struct wlr_scene_rect *scene_rect = scene_rect_from_node(node);
if (scene_rect->color[3] != 1) { if (scene_rect->color[3] != 1) {
@ -236,19 +236,16 @@ static void scene_node_cull_hidden(struct wlr_scene_node *node, int x, int y,
} }
if (!buffer_is_opaque(scene_buffer->buffer)) { if (!buffer_is_opaque(scene_buffer->buffer)) {
pixman_region32_translate(visible, -x, -y); pixman_region32_copy(opaque, &scene_buffer->opaque_region);
pixman_region32_subtract(visible, visible, &scene_buffer->opaque_region); pixman_region32_translate(opaque, x, y);
pixman_region32_translate(visible, x, y);
return; return;
} }
} }
int width, height; int width, height;
scene_node_get_size(node, &width, &height); scene_node_get_size(node, &width, &height);
pixman_region32_t opaque; pixman_region32_fini(opaque);
pixman_region32_init_rect(&opaque, x, y, width, height); pixman_region32_init_rect(opaque, x, y, width, height);
pixman_region32_subtract(visible, visible, &opaque);
pixman_region32_fini(&opaque);
} }
struct scene_update_data { struct scene_update_data {
@ -370,7 +367,11 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node,
lx, ly, box.width, box.height); lx, ly, box.width, box.height);
if (data->calculate_visibility) { if (data->calculate_visibility) {
scene_node_cull_hidden(node, lx, ly, data->visible); pixman_region32_t opaque;
pixman_region32_init(&opaque);
scene_node_opaque_region(node, lx, ly, &opaque);
pixman_region32_subtract(data->visible, data->visible, &opaque);
pixman_region32_fini(&opaque);
} }
update_node_update_outputs(node, data->outputs, NULL); update_node_update_outputs(node, data->outputs, NULL);
@ -1507,7 +1508,20 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
struct wlr_scene_node *node = list_data[i]; struct wlr_scene_node *node = list_data[i];
int x, y; int x, y;
wlr_scene_node_coords(node, &x, &y); wlr_scene_node_coords(node, &x, &y);
scene_node_cull_hidden(node, x, y, &background);
// We must only cull opaque regions that are visible by the node.
// The node's visibility will have the knowledge of a black rect
// that may have been omitted from the render list via the black
// rect optimization. In order to ensure we don't cull background
// rendering in that black rect region, consider the node's visibility.
pixman_region32_t opaque;
pixman_region32_init(&opaque);
scene_node_opaque_region(node, x, y, &opaque);
pixman_region32_intersect(&opaque, &opaque, &node->visible);
wlr_region_scale(&opaque, &opaque, scene_output->output->scale);
pixman_region32_subtract(&background, &background, &opaque);
pixman_region32_fini(&opaque);
} }
} }