Skip to main content

bevy_render/render_resource/
gpu_array_buffer.rs

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