1use bevy_material::descriptor::{
2CachedComputePipelineId, CachedRenderPipelineId, ComputePipelineDescriptor,
3RenderPipelineDescriptor,
4};
56use crate::render_resource::PipelineCache;
7use bevy_ecs::resource::Resource;
8use bevy_log::error;
9use bevy_material::specialize::SpecializedMeshPipelineError;
10use bevy_mesh::{MeshVertexBufferLayoutRef, VertexBufferLayout};
11use bevy_platform::{
12 collections::{
13 hash_map::{Entry, RawEntryMut, VacantEntry},
14HashMap,
15 },
16hash::FixedHasher,
17};
18use bevy_utils::default;
19use core::hash::{BuildHasher, Hash, Hasher};
2021/// A trait that allows constructing different variants of a render pipeline from a key.
22///
23/// Note: This is intended for modifying your pipeline descriptor on the basis of a key. If your key
24/// contains no data then you don't need to specialize. For example, if you are using the
25/// [`AsBindGroup`](crate::render_resource::AsBindGroup) without the `#[bind_group_data]` attribute,
26/// you don't need to specialize. Instead, create the pipeline directly from [`PipelineCache`] and
27/// store its ID.
28///
29/// See [`SpecializedRenderPipelines`] for more info.
30pub trait SpecializedRenderPipeline {
31/// The key that defines each "variant" of the render pipeline.
32type Key: Clone + Hash + PartialEq + Eq;
3334/// Construct a new render pipeline based on the provided key.
35fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor;
36}
3738/// A convenience cache for creating different variants of a render pipeline based on some key.
39///
40/// Some render pipelines may need to be configured differently depending on the exact situation.
41/// This cache allows constructing different render pipelines for each situation based on a key,
42/// making it easy to A) construct the necessary pipelines, and B) reuse already constructed
43/// pipelines.
44///
45/// Note: This is intended for modifying your pipeline descriptor on the basis of a key. If your key
46/// contains no data then you don't need to specialize. For example, if you are using the
47/// [`AsBindGroup`](crate::render_resource::AsBindGroup) without the `#[bind_group_data]` attribute,
48/// you don't need to specialize. Instead, create the pipeline directly from [`PipelineCache`] and
49/// store its ID.
50#[derive(impl<S: SpecializedRenderPipeline> bevy_ecs::component::Component for
SpecializedRenderPipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<SpecializedRenderPipelines<S>>()
{
id
} else {
required_components.components_registrator().register_component::<SpecializedRenderPipelines<S>>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl<S: SpecializedRenderPipeline> bevy_ecs::resource::Resource for
SpecializedRenderPipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {}Resource)]
51pub struct SpecializedRenderPipelines<S: SpecializedRenderPipeline> {
52 cache: HashMap<S::Key, CachedRenderPipelineId>,
53}
5455impl<S: SpecializedRenderPipeline> Defaultfor SpecializedRenderPipelines<S> {
56fn default() -> Self {
57Self { cache: default() }
58 }
59}
6061impl<S: SpecializedRenderPipeline> SpecializedRenderPipelines<S> {
62/// Get or create a specialized instance of the pipeline corresponding to `key`.
63pub fn specialize(
64&mut self,
65 cache: &PipelineCache,
66 pipeline_specializer: &S,
67 key: S::Key,
68 ) -> CachedRenderPipelineId {
69*self.cache.entry(key.clone()).or_insert_with(|| {
70let descriptor = pipeline_specializer.specialize(key);
71cache.queue_render_pipeline(descriptor)
72 })
73 }
74}
7576/// A trait that allows constructing different variants of a compute pipeline from a key.
77///
78/// Note: This is intended for modifying your pipeline descriptor on the basis of a key. If your key
79/// contains no data then you don't need to specialize. For example, if you are using the
80/// [`AsBindGroup`](crate::render_resource::AsBindGroup) without the `#[bind_group_data]` attribute,
81/// you don't need to specialize. Instead, create the pipeline directly from [`PipelineCache`] and
82/// store its ID.
83///
84/// See [`SpecializedComputePipelines`] for more info.
85pub trait SpecializedComputePipeline {
86/// The key that defines each "variant" of the compute pipeline.
87type Key: Clone + Hash + PartialEq + Eq;
8889/// Construct a new compute pipeline based on the provided key.
90fn specialize(&self, key: Self::Key) -> ComputePipelineDescriptor;
91}
9293/// A convenience cache for creating different variants of a compute pipeline based on some key.
94///
95/// Some compute pipelines may need to be configured differently depending on the exact situation.
96/// This cache allows constructing different compute pipelines for each situation based on a key,
97/// making it easy to A) construct the necessary pipelines, and B) reuse already constructed
98/// pipelines.
99///
100/// Note: This is intended for modifying your pipeline descriptor on the basis of a key. If your key
101/// contains no data then you don't need to specialize. For example, if you are using the
102/// [`AsBindGroup`](crate::render_resource::AsBindGroup) without the `#[bind_group_data]` attribute,
103/// you don't need to specialize. Instead, create the pipeline directly from [`PipelineCache`] and
104/// store its ID.
105#[derive(impl<S: SpecializedComputePipeline> bevy_ecs::component::Component for
SpecializedComputePipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<SpecializedComputePipelines<S>>()
{
id
} else {
required_components.components_registrator().register_component::<SpecializedComputePipelines<S>>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl<S: SpecializedComputePipeline> bevy_ecs::resource::Resource for
SpecializedComputePipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {}Resource)]
106pub struct SpecializedComputePipelines<S: SpecializedComputePipeline> {
107 cache: HashMap<S::Key, CachedComputePipelineId>,
108}
109110impl<S: SpecializedComputePipeline> Defaultfor SpecializedComputePipelines<S> {
111fn default() -> Self {
112Self { cache: default() }
113 }
114}
115116impl<S: SpecializedComputePipeline> SpecializedComputePipelines<S> {
117/// Get or create a specialized instance of the pipeline corresponding to `key`.
118pub fn specialize(
119&mut self,
120 cache: &PipelineCache,
121 specialize_pipeline: &S,
122 key: S::Key,
123 ) -> CachedComputePipelineId {
124*self.cache.entry(key.clone()).or_insert_with(|| {
125let descriptor = specialize_pipeline.specialize(key);
126cache.queue_compute_pipeline(descriptor)
127 })
128 }
129}
130131/// A trait that allows constructing different variants of a render pipeline from a key and the
132/// particular mesh's vertex buffer layout.
133///
134/// See [`SpecializedMeshPipelines`] for more info.
135pub trait SpecializedMeshPipeline {
136/// The key that defines each "variant" of the render pipeline.
137type Key: Clone + Hash + PartialEq + Eq;
138139/// Construct a new render pipeline based on the provided key and vertex layout.
140 ///
141 /// The returned pipeline descriptor should have a single vertex buffer, which is derived from
142 /// `layout`.
143fn specialize(
144&self,
145 key: Self::Key,
146 layout: &MeshVertexBufferLayoutRef,
147 ) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError>;
148}
149150/// A cache of different variants of a render pipeline based on a key and the particular mesh's
151/// vertex buffer layout.
152#[derive(impl<S: SpecializedMeshPipeline> bevy_ecs::component::Component for
SpecializedMeshPipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<SpecializedMeshPipelines<S>>()
{
id
} else {
required_components.components_registrator().register_component::<SpecializedMeshPipelines<S>>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl<S: SpecializedMeshPipeline> bevy_ecs::resource::Resource for
SpecializedMeshPipelines<S> where Self: ::core::marker::Send +
::core::marker::Sync + 'static {}Resource)]
153pub struct SpecializedMeshPipelines<S: SpecializedMeshPipeline> {
154 mesh_layout_cache: HashMap<(MeshVertexBufferLayoutRef, S::Key), CachedRenderPipelineId>,
155 vertex_layout_cache: VertexLayoutCache<S>,
156}
157158type VertexLayoutCache<S> = HashMap<
159VertexBufferLayout,
160HashMap<<S as SpecializedMeshPipeline>::Key, CachedRenderPipelineId>,
161>;
162163impl<S: SpecializedMeshPipeline> Defaultfor SpecializedMeshPipelines<S> {
164fn default() -> Self {
165Self {
166 mesh_layout_cache: Default::default(),
167 vertex_layout_cache: Default::default(),
168 }
169 }
170}
171172impl<S: SpecializedMeshPipeline> SpecializedMeshPipelines<S> {
173#[inline]
174pub fn get_pipeline(
175&self,
176 key: &S::Key,
177 layout: &MeshVertexBufferLayoutRef,
178 ) -> Option<CachedRenderPipelineId> {
179let hash = {
180let mut hasher = self.mesh_layout_cache.hasher().build_hasher();
181layout.hash(&mut hasher);
182key.hash(&mut hasher);
183hasher.finish()
184 };
185186self.mesh_layout_cache
187 .raw_entry()
188 .from_hash(hash, |(l, k)| l == layout && k == key)
189 .map(|(_, id)| *id)
190 }
191192/// Construct a new render pipeline based on the provided key and the mesh's vertex buffer
193 /// layout.
194#[inline]
195pub fn specialize(
196&mut self,
197 cache: &PipelineCache,
198 pipeline_specializer: &S,
199 key: S::Key,
200 layout: &MeshVertexBufferLayoutRef,
201 ) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError> {
202return match self.mesh_layout_cache.entry((layout.clone(), key.clone())) {
203Entry::Occupied(entry) => Ok(*entry.into_mut()),
204Entry::Vacant(entry) => specialize_slow(
205&mut self.vertex_layout_cache,
206cache,
207pipeline_specializer,
208key,
209layout,
210entry,
211 ),
212 };
213214#[cold]
215fn specialize_slow<S>(
216 vertex_layout_cache: &mut VertexLayoutCache<S>,
217 cache: &PipelineCache,
218 specialize_pipeline: &S,
219 key: S::Key,
220 layout: &MeshVertexBufferLayoutRef,
221 entry: VacantEntry<
222 (MeshVertexBufferLayoutRef, S::Key),
223CachedRenderPipelineId,
224FixedHasher,
225 >,
226 ) -> Result<CachedRenderPipelineId, SpecializedMeshPipelineError>
227where
228S: SpecializedMeshPipeline,
229 {
230let descriptor = specialize_pipeline
231 .specialize(key.clone(), layout)
232 .map_err(|mut err| {
233 {
234let SpecializedMeshPipelineError::MissingVertexAttribute(err) = &mut err;
235 err.pipeline_type = Some(core::any::type_name::<S>());
236 }
237 err
238 })?;
239// Different MeshVertexBufferLayouts can produce the same final VertexBufferLayout
240 // We want compatible vertex buffer layouts to use the same pipelines, so we must "deduplicate" them
241let layout_map = match vertex_layout_cache242 .raw_entry_mut()
243 .from_key(&descriptor.vertex.buffers[0])
244 {
245 RawEntryMut::Occupied(entry) => entry.into_mut(),
246 RawEntryMut::Vacant(entry) => {
247entry248 .insert(descriptor.vertex.buffers[0].clone(), Default::default())
249 .1
250}
251 };
252Ok(*entry.insert(match layout_map.entry(key) {
253Entry::Occupied(entry) => {
254if truecfg!(debug_assertions) {
255let stored_descriptor = cache.get_render_pipeline_descriptor(*entry.get());
256if stored_descriptor != &descriptor {
257{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/render_resource/pipeline_specializer.rs:257",
"bevy_render::render_resource::pipeline_specializer",
::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/render_resource/pipeline_specializer.rs"),
::tracing_core::__macro_support::Option::Some(257u32),
::tracing_core::__macro_support::Option::Some("bevy_render::render_resource::pipeline_specializer"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("The cached pipeline descriptor for {0} 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.",
core::any::type_name::<S>()) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};error!(
258"The cached pipeline descriptor for {} is not \
259 equal to the generated descriptor for the given key. \
260 This means the SpecializePipeline implementation uses \
261 unused' MeshVertexBufferLayout information to specialize \
262 the pipeline. This is not allowed because it would invalidate \
263 the pipeline cache.",
264 core::any::type_name::<S>()
265 );
266 }
267 }
268*entry.into_mut()
269 }
270Entry::Vacant(entry) => *entry.insert(cache.queue_render_pipeline(descriptor)),
271 }))
272 }
273 }
274}