From 4b671763b2b3bff24dc2cbf91c6d0eb9c476baa3 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Mon, 7 Jul 2025 23:45:24 +0200 Subject: [PATCH 1/4] Move renderer initialization to RenderStartup --- crates/bevy_render/src/lib.rs | 185 ++++++++++++++++++---------------- 1 file changed, 100 insertions(+), 85 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7a2ad060878b0..69d732c316059 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -104,6 +104,7 @@ use sync_world::{ }; use crate::gpu_readback::GpuReadbackPlugin; +use crate::settings::WgpuSettings; use crate::{ camera::CameraPlugin, mesh::{MeshPlugin, MorphPlugin, RenderMesh}, @@ -121,6 +122,7 @@ use bevy_ecs::{prelude::*, schedule::ScheduleLabel}; use bevy_utils::WgpuWrapper; use bitflags::bitflags; use core::ops::{Deref, DerefMut}; +use std::panic; use std::sync::Mutex; use tracing::debug; @@ -342,96 +344,12 @@ impl Plugin for RenderPlugin { unsafe { initialize_render_app(app) }; } RenderCreation::Automatic(render_creation) => { - if let Some(backends) = render_creation.backends { + if render_creation.backends.is_some() { let future_render_resources_wrapper = Arc::new(Mutex::new(None)); app.insert_resource(FutureRenderResources( future_render_resources_wrapper.clone(), )); - let primary_window = app - .world_mut() - .query_filtered::<&RawHandleWrapperHolder, With>() - .single(app.world()) - .ok() - .cloned(); - let settings = render_creation.clone(); - let async_renderer = async move { - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { - backends, - flags: settings.instance_flags, - backend_options: wgpu::BackendOptions { - gl: wgpu::GlBackendOptions { - gles_minor_version: settings.gles3_minor_version, - fence_behavior: wgpu::GlFenceBehavior::Normal, - }, - dx12: wgpu::Dx12BackendOptions { - shader_compiler: settings.dx12_shader_compiler.clone(), - }, - noop: wgpu::NoopBackendOptions { enable: false }, - }, - }); - - let surface = primary_window.and_then(|wrapper| { - let maybe_handle = wrapper.0.lock().expect( - "Couldn't get the window handle in time for renderer initialization", - ); - if let Some(wrapper) = maybe_handle.as_ref() { - // SAFETY: Plugins should be set up on the main thread. - let handle = unsafe { wrapper.get_handle() }; - Some( - instance - .create_surface(handle) - .expect("Failed to create wgpu surface"), - ) - } else { - None - } - }); - - let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER") - .map_or(settings.force_fallback_adapter, |v| { - !(v.is_empty() || v == "0" || v == "false") - }); - - let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME") - .as_deref() - .map_or(settings.adapter_name.clone(), |x| Some(x.to_lowercase())); - - let request_adapter_options = wgpu::RequestAdapterOptions { - power_preference: settings.power_preference, - compatible_surface: surface.as_ref(), - force_fallback_adapter, - }; - - let (device, queue, adapter_info, render_adapter) = - renderer::initialize_renderer( - &instance, - &settings, - &request_adapter_options, - desired_adapter_name, - ) - .await; - debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); - debug!("Configured wgpu adapter Features: {:#?}", device.features()); - let mut future_render_resources_inner = - future_render_resources_wrapper.lock().unwrap(); - *future_render_resources_inner = Some(RenderResources( - device, - queue, - adapter_info, - render_adapter, - RenderInstance(Arc::new(WgpuWrapper::new(instance))), - )); - }; - // In wasm, spawn a task and detach it for execution - #[cfg(target_arch = "wasm32")] - bevy_tasks::IoTaskPool::get() - .spawn_local(async_renderer) - .detach(); - // Otherwise, just block for it to complete - #[cfg(not(target_arch = "wasm32"))] - futures_lite::future::block_on(async_renderer); - // SAFETY: Plugins should be set up on the main thread. unsafe { initialize_render_app(app) }; } @@ -465,6 +383,13 @@ impl Plugin for RenderPlugin { Render, reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup), ); + if let RenderCreation::Automatic(render_creation) = &self.render_creation { + if render_creation.backends.is_some() { + render_app + .insert_resource(AutomaticRendererCreationSettings(render_creation.clone())) + .add_systems(RenderStartup, initialize_renderer); + } + } } app.register_type::() @@ -520,6 +445,96 @@ impl Plugin for RenderPlugin { } } +#[derive(Resource)] +struct AutomaticRendererCreationSettings(WgpuSettings); + +fn initialize_renderer( + primary_window: Option>>, + future_render_resources: Res, + render_creation: When>, +) { + let primary_window = primary_window.map(|primary_window| primary_window.clone()); + let settings = render_creation.into_inner().0.clone(); + let Some(backends) = settings.backends else { + return; + }; + let async_renderer = async move { + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + backends, + flags: settings.instance_flags, + backend_options: wgpu::BackendOptions { + gl: wgpu::GlBackendOptions { + gles_minor_version: settings.gles3_minor_version, + fence_behavior: wgpu::GlFenceBehavior::Normal, + }, + dx12: wgpu::Dx12BackendOptions { + shader_compiler: settings.dx12_shader_compiler.clone(), + }, + noop: wgpu::NoopBackendOptions { enable: false }, + }, + }); + + let surface = primary_window.and_then(|wrapper| { + let maybe_handle = wrapper + .0 + .lock() + .expect("Couldn't get the window handle in time for renderer initialization"); + if let Some(wrapper) = maybe_handle.as_ref() { + // SAFETY: Plugins should be set up on the main thread. + let handle = unsafe { wrapper.get_handle() }; + Some( + instance + .create_surface(handle) + .expect("Failed to create wgpu surface"), + ) + } else { + None + } + }); + + let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER") + .map_or(settings.force_fallback_adapter, |v| { + !(v.is_empty() || v == "0" || v == "false") + }); + + let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME") + .as_deref() + .map_or(settings.adapter_name.clone(), |x| Some(x.to_lowercase())); + + let request_adapter_options = wgpu::RequestAdapterOptions { + power_preference: settings.power_preference, + compatible_surface: surface.as_ref(), + force_fallback_adapter, + }; + + let (device, queue, adapter_info, render_adapter) = renderer::initialize_renderer( + &instance, + &settings, + &request_adapter_options, + desired_adapter_name, + ) + .await; + debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); + debug!("Configured wgpu adapter Features: {:#?}", device.features()); + let mut future_render_resources_inner = future_render_resources.0.lock().unwrap(); + *future_render_resources_inner = Some(RenderResources( + device, + queue, + adapter_info, + render_adapter, + RenderInstance(Arc::new(WgpuWrapper::new(instance))), + )); + }; + // In wasm, spawn a task and detach it for execution + #[cfg(target_arch = "wasm32")] + bevy_tasks::IoTaskPool::get() + .spawn_local(async_renderer) + .detach(); + // Otherwise, just block for it to complete + #[cfg(not(target_arch = "wasm32"))] + futures_lite::future::block_on(async_renderer); +} + /// A "scratch" world used to avoid allocating new worlds every frame when /// swapping out the [`MainWorld`] for [`ExtractSchedule`]. #[derive(Resource, Default)] From d748ea442732f70c490bab18cb18f747dcbe7e62 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 8 Jul 2025 00:19:00 +0200 Subject: [PATCH 2/4] Move renderer initialization to PreStartup --- crates/bevy_render/src/lib.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 69d732c316059..27174e7dab46d 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -116,7 +116,7 @@ use crate::{ view::{ViewPlugin, WindowRenderPlugin}, }; use alloc::sync::Arc; -use bevy_app::{App, AppLabel, Plugin, SubApp}; +use bevy_app::{App, AppLabel, Plugin, PreStartup, SubApp}; use bevy_asset::{AssetApp, AssetServer}; use bevy_ecs::{prelude::*, schedule::ScheduleLabel}; use bevy_utils::WgpuWrapper; @@ -348,7 +348,9 @@ impl Plugin for RenderPlugin { let future_render_resources_wrapper = Arc::new(Mutex::new(None)); app.insert_resource(FutureRenderResources( future_render_resources_wrapper.clone(), - )); + )) + .insert_resource(AutomaticRendererCreationSettings(render_creation.clone())) + .add_systems(PreStartup, initialize_renderer); // SAFETY: Plugins should be set up on the main thread. unsafe { initialize_render_app(app) }; @@ -383,13 +385,6 @@ impl Plugin for RenderPlugin { Render, reset_render_asset_bytes_per_frame.in_set(RenderSystems::Cleanup), ); - if let RenderCreation::Automatic(render_creation) = &self.render_creation { - if render_creation.backends.is_some() { - render_app - .insert_resource(AutomaticRendererCreationSettings(render_creation.clone())) - .add_systems(RenderStartup, initialize_renderer); - } - } } app.register_type::() From ae6f9c8e22f8fc2369e6fab92ba225bc7376dbae Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 8 Jul 2025 00:35:35 +0200 Subject: [PATCH 3/4] Fix FutureRenderResources being removed too early --- crates/bevy_render/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 27174e7dab46d..d59dff76b1b3b 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -408,7 +408,7 @@ impl Plugin for RenderPlugin { load_shader_library!(app, "color_operations.wgsl"); load_shader_library!(app, "bindless.wgsl"); if let Some(future_render_resources) = - app.world_mut().remove_resource::() + app.world_mut().get_resource::() { let RenderResources(device, queue, adapter_info, render_adapter, instance) = future_render_resources.0.lock().unwrap().take().unwrap(); @@ -446,7 +446,7 @@ struct AutomaticRendererCreationSettings(WgpuSettings); fn initialize_renderer( primary_window: Option>>, future_render_resources: Res, - render_creation: When>, + render_creation: Res, ) { let primary_window = primary_window.map(|primary_window| primary_window.clone()); let settings = render_creation.into_inner().0.clone(); From 362143b7adb8011484e2cbf41b8b43bae8a813fa Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 8 Jul 2025 00:38:40 +0200 Subject: [PATCH 4/4] Fix render app never being ready --- crates/bevy_render/src/lib.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index d59dff76b1b3b..b534ad7ba0a72 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -122,7 +122,6 @@ use bevy_ecs::{prelude::*, schedule::ScheduleLabel}; use bevy_utils::WgpuWrapper; use bitflags::bitflags; use core::ops::{Deref, DerefMut}; -use std::panic; use std::sync::Mutex; use tracing::debug; @@ -396,13 +395,6 @@ impl Plugin for RenderPlugin { .register_type::(); } - fn ready(&self, app: &App) -> bool { - app.world() - .get_resource::() - .and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok()) - .unwrap_or(true) - } - fn finish(&self, app: &mut App) { load_shader_library!(app, "maths.wgsl"); load_shader_library!(app, "color_operations.wgsl");