1use std::{fmt::Debug, marker::PhantomData, ops::Deref};
2
3use tracing::{info, warn};
4use wgpu::util::DeviceExt;
5
6#[derive(bevy_ecs::prelude::Resource, Clone)]
8pub struct WgpuContext {
9 pub instance: wgpu::Instance,
11 pub adapter: wgpu::Adapter,
13 pub device: wgpu::Device,
15 pub queue: wgpu::Queue,
17}
18
19impl WgpuContext {
20 pub async fn new() -> Self {
22 let instance = wgpu::Instance::default();
23 let adapter = instance
24 .request_adapter(&wgpu::RequestAdapterOptions {
25 power_preference: wgpu::PowerPreference::HighPerformance,
26 ..Default::default()
27 })
28 .await
29 .unwrap();
30 info!("wgpu adapter info: {:?}", adapter.get_info());
31 let required_limits = adapter.limits();
32
33 #[cfg(feature = "profiling")]
34 let (device, queue) = adapter
35 .request_device(&wgpu::DeviceDescriptor {
36 required_features: wgpu_profiler::GpuProfiler::ALL_WGPU_TIMER_FEATURES,
37 required_limits,
38 ..Default::default()
39 })
40 .await
41 .unwrap();
42 #[cfg(not(feature = "profiling"))]
43 let (device, queue) = adapter
44 .request_device(&wgpu::DeviceDescriptor {
45 required_limits,
46 ..Default::default()
47 })
48 .await
49 .unwrap();
50
51 Self {
52 instance,
53 adapter,
54 device,
55 queue,
56 }
57 }
58}
59
60#[allow(unused)]
61pub(crate) struct WgpuBuffer<T: bytemuck::Pod + bytemuck::Zeroable + Debug> {
62 label: Option<&'static str>,
63 buffer: wgpu::Buffer,
64 usage: wgpu::BufferUsages,
65 inner: T,
66}
67
68impl<T: bytemuck::Pod + bytemuck::Zeroable + Debug> AsRef<wgpu::Buffer> for WgpuBuffer<T> {
69 fn as_ref(&self) -> &wgpu::Buffer {
70 &self.buffer
71 }
72}
73
74#[allow(unused)]
75impl<T: bytemuck::Pod + bytemuck::Zeroable + Debug> WgpuBuffer<T> {
76 pub(crate) fn new_init(
77 ctx: &WgpuContext,
78 label: Option<&'static str>,
79 usage: wgpu::BufferUsages,
80 data: T,
81 ) -> Self {
82 assert!(
83 usage.contains(wgpu::BufferUsages::COPY_DST),
84 "Buffer {label:?} does not contains COPY_DST"
85 );
86 Self {
88 label,
89 buffer: ctx
90 .device
91 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
92 label,
93 contents: bytemuck::bytes_of(&data),
94 usage,
95 }),
96 usage,
97 inner: data,
98 }
99 }
100
101 pub(crate) fn get(&self) -> &T {
102 &self.inner
103 }
104
105 pub(crate) fn set(&mut self, ctx: &WgpuContext, data: T) {
106 {
107 let mut view = ctx
108 .queue
109 .write_buffer_with(
110 &self.buffer,
111 0,
112 wgpu::BufferSize::new(std::mem::size_of_val(&data) as u64).unwrap(),
113 )
114 .unwrap();
115 view.copy_from_slice(bytemuck::bytes_of(&data));
116 }
117 self.inner = data;
119 }
120
121 #[allow(unused)]
122 pub(crate) fn read_buffer(&self, ctx: &WgpuContext) -> Vec<u8> {
123 let size = std::mem::size_of::<T>();
124 let staging_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
125 label: Some("Debug Staging Buffer"),
126 size: size as u64,
127 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
128 mapped_at_creation: false,
129 });
130
131 let mut encoder = ctx
132 .device
133 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
134 label: Some("Debug Read Encoder"),
135 });
136
137 encoder.copy_buffer_to_buffer(&self.buffer, 0, &staging_buffer, 0, size as u64);
138 ctx.queue.submit(Some(encoder.finish()));
139
140 let buffer_slice = staging_buffer.slice(..);
141 let (tx, rx) = async_channel::bounded(1);
142 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
143 pollster::block_on(tx.send(result)).unwrap()
144 });
145 ctx.device
146 .poll(wgpu::PollType::wait_indefinitely())
147 .unwrap();
148 pollster::block_on(rx.recv()).unwrap().unwrap();
149
150 buffer_slice.get_mapped_range().unwrap().to_vec()
151 }
152}
153
154pub(crate) struct WgpuVecBuffer<T: Default + bytemuck::Pod + bytemuck::Zeroable + Debug> {
155 label: Option<&'static str>,
156 pub(crate) buffer: wgpu::Buffer,
157 usage: wgpu::BufferUsages,
158 len: usize,
160 _phantom: PhantomData<T>,
161 }
163
164impl<T: Default + bytemuck::Pod + bytemuck::Zeroable + Debug> WgpuVecBuffer<T> {
165 pub(crate) fn new(
166 ctx: &WgpuContext,
167 label: Option<&'static str>,
168 usage: wgpu::BufferUsages,
169 len: usize,
170 ) -> Self {
171 assert!(
172 usage.contains(wgpu::BufferUsages::COPY_DST),
173 "Buffer {label:?} does not contains COPY_DST"
174 );
175 let size = (std::mem::size_of::<T>() * len) as u64;
176 Self {
177 label,
178 buffer: ctx.device.create_buffer(&wgpu::BufferDescriptor {
179 label,
180 size,
181 usage,
182 mapped_at_creation: false,
183 }),
184 usage,
185 len: 0,
186 _phantom: PhantomData,
187 }
189 }
190
191 #[allow(unused)]
192 pub(crate) fn new_init(
193 ctx: &WgpuContext,
194 label: Option<&'static str>,
195 usage: wgpu::BufferUsages,
196 data: &[T],
197 ) -> Self {
198 let mut buffer = Self::new(ctx, label, usage, data.len());
199 buffer.set(ctx, data);
200 buffer
201 }
202
203 #[allow(unused)]
204 pub(crate) fn len(&self) -> usize {
205 self.len
206 }
207 #[allow(unused)]
212 pub(crate) fn resize(&mut self, ctx: &WgpuContext, len: usize) -> bool {
213 let size = (std::mem::size_of::<T>() * len) as u64;
214 let realloc = self.buffer.size() != size;
215 if realloc {
216 self.len = len;
217 self.buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
219 label: self.label,
220 size,
221 usage: self.usage,
222 mapped_at_creation: false,
223 })
224 }
225 realloc
226 }
227
228 pub(crate) fn set(&mut self, ctx: &WgpuContext, data: &[T]) -> bool {
229 self.len = data.len();
233 let realloc = self.buffer.size() != std::mem::size_of_val(data) as u64;
234
235 if realloc {
236 let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
239 label: self.label,
240 size: (std::mem::size_of_val(data)) as u64,
241 usage: self.usage,
242 mapped_at_creation: false,
243 });
244 ctx.queue
245 .write_buffer(&buffer, 0, bytemuck::cast_slice(data));
246 self.buffer = buffer;
248 } else {
249 {
251 let mut view = ctx
252 .queue
253 .write_buffer_with(
254 &self.buffer,
255 0,
256 wgpu::BufferSize::new((std::mem::size_of_val(data)) as u64).unwrap(),
257 )
258 .unwrap();
259 view.copy_from_slice(bytemuck::cast_slice(data));
260 }
261 }
263 realloc
265 }
266
267 #[allow(unused)]
268 pub(crate) fn read_buffer(&self, ctx: &WgpuContext) -> Option<Vec<u8>> {
269 let size = std::mem::size_of::<T>() * self.len;
270 let staging_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
271 label: Some("Debug Staging Buffer"),
272 size: size as u64,
273 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
274 mapped_at_creation: false,
275 });
276
277 let mut encoder = ctx
278 .device
279 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
280 label: Some("Debug Read Encoder"),
281 });
282
283 encoder.copy_buffer_to_buffer(&self.buffer, 0, &staging_buffer, 0, size as u64);
284 ctx.queue.submit(Some(encoder.finish()));
285
286 let buffer_slice = staging_buffer.slice(..);
287 let (tx, rx) = async_channel::bounded(1);
288 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
289 tx.try_send(result).unwrap()
290 });
291 ctx.device
292 .poll(wgpu::PollType::wait_indefinitely())
293 .unwrap();
294 pollster::block_on(rx.recv()).unwrap().unwrap();
295
296 let x = buffer_slice.get_mapped_range().unwrap().to_vec();
297 Some(x)
298 }
299}
300
301pub struct WgpuTexture {
302 inner: wgpu::Texture,
303}
304
305impl WgpuTexture {
306 pub fn new(ctx: &WgpuContext, desc: &wgpu::TextureDescriptor) -> Self {
307 Self {
308 inner: ctx.device.create_texture(desc),
309 }
310 }
311}
312
313impl Deref for WgpuTexture {
314 type Target = wgpu::Texture;
315 fn deref(&self) -> &Self::Target {
316 &self.inner
317 }
318}
319
320pub struct ReadbackWgpuTexture {
323 inner: WgpuTexture,
324 aligned_bytes_per_row: usize,
325 staging_buffer: wgpu::Buffer,
326 bytes: Vec<u8>,
327 pending_rx: Option<async_channel::Receiver<Result<(), wgpu::BufferAsyncError>>>,
330}
331
332impl Deref for ReadbackWgpuTexture {
333 type Target = WgpuTexture;
334 fn deref(&self) -> &Self::Target {
335 &self.inner
336 }
337}
338
339const ALIGNMENT: usize = 256;
340impl ReadbackWgpuTexture {
341 pub fn new(ctx: &WgpuContext, desc: &wgpu::TextureDescriptor) -> Self {
342 if !desc.usage.contains(wgpu::TextureUsages::COPY_SRC) {
343 warn!(
344 "ReadbackWgpuTexture should have COPY_SRC usage, but got {:?}, will auto add this usage",
345 desc.usage
346 );
347 }
348 let texture = WgpuTexture::new(
349 ctx,
350 &wgpu::TextureDescriptor {
351 usage: desc.usage | wgpu::TextureUsages::COPY_SRC,
352 ..*desc
353 },
354 );
355 let block_size = desc.format.block_copy_size(None).unwrap();
356 let bytes_per_row =
357 (texture.size().width * block_size).div_ceil(ALIGNMENT as u32) as usize * ALIGNMENT;
358
359 let staging_buffer_label = desc.label.map(|s| format!("{s} Staging Buffer"));
360 let staging_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
361 label: staging_buffer_label.as_deref(),
362 size: (bytes_per_row * texture.size().height as usize) as u64,
363 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
364 mapped_at_creation: false,
365 });
366 let len = texture.size().width * texture.size().height * block_size;
367 let bytes = vec![0u8; len as usize];
368
369 Self {
370 inner: texture,
371 aligned_bytes_per_row: bytes_per_row,
372 staging_buffer,
373 bytes,
374 pending_rx: None,
375 }
376 }
377 pub fn texture_data(&self) -> &[u8] {
378 &self.bytes
379 }
380
381 pub fn start_readback(&mut self, ctx: &WgpuContext) {
386 let size = self.size();
387
388 let mut encoder = ctx
389 .device
390 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
391 label: Some("Readback Copy Encoder"),
392 });
393 encoder.copy_texture_to_buffer(
394 wgpu::TexelCopyTextureInfo {
395 aspect: wgpu::TextureAspect::All,
396 texture: self,
397 mip_level: 0,
398 origin: wgpu::Origin3d::ZERO,
399 },
400 wgpu::TexelCopyBufferInfo {
401 buffer: &self.staging_buffer,
402 layout: wgpu::TexelCopyBufferLayout {
403 offset: 0,
404 bytes_per_row: Some(self.aligned_bytes_per_row as u32),
405 rows_per_image: Some(size.height),
406 },
407 },
408 size,
409 );
410 ctx.queue.submit(Some(encoder.finish()));
411
412 let buffer_slice = self.staging_buffer.slice(..);
413 let (tx, rx) = async_channel::bounded(1);
414 buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
415 let _ = tx.try_send(result);
416 });
417 self.pending_rx = Some(rx);
418 }
419
420 pub fn finish_readback(&mut self, ctx: &WgpuContext) {
425 let Some(rx) = self.pending_rx.take() else {
426 return;
427 };
428
429 ctx.device
430 .poll(wgpu::PollType::wait_indefinitely())
431 .unwrap();
432 pollster::block_on(rx.recv()).unwrap().unwrap();
433
434 self.copy_staging_to_bytes();
435 }
436
437 pub fn try_finish_readback(&mut self, ctx: &WgpuContext) -> bool {
440 let Some(rx) = self.pending_rx.as_ref() else {
441 return true;
442 };
443
444 let _ = ctx.device.poll(wgpu::PollType::Poll);
446
447 match rx.try_recv() {
449 Ok(result) => {
450 result.unwrap();
451 self.pending_rx = None;
452 self.copy_staging_to_bytes();
453 true
454 }
455 Err(async_channel::TryRecvError::Empty) => false,
456 Err(async_channel::TryRecvError::Closed) => {
457 self.pending_rx = None;
458 true
459 }
460 }
461 }
462
463 fn copy_staging_to_bytes(&mut self) {
464 let size = self.size();
465 let buffer_slice = self.staging_buffer.slice(..);
466 let view = buffer_slice.get_mapped_range().unwrap();
467 let block_size = self.inner.format().block_copy_size(None).unwrap();
468 let bytes_in_row = (size.width * block_size) as usize;
469
470 for y in 0..size.height as usize {
471 let src_row_start = y * self.aligned_bytes_per_row;
472 let dst_row_start = y * bytes_in_row;
473
474 self.bytes[dst_row_start..dst_row_start + bytes_in_row]
475 .copy_from_slice(&view[src_row_start..src_row_start + bytes_in_row]);
476 }
477 drop(view);
478 self.staging_buffer.unmap();
479 }
480
481 pub fn update_texture_data(&mut self, ctx: &WgpuContext) -> &[u8] {
483 self.start_readback(ctx);
484 self.finish_readback(ctx);
485 &self.bytes
486 }
487}
488
489#[cfg(test)]
490mod test {
491 #[test]
492 fn test() {
493 }
499}