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 =
pipeline_cache.queue_render_pipeline(RenderPipelineDescriptor {

View File

@ -223,7 +223,7 @@ impl SpecializedRenderPipeline for FxaaPipeline {
pub fn prepare_fxaa_pipelines(
mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<FxaaPipeline>>,
fxaa_pipeline: Res<FxaaPipeline>,
views: Query<(Entity, &ExtractedView, &Fxaa)>,
@ -233,7 +233,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,

View File

@ -126,7 +126,7 @@ pub struct ViewTonemappingPipeline(CachedRenderPipelineId);
pub fn queue_view_tonemapping_pipelines(
mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<TonemappingPipeline>>,
upscaling_pipeline: Res<TonemappingPipeline>,
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)

View File

@ -112,7 +112,7 @@ pub struct ViewUpscalingPipeline(CachedRenderPipelineId);
fn queue_view_upscaling_pipelines(
mut commands: Commands,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<UpscalingPipeline>>,
upscaling_pipeline: Res<UpscalingPipeline>,
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -451,7 +451,7 @@ pub fn queue_sprites(
view_uniforms: Res<ViewUniforms>,
sprite_pipeline: Res<SpritePipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<SpritePipeline>>,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
mut image_bind_groups: ResMut<ImageBindGroups>,
gpu_images: Res<RenderAssets<Image>>,
msaa: Res<Msaa>,
@ -528,12 +528,12 @@ 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 File

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

View File

@ -312,7 +312,7 @@ pub fn queue_colored_mesh2d(
transparent_draw_functions: Res<DrawFunctions<Transparent2d>>,
colored_mesh2d_pipeline: Res<ColoredMesh2dPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<ColoredMesh2dPipeline>>,
mut pipeline_cache: ResMut<PipelineCache>,
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<Mesh>>,
colored_mesh2d: Query<(&Mesh2dHandle, &Mesh2dUniform), With<ColoredMesh2d>>,
@ -343,7 +343,7 @@ pub fn queue_colored_mesh2d(
}
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;
transparent_phase.add(Transparent2d {

View File

@ -137,7 +137,7 @@ impl FromWorld for GameOfLifePipeline {
let shader = world
.resource::<AssetServer>()
.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 {
label: None,
layout: Some(vec![texture_bind_group_layout.clone()]),

View File

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