wgpu_examples/render_to_texture/
mod.rs1#[cfg(not(target_arch = "wasm32"))]
2use crate::utils::output_image_native;
3#[cfg(target_arch = "wasm32")]
4use crate::utils::output_image_wasm;
5
6const TEXTURE_DIMS: (usize, usize) = (512, 512);
7
8async fn run(_path: Option<String>) {
9 let mut texture_data = Vec::<u8>::with_capacity(TEXTURE_DIMS.0 * TEXTURE_DIMS.1 * 4);
12
13 let instance = wgpu::Instance::default();
14 let adapter = instance
15 .request_adapter(&wgpu::RequestAdapterOptions::default())
16 .await
17 .unwrap();
18 let (device, queue) = adapter
19 .request_device(&wgpu::DeviceDescriptor {
20 label: None,
21 required_features: wgpu::Features::empty(),
22 required_limits: wgpu::Limits::downlevel_defaults(),
23 experimental_features: wgpu::ExperimentalFeatures::disabled(),
24 memory_hints: wgpu::MemoryHints::MemoryUsage,
25 trace: wgpu::Trace::Off,
26 })
27 .await
28 .unwrap();
29
30 let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
31
32 let render_target = device.create_texture(&wgpu::TextureDescriptor {
33 label: None,
34 size: wgpu::Extent3d {
35 width: TEXTURE_DIMS.0 as u32,
36 height: TEXTURE_DIMS.1 as u32,
37 depth_or_array_layers: 1,
38 },
39 mip_level_count: 1,
40 sample_count: 1,
41 dimension: wgpu::TextureDimension::D2,
42 format: wgpu::TextureFormat::Rgba8UnormSrgb,
43 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
44 view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
45 });
46 let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
47 label: None,
48 size: texture_data.capacity() as u64,
49 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
50 mapped_at_creation: false,
51 });
52
53 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
54 label: None,
55 layout: None,
56 vertex: wgpu::VertexState {
57 module: &shader,
58 entry_point: Some("vs_main"),
59 compilation_options: Default::default(),
60 buffers: &[],
61 },
62 fragment: Some(wgpu::FragmentState {
63 module: &shader,
64 entry_point: Some("fs_main"),
65 compilation_options: Default::default(),
66 targets: &[Some(wgpu::TextureFormat::Rgba8UnormSrgb.into())],
67 }),
68 primitive: wgpu::PrimitiveState::default(),
69 depth_stencil: None,
70 multisample: wgpu::MultisampleState::default(),
71 multiview: None,
72 cache: None,
73 });
74
75 log::info!("Wgpu context set up.");
76
77 let texture_view = render_target.create_view(&wgpu::TextureViewDescriptor::default());
80
81 let mut command_encoder =
82 device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
83 {
84 let mut render_pass = command_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
85 label: None,
86 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
87 view: &texture_view,
88 depth_slice: None,
89 resolve_target: None,
90 ops: wgpu::Operations {
91 load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
92 store: wgpu::StoreOp::Store,
93 },
94 })],
95 depth_stencil_attachment: None,
96 occlusion_query_set: None,
97 timestamp_writes: None,
98 });
99 render_pass.set_pipeline(&pipeline);
100 render_pass.draw(0..3, 0..1);
101 }
102 command_encoder.copy_texture_to_buffer(
104 wgpu::TexelCopyTextureInfo {
105 texture: &render_target,
106 mip_level: 0,
107 origin: wgpu::Origin3d::ZERO,
108 aspect: wgpu::TextureAspect::All,
109 },
110 wgpu::TexelCopyBufferInfo {
111 buffer: &output_staging_buffer,
112 layout: wgpu::TexelCopyBufferLayout {
113 offset: 0,
114 bytes_per_row: Some((TEXTURE_DIMS.0 * 4) as u32),
117 rows_per_image: Some(TEXTURE_DIMS.1 as u32),
118 },
119 },
120 wgpu::Extent3d {
121 width: TEXTURE_DIMS.0 as u32,
122 height: TEXTURE_DIMS.1 as u32,
123 depth_or_array_layers: 1,
124 },
125 );
126 queue.submit(Some(command_encoder.finish()));
127 log::info!("Commands submitted.");
128
129 let buffer_slice = output_staging_buffer.slice(..);
133 let (sender, receiver) = flume::bounded(1);
134 buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
135 device.poll(wgpu::PollType::wait()).unwrap();
136 receiver.recv_async().await.unwrap().unwrap();
137 log::info!("Output buffer mapped.");
138 {
139 let view = buffer_slice.get_mapped_range();
140 texture_data.extend_from_slice(&view[..]);
141 }
142 log::info!("Image data copied to local.");
143 output_staging_buffer.unmap();
144
145 #[cfg(not(target_arch = "wasm32"))]
146 output_image_native(texture_data.to_vec(), TEXTURE_DIMS, _path.unwrap());
147 #[cfg(target_arch = "wasm32")]
148 output_image_wasm(texture_data.to_vec(), TEXTURE_DIMS);
149 log::info!("Done.");
150}
151
152pub fn main() {
153 #[cfg(not(target_arch = "wasm32"))]
154 {
155 env_logger::builder()
156 .filter_level(log::LevelFilter::Info)
157 .format_timestamp_nanos()
158 .init();
159
160 let path = std::env::args()
161 .nth(2)
162 .unwrap_or_else(|| "please_don't_git_push_me.png".to_string());
163 pollster::block_on(run(Some(path)));
164 }
165 #[cfg(target_arch = "wasm32")]
166 {
167 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
168 console_log::init_with_level(log::Level::Info).expect("could not initialize logger");
169 wasm_bindgen_futures::spawn_local(run(None));
170 }
171}