use crate::core::Rgba;
use crate::gpu::context::Error;
use crate::gpu::renderer::GpuRenderer;
use crate::gpu::scene::Scene;
use std::sync::mpsc;
pub struct CapturedFrame {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
}
impl GpuRenderer {
pub fn capture(&mut self, scene: &Scene, scale: f32, clear: Rgba) -> Result<CapturedFrame, Error> {
let (width, height, format) =
(self.context.config.width, self.context.config.height, self.context.config.format);
self.upload(scene, scale);
let device = &self.context.device;
let extent = wgpu::Extent3d { width, height, depth_or_array_layers: 1 };
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("aura_capture"),
size: extent,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let padded_row = (width * 4).next_multiple_of(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT);
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("aura_capture_readback"),
size: padded_row as u64 * height as u64,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let mut encoder = self.encode(&view, scene, clear);
encoder.copy_texture_to_buffer(
wgpu::TexelCopyTextureInfo {
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyBufferInfo {
buffer: &buffer,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(padded_row),
rows_per_image: Some(height),
},
},
extent,
);
self.context.queue.submit(Some(encoder.finish()));
let (sender, receiver) = mpsc::channel();
buffer.map_async(wgpu::MapMode::Read, .., move |result| {
sender.send(result).ok();
});
self.context
.device
.poll(wgpu::PollType::Wait { submission_index: None, timeout: None })
.map_err(|error| Error(error.to_string()))?;
receiver.recv().map_err(|error| Error(error.to_string()))?.map_err(|error| Error(error.to_string()))?;
let swap_red_blue = matches!(format, wgpu::TextureFormat::Bgra8Unorm | wgpu::TextureFormat::Bgra8UnormSrgb);
let mapped = buffer.slice(..).get_mapped_range().map_err(|error| Error(error.to_string()))?;
let mut rgba = Vec::with_capacity((width * height * 4) as usize);
for row in mapped.chunks(padded_row as usize) {
for pixel in row[..(width * 4) as usize].as_chunks::<4>().0 {
let [first, green, third, alpha] = *pixel;
let (red, blue) = if swap_red_blue { (third, first) } else { (first, third) };
rgba.extend_from_slice(&[red, green, blue, alpha]);
}
}
drop(mapped);
buffer.unmap();
Ok(CapturedFrame { width, height, rgba })
}
}