Selkie.git | lib/Selkie/App/Internal/ | IdleBudget.rakumod


=begin pod

=head1 NAME

Selkie::App::Internal::IdleBudget - internal frame-budget helpers for Selkie::App

=head1 DESCRIPTION

Implementation detail for C<Selkie::App>. Use C<Selkie::App> and
C<Selkie::App::pick-frame-budget> from application code.

=end pod

unit module Selkie::App::Internal::IdleBudget;

# Idle-tier constants are implementation detail for Selkie::App's run loop.
my constant IDLE-HALF    = 1e0 / 30e0;   # 30 Hz = ~33 ms
my constant IDLE-QUARTER = 1e0 / 12e0;   # 12 Hz = ~83 ms
my constant IDLE-DEEP    = 1e0 /  4e0;   #  4 Hz = 250 ms
my constant STORE-ACTIVITY-MIN-BUDGET = IDLE-HALF;

our constant APP-SLEEP-CHUNK-MAX is export = 0.1e0;

sub app-ladder-tier(Num:D $idle-for, Num:D $hot-budget --> Num:D) {
    given $idle-for {
        when * <  30e0 { $hot-budget  }
        when * <  60e0 { IDLE-HALF    }
        when * < 120e0 { IDLE-QUARTER }
        default        { IDLE-DEEP    }
    }
}

sub pick-app-frame-budget(
    Num:D $hot-budget,
    Num:D $user-idle-for,
    Num:D $store-idle-for,
    Bool:D :$animating = False,
    --> Num:D
) is export {
    # An animation is sampled, not triggered: it needs frames whether or
    # not anything has happened recently. Bypassing the ladder here (as
    # opposed to poking the user-activity clock) keeps the tween from
    # leaving a 30-second hot tail behind it once it finishes.
    return $hot-budget if $animating;

    my Num $user-budget  = app-ladder-tier($user-idle-for, $hot-budget);
    my Num $store-budget = max(
        app-ladder-tier($store-idle-for, $hot-budget),
        STORE-ACTIVITY-MIN-BUDGET,
    );
    max(min($user-budget, $store-budget), $hot-budget);
}