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
@ -45,7 +45,6 @@ use bevy_hierarchy::ValidParentCheckPlugin;
|
|||||||
use bevy_window::{PrimaryWindow, RawHandleWrapper};
|
use bevy_window::{PrimaryWindow, RawHandleWrapper};
|
||||||
use globals::GlobalsPlugin;
|
use globals::GlobalsPlugin;
|
||||||
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue};
|
||||||
use wgpu::Instance;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::CameraPlugin,
|
camera::CameraPlugin,
|
||||||
@ -53,7 +52,7 @@ use crate::{
|
|||||||
render_asset::prepare_assets,
|
render_asset::prepare_assets,
|
||||||
render_resource::{PipelineCache, Shader, ShaderLoader},
|
render_resource::{PipelineCache, Shader, ShaderLoader},
|
||||||
renderer::{render_system, RenderInstance},
|
renderer::{render_system, RenderInstance},
|
||||||
settings::WgpuSettings,
|
settings::RenderCreation,
|
||||||
view::{ViewPlugin, WindowRenderPlugin},
|
view::{ViewPlugin, WindowRenderPlugin},
|
||||||
};
|
};
|
||||||
use bevy_app::{App, AppLabel, Plugin, SubApp};
|
use bevy_app::{App, AppLabel, Plugin, SubApp};
|
||||||
@ -68,7 +67,7 @@ use std::{
|
|||||||
/// Contains the default Bevy rendering backend based on wgpu.
|
/// Contains the default Bevy rendering backend based on wgpu.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RenderPlugin {
|
pub struct RenderPlugin {
|
||||||
pub wgpu_settings: WgpuSettings,
|
pub render_creation: RenderCreation,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The labels of the default App rendering sets.
|
/// The labels of the default App rendering sets.
|
||||||
@ -221,7 +220,7 @@ struct FutureRendererResources(
|
|||||||
RenderQueue,
|
RenderQueue,
|
||||||
RenderAdapterInfo,
|
RenderAdapterInfo,
|
||||||
RenderAdapter,
|
RenderAdapter,
|
||||||
Instance,
|
RenderInstance,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
>,
|
>,
|
||||||
@ -241,17 +240,33 @@ impl Plugin for RenderPlugin {
|
|||||||
app.init_asset::<Shader>()
|
app.init_asset::<Shader>()
|
||||||
.init_asset_loader::<ShaderLoader>();
|
.init_asset_loader::<ShaderLoader>();
|
||||||
|
|
||||||
if let Some(backends) = self.wgpu_settings.backends {
|
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));
|
let future_renderer_resources_wrapper = Arc::new(Mutex::new(None));
|
||||||
app.insert_resource(FutureRendererResources(
|
app.insert_resource(FutureRendererResources(
|
||||||
future_renderer_resources_wrapper.clone(),
|
future_renderer_resources_wrapper.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut system_state: SystemState<Query<&RawHandleWrapper, With<PrimaryWindow>>> =
|
let mut system_state: SystemState<
|
||||||
SystemState::new(&mut app.world);
|
Query<&RawHandleWrapper, With<PrimaryWindow>>,
|
||||||
|
> = SystemState::new(&mut app.world);
|
||||||
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
let primary_window = system_state.get(&app.world).get_single().ok().cloned();
|
||||||
|
|
||||||
let settings = self.wgpu_settings.clone();
|
let settings = render_creation.clone();
|
||||||
let async_renderer = async move {
|
let async_renderer = async move {
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
backends,
|
backends,
|
||||||
@ -272,14 +287,23 @@ impl Plugin for RenderPlugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (device, queue, adapter_info, render_adapter) =
|
let (device, queue, adapter_info, render_adapter) =
|
||||||
renderer::initialize_renderer(&instance, &settings, &request_adapter_options)
|
renderer::initialize_renderer(
|
||||||
|
&instance,
|
||||||
|
&settings,
|
||||||
|
&request_adapter_options,
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
|
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
|
||||||
debug!("Configured wgpu adapter Features: {:#?}", device.features());
|
debug!("Configured wgpu adapter Features: {:#?}", device.features());
|
||||||
let mut future_renderer_resources_inner =
|
let mut future_renderer_resources_inner =
|
||||||
future_renderer_resources_wrapper.lock().unwrap();
|
future_renderer_resources_wrapper.lock().unwrap();
|
||||||
*future_renderer_resources_inner =
|
*future_renderer_resources_inner = Some((
|
||||||
Some((device, queue, adapter_info, render_adapter, instance));
|
device,
|
||||||
|
queue,
|
||||||
|
adapter_info,
|
||||||
|
render_adapter,
|
||||||
|
RenderInstance(Arc::new(instance)),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
// In wasm, spawn a task and detach it for execution
|
// In wasm, spawn a task and detach it for execution
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -290,6 +314,94 @@ impl Plugin for RenderPlugin {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
futures_lite::future::block_on(async_renderer);
|
futures_lite::future::block_on(async_renderer);
|
||||||
|
|
||||||
|
unsafe { initialize_render_app(app) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
app.add_plugins((
|
||||||
|
ValidParentCheckPlugin::<view::InheritedVisibility>::default(),
|
||||||
|
WindowRenderPlugin,
|
||||||
|
CameraPlugin,
|
||||||
|
ViewPlugin,
|
||||||
|
MeshPlugin,
|
||||||
|
GlobalsPlugin,
|
||||||
|
MorphPlugin,
|
||||||
|
));
|
||||||
|
|
||||||
|
app.register_type::<color::Color>()
|
||||||
|
.register_type::<primitives::Aabb>()
|
||||||
|
.register_type::<primitives::CascadesFrusta>()
|
||||||
|
.register_type::<primitives::CubemapFrusta>()
|
||||||
|
.register_type::<primitives::Frustum>();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(&self, app: &App) -> bool {
|
||||||
|
app.world
|
||||||
|
.get_resource::<FutureRendererResources>()
|
||||||
|
.and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
load_internal_asset!(
|
||||||
|
app,
|
||||||
|
INSTANCE_INDEX_SHADER_HANDLE,
|
||||||
|
"instance_index.wgsl",
|
||||||
|
Shader::from_wgsl_with_defs,
|
||||||
|
vec![
|
||||||
|
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
|
||||||
|
"BASE_INSTANCE_WORKAROUND".into()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
|
||||||
|
if let Some(future_renderer_resources) =
|
||||||
|
app.world.remove_resource::<FutureRendererResources>()
|
||||||
|
{
|
||||||
|
let (device, queue, adapter_info, render_adapter, instance) =
|
||||||
|
future_renderer_resources.0.lock().unwrap().take().unwrap();
|
||||||
|
|
||||||
|
app.insert_resource(device.clone())
|
||||||
|
.insert_resource(queue.clone())
|
||||||
|
.insert_resource(adapter_info.clone())
|
||||||
|
.insert_resource(render_adapter.clone());
|
||||||
|
|
||||||
|
let render_app = app.sub_app_mut(RenderApp);
|
||||||
|
|
||||||
|
render_app
|
||||||
|
.insert_resource(instance)
|
||||||
|
.insert_resource(PipelineCache::new(device.clone()))
|
||||||
|
.insert_resource(device)
|
||||||
|
.insert_resource(queue)
|
||||||
|
.insert_resource(render_adapter)
|
||||||
|
.insert_resource(adapter_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A "scratch" world used to avoid allocating new worlds every frame when
|
||||||
|
/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
struct ScratchMainWorld(World);
|
||||||
|
|
||||||
|
/// Executes the [`ExtractSchedule`] step of the renderer.
|
||||||
|
/// This updates the render world with the extracted ECS data of the current frame.
|
||||||
|
fn extract(main_world: &mut World, render_app: &mut App) {
|
||||||
|
// temporarily add the app world to the render world as a resource
|
||||||
|
let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
|
||||||
|
let inserted_world = std::mem::replace(main_world, scratch_world.0);
|
||||||
|
render_app.world.insert_resource(MainWorld(inserted_world));
|
||||||
|
|
||||||
|
render_app.world.run_schedule(ExtractSchedule);
|
||||||
|
|
||||||
|
// move the app world back, as if nothing happened.
|
||||||
|
let inserted_world = render_app.world.remove_resource::<MainWorld>().unwrap();
|
||||||
|
let scratch_world = std::mem::replace(main_world, inserted_world.0);
|
||||||
|
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>();
|
app.init_resource::<ScratchMainWorld>();
|
||||||
|
|
||||||
let mut render_app = App::empty();
|
let mut render_app = App::empty();
|
||||||
@ -356,87 +468,6 @@ impl Plugin for RenderPlugin {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
app.add_plugins((
|
|
||||||
ValidParentCheckPlugin::<view::InheritedVisibility>::default(),
|
|
||||||
WindowRenderPlugin,
|
|
||||||
CameraPlugin,
|
|
||||||
ViewPlugin,
|
|
||||||
MeshPlugin,
|
|
||||||
GlobalsPlugin,
|
|
||||||
MorphPlugin,
|
|
||||||
));
|
|
||||||
|
|
||||||
app.register_type::<color::Color>()
|
|
||||||
.register_type::<primitives::Aabb>()
|
|
||||||
.register_type::<primitives::CascadesFrusta>()
|
|
||||||
.register_type::<primitives::CubemapFrusta>()
|
|
||||||
.register_type::<primitives::Frustum>();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ready(&self, app: &App) -> bool {
|
|
||||||
app.world
|
|
||||||
.get_resource::<FutureRendererResources>()
|
|
||||||
.and_then(|frr| frr.0.try_lock().map(|locked| locked.is_some()).ok())
|
|
||||||
.unwrap_or(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&self, app: &mut App) {
|
|
||||||
load_internal_asset!(
|
|
||||||
app,
|
|
||||||
INSTANCE_INDEX_SHADER_HANDLE,
|
|
||||||
"instance_index.wgsl",
|
|
||||||
Shader::from_wgsl_with_defs,
|
|
||||||
vec![
|
|
||||||
#[cfg(all(feature = "webgl", target_arch = "wasm32"))]
|
|
||||||
"BASE_INSTANCE_WORKAROUND".into()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
load_internal_asset!(app, MATHS_SHADER_HANDLE, "maths.wgsl", Shader::from_wgsl);
|
|
||||||
if let Some(future_renderer_resources) =
|
|
||||||
app.world.remove_resource::<FutureRendererResources>()
|
|
||||||
{
|
|
||||||
let (device, queue, adapter_info, render_adapter, instance) =
|
|
||||||
future_renderer_resources.0.lock().unwrap().take().unwrap();
|
|
||||||
|
|
||||||
app.insert_resource(device.clone())
|
|
||||||
.insert_resource(queue.clone())
|
|
||||||
.insert_resource(adapter_info.clone())
|
|
||||||
.insert_resource(render_adapter.clone());
|
|
||||||
|
|
||||||
let render_app = app.sub_app_mut(RenderApp);
|
|
||||||
|
|
||||||
render_app
|
|
||||||
.insert_resource(RenderInstance(instance))
|
|
||||||
.insert_resource(PipelineCache::new(device.clone()))
|
|
||||||
.insert_resource(device)
|
|
||||||
.insert_resource(queue)
|
|
||||||
.insert_resource(render_adapter)
|
|
||||||
.insert_resource(adapter_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A "scratch" world used to avoid allocating new worlds every frame when
|
|
||||||
/// swapping out the [`MainWorld`] for [`ExtractSchedule`].
|
|
||||||
#[derive(Resource, Default)]
|
|
||||||
struct ScratchMainWorld(World);
|
|
||||||
|
|
||||||
/// Executes the [`ExtractSchedule`] step of the renderer.
|
|
||||||
/// This updates the render world with the extracted ECS data of the current frame.
|
|
||||||
fn extract(main_world: &mut World, render_app: &mut App) {
|
|
||||||
// temporarily add the app world to the render world as a resource
|
|
||||||
let scratch_world = main_world.remove_resource::<ScratchMainWorld>().unwrap();
|
|
||||||
let inserted_world = std::mem::replace(main_world, scratch_world.0);
|
|
||||||
render_app.world.insert_resource(MainWorld(inserted_world));
|
|
||||||
|
|
||||||
render_app.world.run_schedule(ExtractSchedule);
|
|
||||||
|
|
||||||
// move the app world back, as if nothing happened.
|
|
||||||
let inserted_world = render_app.world.remove_resource::<MainWorld>().unwrap();
|
|
||||||
let scratch_world = std::mem::replace(main_world, inserted_world.0);
|
|
||||||
main_world.insert_resource(ScratchMainWorld(scratch_world));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies the commands from the extract schedule. This happens during
|
/// 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
|
/// the render schedule rather than during extraction to allow the commands to run in parallel with the
|
||||||
/// main app when pipelined rendering is enabled.
|
/// 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`],
|
/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`],
|
||||||
/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
|
/// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
|
||||||
#[derive(Resource, Deref, DerefMut)]
|
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||||
pub struct RenderInstance(pub Instance);
|
pub struct RenderInstance(pub Arc<Instance>);
|
||||||
|
|
||||||
/// The [`AdapterInfo`] of the adapter in use by the renderer.
|
/// The [`AdapterInfo`] of the adapter in use by the renderer.
|
||||||
#[derive(Resource, Clone, Deref, DerefMut)]
|
#[derive(Resource, Clone, Deref, DerefMut)]
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
use crate::renderer::{
|
||||||
|
RenderAdapter, RenderAdapterInfo, RenderDevice, RenderInstance, RenderQueue,
|
||||||
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub use wgpu::{
|
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`
|
/// Get a features/limits priority from the environment variable `WGPU_SETTINGS_PRIO`
|
||||||
pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
|
pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
|
||||||
Some(
|
Some(
|
||||||
|
@ -10,10 +10,11 @@ fn main() {
|
|||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins.set(RenderPlugin {
|
DefaultPlugins.set(RenderPlugin {
|
||||||
wgpu_settings: WgpuSettings {
|
render_creation: WgpuSettings {
|
||||||
features: WgpuFeatures::POLYGON_MODE_LINE,
|
features: WgpuFeatures::POLYGON_MODE_LINE,
|
||||||
..default()
|
..default()
|
||||||
},
|
}
|
||||||
|
.into(),
|
||||||
}),
|
}),
|
||||||
WireframePlugin,
|
WireframePlugin,
|
||||||
))
|
))
|
||||||
|
@ -11,11 +11,14 @@ use bevy::{
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins(DefaultPlugins.set(RenderPlugin {
|
.add_plugins(
|
||||||
wgpu_settings: WgpuSettings {
|
DefaultPlugins.set(RenderPlugin {
|
||||||
|
render_creation: WgpuSettings {
|
||||||
backends: None,
|
backends: None,
|
||||||
..default()
|
..default()
|
||||||
},
|
}
|
||||||
}))
|
.into(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user