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}