From fb14c55beb3bfafc4bf7c8f2cabc100967d06e26 Mon Sep 17 00:00:00 2001 From: awtterpip Date: Mon, 25 Sep 2023 19:00:11 -0500 Subject: [PATCH 1/5] changed render initialization to allow other plugins to create render resources --- crates/bevy_render/src/lib.rs | 267 ++++++++++++++----------- crates/bevy_render/src/renderer/mod.rs | 4 +- crates/bevy_render/src/settings.rs | 42 ++++ examples/3d/wireframe.rs | 5 +- examples/app/no_renderer.rs | 15 +- 5 files changed, 205 insertions(+), 128 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index c72353df47fd6..14a00bb5dd38a 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -45,7 +45,6 @@ use bevy_hierarchy::ValidParentCheckPlugin; use bevy_window::{PrimaryWindow, RawHandleWrapper}; use globals::GlobalsPlugin; use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}; -use wgpu::Instance; use crate::{ camera::CameraPlugin, @@ -53,7 +52,7 @@ use crate::{ render_asset::prepare_assets, render_resource::{PipelineCache, Shader, ShaderLoader}, renderer::{render_system, RenderInstance}, - settings::WgpuSettings, + settings::RenderSettings, view::{ViewPlugin, WindowRenderPlugin}, }; use bevy_app::{App, AppLabel, Plugin, SubApp}; @@ -68,7 +67,7 @@ use std::{ /// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] pub struct RenderPlugin { - pub wgpu_settings: WgpuSettings, + pub render_settings: RenderSettings, } /// The labels of the default App rendering sets. @@ -221,7 +220,7 @@ struct FutureRendererResources( RenderQueue, RenderAdapterInfo, RenderAdapter, - Instance, + RenderInstance, )>, >, >, @@ -241,120 +240,84 @@ impl Plugin for RenderPlugin { app.init_asset::() .init_asset_loader::(); - if let Some(backends) = self.wgpu_settings.backends { - let future_renderer_resources_wrapper = Arc::new(Mutex::new(None)); - app.insert_resource(FutureRendererResources( - future_renderer_resources_wrapper.clone(), - )); - - let mut system_state: SystemState>> = - SystemState::new(&mut app.world); - let primary_window = system_state.get(&app.world).get_single().ok().cloned(); - - let settings = self.wgpu_settings.clone(); - let async_renderer = async move { - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends, - dx12_shader_compiler: settings.dx12_shader_compiler.clone(), - }); - let surface = primary_window.map(|wrapper| unsafe { - // SAFETY: Plugins should be set up on the main thread. - let handle = wrapper.get_handle(); - instance - .create_surface(&handle) - .expect("Failed to create wgpu surface") - }); - - let request_adapter_options = wgpu::RequestAdapterOptions { - power_preference: settings.power_preference, - compatible_surface: surface.as_ref(), - ..Default::default() - }; - - let (device, queue, adapter_info, render_adapter) = - renderer::initialize_renderer(&instance, &settings, &request_adapter_options) - .await; - debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); - debug!("Configured wgpu adapter Features: {:#?}", device.features()); - let mut future_renderer_resources_inner = - future_renderer_resources_wrapper.lock().unwrap(); - *future_renderer_resources_inner = - Some((device, queue, adapter_info, render_adapter, 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); - - app.init_resource::(); - - let mut render_app = App::empty(); - render_app.main_schedule_label = Box::new(Render); - - let mut extract_schedule = Schedule::new(ExtractSchedule); - extract_schedule.set_apply_final_deferred(false); - - render_app - .add_schedule(extract_schedule) - .add_schedule(Render::base_schedule()) - .init_resource::() - .insert_resource(app.world.resource::().clone()) - .add_systems(ExtractSchedule, PipelineCache::extract_shaders) - .add_systems( - Render, - ( - // This set applies the commands from the extract schedule while the render schedule - // is running in parallel with the main app. - apply_extract_commands.in_set(RenderSet::ExtractCommands), - ( - PipelineCache::process_pipeline_queue_system.before(render_system), - render_system, - ) - .in_set(RenderSet::Render), - World::clear_entities.in_set(RenderSet::Cleanup), - ), - ); - - let (sender, receiver) = bevy_time::create_time_channels(); - app.insert_resource(receiver); - render_app.insert_resource(sender); - - app.insert_sub_app(RenderApp, SubApp::new(render_app, move |main_world, render_app| { - #[cfg(feature = "trace")] - let _render_span = bevy_utils::tracing::info_span!("extract main app to render subapp").entered(); - { - #[cfg(feature = "trace")] - let _stage_span = - bevy_utils::tracing::info_span!("reserve_and_flush") - .entered(); - - // reserve all existing main world entities for use in render_app - // they can only be spawned using `get_or_spawn()` - let total_count = main_world.entities().total_count(); - - assert_eq!( - render_app.world.entities().len(), - 0, - "An entity was spawned after the entity list was cleared last frame and before the extract schedule began. This is not supported", - ); - - // This is safe given the clear_entities call in the past frame and the assert above - unsafe { - render_app - .world - .entities_mut() - .flush_and_reserve_invalid_assuming_no_entities(total_count); - } + match &self.render_settings { + RenderSettings::Manual(device, queue, adapter_info, adapter, instance) => { + let future_renderer_resources_wrapper = Arc::new(Mutex::new(Some(( + device.clone(), + queue.clone(), + adapter_info.clone(), + adapter.clone(), + instance.clone(), + )))); + app.insert_resource(FutureRendererResources( + future_renderer_resources_wrapper.clone(), + )); + unsafe { initialize_render_app(app) }; + } + RenderSettings::Automatic(render_settings) => { + if let Some(backends) = render_settings.backends { + let future_renderer_resources_wrapper = Arc::new(Mutex::new(None)); + app.insert_resource(FutureRendererResources( + future_renderer_resources_wrapper.clone(), + )); + + let mut system_state: SystemState< + Query<&RawHandleWrapper, With>, + > = SystemState::new(&mut app.world); + let primary_window = system_state.get(&app.world).get_single().ok().cloned(); + + let settings = render_settings.clone(); + let async_renderer = async move { + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends, + dx12_shader_compiler: settings.dx12_shader_compiler.clone(), + }); + let surface = primary_window.map(|wrapper| unsafe { + // SAFETY: Plugins should be set up on the main thread. + let handle = wrapper.get_handle(); + instance + .create_surface(&handle) + .expect("Failed to create wgpu surface") + }); + + let request_adapter_options = wgpu::RequestAdapterOptions { + power_preference: settings.power_preference, + compatible_surface: surface.as_ref(), + ..Default::default() + }; + + let (device, queue, adapter_info, render_adapter) = + renderer::initialize_renderer( + &instance, + &settings, + &request_adapter_options, + ) + .await; + debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); + debug!("Configured wgpu adapter Features: {:#?}", device.features()); + let mut future_renderer_resources_inner = + future_renderer_resources_wrapper.lock().unwrap(); + *future_renderer_resources_inner = Some(( + device, + queue, + adapter_info, + render_adapter, + RenderInstance(Arc::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); + + unsafe { initialize_render_app(app) }; } - - // run extract schedule - extract(main_world, render_app); - })); - } + } + }; app.add_plugins(( ValidParentCheckPlugin::::default(), @@ -406,7 +369,7 @@ impl Plugin for RenderPlugin { let render_app = app.sub_app_mut(RenderApp); render_app - .insert_resource(RenderInstance(instance)) + .insert_resource(instance) .insert_resource(PipelineCache::new(device.clone())) .insert_resource(device) .insert_resource(queue) @@ -437,6 +400,74 @@ fn extract(main_world: &mut World, render_app: &mut App) { main_world.insert_resource(ScratchMainWorld(scratch_world)); } +/// SAFETY: this function must be called from the main thread. +unsafe fn initialize_render_app(app: &mut App) { + app.init_resource::(); + + let mut render_app = App::empty(); + render_app.main_schedule_label = Box::new(Render); + + let mut extract_schedule = Schedule::new(ExtractSchedule); + extract_schedule.set_apply_final_deferred(false); + + render_app + .add_schedule(extract_schedule) + .add_schedule(Render::base_schedule()) + .init_resource::() + .insert_resource(app.world.resource::().clone()) + .add_systems(ExtractSchedule, PipelineCache::extract_shaders) + .add_systems( + Render, + ( + // This set applies the commands from the extract schedule while the render schedule + // is running in parallel with the main app. + apply_extract_commands.in_set(RenderSet::ExtractCommands), + ( + PipelineCache::process_pipeline_queue_system.before(render_system), + render_system, + ) + .in_set(RenderSet::Render), + World::clear_entities.in_set(RenderSet::Cleanup), + ), + ); + + let (sender, receiver) = bevy_time::create_time_channels(); + app.insert_resource(receiver); + render_app.insert_resource(sender); + + app.insert_sub_app(RenderApp, SubApp::new(render_app, move |main_world, render_app| { + #[cfg(feature = "trace")] + let _render_span = bevy_utils::tracing::info_span!("extract main app to render subapp").entered(); + { + #[cfg(feature = "trace")] + let _stage_span = + bevy_utils::tracing::info_span!("reserve_and_flush") + .entered(); + + // reserve all existing main world entities for use in render_app + // they can only be spawned using `get_or_spawn()` + let total_count = main_world.entities().total_count(); + + assert_eq!( + render_app.world.entities().len(), + 0, + "An entity was spawned after the entity list was cleared last frame and before the extract schedule began. This is not supported", + ); + + // This is safe given the clear_entities call in the past frame and the assert above + unsafe { + render_app + .world + .entities_mut() + .flush_and_reserve_invalid_assuming_no_entities(total_count); + } + } + + // run extract schedule + extract(main_world, render_app); + })); +} + /// Applies the commands from the extract schedule. This happens during /// the render schedule rather than during extraction to allow the commands to run in parallel with the /// main app when pipelined rendering is enabled. diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 6a9d9be6e162d..f98eb81189375 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -104,8 +104,8 @@ pub struct RenderAdapter(pub Arc); /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], /// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). -#[derive(Resource, Deref, DerefMut)] -pub struct RenderInstance(pub Instance); +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct RenderInstance(pub Arc); /// The [`AdapterInfo`] of the adapter in use by the renderer. #[derive(Resource, Clone, Deref, DerefMut)] diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index 5f05770c12e28..c93165554bce6 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -1,3 +1,6 @@ +use crate::renderer::{ + RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue, +}; use std::borrow::Cow; pub use wgpu::{ @@ -93,6 +96,45 @@ impl Default for WgpuSettings { } } +/// An enum describing how the renderer will initialize resources. This is used when creating the [`RenderPlugin`](crate::RenderPlugin). +pub enum RenderSettings { + /// Allows renderer resource initialization to happen outside of the rendering plugin. + Manual( + RenderDevice, + RenderQueue, + RenderAdapterInfo, + RenderAdapter, + RenderInstance, + ), + /// Lets the rendering plugin create resources itself. + Automatic(WgpuSettings), +} + +impl RenderSettings { + /// Function to create a [`RenderSettings::Manual`] variant. + pub fn manual( + device: RenderDevice, + queue: RenderQueue, + adapter_info: RenderAdapterInfo, + adapter: RenderAdapter, + instance: RenderInstance, + ) -> Self { + Self::Manual(device, queue, adapter_info, adapter, instance) + } +} + +impl Default for RenderSettings { + fn default() -> Self { + Self::Automatic(Default::default()) + } +} + +impl From for RenderSettings { + fn from(value: WgpuSettings) -> Self { + Self::Automatic(value) + } +} + /// Get a features/limits priority from the environment variable `WGPU_SETTINGS_PRIO` pub fn settings_priority_from_env() -> Option { Some( diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 277ab902a1910..9ea4aebbf6c21 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -10,10 +10,11 @@ fn main() { App::new() .add_plugins(( DefaultPlugins.set(RenderPlugin { - wgpu_settings: WgpuSettings { + render_settings: WgpuSettings { features: WgpuFeatures::POLYGON_MODE_LINE, ..default() - }, + } + .into(), }), WireframePlugin, )) diff --git a/examples/app/no_renderer.rs b/examples/app/no_renderer.rs index 2c892e4b123fd..cb0b766474782 100644 --- a/examples/app/no_renderer.rs +++ b/examples/app/no_renderer.rs @@ -11,11 +11,14 @@ use bevy::{ fn main() { App::new() - .add_plugins(DefaultPlugins.set(RenderPlugin { - wgpu_settings: WgpuSettings { - backends: None, - ..default() - }, - })) + .add_plugins( + DefaultPlugins.set(RenderPlugin { + render_settings: WgpuSettings { + backends: None, + ..default() + } + .into(), + }), + ) .run(); } From bfb10b1a0f9a0b3e5d596e309219ddd604ada4c1 Mon Sep 17 00:00:00 2001 From: awtterpip Date: Tue, 26 Sep 2023 12:33:00 -0500 Subject: [PATCH 2/5] renamed RenderSettings to RenderCreation --- crates/bevy_render/src/lib.rs | 8 ++++---- crates/bevy_render/src/settings.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 14a00bb5dd38a..27ec8260ed5e7 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -52,7 +52,7 @@ use crate::{ render_asset::prepare_assets, render_resource::{PipelineCache, Shader, ShaderLoader}, renderer::{render_system, RenderInstance}, - settings::RenderSettings, + settings::RenderCreation, view::{ViewPlugin, WindowRenderPlugin}, }; use bevy_app::{App, AppLabel, Plugin, SubApp}; @@ -67,7 +67,7 @@ use std::{ /// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] pub struct RenderPlugin { - pub render_settings: RenderSettings, + pub render_settings: RenderCreation, } /// The labels of the default App rendering sets. @@ -241,7 +241,7 @@ impl Plugin for RenderPlugin { .init_asset_loader::(); match &self.render_settings { - RenderSettings::Manual(device, queue, adapter_info, adapter, instance) => { + RenderCreation::Manual(device, queue, adapter_info, adapter, instance) => { let future_renderer_resources_wrapper = Arc::new(Mutex::new(Some(( device.clone(), queue.clone(), @@ -254,7 +254,7 @@ impl Plugin for RenderPlugin { )); unsafe { initialize_render_app(app) }; } - RenderSettings::Automatic(render_settings) => { + RenderCreation::Automatic(render_settings) => { if let Some(backends) = render_settings.backends { let future_renderer_resources_wrapper = Arc::new(Mutex::new(None)); app.insert_resource(FutureRendererResources( diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index c93165554bce6..9d8f172200835 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -97,7 +97,7 @@ impl Default for WgpuSettings { } /// An enum describing how the renderer will initialize resources. This is used when creating the [`RenderPlugin`](crate::RenderPlugin). -pub enum RenderSettings { +pub enum RenderCreation { /// Allows renderer resource initialization to happen outside of the rendering plugin. Manual( RenderDevice, @@ -110,7 +110,7 @@ pub enum RenderSettings { Automatic(WgpuSettings), } -impl RenderSettings { +impl RenderCreation { /// Function to create a [`RenderSettings::Manual`] variant. pub fn manual( device: RenderDevice, @@ -123,13 +123,13 @@ impl RenderSettings { } } -impl Default for RenderSettings { +impl Default for RenderCreation { fn default() -> Self { Self::Automatic(Default::default()) } } -impl From for RenderSettings { +impl From for RenderCreation { fn from(value: WgpuSettings) -> Self { Self::Automatic(value) } From fa763e3f9f9cbb48e4a662800901f061b40d88ae Mon Sep 17 00:00:00 2001 From: piper <107778087+awtterpip@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:40:25 -0500 Subject: [PATCH 3/5] fixed doc things --- crates/bevy_render/src/settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index 9d8f172200835..56b4f6bc526d3 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -111,7 +111,7 @@ pub enum RenderCreation { } impl RenderCreation { - /// Function to create a [`RenderSettings::Manual`] variant. + /// Function to create a [`RenderCreation::Manual`] variant. pub fn manual( device: RenderDevice, queue: RenderQueue, From f3a3ffb880a4875d2d3abff8331ae1049d49f744 Mon Sep 17 00:00:00 2001 From: Malek Date: Tue, 26 Sep 2023 12:09:55 -0700 Subject: [PATCH 4/5] Update crates/bevy_render/src/lib.rs Co-authored-by: Robert Swain --- crates/bevy_render/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 27ec8260ed5e7..73dde677b1166 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -67,7 +67,7 @@ use std::{ /// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] pub struct RenderPlugin { - pub render_settings: RenderCreation, + pub render_creation: RenderCreation, } /// The labels of the default App rendering sets. From 55d829372d31e606d8298703773472cfc9df3512 Mon Sep 17 00:00:00 2001 From: MalekiRe Date: Tue, 26 Sep 2023 12:15:01 -0700 Subject: [PATCH 5/5] fixed render_settings -> render_creation --- crates/bevy_render/src/lib.rs | 8 ++++---- examples/3d/wireframe.rs | 2 +- examples/app/no_renderer.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 73dde677b1166..1709c0ea048c5 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -240,7 +240,7 @@ impl Plugin for RenderPlugin { app.init_asset::() .init_asset_loader::(); - match &self.render_settings { + match &self.render_creation { RenderCreation::Manual(device, queue, adapter_info, adapter, instance) => { let future_renderer_resources_wrapper = Arc::new(Mutex::new(Some(( device.clone(), @@ -254,8 +254,8 @@ impl Plugin for RenderPlugin { )); unsafe { initialize_render_app(app) }; } - RenderCreation::Automatic(render_settings) => { - if let Some(backends) = render_settings.backends { + RenderCreation::Automatic(render_creation) => { + if let Some(backends) = render_creation.backends { let future_renderer_resources_wrapper = Arc::new(Mutex::new(None)); app.insert_resource(FutureRendererResources( future_renderer_resources_wrapper.clone(), @@ -266,7 +266,7 @@ impl Plugin for RenderPlugin { > = SystemState::new(&mut app.world); let primary_window = system_state.get(&app.world).get_single().ok().cloned(); - let settings = render_settings.clone(); + let settings = render_creation.clone(); let async_renderer = async move { let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends, diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 9ea4aebbf6c21..0192b6ee43709 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -10,7 +10,7 @@ fn main() { App::new() .add_plugins(( DefaultPlugins.set(RenderPlugin { - render_settings: WgpuSettings { + render_creation: WgpuSettings { features: WgpuFeatures::POLYGON_MODE_LINE, ..default() } diff --git a/examples/app/no_renderer.rs b/examples/app/no_renderer.rs index cb0b766474782..fc6aa72294340 100644 --- a/examples/app/no_renderer.rs +++ b/examples/app/no_renderer.rs @@ -13,7 +13,7 @@ fn main() { App::new() .add_plugins( DefaultPlugins.set(RenderPlugin { - render_settings: WgpuSettings { + render_creation: WgpuSettings { backends: None, ..default() }