docs/bevy/examples/shader/pipeline_constants.rs 3 K · 99 lines · raw · history

1 //! Demonstrates pipeline-overridable constants using posterization.
2 //!
3 //! The same shader is compiled into three distinct pipeline variants by setting the `LEVELS`
4 //! `override` constant to different values at pipeline creation time. Each quad on screen uses a
5 //! different variant, producing 2, 4, and 8 discrete color steps from the same smooth gradient.
6 //!
7 //! This is similar to `shader_defs` but operates at the GPU compiler level: constants are
8 //! substituted into the shader source before compilation, allowing the driver to optimize each
9 //! variant independently. Unlike uniforms, pipeline constants cannot change at draw time.
10
11 use bevy::{
12 mesh::MeshVertexBufferLayoutRef,
13 prelude::*,
14 reflect::TypePath,
15 render::render_resource::{
16 AsBindGroup, RenderPipelineDescriptor, SpecializedMeshPipelineError,
17 },
18 shader::ShaderRef,
19 sprite_render::{Material2d, Material2dKey, Material2dPipeline, Material2dPlugin},
20 };
21
22 const SHADER_ASSET_PATH: &str = "shaders/pipeline_constants.wesl";
23
24 fn main() {
25 App::new()
26 .add_plugins((
27 DefaultPlugins,
28 Material2dPlugin::<PosterizeMaterial>::default(),
29 ))
30 .add_systems(Startup, setup)
31 .run();
32 }
33
34 fn setup(
35 mut commands: Commands,
36 mut meshes: ResMut<Assets<Mesh>>,
37 mut materials: ResMut<Assets<PosterizeMaterial>>,
38 ) {
39 commands.spawn(Camera2d);
40
41 let quad = meshes.add(Rectangle::new(200.0, 150.0));
42
43 for (i, levels) in [2u32, 4, 8].into_iter().enumerate() {
44 let x = (i as f32 - 1.0) * 220.0;
45
46 commands.spawn((
47 Mesh2d(quad.clone()),
48 MeshMaterial2d(materials.add(PosterizeMaterial { levels })),
49 Transform::from_xyz(x, 20.0, 0.0),
50 ));
51
52 commands.spawn((
53 Text2d::new(format!("{levels} levels")),
54 Transform::from_xyz(x, -65.0, 0.0),
55 ));
56 }
57 }
58
59 /// A material that posterizes a color gradient using a pipeline-overridable constant.
60 ///
61 /// Each distinct `levels` value produces a separate compiled pipeline variant.
62 #[derive(Asset, TypePath, AsBindGroup, Clone)]
63 #[bind_group_data(PosterizeMaterialKey)]
64 struct PosterizeMaterial {
65 /// Number of discrete color steps. Compiled into the shader as a pipeline constant.
66 levels: u32,
67 }
68
69 #[derive(Clone, PartialEq, Eq, Hash)]
70 struct PosterizeMaterialKey {
71 levels: u32,
72 }
73
74 impl From<&PosterizeMaterial> for PosterizeMaterialKey {
75 fn from(m: &PosterizeMaterial) -> Self {
76 Self { levels: m.levels }
77 }
78 }
79
80 impl Material2d for PosterizeMaterial {
81 fn fragment_shader() -> ShaderRef {
82 SHADER_ASSET_PATH.into()
83 }
84
85 fn specialize(
86 _pipeline: &Material2dPipeline,
87 descriptor: &mut RenderPipelineDescriptor,
88 _layout: &MeshVertexBufferLayoutRef,
89 key: Material2dKey<Self>,
90 ) -> Result<(), SpecializedMeshPipelineError> {
91 descriptor
92 .fragment
93 .as_mut()
94 .unwrap()
95 .constants
96 .push(("LEVELS".into(), key.bind_group_data.levels as f64));
97 Ok(())
98 }
99 }