Merge branch 'output-layers-v2' into 'master'

wlr_scene: Implement output layers

See merge request wlroots/wlroots!4015
This commit is contained in:
Alexander Orzechowski 2023-04-04 18:51:31 +00:00
commit 4a12445dab
4 changed files with 435 additions and 91 deletions

View file

@ -15,4 +15,15 @@ void array_remove_at(struct wl_array *arr, size_t offset, size_t size);
*/ */
bool array_realloc(struct wl_array *arr, size_t size); bool array_realloc(struct wl_array *arr, size_t size);
/**
* Returns a pointer to the first valid element in a reversed array.
*/
void *array_reversed_start(struct wl_array *arr);
/**
* Adds a new element to the array inserting them starting from a higher
* memory address effectively inserting them in reverse order.
*/
void *array_reversed_add(struct wl_array *arr, size_t size);
#endif #endif

View file

@ -106,6 +106,8 @@ struct wlr_scene {
enum wlr_scene_debug_damage_option debug_damage_option; enum wlr_scene_debug_damage_option debug_damage_option;
bool direct_scanout; bool direct_scanout;
bool calculate_visibility; bool calculate_visibility;
struct wl_array render_list;
}; };
/** A scene-graph node displaying a single surface. */ /** A scene-graph node displaying a single surface. */
@ -201,7 +203,7 @@ struct wlr_scene_output {
struct wl_list damage_highlight_regions; struct wl_list damage_highlight_regions;
struct wl_array render_list; struct wl_array output_layers;
}; };
/** A layer shell scene helper */ /** A layer shell scene helper */

View file

@ -8,6 +8,7 @@
#include <wlr/types/wlr_damage_ring.h> #include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h> #include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_output_layer.h>
#include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_scene.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -122,6 +123,8 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) {
wl_list_remove(&scene->presentation_destroy.link); wl_list_remove(&scene->presentation_destroy.link);
wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); wl_list_remove(&scene->linux_dmabuf_v1_destroy.link);
wl_array_release(&scene->render_list);
} else { } else {
assert(node->parent); assert(node->parent);
} }
@ -1335,7 +1338,8 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_damage.link);
wl_list_remove(&scene_output->output_needs_frame.link); wl_list_remove(&scene_output->output_needs_frame.link);
wl_array_release(&scene_output->render_list); wl_array_release(&scene_output->output_layers);
free(scene_output); free(scene_output);
} }
@ -1363,66 +1367,83 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output,
scene_output_update_geometry(scene_output); scene_output_update_geometry(scene_output);
} }
static bool scene_node_invisible(struct wlr_scene_node *node) { struct render_list_entry {
struct wlr_scene_node *node;
struct wlr_output_layer_state *state;
bool sent_feedback;
bool visible;
};
static bool scene_construct_render_list(struct wlr_scene_node *node,
struct wl_array *render_list, struct wlr_box *box, bool calculate_visibility,
bool enabled) {
enabled = enabled && node->enabled;
if (node->type == WLR_SCENE_NODE_TREE) { 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_reverse(child, &scene_tree->children, link) {
if (!scene_construct_render_list(child, render_list, box,
calculate_visibility, enabled)) {
return false;
}
}
return true; return true;
} else if (node->type == WLR_SCENE_NODE_RECT) { }
struct render_list_entry *entry = wl_array_add(render_list, sizeof(*entry));
if (!entry) {
return false;
}
memset(entry, 0, sizeof(*entry));
entry->node = node;
if (!enabled) {
return true;
}
if (node->type == WLR_SCENE_NODE_RECT) {
struct wlr_scene_rect *rect = scene_rect_from_node(node); struct wlr_scene_rect *rect = scene_rect_from_node(node);
return rect->color[3] == 0.f; if (rect->color[3] == 0.f) {
return true;
}
} else if (node->type == WLR_SCENE_NODE_BUFFER) { } else if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
return buffer->buffer == NULL; if (!buffer->buffer) {
} return true;
}
return false;
}
struct render_list_constructor_data {
struct wlr_box box;
struct wl_array *render_list;
bool calculate_visibility;
};
static bool construct_render_list_iterator(struct wlr_scene_node *node,
int lx, int ly, void *_data) {
struct render_list_constructor_data *data = _data;
if (scene_node_invisible(node)) {
return false;
} }
// while rendering, the background should always be black. // while rendering, the background should always be black.
// If we see a black rect, we can ignore rendering everything under the rect // If we see a black rect, we can ignore rendering everything under the rect
// and even the rect itself. // and even the rect itself.
if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { if (node->type == WLR_SCENE_NODE_RECT && calculate_visibility) {
struct wlr_scene_rect *rect = scene_rect_from_node(node); struct wlr_scene_rect *rect = scene_rect_from_node(node);
float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f };
if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { if (memcmp(rect->color, black, sizeof(float) * 4) == 0) {
return false; return true;
} }
} }
pixman_region32_t intersection; pixman_region32_t intersection;
pixman_region32_init(&intersection); pixman_region32_init(&intersection);
pixman_region32_intersect_rect(&intersection, &node->visible, pixman_region32_intersect_rect(&intersection, &node->visible,
data->box.x, data->box.y, box->x, box->y, box->width, box->height);
data->box.width, data->box.height);
if (!pixman_region32_not_empty(&intersection)) { if (!pixman_region32_not_empty(&intersection)) {
pixman_region32_fini(&intersection); pixman_region32_fini(&intersection);
return false; return true;
} }
pixman_region32_fini(&intersection); pixman_region32_fini(&intersection);
struct wlr_scene_node **entry = wl_array_add(data->render_list, entry->visible = true;
sizeof(struct wlr_scene_node *)); return true;
if (entry) {
*entry = node;
}
return false;
} }
static void get_frame_damage(struct wlr_scene_output *scene_output, pixman_region32_t *frame_damage) { static void get_frame_damage(struct wlr_scene_output *scene_output, pixman_region32_t *frame_damage) {
@ -1471,10 +1492,8 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene,
wlr_linux_dmabuf_feedback_v1_finish(&feedback); wlr_linux_dmabuf_feedback_v1_finish(&feedback);
} }
static bool scene_buffer_can_consider_direct_scanout(struct wlr_scene_buffer *buffer, static bool scene_buffer_try_direct_scanout(struct render_list_entry *entry,
const struct wlr_scene_output *scene_output) { struct wlr_scene_output *scene_output) {
struct wlr_scene_node *node = &buffer->node;
if (!scene_output->scene->direct_scanout) { if (!scene_output->scene->direct_scanout) {
return false; return false;
} }
@ -1486,10 +1505,13 @@ static bool scene_buffer_can_consider_direct_scanout(struct wlr_scene_buffer *bu
return false; return false;
} }
struct wlr_scene_node *node = entry->node;
if (node->type != WLR_SCENE_NODE_BUFFER) { if (node->type != WLR_SCENE_NODE_BUFFER) {
return false; return false;
} }
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
struct wlr_fbox default_box = {0}; struct wlr_fbox default_box = {0};
if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { if (buffer->transform & WL_OUTPUT_TRANSFORM_90) {
default_box.width = buffer->buffer->height; default_box.width = buffer->buffer->height;
@ -1523,11 +1545,16 @@ static bool scene_buffer_can_consider_direct_scanout(struct wlr_scene_buffer *bu
return false; return false;
} }
return true; if (buffer->primary_output == scene_output) {
} struct wlr_linux_dmabuf_feedback_v1_init_options options = {
.main_renderer = scene_output->output->renderer,
.scanout_primary_output = scene_output->output,
};
scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
entry->sent_feedback = true;
}
static bool scene_buffer_try_direct_scanout(struct wlr_scene_buffer *buffer,
struct wlr_scene_output *scene_output) {
struct wlr_output_state state = { struct wlr_output_state state = {
.committed = WLR_OUTPUT_STATE_BUFFER, .committed = WLR_OUTPUT_STATE_BUFFER,
.buffer = buffer->buffer, .buffer = buffer->buffer,
@ -1550,6 +1577,260 @@ static bool scene_buffer_try_direct_scanout(struct wlr_scene_buffer *buffer,
return true; return true;
} }
static bool scene_buffer_can_be_output_layer(struct wlr_scene_buffer *buffer,
struct wlr_scene_output *scene_output) {
struct wlr_fbox default_box = {0};
if (buffer->transform & WL_OUTPUT_TRANSFORM_90) {
default_box.width = buffer->buffer->height;
default_box.height = buffer->buffer->width;
} else {
default_box.width = buffer->buffer->width;
default_box.height = buffer->buffer->height;
}
if (!wlr_fbox_empty(&buffer->src_box) &&
!wlr_fbox_equal(&buffer->src_box, &default_box)) {
return false;
}
if (buffer->transform != scene_output->output->transform) {
return false;
}
return true;
}
struct wlr_scene_node_output_layer {
struct wlr_addon addon;
struct wlr_output_layer *layer;
struct wlr_scene_buffer *buffer;
struct wlr_scene_output *output;
struct render_list_entry *current_render_entry;
struct wl_listener layer_feedback;
struct wl_listener output_destroy;
};
static void node_output_layer_destroy(struct wlr_scene_node_output_layer *layer) {
wlr_output_layer_destroy(layer->layer);
wlr_addon_finish(&layer->addon);
wl_list_remove(&layer->output_destroy.link);
free(layer);
}
static void handle_output_layer_addon_finish(struct wlr_addon *addon) {
struct wlr_scene_node_output_layer *layer = wl_container_of(addon, layer, addon);
node_output_layer_destroy(layer);
}
static void handle_output_layer_output_destroy(struct wl_listener *listener,
void *data) {
struct wlr_scene_node_output_layer *layer = wl_container_of(listener, layer, output_destroy);
wl_list_remove(&layer->layer_feedback.link);
node_output_layer_destroy(layer);
}
static const struct wlr_addon_interface node_addon_output_layer = {
.name = "wlr_scene_output_layer",
.destroy = handle_output_layer_addon_finish,
};
static struct wlr_scene_node_output_layer *scene_node_get_output_layer(
struct wlr_scene_node *node, struct wlr_scene_output *output) {
struct wlr_addon *addon = wlr_addon_find(
&node->addons, output, &node_addon_output_layer);
if (!addon) {
return NULL;
}
struct wlr_scene_node_output_layer *layer = wl_container_of(addon, layer, addon);
return layer;
}
static void output_handle_layer_feedback(struct wl_listener *listener, void *data) {
struct wlr_scene_node_output_layer *layer =
wl_container_of(listener, layer, layer_feedback);
struct wlr_output_layer_feedback_event *event = data;
struct wlr_linux_dmabuf_feedback_v1_init_options options = {
.main_renderer = layer->buffer->primary_output->output->renderer,
.output_layer_feedback_event = event,
};
if (layer->buffer->primary_output == layer->output) {
scene_buffer_send_dmabuf_feedback(scene_node_get_root(&layer->buffer->node),
layer->buffer, &options);
layer->current_render_entry->sent_feedback = true;
}
}
static struct wlr_scene_node_output_layer *scene_buffer_create_output_layer(
struct wlr_scene_buffer *buffer, struct wlr_scene_output *output) {
struct wlr_scene_node_output_layer *layer = calloc(1, sizeof(*layer));
if (!layer) {
return NULL;
}
layer->layer = wlr_output_layer_create(output->output);
if (!layer->layer) {
free(layer);
return NULL;
}
layer->layer_feedback.notify = output_handle_layer_feedback;
wl_signal_add(&layer->layer->events.feedback, &layer->layer_feedback);
layer->output_destroy.notify = handle_output_layer_output_destroy;
wl_signal_add(&output->output->events.destroy, &layer->output_destroy);
layer->buffer = buffer;
layer->output = output;
wlr_addon_init(&layer->addon, &buffer->node.addons, output, &node_addon_output_layer);
return layer;
}
static bool scene_submit_output_layers(struct wlr_scene_output *scene_output,
struct wl_array *render_list) {
// if we render software cursors, that means they will be a part of the
// primary plane. If we render output layers, it means we could render
// over the cursor. A lock will also be registered if there is screen share.
// Screen share cannot use output layers.
if (scene_output->output->software_cursor_locks) {
return false;
}
if (scene_output->scene->debug_damage_option ==
WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
return false;
}
struct render_list_entry *list_data = render_list->data;
int list_len = render_list->size / sizeof(*list_data);
scene_output->output_layers.size = 0;
if (!array_realloc(&scene_output->output_layers,
list_len * sizeof(struct wlr_output_layer_state))) {
return false;
}
// area of that the base layer must render (such as rects)
pixman_region32_t base_layer_area;
pixman_region32_init(&base_layer_area);
for (int i = 0; i < list_len; i++) {
struct render_list_entry *entry = &list_data[i];
struct wlr_scene_node *node = entry->node;
if (node->type != WLR_SCENE_NODE_BUFFER) {
continue;
}
struct wlr_box box;
wlr_scene_node_coords(node, &box.x, &box.y);
scene_node_get_size(node, &box.width, &box.height);
box.x = floor(box.x * scene_output->output->scale) - scene_output->x;
box.y = floor(box.y * scene_output->output->scale) - scene_output->y;
box.width = ceil(box.width * scene_output->output->scale);
box.height = ceil(box.height * scene_output->output->scale);
struct wlr_scene_node_output_layer *layer =
scene_node_get_output_layer(node, scene_output);
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
if (entry->visible && !scene_buffer_can_be_output_layer(buffer, scene_output)) {
pixman_region32_union_rect(&base_layer_area, &base_layer_area,
box.x, box.y, box.width, box.height);
if (layer) {
node_output_layer_destroy(layer);
}
continue;
}
if (entry->visible && !layer) {
layer = scene_buffer_create_output_layer(buffer, scene_output);
if (!layer) {
pixman_region32_union_rect(&base_layer_area, &base_layer_area,
box.x, box.y, box.width, box.height);
continue;
}
}
if (layer) {
// we preallocted the array length, this should never return NULL.
entry->state = array_reversed_add(&scene_output->output_layers, sizeof(*entry->state));
layer->current_render_entry = entry;
*entry->state = (struct wlr_output_layer_state){
.layer = layer->layer,
.buffer = NULL,
.dst_box = box,
};
}
if (!entry->visible) {
continue;
}
pixman_region32_t intersection;
pixman_region32_init(&intersection);
pixman_region32_intersect_rect(&intersection, &base_layer_area,
box.x, box.y, box.width, box.height);
if (pixman_region32_not_empty(&intersection)) {
pixman_region32_union_rect(&base_layer_area, &base_layer_area,
box.x, box.y, box.width, box.height);
pixman_region32_fini(&intersection);
continue;
}
pixman_region32_fini(&intersection);
// only consider nodes that are in the damage
pixman_region32_init(&intersection);
pixman_region32_intersect_rect(&intersection, &scene_output->damage_ring.current,
box.x, box.y, box.width, box.height);
if (!pixman_region32_not_empty(&intersection)) {
pixman_region32_union_rect(&base_layer_area, &base_layer_area,
box.x, box.y, box.width, box.height);
pixman_region32_fini(&intersection);
continue;
}
if (!buffer_is_opaque(buffer->buffer)) {
// With transparent nodes, we can't get away with just having the
// node partially in the damage or else the user can see through
// the node to find an older version of the node software composited
// in the base layer.
pixman_region32_t node_box;
pixman_region32_init_rect(&node_box, box.x, box.y, box.width, box.height);
pixman_region32_subtract(&intersection, &node_box, &intersection);
pixman_region32_fini(&node_box);
if (pixman_region32_not_empty(&intersection)) {
pixman_region32_union_rect(&base_layer_area, &base_layer_area,
box.x, box.y, box.width, box.height);
pixman_region32_fini(&intersection);
continue;
}
}
pixman_region32_fini(&intersection);
entry->state->buffer = buffer->buffer;
}
pixman_region32_fini(&base_layer_area);
wlr_output_set_layers(scene_output->output, array_reversed_start(&scene_output->output_layers),
scene_output->output_layers.size / sizeof(struct wlr_output_layer_state));
if (!wlr_output_test(scene_output->output)) {
return false;
}
return true;
}
bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
struct wlr_output *output = scene_output->output; struct wlr_output *output = scene_output->output;
enum wlr_scene_debug_damage_option debug_damage = enum wlr_scene_debug_damage_option debug_damage =
@ -1563,50 +1844,58 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
return true; return true;
} }
struct render_list_constructor_data list_con = { struct wl_array *render_list = &scene_output->scene->render_list;
.box = { .x = scene_output->x, .y = scene_output->y }, render_list->size = 0;
.render_list = &scene_output->render_list,
.calculate_visibility = scene_output->scene->calculate_visibility,
};
wlr_output_effective_resolution(output,
&list_con.box.width, &list_con.box.height);
list_con.render_list->size = 0; struct wlr_box box = { .x = scene_output->x, .y = scene_output->y };
scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, wlr_output_effective_resolution(output, &box.width, &box.height);
construct_render_list_iterator, &list_con); if (!scene_construct_render_list(&scene_output->scene->tree.node, render_list,
array_realloc(list_con.render_list, list_con.render_list->size); &box, scene_output->scene->calculate_visibility, true)) {
return false;
}
array_realloc(render_list, render_list->size);
int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); scene_submit_output_layers(scene_output, render_list);
struct wlr_scene_node **list_data = list_con.render_list->data;
bool sent_direct_scanout_feedback = false; struct render_list_entry *list_data = render_list->data;
int list_len = render_list->size / sizeof(*list_data);
// if there is only one thing to render let's see if that thing can be struct render_list_entry *scanout = NULL;
// directly scanned out int render_count = 0;
bool scanout = false; pixman_region32_t forward_damage;
if (list_len == 1) { pixman_region32_init(&forward_damage);
struct wlr_scene_node *node = list_data[0];
if (node->type == WLR_SCENE_NODE_BUFFER) { for (int i = list_len - 1; i >= 0; i--) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); struct render_list_entry *entry = &list_data[i];
if (!entry->visible) {
if (scene_buffer_can_consider_direct_scanout(buffer, scene_output)) { continue;
if (buffer->primary_output == scene_output) {
struct wlr_linux_dmabuf_feedback_v1_init_options options = {
.main_renderer = output->renderer,
.scanout_primary_output = output,
};
scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options);
sent_direct_scanout_feedback = true;
}
scanout = scene_buffer_try_direct_scanout(buffer, scene_output);
}
} }
if (entry->state && entry->state->accepted && entry->state->buffer) {
// we need to damage the output here for the next frame because since
// the we're using output layers here. The contents won't be part
// of the primary plane and will therefore have stale contents.
pixman_region32_union(&forward_damage, &forward_damage, &entry->node->visible);
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
wl_signal_emit_mutable(&buffer->events.output_present, scene_output);
continue;
}
scanout = entry;
render_count++;
} }
if (scene_output->prev_scanout != scanout) { pixman_region32_translate(&forward_damage, -scene_output->x, -scene_output->y);
scale_output_damage(&forward_damage, scene_output->output->scale);
pixman_region32_intersect(&forward_damage, &forward_damage, &scene_output->damage_ring.current);
if (render_count != 1 || !scene_buffer_try_direct_scanout(scanout, scene_output)) {
scanout = NULL;
}
if (scene_output->prev_scanout == !scanout) {
scene_output->prev_scanout = scanout; scene_output->prev_scanout = scanout;
wlr_log(WLR_DEBUG, "Direct scan-out %s", wlr_log(WLR_DEBUG, "Direct scan-out %s",
scanout ? "enabled" : "disabled"); scanout ? "enabled" : "disabled");
@ -1617,11 +1906,12 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
} }
if (scanout) { if (scanout) {
struct wlr_scene_node *node = list_data[0]; struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(scanout->node);
assert(node->type == WLR_SCENE_NODE_BUFFER);
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
wl_signal_emit_mutable(&buffer->events.output_present, scene_output); wl_signal_emit_mutable(&buffer->events.output_present, scene_output);
wlr_damage_ring_add(&scene_output->damage_ring, &forward_damage);
pixman_region32_fini(&forward_damage);
return true; return true;
} }
@ -1629,6 +1919,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
wlr_damage_ring_add_whole(&scene_output->damage_ring); wlr_damage_ring_add_whole(&scene_output->damage_ring);
} }
struct timespec now; struct timespec now;
if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) {
struct wl_list *regions = &scene_output->damage_highlight_regions; struct wl_list *regions = &scene_output->damage_highlight_regions;
@ -1670,6 +1961,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
int buffer_age; int buffer_age;
if (!wlr_output_attach_render(output, &buffer_age)) { if (!wlr_output_attach_render(output, &buffer_age)) {
pixman_region32_fini(&forward_damage);
return false; return false;
} }
@ -1679,6 +1971,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
buffer_age, &damage); buffer_age, &damage);
if (!wlr_renderer_begin(renderer, output->width, output->height)) { if (!wlr_renderer_begin(renderer, output->width, output->height)) {
pixman_region32_fini(&forward_damage);
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
wlr_output_rollback(output); wlr_output_rollback(output);
return false; return false;
@ -1695,9 +1988,13 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
float output_scale = scene_output->output->scale; float output_scale = scene_output->output->scale;
for (int i = list_len - 1; i >= 0; i--) { for (int i = list_len - 1; i >= 0; i--) {
struct wlr_scene_node *node = list_data[i]; struct render_list_entry *entry = &list_data[i];
if (!entry->visible) {
continue;
}
int x, y; int x, y;
wlr_scene_node_coords(node, &x, &y); wlr_scene_node_coords(entry->node, &x, &y);
// We must only cull opaque regions that are visible by the node. // We must only cull opaque regions that are visible by the node.
// The node's visibility will have the knowledge of a black rect // The node's visibility will have the knowledge of a black rect
@ -1706,8 +2003,8 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
// rendering in that black rect region, consider the node's visibility. // rendering in that black rect region, consider the node's visibility.
pixman_region32_t opaque; pixman_region32_t opaque;
pixman_region32_init(&opaque); pixman_region32_init(&opaque);
scene_node_opaque_region(node, x, y, &opaque); scene_node_opaque_region(entry->node, x, y, &opaque);
pixman_region32_intersect(&opaque, &opaque, &node->visible); pixman_region32_intersect(&opaque, &opaque, &entry->node->visible);
pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y);
wlr_region_scale(&opaque, &opaque, output_scale); wlr_region_scale(&opaque, &opaque, output_scale);
@ -1733,13 +2030,21 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
pixman_region32_fini(&background); pixman_region32_fini(&background);
for (int i = list_len - 1; i >= 0; i--) { for (int i = list_len - 1; i >= 0; i--) {
struct wlr_scene_node *node = list_data[i]; struct render_list_entry *entry = &list_data[i];
scene_node_render(node, scene_output, &damage); if (!entry->visible) {
continue;
}
if (node->type == WLR_SCENE_NODE_BUFFER) { if (entry->state && entry->state->accepted && entry->state->buffer) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); continue;
}
if (buffer->primary_output == scene_output && !sent_direct_scanout_feedback) { scene_node_render(entry->node, scene_output, &damage);
if (entry->node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
if (buffer->primary_output == scene_output && !entry->sent_feedback) {
struct wlr_linux_dmabuf_feedback_v1_init_options options = { struct wlr_linux_dmabuf_feedback_v1_init_options options = {
.main_renderer = output->renderer, .main_renderer = output->renderer,
.scanout_primary_output = NULL, .scanout_primary_output = NULL,
@ -1797,6 +2102,9 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) {
wlr_output_schedule_frame(scene_output->output); wlr_output_schedule_frame(scene_output->output);
} }
wlr_damage_ring_add(&scene_output->damage_ring, &forward_damage);
pixman_region32_fini(&forward_damage);
return success; return success;
} }

View file

@ -38,3 +38,26 @@ bool array_realloc(struct wl_array *arr, size_t size) {
arr->alloc = alloc; arr->alloc = alloc;
return true; return true;
} }
void *array_reversed_start(struct wl_array *arr) {
char *data = arr->data;
return data + arr->alloc - arr->size;
}
void *array_reversed_add(struct wl_array *arr, size_t size) {
if (arr->size + size > arr->alloc) {
size_t new_alloc = arr->alloc * 2;
char *new = malloc(new_alloc);
if (!new) {
return NULL;
}
memcpy(new + (new_alloc - arr->size), array_reversed_start(arr), arr->size);
free(arr->data);
arr->data = new;
arr->alloc = new_alloc;
}
arr->size += size;
return array_reversed_start(arr);
}