wgpu_examples/storage_texture/
mod.rs1#[cfg(not(target_arch = "wasm32"))]
18use crate::utils::output_image_native;
19#[cfg(target_arch = "wasm32")]
20use crate::utils::output_image_wasm;
21
22const TEXTURE_DIMS: (usize, usize) = (512, 512);
23
24async fn run(_path: Option<String>) {
25 let mut texture_data = vec![0u8; TEXTURE_DIMS.0 * TEXTURE_DIMS.1 * 4];
26
27 let instance = wgpu::Instance::default();
28 let adapter = instance
29 .request_adapter(&wgpu::RequestAdapterOptions::default())
30 .await
31 .unwrap();
32 let (device, queue) = adapter
33 .request_device(&wgpu::DeviceDescriptor {
34 label: None,
35 required_features: wgpu::Features::empty(),
36 required_limits: wgpu::Limits::downlevel_defaults(),
37 experimental_features: wgpu::ExperimentalFeatures::disabled(),
38 memory_hints: wgpu::MemoryHints::MemoryUsage,
39 trace: wgpu::Trace::Off,
40 })
41 .await
42 .unwrap();
43
44 let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
45
46 let storage_texture = device.create_texture(&wgpu::TextureDescriptor {
47 label: None,
48 size: wgpu::Extent3d {
49 width: TEXTURE_DIMS.0 as u32,
50 height: TEXTURE_DIMS.1 as u32,
51 depth_or_array_layers: 1,
52 },
53 mip_level_count: 1,
54 sample_count: 1,
55 dimension: wgpu::TextureDimension::D2,
56 format: wgpu::TextureFormat::Rgba8Unorm,
57 usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::COPY_SRC,
58 view_formats: &[],
59 });
60 let storage_texture_view = storage_texture.create_view(&wgpu::TextureViewDescriptor::default());
61 let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
62 label: None,
63 size: size_of_val(&texture_data[..]) as u64,
64 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
65 mapped_at_creation: false,
66 });
67
68 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
69 label: None,
70 entries: &[wgpu::BindGroupLayoutEntry {
71 binding: 0,
72 visibility: wgpu::ShaderStages::COMPUTE,
73 ty: wgpu::BindingType::StorageTexture {
74 access: wgpu::StorageTextureAccess::WriteOnly,
75 format: wgpu::TextureFormat::Rgba8Unorm,
76 view_dimension: wgpu::TextureViewDimension::D2,
77 },
78 count: None,
79 }],
80 });
81 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
82 label: None,
83 layout: &bind_group_layout,
84 entries: &[wgpu::BindGroupEntry {
85 binding: 0,
86 resource: wgpu::BindingResource::TextureView(&storage_texture_view),
87 }],
88 });
89
90 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
91 label: None,
92 bind_group_layouts: &[&bind_group_layout],
93 push_constant_ranges: &[],
94 });
95 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
96 label: None,
97 layout: Some(&pipeline_layout),
98 module: &shader,
99 entry_point: Some("main"),
100 compilation_options: Default::default(),
101 cache: None,
102 });
103
104 log::info!("Wgpu context set up.");
105 let mut command_encoder =
108 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
109 {
110 let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
111 label: None,
112 timestamp_writes: None,
113 });
114 compute_pass.set_bind_group(0, &bind_group, &[]);
115 compute_pass.set_pipeline(&pipeline);
116 compute_pass.dispatch_workgroups(TEXTURE_DIMS.0 as u32, TEXTURE_DIMS.1 as u32, 1);
117 }
118 command_encoder.copy_texture_to_buffer(
119 wgpu::TexelCopyTextureInfo {
120 texture: &storage_texture,
121 mip_level: 0,
122 origin: wgpu::Origin3d::ZERO,
123 aspect: wgpu::TextureAspect::All,
124 },
125 wgpu::TexelCopyBufferInfo {
126 buffer: &output_staging_buffer,
127 layout: wgpu::TexelCopyBufferLayout {
128 offset: 0,
129 bytes_per_row: Some((TEXTURE_DIMS.0 * 4) as u32),
131 rows_per_image: Some(TEXTURE_DIMS.1 as u32),
132 },
133 },
134 wgpu::Extent3d {
135 width: TEXTURE_DIMS.0 as u32,
136 height: TEXTURE_DIMS.1 as u32,
137 depth_or_array_layers: 1,
138 },
139 );
140 queue.submit(Some(command_encoder.finish()));
141
142 let buffer_slice = output_staging_buffer.slice(..);
143 let (sender, receiver) = flume::bounded(1);
144 buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
145 device.poll(wgpu::PollType::wait()).unwrap();
146 receiver.recv_async().await.unwrap().unwrap();
147 log::info!("Output buffer mapped");
148 {
149 let view = buffer_slice.get_mapped_range();
150 texture_data.copy_from_slice(&view[..]);
151 }
152 log::info!("GPU data copied to local.");
153 output_staging_buffer.unmap();
154
155 #[cfg(not(target_arch = "wasm32"))]
156 output_image_native(texture_data.to_vec(), TEXTURE_DIMS, _path.unwrap());
157 #[cfg(target_arch = "wasm32")]
158 output_image_wasm(texture_data.to_vec(), TEXTURE_DIMS);
159 log::info!("Done.")
160}
161
162pub fn main() {
163 #[cfg(not(target_arch = "wasm32"))]
164 {
165 env_logger::builder()
166 .filter_level(log::LevelFilter::Info)
167 .format_timestamp_nanos()
168 .init();
169
170 let path = std::env::args()
171 .nth(2)
172 .unwrap_or_else(|| "please_don't_git_push_me.png".to_string());
173 pollster::block_on(run(Some(path)));
174 }
175 #[cfg(target_arch = "wasm32")]
176 {
177 std::panic::set_hook(Box::new(console_error_panic_hook::hook));
178 console_log::init_with_level(log::Level::Info).expect("could not initialize logger");
179 wasm_bindgen_futures::spawn_local(run(None));
180 }
181}