Skip to content

Commit b3bea10

Browse files
Balancing frame pacing and frame throttling
Balancing Frame Pacing and Frame Throttling Implement flexible frame throttling by: 1. Waiting for half of the target frame duration (`delay_til_next_frame_ns`). 2. Calculating the elapsed time for a frame capture: - If we are ahead of the next frame capture time, wait for `delay_til_next_frame_ns`. - If we are behind the next frame capture time, accumulate the delay into `accumulated_frame_debt_ns`. 3. Using `accumulated_frame_debt_ns` to reduce waiting time in `delay_before_capture_frame_ns` and `delay_til_next_frame_ns`, helping to reach the target frame rate.
1 parent 5db0d72 commit b3bea10

File tree

1 file changed

+58
-20
lines changed

1 file changed

+58
-20
lines changed

src/screencast_thread.rs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use wayland_client::{
3636
};
3737

3838
const TIMESPEC_NSEC_PER_SEC: u32 = 1_000_000_000;
39-
const FPS_MEASURE_PERIOD_SEC: u64 = 5;
39+
const FPS_MEASURE_PERIOD_SEC: f64 = 5.;
4040

4141
pub struct ScreencastThread {
4242
node_id: u32,
@@ -85,7 +85,9 @@ struct FPSLimit {
8585
frame_last_time: Instant,
8686
fps_last_time: Instant,
8787
fps_frame_count: u32,
88+
delay_before_capture_frame_ns: u64,
8889
delay_til_next_frame_ns: u64,
90+
accumulated_frame_debt_ns: u64,
8991
}
9092

9193
impl FPSLimit {
@@ -94,7 +96,9 @@ impl FPSLimit {
9496
frame_last_time: Instant::now(),
9597
fps_last_time: Instant::now(),
9698
fps_frame_count: 0,
99+
delay_before_capture_frame_ns: 0,
97100
delay_til_next_frame_ns: 0,
101+
accumulated_frame_debt_ns: 0,
98102
}
99103
}
100104

@@ -110,35 +114,60 @@ impl FPSLimit {
110114
let now = Instant::now();
111115
self.fps_frame_count += 1;
112116
let elapsed_sec = (now - self.fps_last_time).as_secs_f64();
113-
if elapsed_sec < 1. {
117+
118+
if elapsed_sec < FPS_MEASURE_PERIOD_SEC {
114119
return;
115120
}
116-
// log::info!(
117-
// "fps_limit: average FPS in the last {:.2} seconds: {:.2}",
118-
// elapsed_sec,
119-
// self.fps_frame_count
120-
// );
121+
let avg_frames_per_sec = self.fps_frame_count as f64 / elapsed_sec;
122+
123+
log::info!(
124+
"fps_limit: average FPS in the last {:.2} seconds: {:.2}",
125+
elapsed_sec,
126+
avg_frames_per_sec
127+
);
121128
self.fps_frame_count = 0;
122129
self.fps_last_time = now;
123130
}
124131

125132
fn fps_limit_measure_end(&mut self, max_fps: u32) {
126133
if max_fps <= 0 {
134+
self.delay_before_capture_frame_ns = 0;
127135
self.delay_til_next_frame_ns = 0;
136+
self.accumulated_frame_debt_ns = 0;
128137
return;
129138
}
130139
self.measure_fps();
131140

132-
// Throttling will not be applied if the current FPS is unlikely to exceed the target frame rate.
133-
if self.fps_frame_count < max_fps - 1 {
141+
let elapsed_ns = self.frame_last_time.elapsed().as_nanos();
142+
let target_ns = (TIMESPEC_NSEC_PER_SEC / max_fps) as u128;
143+
144+
// Wait for half of the target frame rate duration before requesting a frame capture.
145+
self.delay_before_capture_frame_ns = (target_ns / 2) as u64;
146+
147+
// Throttle after the current frame has been captured:
148+
let total_elapsed_ns = elapsed_ns + self.accumulated_frame_debt_ns as u128;
149+
if target_ns > total_elapsed_ns {
150+
// If it is before the next frame capture time -> wait for the right time.
151+
self.delay_til_next_frame_ns = (target_ns - total_elapsed_ns) as u64;
152+
} else {
153+
// If it is after the next frame capture time, Set value of `delay_til_next_frame_ns` to 0 and increase value of `accumulated_frame_debt_ns` by the amount of time it has been delayed.
134154
self.delay_til_next_frame_ns = 0;
135-
return;
155+
self.accumulated_frame_debt_ns = target_ns.abs_diff(total_elapsed_ns) as u64;
136156
}
137157

138-
let elapsed_ns = self.frame_last_time.elapsed().as_nanos();
139-
let target_ns = (TIMESPEC_NSEC_PER_SEC / max_fps) as u128;
140-
self.delay_til_next_frame_ns =
141-
target_ns.saturating_sub(elapsed_ns + 3_000_000 /* safety margin */) as u64;
158+
// Set `delay_before_capture_frame_ns` to its current value minus the overrun time, if any.
159+
if self.accumulated_frame_debt_ns > self.delay_before_capture_frame_ns {
160+
self.accumulated_frame_debt_ns -= self.delay_before_capture_frame_ns;
161+
self.delay_before_capture_frame_ns = 0;
162+
} else {
163+
self.delay_before_capture_frame_ns -= self.accumulated_frame_debt_ns;
164+
self.accumulated_frame_debt_ns = 0;
165+
}
166+
167+
// Reset at the end of each capture cycle, this helps prevent `accumulated_frame_debt_ns` from increasing indefinitely.
168+
if self.fps_frame_count % max_fps == 0 {
169+
self.accumulated_frame_debt_ns = 0;
170+
}
142171
}
143172
}
144173

@@ -368,15 +397,16 @@ impl StreamData {
368397
fn process(&mut self, stream: &StreamRef) {
369398
let buffer = unsafe { stream.dequeue_raw_buffer() };
370399
if !buffer.is_null() {
371-
if self.fps_limit.delay_til_next_frame_ns != 0 {
400+
self.fps_limit.fps_limit_measure_start(self.framerate);
401+
if self.fps_limit.delay_before_capture_frame_ns != 0 {
372402
// log::info!(
373-
// "fps_limit: wait {}ns til next frame",
374-
// self.fps_limit.delay_til_next_frame_ns
403+
// "fps_limit: wait {}ns before capture frame",
404+
// self.fps_limit.delay_before_capture_frame_ns
375405
// );
376-
std::thread::sleep(Duration::from_nanos(self.fps_limit.delay_til_next_frame_ns));
406+
std::thread::sleep(Duration::from_nanos(
407+
self.fps_limit.delay_before_capture_frame_ns,
408+
));
377409
}
378-
379-
self.fps_limit.fps_limit_measure_start(self.framerate);
380410
let wl_buffer = unsafe { &*((*buffer).user_data as *const wl_buffer::WlBuffer) };
381411
let full_damage = &[Rect {
382412
x: 0,
@@ -417,6 +447,14 @@ impl StreamData {
417447
}
418448
unsafe { stream.queue_raw_buffer(buffer) };
419449
self.fps_limit.fps_limit_measure_end(self.framerate);
450+
451+
if self.fps_limit.delay_til_next_frame_ns != 0 {
452+
// log::info!(
453+
// "fps_limit: wait {}ns til next frame",
454+
// self.fps_limit.delay_til_next_frame_ns
455+
// );
456+
std::thread::sleep(Duration::from_nanos(self.fps_limit.delay_til_next_frame_ns));
457+
}
420458
}
421459
}
422460
}

0 commit comments

Comments
 (0)