mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Allow other plugins to create renderer resources (#9925)
This is a duplicate of #9632, it was created since I forgot to make a new branch when I first made this PR, so I was having trouble resolving merge conflicts, meaning I had to rebuild my PR. # Objective - Allow other plugins to create the renderer resources. An example of where this would be required is my [OpenXR plugin](https://github.com/awtterpip/bevy_openxr) ## Solution - Changed the bevy RenderPlugin to optionally take precreated render resources instead of a configuration. ## Migration Guide The `RenderPlugin` now takes a `RenderCreation` enum instead of `WgpuSettings`. `RenderSettings::default()` returns `RenderSettings::Automatic(WgpuSettings::default())`. `RenderSettings` also implements `From<WgpuSettings>`. ```rust // before RenderPlugin { wgpu_settings: WgpuSettings { ... }, } // now RenderPlugin { render_creation: RenderCreation::Automatic(WgpuSettings { ... }), } // or RenderPlugin { render_creation: WgpuSettings { ... }.into(), } ``` --------- Co-authored-by: Malek <pocmalek@gmail.com> Co-authored-by: Robert Swain <robert.swain@gmail.com>
This commit is contained in:
parent
bc1f33d50b
commit
bc88f33e48
5 changed files with 200 additions and 123 deletions
|
@ -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::RenderCreation,
|
||||
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_creation: RenderCreation,
|
||||
}
|
||||
|
||||
/// 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::<Shader>()
|
||||
.init_asset_loader::<ShaderLoader>();
|
||||
|
||||
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(),
|
||||
));
|
||||
match &self.render_creation {
|
||||
RenderCreation::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) };
|
||||
}
|
||||
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(),
|
||||
));
|
||||
|
||||
let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> =
|
||||
SystemState::new(&mut app.world);
|
||||
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
||||
let mut system_state: SystemState<
|
||||
Query<&RawHandleWrapper, With<PrimaryWindow>>,
|
||||
> = 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 settings = render_creation.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 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);
|
||||
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);
|
||||
|
||||
app.init_resource::<ScratchMainWorld>();
|
||||
|
||||
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::<render_graph::RenderGraph>()
|
||||
.insert_resource(app.world.resource::<AssetServer>().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);
|
||||
}
|
||||
unsafe { initialize_render_app(app) };
|
||||
}
|
||||
|
||||
// run extract schedule
|
||||
extract(main_world, render_app);
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.add_plugins((
|
||||
ValidParentCheckPlugin::<view::InheritedVisibility>::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::<ScratchMainWorld>();
|
||||
|
||||
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::<render_graph::RenderGraph>()
|
||||
.insert_resource(app.world.resource::<AssetServer>().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.
|
||||
|
|
|
@ -104,8 +104,8 @@ pub struct RenderAdapter(pub Arc<Adapter>);
|
|||
|
||||
/// 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<Instance>);
|
||||
|
||||
/// The [`AdapterInfo`] of the adapter in use by the renderer.
|
||||
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||
|
|
|
@ -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 RenderCreation {
|
||||
/// 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 RenderCreation {
|
||||
/// Function to create a [`RenderCreation::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 RenderCreation {
|
||||
fn default() -> Self {
|
||||
Self::Automatic(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WgpuSettings> for RenderCreation {
|
||||
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<WgpuSettingsPriority> {
|
||||
Some(
|
||||
|
|
|
@ -10,10 +10,11 @@ fn main() {
|
|||
App::new()
|
||||
.add_plugins((
|
||||
DefaultPlugins.set(RenderPlugin {
|
||||
wgpu_settings: WgpuSettings {
|
||||
render_creation: WgpuSettings {
|
||||
features: WgpuFeatures::POLYGON_MODE_LINE,
|
||||
..default()
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
}),
|
||||
WireframePlugin,
|
||||
))
|
||||
|
|
|
@ -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_creation: WgpuSettings {
|
||||
backends: None,
|
||||
..default()
|
||||
}
|
||||
.into(),
|
||||
}),
|
||||
)
|
||||
.run();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue