flatland_frame_scheduling_lib/
throughput_scheduler.rsuse crate::{
PresentParameters, PresentationInfo, SchedulingFuture, SchedulingFutureState, SchedulingLib,
};
use async_trait::async_trait;
use fuchsia_async::MonotonicInstant as fasync_time;
use fuchsia_trace as trace;
use std::cell::{Cell, RefCell};
use std::task::Waker;
pub struct ThroughputScheduler {
data: RefCell<WakeupData>,
next_expected_times: Cell<PresentationInfo>,
wait_guard: RefCell<()>,
}
struct WakeupData {
frame_requested: bool,
next_frame_begin: bool,
waker: Option<Waker>,
}
impl ThroughputScheduler {
pub fn new() -> ThroughputScheduler {
let now = fasync_time::now().into_zx();
ThroughputScheduler {
data: RefCell::new(WakeupData {
frame_requested: false,
next_frame_begin: true,
waker: None,
}),
next_expected_times: Cell::new(PresentationInfo {
latch_point: now,
presentation_time: now,
}),
wait_guard: RefCell::new(()),
}
}
}
#[async_trait(?Send)]
impl SchedulingLib for ThroughputScheduler {
fn request_present(&self) {
self.data.borrow_mut().frame_requested = true;
self.data.borrow().maybe_wakeup();
}
fn on_next_frame_begin(
&self,
_additional_present_credits: u32,
future_presentation_infos: Vec<PresentationInfo>,
) {
assert!(!future_presentation_infos.is_empty());
self.next_expected_times.set(future_presentation_infos[0]);
self.data.borrow_mut().next_frame_begin = true;
self.data.borrow().maybe_wakeup();
}
async fn wait_to_update(&self) -> PresentParameters {
let _guard = self.wait_guard.try_borrow_mut().expect("Only one wait at a time allowed");
let _trace_guard =
trace::async_enter!(trace::Id::new(), c"gfx", c"ThroughputScheduler::WaitForPresent");
SchedulingFuture { sched: &self.data }.await;
{
let mut data = self.data.borrow_mut();
data.frame_requested = false;
data.next_frame_begin = false;
data.waker = None;
}
let PresentationInfo { latch_point, presentation_time } = self.next_expected_times.get();
PresentParameters {
expected_latch_point: latch_point,
expected_presentation_time: presentation_time,
requested_presentation_time: zx::MonotonicInstant::from_nanos(0),
unsquashable: false,
}
}
}
impl SchedulingFutureState for WakeupData {
fn ready_to_wake_up(&self) -> bool {
self.frame_requested && self.next_frame_begin
}
fn set_waker(&mut self, waker: Waker) {
self.waker = Some(waker);
}
fn get_waker(&self) -> &Option<Waker> {
&self.waker
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use fuchsia_async as fasync;
use std::task::Poll;
#[test]
fn wait_without_request_present_never_completes() {
let mut exec = fasync::TestExecutor::new();
let sched = ThroughputScheduler::new();
let mut fut = sched.wait_to_update();
assert!(exec.run_until_stalled(&mut fut).is_pending());
sched.request_present();
assert_matches!(
exec.run_until_stalled(&mut fut),
Poll::Ready(PresentParameters {
expected_latch_point: _,
expected_presentation_time: _,
requested_presentation_time: _,
unsquashable: false,
})
);
}
#[fasync::run_until_stalled(test)]
async fn wait_after_initial_request_present_completes_immediately() {
let sched = ThroughputScheduler::new();
sched.request_present();
assert_matches!(
sched.wait_to_update().await,
PresentParameters {
expected_latch_point: _,
expected_presentation_time: _,
requested_presentation_time: _,
unsquashable: false,
}
);
}
#[test]
fn following_waits_never_completes_without_on_next_frame_begin() {
let mut exec = fasync::TestExecutor::new();
let sched = ThroughputScheduler::new();
sched.request_present();
let mut fut = sched.wait_to_update();
exec.run_until_stalled(&mut fut).is_ready();
sched.request_present();
let mut fut = sched.wait_to_update();
assert!(exec.run_until_stalled(&mut fut).is_pending());
sched.on_next_frame_begin(
10,
vec![PresentationInfo {
latch_point: zx::MonotonicInstant::from_nanos(1),
presentation_time: zx::MonotonicInstant::from_nanos(1),
}],
);
assert_eq!(
exec.run_until_stalled(&mut fut),
Poll::Ready(PresentParameters {
expected_latch_point: zx::MonotonicInstant::from_nanos(1),
expected_presentation_time: zx::MonotonicInstant::from_nanos(1),
requested_presentation_time: zx::MonotonicInstant::from_nanos(0),
unsquashable: false,
})
);
}
}