bevy_render/render_resource/
gpu_array_buffer.rs

1use super::{
2    binding_types::{storage_buffer_read_only, uniform_buffer_sized},
3    BindGroupLayoutEntryBuilder, BufferVec,
4};
5use crate::{
6    render_resource::batched_uniform_buffer::BatchedUniformBuffer,
7    renderer::{RenderDevice, RenderQueue},
8};
9use bevy_ecs::{prelude::Component, system::Resource};
10use core::marker::PhantomData;
11use encase::{private::WriteInto, ShaderSize, ShaderType};
12use nonmax::NonMaxU32;
13use wgpu::{BindingResource, BufferUsages};
14
15/// Trait for types able to go in a [`GpuArrayBuffer`].
16pub trait GpuArrayBufferable: ShaderType + ShaderSize + WriteInto + Clone {}
17impl<T: ShaderType + ShaderSize + WriteInto + Clone> GpuArrayBufferable for T {}
18
19/// Stores an array of elements to be transferred to the GPU and made accessible to shaders as a read-only array.
20///
21/// On platforms that support storage buffers, this is equivalent to
22/// [`BufferVec<T>`]. Otherwise, this falls back to a dynamic offset
23/// uniform buffer with the largest array of T that fits within a uniform buffer
24/// binding (within reasonable limits).
25///
26/// Other options for storing GPU-accessible data are:
27/// * [`StorageBuffer`](crate::render_resource::StorageBuffer)
28/// * [`DynamicStorageBuffer`](crate::render_resource::DynamicStorageBuffer)
29/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)
30/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)
31/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)
32/// * [`BufferVec`]
33/// * [`Texture`](crate::render_resource::Texture)
34#[derive(Resource)]
35pub enum GpuArrayBuffer<T: GpuArrayBufferable> {
36    Uniform(BatchedUniformBuffer<T>),
37    Storage(BufferVec<T>),
38}
39
40impl<T: GpuArrayBufferable> GpuArrayBuffer<T> {
41    pub fn new(device: &RenderDevice) -> Self {
42        let limits = device.limits();
43        if limits.max_storage_buffers_per_shader_stage == 0 {
44            GpuArrayBuffer::Uniform(BatchedUniformBuffer::new(&limits))
45        } else {
46            GpuArrayBuffer::Storage(BufferVec::new(BufferUsages::STORAGE))
47        }
48    }
49
50    pub fn clear(&mut self) {
51        match self {
52            GpuArrayBuffer::Uniform(buffer) => buffer.clear(),
53            GpuArrayBuffer::Storage(buffer) => buffer.clear(),
54        }
55    }
56
57    pub fn push(&mut self, value: T) -> GpuArrayBufferIndex<T> {
58        match self {
59            GpuArrayBuffer::Uniform(buffer) => buffer.push(value),
60            GpuArrayBuffer::Storage(buffer) => {
61                let index = buffer.push(value) as u32;
62                GpuArrayBufferIndex {
63                    index,
64                    dynamic_offset: None,
65                    element_type: PhantomData,
66                }
67            }
68        }
69    }
70
71    pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
72        match self {
73            GpuArrayBuffer::Uniform(buffer) => buffer.write_buffer(device, queue),
74            GpuArrayBuffer::Storage(buffer) => buffer.write_buffer(device, queue),
75        }
76    }
77
78    pub fn binding_layout(device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
79        if device.limits().max_storage_buffers_per_shader_stage == 0 {
80            uniform_buffer_sized(
81                true,
82                // BatchedUniformBuffer uses a MaxCapacityArray that is runtime-sized, so we use
83                // None here and let wgpu figure out the size.
84                None,
85            )
86        } else {
87            storage_buffer_read_only::<T>(false)
88        }
89    }
90
91    pub fn binding(&self) -> Option<BindingResource> {
92        match self {
93            GpuArrayBuffer::Uniform(buffer) => buffer.binding(),
94            GpuArrayBuffer::Storage(buffer) => buffer.binding(),
95        }
96    }
97
98    pub fn batch_size(device: &RenderDevice) -> Option<u32> {
99        let limits = device.limits();
100        if limits.max_storage_buffers_per_shader_stage == 0 {
101            Some(BatchedUniformBuffer::<T>::batch_size(&limits) as u32)
102        } else {
103            None
104        }
105    }
106}
107
108/// An index into a [`GpuArrayBuffer`] for a given element.
109#[derive(Component, Clone)]
110pub struct GpuArrayBufferIndex<T: GpuArrayBufferable> {
111    /// The index to use in a shader into the array.
112    pub index: u32,
113    /// The dynamic offset to use when setting the bind group in a pass.
114    /// Only used on platforms that don't support storage buffers.
115    pub dynamic_offset: Option<NonMaxU32>,
116    pub element_type: PhantomData<T>,
117}