aboutsummaryrefslogtreecommitdiffstats
path: root/sway/desktop/output.c
diff options
context:
space:
mode:
authorLibravatar Ivan Molodetskikh <yalterz@gmail.com>2019-09-25 13:58:27 +0300
committerLibravatar Simon Ser <contact@emersion.fr>2019-11-17 20:18:42 +0100
commit022df2542baa057b1965a7c7ee9c32e738f637d2 (patch)
treee650befe8f210c0fc44eb8fadf3b125c70a1b01f /sway/desktop/output.c
parentAdd -Wno-missing-braces (diff)
downloadsway-022df2542baa057b1965a7c7ee9c32e738f637d2.tar.gz
sway-022df2542baa057b1965a7c7ee9c32e738f637d2.tar.zst
sway-022df2542baa057b1965a7c7ee9c32e738f637d2.zip
output: add max_render_time
Diffstat (limited to 'sway/desktop/output.c')
-rw-r--r--sway/desktop/output.c91
1 files changed, 78 insertions, 13 deletions
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 05b5cd44..0f715c19 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -480,19 +480,13 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
480 return wlr_output_commit(wlr_output); 480 return wlr_output_commit(wlr_output);
481} 481}
482 482
483static void damage_handle_frame(struct wl_listener *listener, void *data) { 483int output_repaint_timer_handler(void *data) {
484 struct sway_output *output = 484 struct sway_output *output = data;
485 wl_container_of(listener, output, damage_frame); 485 output->wlr_output->block_idle_frame = false;
486 if (!output->enabled || !output->wlr_output->enabled) {
487 return;
488 }
489
490 struct timespec now;
491 clock_gettime(CLOCK_MONOTONIC, &now);
492 486
493 struct sway_workspace *workspace = output->current.active_workspace; 487 struct sway_workspace *workspace = output->current.active_workspace;
494 if (workspace == NULL) { 488 if (workspace == NULL) {
495 return; 489 return 0;
496 } 490 }
497 491
498 struct sway_container *fullscreen_con = root->fullscreen_global; 492 struct sway_container *fullscreen_con = root->fullscreen_global;
@@ -515,7 +509,7 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
515 last_scanned_out = scanned_out; 509 last_scanned_out = scanned_out;
516 510
517 if (scanned_out) { 511 if (scanned_out) {
518 goto frame_done; 512 return 0;
519 } 513 }
520 } 514 }
521 515
@@ -524,17 +518,82 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) {
524 pixman_region32_init(&damage); 518 pixman_region32_init(&damage);
525 if (!wlr_output_damage_attach_render(output->damage, 519 if (!wlr_output_damage_attach_render(output->damage,
526 &needs_frame, &damage)) { 520 &needs_frame, &damage)) {
527 return; 521 return 0;
528 } 522 }
529 523
530 if (needs_frame) { 524 if (needs_frame) {
525 struct timespec now;
526 clock_gettime(CLOCK_MONOTONIC, &now);
527
531 output_render(output, &now, &damage); 528 output_render(output, &now, &damage);
532 } 529 }
533 530
534 pixman_region32_fini(&damage); 531 pixman_region32_fini(&damage);
535 532
536frame_done: 533 return 0;
534}
535
536static void damage_handle_frame(struct wl_listener *listener, void *data) {
537 struct sway_output *output =
538 wl_container_of(listener, output, damage_frame);
539 if (!output->enabled || !output->wlr_output->enabled) {
540 return;
541 }
542
543 // Compute predicted milliseconds until the next refresh. It's used for
544 // delaying both output rendering and surface frame callbacks.
545 int msec_until_refresh = 0;
546
547 if (output->max_render_time != 0) {
548 struct timespec now;
549 clockid_t presentation_clock
550 = wlr_backend_get_presentation_clock(server.backend);
551 clock_gettime(presentation_clock, &now);
552
553 const long NSEC_IN_SECONDS = 1000000000;
554 struct timespec predicted_refresh = output->last_presentation;
555 predicted_refresh.tv_nsec += output->refresh_nsec % NSEC_IN_SECONDS;
556 predicted_refresh.tv_sec += output->refresh_nsec / NSEC_IN_SECONDS;
557 if (predicted_refresh.tv_nsec >= NSEC_IN_SECONDS) {
558 predicted_refresh.tv_sec += 1;
559 predicted_refresh.tv_nsec -= NSEC_IN_SECONDS;
560 }
561
562 // If the predicted refresh time is before the current time then
563 // there's no point in delaying.
564 //
565 // We only check tv_sec because if the predicted refresh time is less
566 // than a second before the current time, then msec_until_refresh will
567 // end up slightly below zero, which will effectively disable the delay
568 // without potential disasterous negative overflows that could occur if
569 // tv_sec was not checked.
570 if (predicted_refresh.tv_sec >= now.tv_sec) {
571 long nsec_until_refresh
572 = (predicted_refresh.tv_sec - now.tv_sec) * NSEC_IN_SECONDS
573 + (predicted_refresh.tv_nsec - now.tv_nsec);
574
575 // We want msec_until_refresh to be conservative, that is, floored.
576 // If we have 7.9 msec until refresh, we better compute the delay
577 // as if we had only 7 msec, so that we don't accidentally delay
578 // more than necessary and miss a frame.
579 msec_until_refresh = nsec_until_refresh / 1000000;
580 }
581 }
582
583 int delay = msec_until_refresh - output->max_render_time;
584
585 // If the delay is less than 1 millisecond (which is the least we can wait)
586 // then just render right away.
587 if (delay < 1) {
588 output_repaint_timer_handler(output);
589 } else {
590 output->wlr_output->block_idle_frame = true;
591 wl_event_source_timer_update(output->repaint_timer, delay);
592 }
593
537 // Send frame done to all visible surfaces 594 // Send frame done to all visible surfaces
595 struct timespec now;
596 clock_gettime(CLOCK_MONOTONIC, &now);
538 send_frame_done(output, &now); 597 send_frame_done(output, &now);
539} 598}
540 599
@@ -768,6 +827,9 @@ static void handle_present(struct wl_listener *listener, void *data) {
768 return; 827 return;
769 } 828 }
770 829
830 output->last_presentation = *output_event->when;
831 output->refresh_nsec = output_event->refresh;
832
771 struct wlr_presentation_event event = { 833 struct wlr_presentation_event event = {
772 .output = output->wlr_output, 834 .output = output->wlr_output,
773 .tv_sec = (uint64_t)output_event->when->tv_sec, 835 .tv_sec = (uint64_t)output_event->when->tv_sec,
@@ -806,6 +868,9 @@ void handle_new_output(struct wl_listener *listener, void *data) {
806 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); 868 wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
807 output->damage_destroy.notify = damage_handle_destroy; 869 output->damage_destroy.notify = damage_handle_destroy;
808 870
871 output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
872 output_repaint_timer_handler, output);
873
809 struct output_config *oc = find_output_config(output); 874 struct output_config *oc = find_output_config(output);
810 if (!oc || oc->enabled) { 875 if (!oc || oc->enabled) {
811 output_enable(output, oc); 876 output_enable(output, oc);