Skip to main content

limnus_default_schedulers/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/limnus
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5
6use limnus_app::prelude::{App, Plugin};
7use limnus_clock::MonotonicTime;
8use limnus_default_stages::{
9    First, FixedFirst, FixedPostUpdate, FixedPreUpdate, FixedUpdate, PostUpdate, PreUpdate,
10    RenderFirst, RenderPostUpdate, RenderPreUpdate, RenderUpdate, Update,
11};
12use limnus_resource::prelude::Resource;
13use limnus_scheduler::Scheduler;
14use limnus_stage::Stages;
15use limnus_system_params::MsgAll;
16use limnus_system_state::State;
17use monotonic_time_rs::{Millis, MillisDuration};
18use std::any::TypeId;
19
20#[derive(Debug)]
21pub struct MainScheduler;
22impl Scheduler for MainScheduler {
23    fn schedule(&self, stages: &Stages, state: &mut State) {
24        let stage_ids = {
25            vec![
26                TypeId::of::<First>(),
27                TypeId::of::<PreUpdate>(),
28                TypeId::of::<Update>(),
29                TypeId::of::<PostUpdate>(),
30            ]
31        };
32
33        for &stage_id in &stage_ids {
34            stages
35                .get_by_id(&stage_id)
36                .expect("stage missing")
37                .run(state);
38        }
39    }
40}
41
42#[derive(Debug, Resource)]
43pub struct FixedSchedulerData {
44    pub consumed_up_to_time: Millis,
45    pub ticks_per_second: usize,
46}
47
48#[derive(Debug)]
49pub struct FixedScheduler;
50impl Scheduler for FixedScheduler {
51    fn schedule(&self, stages: &Stages, state: &mut State) {
52        let current_time = { state.resources().fetch::<MonotonicTime>().time };
53
54        let (mut consumed_time, ticks_per_second) = {
55            let data = state.resources().fetch::<FixedSchedulerData>();
56            (data.consumed_up_to_time, data.ticks_per_second)
57        };
58
59        let steps_to_perform = if consumed_time > current_time {
60            // We are ahead
61            let time_ahead = consumed_time - current_time;
62            let exact_steps_ahead = (time_ahead.as_millis() * ticks_per_second as u64) / 1_000;
63            #[allow(clippy::bool_to_int_with_if)]
64            if exact_steps_ahead > 4 { 0 } else { 1 }
65        } else {
66            // We are behind
67            let time_debt = current_time - consumed_time;
68
69            let exact_steps_needed = (time_debt.as_millis() * ticks_per_second as u64) / 1_000;
70
71            // If we need significantly more than 1 step, consider doing 2
72            if exact_steps_needed > 1 { 2 } else { 1 }
73        };
74
75        let stage_ids = {
76            vec![
77                TypeId::of::<FixedFirst>(),
78                TypeId::of::<FixedPreUpdate>(),
79                TypeId::of::<FixedUpdate>(),
80                TypeId::of::<FixedPostUpdate>(),
81            ]
82        };
83
84        let fixed_time_step_ms = 1000 / ticks_per_second;
85
86        for _ in 0..steps_to_perform {
87            for &stage_id in &stage_ids {
88                stages
89                    .get_by_id(&stage_id)
90                    .expect("stage missing")
91                    .run(state);
92            }
93            consumed_time += MillisDuration::from_millis(fixed_time_step_ms as u64);
94        }
95
96        {
97            let fixed_scheduler_data = state.resources_mut().fetch_mut::<FixedSchedulerData>();
98            fixed_scheduler_data.consumed_up_to_time = consumed_time;
99        }
100    }
101}
102
103#[derive(Debug)]
104pub struct RenderScheduler;
105impl Scheduler for RenderScheduler {
106    fn schedule(&self, stages: &Stages, state: &mut State) {
107        // TODO: Should have settings for min and max fps
108        let stage_ids = {
109            vec![
110                TypeId::of::<RenderFirst>(),
111                TypeId::of::<RenderPreUpdate>(),
112                TypeId::of::<RenderUpdate>(),
113                TypeId::of::<RenderPostUpdate>(),
114            ]
115        };
116
117        for &stage_id in &stage_ids {
118            stages
119                .get_by_id(&stage_id)
120                .expect("stage missing")
121                .run(state);
122        }
123    }
124}
125
126fn swap_messages(mut messages: MsgAll) {
127    messages.swap_all();
128}
129
130pub struct DefaultSchedulersPlugin;
131
132impl Plugin for DefaultSchedulersPlugin {
133    fn build(&self, app: &mut App) {
134        let time = { app.resources().fetch::<MonotonicTime>().time };
135
136        app.insert_resource(FixedSchedulerData {
137            consumed_up_to_time: time,
138            ticks_per_second: 60,
139        });
140
141        app.add_scheduler(MainScheduler);
142        app.add_scheduler(FixedScheduler);
143        app.add_scheduler(RenderScheduler);
144
145        app.add_system(First, swap_messages);
146    }
147}