teksilo_render/
test_support.rs1use std::sync::mpsc;
5
6use crate::Renderer;
7
8pub async fn create_test_renderer(
9 label: &'static str,
10) -> Option<(Renderer, wgpu::Device, wgpu::Queue)> {
11 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
12 let adapter = instance
13 .request_adapter(&wgpu::RequestAdapterOptions {
14 power_preference: wgpu::PowerPreference::LowPower,
15 compatible_surface: None,
16 force_fallback_adapter: false,
17 ..Default::default()
18 })
19 .await
20 .ok()?;
21
22 let (device, queue) = adapter
23 .request_device(&wgpu::DeviceDescriptor {
24 label: Some(label),
25 required_features: wgpu::Features::empty(),
26 required_limits: wgpu::Limits::downlevel_defaults(),
27 ..Default::default()
28 })
29 .await
30 .ok()?;
31
32 let renderer = Renderer::new(
33 device.clone(),
34 queue.clone(),
35 wgpu::TextureFormat::Rgba8UnormSrgb,
36 );
37
38 Some((renderer, device, queue))
39}
40
41pub fn read_texture_rgba(
42 device: &wgpu::Device,
43 queue: &wgpu::Queue,
44 texture: &wgpu::Texture,
45 width: u32,
46 height: u32,
47) -> Vec<u8> {
48 let bytes_per_pixel = 4u32;
49 let unpadded_bytes_per_row = width * bytes_per_pixel;
50 let padded_bytes_per_row = unpadded_bytes_per_row.div_ceil(256) * 256;
51 let buffer_size = padded_bytes_per_row as u64 * height as u64;
52
53 let buffer = device.create_buffer(&wgpu::BufferDescriptor {
54 label: Some("teksilo_render_test_readback"),
55 size: buffer_size,
56 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
57 mapped_at_creation: false,
58 });
59
60 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
61 label: Some("teksilo_render_test_copy"),
62 });
63 encoder.copy_texture_to_buffer(
64 wgpu::TexelCopyTextureInfo {
65 texture,
66 mip_level: 0,
67 origin: wgpu::Origin3d::ZERO,
68 aspect: wgpu::TextureAspect::All,
69 },
70 wgpu::TexelCopyBufferInfo {
71 buffer: &buffer,
72 layout: wgpu::TexelCopyBufferLayout {
73 offset: 0,
74 bytes_per_row: Some(padded_bytes_per_row),
75 rows_per_image: Some(height),
76 },
77 },
78 wgpu::Extent3d {
79 width,
80 height,
81 depth_or_array_layers: 1,
82 },
83 );
84 queue.submit(std::iter::once(encoder.finish()));
85
86 let slice = buffer.slice(..);
87 let (tx, rx) = mpsc::channel();
88 slice.map_async(wgpu::MapMode::Read, move |result| {
89 let _ = tx.send(result);
90 });
91 let _ = device.poll(wgpu::PollType::Wait {
92 submission_index: None,
93 timeout: None,
94 });
95 rx.recv()
96 .expect("map_async callback dropped")
97 .expect("map_async failed");
98
99 let mapped = slice
100 .get_mapped_range()
101 .expect("mapped range unavailable after a successful map_async");
102 let mut pixels = vec![0u8; (width * height * bytes_per_pixel) as usize];
103 for row in 0..height as usize {
104 let src_offset = row * padded_bytes_per_row as usize;
105 let dst_offset = row * unpadded_bytes_per_row as usize;
106 pixels[dst_offset..dst_offset + unpadded_bytes_per_row as usize]
107 .copy_from_slice(&mapped[src_offset..src_offset + unpadded_bytes_per_row as usize]);
108 }
109 drop(mapped);
110 buffer.unmap();
111 pixels
112}