Skip to main content

dynamis_gpu/
buffer.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2use wgpu::{Buffer, BufferAddress, BufferDescriptor, BufferUsages, Device, Queue};
3
4static NEXT_BUFFER_TOKEN: AtomicU64 = AtomicU64::new(1);
5
6pub struct GpuBuffer {
7    buffer: Buffer,
8    size: BufferAddress,
9    usage: BufferUsages,
10    token: u64,
11}
12
13impl GpuBuffer {
14    pub fn new(device: &Device, label: &str, size: BufferAddress, usage: BufferUsages) -> Self {
15        let buffer = device.create_buffer(&BufferDescriptor {
16            label: Some(label),
17            size,
18            usage,
19            mapped_at_creation: false,
20        });
21        Self {
22            buffer,
23            size,
24            usage,
25            token: NEXT_BUFFER_TOKEN.fetch_add(1, Ordering::Relaxed),
26        }
27    }
28
29    pub fn zeroed(device: &Device, label: &str, size: BufferAddress, usage: BufferUsages) -> Self {
30        let buffer = device.create_buffer(&BufferDescriptor {
31            label: Some(label),
32            size,
33            usage,
34            mapped_at_creation: true,
35        });
36        buffer
37            .slice(..)
38            .get_mapped_range_mut()
39            .expect("mapped at creation range unavailable")
40            .slice(..)
41            .fill(0);
42        buffer.unmap();
43        Self {
44            buffer,
45            size,
46            usage,
47            token: NEXT_BUFFER_TOKEN.fetch_add(1, Ordering::Relaxed),
48        }
49    }
50
51    pub fn token(&self) -> u64 {
52        self.token
53    }
54
55    pub fn write(&self, queue: &Queue, bytes: &[u8]) {
56        assert!(
57            self.usage.contains(BufferUsages::COPY_DST),
58            "buffer write requires COPY_DST usage"
59        );
60        assert!(bytes.len() as u64 <= self.size, "write exceeds buffer size");
61        queue.write_buffer(&self.buffer, 0, bytes);
62    }
63
64    pub fn write_at(&self, queue: &Queue, offset: u64, bytes: &[u8]) {
65        assert!(
66            self.usage.contains(BufferUsages::COPY_DST),
67            "buffer write requires COPY_DST usage"
68        );
69        assert!(
70            offset + bytes.len() as u64 <= self.size,
71            "write exceeds buffer size"
72        );
73        queue.write_buffer(&self.buffer, offset, bytes);
74    }
75
76    pub fn as_binding(&self) -> wgpu::BindingResource<'_> {
77        wgpu::BindingResource::Buffer(wgpu::BufferBinding {
78            buffer: &self.buffer,
79            offset: 0,
80            size: None,
81        })
82    }
83
84    pub fn as_binding_at(&self, offset: u64, size: u64) -> wgpu::BindingResource<'_> {
85        let length = core::num::NonZeroU64::new(size).expect("binding size must be non-zero");
86        wgpu::BindingResource::Buffer(wgpu::BufferBinding {
87            buffer: &self.buffer,
88            offset,
89            size: Some(length),
90        })
91    }
92
93    pub fn buffer(&self) -> &Buffer {
94        &self.buffer
95    }
96
97    pub fn size(&self) -> BufferAddress {
98        self.size
99    }
100}
101
102impl<'a> From<&'a GpuBuffer> for GpuSlot<'a> {
103    fn from(buffer: &'a GpuBuffer) -> Self {
104        Self::whole(buffer)
105    }
106}
107
108#[derive(Clone, Copy)]
109pub struct GpuSlot<'a> {
110    buffer: &'a GpuBuffer,
111    offset: BufferAddress,
112    size: BufferAddress,
113}
114
115impl<'a> GpuSlot<'a> {
116    pub fn whole(buffer: &'a GpuBuffer) -> Self {
117        Self {
118            buffer,
119            offset: 0,
120            size: buffer.size(),
121        }
122    }
123
124    pub fn range(buffer: &'a GpuBuffer, offset: BufferAddress, size: BufferAddress) -> Self {
125        assert!(size > 0, "binding range must be non-empty");
126        assert_eq!(offset % 4, 0, "binding offset must be word aligned");
127        assert_eq!(size % 4, 0, "binding size must be word aligned");
128        Self {
129            buffer,
130            offset,
131            size,
132        }
133    }
134
135    pub fn as_binding(&self) -> wgpu::BindingResource<'a> {
136        self.buffer.as_binding_at(self.offset, self.size)
137    }
138
139    pub fn identity(&self) -> (u64, BufferAddress, BufferAddress) {
140        (self.buffer.token(), self.offset, self.size)
141    }
142}