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