Skip to content

Commit 6cc8153

Browse files
committed
bevy_winit: defer render state initialization
On Android the render state is now only initialized when the application resumes for the first time (at which point it as a valid SurfaceView). On all other platforms the render state will be initialized based on the `NewEvents(Init)` event (though this may change in the future if Winit starts to emit Resumed events consistently for all platforms). Considering how some events (CreateWindow) events can only be handled along with render state initialization (at least on Android) this change also blocks all app update()s until the render state is initialized, otherwise these events are liable to be cleared before they can be handled.
1 parent 5888b26 commit 6cc8153

File tree

1 file changed

+49
-27
lines changed

1 file changed

+49
-27
lines changed

crates/bevy_winit/src/lib.rs

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,8 @@ impl Plugin for WinitPlugin {
4848
#[cfg(target_arch = "wasm32")]
4949
app.add_plugin(web_resize::CanvasParentResizePlugin);
5050
let event_loop = EventLoop::new();
51-
let mut create_window_reader = WinitCreateWindowReader::default();
52-
// Note that we create a window here "early" because WASM/WebGL requires the window to exist prior to initializing
53-
// the renderer.
54-
handle_create_window_events(&mut app.world, &event_loop, &mut create_window_reader.0);
55-
app.insert_resource(create_window_reader)
56-
.insert_non_send_resource(event_loop);
51+
52+
app.insert_non_send_resource(event_loop);
5753
}
5854
}
5955

@@ -256,6 +252,8 @@ pub fn winit_runner(app: App) {
256252

257253
/// Stores state that must persist between frames.
258254
struct WinitPersistentState {
255+
/// Tracks whether the application has reached its first `Resumed` event
256+
reached_first_resume: bool,
259257
/// Tracks whether or not the application is active or suspended.
260258
active: bool,
261259
/// Tracks whether or not an event has occurred this frame that would trigger an update in low
@@ -270,7 +268,14 @@ struct WinitPersistentState {
270268
impl Default for WinitPersistentState {
271269
fn default() -> Self {
272270
Self {
273-
active: true,
271+
reached_first_resume: false,
272+
273+
// We have to start non-active and block update()s until we get our
274+
// first 'Resumed' event where we will first check for any CreateWindow requests.
275+
// If we don't block updates then the Events::update system will clear the requests
276+
// before they are handled!
277+
active: false,
278+
274279
low_power_event: false,
275280
redraw_request_sent: false,
276281
timeout_reached: false,
@@ -279,20 +284,12 @@ impl Default for WinitPersistentState {
279284
}
280285
}
281286

282-
#[derive(Default)]
283-
struct WinitCreateWindowReader(ManualEventReader<CreateWindow>);
284-
285287
pub fn winit_runner_with(mut app: App) {
286-
app.render_init();
287288
let mut event_loop = app
288289
.world
289290
.remove_non_send_resource::<EventLoop<()>>()
290291
.unwrap();
291-
let mut create_window_event_reader = app
292-
.world
293-
.remove_resource::<WinitCreateWindowReader>()
294-
.unwrap()
295-
.0;
292+
let mut create_window_event_reader = ManualEventReader::<CreateWindow>::default();
296293
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
297294
let mut redraw_event_reader = ManualEventReader::<RequestRedraw>::default();
298295
let mut winit_state = WinitPersistentState::default();
@@ -307,6 +304,32 @@ pub fn winit_runner_with(mut app: App) {
307304
event_loop: &EventLoopWindowTarget<()>,
308305
control_flow: &mut ControlFlow| {
309306
match event {
307+
// Ideally Winit would emit `Resumed` events consistently for all platforms but for
308+
// now we treat NewEvents(Init) the same as a Resumed event for all platforms except
309+
// Android
310+
event::Event::Resumed | event::Event::NewEvents(StartCause::Init) => {
311+
if cfg!(not(target_os = "android")) || matches!(event, event::Event::Resumed) {
312+
winit_state.active = true;
313+
314+
if !winit_state.reached_first_resume {
315+
// Create any primary winit window that's been requested before we initialize
316+
// render state. This ensures it's possible to create a surface for the
317+
// window that can in turn be used to find a compatible adapter.
318+
//
319+
// In particular this is required for webgl
320+
handle_create_window_events(
321+
&mut app.world,
322+
event_loop,
323+
&mut create_window_event_reader,
324+
);
325+
app.render_init();
326+
winit_state.reached_first_resume = true;
327+
}
328+
}
329+
}
330+
event::Event::Suspended => {
331+
winit_state.active = false;
332+
}
310333
event::Event::NewEvents(start) => {
311334
let winit_config = app.world.resource::<WinitSettings>();
312335
let windows = app.world.resource::<Windows>();
@@ -550,18 +573,17 @@ pub fn winit_runner_with(mut app: App) {
550573
delta: Vec2::new(delta.0 as f32, delta.1 as f32),
551574
});
552575
}
553-
event::Event::Suspended => {
554-
winit_state.active = false;
555-
}
556-
event::Event::Resumed => {
557-
winit_state.active = true;
558-
}
559576
event::Event::MainEventsCleared => {
560-
handle_create_window_events(
561-
&mut app.world,
562-
event_loop,
563-
&mut create_window_event_reader,
564-
);
577+
// We only initialize render state and start creating any
578+
// windows once the app has has 'resumed' for the first time.
579+
if winit_state.reached_first_resume {
580+
handle_create_window_events(
581+
&mut app.world,
582+
event_loop,
583+
&mut create_window_event_reader,
584+
);
585+
}
586+
565587
let winit_config = app.world.resource::<WinitSettings>();
566588
let update = if winit_state.active {
567589
let windows = app.world.resource::<Windows>();

0 commit comments

Comments
 (0)