diff --git a/examples/scene-graph.c b/examples/scene-graph.c index 3cf150d0..7b7e14d6 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -57,7 +57,7 @@ struct output { static void output_handle_frame(struct wl_listener *listener, void *data) { struct output *output = wl_container_of(listener, output, frame); - if (!wlr_scene_output_commit(output->scene_output)) { + if (!wlr_scene_output_commit(output->scene_output, NULL)) { return; } diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 36ad2361..b5487528 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -209,6 +209,11 @@ struct wlr_scene_output { struct wl_array render_list; }; +struct wlr_scene_timer { + int64_t pre_render_duration; + struct wlr_render_timer *render_timer; +}; + /** A layer shell scene helper */ struct wlr_scene_layer_surface_v1 { struct wlr_scene_tree *tree; @@ -455,16 +460,30 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output); void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly); +struct wlr_scene_output_state_options { + struct wlr_scene_timer *timer; +}; + /** * Render and commit an output. */ -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, + const struct wlr_scene_output_state_options *options); /** * Render and populate given output state. */ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, - struct wlr_output_state *state); + struct wlr_output_state *state, const struct wlr_scene_output_state_options *options); + +/** + * Retrieve the duration in nanoseconds between the last wlr_scene_output_commit() call and the end + * of its operations, including those on the GPU that may have finished after the call returned. + * + * Returns -1 if the duration is unavailable. + */ +int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer); +void wlr_scene_timer_finish(struct wlr_scene_timer *timer); /** * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 8324c500..61c08928 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -559,7 +559,7 @@ static void output_frame(struct wl_listener *listener, void *data) { scene, output->wlr_output); /* Render the scene if needed and commit the output */ - wlr_scene_output_commit(scene_output); + wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 3f901669..a6a786f5 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -1584,7 +1584,8 @@ static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, return true; } -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, + const struct wlr_scene_output_state_options *options) { if (!scene_output->output->needs_frame && !pixman_region32_not_empty( &scene_output->damage_ring.current)) { return true; @@ -1593,7 +1594,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { bool ok = false; struct wlr_output_state state; wlr_output_state_init(&state); - if (!wlr_scene_output_build_state(scene_output, &state)) { + if (!wlr_scene_output_build_state(scene_output, &state, options)) { goto out; } @@ -1610,7 +1611,19 @@ out: } bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, - struct wlr_output_state *state) { + struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { + struct wlr_scene_output_state_options default_options = {0}; + if (!options) { + options = &default_options; + } + struct wlr_scene_timer *timer = options->timer; + struct timespec start_time; + if (timer) { + clock_gettime(CLOCK_MONOTONIC, &start_time); + wlr_scene_timer_finish(timer); + *timer = (struct wlr_scene_timer){0}; + } + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { // if the state is being disabled, do nothing. return true; @@ -1683,6 +1696,12 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, } if (scanout) { + if (timer) { + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); + } return true; } @@ -1742,7 +1761,19 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, return false; } - struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, NULL); + if (timer) { + timer->render_timer = wlr_render_timer_create(output->renderer); + + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); + } + + struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, + &(struct wlr_buffer_pass_options){ + .timer = timer ? timer->render_timer : NULL, + }); if (render_pass == NULL) { wlr_buffer_unlock(buffer); return false; @@ -1859,6 +1890,21 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, return true; } +int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) { + int64_t pre_render = timer->pre_render_duration; + if (!timer->render_timer) { + return pre_render; + } + int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer); + return render != -1 ? pre_render + render : -1; +} + +void wlr_scene_timer_finish(struct wlr_scene_timer *timer) { + if (timer->render_timer) { + wlr_render_timer_destroy(timer->render_timer); + } +} + static void scene_node_send_frame_done(struct wlr_scene_node *node, struct wlr_scene_output *scene_output, struct timespec *now) { if (!node->enabled) {