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
102#[derive(Clone, Copy)]
103pub struct GpuSlot<'a> {
104    buffer: &'a GpuBuffer,
105    offset: BufferAddress,
106    size: BufferAddress,
107}
108
109impl<'a> GpuSlot<'a> {
110    pub fn whole(buffer: &'a GpuBuffer) -> Self {
111        Self {
112            buffer,
113            offset: 0,
114            size: buffer.size(),
115        }
116    }
117
118    pub fn range(buffer: &'a GpuBuffer, offset: BufferAddress, size: BufferAddress) -> Self {
119        assert!(size > 0, "binding range must be non-empty");
120        assert_eq!(offset % 4, 0, "binding offset must be word aligned");
121        assert_eq!(size % 4, 0, "binding size must be word aligned");
122        Self {
123            buffer,
124            offset,
125            size,
126        }
127    }
128
129    pub fn as_binding(&self) -> wgpu::BindingResource<'a> {
130        self.buffer.as_binding_at(self.offset, self.size)
131    }
132
133    pub fn identity(&self) -> (u64, BufferAddress, BufferAddress) {
134        (self.buffer.token(), self.offset, self.size)
135    }
136}