bevy_shader_plugin/
gpu_buffer.rs1use std::{
2 any::{TypeId, type_name},
3 collections::HashMap,
4 marker::PhantomData,
5};
6
7use bevy::{
8 asset::RenderAssetUsages,
9 prelude::*,
10 render::{
11 Render, RenderApp,
12 render_asset::RenderAssets,
13 render_resource::BufferUsages,
14 storage::{GpuShaderBuffer, ShaderBuffer},
15 },
16};
17use encase::{ShaderSize, internal::WriteInto};
18
19use crate::{
20 ShaderSystems,
21 auto_array::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 register_binding(
99 render_app,
100 group_index,
101 binding_index,
102 AutoBufferKind::Storage { read_only: false },
103 );
104
105 render_app.add_systems(
106 Render,
107 (move |buffers: Res<RenderAssets<GpuShaderBuffer>>, mut table: ResMut<BindingTable>| {
108 if let Some(gpu_buffer) = buffers.get(asset_id) {
109 table.set_buffer(group_index, binding_index, &gpu_buffer.buffer);
110 }
111 })
112 .in_set(ShaderSystems::PrepareBindings),
113 );
114}