gpu_component_array_buffer/
gpu_component_array_buffer.rs1use std::time::Duration;
11
12use bevy::{
13 ecs::{query::QueryItem, system::lifetimeless::Read},
14 prelude::*,
15 reflect::TypePath,
16 render::{
17 gpu_component_array_buffer::{
18 GpuComponentArray, GpuComponentArrayBuffer, GpuComponentArrayBufferPlugin,
19 },
20 render_resource::AsBindGroup,
21 storage::ShaderBuffer,
22 },
23 shader::ShaderRef,
24 time::common_conditions::on_timer,
25};
26use bytemuck::{Pod, Zeroable};
27use chacha20::ChaCha8Rng;
28use rand::{seq::IndexedRandom, RngExt as _, SeedableRng as _};
29
30const SHADER_ASSET_PATH: &str = "shaders/gpu_component_array_buffer.wesl";
32
33#[derive(Resource)]
35struct AppData {
36 mesh: Handle<Mesh>,
38 material_light: Handle<CustomMaterial>,
40 material_dark: Handle<CustomMaterial>,
42 rng: ChaCha8Rng,
47}
48
49#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
53#[bindless(index_table(range(0..4)))]
54struct CustomMaterial {
55 #[storage(1, read_only, binding_array(4))]
61 data: Handle<ShaderBuffer>,
62
63 #[texture(2)]
65 #[sampler(3)]
66 color_texture: Handle<Image>,
67}
68
69#[derive(Clone, Copy, Component, Debug)]
72struct CustomMaterialData {
73 color: Vec3,
75}
76
77#[derive(Clone, Copy, Pod, Zeroable)]
82#[repr(C)]
83struct GpuCustomMaterialData {
84 color: Vec3,
86 pad: u32,
88}
89
90fn main() {
91 App::new()
92 .add_plugins((
93 DefaultPlugins,
94 MaterialPlugin::<CustomMaterial>::default(),
95 GpuComponentArrayBufferPlugin::<CustomMaterialData>::default(),
98 ))
99 .add_systems(Startup, setup)
100 .add_systems(
101 Update,
102 (
103 add_cube.run_if(on_timer(Duration::from_millis(300))),
105 remove_cube.run_if(on_timer(Duration::from_millis(1000))),
107 ),
108 )
109 .run();
110}
111
112fn setup(
114 mut commands: Commands,
115 mut meshes: ResMut<Assets<Mesh>>,
116 mut bindless_materials: ResMut<Assets<CustomMaterial>>,
117 component_array: Res<GpuComponentArray<CustomMaterialData>>,
118 asset_server: Res<AssetServer>,
119) {
120 let mesh = meshes.add(Cuboid::default());
122
123 let (texture_dark, texture_light) = (
125 asset_server.load("branding/bevy_bird_dark.png"),
126 asset_server.load("branding/icon.png"),
127 );
128
129 let buffer = component_array.buffer.clone();
132 let material_light = bindless_materials.add(CustomMaterial {
133 data: buffer.clone(),
134 color_texture: texture_light,
135 });
136 let material_dark = bindless_materials.add(CustomMaterial {
137 data: buffer.clone(),
138 color_texture: texture_dark,
139 });
140
141 commands.insert_resource(AppData {
143 mesh,
144 material_light,
145 material_dark,
146 rng: ChaCha8Rng::seed_from_u64(12345),
147 });
148
149 commands.spawn((
151 Camera3d::default(),
152 Transform::from_xyz(-2.0, 1.25, 2.5).looking_at(Vec3::ZERO, Vec3::Y),
153 ));
154}
155
156fn add_cube(mut commands: Commands, mut app_data: ResMut<AppData>) {
158 let xz_offset = vec2(
160 app_data.rng.random_range((-1.0)..1.0),
161 app_data.rng.random_range((-1.0)..1.0),
162 );
163 let color = vec3(
165 app_data.rng.random_range((0.0)..1.0),
166 app_data.rng.random_range((0.0)..1.0),
167 app_data.rng.random_range((0.0)..1.0),
168 );
169 let material = if app_data.rng.random_bool(0.5) {
171 app_data.material_light.clone()
172 } else {
173 app_data.material_dark.clone()
174 };
175
176 commands.spawn((
178 Mesh3d(app_data.mesh.clone()),
179 MeshMaterial3d(material),
180 Transform::from_xyz(xz_offset.x, 0.5, xz_offset.y).with_scale(Vec3::splat(0.1)),
181 CustomMaterialData { color },
182 ));
183}
184
185fn remove_cube(
187 mut commands: Commands,
188 mut app_data: ResMut<AppData>,
189 cubes: Query<Entity, With<CustomMaterialData>>,
190) {
191 let all_cubes: Vec<Entity> = cubes.iter().collect();
193 if let Some(&cube_to_despawn) = all_cubes.choose(&mut app_data.rng) {
195 commands.entity(cube_to_despawn).despawn();
196 }
197}
198
199impl GpuComponentArrayBuffer for CustomMaterialData {
200 type QueryData = Read<CustomMaterialData>;
202 type QueryFilter = Changed<CustomMaterialData>;
206 type Out = GpuCustomMaterialData;
208
209 fn extract_component(data: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
212 Some(GpuCustomMaterialData {
213 color: data.color,
214 pad: 0,
215 })
216 }
217}
218
219impl Material for CustomMaterial {
220 fn fragment_shader() -> ShaderRef {
221 SHADER_ASSET_PATH.into()
222 }
223}