feat(snake): 8 steps/s on fixed-period clock (draw time no longer adds to step)
This commit is contained in:
@@ -13,7 +13,9 @@ use libkos::syscall;
|
||||
use server_framework::Encode;
|
||||
use display_server::protocol::DisplayRequest;
|
||||
|
||||
const TICK_DELAY: u64 = 5_000_000;
|
||||
/// Steps a second; QEMU virt's timebase is 10 MHz.
|
||||
const STEPS_PER_SECOND: u64 = 8;
|
||||
const TICK_DELAY: u64 = 10_000_000 / STEPS_PER_SECOND;
|
||||
const MAX_RENDER_CMDS: usize = 140;
|
||||
|
||||
/// The display server, which the shell hands over because snake's manifest
|
||||
@@ -53,6 +55,7 @@ fn main() {
|
||||
};
|
||||
// Takes focus; keys are polled every tick, the signal only wakes it.
|
||||
let _ = libkos::display::subscribe(display_ep(), alarm, types::badge::Badge::new(1 << 1));
|
||||
let mut clock = Clock::new(alarm);
|
||||
let mut snake = Snake::new();
|
||||
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_CMDS];
|
||||
|
||||
@@ -71,12 +74,12 @@ fn main() {
|
||||
flush();
|
||||
// Any key leaves.
|
||||
while next_input() == Input::None {
|
||||
wait_tick(alarm);
|
||||
clock.wait_tick();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
wait_tick(alarm);
|
||||
clock.wait_tick();
|
||||
if handle_input(&mut snake) == Input::Quit {
|
||||
return;
|
||||
}
|
||||
@@ -126,17 +129,31 @@ fn handle_input(snake: &mut Snake) -> Input {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sleeps until the next tick on `alarm`, instead of spinning. Keys
|
||||
/// signal it too (snake has one notification), so it waits again until
|
||||
/// the tick is really due.
|
||||
fn wait_tick(alarm: CapSlot) {
|
||||
let deadline = syscall::get_time().raw() + TICK_DELAY;
|
||||
if syscall::set_timer(alarm, Ticks::from_raw(deadline)).is_err() {
|
||||
/// The game's clock: a step every `TICK_DELAY`, counted from the last
|
||||
/// step's deadline, so the time drawing takes does not slow it down.
|
||||
struct Clock {
|
||||
alarm: CapSlot,
|
||||
next: u64,
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
fn new(alarm: CapSlot) -> Self {
|
||||
Self { alarm, next: syscall::get_time().raw() }
|
||||
}
|
||||
|
||||
/// Sleeps until the next step is due, instead of spinning. Keys
|
||||
/// signal `alarm` too (snake has one notification), so it waits again
|
||||
/// until the step really is due.
|
||||
fn wait_tick(&mut self) {
|
||||
// After a stall, carry on from now rather than rushing to catch up.
|
||||
self.next = (self.next + TICK_DELAY).max(syscall::get_time().raw());
|
||||
if syscall::set_timer(self.alarm, Ticks::from_raw(self.next)).is_err() {
|
||||
return;
|
||||
}
|
||||
while syscall::get_time().raw() < deadline {
|
||||
if syscall::wait(alarm).is_err() {
|
||||
while syscall::get_time().raw() < self.next {
|
||||
if syscall::wait(self.alarm).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user