flatland_frame_scheduling_lib/
throughput_scheduler.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{
6    PresentParameters, PresentationInfo, SchedulingFuture, SchedulingFutureState, SchedulingLib,
7};
8use async_trait::async_trait;
9use fuchsia_async::MonotonicInstant as fasync_time;
10use fuchsia_trace as trace;
11use std::cell::{Cell, RefCell};
12use std::task::Waker;
13
14// Scheduler for maximum throughput. Tries to schedule a frame at each on_next_frame_begin, if
15// there's something to draw (i.e. request_present() has been called). Presents are always
16// squashable, so if scenic misses a deadline the frame may be dropped.
17//
18// Notes:
19// Does not track present credits. Since it's limited to at most one Present() call per
20// OnNextFrameBegin() event, we're guaranteed not to run out of credits.
21//
22// TODO(https://fxbug.dev/42163690): Due OnNextFrameBegin() currently only firing after acquire fences complete
23// this scheduler will not manage to produce pipelined frames. This should resolve itself once
24// the bug is resolved, but testing to confirm will be necessary.
25pub struct ThroughputScheduler {
26    data: RefCell<WakeupData>,
27    next_expected_times: Cell<PresentationInfo>,
28    wait_guard: RefCell<()>,
29}
30
31// Data used to determine when to wake up. Checked as part of SchedulingFuture polling.
32// Must be separate to satisfy Pin<> in SchedulingFuture.
33struct WakeupData {
34    frame_requested: bool,
35    next_frame_begin: bool,
36    waker: Option<Waker>,
37}
38
39impl ThroughputScheduler {
40    pub fn new() -> ThroughputScheduler {
41        let now = fasync_time::now().into_zx();
42        ThroughputScheduler {
43            data: RefCell::new(WakeupData {
44                frame_requested: false,
45                next_frame_begin: true,
46                waker: None,
47            }),
48            next_expected_times: Cell::new(PresentationInfo {
49                latch_point: now,
50                presentation_time: now,
51            }),
52            wait_guard: RefCell::new(()),
53        }
54    }
55}
56
57#[async_trait(?Send)]
58impl SchedulingLib for ThroughputScheduler {
59    fn request_present(&self) {
60        self.data.borrow_mut().frame_requested = true;
61        self.data.borrow().maybe_wakeup();
62    }
63
64    fn on_next_frame_begin(
65        &self,
66        _additional_present_credits: u32,
67        future_presentation_infos: Vec<PresentationInfo>,
68    ) {
69        assert!(!future_presentation_infos.is_empty());
70        self.next_expected_times.set(future_presentation_infos[0]);
71        self.data.borrow_mut().next_frame_begin = true;
72        self.data.borrow().maybe_wakeup();
73    }
74
75    // Waits until the next on_next_frame_begin() after a request_frame().
76    async fn wait_to_update(&self) -> PresentParameters {
77        // Mutably borrow the wait_guard to prevent any simultaneous waits.
78        // TODO: this variable triggered the `must_not_suspend` lint and may be held across an await
79        // If this is the case, it is an error. See https://fxbug.dev/42168913 for more details
80        let _guard = self.wait_guard.try_borrow_mut().expect("Only one wait at a time allowed");
81        // Async tracing for the waiting period
82        let _trace_guard =
83            trace::async_enter!(trace::Id::new(), c"gfx", c"ThroughputScheduler::WaitForPresent");
84
85        // Wait until we're ready to draw.
86        SchedulingFuture { sched: &self.data }.await;
87
88        {
89            // Update state for next frame.
90            let mut data = self.data.borrow_mut();
91            data.frame_requested = false;
92            data.next_frame_begin = false;
93            data.waker = None;
94        }
95
96        let PresentationInfo { latch_point, presentation_time } = self.next_expected_times.get();
97        PresentParameters {
98            expected_latch_point: latch_point,
99            expected_presentation_time: presentation_time,
100            requested_presentation_time: zx::MonotonicInstant::from_nanos(0),
101            unsquashable: false,
102        }
103    }
104}
105
106impl SchedulingFutureState for WakeupData {
107    fn ready_to_wake_up(&self) -> bool {
108        self.frame_requested && self.next_frame_begin
109    }
110
111    fn set_waker(&mut self, waker: Waker) {
112        self.waker = Some(waker);
113    }
114
115    fn get_waker(&self) -> &Option<Waker> {
116        &self.waker
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use assert_matches::assert_matches;
124    use fuchsia_async as fasync;
125    use std::task::Poll;
126
127    #[test]
128    fn wait_without_request_present_never_completes() {
129        let mut exec = fasync::TestExecutor::new();
130        let sched = ThroughputScheduler::new();
131        let mut fut = sched.wait_to_update();
132        assert!(exec.run_until_stalled(&mut fut).is_pending());
133
134        // Should complete after request_present().
135        sched.request_present();
136        assert_matches!(
137            exec.run_until_stalled(&mut fut),
138            Poll::Ready(PresentParameters {
139                expected_latch_point: _,
140                expected_presentation_time: _,
141                requested_presentation_time: _,
142                unsquashable: false,
143            })
144        );
145    }
146
147    #[fasync::run_until_stalled(test)]
148    async fn wait_after_initial_request_present_completes_immediately() {
149        let sched = ThroughputScheduler::new();
150        sched.request_present();
151        assert_matches!(
152            sched.wait_to_update().await,
153            PresentParameters {
154                expected_latch_point: _,
155                expected_presentation_time: _,
156                requested_presentation_time: _,
157                unsquashable: false,
158            }
159        );
160    }
161
162    #[test]
163    fn following_waits_never_completes_without_on_next_frame_begin() {
164        let mut exec = fasync::TestExecutor::new();
165        let sched = ThroughputScheduler::new();
166        // Initial wait always completes immediately.
167        sched.request_present();
168        let mut fut = sched.wait_to_update();
169        exec.run_until_stalled(&mut fut).is_ready();
170
171        // Second wait doesn't complete until after on_frame_presented().
172        sched.request_present();
173        let mut fut = sched.wait_to_update();
174        assert!(exec.run_until_stalled(&mut fut).is_pending());
175
176        sched.on_next_frame_begin(
177            10,
178            vec![PresentationInfo {
179                latch_point: zx::MonotonicInstant::from_nanos(1),
180                presentation_time: zx::MonotonicInstant::from_nanos(1),
181            }],
182        );
183        assert_eq!(
184            exec.run_until_stalled(&mut fut),
185            Poll::Ready(PresentParameters {
186                expected_latch_point: zx::MonotonicInstant::from_nanos(1),
187                expected_presentation_time: zx::MonotonicInstant::from_nanos(1),
188                requested_presentation_time: zx::MonotonicInstant::from_nanos(0),
189                unsquashable: false,
190            })
191        );
192    }
193}