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, Limits};
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(limits: &Limits) -> Self {
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(limits: &Limits) -> BindGroupLayoutEntryBuilder {
79        if 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(limits: &Limits) -> Option<u32> {
99        if limits.max_storage_buffers_per_shader_stage == 0 {
100            Some(BatchedUniformBuffer::<T>::batch_size(limits) as u32)
101        } else {
102            None
103        }
104    }
105}
106
107/// An index into a [`GpuArrayBuffer`] for a given element.
108#[derive(Component, Clone)]
109pub struct GpuArrayBufferIndex<T: GpuArrayBufferable> {
110    /// The index to use in a shader into the array.
111    pub index: u32,
112    /// The dynamic offset to use when setting the bind group in a pass.
113    /// Only used on platforms that don't support storage buffers.
114    pub dynamic_offset: Option<NonMaxU32>,
115    pub element_type: PhantomData<T>,
116}