Make PipelineCache internally mutable. (#7205)

# Objective

- Allow rendering queue systems to use a `Res<PipelineCache>` even for queueing up new rendering pipelines. This is part of unblocking parallel execution queue systems.

## Solution

- Make `PipelineCache` internally mutable w.r.t to queueing new pipelines. Pipelines are no longer immediately updated into the cache state, but rather queued into a Vec. The Vec of pending new pipelines is then later processed at the same time we actually create the queued pipelines on the GPU device.

---

## Changelog

`PipelineCache` no longer requires mutable access in order to queue render / compute pipelines.

## Migration Guide

* Most usages of `resource_mut::<PipelineCache>` and `ResMut<PipelineCache>` can be changed to `resource::<PipelineCache>` and `Res<PipelineCache>` as long as they don't use any methods requiring mutability - the only public method requiring it is `process_queue`.
This commit is contained in:
Daniel Chia 2023-01-16 15:41:14 +00:00
parent 4b326fb4ca
commit 517deda215
16 changed files with 50 additions and 36 deletions

View File

@ -434,7 +434,7 @@ impl FromWorld for BloomPipelines {
], ],
}); });
let mut pipeline_cache = world.resource_mut::<PipelineCache>(); let pipeline_cache = world.resource::<PipelineCache>();
let downsampling_prefilter_pipeline = let downsampling_prefilter_pipeline =
pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor { pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {

View File

@ -223,7 +223,7 @@ impl SpecializedRenderPipeline for FxaaPipeline {
pub fn prepare_fxaa_pipelines( pub fn prepare_fxaa_pipelines(
mut commands: Commands, mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<FxaaPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<FxaaPipeline>>,
fxaa_pipeline: Res<FxaaPipeline>, fxaa_pipeline: Res<FxaaPipeline>,
views: Query<(Entity, &ExtractedView, &Fxaa)>, views: Query<(Entity, &ExtractedView, &Fxaa)>,
@ -233,7 +233,7 @@ pub fn prepare_fxaa_pipelines(
continue; continue;
} }
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&fxaa_pipeline, &fxaa_pipeline,
FxaaPipelineKey { FxaaPipelineKey {
edge_threshold: fxaa.edge_threshold, edge_threshold: fxaa.edge_threshold,

View File

@ -126,7 +126,7 @@ pub struct ViewTonemappingPipeline(CachedRenderPipelineId);
pub fn queue_view_tonemapping_pipelines( pub fn queue_view_tonemapping_pipelines(
mut commands: Commands, mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<TonemappingPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<TonemappingPipeline>>,
upscaling_pipeline: Res<TonemappingPipeline>, upscaling_pipeline: Res<TonemappingPipeline>,
view_targets: Query<(Entity, &Tonemapping)>, view_targets: Query<(Entity, &Tonemapping)>,
@ -136,7 +136,7 @@ pub fn queue_view_tonemapping_pipelines(
let key = TonemappingPipelineKey { let key = TonemappingPipelineKey {
deband_dither: *deband_dither, deband_dither: *deband_dither,
}; };
let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key); let pipeline = pipelines.specialize(&pipeline_cache, &upscaling_pipeline, key);
commands commands
.entity(entity) .entity(entity)

View File

@ -112,7 +112,7 @@ pub struct ViewUpscalingPipeline(CachedRenderPipelineId);
fn queue_view_upscaling_pipelines( fn queue_view_upscaling_pipelines(
mut commands: Commands, mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<UpscalingPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<UpscalingPipeline>>,
upscaling_pipeline: Res<UpscalingPipeline>, upscaling_pipeline: Res<UpscalingPipeline>,
view_targets: Query<(Entity, &ViewTarget)>, view_targets: Query<(Entity, &ViewTarget)>,
@ -122,7 +122,7 @@ fn queue_view_upscaling_pipelines(
upscaling_mode: UpscalingMode::Filtering, upscaling_mode: UpscalingMode::Filtering,
texture_format: view_target.out_texture_format(), 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 commands
.entity(entity) .entity(entity)

View File

@ -333,7 +333,7 @@ pub fn queue_material_meshes<M: Material>(
transparent_draw_functions: Res<DrawFunctions<Transparent3d>>, transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
material_pipeline: Res<MaterialPipeline<M>>, material_pipeline: Res<MaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>, mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials<M>>, render_materials: Res<RenderMaterials<M>>,
@ -391,7 +391,7 @@ pub fn queue_material_meshes<M: Material>(
} }
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&material_pipeline, &material_pipeline,
MaterialPipelineKey { MaterialPipelineKey {
mesh_key, mesh_key,

View File

@ -1626,7 +1626,7 @@ pub fn queue_shadows(
casting_meshes: Query<&Handle<Mesh>, Without<NotShadowCaster>>, casting_meshes: Query<&Handle<Mesh>, Without<NotShadowCaster>>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
mut pipelines: ResMut<SpecializedMeshPipelines<ShadowPipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<ShadowPipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
view_lights: Query<&ViewLightEntities>, view_lights: Query<&ViewLightEntities>,
mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>, mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>,
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>, point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
@ -1661,7 +1661,7 @@ pub fn queue_shadows(
let key = let key =
ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology); ShadowPipelineKey::from_primitive_topology(mesh.primitive_topology);
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&shadow_pipeline, &shadow_pipeline,
key, key,
&mesh.layout, &mesh.layout,

View File

@ -108,7 +108,7 @@ fn queue_wireframes(
wireframe_config: Res<WireframeConfig>, wireframe_config: Res<WireframeConfig>,
wireframe_pipeline: Res<WireframePipeline>, wireframe_pipeline: Res<WireframePipeline>,
mut pipelines: ResMut<SpecializedMeshPipelines<WireframePipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<WireframePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
mut material_meshes: ParamSet<( mut material_meshes: ParamSet<(
Query<(Entity, &Handle<Mesh>, &MeshUniform)>, Query<(Entity, &Handle<Mesh>, &MeshUniform)>,
@ -128,7 +128,7 @@ fn queue_wireframes(
let key = view_key let key = view_key
| MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&wireframe_pipeline, &wireframe_pipeline,
key, key,
&mesh.layout, &mesh.layout,

View File

@ -198,6 +198,8 @@ impl Plugin for RenderPlugin {
.add_stage( .add_stage(
RenderStage::Render, RenderStage::Render,
SystemStage::parallel() SystemStage::parallel()
// Note: Must run before `render_system` in order to
// processed newly queued pipelines.
.with_system(PipelineCache::process_pipeline_queue_system) .with_system(PipelineCache::process_pipeline_queue_system)
.with_system(render_system.at_end()), .with_system(render_system.at_end()),
) )

View File

@ -17,6 +17,7 @@ use bevy_utils::{
tracing::{debug, error}, tracing::{debug, error},
Entry, HashMap, HashSet, Entry, HashMap, HashSet,
}; };
use parking_lot::Mutex;
use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref}; use std::{hash::Hash, iter::FusedIterator, mem, ops::Deref};
use thiserror::Error; use thiserror::Error;
use wgpu::{PipelineLayoutDescriptor, VertexBufferLayout as RawVertexBufferLayout}; use wgpu::{PipelineLayoutDescriptor, VertexBufferLayout as RawVertexBufferLayout};
@ -343,6 +344,7 @@ pub struct PipelineCache {
device: RenderDevice, device: RenderDevice,
pipelines: Vec<CachedPipeline>, pipelines: Vec<CachedPipeline>,
waiting_pipelines: HashSet<CachedPipelineId>, waiting_pipelines: HashSet<CachedPipelineId>,
new_pipelines: Mutex<Vec<CachedPipeline>>,
} }
impl PipelineCache { impl PipelineCache {
@ -357,6 +359,7 @@ impl PipelineCache {
layout_cache: default(), layout_cache: default(),
shader_cache: default(), shader_cache: default(),
waiting_pipelines: default(), waiting_pipelines: default(),
new_pipelines: default(),
pipelines: default(), pipelines: default(),
} }
} }
@ -455,15 +458,15 @@ impl PipelineCache {
/// [`get_render_pipeline_state()`]: PipelineCache::get_render_pipeline_state /// [`get_render_pipeline_state()`]: PipelineCache::get_render_pipeline_state
/// [`get_render_pipeline()`]: PipelineCache::get_render_pipeline /// [`get_render_pipeline()`]: PipelineCache::get_render_pipeline
pub fn queue_render_pipeline( pub fn queue_render_pipeline(
&mut self, &self,
descriptor: RenderPipelineDescriptor, descriptor: RenderPipelineDescriptor,
) -> CachedRenderPipelineId { ) -> CachedRenderPipelineId {
let id = CachedRenderPipelineId(self.pipelines.len()); let mut new_pipelines = self.new_pipelines.lock();
self.pipelines.push(CachedPipeline { let id = CachedRenderPipelineId(self.pipelines.len() + new_pipelines.len());
new_pipelines.push(CachedPipeline {
descriptor: PipelineDescriptor::RenderPipelineDescriptor(Box::new(descriptor)), descriptor: PipelineDescriptor::RenderPipelineDescriptor(Box::new(descriptor)),
state: CachedPipelineState::Queued, state: CachedPipelineState::Queued,
}); });
self.waiting_pipelines.insert(id.0);
id id
} }
@ -481,15 +484,15 @@ impl PipelineCache {
/// [`get_compute_pipeline_state()`]: PipelineCache::get_compute_pipeline_state /// [`get_compute_pipeline_state()`]: PipelineCache::get_compute_pipeline_state
/// [`get_compute_pipeline()`]: PipelineCache::get_compute_pipeline /// [`get_compute_pipeline()`]: PipelineCache::get_compute_pipeline
pub fn queue_compute_pipeline( pub fn queue_compute_pipeline(
&mut self, &self,
descriptor: ComputePipelineDescriptor, descriptor: ComputePipelineDescriptor,
) -> CachedComputePipelineId { ) -> CachedComputePipelineId {
let id = CachedComputePipelineId(self.pipelines.len()); let mut new_pipelines = self.new_pipelines.lock();
self.pipelines.push(CachedPipeline { let id = CachedComputePipelineId(self.pipelines.len() + new_pipelines.len());
new_pipelines.push(CachedPipeline {
descriptor: PipelineDescriptor::ComputePipelineDescriptor(Box::new(descriptor)), descriptor: PipelineDescriptor::ComputePipelineDescriptor(Box::new(descriptor)),
state: CachedPipelineState::Queued, state: CachedPipelineState::Queued,
}); });
self.waiting_pipelines.insert(id.0);
id id
} }
@ -632,9 +635,18 @@ impl PipelineCache {
/// ///
/// [`RenderStage::Render`]: crate::RenderStage::Render /// [`RenderStage::Render`]: crate::RenderStage::Render
pub fn process_queue(&mut self) { pub fn process_queue(&mut self) {
let waiting_pipelines = mem::take(&mut self.waiting_pipelines); let mut waiting_pipelines = mem::take(&mut self.waiting_pipelines);
let mut pipelines = mem::take(&mut self.pipelines); let mut pipelines = mem::take(&mut self.pipelines);
{
let mut new_pipelines = self.new_pipelines.lock();
for new_pipeline in new_pipelines.drain(..) {
let id = pipelines.len();
pipelines.push(new_pipeline);
waiting_pipelines.insert(id);
}
}
for id in waiting_pipelines { for id in waiting_pipelines {
let pipeline = &mut pipelines[id]; let pipeline = &mut pipelines[id];
if matches!(pipeline.state, CachedPipelineState::Ok(_)) { if matches!(pipeline.state, CachedPipelineState::Ok(_)) {

View File

@ -33,7 +33,7 @@ impl<S: SpecializedRenderPipeline> Default for SpecializedRenderPipelines<S> {
impl<S: SpecializedRenderPipeline> SpecializedRenderPipelines<S> { impl<S: SpecializedRenderPipeline> SpecializedRenderPipelines<S> {
pub fn specialize( pub fn specialize(
&mut self, &mut self,
cache: &mut PipelineCache, cache: &PipelineCache,
specialize_pipeline: &S, specialize_pipeline: &S,
key: S::Key, key: S::Key,
) -> CachedRenderPipelineId { ) -> CachedRenderPipelineId {
@ -103,7 +103,7 @@ impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
#[inline] #[inline]
pub fn specialize( pub fn specialize(
&mut self, &mut self,
cache: &mut PipelineCache, cache: &PipelineCache,
specialize_pipeline: &S, specialize_pipeline: &S,
key: S::Key, key: S::Key,
layout: &MeshVertexBufferLayout, layout: &MeshVertexBufferLayout,

View File

@ -319,7 +319,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
transparent_draw_functions: Res<DrawFunctions<Transparent2d>>, transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
material2d_pipeline: Res<Material2dPipeline<M>>, material2d_pipeline: Res<Material2dPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>, mut pipelines: ResMut<SpecializedMeshPipelines<Material2dPipeline<M>>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
render_materials: Res<RenderMaterials2d<M>>, render_materials: Res<RenderMaterials2d<M>>,
@ -363,7 +363,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
| Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology); | Mesh2dPipelineKey::from_primitive_topology(mesh.primitive_topology);
let pipeline_id = pipelines.specialize( let pipeline_id = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&material2d_pipeline, &material2d_pipeline,
Material2dKey { Material2dKey {
mesh_key, mesh_key,

View File

@ -451,7 +451,7 @@ pub fn queue_sprites(
view_uniforms: Res<ViewUniforms>, view_uniforms: Res<ViewUniforms>,
sprite_pipeline: Res<SpritePipeline>, sprite_pipeline: Res<SpritePipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut image_bind_groups: ResMut<ImageBindGroups>, mut image_bind_groups: ResMut<ImageBindGroups>,
gpu_images: Res<RenderAssets<Image>>, gpu_images: Res<RenderAssets<Image>>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
@ -528,12 +528,12 @@ pub fn queue_sprites(
} }
} }
let pipeline = pipelines.specialize( let pipeline = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&sprite_pipeline, &sprite_pipeline,
view_key | SpritePipelineKey::from_colored(false), view_key | SpritePipelineKey::from_colored(false),
); );
let colored_pipeline = pipelines.specialize( let colored_pipeline = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&sprite_pipeline, &sprite_pipeline,
view_key | SpritePipelineKey::from_colored(true), view_key | SpritePipelineKey::from_colored(true),
); );

View File

@ -557,7 +557,7 @@ pub fn queue_uinodes(
view_uniforms: Res<ViewUniforms>, view_uniforms: Res<ViewUniforms>,
ui_pipeline: Res<UiPipeline>, ui_pipeline: Res<UiPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<UiPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<UiPipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
mut image_bind_groups: ResMut<UiImageBindGroups>, mut image_bind_groups: ResMut<UiImageBindGroups>,
gpu_images: Res<RenderAssets<Image>>, gpu_images: Res<RenderAssets<Image>>,
ui_batches: Query<(Entity, &UiBatch)>, ui_batches: Query<(Entity, &UiBatch)>,
@ -586,7 +586,7 @@ pub fn queue_uinodes(
let draw_ui_function = draw_functions.read().id::<DrawUi>(); let draw_ui_function = draw_functions.read().id::<DrawUi>();
for (view, mut transparent_phase) in &mut views { for (view, mut transparent_phase) in &mut views {
let pipeline = pipelines.specialize( let pipeline = pipelines.specialize(
&mut pipeline_cache, &pipeline_cache,
&ui_pipeline, &ui_pipeline,
UiPipelineKey { hdr: view.hdr }, UiPipelineKey { hdr: view.hdr },
); );

View File

@ -312,7 +312,7 @@ pub fn queue_colored_mesh2d(
transparent_draw_functions: Res<DrawFunctions<Transparent2d>>, transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>, colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>, mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>, render_meshes: Res<RenderAssets<Mesh>>,
colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh2d>>, colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh2d>>,
@ -343,7 +343,7 @@ pub fn queue_colored_mesh2d(
} }
let pipeline_id = let pipeline_id =
pipelines.specialize(&mut pipeline_cache, &colored_mesh2d_pipeline, mesh2d_key); pipelines.specialize(&pipeline_cache, &colored_mesh2d_pipeline, mesh2d_key);
let mesh_z = mesh2d_uniform.transform.w_axis.z; let mesh_z = mesh2d_uniform.transform.w_axis.z;
transparent_phase.add(Transparent2d { transparent_phase.add(Transparent2d {

View File

@ -137,7 +137,7 @@ impl FromWorld for GameOfLifePipeline {
let shader = world let shader = world
.resource::<AssetServer>() .resource::<AssetServer>()
.load("shaders/game_of_life.wgsl"); .load("shaders/game_of_life.wgsl");
let mut pipeline_cache = world.resource_mut::<PipelineCache>(); let pipeline_cache = world.resource::<PipelineCache>();
let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor { let init_pipeline = pipeline_cache.queue_compute_pipeline(ComputePipelineDescriptor {
label: None, label: None,
layout: Some(vec![texture_bind_group_layout.clone()]), layout: Some(vec![texture_bind_group_layout.clone()]),

View File

@ -104,7 +104,7 @@ fn queue_custom(
custom_pipeline: Res<CustomPipeline>, custom_pipeline: Res<CustomPipeline>,
msaa: Res<Msaa>, msaa: Res<Msaa>,
mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>, mut pipelines: ResMut<SpecializedMeshPipelines<CustomPipeline>>,
mut pipeline_cache: ResMut<PipelineCache>, pipeline_cache: Res<PipelineCache>,
meshes: Res<RenderAssets<Mesh>>, meshes: Res<RenderAssets<Mesh>>,
material_meshes: Query<(Entity, &MeshUniform, &Handle<Mesh>), With<InstanceMaterialData>>, material_meshes: Query<(Entity, &MeshUniform, &Handle<Mesh>), With<InstanceMaterialData>>,
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>, mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
@ -121,7 +121,7 @@ fn queue_custom(
let key = let key =
view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology); view_key | MeshPipelineKey::from_primitive_topology(mesh.primitive_topology);
let pipeline = pipelines let pipeline = pipelines
.specialize(&mut pipeline_cache, &custom_pipeline, key, &mesh.layout) .specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout)
.unwrap(); .unwrap();
transparent_phase.add(Transparent3d { transparent_phase.add(Transparent3d {
entity, entity,