-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds
ShaderStorageBuffer
asset (#14663)
Adds a new `Handle<Storage>` asset type that can be used as a render asset, particularly for use with `AsBindGroup`. Closes: #13658 # Objective Allow users to create storage buffers in the main world without having to access the `RenderDevice`. While this resource is technically available, it's bad form to use in the main world and requires mixing rendering details with main world code. Additionally, this makes storage buffers easier to use with `AsBindGroup`, particularly in the following scenarios: - Sharing the same buffers between a compute stage and material shader. We already have examples of this for storage textures (see game of life example) and these changes allow a similar pattern to be used with storage buffers. - Preventing repeated gpu upload (see the previous easier to use `Vec` `AsBindGroup` option). - Allow initializing custom materials using `Default`. Previously, the lack of a `Default` implement for the raw `wgpu::Buffer` type made implementing a `AsBindGroup + Default` bound difficult in the presence of buffers. ## Solution Adds a new `Handle<Storage>` asset type that is prepared into a `GpuStorageBuffer` render asset. This asset can either be initialized with a `Vec<u8>` of properly aligned data or with a size hint. Users can modify the underlying `wgpu::BufferDescriptor` to provide additional usage flags. ## Migration Guide The `AsBindGroup` `storage` attribute has been modified to reference the new `Handle<Storage>` asset instead. Usages of Vec` should be converted into assets instead. --------- Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
- Loading branch information
1 parent
3a8d559
commit a464004
Showing
8 changed files
with
297 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#import bevy_pbr::{ | ||
mesh_functions, | ||
view_transformations::position_world_to_clip | ||
} | ||
|
||
@group(2) @binding(0) var<storage, read> colors: array<vec4<f32>, 5>; | ||
|
||
struct Vertex { | ||
@builtin(instance_index) instance_index: u32, | ||
@location(0) position: vec3<f32>, | ||
}; | ||
|
||
struct VertexOutput { | ||
@builtin(position) clip_position: vec4<f32>, | ||
@location(0) world_position: vec4<f32>, | ||
@location(1) color: vec4<f32>, | ||
}; | ||
|
||
@vertex | ||
fn vertex(vertex: Vertex) -> VertexOutput { | ||
var out: VertexOutput; | ||
var world_from_local = mesh_functions::get_world_from_local(vertex.instance_index); | ||
out.world_position = mesh_functions::mesh_position_local_to_world(world_from_local, vec4(vertex.position, 1.0)); | ||
out.clip_position = position_world_to_clip(out.world_position.xyz); | ||
|
||
// We have 5 colors in the storage buffer, but potentially many instances of the mesh, so | ||
// we use the instance index to select a color from the storage buffer. | ||
out.color = colors[vertex.instance_index % 5]; | ||
|
||
return out; | ||
} | ||
|
||
@fragment | ||
fn fragment( | ||
mesh: VertexOutput, | ||
) -> @location(0) vec4<f32> { | ||
return mesh.color; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use crate::render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssetUsages}; | ||
use crate::render_resource::{Buffer, BufferUsages}; | ||
use crate::renderer::RenderDevice; | ||
use bevy_app::{App, Plugin}; | ||
use bevy_asset::{Asset, AssetApp}; | ||
use bevy_ecs::system::lifetimeless::SRes; | ||
use bevy_ecs::system::SystemParamItem; | ||
use bevy_reflect::prelude::ReflectDefault; | ||
use bevy_reflect::Reflect; | ||
use bevy_utils::default; | ||
use wgpu::util::BufferInitDescriptor; | ||
|
||
/// Adds [`ShaderStorageBuffer`] as an asset that is extracted and uploaded to the GPU. | ||
#[derive(Default)] | ||
pub struct StoragePlugin; | ||
|
||
impl Plugin for StoragePlugin { | ||
fn build(&self, app: &mut App) { | ||
app.add_plugins(RenderAssetPlugin::<GpuShaderStorageBuffer>::default()) | ||
.register_type::<ShaderStorageBuffer>() | ||
.init_asset::<ShaderStorageBuffer>() | ||
.register_asset_reflect::<ShaderStorageBuffer>(); | ||
} | ||
} | ||
|
||
/// A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU. | ||
#[derive(Asset, Reflect, Debug, Clone)] | ||
#[reflect_value(Default)] | ||
pub struct ShaderStorageBuffer { | ||
/// Optional data used to initialize the buffer. | ||
pub data: Option<Vec<u8>>, | ||
/// The buffer description used to create the buffer. | ||
pub buffer_description: wgpu::BufferDescriptor<'static>, | ||
/// The asset usage of the storage buffer. | ||
pub asset_usage: RenderAssetUsages, | ||
} | ||
|
||
impl Default for ShaderStorageBuffer { | ||
fn default() -> Self { | ||
Self { | ||
data: None, | ||
buffer_description: wgpu::BufferDescriptor { | ||
label: None, | ||
size: 0, | ||
usage: BufferUsages::STORAGE, | ||
mapped_at_creation: false, | ||
}, | ||
asset_usage: RenderAssetUsages::default(), | ||
} | ||
} | ||
} | ||
|
||
impl ShaderStorageBuffer { | ||
/// Creates a new storage buffer with the given data and asset usage. | ||
pub fn new(data: &[u8], asset_usage: RenderAssetUsages) -> Self { | ||
let mut storage = ShaderStorageBuffer { | ||
data: Some(data.to_vec()), | ||
..default() | ||
}; | ||
storage.asset_usage = asset_usage; | ||
storage | ||
} | ||
|
||
/// Creates a new storage buffer with the given size and asset usage. | ||
pub fn with_size(size: usize, asset_usage: RenderAssetUsages) -> Self { | ||
let mut storage = ShaderStorageBuffer { | ||
data: None, | ||
..default() | ||
}; | ||
storage.buffer_description.size = size as u64; | ||
storage.buffer_description.mapped_at_creation = false; | ||
storage.asset_usage = asset_usage; | ||
storage | ||
} | ||
} | ||
|
||
/// A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU. | ||
pub struct GpuShaderStorageBuffer { | ||
pub buffer: Buffer, | ||
} | ||
|
||
impl RenderAsset for GpuShaderStorageBuffer { | ||
type SourceAsset = ShaderStorageBuffer; | ||
type Param = SRes<RenderDevice>; | ||
|
||
fn asset_usage(source_asset: &Self::SourceAsset) -> RenderAssetUsages { | ||
source_asset.asset_usage | ||
} | ||
|
||
fn prepare_asset( | ||
source_asset: Self::SourceAsset, | ||
render_device: &mut SystemParamItem<Self::Param>, | ||
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> { | ||
match source_asset.data { | ||
Some(data) => { | ||
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor { | ||
label: source_asset.buffer_description.label, | ||
contents: &data, | ||
usage: source_asset.buffer_description.usage, | ||
}); | ||
Ok(GpuShaderStorageBuffer { buffer }) | ||
} | ||
None => { | ||
let buffer = render_device.create_buffer(&source_asset.buffer_description); | ||
Ok(GpuShaderStorageBuffer { buffer }) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.