diff options
Diffstat (limited to 'vgcodec/src/paint.rs')
-rw-r--r-- | vgcodec/src/paint.rs | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/vgcodec/src/paint.rs b/vgcodec/src/paint.rs new file mode 100644 index 0000000..3da0647 --- /dev/null +++ b/vgcodec/src/paint.rs @@ -0,0 +1,120 @@ +use std::{borrow::Cow, sync::Arc}; +use wgpu::{util::DeviceExt, BindGroup, Buffer, ComputePipeline, Extent3d, Texture}; + +use crate::app::App; + +pub struct Painter { + app: Arc<App>, + size: Extent3d, + + pipeline: ComputePipeline, + bind_group: BindGroup, + + uniform_buffer_size: u64, + uniform_buffer: Buffer, + uniform_staging_buffer: Buffer, +} + +impl Painter { + pub fn new(app: &Arc<App>, extent: Extent3d, tex_a: &Texture, tex_b: &Texture) -> Self { + let App { device, .. } = app.as_ref(); + let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("paint.wgsl"))), + }); + + let uniform_buffer_size = std::mem::size_of::<u32>() as wgpu::BufferAddress; + let uniform_staging_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: uniform_buffer_size, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice(&[0u32]), + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_DST + | wgpu::BufferUsages::COPY_SRC, + }); + + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: None, + module: &cs_module, + entry_point: "main", + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView( + &tex_a.create_view(&wgpu::TextureViewDescriptor::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView( + &tex_b.create_view(&wgpu::TextureViewDescriptor::default()), + ), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: uniform_buffer.as_entire_binding(), + }, + ], + }); + Self { + app: app.clone(), + size: extent, + pipeline, + uniform_buffer_size, + uniform_buffer, + bind_group, + uniform_staging_buffer, + } + } + + pub async fn run(&mut self) -> u32 { + let App { device, queue, .. } = self.app.as_ref(); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut cpass = + encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None }); + cpass.set_pipeline(&self.pipeline); + cpass.set_bind_group(0, &self.bind_group, &[]); + cpass.dispatch_workgroups(self.size.width, self.size.height, 1); + } + + encoder.copy_buffer_to_buffer( + &self.uniform_buffer, + 0, + &self.uniform_staging_buffer, + 0, + self.uniform_buffer_size, + ); + + queue.submit(Some(encoder.finish())); + + let buffer_slice = self.uniform_staging_buffer.slice(..); + let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel(); + buffer_slice.map_async(wgpu::MapMode::Read, move |v| { + sender.send(v.unwrap()).unwrap() + }); + device.poll(wgpu::Maintain::Wait); + receiver.receive().await; + + let data = buffer_slice.get_mapped_range(); + let result: u32 = bytemuck::cast_slice(&data).to_vec()[0]; + + drop(data); + self.uniform_staging_buffer.unmap(); + + result + } +} |