Skip to content

Commit 3328fe8

Browse files
committed
add storage buffers support
1 parent 1aeaafa commit 3328fe8

File tree

3 files changed

+93
-3
lines changed

3 files changed

+93
-3
lines changed

crates/bevy_render/macros/src/as_bind_group.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use syn::{
1111
const UNIFORM_ATTRIBUTE_NAME: Symbol = Symbol("uniform");
1212
const TEXTURE_ATTRIBUTE_NAME: Symbol = Symbol("texture");
1313
const SAMPLER_ATTRIBUTE_NAME: Symbol = Symbol("sampler");
14+
const STORAGE_ATTRIBUTE_NAME: Symbol = Symbol("storage");
1415
const BIND_GROUP_DATA_ATTRIBUTE_NAME: Symbol = Symbol("bind_group_data");
1516

1617
#[derive(Copy, Clone, Debug)]
1718
enum BindingType {
1819
Uniform,
1920
Texture,
2021
Sampler,
22+
Storage,
2123
}
2224

2325
#[derive(Clone)]
@@ -55,7 +57,6 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
5557
}
5658
} else if attr_ident == UNIFORM_ATTRIBUTE_NAME {
5759
let (binding_index, converted_shader_type) = get_uniform_binding_attr(attr)?;
58-
5960
binding_impls.push(quote! {{
6061
use #render_path::render_resource::AsBindGroupShaderType;
6162
let mut buffer = #render_path::render_resource::encase::UniformBuffer::new(Vec::new());
@@ -126,6 +127,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
126127
BindingType::Texture
127128
} else if attr_ident == SAMPLER_ATTRIBUTE_NAME {
128129
BindingType::Sampler
130+
} else if attr_ident == STORAGE_ATTRIBUTE_NAME {
131+
BindingType::Storage
129132
} else {
130133
continue;
131134
};
@@ -190,7 +193,45 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result<TokenStream> {
190193
}
191194

192195
match binding_type {
193-
BindingType::Uniform => { /* uniform codegen is deferred to account for combined uniform bindings */
196+
BindingType::Uniform => {
197+
// uniform codegen is deferred to account for combined uniform bindings
198+
}
199+
BindingType::Storage => {
200+
let StorageAttrs {
201+
visibility,
202+
read_only,
203+
} = get_storage_binding_attr(nested_meta_items)?;
204+
let visibility =
205+
visibility.hygenic_quote(&quote! { #render_path::render_resource });
206+
207+
let field_name = field.ident.as_ref().unwrap();
208+
let field_ty = &field.ty;
209+
210+
binding_impls.push(quote! {{
211+
use #render_path::render_resource::AsBindGroupShaderType;
212+
let mut buffer = #render_path::render_resource::encase::StorageBuffer::new(Vec::new());
213+
buffer.write(&self.#field_name).unwrap();
214+
#render_path::render_resource::OwnedBindingResource::Buffer(render_device.create_buffer_with_data(
215+
&#render_path::render_resource::BufferInitDescriptor {
216+
label: None,
217+
usage: #render_path::render_resource::BufferUsages::COPY_DST | #render_path::render_resource::BufferUsages::STORAGE,
218+
contents: buffer.as_ref(),
219+
},
220+
))
221+
}});
222+
223+
binding_layouts.push(quote! {
224+
#render_path::render_resource::BindGroupLayoutEntry {
225+
binding: #binding_index,
226+
visibility: #visibility,
227+
ty: #render_path::render_resource::BindingType::Buffer {
228+
ty: #render_path::render_resource::BufferBindingType::Storage { read_only: #read_only },
229+
has_dynamic_offset: false,
230+
min_binding_size: Some(<#field_ty as #render_path::render_resource::ShaderType>::min_size()),
231+
},
232+
count: None,
233+
}
234+
});
194235
}
195236
BindingType::Texture => {
196237
let TextureAttrs {
@@ -861,3 +902,40 @@ fn get_sampler_binding_type_value(lit_str: &LitStr) -> Result<SamplerBindingType
861902
)),
862903
}
863904
}
905+
906+
#[derive(Default)]
907+
struct StorageAttrs {
908+
visibility: ShaderStageVisibility,
909+
read_only: bool,
910+
}
911+
912+
const READ_ONLY: Symbol = Symbol("read_only");
913+
914+
fn get_storage_binding_attr(metas: Vec<NestedMeta>) -> Result<StorageAttrs> {
915+
let mut visibility = ShaderStageVisibility::vertex_fragment();
916+
let mut read_only = false;
917+
918+
for meta in metas {
919+
use syn::{Meta::List, Meta::Path, NestedMeta::Meta};
920+
match meta {
921+
// Parse #[storage(0, visibility(...))].
922+
Meta(List(m)) if m.path == VISIBILITY => {
923+
visibility = get_visibility_flag_value(&m.nested)?;
924+
}
925+
Meta(Path(path)) if path == READ_ONLY => {
926+
read_only = true;
927+
}
928+
_ => {
929+
return Err(Error::new_spanned(
930+
meta,
931+
"Not a valid attribute. Available attributes: `read_only`, `visibility`",
932+
));
933+
}
934+
}
935+
}
936+
937+
Ok(StorageAttrs {
938+
visibility,
939+
read_only,
940+
})
941+
}

crates/bevy_render/macros/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
1717
extract_resource::derive_extract_resource(input)
1818
}
1919

20-
#[proc_macro_derive(AsBindGroup, attributes(uniform, texture, sampler, bind_group_data))]
20+
#[proc_macro_derive(
21+
AsBindGroup,
22+
attributes(uniform, texture, sampler, bind_group_data, storage)
23+
)]
2124
pub fn derive_as_bind_group(input: TokenStream) -> TokenStream {
2225
let input = parse_macro_input!(input as DeriveInput);
2326

crates/bevy_render/src/render_resource/bind_group.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ impl Deref for BindGroup {
137137
/// | `sampler_type` = "..." | `"filtering"`, `"non_filtering"`, `"comparison"`. | `"filtering"` |
138138
/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |
139139
///
140+
/// * `storage(BINDING_INDEX, arguments)`
141+
/// * The field will be converted to a shader-compatible type using the [`ShaderType`] trait, written to a [`Buffer`], and bound as a storage buffer.
142+
/// * It supports and optional `read_only` parameter. Defaults to false if not present.
143+
///
144+
/// | Arguments | Values | Default |
145+
/// |------------------------|-------------------------------------------------------------------------|----------------------|
146+
/// | `visibility(...)` | `all`, `none`, or a list-combination of `vertex`, `fragment`, `compute` | `vertex`, `fragment` |
147+
/// | `read_only` | if present then value is true, otherwise false | `false` |
148+
///
140149
/// Note that fields without field-level binding attributes will be ignored.
141150
/// ```
142151
/// # use bevy_render::{color::Color, render_resource::AsBindGroup};

0 commit comments

Comments
 (0)