diff --git a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs index 1067643e03b12..6eb035e51912a 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs @@ -108,7 +108,7 @@ impl Node for MainPass3dNode { opaque_phase.render(&mut render_pass, world, view_entity); } - if !alpha_mask_phase.items.is_empty() { + if !alpha_mask_phase.sorted.is_empty() { // Run the alpha mask pass, sorted front-to-back // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] @@ -143,7 +143,7 @@ impl Node for MainPass3dNode { alpha_mask_phase.render(&mut render_pass, world, view_entity); } - if !transparent_phase.items.is_empty() { + if !transparent_phase.sorted.is_empty() { // Run the transparent pass, sorted back-to-front // NOTE: Scoped to drop the mutable borrow of render_context #[cfg(feature = "trace")] diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index f49425e7250c4..3e33ace71afdb 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -222,7 +222,7 @@ impl SpecializedRenderPipeline for FxaaPipeline { pub fn prepare_fxaa_pipelines( mut commands: Commands, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut pipelines: ResMut>, fxaa_pipeline: Res, views: Query<(Entity, &ExtractedView, &Fxaa)>, @@ -232,7 +232,7 @@ pub fn prepare_fxaa_pipelines( continue; } let pipeline_id = pipelines.specialize( - &mut pipeline_cache, + &pipeline_cache, &fxaa_pipeline, FxaaPipelineKey { edge_threshold: fxaa.edge_threshold, diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index 785c231a43fb9..3e74fa1d681a1 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -126,7 +126,7 @@ pub struct ViewTonemappingPipeline(CachedRenderPipelineId); pub fn queue_view_tonemapping_pipelines( mut commands: Commands, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut pipelines: ResMut>, upscaling_pipeline: Res, view_targets: Query<(Entity, &Tonemapping)>, @@ -136,7 +136,7 @@ pub fn queue_view_tonemapping_pipelines( let key = TonemappingPipelineKey { deband_dither: *deband_dither, }; - let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key); + let pipeline = pipelines.specialize(&pipeline_cache, &upscaling_pipeline, key); commands .entity(entity) diff --git a/crates/bevy_core_pipeline/src/upscaling/mod.rs b/crates/bevy_core_pipeline/src/upscaling/mod.rs index b8fa9718d000f..12399e14b245e 100644 --- a/crates/bevy_core_pipeline/src/upscaling/mod.rs +++ b/crates/bevy_core_pipeline/src/upscaling/mod.rs @@ -112,7 +112,7 @@ pub struct ViewUpscalingPipeline(CachedRenderPipelineId); fn queue_view_upscaling_pipelines( mut commands: Commands, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut pipelines: ResMut>, upscaling_pipeline: Res, view_targets: Query<(Entity, &ViewTarget)>, @@ -122,7 +122,7 @@ fn queue_view_upscaling_pipelines( upscaling_mode: UpscalingMode::Filtering, texture_format: view_target.out_texture_format(), }; - let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key); + let pipeline = pipelines.specialize(&pipeline_cache, &upscaling_pipeline, key); commands .entity(entity) diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 7238e48d2b4c6..d00cea9df443f 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -29,4 +29,5 @@ bevy_derive = { path = "../bevy_derive", version = "0.9.0" } bitflags = "1.2" # direct dependency required for derive macro bytemuck = { version = "1", features = ["derive"] } +copyless = "0.1" radsort = "0.1" diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index d10fac03a684e..9326c9ee26a3a 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -30,8 +30,8 @@ use bevy_render::{ RenderPhase, SetItemPipeline, TrackedRenderPass, }, render_resource::{ - AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource, - PipelineCache, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline, + AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, LockablePipelineCache, + OwnedBindingResource, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, @@ -321,30 +321,24 @@ pub fn queue_material_meshes( transparent_draw_functions: Res>, material_pipeline: Res>, mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, msaa: Res, render_meshes: Res>, render_materials: Res>, material_meshes: Query<(&Handle, &Handle, &MeshUniform)>, - mut views: Query<( + views: Query<( &ExtractedView, &VisibleEntities, Option<&Tonemapping>, - &mut RenderPhase, - &mut RenderPhase, - &mut RenderPhase, + &RenderPhase, + &RenderPhase, + &RenderPhase, )>, ) where M::Data: PartialEq + Eq + Hash + Clone, { - for ( - view, - visible_entities, - tonemapping, - mut opaque_phase, - mut alpha_mask_phase, - mut transparent_phase, - ) in &mut views + for (view, visible_entities, tonemapping, opaque_phase, alpha_mask_phase, transparent_phase) in + &views { let draw_opaque_pbr = opaque_draw_functions.read().id::>(); let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::>(); @@ -364,6 +358,14 @@ pub fn queue_material_meshes( } let rangefinder = view.rangefinder3d(); + let opaque_phase_cell = opaque_phase.get(); + let alpha_mask_phase_cell = alpha_mask_phase.get(); + let transparent_phase_cell = transparent_phase.get(); + + let mut opaque_phase_queue = opaque_phase_cell.take(); + let mut alpha_mask_phase_queue = alpha_mask_phase_cell.take(); + let mut transparent_phase_queue = transparent_phase_cell.take(); + for visible_entity in &visible_entities.entities { if let Ok((material_handle, mesh_handle, mesh_uniform)) = material_meshes.get(*visible_entity) @@ -379,7 +381,7 @@ pub fn queue_material_meshes( } let pipeline_id = pipelines.specialize( - &mut pipeline_cache, + &pipeline_cache, &material_pipeline, MaterialPipelineKey { mesh_key, @@ -399,7 +401,7 @@ pub fn queue_material_meshes( + material.properties.depth_bias; match alpha_mode { AlphaMode::Opaque => { - opaque_phase.add(Opaque3d { + opaque_phase_queue.push(Opaque3d { entity: *visible_entity, draw_function: draw_opaque_pbr, pipeline: pipeline_id, @@ -407,7 +409,7 @@ pub fn queue_material_meshes( }); } AlphaMode::Mask(_) => { - alpha_mask_phase.add(AlphaMask3d { + alpha_mask_phase_queue.push(AlphaMask3d { entity: *visible_entity, draw_function: draw_alpha_mask_pbr, pipeline: pipeline_id, @@ -415,7 +417,7 @@ pub fn queue_material_meshes( }); } AlphaMode::Blend => { - transparent_phase.add(Transparent3d { + transparent_phase_queue.push(Transparent3d { entity: *visible_entity, draw_function: draw_transparent_pbr, pipeline: pipeline_id, @@ -427,6 +429,10 @@ pub fn queue_material_meshes( } } } + + opaque_phase_cell.set(opaque_phase_queue); + alpha_mask_phase_cell.set(alpha_mask_phase_queue); + transparent_phase_cell.set(transparent_phase_queue); } } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 9cd2bf0bbd83c..dea5d287841e1 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1626,9 +1626,9 @@ pub fn queue_shadows( casting_meshes: Query<&Handle, Without>, render_meshes: Res>, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, view_lights: Query<&ViewLightEntities>, - mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase)>, + view_light_shadow_phases: Query<(&LightEntity, &RenderPhase)>, point_light_entities: Query<&CubemapVisibleEntities, With>, directional_light_entities: Query<&VisibleEntities, With>, spot_light_entities: Query<&VisibleEntities, With>, @@ -1636,8 +1636,8 @@ pub fn queue_shadows( for view_lights in &view_lights { let draw_shadow_mesh = shadow_draw_functions.read().id::(); for view_light_entity in view_lights.lights.iter().copied() { - let (light_entity, mut shadow_phase) = - view_light_shadow_phases.get_mut(view_light_entity).unwrap(); + let (light_entity, shadow_phase) = + view_light_shadow_phases.get(view_light_entity).unwrap(); let visible_entities = match light_entity { LightEntity::Directional { light_entity } => directional_light_entities .get(*light_entity) @@ -1653,37 +1653,39 @@ pub fn queue_shadows( .get(*light_entity) .expect("Failed to get spot light visible entities"), }; - // NOTE: Lights with shadow mapping disabled will have no visible entities - // so no meshes will be queued - for entity in visible_entities.iter().copied() { - if let Ok(mesh_handle) = casting_meshes.get(entity) { - if let Some(mesh) = render_meshes.get(mesh_handle) { - let key = - ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); - let pipeline_id = pipelines.specialize( - &mut pipeline_cache, - &shadow_pipeline, - key, - &mesh.layout, - ); - - let pipeline_id = match pipeline_id { - Ok(id) => id, - Err(err) => { - error!("{}", err); - continue; - } - }; - - shadow_phase.add(Shadow { - draw_function: draw_shadow_mesh, - pipeline: pipeline_id, - entity, - distance: 0.0, // TODO: sort back-to-front - }); + shadow_phase.phase_scope(|mut phase| { + // NOTE: Lights with shadow mapping disabled will have no visible entities + // so no meshes will be queued + for entity in visible_entities.iter().copied() { + if let Ok(mesh_handle) = casting_meshes.get(entity) { + if let Some(mesh) = render_meshes.get(mesh_handle) { + let key = + ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &shadow_pipeline, + key, + &mesh.layout, + ); + + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + continue; + } + }; + + phase.add(Shadow { + draw_function: draw_shadow_mesh, + pipeline: pipeline_id, + entity, + distance: 0.0, // TODO: sort back-to-front + }); + } } } - } + }); } } } @@ -1766,7 +1768,7 @@ impl Node for ShadowPassNode { .get_manual(world, view_light_entity) .unwrap(); - if shadow_phase.items.is_empty() { + if shadow_phase.sorted.is_empty() { continue; } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 2b31a051317d2..8e97bcccaac12 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -13,8 +13,8 @@ use bevy_render::{ render_asset::RenderAssets, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ - PipelineCache, PolygonMode, RenderPipelineDescriptor, Shader, SpecializedMeshPipeline, - SpecializedMeshPipelineError, SpecializedMeshPipelines, + LockablePipelineCache, PolygonMode, RenderPipelineDescriptor, Shader, + SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, view::{ExtractedView, Msaa, VisibleEntities}, RenderApp, RenderStage, @@ -108,62 +108,64 @@ fn queue_wireframes( wireframe_config: Res, wireframe_pipeline: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, msaa: Res, mut material_meshes: ParamSet<( Query<(Entity, &Handle, &MeshUniform)>, Query<(Entity, &Handle, &MeshUniform), With>, )>, - mut views: Query<(&ExtractedView, &VisibleEntities, &mut RenderPhase)>, + views: Query<(&ExtractedView, &VisibleEntities, &RenderPhase)>, ) { let draw_custom = opaque_3d_draw_functions.read().id::(); let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); - for (view, visible_entities, mut opaque_phase) in &mut views { - let rangefinder = view.rangefinder3d(); + for (view, visible_entities, opaque_phase) in views.iter() { + let view_matrix = view.transform.compute_matrix(); + let view_row_2 = view_matrix.row(2); - let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); - let add_render_phase = - |(entity, mesh_handle, mesh_uniform): (Entity, &Handle, &MeshUniform)| { - if let Some(mesh) = render_meshes.get(mesh_handle) { - let key = view_key - | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - let pipeline_id = pipelines.specialize( - &mut pipeline_cache, - &wireframe_pipeline, - key, - &mesh.layout, - ); - let pipeline_id = match pipeline_id { - Ok(id) => id, - Err(err) => { - error!("{}", err); - return; - } - }; - opaque_phase.add(Opaque3d { - entity, - pipeline: pipeline_id, - draw_function: draw_custom, - distance: rangefinder.distance(&mesh_uniform.transform), - }); - } - }; + opaque_phase.phase_scope(|mut phase| { + let add_render_phase = + |(entity, mesh_handle, mesh_uniform): (Entity, &Handle, &MeshUniform)| { + if let Some(mesh) = render_meshes.get(mesh_handle) { + let key = msaa_key + | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &wireframe_pipeline, + key, + &mesh.layout, + ); + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + return; + } + }; + phase.add(Opaque3d { + entity, + pipeline: pipeline_id, + draw_function: draw_custom, + distance: view_row_2.dot(mesh_uniform.transform.col(3)), + }); + } + }; - if wireframe_config.global { - let query = material_meshes.p0(); - visible_entities - .entities - .iter() - .filter_map(|visible_entity| query.get(*visible_entity).ok()) - .for_each(add_render_phase); - } else { - let query = material_meshes.p1(); - visible_entities - .entities - .iter() - .filter_map(|visible_entity| query.get(*visible_entity).ok()) - .for_each(add_render_phase); - } + if wireframe_config.global { + let query = material_meshes.p0(); + visible_entities + .entities + .iter() + .filter_map(|visible_entity| query.get(*visible_entity).ok()) + .for_each(add_render_phase); + } else { + let query = material_meshes.p1(); + visible_entities + .entities + .iter() + .filter_map(|visible_entity| query.get(*visible_entity).ok()) + .for_each(add_render_phase); + } + }); } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 715c1a1f9548b..6bad70f77bc62 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -44,7 +44,9 @@ pub use once_cell; use crate::{ camera::CameraPlugin, mesh::MeshPlugin, - render_resource::{PipelineCache, Shader, ShaderLoader}, + render_resource::{ + lock_pipeline_cache, unlock_pipeline_cache, PipelineCache, Shader, ShaderLoader, + }, renderer::{render_system, RenderInstance}, settings::WgpuSettings, view::{ViewPlugin, WindowRenderPlugin}, @@ -170,8 +172,9 @@ impl Plugin for RenderPlugin { let asset_server = app.world.resource::().clone(); let mut render_app = App::empty(); - let mut extract_stage = - SystemStage::parallel().with_system(PipelineCache::extract_shaders); + let mut extract_stage = SystemStage::parallel() + .with_system(PipelineCache::extract_shaders) + .with_system(lock_pipeline_cache); // Get the ComponentId for MainWorld. This does technically 'waste' a `WorldId`, but that's probably fine render_app.init_resource::(); render_app.world.remove_resource::(); @@ -191,7 +194,10 @@ impl Plugin for RenderPlugin { .add_stage(RenderStage::Extract, extract_stage) .add_stage(RenderStage::Prepare, SystemStage::parallel()) .add_stage(RenderStage::Queue, SystemStage::parallel()) - .add_stage(RenderStage::PhaseSort, SystemStage::parallel()) + .add_stage( + RenderStage::PhaseSort, + SystemStage::parallel().with_system(unlock_pipeline_cache), + ) .add_stage( RenderStage::Render, SystemStage::parallel() diff --git a/crates/bevy_render/src/render_phase/mod.rs b/crates/bevy_render/src/render_phase/mod.rs index b0b502422b928..7e38d39bdd917 100644 --- a/crates/bevy_render/src/render_phase/mod.rs +++ b/crates/bevy_render/src/render_phase/mod.rs @@ -7,29 +7,64 @@ pub use draw_state::*; use bevy_ecs::prelude::{Component, Query}; use bevy_ecs::world::World; +use std::cell::Cell; +use thread_local::ThreadLocal; + +pub struct RenderPhaseScope<'a, I: PhaseItem> { + phase_queue: &'a mut Vec, +} + +impl<'a, I: PhaseItem> RenderPhaseScope<'a, I> { + /// Adds a [`PhaseItem`] to this render phase. + #[inline] + pub fn add(&mut self, item: I) { + self.phase_queue.push(item); + } +} /// A resource to collect and sort draw requests for specific [`PhaseItems`](PhaseItem). #[derive(Component)] pub struct RenderPhase { - pub items: Vec, + items: ThreadLocal>>, + pub sorted: Vec, } impl Default for RenderPhase { fn default() -> Self { - Self { items: Vec::new() } + Self { + items: ThreadLocal::new(), + sorted: Vec::new(), + } } } impl RenderPhase { - /// Adds a [`PhaseItem`] to this render phase. - #[inline] - pub fn add(&mut self, item: I) { - self.items.push(item); + pub fn get(&self) -> &Cell> { + self.items.get_or_default() + } + + pub fn phase_scope(&self, f: impl FnOnce(RenderPhaseScope<'_, I>)) { + let store = self.get(); + let mut phase_queue = store.take(); + f(RenderPhaseScope { + phase_queue: &mut phase_queue, + }); + store.set(phase_queue); } /// Sorts all of its [`PhaseItems`](PhaseItem). pub fn sort(&mut self) { - I::sort(&mut self.items); + self.sorted.clear(); + self.sorted.reserve( + self.items + .iter_mut() + .map(|queue| queue.get_mut().len()) + .sum(), + ); + for queue in self.items.iter_mut() { + self.sorted.append(queue.get_mut()); + } + I::sort(&mut self.sorted); } pub fn render<'w>( @@ -42,7 +77,7 @@ impl RenderPhase { let mut draw_functions = draw_functions.write(); draw_functions.prepare(world); - for item in &self.items { + for item in &self.sorted { let draw_function = draw_functions.get_mut(item.draw_function()).unwrap(); draw_function.draw(world, render_pass, view, item); } @@ -53,9 +88,9 @@ impl RenderPhase { /// Batches the compatible [`BatchedPhaseItem`]s of this render phase pub fn batch(&mut self) { // TODO: this could be done in-place - let mut items = std::mem::take(&mut self.items).into_iter(); + let mut items = std::mem::take(&mut self.sorted).into_iter(); - self.items.reserve(items.len()); + self.sorted.reserve(items.len()); // Start the first batch from the first item if let Some(mut current_batch) = items.next() { @@ -66,12 +101,12 @@ impl RenderPhase { BatchResult::IncompatibleItems ) { // Store the completed batch, and start a new one from the incompatible item - self.items.push(current_batch); + self.sorted.push(current_batch); current_batch = next_item; } } // Store the last batch - self.items.push(current_batch); + self.sorted.push(current_batch); } } } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 0209c2e8c9d97..5f70864cc58e5 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -10,13 +10,15 @@ use crate::{ Extract, }; use bevy_asset::{AssetEvent, Assets, Handle}; -use bevy_ecs::system::{Res, ResMut}; +use bevy_ecs::system::{Commands, Res, ResMut}; +use bevy_ecs::world::{FromWorld, World}; use bevy_ecs::{event::EventReader, system::Resource}; use bevy_utils::{ default, tracing::{debug, error}, Entry, HashMap, HashSet, }; +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref}; use thiserror::Error; use wgpu::{PipelineLayoutDescriptor, VertexBufferLayout as RawVertexBufferLayout}; @@ -44,15 +46,15 @@ pub enum Pipeline { ComputePipeline(ComputePipeline), } -type CachedPipelineId = usize; +type CachedPipelineId = u32; /// Index of a cached render pipeline in a [`PipelineCache`]. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub struct CachedRenderPipelineId(CachedPipelineId); +pub struct CachedRenderPipelineId(u32); impl CachedRenderPipelineId { /// An invalid cached render pipeline index, often used to initialize a variable. - pub const INVALID: Self = CachedRenderPipelineId(usize::MAX); + pub const INVALID: Self = CachedRenderPipelineId(u32::MAX); } /// Index of a cached compute pipeline in a [`PipelineCache`]. @@ -61,7 +63,7 @@ pub struct CachedComputePipelineId(CachedPipelineId); impl CachedComputePipelineId { /// An invalid cached compute pipeline index, often used to initialize a variable. - pub const INVALID: Self = CachedComputePipelineId(usize::MAX); + pub const INVALID: Self = CachedComputePipelineId(u32::MAX); } pub struct CachedPipeline { @@ -324,6 +326,27 @@ impl LayoutCache { } } +#[derive(Resource)] +pub struct LockablePipelineCache(RwLock); + +impl LockablePipelineCache { + pub fn read(&self) -> RwLockReadGuard<'_, PipelineCache> { + self.0.read() + } + + pub fn write(&self) -> RwLockWriteGuard<'_, PipelineCache> { + self.0.write() + } +} + +impl FromWorld for LockablePipelineCache { + fn from_world(world: &mut World) -> Self { + Self(RwLock::new( + world.remove_resource::().unwrap(), + )) + } +} + /// Cache for render and compute pipelines. /// /// The cache stores existing render and compute pipelines allocated on the GPU, as well as @@ -345,6 +368,16 @@ pub struct PipelineCache { waiting_pipelines: HashSet, } +impl FromWorld for PipelineCache { + fn from_world(world: &mut World) -> Self { + world + .remove_resource::() + .unwrap() + .0 + .into_inner() + } +} + impl PipelineCache { pub fn pipelines(&self) -> impl Iterator { self.pipelines.iter() @@ -366,7 +399,7 @@ impl PipelineCache { /// See [`PipelineCache::queue_render_pipeline()`]. #[inline] pub fn get_render_pipeline_state(&self, id: CachedRenderPipelineId) -> &CachedPipelineState { - &self.pipelines[id.0].state + &self.pipelines[id.0 as usize].state } /// Get the state of a cached compute pipeline. @@ -374,7 +407,7 @@ impl PipelineCache { /// See [`PipelineCache::queue_compute_pipeline()`]. #[inline] pub fn get_compute_pipeline_state(&self, id: CachedComputePipelineId) -> &CachedPipelineState { - &self.pipelines[id.0].state + &self.pipelines[id.0 as usize].state } /// Get the render pipeline descriptor a cached render pipeline was inserted from. @@ -385,7 +418,7 @@ impl PipelineCache { &self, id: CachedRenderPipelineId, ) -> &RenderPipelineDescriptor { - match &self.pipelines[id.0].descriptor { + match &self.pipelines[id.0 as usize].descriptor { PipelineDescriptor::RenderPipelineDescriptor(descriptor) => descriptor, PipelineDescriptor::ComputePipelineDescriptor(_) => unreachable!(), } @@ -399,7 +432,7 @@ impl PipelineCache { &self, id: CachedComputePipelineId, ) -> &ComputePipelineDescriptor { - match &self.pipelines[id.0].descriptor { + match &self.pipelines[id.0 as usize].descriptor { PipelineDescriptor::RenderPipelineDescriptor(_) => unreachable!(), PipelineDescriptor::ComputePipelineDescriptor(descriptor) => descriptor, } @@ -415,7 +448,7 @@ impl PipelineCache { #[inline] pub fn get_render_pipeline(&self, id: CachedRenderPipelineId) -> Option<&RenderPipeline> { if let CachedPipelineState::Ok(Pipeline::RenderPipeline(pipeline)) = - &self.pipelines[id.0].state + &self.pipelines[id.0 as usize].state { Some(pipeline) } else { @@ -433,7 +466,7 @@ impl PipelineCache { #[inline] pub fn get_compute_pipeline(&self, id: CachedComputePipelineId) -> Option<&ComputePipeline> { if let CachedPipelineState::Ok(Pipeline::ComputePipeline(pipeline)) = - &self.pipelines[id.0].state + &self.pipelines[id.0 as usize].state { Some(pipeline) } else { @@ -458,7 +491,8 @@ impl PipelineCache { &mut self, descriptor: RenderPipelineDescriptor, ) -> CachedRenderPipelineId { - let id = CachedRenderPipelineId(self.pipelines.len()); + assert!(self.pipelines.len() < u32::MAX as usize); + let id = CachedRenderPipelineId(self.pipelines.len() as u32); self.pipelines.push(CachedPipeline { descriptor: PipelineDescriptor::RenderPipelineDescriptor(Box::new(descriptor)), state: CachedPipelineState::Queued, @@ -484,7 +518,8 @@ impl PipelineCache { &mut self, descriptor: ComputePipelineDescriptor, ) -> CachedComputePipelineId { - let id = CachedComputePipelineId(self.pipelines.len()); + assert!(self.pipelines.len() < u32::MAX as usize); + let id = CachedComputePipelineId(self.pipelines.len() as u32); self.pipelines.push(CachedPipeline { descriptor: PipelineDescriptor::ComputePipelineDescriptor(Box::new(descriptor)), state: CachedPipelineState::Queued, @@ -496,7 +531,7 @@ impl PipelineCache { fn set_shader(&mut self, handle: &Handle, shader: &Shader) { let pipelines_to_queue = self.shader_cache.set_shader(handle, shader.clone()); for cached_pipeline in pipelines_to_queue { - self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; + self.pipelines[cached_pipeline as usize].state = CachedPipelineState::Queued; self.waiting_pipelines.insert(cached_pipeline); } } @@ -504,7 +539,7 @@ impl PipelineCache { fn remove_shader(&mut self, shader: &Handle) { let pipelines_to_queue = self.shader_cache.remove(shader); for cached_pipeline in pipelines_to_queue { - self.pipelines[cached_pipeline].state = CachedPipelineState::Queued; + self.pipelines[cached_pipeline as usize].state = CachedPipelineState::Queued; self.waiting_pipelines.insert(cached_pipeline); } } @@ -636,17 +671,17 @@ impl PipelineCache { let mut pipelines = mem::take(&mut self.pipelines); for id in waiting_pipelines { - let pipeline = &mut pipelines[id]; + let pipeline = &mut pipelines[id as usize]; if matches!(pipeline.state, CachedPipelineState::Ok(_)) { continue; } pipeline.state = match &pipeline.descriptor { PipelineDescriptor::RenderPipelineDescriptor(descriptor) => { - self.process_render_pipeline(id, descriptor) + self.process_render_pipeline(id, &descriptor) } PipelineDescriptor::ComputePipelineDescriptor(descriptor) => { - self.process_compute_pipeline(id, descriptor) + self.process_compute_pipeline(id, &descriptor) } }; @@ -768,7 +803,7 @@ fn log_shader_error(source: &ProcessedShader, error: &AsModuleDescriptorError) { .collect(), ) .with_notes( - ErrorSources::of(error) + ErrorSources::of(&error) .map(|source| source.to_string()) .collect(), ); @@ -830,3 +865,11 @@ impl<'a> Iterator for ErrorSources<'a> { } impl<'a> FusedIterator for ErrorSources<'a> {} + +pub(crate) fn lock_pipeline_cache(mut commands: Commands) { + commands.init_resource::(); +} + +pub(crate) fn unlock_pipeline_cache(mut commands: Commands) { + commands.init_resource::(); +} diff --git a/crates/bevy_render/src/render_resource/pipeline_specializer.rs b/crates/bevy_render/src/render_resource/pipeline_specializer.rs index d685ca7f228a5..8745b7e2fb7ed 100644 --- a/crates/bevy_render/src/render_resource/pipeline_specializer.rs +++ b/crates/bevy_render/src/render_resource/pipeline_specializer.rs @@ -2,8 +2,8 @@ use crate::render_resource::CachedComputePipelineId; use crate::{ mesh::{InnerMeshVertexBufferLayout, MeshVertexBufferLayout, MissingVertexAttributeError}, render_resource::{ - CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache, RenderPipelineDescriptor, - VertexBufferLayout, + CachedRenderPipelineId, ComputePipelineDescriptor, LockablePipelineCache, + RenderPipelineDescriptor, VertexBufferLayout, }, }; use bevy_ecs::system::Resource; @@ -33,13 +33,13 @@ impl Default for SpecializedRenderPipelines { impl SpecializedRenderPipelines { pub fn specialize( &mut self, - cache: &mut PipelineCache, + cache: &LockablePipelineCache, specialize_pipeline: &S, key: S::Key, ) -> CachedRenderPipelineId { *self.cache.entry(key.clone()).or_insert_with(|| { let descriptor = specialize_pipeline.specialize(key); - cache.queue_render_pipeline(descriptor) + cache.write().queue_render_pipeline(descriptor) }) } } @@ -63,13 +63,13 @@ impl Default for SpecializedComputePipelines { impl SpecializedComputePipelines { pub fn specialize( &mut self, - cache: &mut PipelineCache, + cache: &LockablePipelineCache, specialize_pipeline: &S, key: S::Key, ) -> CachedComputePipelineId { *self.cache.entry(key.clone()).or_insert_with(|| { let descriptor = specialize_pipeline.specialize(key); - cache.queue_compute_pipeline(descriptor) + cache.write().queue_compute_pipeline(descriptor) }) } } @@ -103,7 +103,7 @@ impl SpecializedMeshPipelines { #[inline] pub fn specialize( &mut self, - cache: &mut PipelineCache, + cache: &LockablePipelineCache, specialize_pipeline: &S, key: S::Key, layout: &MeshVertexBufferLayout, @@ -141,6 +141,7 @@ impl SpecializedMeshPipelines { Ok(*entry.insert(match layout_map.entry(key) { Entry::Occupied(entry) => { if cfg!(debug_assertions) { + let cache = cache.read(); let stored_descriptor = cache.get_render_pipeline_descriptor(*entry.get()); if stored_descriptor != &descriptor { error!("The cached pipeline descriptor for {} is not equal to the generated descriptor for the given key. This means the SpecializePipeline implementation uses 'unused' MeshVertexBufferLayout information to specialize the pipeline. This is not allowed because it would invalidate the pipeline cache.", std::any::type_name::()); @@ -149,6 +150,7 @@ impl SpecializedMeshPipelines { *entry.into_mut() } Entry::Vacant(entry) => { + let mut cache = cache.write(); *entry.insert(cache.queue_render_pipeline(descriptor)) } })) diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 0a2a0b35ee116..7f27d1bbe9cef 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -25,8 +25,8 @@ use bevy_render::{ RenderPhase, SetItemPipeline, TrackedRenderPass, }, render_resource::{ - AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource, - PipelineCache, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline, + AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, LockablePipelineCache, + OwnedBindingResource, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, @@ -307,16 +307,16 @@ pub fn queue_material2d_meshes( transparent_draw_functions: Res>, material2d_pipeline: Res>, mut pipelines: ResMut>>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, msaa: Res, render_meshes: Res>, render_materials: Res>, material2d_meshes: Query<(&Handle, &Mesh2dHandle, &Mesh2dUniform)>, - mut views: Query<( + views: Query<( &ExtractedView, &VisibleEntities, Option<&Tonemapping>, - &mut RenderPhase, + &RenderPhase, )>, ) where M::Data: PartialEq + Eq + Hash + Clone, @@ -325,7 +325,7 @@ pub fn queue_material2d_meshes( return; } - for (view, visible_entities, tonemapping, mut transparent_phase) in &mut views { + for (view, visible_entities, tonemapping, transparent_phase) in &views { let draw_transparent_pbr = transparent_draw_functions.read().id::>(); let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples) @@ -341,50 +341,54 @@ pub fn queue_material2d_meshes( } } - for visible_entity in &visible_entities.entities { - if let Ok((material2d_handle, mesh2d_handle, mesh2d_uniform)) = - material2d_meshes.get(*visible_entity) - { - if let Some(material2d) = render_materials.get(material2d_handle) { - if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) { - let mesh_key = view_key - | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); - - let pipeline_id = pipelines.specialize( - &mut pipeline_cache, - &material2d_pipeline, - Material2dKey { - mesh_key, - bind_group_data: material2d.key.clone(), - }, - &mesh.layout, - ); - - let pipeline_id = match pipeline_id { - Ok(id) => id, - Err(err) => { - error!("{}", err); - continue; - } - }; - - let mesh_z = mesh2d_uniform.transform.w_axis.z; - transparent_phase.add(Transparent2d { - entity: *visible_entity, - draw_function: draw_transparent_pbr, - pipeline: pipeline_id, - // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the - // lowest sort key and getting closer should increase. As we have - // -z in front of the camera, the largest distance is -far with values increasing toward the - // camera. As such we can just use mesh_z as the distance - sort_key: FloatOrd(mesh_z), - // This material is not batched - batch_range: None, - }); + transparent_phase.phase_scope(|mut phase| { + for visible_entity in &visible_entities.entities { + if let Ok((material2d_handle, mesh2d_handle, mesh2d_uniform)) = + material2d_meshes.get(*visible_entity) + { + if let Some(material2d) = render_materials.get(material2d_handle) { + if let Some(mesh) = render_meshes.get(&mesh2d_handle.0) { + let mesh_key = view_key + | Mesh2dPipelineKey::from_primitive_topology( + mesh.primitive_topology, + ); + + let pipeline_id = pipelines.specialize( + &pipeline_cache, + &material2d_pipeline, + Material2dKey { + mesh_key, + bind_group_data: material2d.key.clone(), + }, + &mesh.layout, + ); + + let pipeline_id = match pipeline_id { + Ok(id) => id, + Err(err) => { + error!("{}", err); + continue; + } + }; + + let mesh_z = mesh2d_uniform.transform.w_axis.z; + phase.add(Transparent2d { + entity: *visible_entity, + draw_function: draw_transparent_pbr, + pipeline: pipeline_id, + // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the + // lowest sort key and getting closer should increase. As we have + // -z in front of the camera, the largest distance is -far with values increasing toward the + // camera. As such we can just use mesh_z as the distance + sort_key: FloatOrd(mesh_z), + // This material is not batched + batch_range: None, + }); + } } } } - } + }); } } diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index e7df0c1c323ca..ee8bcf3c2532e 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -451,7 +451,7 @@ pub fn queue_sprites( view_uniforms: Res, sprite_pipeline: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut image_bind_groups: ResMut, gpu_images: Res>, msaa: Res, @@ -516,7 +516,7 @@ pub fn queue_sprites( }); let image_bind_groups = &mut *image_bind_groups; - for (mut transparent_phase, visible_entities, view, tonemapping) in &mut views { + for (transparent_phase, visible_entities, view, tonemapping) in &mut views { let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key; if let Some(Tonemapping::Enabled { deband_dither }) = tonemapping { if !view.hdr { @@ -528,19 +528,18 @@ pub fn queue_sprites( } } let pipeline = pipelines.specialize( - &mut pipeline_cache, + &pipeline_cache, &sprite_pipeline, view_key | SpritePipelineKey::from_colored(false), ); let colored_pipeline = pipelines.specialize( - &mut pipeline_cache, + &pipeline_cache, &sprite_pipeline, view_key | SpritePipelineKey::from_colored(true), ); view_entities.clear(); view_entities.extend(visible_entities.entities.iter().map(|e| e.index() as usize)); - transparent_phase.items.reserve(extracted_sprites.len()); // Impossible starting values that will be replaced on the first iteration let mut current_batch = SpriteBatch { @@ -549,135 +548,161 @@ pub fn queue_sprites( }; let mut current_batch_entity = Entity::PLACEHOLDER; let mut current_image_size = Vec2::ZERO; - // Add a phase item for each sprite, and detect when succesive items can be batched. - // Spawn an entity with a `SpriteBatch` component for each possible batch. - // Compatible items share the same entity. - // Batches are merged later (in `batch_phase_system()`), so that they can be interrupted - // by any other phase item (and they can interrupt other items from batching). - for extracted_sprite in extracted_sprites.iter() { - if !view_entities.contains(extracted_sprite.entity.index() as usize) { - continue; - } - let new_batch = SpriteBatch { - image_handle_id: extracted_sprite.image_handle_id, - colored: extracted_sprite.color != Color::WHITE, - }; - if new_batch != current_batch { - // Set-up a new possible batch - if let Some(gpu_image) = - gpu_images.get(&Handle::weak(new_batch.image_handle_id)) - { - current_batch = new_batch; - current_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); - current_batch_entity = commands.spawn(current_batch).id(); - - image_bind_groups - .values - .entry(Handle::weak(current_batch.image_handle_id)) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView( - &gpu_image.texture_view, - ), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("sprite_material_bind_group"), - layout: &sprite_pipeline.material_layout, - }) - }); - } else { - // Skip this item if the texture is not ready + transparent_phase.phase_scope(|mut phase| { + // Add a phase item for each sprite, and detect when succesive items can be batched. + // Spawn an entity with a `SpriteBatch` component for each possible batch. + // Compatible items share the same entity. + // Batches are merged later (in `batch_phase_system()`), so that they can be interrupted + // by any other phase item (and they can interrupt other items from batching). + for extracted_sprite in extracted_sprites.iter() { + if !view_entities.contains(extracted_sprite.entity.index() as usize) { continue; } - } - - // Calculate vertex data for this item - - let mut uvs = QUAD_UVS; - if extracted_sprite.flip_x { - uvs = [uvs[1], uvs[0], uvs[3], uvs[2]]; - } - if extracted_sprite.flip_y { - uvs = [uvs[3], uvs[2], uvs[1], uvs[0]]; - } - - // By default, the size of the quad is the size of the texture - let mut quad_size = current_image_size; - - // If a rect is specified, adjust UVs and the size of the quad - if let Some(rect) = extracted_sprite.rect { - let rect_size = rect.size(); - for uv in &mut uvs { - *uv = (rect.min + *uv * rect_size) / current_image_size; - } - quad_size = rect_size; - } + let new_batch = SpriteBatch { + image_handle_id: extracted_sprite.image_handle_id, + colored: extracted_sprite.color != Color::WHITE, + }; + if new_batch != current_batch { + // Set-up a new possible batch + if let Some(gpu_image) = + gpu_images.get(&Handle::weak(new_batch.image_handle_id)) + { + current_batch = new_batch; + current_image_size = Vec2::new(gpu_image.size.x, gpu_image.size.y); + current_batch_entity = commands.spawn(current_batch).id(); + + image_bind_groups + .values + .entry(Handle::weak(current_batch.image_handle_id)) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler( + &gpu_image.sampler, + ), + }, + ], + label: Some("sprite_material_bind_group"), + layout: &sprite_pipeline.material_layout, + }) + }); + } else { + // Skip this item if the texture is not ready + continue; + } + + // Calculate vertex data for this item + + let mut uvs = QUAD_UVS; + if extracted_sprite.flip_x { + uvs = [uvs[1], uvs[0], uvs[3], uvs[2]]; + } + if extracted_sprite.flip_y { + uvs = [uvs[3], uvs[2], uvs[1], uvs[0]]; + } + + // By default, the size of the quad is the size of the texture + let mut quad_size = current_image_size; + + // If a rect is specified, adjust UVs and the size of the quad + if let Some(rect) = extracted_sprite.rect { + let rect_size = rect.size(); + for uv in &mut uvs { + *uv = (rect.min + *uv * rect_size) / current_image_size; + } + quad_size = rect_size; + } + + // Override the size if a custom one is specified + if let Some(custom_size) = extracted_sprite.custom_size { + quad_size = custom_size; + } + + // Apply size and global transform + let positions = QUAD_VERTEX_POSITIONS.map(|quad_pos| { + extracted_sprite + .transform + .transform_point( + ((quad_pos - extracted_sprite.anchor) * quad_size).extend(0.), + ) + .into() + }); - // Override the size if a custom one is specified - if let Some(custom_size) = extracted_sprite.custom_size { - quad_size = custom_size; - } + // Store the vertex data and add the item to the render phase + if current_batch.colored { + for i in QUAD_INDICES { + sprite_meta.colored_vertices.push(ColoredSpriteVertex { + position: positions[i], + uv: uvs[i].into(), + color: extracted_sprite.color.as_linear_rgba_f32(), + }); + } + + // Apply size and global transform + let positions = QUAD_VERTEX_POSITIONS.map(|quad_pos| { + extracted_sprite + .transform + .transform_point( + ((quad_pos - extracted_sprite.anchor) * quad_size) + .extend(0.), + ) + .into() + }); - // Apply size and global transform - let positions = QUAD_VERTEX_POSITIONS.map(|quad_pos| { - extracted_sprite - .transform - .transform_point( - ((quad_pos - extracted_sprite.anchor) * quad_size).extend(0.), - ) - .into() - }); - - // These items will be sorted by depth with other phase items - let sort_key = FloatOrd(extracted_sprite.transform.translation().z); - - // Store the vertex data and add the item to the render phase - if current_batch.colored { - for i in QUAD_INDICES { - sprite_meta.colored_vertices.push(ColoredSpriteVertex { - position: positions[i], - uv: uvs[i].into(), - color: extracted_sprite.color.as_linear_rgba_f32(), - }); - } - let item_start = colored_index; - colored_index += QUAD_INDICES.len() as u32; - let item_end = colored_index; - - transparent_phase.add(Transparent2d { - draw_function: draw_sprite_function, - pipeline: colored_pipeline, - entity: current_batch_entity, - sort_key, - batch_range: Some(item_start..item_end), - }); - } else { - for i in QUAD_INDICES { - sprite_meta.vertices.push(SpriteVertex { - position: positions[i], - uv: uvs[i].into(), - }); + // These items will be sorted by depth with other phase items + let sort_key = FloatOrd(extracted_sprite.transform.translation().z); + + // Store the vertex data and add the item to the render phase + if current_batch.colored { + for i in QUAD_INDICES { + sprite_meta.colored_vertices.push(ColoredSpriteVertex { + position: positions[i], + uv: uvs[i].into(), + color: extracted_sprite.color.as_linear_rgba_f32(), + }); + } + let item_start = colored_index; + colored_index += QUAD_INDICES.len() as u32; + let item_end = colored_index; + + phase.add(Transparent2d { + draw_function: draw_sprite_function, + pipeline: colored_pipeline, + entity: current_batch_entity, + sort_key, + batch_range: Some(item_start..item_end), + }); + } else { + for i in QUAD_INDICES { + sprite_meta.vertices.push(SpriteVertex { + position: positions[i], + uv: uvs[i].into(), + }); + } + let item_start = index; + index += QUAD_INDICES.len() as u32; + let item_end = index; + + phase.add(Transparent2d { + draw_function: draw_sprite_function, + pipeline, + entity: current_batch_entity, + sort_key, + batch_range: Some(item_start..item_end), + }); + } + } } - let item_start = index; - index += QUAD_INDICES.len() as u32; - let item_end = index; - - transparent_phase.add(Transparent2d { - draw_function: draw_sprite_function, - pipeline, - entity: current_batch_entity, - sort_key, - batch_range: Some(item_start..item_end), - }); } - } + }); } sprite_meta .vertices diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 595896c1fdfec..d8b9181730b28 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -562,11 +562,11 @@ pub fn queue_uinodes( view_uniforms: Res, ui_pipeline: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, mut image_bind_groups: ResMut, gpu_images: Res>, ui_batches: Query<(Entity, &UiBatch)>, - mut views: Query<(&ExtractedView, &mut RenderPhase)>, + views: Query<(&ExtractedView, &RenderPhase)>, events: Res, ) { // If an image has changed, the GpuImage has (probably) changed @@ -588,41 +588,46 @@ pub fn queue_uinodes( label: Some("ui_view_bind_group"), layout: &ui_pipeline.view_layout, })); - let draw_ui_function = draw_functions.read().id::(); - for (view, mut transparent_phase) in &mut views { + let draw_ui_function = draw_functions.read().get_id::().unwrap(); + for (view, transparent_phase) in &views { let pipeline = pipelines.specialize( - &mut pipeline_cache, + &pipeline_cache, &ui_pipeline, UiPipelineKey { hdr: view.hdr }, ); - for (entity, batch) in &ui_batches { - image_bind_groups - .values - .entry(batch.image.clone_weak()) - .or_insert_with(|| { - let gpu_image = gpu_images.get(&batch.image).unwrap(); - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView(&gpu_image.texture_view), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("ui_material_bind_group"), - layout: &ui_pipeline.image_layout, - }) + transparent_phase.phase_scope(|mut phase| { + for (entity, batch) in ui_batches.iter() { + image_bind_groups + .values + .entry(batch.image.clone_weak()) + .or_insert_with(|| { + let gpu_image = gpu_images.get(&batch.image).unwrap(); + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("ui_material_bind_group"), + layout: &ui_pipeline.image_layout, + }) + }); + + phase.add(TransparentUi { + draw_function: draw_ui_function, + pipeline, + entity, + sort_key: FloatOrd(batch.z), }); - transparent_phase.add(TransparentUi { - draw_function: draw_ui_function, - pipeline, - entity, - sort_key: FloatOrd(batch.z), - }); - } + } + }); } } } diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index ee05cf6f8a21b..17a00a074b13f 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -59,7 +59,7 @@ impl Node for UiPassNode { else { return Ok(()); }; - if transparent_phase.items.is_empty() { + if transparent_phase.sorted.is_empty() { return Ok(()); } // Don't render UI for cameras where it is explicitly disabled diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 4147a4a4966f4..6c739bbf49f7b 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -312,7 +312,7 @@ pub fn queue_colored_mesh2d( transparent_draw_functions: Res>, colored_mesh2d_pipeline: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, msaa: Res, render_meshes: Res>, colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With>, diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 7217722253b50..df11ae79e5127 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -104,33 +104,35 @@ fn queue_custom( custom_pipeline: Res, msaa: Res, mut pipelines: ResMut>, - mut pipeline_cache: ResMut, + pipeline_cache: Res, meshes: Res>, material_meshes: Query<(Entity, &MeshUniform, &Handle), With>, - mut views: Query<(&ExtractedView, &mut RenderPhase)>, + views: Query<(&ExtractedView, &RenderPhase)>, ) { let draw_custom = transparent_3d_draw_functions.read().id::(); let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples); - for (view, mut transparent_phase) in &mut views { + for (view, transparent_phase) in &views { let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); let rangefinder = view.rangefinder3d(); - for (entity, mesh_uniform, mesh_handle) in &material_meshes { - if let Some(mesh) = meshes.get(mesh_handle) { - let key = - view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); - let pipeline = pipelines - .specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout) - .unwrap(); - transparent_phase.add(Transparent3d { - entity, - pipeline, - draw_function: draw_custom, - distance: rangefinder.distance(&mesh_uniform.transform), - }); + transparent_phase.phase_scope(|mut phase| { + for (entity, mesh_uniform, mesh_handle) in &material_meshes { + if let Some(mesh) = meshes.get(mesh_handle) { + let key = view_key + | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); + let pipeline = pipelines + .specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout) + .unwrap(); + phase.add(Transparent3d { + entity, + pipeline, + draw_function: draw_custom, + distance: rangefinder.distance(&mesh_uniform.transform), + }); + } } - } + }); } }