diff --git a/Cargo.toml b/Cargo.toml index a0e52239fa..4cbdd7af92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,4 @@ cgmath = "0.17" env_logger = "0.6" glsl-to-spirv = "0.1" log = "0.4" +png = "0.15" diff --git a/examples/capture/main.rs b/examples/capture/main.rs new file mode 100644 index 0000000000..55b4eaa1d0 --- /dev/null +++ b/examples/capture/main.rs @@ -0,0 +1,106 @@ +/// This example shows how to capture an image by rendering it to a texture, copying the texture to +/// a buffer, and retrieving it from the buffer. This could be used for "taking a screenshot," with +/// the added benefit that this method doesn't require a window to be created. +use std::fs::File; +use std::mem::size_of; + +fn main() { + env_logger::init(); + + let instance = wgpu::Instance::new(); + + let adapter = instance.get_adapter(&wgpu::AdapterDescriptor { + power_preference: wgpu::PowerPreference::LowPower, + }); + + let mut device = adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits::default(), + }); + + // Rendered image is 256×256 with 32-bit RGBA color + let size = 256u32; + + // The output buffer lets us retrieve the data as an array + let output_buffer = device.create_buffer(&wgpu::BufferDescriptor { + size: (size * size) as u64 * size_of::() as u64, + usage: wgpu::BufferUsage::MAP_READ | wgpu::BufferUsage::TRANSFER_DST, + }); + + let texture_extent = wgpu::Extent3d { + width: size, + height: size, + depth: 1, + }; + + // The render pipeline renders data into this texture + let texture = device.create_texture(&wgpu::TextureDescriptor { + size: texture_extent, + array_layer_count: 1, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsage::STORAGE + | wgpu::TextureUsage::OUTPUT_ATTACHMENT + | wgpu::TextureUsage::TRANSFER_SRC, + }); + + // Set the background to be red + let command_buffer = { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &texture.create_default_view(), + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color::RED, + }], + depth_stencil_attachment: None, + }); + + // Copy the data from the texture to the buffer + encoder.copy_texture_to_buffer( + wgpu::TextureCopyView { + texture: &texture, + mip_level: 0, + array_layer: 0, + origin: wgpu::Origin3d::ZERO, + }, + wgpu::BufferCopyView { + buffer: &output_buffer, + offset: 0, + row_pitch: size_of::() as u32 * size, + image_height: size, + }, + texture_extent, + ); + + encoder.finish() + }; + + device.get_queue().submit(&[command_buffer]); + + // Write the buffer as a PNG + output_buffer.map_read_async( + 0, + (size * size) as u64 * size_of::() as u64, + move |result: wgpu::BufferMapAsyncResult<&[u8]>| { + let mut png_encoder = png::Encoder::new(File::create("red.png").unwrap(), size, size); + png_encoder.set_depth(png::BitDepth::Eight); + png_encoder.set_color(png::ColorType::RGBA); + png_encoder + .write_header() + .unwrap() + .write_image_data(result.unwrap().data) + .unwrap(); + }, + ); + + // The device will be polled when it is dropped but we can also poll it explicitly + device.poll(true); +}