bevy_shader_plugin/
gpu_buffer.rs1use std::{
2 any::{TypeId, type_name},
3 collections::HashMap,
4 marker::PhantomData,
5 num::NonZeroU64,
6};
7
8use bevy::{
9 asset::RenderAssetUsages,
10 prelude::*,
11 render::{
12 Render, RenderApp,
13 render_asset::RenderAssets,
14 render_resource::{BufferUsages, ShaderSize, encase::internal::WriteInto},
15 storage::{GpuShaderBuffer, ShaderBuffer},
16 },
17};
18
19use crate::{
20 ShaderSystems,
21 auto_array::{array_stride, default_filled_bytes},
22 bindings::{AutoBufferKind, BindingTable, register_binding},
23};
24
25#[derive(Resource)]
31pub struct GpuBuffer<Tag> {
32 handle: Handle<ShaderBuffer>,
33 len: u32,
34 _marker: PhantomData<Tag>,
35}
36
37impl<Tag> GpuBuffer<Tag> {
38 pub fn handle(&self) -> &Handle<ShaderBuffer> {
40 &self.handle
41 }
42
43 pub fn len(&self) -> u32 {
45 self.len
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.len == 0
51 }
52}
53
54#[derive(Resource, Default)]
57pub(crate) struct GpuBufferLengths(pub HashMap<TypeId, u32>);
58
59pub(crate) fn register<Tag, T>(app: &mut App, group_index: u32, binding_index: u32, len: u32)
62where
63 Tag: Send + Sync + 'static,
64 T: ShaderSize + WriteInto + Default + Send + Sync + 'static,
65{
66 let mut storage = ShaderBuffer::new(
67 &default_filled_bytes::<T>(len as usize),
68 RenderAssetUsages::default(),
69 );
70 storage.buffer_description.label = Some("gpu_buffer");
71 storage.buffer_description.usage |= BufferUsages::COPY_DST | BufferUsages::COPY_SRC;
73
74 let handle = app
75 .world_mut()
76 .resource_mut::<Assets<ShaderBuffer>>()
77 .add(storage);
78 let asset_id = handle.id();
79
80 let previous = app
81 .world_mut()
82 .get_resource_or_insert_with(GpuBufferLengths::default)
83 .0
84 .insert(TypeId::of::<Tag>(), len);
85 assert!(
86 previous.is_none(),
87 "register_gpu_buffer: tag `{}` is already registered; use a distinct tag per buffer",
88 type_name::<Tag>()
89 );
90
91 app.insert_resource(GpuBuffer::<Tag> {
92 handle,
93 len,
94 _marker: PhantomData,
95 });
96
97 let render_app = app.sub_app_mut(RenderApp);
98 let min_size = (len > 0)
101 .then(|| NonZeroU64::new(array_stride::<T>() as u64))
102 .flatten();
103 register_binding(
104 render_app,
105 group_index,
106 binding_index,
107 AutoBufferKind::Storage { read_only: false },
108 min_size,
109 );
110
111 render_app.add_systems(
112 Render,
113 (move |buffers: Res<RenderAssets<GpuShaderBuffer>>, mut table: ResMut<BindingTable>| {
114 if let Some(gpu_buffer) = buffers.get(asset_id) {
115 table.set_buffer(group_index, binding_index, &gpu_buffer.buffer);
116 }
117 })
118 .in_set(ShaderSystems::PrepareBindings),
119 );
120}