Merge branch 'scene-presentation' into 'master'

wlr_scene: Refactor presentation events

See merge request wlroots/wlroots!4083
This commit is contained in:
Alexander Orzechowski 2023-04-25 10:02:27 +00:00
commit 9e865356fb
5 changed files with 172 additions and 98 deletions

View file

@ -96,4 +96,12 @@ void wlr_presentation_surface_sampled_on_output(
struct wlr_presentation *presentation, struct wlr_surface *surface, struct wlr_presentation *presentation, struct wlr_surface *surface,
struct wlr_output *output); struct wlr_output *output);
/**
* Send the presentation event immediately to this surface if the surface is
* asking for presentation events. This must be called during the present handler
* of an output.
*/
void wlr_presentation_send_presented(struct wlr_presentation *presentation,
struct wlr_surface *surface, struct wlr_presentation_event *event);
#endif #endif

View file

@ -149,7 +149,7 @@ struct wlr_scene_buffer {
struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event
struct wl_signal output_enter; // struct wlr_scene_output struct wl_signal output_enter; // struct wlr_scene_output
struct wl_signal output_leave; // struct wlr_scene_output struct wl_signal output_leave; // struct wlr_scene_output
struct wl_signal output_present; // struct wlr_scene_output struct wl_signal output_present; // struct wlr_output_event_present
struct wl_signal frame_done; // struct timespec struct wl_signal frame_done; // struct timespec
} events; } events;
@ -198,6 +198,7 @@ struct wlr_scene_output {
struct wl_listener output_commit; struct wl_listener output_commit;
struct wl_listener output_damage; struct wl_listener output_damage;
struct wl_listener output_needs_frame; struct wl_listener output_needs_frame;
struct wl_listener output_present;
struct wl_list damage_highlight_regions; struct wl_list damage_highlight_regions;

View file

@ -40,15 +40,23 @@ static void handle_scene_buffer_output_present(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct wlr_scene_surface *surface = struct wlr_scene_surface *surface =
wl_container_of(listener, surface, output_present); wl_container_of(listener, surface, output_present);
struct wlr_scene_output *scene_output = data; struct wlr_output_event_present *output_event = data;
if (surface->buffer->primary_output == scene_output) { if (surface->buffer->primary_output->output == output_event->output) {
struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); struct wlr_scene *root = scene_node_get_root(&surface->buffer->node);
struct wlr_presentation *presentation = root->presentation; struct wlr_presentation *presentation = root->presentation;
if (presentation) { if (presentation) {
wlr_presentation_surface_sampled_on_output( struct wlr_presentation_event event = {
presentation, surface->surface, scene_output->output); .output = output_event->output,
.tv_sec = (uint64_t)output_event->when->tv_sec,
.tv_nsec = (uint32_t)output_event->when->tv_nsec,
.refresh = (uint32_t)output_event->refresh,
.seq = (uint64_t)output_event->seq,
.flags = output_event->flags,
};
wlr_presentation_send_presented(presentation, surface->surface, &event);
} }
} }
} }

View file

@ -1100,8 +1100,17 @@ static void render_texture(struct wlr_output *output,
} }
} }
static void scene_node_render(struct wlr_scene_node *node, struct render_list_entry {
struct wlr_scene_node *node;
bool sent_feedback;
bool visible;
bool presented;
};
static void scene_render_list_entry_render(struct render_list_entry *entry,
struct wlr_scene_output *scene_output, pixman_region32_t *damage) { struct wlr_scene_output *scene_output, pixman_region32_t *damage) {
struct wlr_scene_node *node = entry->node;
int x, y; int x, y;
wlr_scene_node_coords(node, &x, &y); wlr_scene_node_coords(node, &x, &y);
x -= scene_output->x; x -= scene_output->x;
@ -1120,6 +1129,8 @@ static void scene_node_render(struct wlr_scene_node *node,
return; return;
} }
entry->presented = true;
struct wlr_box dst_box = { struct wlr_box dst_box = {
.x = x, .x = x,
.y = y, .y = y,
@ -1156,8 +1167,6 @@ static void scene_node_render(struct wlr_scene_node *node,
render_texture(output, &render_region, texture, &scene_buffer->src_box, render_texture(output, &render_region, texture, &scene_buffer->src_box,
&dst_box, matrix); &dst_box, matrix);
wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output);
break; break;
} }
@ -1261,6 +1270,26 @@ static void scene_output_handle_needs_frame(struct wl_listener *listener, void *
wlr_output_schedule_frame(scene_output->output); wlr_output_schedule_frame(scene_output->output);
} }
static void scene_output_handle_present(struct wl_listener *listener, void *data) {
struct wlr_scene_output *scene_output = wl_container_of(listener,
scene_output, output_present);
struct wlr_output_event_present *output_event = data;
struct wl_array *render_list = &scene_output->render_list;
struct render_list_entry *list_data = render_list->data;
int list_len = render_list->size / sizeof(*list_data);
for (int i = list_len - 1; i >= 0; i--) {
struct render_list_entry *entry = &list_data[i];
if (!entry->presented || entry->node->type != WLR_SCENE_NODE_BUFFER) {
continue;
}
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node);
wl_signal_emit_mutable(&buffer->events.output_present, output_event);
}
}
struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
struct wlr_output *output) { struct wlr_output *output) {
struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output)); struct wlr_scene_output *scene_output = calloc(1, sizeof(*scene_output));
@ -1303,6 +1332,9 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene,
scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; scene_output->output_needs_frame.notify = scene_output_handle_needs_frame;
wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame);
scene_output->output_present.notify = scene_output_handle_present;
wl_signal_add(&output->events.present, &scene_output->output_present);
scene_output_update_geometry(scene_output); scene_output_update_geometry(scene_output);
return scene_output; return scene_output;
@ -1335,6 +1367,7 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) {
wl_list_remove(&scene_output->output_commit.link); wl_list_remove(&scene_output->output_commit.link);
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_list_remove(&scene_output->output_present.link);
wl_array_release(&scene_output->render_list); wl_array_release(&scene_output->render_list);
free(scene_output); free(scene_output);
@ -1364,66 +1397,76 @@ 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) { 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) {
@ -1472,10 +1515,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;
} }
@ -1487,13 +1528,16 @@ static bool scene_buffer_can_consider_direct_scanout(struct wlr_scene_buffer *bu
return false; return false;
} }
if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) {
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;
} }
if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
return false;
}
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) {
@ -1528,11 +1572,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,
@ -1542,7 +1591,7 @@ static bool scene_buffer_try_direct_scanout(struct wlr_scene_buffer *buffer,
return false; return false;
} }
wl_signal_emit_mutable(&buffer->events.output_present, scene_output); entry->presented = true;
state.committed |= WLR_OUTPUT_STATE_DAMAGE; state.committed |= WLR_OUTPUT_STATE_DAMAGE;
get_frame_damage(scene_output, &state.damage); get_frame_damage(scene_output, &state.damage);
@ -1570,50 +1619,38 @@ 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->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 *); struct render_list_entry *list_data = render_list->data;
struct wlr_scene_node **list_data = list_con.render_list->data; int list_len = render_list->size / sizeof(*list_data);
bool sent_direct_scanout_feedback = false; struct render_list_entry *scanout = NULL;
int render_count = 0;
// if there is only one thing to render let's see if that thing can be for (int i = list_len - 1; i >= 0; i--) {
// directly scanned out struct render_list_entry *entry = &list_data[i];
bool scanout = false; if (!entry->visible) {
if (list_len == 1) { continue;
struct wlr_scene_node *node = list_data[0];
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
if (scene_buffer_can_consider_direct_scanout(buffer, scene_output)) {
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);
}
} }
scanout = entry;
render_count++;
} }
if (scene_output->prev_scanout != scanout) { 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");
@ -1697,9 +1734,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
@ -1708,8 +1749,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);
@ -1735,13 +1776,17 @@ 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) { scene_render_list_entry_render(entry, scene_output, &damage);
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
if (buffer->primary_output == scene_output && !sent_direct_scanout_feedback) { 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,

View file

@ -325,3 +325,15 @@ void wlr_presentation_surface_sampled_on_output(
feedback->output_destroy.notify = feedback_handle_output_destroy; feedback->output_destroy.notify = feedback_handle_output_destroy;
wl_signal_add(&output->events.destroy, &feedback->output_destroy); wl_signal_add(&output->events.destroy, &feedback->output_destroy);
} }
void wlr_presentation_send_presented(struct wlr_presentation *presentation,
struct wlr_surface *surface, struct wlr_presentation_event *event) {
struct wlr_presentation_feedback *feedback =
wlr_presentation_surface_sampled(presentation, surface);
if (!feedback) {
return;
}
wlr_presentation_feedback_send_presented(feedback, event);
wlr_presentation_feedback_destroy(feedback);
}