@@ -876,7 +876,9 @@ pub struct Window {
active: Rc<Cell<bool>>,
hovered: Rc<Cell<bool>>,
pub(crate) needs_present: Rc<Cell<bool>>,
- pub(crate) last_input_timestamp: Rc<Cell<Instant>>,
+ /// Tracks recent input event timestamps to determine if input is arriving at a high rate.
+ /// Used to selectively enable VRR optimization only when input rate exceeds 60fps.
+ pub(crate) input_rate_tracker: Rc<RefCell<InputRateTracker>>,
last_input_modality: InputModality,
pub(crate) refreshing: bool,
pub(crate) activation_observers: SubscriberSet<(), AnyObserver>,
@@ -897,6 +899,51 @@ struct ModifierState {
saw_keystroke: bool,
}
+/// Tracks input event timestamps to determine if input is arriving at a high rate.
+/// Used for selective VRR (Variable Refresh Rate) optimization.
+#[derive(Clone, Debug)]
+pub(crate) struct InputRateTracker {
+ timestamps: Vec<Instant>,
+ window: Duration,
+ inputs_per_second: u32,
+ sustain_until: Instant,
+ sustain_duration: Duration,
+}
+
+impl Default for InputRateTracker {
+ fn default() -> Self {
+ Self {
+ timestamps: Vec::new(),
+ window: Duration::from_millis(100),
+ inputs_per_second: 60,
+ sustain_until: Instant::now(),
+ sustain_duration: Duration::from_secs(1),
+ }
+ }
+}
+
+impl InputRateTracker {
+ pub fn record_input(&mut self) {
+ let now = Instant::now();
+ self.timestamps.push(now);
+ self.prune_old_timestamps(now);
+
+ let min_events = self.inputs_per_second as u128 * self.window.as_millis() / 1000;
+ if self.timestamps.len() as u128 >= min_events {
+ self.sustain_until = now + self.sustain_duration;
+ }
+ }
+
+ pub fn is_high_rate(&self) -> bool {
+ Instant::now() < self.sustain_until
+ }
+
+ fn prune_old_timestamps(&mut self, now: Instant) {
+ self.timestamps
+ .retain(|&t| now.duration_since(t) <= self.window);
+ }
+}
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DrawPhase {
None,
@@ -1047,7 +1094,7 @@ impl Window {
let hovered = Rc::new(Cell::new(platform_window.is_hovered()));
let needs_present = Rc::new(Cell::new(false));
let next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>> = Default::default();
- let last_input_timestamp = Rc::new(Cell::new(Instant::now()));
+ let input_rate_tracker = Rc::new(RefCell::new(InputRateTracker::default()));
platform_window
.request_decorations(window_decorations.unwrap_or(WindowDecorations::Server));
@@ -1075,7 +1122,7 @@ impl Window {
let active = active.clone();
let needs_present = needs_present.clone();
let next_frame_callbacks = next_frame_callbacks.clone();
- let last_input_timestamp = last_input_timestamp.clone();
+ let input_rate_tracker = input_rate_tracker.clone();
move |request_frame_options| {
let next_frame_callbacks = next_frame_callbacks.take();
if !next_frame_callbacks.is_empty() {
@@ -1088,12 +1135,12 @@ impl Window {
.log_err();
}
- // Keep presenting the current scene for 1 extra second since the
- // last input to prevent the display from underclocking the refresh rate.
+ // Keep presenting if input was recently arriving at a high rate (>= 60fps).
+ // Once high-rate input is detected, we sustain presentation for 1 second
+ // to prevent display underclocking during active input.
let needs_present = request_frame_options.require_presentation
|| needs_present.get()
- || (active.get()
- && last_input_timestamp.get().elapsed() < Duration::from_secs(1));
+ || (active.get() && input_rate_tracker.borrow_mut().is_high_rate());
if invalidator.is_dirty() || request_frame_options.force_render {
measure("frame duration", || {
@@ -1101,7 +1148,6 @@ impl Window {
.update(&mut cx, |_, window, cx| {
let arena_clear_needed = window.draw(cx);
window.present();
- // drop the arena elements after present to reduce latency
arena_clear_needed.clear();
})
.log_err();
@@ -1299,7 +1345,7 @@ impl Window {
active,
hovered,
needs_present,
- last_input_timestamp,
+ input_rate_tracker,
last_input_modality: InputModality::Mouse,
refreshing: false,
activation_observers: SubscriberSet::new(),
@@ -3691,8 +3737,6 @@ impl Window {
/// Dispatch a mouse or keyboard event on the window.
#[profiling::function]
pub fn dispatch_event(&mut self, event: PlatformInput, cx: &mut App) -> DispatchEventResult {
- self.last_input_timestamp.set(Instant::now());
-
// Track whether this input was keyboard-based for focus-visible styling
self.last_input_modality = match &event {
PlatformInput::KeyDown(_) | PlatformInput::ModifiersChanged(_) => {
@@ -3793,6 +3837,10 @@ impl Window {
self.dispatch_key_event(any_key_event, cx);
}
+ if self.invalidator.is_dirty() {
+ self.input_rate_tracker.borrow_mut().record_input();
+ }
+
DispatchEventResult {
propagate: cx.propagate_event,
default_prevented: self.default_prevented,