bevy/crates/bevy_pbr/src/mesh_material.rs
charlotte 2ea5e9b846
Cold Specialization (#17567)
# 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>
2025-02-05 18:31:20 +00:00

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()
}
}