Bevy version and features
Bevy 0.19.1, default-features = false, features = ["3d"]. No third-party plugins.
Relevant system information
- rustc 1.97.1 (8bab26f4f 2026-07-14), cargo 1.97.1
- Windows 11 Pro 10.0.26200
AdapterInfo { name: "NVIDIA GeForce RTX 5070 Ti", vendor: 4318, device: 11269, device_type: DiscreteGpu, device_pci_bus_id: "0000:01:00.0", driver: "NVIDIA", driver_info: "616.56", backend: Vulkan, subgroup_min_size: 32, subgroup_max_size: 32, transient_saves_memory: false }
What you did
I wanted to cut the update rate right down while the window is being dragged by
its title bar, as a workaround for rust-windowing/winit#4708. On the first
WindowMoved I switch both update modes to a long Reactive wait with no wake
sources, and switch back to Continuous once the moves stop.
use bevy::prelude::*;
use bevy::window::WindowMoved;
use bevy::winit::{UpdateMode, WinitSettings};
use std::time::{Duration, Instant};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Update, (count, throttle_while_moved))
.run();
}
fn count(mut n: Local<u32>, mut started: Local<Option<Instant>>) {
let began = *started.get_or_insert_with(Instant::now);
*n += 1;
println!("update {} at {:.2}s", *n, began.elapsed().as_secs_f64());
}
fn throttle_while_moved(
mut moves: MessageReader<WindowMoved>,
mut settings: ResMut<WinitSettings>,
mut last: Local<Option<Instant>>,
mut was: Local<bool>,
) {
if !moves.is_empty() {
*last = Some(Instant::now());
}
moves.clear();
let moving = last.is_some_and(|w| w.elapsed() < Duration::from_millis(300));
if moving == *was {
return;
}
*was = moving;
println!(">>> {}", if moving { "moving" } else { "settled" });
let mode = if moving {
UpdateMode::Reactive {
wait: Duration::from_secs(2),
react_to_device_events: false,
react_to_user_events: false,
react_to_window_events: false,
}
} else {
UpdateMode::Continuous
};
settings.focused_mode = mode;
settings.unfocused_mode = mode;
}
Run it and drag the window by its title bar.
What went wrong
Expected: updates drop to one every 2 seconds while the window is moving, and
the system sees the moves stop and puts the mode back to Continuous.
Actually happened: the app updates for about another 0.6 seconds, then stops
updating permanently. Not a low rate, zero. The window never repaints again, does
not respond to clicks or keys, and never prints ">>> settled" because no system
ever runs again. The process has to be killed.
update 7425 at 6.41s
>>> moving
update 7426 at 6.41s
...
update 7984 at 7.03s <- last update, ~25s before the process was killed
Additional information
The same config is fine when no drag is involved. Replacing the trigger with
a plain timer, so that nothing else changes, gives exactly the documented
behaviour forever:
fn throttle_after_three_seconds(
mut settings: ResMut<WinitSettings>,
mut started: Local<Option<Instant>>,
mut done: Local<bool>,
) {
let began = *started.get_or_insert_with(Instant::now);
if *done || began.elapsed() < Duration::from_secs(3) { return; }
*done = true;
// ...same mode assignment as above...
}
update 5640 at 5.00s
update 5641 at 7.00s
update 5642 at 9.00s
update 5643 at 11.00s
update 5644 at 13.00s
update 5645 at 15.00s
update 5646 at 17.00s
update 5647 at 19.00s
So Reactive with no wake sources is not broken by itself. Something about
entering that mode during the Windows modal move loop leaves the event loop with
no standing wake-up, and there is then no path back.
I instrumented a local bevy_winit/src/state.rs to print the StartCause and the
gate values on each pass. During the hang: 950 consecutive passes of
cause=WaitCancelled wait_elapsed=false should_update=false, and
StartCause::ResumeTimeReached never appears again.
One candidate: about_to_wait only arms the timeout when wait_elapsed is
already true.
UpdateMode::Reactive { wait, .. } => {
if self.wait_elapsed {
...
event_loop.set_control_flow(ControlFlow::WaitUntil(next));
}
}
I do not know whether this belongs to Bevy or to winit's handling of the modal
move loop.
Related: rust-windowing/winit#4708
Bevy version and features
Bevy 0.19.1,
default-features = false, features = ["3d"]. No third-party plugins.Relevant system information
AdapterInfo { name: "NVIDIA GeForce RTX 5070 Ti", vendor: 4318, device: 11269, device_type: DiscreteGpu, device_pci_bus_id: "0000:01:00.0", driver: "NVIDIA", driver_info: "616.56", backend: Vulkan, subgroup_min_size: 32, subgroup_max_size: 32, transient_saves_memory: false }What you did
I wanted to cut the update rate right down while the window is being dragged by
its title bar, as a workaround for rust-windowing/winit#4708. On the first
WindowMoved I switch both update modes to a long Reactive wait with no wake
sources, and switch back to Continuous once the moves stop.
Run it and drag the window by its title bar.
What went wrong
Expected: updates drop to one every 2 seconds while the window is moving, and
the system sees the moves stop and puts the mode back to Continuous.
Actually happened: the app updates for about another 0.6 seconds, then stops
updating permanently. Not a low rate, zero. The window never repaints again, does
not respond to clicks or keys, and never prints ">>> settled" because no system
ever runs again. The process has to be killed.
Additional information
The same config is fine when no drag is involved. Replacing the trigger with
a plain timer, so that nothing else changes, gives exactly the documented
behaviour forever:
So
Reactivewith no wake sources is not broken by itself. Something aboutentering that mode during the Windows modal move loop leaves the event loop with
no standing wake-up, and there is then no path back.
I instrumented a local
bevy_winit/src/state.rsto print the StartCause and thegate values on each pass. During the hang: 950 consecutive passes of
cause=WaitCancelled wait_elapsed=false should_update=false, andStartCause::ResumeTimeReachednever appears again.One candidate:
about_to_waitonly arms the timeout whenwait_elapsedisalready true.
I do not know whether this belongs to Bevy or to winit's handling of the modal
move loop.
Related: rust-windowing/winit#4708