bevy_render/render_resource/
pipeline_specializer.rs1use crate::{
2 mesh::{MeshVertexBufferLayoutRef, MissingVertexAttributeError, VertexBufferLayout},
3 render_resource::{
4 CachedComputePipelineId, CachedRenderPipelineId, ComputePipelineDescriptor, PipelineCache,
5 RenderPipelineDescriptor,
6 },
7};
8use bevy_ecs::system::Resource;
9use bevy_utils::{
10 default,
11 hashbrown::hash_map::{RawEntryMut, VacantEntry},
12 tracing::error,
13 Entry, HashMap,
14};
15use core::{fmt::Debug, hash::Hash};
16use derive_more::derive::{Display, Error, From};
17
18pub trait SpecializedRenderPipeline {
19 type Key: Clone + Hash + PartialEq + Eq;
20 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor;
21}
22
23#[derive(Resource)]
24pub struct SpecializedRenderPipelines<S: SpecializedRenderPipeline> {
25 cache: HashMap<S::Key, CachedRenderPipelineId>,
26}
27
28impl<S: SpecializedRenderPipeline> Default for SpecializedRenderPipelines<S> {
29 fn default() -> Self {
30 Self { cache: default() }
31 }
32}
33
34impl<S: SpecializedRenderPipeline> SpecializedRenderPipelines<S> {
35 pub fn specialize(
36 &mut self,
37 cache: &PipelineCache,
38 specialize_pipeline: &S,
39 key: S::Key,
40 ) -> CachedRenderPipelineId {
41 *self.cache.entry(key.clone()).or_insert_with(|| {
42 let descriptor = specialize_pipeline.specialize(key);
43 cache.queue_render_pipeline(descriptor)
44 })
45 }
46}
47
48pub trait SpecializedComputePipeline {
49 type Key: Clone + Hash + PartialEq + Eq;
50 fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor;
51}
52
53#[derive(Resource)]
54pub struct SpecializedComputePipelines<S: SpecializedComputePipeline> {
55 cache: HashMap<S::Key, CachedComputePipelineId>,
56}
57
58impl<S: SpecializedComputePipeline> Default for SpecializedComputePipelines<S> {
59 fn default() -> Self {
60 Self { cache: default() }
61 }
62}
63
64impl<S: SpecializedComputePipeline> SpecializedComputePipelines<S> {
65 pub fn specialize(
66 &mut self,
67 cache: &PipelineCache,
68 specialize_pipeline: &S,
69 key: S::Key,
70 ) -> CachedComputePipelineId {
71 *self.cache.entry(key.clone()).or_insert_with(|| {
72 let descriptor = specialize_pipeline.specialize(key);
73 cache.queue_compute_pipeline(descriptor)
74 })
75 }
76}
77
78pub trait SpecializedMeshPipeline {
79 type Key: Clone + Hash + PartialEq + Eq;
80 fn specialize(
81 &self,
82 key: Self::Key,
83 layout: &MeshVertexBufferLayoutRef,
84 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>;
85}
86
87#[derive(Resource)]
88pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
89 mesh_layout_cache: HashMap<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>,
90 vertex_layout_cache: VertexLayoutCache<S>,
91}
92
93pub type VertexLayoutCache<S> = HashMap<
94 VertexBufferLayout,
95 HashMap<<S as SpecializedMeshPipeline>::Key, CachedRenderPipelineId>,
96>;
97
98impl<S: SpecializedMeshPipeline> Default for SpecializedMeshPipelines<S> {
99 fn default() -> Self {
100 Self {
101 mesh_layout_cache: Default::default(),
102 vertex_layout_cache: Default::default(),
103 }
104 }
105}
106
107impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
108 #[inline]
109 pub fn specialize(
110 &mut self,
111 cache: &PipelineCache,
112 specialize_pipeline: &S,
113 key: S::Key,
114 layout: &MeshVertexBufferLayoutRef,
115 ) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
116 return match self.mesh_layout_cache.entry((layout.clone(), key.clone())) {
117 Entry::Occupied(entry) => Ok(*entry.into_mut()),
118 Entry::Vacant(entry) => specialize_slow(
119 &mut self.vertex_layout_cache,
120 cache,
121 specialize_pipeline,
122 key,
123 layout,
124 entry,
125 ),
126 };
127
128 #[cold]
129 fn specialize_slow<S>(
130 vertex_layout_cache: &mut VertexLayoutCache<S>,
131 cache: &PipelineCache,
132 specialize_pipeline: &S,
133 key: S::Key,
134 layout: &MeshVertexBufferLayoutRef,
135 entry: VacantEntry<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>,
136 ) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError>
137 where
138 S: SpecializedMeshPipeline,
139 {
140 let descriptor = specialize_pipeline
141 .specialize(key.clone(), layout)
142 .map_err(|mut err| {
143 {
144 let SpecializedMeshPipelineError::MissingVertexAttribute(err) = &mut err;
145 err.pipeline_type = Some(core::any::type_name::<S>());
146 }
147 err
148 })?;
149 let layout_map = match vertex_layout_cache
152 .raw_entry_mut()
153 .from_key(&descriptor.vertex.buffers[0])
154 {
155 RawEntryMut::Occupied(entry) => entry.into_mut(),
156 RawEntryMut::Vacant(entry) => {
157 entry
158 .insert(descriptor.vertex.buffers[0].clone(), Default::default())
159 .1
160 }
161 };
162 Ok(*entry.insert(match layout_map.entry(key) {
163 Entry::Occupied(entry) => {
164 if cfg!(debug_assertions) {
165 let stored_descriptor = cache.get_render_pipeline_descriptor(*entry.get());
166 if stored_descriptor != &descriptor {
167 error!(
168 "The cached pipeline descriptor for {} is not \
169 equal to the generated descriptor for the given key. \
170 This means the SpecializePipeline implementation uses \
171 unused' MeshVertexBufferLayout information to specialize \
172 the pipeline. This is not allowed because it would invalidate \
173 the pipeline cache.",
174 core::any::type_name::<S>()
175 );
176 }
177 }
178 *entry.into_mut()
179 }
180 Entry::Vacant(entry) => *entry.insert(cache.queue_render_pipeline(descriptor)),
181 }))
182 }
183 }
184}
185
186#[derive(Error, Display, Debug, From)]
187pub enum SpecializedMeshPipelineError {
188 MissingVertexAttribute(MissingVertexAttributeError),
189}