diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index d94c60d14c725..045119267e94d 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -85,6 +85,9 @@ use std::{ #[derive(Default)] pub struct RenderPlugin { pub render_creation: RenderCreation, + /// If `true`, disables asynchronous pipeline compilation. + /// This has no effect on macOS, Wasm, or without the `multi-threaded` feature. + pub synchronous_pipeline_compilation: bool, } /// The labels of the default App rendering sets. @@ -355,7 +358,10 @@ impl Plugin for RenderPlugin { render_app .insert_resource(instance) - .insert_resource(PipelineCache::new(device.clone())) + .insert_resource(PipelineCache::new( + device.clone(), + self.synchronous_pipeline_compilation, + )) .insert_resource(device) .insert_resource(queue) .insert_resource(render_adapter) diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 8e45938b04401..dc5190170a35b 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -480,6 +480,9 @@ pub struct PipelineCache { pipelines: Vec, waiting_pipelines: HashSet, new_pipelines: Mutex>, + /// If `true`, disables asynchronous pipeline compilation. + /// This has no effect on MacOS, wasm, or without the `multi_threaded` feature. + synchronous_pipeline_compilation: bool, } impl PipelineCache { @@ -488,7 +491,7 @@ impl PipelineCache { } /// Create a new pipeline cache associated with the given render device. - pub fn new(device: RenderDevice) -> Self { + pub fn new(device: RenderDevice, synchronous_pipeline_compilation: bool) -> Self { Self { shader_cache: Arc::new(Mutex::new(ShaderCache::new(&device))), device, @@ -496,6 +499,7 @@ impl PipelineCache { waiting_pipelines: default(), new_pipelines: default(), pipelines: default(), + synchronous_pipeline_compilation, } } @@ -679,88 +683,95 @@ impl PipelineCache { let device = self.device.clone(); let shader_cache = self.shader_cache.clone(); let layout_cache = self.layout_cache.clone(); - create_pipeline_task(async move { - let mut shader_cache = shader_cache.lock().unwrap(); - let mut layout_cache = layout_cache.lock().unwrap(); - - let vertex_module = match shader_cache.get( - &device, - id, - descriptor.vertex.shader.id(), - &descriptor.vertex.shader_defs, - ) { - Ok(module) => module, - Err(err) => return Err(err), - }; + create_pipeline_task( + async move { + let mut shader_cache = shader_cache.lock().unwrap(); + let mut layout_cache = layout_cache.lock().unwrap(); + + let vertex_module = match shader_cache.get( + &device, + id, + descriptor.vertex.shader.id(), + &descriptor.vertex.shader_defs, + ) { + Ok(module) => module, + Err(err) => return Err(err), + }; - let fragment_module = match &descriptor.fragment { - Some(fragment) => { - match shader_cache.get(&device, id, fragment.shader.id(), &fragment.shader_defs) - { - Ok(module) => Some(module), - Err(err) => return Err(err), + let fragment_module = match &descriptor.fragment { + Some(fragment) => { + match shader_cache.get( + &device, + id, + fragment.shader.id(), + &fragment.shader_defs, + ) { + Ok(module) => Some(module), + Err(err) => return Err(err), + } } - } - None => None, - }; - - let layout = - if descriptor.layout.is_empty() && descriptor.push_constant_ranges.is_empty() { - None - } else { - Some(layout_cache.get( - &device, - &descriptor.layout, - descriptor.push_constant_ranges.to_vec(), - )) + None => None, }; - drop((shader_cache, layout_cache)); - - let vertex_buffer_layouts = descriptor - .vertex - .buffers - .iter() - .map(|layout| RawVertexBufferLayout { - array_stride: layout.array_stride, - attributes: &layout.attributes, - step_mode: layout.step_mode, - }) - .collect::>(); - - let fragment_data = descriptor.fragment.as_ref().map(|fragment| { - ( - fragment_module.unwrap(), - fragment.entry_point.deref(), - fragment.targets.as_slice(), - ) - }); - - let descriptor = RawRenderPipelineDescriptor { - multiview: None, - depth_stencil: descriptor.depth_stencil.clone(), - label: descriptor.label.as_deref(), - layout: layout.as_deref(), - multisample: descriptor.multisample, - primitive: descriptor.primitive, - vertex: RawVertexState { - buffers: &vertex_buffer_layouts, - entry_point: descriptor.vertex.entry_point.deref(), - module: &vertex_module, - }, - fragment: fragment_data - .as_ref() - .map(|(module, entry_point, targets)| RawFragmentState { - entry_point, - module, - targets, - }), - }; + let layout = + if descriptor.layout.is_empty() && descriptor.push_constant_ranges.is_empty() { + None + } else { + Some(layout_cache.get( + &device, + &descriptor.layout, + descriptor.push_constant_ranges.to_vec(), + )) + }; + + drop((shader_cache, layout_cache)); + + let vertex_buffer_layouts = descriptor + .vertex + .buffers + .iter() + .map(|layout| RawVertexBufferLayout { + array_stride: layout.array_stride, + attributes: &layout.attributes, + step_mode: layout.step_mode, + }) + .collect::>(); + + let fragment_data = descriptor.fragment.as_ref().map(|fragment| { + ( + fragment_module.unwrap(), + fragment.entry_point.deref(), + fragment.targets.as_slice(), + ) + }); + + let descriptor = RawRenderPipelineDescriptor { + multiview: None, + depth_stencil: descriptor.depth_stencil.clone(), + label: descriptor.label.as_deref(), + layout: layout.as_deref(), + multisample: descriptor.multisample, + primitive: descriptor.primitive, + vertex: RawVertexState { + buffers: &vertex_buffer_layouts, + entry_point: descriptor.vertex.entry_point.deref(), + module: &vertex_module, + }, + fragment: fragment_data + .as_ref() + .map(|(module, entry_point, targets)| RawFragmentState { + entry_point, + module, + targets, + }), + }; - Ok(Pipeline::RenderPipeline( - device.create_render_pipeline(&descriptor), - )) - }) + Ok(Pipeline::RenderPipeline( + device.create_render_pipeline(&descriptor), + )) + }, + self.synchronous_pipeline_compilation, + ) } fn start_create_compute_pipeline( @@ -771,44 +782,47 @@ impl PipelineCache { let device = self.device.clone(); let shader_cache = self.shader_cache.clone(); let layout_cache = self.layout_cache.clone(); - create_pipeline_task(async move { - let mut shader_cache = shader_cache.lock().unwrap(); - let mut layout_cache = layout_cache.lock().unwrap(); - - let compute_module = match shader_cache.get( - &device, - id, - descriptor.shader.id(), - &descriptor.shader_defs, - ) { - Ok(module) => module, - Err(err) => return Err(err), - }; - - let layout = - if descriptor.layout.is_empty() && descriptor.push_constant_ranges.is_empty() { - None - } else { - Some(layout_cache.get( - &device, - &descriptor.layout, - descriptor.push_constant_ranges.to_vec(), - )) + create_pipeline_task( + async move { + let mut shader_cache = shader_cache.lock().unwrap(); + let mut layout_cache = layout_cache.lock().unwrap(); + + let compute_module = match shader_cache.get( + &device, + id, + descriptor.shader.id(), + &descriptor.shader_defs, + ) { + Ok(module) => module, + Err(err) => return Err(err), }; - drop((shader_cache, layout_cache)); - - let descriptor = RawComputePipelineDescriptor { - label: descriptor.label.as_deref(), - layout: layout.as_deref(), - module: &compute_module, - entry_point: &descriptor.entry_point, - }; + let layout = + if descriptor.layout.is_empty() && descriptor.push_constant_ranges.is_empty() { + None + } else { + Some(layout_cache.get( + &device, + &descriptor.layout, + descriptor.push_constant_ranges.to_vec(), + )) + }; + + drop((shader_cache, layout_cache)); + + let descriptor = RawComputePipelineDescriptor { + label: descriptor.label.as_deref(), + layout: layout.as_deref(), + module: &compute_module, + entry_point: &descriptor.entry_point, + }; - Ok(Pipeline::ComputePipeline( - device.create_compute_pipeline(&descriptor), - )) - }) + Ok(Pipeline::ComputePipeline( + device.create_compute_pipeline(&descriptor), + )) + }, + self.synchronous_pipeline_compilation, + ) } /// Process the pipeline queue and create all pending pipelines if possible. @@ -917,21 +931,34 @@ impl PipelineCache { } } +#[cfg(all( + not(target_arch = "wasm32"), + not(target_os = "macos"), + feature = "multi-threaded" +))] +fn create_pipeline_task( + task: impl Future> + Send + 'static, + sync: bool, +) -> CachedPipelineState { + if !sync { + return CachedPipelineState::Creating(bevy_tasks::AsyncComputeTaskPool::get().spawn(task)); + } + + match futures_lite::future::block_on(task) { + Ok(pipeline) => CachedPipelineState::Ok(pipeline), + Err(err) => CachedPipelineState::Err(err), + } +} + +#[cfg(any( + target_arch = "wasm32", + target_os = "macos", + not(feature = "multi-threaded") +))] fn create_pipeline_task( task: impl Future> + Send + 'static, + _sync: bool, ) -> CachedPipelineState { - #[cfg(all( - not(target_arch = "wasm32"), - not(target_os = "macos"), - feature = "multi-threaded" - ))] - return CachedPipelineState::Creating(bevy_tasks::AsyncComputeTaskPool::get().spawn(task)); - - #[cfg(any( - target_arch = "wasm32", - target_os = "macos", - not(feature = "multi-threaded") - ))] match futures_lite::future::block_on(task) { Ok(pipeline) => CachedPipelineState::Ok(pipeline), Err(err) => CachedPipelineState::Err(err), diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 3b1b1dfa527c7..7f0443aa180a2 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -27,6 +27,7 @@ fn main() { features: WgpuFeatures::POLYGON_MODE_LINE, ..default() }), + ..default() }), // You need to add this plugin to enable wireframe rendering WireframePlugin, diff --git a/examples/app/no_renderer.rs b/examples/app/no_renderer.rs index fc6aa72294340..5109bba128c8f 100644 --- a/examples/app/no_renderer.rs +++ b/examples/app/no_renderer.rs @@ -18,6 +18,7 @@ fn main() { ..default() } .into(), + ..default() }), ) .run();