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}