|
|
|
@ -480,19 +480,13 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
|
|
|
|
|
return wlr_output_commit(wlr_output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
|
|
|
|
struct sway_output *output =
|
|
|
|
|
wl_container_of(listener, output, damage_frame);
|
|
|
|
|
if (!output->enabled || !output->wlr_output->enabled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
int output_repaint_timer_handler(void *data) {
|
|
|
|
|
struct sway_output *output = data;
|
|
|
|
|
output->wlr_output->block_idle_frame = false;
|
|
|
|
|
|
|
|
|
|
struct sway_workspace *workspace = output->current.active_workspace;
|
|
|
|
|
if (workspace == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sway_container *fullscreen_con = root->fullscreen_global;
|
|
|
|
@ -515,7 +509,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
|
|
|
|
last_scanned_out = scanned_out;
|
|
|
|
|
|
|
|
|
|
if (scanned_out) {
|
|
|
|
|
goto frame_done;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -524,17 +518,82 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
|
|
|
|
pixman_region32_init(&damage);
|
|
|
|
|
if (!wlr_output_damage_attach_render(output->damage,
|
|
|
|
|
&needs_frame, &damage)) {
|
|
|
|
|
return;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needs_frame) {
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
|
|
|
|
|
output_render(output, &now, &damage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pixman_region32_fini(&damage);
|
|
|
|
|
|
|
|
|
|
frame_done:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void damage_handle_frame(struct wl_listener *listener, void *data) {
|
|
|
|
|
struct sway_output *output =
|
|
|
|
|
wl_container_of(listener, output, damage_frame);
|
|
|
|
|
if (!output->enabled || !output->wlr_output->enabled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute predicted milliseconds until the next refresh. It's used for
|
|
|
|
|
// delaying both output rendering and surface frame callbacks.
|
|
|
|
|
int msec_until_refresh = 0;
|
|
|
|
|
|
|
|
|
|
if (output->max_render_time != 0) {
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clockid_t presentation_clock
|
|
|
|
|
= wlr_backend_get_presentation_clock(server.backend);
|
|
|
|
|
clock_gettime(presentation_clock, &now);
|
|
|
|
|
|
|
|
|
|
const long NSEC_IN_SECONDS = 1000000000;
|
|
|
|
|
struct timespec predicted_refresh = output->last_presentation;
|
|
|
|
|
predicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS;
|
|
|
|
|
predicted_refresh.tv_sec += output->refresh_nsec / NSEC_IN_SECONDS;
|
|
|
|
|
if (predicted_refresh.tv_nsec >= NSEC_IN_SECONDS) {
|
|
|
|
|
predicted_refresh.tv_sec += 1;
|
|
|
|
|
predicted_refresh.tv_nsec -= NSEC_IN_SECONDS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the predicted refresh time is before the current time then
|
|
|
|
|
// there's no point in delaying.
|
|
|
|
|
//
|
|
|
|
|
// We only check tv_sec because if the predicted refresh time is less
|
|
|
|
|
// than a second before the current time, then msec_until_refresh will
|
|
|
|
|
// end up slightly below zero, which will effectively disable the delay
|
|
|
|
|
// without potential disasterous negative overflows that could occur if
|
|
|
|
|
// tv_sec was not checked.
|
|
|
|
|
if (predicted_refresh.tv_sec >= now.tv_sec) {
|
|
|
|
|
long nsec_until_refresh
|
|
|
|
|
= (predicted_refresh.tv_sec - now.tv_sec) * NSEC_IN_SECONDS
|
|
|
|
|
+ (predicted_refresh.tv_nsec - now.tv_nsec);
|
|
|
|
|
|
|
|
|
|
// We want msec_until_refresh to be conservative, that is, floored.
|
|
|
|
|
// If we have 7.9 msec until refresh, we better compute the delay
|
|
|
|
|
// as if we had only 7 msec, so that we don't accidentally delay
|
|
|
|
|
// more than necessary and miss a frame.
|
|
|
|
|
msec_until_refresh = nsec_until_refresh / 1000000;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int delay = msec_until_refresh - output->max_render_time;
|
|
|
|
|
|
|
|
|
|
// If the delay is less than 1 millisecond (which is the least we can wait)
|
|
|
|
|
// then just render right away.
|
|
|
|
|
if (delay < 1) {
|
|
|
|
|
output_repaint_timer_handler(output);
|
|
|
|
|
} else {
|
|
|
|
|
output->wlr_output->block_idle_frame = true;
|
|
|
|
|
wl_event_source_timer_update(output->repaint_timer, delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send frame done to all visible surfaces
|
|
|
|
|
struct timespec now;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
send_frame_done(output, &now);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -768,6 +827,9 @@ static void handle_present(struct wl_listener *listener, void *data) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output->last_presentation = *output_event->when;
|
|
|
|
|
output->refresh_nsec = output_event->refresh;
|
|
|
|
|
|
|
|
|
|
struct wlr_presentation_event event = {
|
|
|
|
|
.output = output->wlr_output,
|
|
|
|
|
.tv_sec = (uint64_t)output_event->when->tv_sec,
|
|
|
|
@ -806,6 +868,9 @@ void handle_new_output(struct wl_listener *listener, void *data) {
|
|
|
|
|
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
|
|
|
|
|
output->damage_destroy.notify = damage_handle_destroy;
|
|
|
|
|
|
|
|
|
|
output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
|
|
|
|
|
output_repaint_timer_handler, output);
|
|
|
|
|
|
|
|
|
|
struct output_config *oc = find_output_config(output);
|
|
|
|
|
if (!oc || oc->enabled) {
|
|
|
|
|
output_enable(output, oc);
|
|
|
|
|