
# Cold Specialization ## Objective An ongoing part of our quest to retain everything in the render world, cold-specialization aims to cache pipeline specialization so that pipeline IDs can be recomputed only when necessary, rather than every frame. This approach reduces redundant work in stable scenes, while still accommodating scenarios in which materials, views, or visibility might change, as well as unlocking future optimization work like retaining render bins. ## Solution Queue systems are split into a specialization system and queue system, the former of which only runs when necessary to compute a new pipeline id. Pipelines are invalidated using a combination of change detection and ECS ticks. ### The difficulty with change detection Detecting “what changed” can be tricky because pipeline specialization depends not only on the entity’s components (e.g., mesh, material, etc.) but also on which view (camera) it is rendering in. In other words, the cache key for a given pipeline id is a view entity/render entity pair. As such, it's not sufficient simply to react to change detection in order to specialize -- an entity could currently be out of view or could be rendered in the future in camera that is currently disabled or hasn't spawned yet. ### Why ticks? Ticks allow us to ensure correctness by allowing us to compare the last time a view or entity was updated compared to the cached pipeline id. This ensures that even if an entity was out of view or has never been seen in a given camera before we can still correctly determine whether it needs to be re-specialized or not. ## Testing TODO: Tested a bunch of different examples, need to test more. ## Migration Guide TODO - `AssetEvents` has been moved into the `PostUpdate` schedule. --------- Co-authored-by: Patrick Walton <pcwalton@mimiga.net>
68 lines
1.9 KiB
Rust
68 lines
1.9 KiB
Rust
use crate::Material;
|
|
use bevy_asset::{AsAssetId, AssetId, Handle};
|
|
use bevy_derive::{Deref, DerefMut};
|
|
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|
use derive_more::derive::From;
|
|
|
|
/// A [material](Material) used for rendering a [`Mesh3d`].
|
|
///
|
|
/// See [`Material`] for general information about 3D materials and how to implement your own materials.
|
|
///
|
|
/// [`Mesh3d`]: bevy_render::mesh::Mesh3d
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```
|
|
/// # use bevy_pbr::{Material, MeshMaterial3d, StandardMaterial};
|
|
/// # use bevy_ecs::prelude::*;
|
|
/// # use bevy_render::mesh::{Mesh, Mesh3d};
|
|
/// # use bevy_color::palettes::basic::RED;
|
|
/// # use bevy_asset::Assets;
|
|
/// # use bevy_math::primitives::Capsule3d;
|
|
/// #
|
|
/// // Spawn an entity with a mesh using `StandardMaterial`.
|
|
/// fn setup(
|
|
/// mut commands: Commands,
|
|
/// mut meshes: ResMut<Assets<Mesh>>,
|
|
/// mut materials: ResMut<Assets<StandardMaterial>>,
|
|
/// ) {
|
|
/// commands.spawn((
|
|
/// Mesh3d(meshes.add(Capsule3d::default())),
|
|
/// MeshMaterial3d(materials.add(StandardMaterial {
|
|
/// base_color: RED.into(),
|
|
/// ..Default::default()
|
|
/// })),
|
|
/// ));
|
|
/// }
|
|
/// ```
|
|
#[derive(Component, Clone, Debug, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
|
#[reflect(Component, Default)]
|
|
pub struct MeshMaterial3d<M: Material>(pub Handle<M>);
|
|
|
|
impl<M: Material> Default for MeshMaterial3d<M> {
|
|
fn default() -> Self {
|
|
Self(Handle::default())
|
|
}
|
|
}
|
|
|
|
impl<M: Material> From<MeshMaterial3d<M>> for AssetId<M> {
|
|
fn from(material: MeshMaterial3d<M>) -> Self {
|
|
material.id()
|
|
}
|
|
}
|
|
|
|
impl<M: Material> From<&MeshMaterial3d<M>> for AssetId<M> {
|
|
fn from(material: &MeshMaterial3d<M>) -> Self {
|
|
material.id()
|
|
}
|
|
}
|
|
|
|
impl<M: Material> AsAssetId for MeshMaterial3d<M> {
|
|
type Asset = M;
|
|
|
|
fn as_asset_id(&self) -> AssetId<Self::Asset> {
|
|
self.id()
|
|
}
|
|
}
|