Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use pollster::FutureExt;
- use crate::gpu_interface::GPUInterface;
- pub struct Life {
- compute_pipeline: wgpu::ComputePipeline,
- input_texture: wgpu::Texture,
- output_texture: wgpu::Texture,
- texture_size: wgpu::Extent3d,
- }
- impl Life {
- pub fn new(gpu: &GPUInterface) -> Life {
- let shader = gpu
- .device
- .create_shader_module(&wgpu::ShaderModuleDescriptor {
- label: Some("Grayscale shader"),
- source: wgpu::ShaderSource::Wgsl(include_str!("grayscale.wgsl").into()),
- });
- let pipeline = gpu
- .device
- .create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
- label: Some("Grayscale pipeline"),
- layout: None,
- module: &shader,
- entry_point: "grayscale_main",
- });
- let input_image = image::load_from_memory(include_bytes!("test1.png"))
- .unwrap()
- .to_rgba8();
- let (width, height) = input_image.dimensions();
- let texture_size = wgpu::Extent3d {
- width,
- height,
- depth_or_array_layers: 1,
- };
- let input_texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
- label: Some("input texture"),
- size: texture_size,
- mip_level_count: 1,
- sample_count: 1,
- dimension: wgpu::TextureDimension::D2,
- format: wgpu::TextureFormat::Rgba8Unorm,
- usage: wgpu::TextureUsages::TEXTURE_BINDING
- | wgpu::TextureUsages::COPY_DST
- | wgpu::TextureUsages::COPY_SRC
- | wgpu::TextureUsages::STORAGE_BINDING,
- });
- let output_texture = gpu.device.create_texture(&wgpu::TextureDescriptor {
- label: Some("output texture"),
- size: texture_size,
- mip_level_count: 1,
- sample_count: 1,
- dimension: wgpu::TextureDimension::D2,
- format: wgpu::TextureFormat::Rgba8Unorm,
- usage: wgpu::TextureUsages::TEXTURE_BINDING
- | wgpu::TextureUsages::COPY_DST
- | wgpu::TextureUsages::COPY_SRC
- | wgpu::TextureUsages::STORAGE_BINDING,
- });
- gpu.queue.write_texture(
- input_texture.as_image_copy(),
- bytemuck::cast_slice(input_image.as_raw()),
- wgpu::ImageDataLayout {
- offset: 0,
- bytes_per_row: std::num::NonZeroU32::new(4 * width),
- rows_per_image: None, // Doesn't need to be specified as we are writing a single image.
- },
- texture_size,
- );
- Life {
- compute_pipeline: pipeline,
- input_texture: input_texture,
- output_texture: output_texture,
- texture_size: texture_size,
- }
- }
- pub fn step(&self, gpu: &GPUInterface) {
- let mut encoder = gpu
- .device
- .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
- let texture_bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {
- label: Some("Texture bind group"),
- layout: &self.compute_pipeline.get_bind_group_layout(0),
- entries: &[
- wgpu::BindGroupEntry {
- binding: 0,
- resource: wgpu::BindingResource::TextureView(
- &self
- .input_texture
- .create_view(&wgpu::TextureViewDescriptor::default()),
- ),
- },
- wgpu::BindGroupEntry {
- binding: 1,
- resource: wgpu::BindingResource::TextureView(
- &self
- .output_texture
- .create_view(&wgpu::TextureViewDescriptor::default()),
- ),
- },
- ],
- });
- // Dispatch
- {
- let (dispatch_with, dispatch_height) = Life::compute_work_group_count(
- (self.texture_size.width, self.texture_size.height),
- (16, 16),
- );
- let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
- label: Some("Grayscale pass"),
- });
- compute_pass.set_pipeline(&self.compute_pipeline);
- compute_pass.set_bind_group(0, &texture_bind_group, &[]);
- compute_pass.dispatch(dispatch_with, dispatch_height, 1);
- }
- //SAVE RESULT TO FILE
- //IF anything below this line is refactored into its own function, my output is blank.
- let padded_bytes_per_row = Life::padded_bytes_per_row(self.texture_size.width);
- let unpadded_bytes_per_row = self.texture_size.width as usize * 4;
- let output_buffer_size = padded_bytes_per_row as u64
- * self.texture_size.height as u64
- * std::mem::size_of::<u8>() as u64;
- let output_buffer = gpu.device.create_buffer(&wgpu::BufferDescriptor {
- label: None,
- size: output_buffer_size,
- usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
- mapped_at_creation: false,
- });
- encoder.copy_texture_to_buffer(
- wgpu::ImageCopyTexture {
- aspect: wgpu::TextureAspect::All,
- texture: &self.output_texture,
- mip_level: 0,
- origin: wgpu::Origin3d::ZERO,
- },
- wgpu::ImageCopyBuffer {
- buffer: &output_buffer,
- layout: wgpu::ImageDataLayout {
- offset: 0,
- bytes_per_row: std::num::NonZeroU32::new(padded_bytes_per_row as u32),
- rows_per_image: std::num::NonZeroU32::new(self.texture_size.height),
- },
- },
- self.texture_size,
- );
- gpu.queue.submit(Some(encoder.finish()));
- let buffer_slice = output_buffer.slice(..);
- let mapping = buffer_slice.map_async(wgpu::MapMode::Read);
- gpu.device.poll(wgpu::Maintain::Wait);
- mapping.block_on();
- let padded_data = buffer_slice.get_mapped_range();
- let mut pixels: Vec<u8> =
- vec![0; unpadded_bytes_per_row * self.texture_size.height as usize];
- for (padded, pixels) in padded_data
- .chunks_exact(padded_bytes_per_row)
- .zip(pixels.chunks_exact_mut(unpadded_bytes_per_row))
- {
- pixels.copy_from_slice(&padded[..unpadded_bytes_per_row]);
- }
- if let Some(output_image) = image::ImageBuffer::<image::Rgba<u8>, _>::from_raw(
- self.texture_size.width,
- self.texture_size.height,
- &pixels[..],
- ) {
- output_image.save("test2.png");
- }
- }
- fn compute_work_group_count(
- (width, height): (u32, u32),
- (workgroup_width, workgroup_height): (u32, u32),
- ) -> (u32, u32) {
- let x = (width + workgroup_width - 1) / workgroup_width;
- let y = (height + workgroup_height - 1) / workgroup_height;
- (x, y)
- }
- /// Compute the next multiple of 256 for texture retrieval padding.
- fn padded_bytes_per_row(width: u32) -> usize {
- let bytes_per_row = width as usize * 4;
- let padding = (256 - bytes_per_row % 256) % 256;
- bytes_per_row + padding
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment