diff options
author | Ivan Molodetskikh <yalterz@gmail.com> | 2019-09-25 13:58:27 +0300 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2019-11-17 20:18:42 +0100 |
commit | 022df2542baa057b1965a7c7ee9c32e738f637d2 (patch) | |
tree | e650befe8f210c0fc44eb8fadf3b125c70a1b01f /sway/desktop/output.c | |
parent | Add -Wno-missing-braces (diff) | |
download | sway-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.c | 91 |
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 | ||
483 | static void damage_handle_frame(struct wl_listener *listener, void *data) { | 483 | int 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 | ||
536 | frame_done: | 533 | return 0; |
534 | } | ||
535 | |||
536 | static 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); |