1use crate::Gpu;
6use fidget_core::render::{ImageSize, VoxelSize};
7use fidget_raster::RenderSize;
8use zerocopy::{FromBytes, IntoBytes};
9
10pub struct FlexBuffer<T: BufferTag> {
15 size: T::S,
17 data: wgpu::Buffer,
19 name: String,
21 _t: std::marker::PhantomData<T>,
23}
24
25pub trait BufferTag {
33 type T;
35 type S: BufferItemCount + Copy;
37 fn usage() -> u32;
41}
42
43pub struct MappedBufferTag<T: BufferTag> {
45 _t: std::marker::PhantomData<T>,
46}
47impl<T: BufferTag> BufferTag for MappedBufferTag<T> {
48 type T = T::T;
49 type S = T::S;
50 fn usage() -> u32 {
51 wgpu::BufferUsages::COPY_DST.bits()
52 | wgpu::BufferUsages::MAP_READ.bits()
53 }
54}
55
56#[macro_export]
58macro_rules! tag {
59 ($vis:vis $name:ident, $t:ty, $s:ty, $($flag:ident)|+ $(,$doc:expr)?) => {
60 $(#[doc = $doc])?
61 $vis struct $name;
62 impl $crate::buf::BufferTag for $name {
63 type T = $t;
64 type S = $s;
65 fn usage() -> u32 {
66 $( wgpu::BufferUsages::$flag.bits() )|+
67 }
68 }
69 }
70}
71
72pub trait BufferItemCount {
74 fn item_count(&self) -> usize;
76}
77
78impl BufferItemCount for usize {
79 fn item_count(&self) -> usize {
80 *self
81 }
82}
83
84impl BufferItemCount for ImageSize {
85 fn item_count(&self) -> usize {
86 usize::try_from(self.width())
87 .unwrap()
88 .checked_mul(usize::try_from(self.height()).unwrap())
89 .unwrap()
90 }
91}
92
93impl BufferItemCount for VoxelSize {
94 fn item_count(&self) -> usize {
95 ImageSize::from(*self).item_count()
96 }
97}
98
99pub(crate) struct FlexConfigSize<C, T> {
103 count: usize,
104 _c: std::marker::PhantomData<C>,
105 _t: std::marker::PhantomData<T>,
106}
107
108impl<C, T> From<usize> for FlexConfigSize<C, T> {
109 fn from(value: usize) -> Self {
110 Self {
111 count: value,
112 _c: std::marker::PhantomData,
113 _t: std::marker::PhantomData,
114 }
115 }
116}
117
118impl<C, T> Clone for FlexConfigSize<C, T> {
119 fn clone(&self) -> Self {
120 *self
121 }
122}
123impl<C, T> Copy for FlexConfigSize<C, T> {}
124
125impl<C, T> BufferItemCount for FlexConfigSize<C, T> {
126 fn item_count(&self) -> usize {
127 self.count * std::mem::size_of::<T>() + std::mem::size_of::<C>()
128 }
129}
130
131pub(crate) struct FlexConfigTag<C, T> {
132 _c: std::marker::PhantomData<C>,
133 _t: std::marker::PhantomData<T>,
134}
135
136impl<C, T> BufferTag for FlexConfigTag<C, T>
137where
138 T: Copy,
139{
140 type T = u8;
141 type S = FlexConfigSize<C, T>;
142 fn usage() -> u32 {
143 wgpu::BufferUsages::COPY_DST.bits() | wgpu::BufferUsages::STORAGE.bits()
144 }
145}
146
147pub(crate) type FlexConfigBuffer<C, T> = FlexBuffer<FlexConfigTag<C, T>>;
149
150impl<C, T> FlexConfigBuffer<C, T>
151where
152 C: zerocopy::IntoBytes + zerocopy::Immutable,
153 T: zerocopy::IntoBytes + zerocopy::Immutable + Copy,
154{
155 pub(crate) fn write(
156 &mut self,
157 gpu: &Gpu,
158 c: &C,
159 data: &[T],
160 ) -> Result<ConfigBufferWrite, BufferSizeError> {
161 let r = self.grow_to_fit(&gpu.device, data.len().into())?;
162 let config_len = std::mem::size_of::<C>();
163 let data_len = std::mem::size_of_val(data);
164 let mut writer = gpu
165 .queue
166 .write_buffer_with(
167 &self.data,
168 0,
169 ((config_len + data_len) as u64).try_into().unwrap(),
170 )
171 .unwrap();
172 writer.slice(..config_len).copy_from_slice(c.as_bytes());
173 writer.slice(config_len..).copy_from_slice(data.as_bytes());
174 Ok(match r {
175 std::cmp::Ordering::Equal => ConfigBufferWrite::BufferUnchanged,
176 std::cmp::Ordering::Less | std::cmp::Ordering::Greater => {
177 ConfigBufferWrite::BufferChanged
178 }
179 })
180 }
181}
182
183#[derive(Copy, Clone, Debug)]
184#[must_use]
185pub(crate) enum ConfigBufferWrite {
186 BufferChanged,
187 BufferUnchanged,
188}
189
190impl<T: BufferTag> FlexBuffer<T> {
191 pub(crate) fn new(
192 device: &wgpu::Device,
193 name: impl AsRef<str>,
194 size: T::S,
195 ) -> Result<Self, BufferSizeError> {
196 Self::check_size(size)?;
197 let size_bytes = Self::calculate_buffer_size(size);
198 let usage = wgpu::BufferUsages::from_bits(T::usage()).unwrap();
199 let data = device.create_buffer(&wgpu::BufferDescriptor {
200 label: Some(name.as_ref()),
201 size: size_bytes,
202 usage,
203 mapped_at_creation: false,
204 });
205 Ok(Self {
206 data,
207 size,
208 name: name.as_ref().to_owned(),
209 _t: std::marker::PhantomData,
210 })
211 }
212
213 pub fn calculate_buffer_size(item_count: T::S) -> u64 {
217 let out = u64::try_from(item_count.item_count())
218 .unwrap()
219 .checked_mul(u64::try_from(std::mem::size_of::<T::T>()).unwrap())
220 .unwrap();
221 out.next_multiple_of(4)
222 }
223
224 pub fn size_bytes(&self) -> u64 {
229 Self::calculate_buffer_size(self.size)
230 }
231
232 pub(crate) fn check_size(size: T::S) -> Result<(), BufferSizeError> {
233 let size = Self::calculate_buffer_size(size);
234 let usage = wgpu::BufferUsages::from_bits(T::usage()).unwrap();
235
236 let buf_ty = if usage.contains(wgpu::BufferUsages::STORAGE) {
237 BufferType::Storage
238 } else if usage.contains(wgpu::BufferUsages::UNIFORM) {
239 BufferType::Uniform
240 } else {
241 BufferType::Generic
242 };
243 buf_ty.check(size)
244 }
245
246 pub(crate) fn grow_to_fit(
256 &mut self,
257 device: &wgpu::Device,
258 size: T::S,
259 ) -> Result<std::cmp::Ordering, BufferSizeError> {
260 Self::check_size(size)?;
261 let r = self.size.item_count().cmp(&size.item_count());
262 let new_size = Self::calculate_buffer_size(size);
263 if new_size > self.capacity() {
264 let usage = self.data.usage();
265 self.data = device.create_buffer(&wgpu::BufferDescriptor {
266 label: Some(self.name.as_str()),
267 size: new_size,
268 usage,
269 mapped_at_creation: false,
270 });
271 }
272 self.size = size;
273 Ok(r)
274 }
275
276 pub fn bind_active(&self) -> wgpu::BindingResource<'_> {
278 self.data.slice(0..self.size_bytes()).try_into().unwrap()
279 }
280
281 pub(crate) fn capacity(&self) -> u64 {
283 self.data.size()
284 }
285
286 pub fn name(&self) -> &str {
288 &self.name
289 }
290
291 pub(crate) fn map_async(
293 &self,
294 callback: impl FnOnce(Result<(), wgpu::BufferAsyncError>)
295 + wgpu::WasmNotSend
296 + 'static,
297 ) -> wgpu::BufferSlice<'_> {
298 let slice = self.data.slice(0..self.size_bytes());
299 slice.map_async(wgpu::MapMode::Read, callback);
300 slice
301 }
302
303 pub(crate) fn clear(&self, encoder: &mut wgpu::CommandEncoder) {
305 encoder.clear_buffer(&self.data, 0, Some(self.size_bytes()));
306 }
307
308 pub fn data(&self) -> &wgpu::Buffer {
314 &self.data
315 }
316
317 pub fn size(&self) -> T::S {
319 self.size
320 }
321}
322
323pub type ReadBuffer<T> = FlexBuffer<MappedBufferTag<T>>;
328
329pub struct MappedBuffer<'a, T: BufferTag> {
333 buf: &'a ReadBuffer<T>,
334 slice: wgpu::BufferSlice<'a>,
335}
336
337impl<T: BufferTag> Drop for MappedBuffer<'_, T> {
338 fn drop(&mut self) {
339 self.buf.data().unmap();
340 }
341}
342
343impl<'a, T> MappedBuffer<'a, T>
344where
345 T: BufferTag,
346 T::T: zerocopy::FromBytes + zerocopy::Immutable + Copy,
347{
348 pub(crate) fn new(
350 buf: &'a ReadBuffer<T>,
351 slice: wgpu::BufferSlice<'a>,
352 ) -> Self {
353 Self { buf, slice }
354 }
355
356 pub fn to_vec(&self) -> Vec<T::T> {
358 let slice = self.slice.get_mapped_range().unwrap();
361 let n = self.buf.size().item_count() * std::mem::size_of::<T::T>();
362 <[T::T]>::ref_from_bytes(&slice[..n]).unwrap().to_owned()
363 }
364}
365
366pub struct MappedImage<'a, T: BufferTag>(MappedBuffer<'a, T>)
370where
371 T::S: RenderSize;
372
373impl<'a, T> From<MappedBuffer<'a, T>> for MappedImage<'a, T>
374where
375 T: BufferTag,
376 T::S: RenderSize,
377{
378 fn from(value: MappedBuffer<'a, T>) -> Self {
379 Self(value)
380 }
381}
382
383impl<'a, T> MappedImage<'a, T>
384where
385 T: BufferTag,
386 T::T: zerocopy::FromBytes + zerocopy::Immutable + Copy,
387 T::S: RenderSize,
388{
389 pub fn image(&self) -> fidget_raster::Image<T::T, T::S> {
391 fidget_raster::Image::build(self.0.to_vec(), self.0.buf.size()).unwrap()
392 }
393}
394
395#[derive(Debug, thiserror::Error)]
406pub enum BufferSizeError {
407 #[error(
409 "requested size {requested_size} exceeds maximum {} for \
410 {buffer_type} buffer",
411 buffer_type.max_size()
412 )]
413 TooLarge {
414 requested_size: u64,
416 buffer_type: BufferType,
418 },
419}
420
421#[derive(Copy, Clone, Debug)]
423pub enum BufferType {
424 Uniform,
426 Storage,
428 Generic,
430}
431
432impl std::fmt::Display for BufferType {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 let s = match self {
435 BufferType::Uniform => "uniform",
436 BufferType::Storage => "storage",
437 BufferType::Generic => "generic",
438 };
439 s.fmt(f)
440 }
441}
442
443impl BufferType {
444 pub const fn max_size(&self) -> u64 {
446 match self {
448 BufferType::Uniform => 64 * 1024,
450 BufferType::Storage => 128 * 1024 * 1024,
452 BufferType::Generic => 256 * 1024 * 1024,
454 }
455 }
456
457 fn check(&self, requested_size: u64) -> Result<(), BufferSizeError> {
458 if requested_size > self.max_size() {
459 Err(BufferSizeError::TooLarge {
460 requested_size,
461 buffer_type: *self,
462 })
463 } else {
464 Ok(())
465 }
466 }
467}
468
469pub(crate) fn buffer_uniform(binding: u32) -> wgpu::BindGroupLayoutEntry {
471 wgpu::BindGroupLayoutEntry {
472 binding,
473 visibility: wgpu::ShaderStages::COMPUTE,
474 ty: wgpu::BindingType::Buffer {
475 ty: wgpu::BufferBindingType::Uniform,
476 has_dynamic_offset: false,
477 min_binding_size: None,
478 },
479 count: None,
480 }
481}
482
483pub(crate) fn buffer_ro(binding: u32) -> wgpu::BindGroupLayoutEntry {
485 wgpu::BindGroupLayoutEntry {
486 binding,
487 visibility: wgpu::ShaderStages::COMPUTE,
488 ty: wgpu::BindingType::Buffer {
489 ty: wgpu::BufferBindingType::Storage { read_only: true },
490 has_dynamic_offset: false,
491 min_binding_size: None,
492 },
493 count: None,
494 }
495}
496
497pub(crate) fn buffer_ro_dyn(binding: u32) -> wgpu::BindGroupLayoutEntry {
499 wgpu::BindGroupLayoutEntry {
500 binding,
501 visibility: wgpu::ShaderStages::COMPUTE,
502 ty: wgpu::BindingType::Buffer {
503 ty: wgpu::BufferBindingType::Storage { read_only: true },
504 has_dynamic_offset: true,
505 min_binding_size: None,
506 },
507 count: None,
508 }
509}
510
511pub(crate) fn buffer_rw(binding: u32) -> wgpu::BindGroupLayoutEntry {
513 wgpu::BindGroupLayoutEntry {
514 binding,
515 visibility: wgpu::ShaderStages::COMPUTE,
516 ty: wgpu::BindingType::Buffer {
517 ty: wgpu::BufferBindingType::Storage { read_only: false },
518 has_dynamic_offset: false,
519 min_binding_size: None,
520 },
521 count: None,
522 }
523}