use std::mem::size_of_val;
#[cfg(not(target_arch = "wasm32"))]
use crate::utils::output_image_native;
#[cfg(target_arch = "wasm32")]
use crate::utils::output_image_wasm;
const TEXTURE_DIMS: (usize, usize) = (512, 512);
async fn run(_path: Option<String>) {
let mut texture_data = vec![0u8; TEXTURE_DIMS.0 * TEXTURE_DIMS.1 * 4];
let instance = wgpu::Instance::default();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions::default())
.await
.unwrap();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::downlevel_defaults(),
memory_hints: wgpu::MemoryHints::MemoryUsage,
},
None,
)
.await
.unwrap();
let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
let storage_texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: TEXTURE_DIMS.0 as u32,
height: TEXTURE_DIMS.1 as u32,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
let storage_texture_view = storage_texture.create_view(&wgpu::TextureViewDescriptor::default());
let output_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: size_of_val(&texture_data[..]) as u64,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::StorageTexture {
access: wgpu::StorageTextureAccess::WriteOnly,
format: wgpu::TextureFormat::Rgba8Unorm,
view_dimension: wgpu::TextureViewDimension::D2,
},
count: None,
}],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&storage_texture_view),
}],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
module: &shader,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
log::info!("Wgpu context set up.");
let mut command_encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut compute_pass = command_encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
compute_pass.set_bind_group(0, &bind_group, &[]);
compute_pass.set_pipeline(&pipeline);
compute_pass.dispatch_workgroups(TEXTURE_DIMS.0 as u32, TEXTURE_DIMS.1 as u32, 1);
}
command_encoder.copy_texture_to_buffer(
wgpu::TexelCopyTextureInfo {
texture: &storage_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyBufferInfo {
buffer: &output_staging_buffer,
layout: wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some((TEXTURE_DIMS.0 * 4) as u32),
rows_per_image: Some(TEXTURE_DIMS.1 as u32),
},
},
wgpu::Extent3d {
width: TEXTURE_DIMS.0 as u32,
height: TEXTURE_DIMS.1 as u32,
depth_or_array_layers: 1,
},
);
queue.submit(Some(command_encoder.finish()));
let buffer_slice = output_staging_buffer.slice(..);
let (sender, receiver) = flume::bounded(1);
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
log::info!("Output buffer mapped");
{
let view = buffer_slice.get_mapped_range();
texture_data.copy_from_slice(&view[..]);
}
log::info!("GPU data copied to local.");
output_staging_buffer.unmap();
#[cfg(not(target_arch = "wasm32"))]
output_image_native(texture_data.to_vec(), TEXTURE_DIMS, _path.unwrap());
#[cfg(target_arch = "wasm32")]
output_image_wasm(texture_data.to_vec(), TEXTURE_DIMS);
log::info!("Done.")
}
pub fn main() {
#[cfg(not(target_arch = "wasm32"))]
{
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.format_timestamp_nanos()
.init();
let path = std::env::args()
.nth(2)
.unwrap_or_else(|| "please_don't_git_push_me.png".to_string());
pollster::block_on(run(Some(path)));
}
#[cfg(target_arch = "wasm32")]
{
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init_with_level(log::Level::Info).expect("could not initialize logger");
wasm_bindgen_futures::spawn_local(run(None));
}
}