- commit
- 3a7e6f558b56625e7308d5f098f7528737d4f01c
- parent
- 125ce65e879e8d7bc0a785d650d30b04d8f7dc9e
- author
- FelineStateMachine <dami.etoile@gmail.com>
| M | crates/ | 57 | ++++++++++++++++++++++++++++-- |
@@ -70,7 +70,19 @@ impl StartupCadenceSample {
7070 }7171 7272 fn startup_source_needs_restart(sample: StartupCadenceSample) -> bool {73- !sample.is_usable()73+ // An open producer with no frames is ambiguous by design: damage-driven74+ // Gamescope capture may be healthy and simply have nothing to repaint.75+ // Restart only on a closed producer or observed encoded progress that76+ // never yielded a decodable GOP.77+ !sample.receiver_open || (sample.frame_progress() > 0 && !sample.decodable_gop_ready)78+}79+80+fn replacement_source_has_failed(sample: StartupCadenceSample) -> bool {81+ // After the one sequential retry, open output channels mean the capture82+ // task is still alive. A damage-driven Gamescope source may legitimately83+ // remain frameless until the next repaint, so do not turn the sampling84+ // deadline into a session-establishment deadline.85+ !sample.receiver_open7486 }7587 7688 async fn sample_startup_cadence(@@ -146,6 +158,11 @@ pub(super) async fn select_gamescope_startup_source(
146158 "sampled primary Gamescope capture startup"147159 );148160 if !startup_source_needs_restart(primary_sample) {161+ if !primary_sample.is_usable() {162+ warn!(163+ "primary Gamescope capture is live but awaiting a damage-driven decodable repaint"164+ );165+ }149166 return Ok(primary);150167 }151168 @@ -171,7 +188,7 @@ pub(super) async fn select_gamescope_startup_source(
171188 target_cadence = replacement_sample.meets_target_cadence(),172189 "sampled sequential Gamescope capture startup replacement"173190 );174- if !replacement_sample.is_usable() {191+ if replacement_source_has_failed(replacement_sample) {175192 let frames = replacement_sample.frame_progress();176193 let fps = replacement_sample.fps();177194 let receiver_open = replacement_sample.receiver_open;@@ -183,6 +200,14 @@ pub(super) async fn select_gamescope_startup_source(
183200 decodable_gop_ready={decodable_gop_ready}"184201 );185202 }203+ if !replacement_sample.is_usable() {204+ warn!(205+ frames = replacement_sample.frame_progress(),206+ fps = replacement_sample.fps(),207+ decodable_gop_ready = replacement_sample.decodable_gop_ready,208+ "sequential Gamescope capture is live but awaiting a damage-driven decodable repaint"209+ );210+ }186211 Ok(replacement)187212 }188213 @@ -215,7 +240,7 @@ mod tests {
215240 }216241 217242 218- fn startup_restart_requires_a_live_decodable_source_not_target_cadence() {243+ fn startup_restart_requires_closed_or_active_undecodable_source() {219244 let slow = startup_sample(12.0, 6, true);220245 let target = startup_sample(60.0, 8, true);221246 assert!(!startup_source_needs_restart(slow));@@ -223,7 +248,7 @@ mod tests {
223248 assert!(!startup_source_needs_restart(target));224249 assert!(target.meets_target_cadence());225250 assert!(startup_source_needs_restart(startup_sample(60.0, 8, false)));226- assert!(startup_source_needs_restart(startup_sample(0.0, 0, true)));251+ assert!(!startup_source_needs_restart(startup_sample(0.0, 0, true)));227252 let mut undecodable = startup_sample(60.0, 8, true);228253 undecodable.decodable_gop_ready = false;229254 assert!(startup_source_needs_restart(undecodable));@@ -238,6 +263,30 @@ mod tests {
238263 assert!(!startup_source_needs_restart(burst));239264 }240265 266+ 267+ fn live_replacement_can_wait_for_damage_driven_repaint() {268+ let frameless = startup_sample(0.0, 0, true);269+ assert!(!startup_source_needs_restart(frameless));270+ assert!(!replacement_source_has_failed(frameless));271+272+ let closed = startup_sample(0.0, 0, false);273+ assert!(replacement_source_has_failed(closed));274+ }275+276+ 277+ async fn startup_sampling_preserves_live_frameless_source() {278+ let (frame_sender, mut frame_receiver) = tokio::sync::watch::channel(None);279+ let (_gop_sender, gop_receiver) = tokio::sync::watch::channel(None);280+281+ let sample =282+ sample_startup_cadence(&mut frame_receiver, &gop_receiver, Duration::ZERO).await;283+284+ assert_eq!(sample.frame_progress(), 0);285+ assert!(sample.receiver_open);286+ assert!(!replacement_source_has_failed(sample));287+ assert!(!frame_sender.is_closed());288+ }289+241290 242291 async fn startup_sampling_preserves_current_decodable_gop() {243292 let now = Instant::now();