1use crate::{
2 ecs::promise::Promise,
3 graphics::{
4 pipeline::{
5 binding::BindGroupLayout, cubemap::GPUCubemap, samplers::Sampler,
6 texture_array::GPUTextureArray, texture_view::TextureView, textures::GPUTexture,
7 },
8 render::{Backend, gpu_context::GpuContext},
9 types::flags::BufferUsages,
10 },
11};
12
13#[derive(Clone)]
18pub struct Buffer {
19 pub(crate) raw: wgpu::Buffer,
20 pub(crate) ctx: GpuContext,
21}
22
23impl Buffer {
24 pub(crate) fn new(raw: wgpu::Buffer, ctx: GpuContext) -> Self {
25 Self { raw, ctx }
26 }
27
28 pub fn write(&self, data: &[u8]) {
30 self.ctx.queue().write_buffer(&self.raw, 0, data);
31 }
32
33 pub fn write_at(&self, offset: u64, data: &[u8]) {
34 self.ctx.queue().write_buffer(&self.raw, offset, data);
35 }
36
37 pub fn read(&self) -> Promise<Vec<u8>> {
41 let usage = self.raw.usage();
42 if !usage.contains(wgpu::BufferUsages::COPY_SRC) {
43 panic!(
44 "Buffer::read: buffer is missing BufferUsages::COPY_SRC — it can't be copied out of"
45 );
46 }
47
48 let size = self.raw.size();
49 let staging = self.ctx.device().create_buffer(&wgpu::BufferDescriptor {
50 label: None,
51 size,
52 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
53 mapped_at_creation: false,
54 });
55
56 let mut encoder = self
57 .ctx
58 .device()
59 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
60 encoder.copy_buffer_to_buffer(&self.raw, 0, &staging, 0, size);
61 self.ctx.queue().submit(std::iter::once(encoder.finish()));
62
63 let (fulfiller, promise) = Promise::new();
64 let readback = staging.clone();
67 staging.map_async(wgpu::MapMode::Read, .., move |result| {
68 if result.is_ok() {
69 let data = readback
70 .get_mapped_range(..)
71 .expect("Failed to get mapped range")
72 .to_vec();
73 readback.unmap();
74 fulfiller.fulfill(data);
75 }
76 });
79 promise
80 }
81
82 pub fn size(&self) -> u64 {
83 self.raw.size()
84 }
85
86 pub(crate) fn raw(&self) -> &wgpu::Buffer {
87 &self.raw
88 }
89}
90
91#[derive(Clone)]
96pub struct DynamicBuffer {
97 pub(crate) buffer: Buffer,
98 pub(crate) stride: u64,
99 pub(crate) element_size: u64,
100}
101
102impl DynamicBuffer {
103 pub(crate) fn new(buffer: Buffer, stride: u64, element_size: u64) -> Self {
104 Self {
105 buffer,
106 stride,
107 element_size,
108 }
109 }
110
111 pub fn write_element(&self, index: u64, data: &[u8]) {
113 self.buffer.write_at(index * self.stride, data);
114 }
115
116 pub fn element_size(&self) -> u64 {
117 self.element_size
118 }
119
120 pub fn stride(&self) -> u64 {
121 self.stride
122 }
123}
124
125enum BufferContents<'a> {
126 Empty(u64),
127 Data(&'a [u8]),
128}
129
130impl<'a> BufferContents<'a> {
131 fn size(&self) -> u64 {
132 match self {
133 BufferContents::Empty(size) => *size,
134 BufferContents::Data(data) => data.len() as u64,
135 }
136 }
137}
138
139pub struct BufferBuilder<'a> {
142 label: Option<&'a str>,
143 usage: BufferUsages,
144 contents: BufferContents<'a>,
145}
146
147impl<'a> BufferBuilder<'a> {
148 pub fn empty(size: u64) -> Self {
149 Self {
150 label: None,
151 usage: BufferUsages::empty(),
152 contents: BufferContents::Empty(size),
153 }
154 }
155
156 pub fn with_data(data: &'a [u8]) -> Self {
157 Self {
158 label: None,
159 usage: BufferUsages::empty(),
160 contents: BufferContents::Data(data),
161 }
162 }
163
164 pub fn with_label(mut self, label: impl Into<Option<&'a str>>) -> Self {
165 self.label = label.into();
166 self
167 }
168
169 pub fn with_usage(mut self, usage: BufferUsages) -> Self {
170 self.usage = usage;
171 self
172 }
173
174 pub fn with_uniform(self) -> Self {
175 self.with_usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST)
176 }
177
178 pub fn with_storage(self) -> Self {
179 self.with_usage(BufferUsages::STORAGE | BufferUsages::COPY_DST)
180 }
181
182 pub fn build(self, backend: &Backend) -> Buffer {
183 let device = &backend.device;
184 check_buffer_size(device, self.label, self.usage, self.contents.size());
185 let raw = match self.contents {
186 BufferContents::Data(data) => {
187 use wgpu::util::DeviceExt;
188 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
189 label: self.label,
190 contents: data,
191 usage: self.usage.into(),
192 })
193 }
194 BufferContents::Empty(size) => device.create_buffer(&wgpu::BufferDescriptor {
195 label: self.label,
196 size,
197 usage: self.usage.into(),
198 mapped_at_creation: false,
199 }),
200 };
201 Buffer::new(raw, GpuContext::from_backend(backend))
202 }
203}
204
205fn check_buffer_size(device: &wgpu::Device, label: Option<&str>, usage: BufferUsages, size: u64) {
206 let limits = device.limits();
207 let labeled = || label.map(|l| format!(" '{l}'")).unwrap_or_default();
208 if size > limits.max_buffer_size {
209 panic!(
210 "buffer{}: {size} bytes exceeds this device's max_buffer_size ({})",
211 labeled(),
212 limits.max_buffer_size
213 );
214 }
215 if usage.contains(BufferUsages::UNIFORM) && size > limits.max_uniform_buffer_binding_size {
216 panic!(
217 "buffer{}: {size} bytes exceeds this device's max_uniform_buffer_binding_size ({})",
218 labeled(),
219 limits.max_uniform_buffer_binding_size
220 );
221 }
222 if usage.contains(BufferUsages::STORAGE) && size > limits.max_storage_buffer_binding_size {
223 panic!(
224 "buffer{}: {size} bytes exceeds this device's max_storage_buffer_binding_size ({})",
225 labeled(),
226 limits.max_storage_buffer_binding_size
227 );
228 }
229}
230
231enum DynamicKind {
232 Uniform,
233 Storage,
234}
235
236pub struct DynamicBufferBuilder<'a> {
240 label: Option<&'a str>,
241 kind: DynamicKind,
242 element_size: u64,
243 count: u64,
244}
245
246impl<'a> DynamicBufferBuilder<'a> {
247 pub fn uniform(element_size: u64, count: u64) -> Self {
248 Self {
249 label: None,
250 kind: DynamicKind::Uniform,
251 element_size,
252 count,
253 }
254 }
255
256 pub fn storage(element_size: u64, count: u64) -> Self {
257 Self {
258 label: None,
259 kind: DynamicKind::Storage,
260 element_size,
261 count,
262 }
263 }
264
265 pub fn with_label(mut self, label: impl Into<Option<&'a str>>) -> Self {
266 self.label = label.into();
267 self
268 }
269
270 pub fn build(self, backend: &Backend) -> DynamicBuffer {
271 let (usage, stride) = match self.kind {
272 DynamicKind::Uniform => (
273 BufferUsages::UNIFORM | BufferUsages::COPY_DST,
274 dynamic_uniform_offset_stride(backend, self.element_size),
275 ),
276 DynamicKind::Storage => (
277 BufferUsages::STORAGE | BufferUsages::COPY_DST,
278 dynamic_storage_offset_stride(backend, self.element_size),
279 ),
280 };
281 let buffer = BufferBuilder::empty(stride * self.count)
282 .with_label(self.label)
283 .with_usage(usage)
284 .build(backend);
285 DynamicBuffer::new(buffer, stride, self.element_size)
286 }
287}
288
289pub fn dynamic_uniform_offset_stride(backend: &Backend, element_size: u64) -> u64 {
290 align_to(
291 element_size,
292 backend.device.limits().min_uniform_buffer_offset_alignment as u64,
293 )
294}
295
296pub fn dynamic_storage_offset_stride(backend: &Backend, element_size: u64) -> u64 {
297 align_to(
298 element_size,
299 backend.device.limits().min_storage_buffer_offset_alignment as u64,
300 )
301}
302
303fn align_to(size: u64, alignment: u64) -> u64 {
304 size.div_ceil(alignment) * alignment
305}
306
307fn dynamic_buffer_binding(buffer: &wgpu::Buffer, element_size: u64) -> wgpu::BindingResource<'_> {
308 wgpu::BindingResource::Buffer(wgpu::BufferBinding {
309 buffer,
310 offset: 0,
311 size: wgpu::BufferSize::new(element_size),
312 })
313}
314
315pub struct BindGroup(wgpu::BindGroup);
317
318impl BindGroup {
319 pub(crate) fn raw(&self) -> &wgpu::BindGroup {
320 &self.0
321 }
322}
323
324pub struct BindGroupBuilder<'a> {
329 label: Option<&'a str>,
330 layout: &'a wgpu::BindGroupLayout,
331 entries: Vec<wgpu::BindGroupEntry<'a>>,
332 next_binding: u32,
333}
334
335impl<'a> BindGroupBuilder<'a> {
336 pub fn new(layout: &'a BindGroupLayout) -> Self {
337 Self {
338 label: None,
339 layout: layout.raw(),
340 entries: Vec::new(),
341 next_binding: 0,
342 }
343 }
344
345 pub fn with_label(mut self, label: impl Into<Option<&'a str>>) -> Self {
346 self.label = label.into();
347 self
348 }
349
350 pub fn with_buffer(self, buffer: &'a Buffer) -> Self {
351 let binding = self.next_binding;
352 self.with_buffer_at(binding, buffer)
353 }
354
355 pub fn with_buffer_at(mut self, binding: u32, buffer: &'a Buffer) -> Self {
356 self.entries.push(wgpu::BindGroupEntry {
357 binding,
358 resource: buffer.raw().as_entire_binding(),
359 });
360 self.next_binding = self.next_binding.max(binding + 1);
361 self
362 }
363
364 pub fn with_dynamic_buffer(self, buffer: &'a DynamicBuffer) -> Self {
365 let binding = self.next_binding;
366 self.with_dynamic_buffer_at(binding, buffer)
367 }
368
369 pub fn with_dynamic_buffer_at(mut self, binding: u32, buffer: &'a DynamicBuffer) -> Self {
370 let resource = dynamic_buffer_binding(buffer.buffer.raw(), buffer.element_size);
371 self.entries
372 .push(wgpu::BindGroupEntry { binding, resource });
373 self.next_binding = self.next_binding.max(binding + 1);
374 self
375 }
376
377 pub fn with_texture_2d(self, texture: &'a GPUTexture) -> Self {
378 let binding = self.next_binding;
379 self.with_texture_2d_at(binding, texture)
380 }
381
382 pub fn with_texture_2d_at(self, binding: u32, texture: &'a GPUTexture) -> Self {
383 self.texture_view_raw_at(binding, texture.view())
384 }
385
386 pub fn with_texture_array(self, texture: &'a GPUTextureArray) -> Self {
387 let binding = self.next_binding;
388 self.with_texture_array_at(binding, texture)
389 }
390
391 pub fn with_texture_array_at(self, binding: u32, texture: &'a GPUTextureArray) -> Self {
392 self.texture_view_raw_at(binding, texture.view())
393 }
394
395 pub fn with_texture_cubemap(self, texture: &'a GPUCubemap) -> Self {
396 let binding = self.next_binding;
397 self.with_texture_cubemap_at(binding, texture)
398 }
399
400 pub fn with_texture_cubemap_at(self, binding: u32, texture: &'a GPUCubemap) -> Self {
401 self.texture_view_raw_at(binding, texture.view())
402 }
403
404 pub fn with_texture_view(self, view: &'a TextureView) -> Self {
405 let binding = self.next_binding;
406 self.with_texture_view_at(binding, view)
407 }
408
409 pub fn with_texture_view_at(self, binding: u32, view: &'a TextureView) -> Self {
410 self.texture_view_raw_at(binding, view.raw())
411 }
412
413 pub(crate) fn texture_view_raw_at(mut self, binding: u32, view: &'a wgpu::TextureView) -> Self {
414 self.entries.push(wgpu::BindGroupEntry {
415 binding,
416 resource: wgpu::BindingResource::TextureView(view),
417 });
418 self.next_binding = self.next_binding.max(binding + 1);
419 self
420 }
421
422 pub fn with_sampler(self, sampler: &'a Sampler) -> Self {
423 let binding = self.next_binding;
424 self.with_sampler_at(binding, sampler)
425 }
426
427 pub fn with_sampler_at(mut self, binding: u32, sampler: &'a Sampler) -> Self {
428 self.entries.push(wgpu::BindGroupEntry {
429 binding,
430 resource: wgpu::BindingResource::Sampler(sampler.raw()),
431 });
432 self.next_binding = self.next_binding.max(binding + 1);
433 self
434 }
435
436 pub fn build(self, backend: &Backend) -> BindGroup {
437 BindGroup(
438 backend
439 .device
440 .create_bind_group(&wgpu::BindGroupDescriptor {
441 label: self.label,
442 layout: self.layout,
443 entries: &self.entries,
444 }),
445 )
446 }
447}