diff options
Diffstat (limited to 'rust/crates/gpu/src')
| -rw-r--r-- | rust/crates/gpu/src/context.rs | 163 | ||||
| -rw-r--r-- | rust/crates/gpu/src/effect_pipeline.rs | 194 | ||||
| -rw-r--r-- | rust/crates/gpu/src/lib.rs | 23 | ||||
| -rw-r--r-- | rust/crates/gpu/src/mask_feather.rs | 259 | ||||
| -rw-r--r-- | rust/crates/gpu/src/sdf_pipeline.rs | 307 | ||||
| -rw-r--r-- | rust/crates/gpu/src/shader_registry.rs | 183 | ||||
| -rw-r--r-- | rust/crates/gpu/src/shaders/gaussian_blur.wgsl | 34 | ||||
| -rw-r--r-- | rust/crates/gpu/src/shaders/jfa_distance.wgsl | 50 | ||||
| -rw-r--r-- | rust/crates/gpu/src/shaders/jfa_init.wgsl | 35 | ||||
| -rw-r--r-- | rust/crates/gpu/src/shaders/jfa_step.wgsl | 75 |
10 files changed, 127 insertions, 1196 deletions
diff --git a/rust/crates/gpu/src/context.rs b/rust/crates/gpu/src/context.rs index 702f65ba..1526a67d 100644 --- a/rust/crates/gpu/src/context.rs +++ b/rust/crates/gpu/src/context.rs @@ -1,12 +1,8 @@ use wgpu::util::DeviceExt; -use crate::{ - GPU_TEXTURE_FORMAT, GpuError, - effect_pipeline::{ApplyEffectsOptions, apply_effects}, - mask_feather::{ApplyMaskFeatherOptions, MaskFeatherPipeline}, - sdf_pipeline::SdfPipeline, - shader_registry::ShaderRegistry, -}; +use crate::{FULLSCREEN_SHADER_SOURCE, GPU_TEXTURE_FORMAT, GpuError}; + +const BLIT_SHADER_SOURCE: &str = include_str!("shaders/blit.wgsl"); const FULLSCREEN_QUAD_POSITIONS: [[f32; 2]; 6] = [ [-1.0, -1.0], @@ -25,9 +21,8 @@ pub struct GpuContext { fullscreen_quad: wgpu::Buffer, linear_sampler: wgpu::Sampler, nearest_sampler: wgpu::Sampler, - shader_registry: ShaderRegistry, - sdf_pipeline: SdfPipeline, - mask_feather_pipeline: MaskFeatherPipeline, + texture_sampler_bind_group_layout: wgpu::BindGroupLayout, + blit_pipeline: wgpu::RenderPipeline, } impl GpuContext { @@ -77,9 +72,74 @@ impl GpuContext { mipmap_filter: wgpu::MipmapFilterMode::Nearest, ..Default::default() }); - let shader_registry = ShaderRegistry::new(&device); - let sdf_pipeline = SdfPipeline::new(&device); - let mask_feather_pipeline = MaskFeatherPipeline::new(&device); + let texture_sampler_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("gpu-texture-sampler-bind-group-layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + view_dimension: wgpu::TextureViewDimension::D2, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); + let vertex_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("gpu-fullscreen-shader"), + source: wgpu::ShaderSource::Wgsl(FULLSCREEN_SHADER_SOURCE.into()), + }); + let blit_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("gpu-blit-shader"), + source: wgpu::ShaderSource::Wgsl(BLIT_SHADER_SOURCE.into()), + }); + let blit_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("gpu-blit-pipeline-layout"), + bind_group_layouts: &[Some(&texture_sampler_bind_group_layout)], + immediate_size: 0, + }); + let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("gpu-blit-pipeline"), + layout: Some(&blit_pipeline_layout), + vertex: wgpu::VertexState { + module: &vertex_shader_module, + entry_point: Some("vertex_main"), + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::<[f32; 2]>() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + format: wgpu::VertexFormat::Float32x2, + offset: 0, + shader_location: 0, + }], + }], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &blit_shader_module, + entry_point: Some("fragment_main"), + targets: &[Some(wgpu::ColorTargetState { + format: GPU_TEXTURE_FORMAT, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview_mask: None, + cache: None, + }); Ok(Self { instance, @@ -89,26 +149,11 @@ impl GpuContext { fullscreen_quad, linear_sampler, nearest_sampler, - shader_registry, - sdf_pipeline, - mask_feather_pipeline, + texture_sampler_bind_group_layout, + blit_pipeline, }) } - pub fn apply_effects( - &self, - options: ApplyEffectsOptions<'_>, - ) -> Result<wgpu::Texture, GpuError> { - apply_effects(self, options) - } - - pub fn apply_mask_feather( - &self, - options: ApplyMaskFeatherOptions<'_>, - ) -> wgpu::Texture { - self.mask_feather_pipeline.apply_mask_feather(self, options) - } - pub fn create_render_texture( &self, width: u32, @@ -162,12 +207,8 @@ impl GpuContext { &self.nearest_sampler } - pub fn shader_registry(&self) -> &ShaderRegistry { - &self.shader_registry - } - - pub fn sdf_pipeline(&self) -> &SdfPipeline { - &self.sdf_pipeline + pub fn texture_sampler_bind_group_layout(&self) -> &wgpu::BindGroupLayout { + &self.texture_sampler_bind_group_layout } pub fn render_texture_to_surface( @@ -202,7 +243,7 @@ impl GpuContext { .create_view(&wgpu::TextureViewDescriptor::default()); let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("gpu-blit-bind-group"), - layout: self.shader_registry.effect_texture_bind_group_layout(), + layout: &self.texture_sampler_bind_group_layout, entries: &[ wgpu::BindGroupEntry { binding: 0, @@ -237,7 +278,7 @@ impl GpuContext { timestamp_writes: None, multiview_mask: None, }); - render_pass.set_pipeline(self.shader_registry.blit_pipeline()); + render_pass.set_pipeline(&self.blit_pipeline); render_pass.set_vertex_buffer(0, self.fullscreen_quad.slice(..)); render_pass.set_bind_group(0, &bind_group, &[]); render_pass.draw(0..6, 0..1); @@ -247,4 +288,50 @@ impl GpuContext { surface_texture.present(); Ok(()) } + + #[cfg(all(feature = "wasm", target_arch = "wasm32"))] + pub fn import_offscreen_canvas_texture( + &self, + canvas: &wgpu::web_sys::OffscreenCanvas, + width: u32, + height: u32, + label: &'static str, + ) -> wgpu::Texture { + let texture = self.create_render_texture(width, height, label); + self.queue.copy_external_image_to_texture( + &wgpu::CopyExternalImageSourceInfo { + source: wgpu::ExternalImageSource::OffscreenCanvas(canvas.clone()), + origin: wgpu::Origin2d::ZERO, + flip_y: true, + }, + wgpu::CopyExternalImageDestInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + color_space: wgpu::PredefinedColorSpace::Srgb, + premultiplied_alpha: false, + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + texture + } + + #[cfg(all(feature = "wasm", target_arch = "wasm32"))] + pub fn render_texture_to_offscreen_canvas( + &self, + texture: &wgpu::Texture, + canvas: &wgpu::web_sys::OffscreenCanvas, + width: u32, + height: u32, + ) -> Result<(), GpuError> { + let surface = self + .instance + .create_surface(wgpu::SurfaceTarget::OffscreenCanvas(canvas.clone()))?; + self.render_texture_to_surface(texture, &surface, width, height) + } } diff --git a/rust/crates/gpu/src/effect_pipeline.rs b/rust/crates/gpu/src/effect_pipeline.rs deleted file mode 100644 index c600551a..00000000 --- a/rust/crates/gpu/src/effect_pipeline.rs +++ /dev/null @@ -1,194 +0,0 @@ -use std::collections::HashMap; - -use bytemuck::{Pod, Zeroable}; -use wgpu::util::DeviceExt; - -use crate::{GpuError, context::GpuContext}; - -pub struct ApplyEffectsOptions<'a> { - pub source: &'a wgpu::Texture, - pub width: u32, - pub height: u32, - pub passes: &'a [EffectPass], -} - -#[derive(Clone, Debug)] -pub struct EffectPass { - pub shader: String, - pub uniforms: HashMap<String, UniformValue>, -} - -#[derive(Clone, Debug)] -pub enum UniformValue { - Number(f32), - Vector(Vec<f32>), -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct EffectUniformBuffer { - resolution: [f32; 2], - direction: [f32; 2], - scalars: [f32; 4], -} - -pub fn apply_effects( - context: &GpuContext, - ApplyEffectsOptions { - source, - width, - height, - passes, - }: ApplyEffectsOptions<'_>, -) -> Result<wgpu::Texture, GpuError> { - let mut current_texture: Option<wgpu::Texture> = None; - - for pass in passes { - let input_texture = current_texture.as_ref().unwrap_or(source); - let output_texture = - context.create_render_texture(width, height, "gpu-effect-pass-output"); - let input_view = input_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let texture_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-effect-texture-bind-group"), - layout: context - .shader_registry() - .effect_texture_bind_group_layout(), - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&input_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(context.linear_sampler()), - }, - ], - }); - let uniform_buffer = context - .device() - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("gpu-effect-uniform-buffer"), - contents: bytemuck::bytes_of(&pack_effect_uniforms(pass, width, height)?), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - let uniform_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-effect-uniform-bind-group"), - layout: context - .shader_registry() - .effect_uniform_bind_group_layout(), - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: uniform_buffer.as_entire_binding(), - }], - }); - let pipeline = context.shader_registry().get_effect_pipeline(&pass.shader)?; - let mut encoder = context - .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("gpu-effect-command-encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("gpu-effect-render-pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &output_view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); - render_pass.set_pipeline(pipeline); - render_pass.set_vertex_buffer(0, context.fullscreen_quad().slice(..)); - render_pass.set_bind_group(0, &texture_bind_group, &[]); - render_pass.set_bind_group(1, &uniform_bind_group, &[]); - render_pass.draw(0..6, 0..1); - } - - context.queue().submit([encoder.finish()]); - current_texture = Some(output_texture); - } - - current_texture.ok_or_else(|| GpuError::UnknownEffectShader { - shader: "missing-effect-pass".to_string(), - }) -} - -fn pack_effect_uniforms( - pass: &EffectPass, - width: u32, - height: u32, -) -> Result<EffectUniformBuffer, GpuError> { - let shader = pass.shader.as_str(); - let sigma = read_number_uniform(pass, "u_sigma")?; - let step = read_number_uniform(pass, "u_step")?; - let direction = read_vec2_uniform(pass, "u_direction")?; - - for uniform in pass.uniforms.keys() { - if uniform == "u_sigma" || uniform == "u_step" || uniform == "u_direction" { - continue; - } - return Err(GpuError::UnsupportedUniform { - shader: shader.to_string(), - uniform: uniform.clone(), - }); - } - - Ok(EffectUniformBuffer { - resolution: [width as f32, height as f32], - direction, - scalars: [sigma, step, 0.0, 0.0], - }) -} - -fn read_number_uniform(pass: &EffectPass, uniform: &str) -> Result<f32, GpuError> { - let Some(value) = pass.uniforms.get(uniform) else { - return Err(GpuError::MissingUniform { - shader: pass.shader.clone(), - uniform: uniform.to_string(), - }); - }; - match value { - UniformValue::Number(value) => Ok(*value), - UniformValue::Vector(_) => Err(GpuError::InvalidNumberUniform { - shader: pass.shader.clone(), - uniform: uniform.to_string(), - }), - } -} - -fn read_vec2_uniform(pass: &EffectPass, uniform: &str) -> Result<[f32; 2], GpuError> { - let Some(value) = pass.uniforms.get(uniform) else { - return Err(GpuError::MissingUniform { - shader: pass.shader.clone(), - uniform: uniform.to_string(), - }); - }; - let UniformValue::Vector(values) = value else { - return Err(GpuError::InvalidVectorUniform { - shader: pass.shader.clone(), - uniform: uniform.to_string(), - expected_length: 2, - }); - }; - if values.len() != 2 { - return Err(GpuError::InvalidVectorUniform { - shader: pass.shader.clone(), - uniform: uniform.to_string(), - expected_length: 2, - }); - } - Ok([values[0], values[1]]) -} diff --git a/rust/crates/gpu/src/lib.rs b/rust/crates/gpu/src/lib.rs index c53e63ab..b6ca6c1c 100644 --- a/rust/crates/gpu/src/lib.rs +++ b/rust/crates/gpu/src/lib.rs @@ -1,17 +1,12 @@ mod context; -mod effect_pipeline; -mod mask_feather; -mod sdf_pipeline; -mod shader_registry; use thiserror::Error; -pub use wgpu; pub use context::GpuContext; -pub use effect_pipeline::{ApplyEffectsOptions, EffectPass, UniformValue}; -pub use mask_feather::ApplyMaskFeatherOptions; +pub use wgpu; pub const GPU_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm; +pub const FULLSCREEN_SHADER_SOURCE: &str = include_str!("shaders/fullscreen.wgsl"); #[derive(Debug, Error)] pub enum GpuError { @@ -23,18 +18,4 @@ pub enum GpuError { CreateSurface(#[from] wgpu::CreateSurfaceError), #[error("The output surface does not support the required texture format")] UnsupportedSurfaceFormat, - #[error("Unknown effect shader '{shader}'")] - UnknownEffectShader { shader: String }, - #[error("Missing uniform '{uniform}' for shader '{shader}'")] - MissingUniform { shader: String, uniform: String }, - #[error("Uniform '{uniform}' for shader '{shader}' must be a number")] - InvalidNumberUniform { shader: String, uniform: String }, - #[error("Uniform '{uniform}' for shader '{shader}' must be a vector of length {expected_length}")] - InvalidVectorUniform { - shader: String, - uniform: String, - expected_length: usize, - }, - #[error("Shader '{shader}' does not support uniform '{uniform}'")] - UnsupportedUniform { shader: String, uniform: String }, } diff --git a/rust/crates/gpu/src/mask_feather.rs b/rust/crates/gpu/src/mask_feather.rs deleted file mode 100644 index 029b59ed..00000000 --- a/rust/crates/gpu/src/mask_feather.rs +++ /dev/null @@ -1,259 +0,0 @@ -use bytemuck::{Pod, Zeroable}; -use wgpu::util::DeviceExt; - -use crate::{GPU_TEXTURE_FORMAT, context::GpuContext}; - -const FULLSCREEN_SHADER_SOURCE: &str = include_str!("shaders/fullscreen.wgsl"); -const JFA_DISTANCE_SHADER_SOURCE: &str = include_str!("shaders/jfa_distance.wgsl"); - -pub struct ApplyMaskFeatherOptions<'a> { - pub mask: &'a wgpu::Texture, - pub width: u32, - pub height: u32, - pub feather: f32, -} - -pub struct MaskFeatherPipeline { - inside_texture_bind_group_layout: wgpu::BindGroupLayout, - outside_texture_bind_group_layout: wgpu::BindGroupLayout, - uniform_bind_group_layout: wgpu::BindGroupLayout, - distance_pipeline: wgpu::RenderPipeline, -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct DistanceUniformBuffer { - resolution: [f32; 2], - feather_half: f32, - _padding: f32, -} - -impl MaskFeatherPipeline { - pub fn new(device: &wgpu::Device) -> Self { - let inside_texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-mask-distance-inside-layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let outside_texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-mask-distance-outside-layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let uniform_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-mask-distance-uniform-layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("gpu-mask-distance-pipeline-layout"), - bind_group_layouts: &[ - Some(&inside_texture_bind_group_layout), - Some(&outside_texture_bind_group_layout), - Some(&uniform_bind_group_layout), - ], - immediate_size: 0, - }); - let vertex_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-mask-distance-fullscreen-shader"), - source: wgpu::ShaderSource::Wgsl(FULLSCREEN_SHADER_SOURCE.into()), - }); - let fragment_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-mask-distance-fragment-shader"), - source: wgpu::ShaderSource::Wgsl(JFA_DISTANCE_SHADER_SOURCE.into()), - }); - let distance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("gpu-mask-distance-pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &vertex_shader_module, - entry_point: Some("vertex_main"), - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::<[f32; 2]>() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }], - }], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &fragment_shader_module, - entry_point: Some("fragment_main"), - targets: &[Some(wgpu::ColorTargetState { - format: GPU_TEXTURE_FORMAT, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview_mask: None, - cache: None, - }); - - Self { - inside_texture_bind_group_layout, - outside_texture_bind_group_layout, - uniform_bind_group_layout, - distance_pipeline, - } - } - - pub fn apply_mask_feather( - &self, - context: &GpuContext, - ApplyMaskFeatherOptions { - mask, - width, - height, - feather, - }: ApplyMaskFeatherOptions<'_>, - ) -> wgpu::Texture { - let sdf = context - .sdf_pipeline() - .compute_signed_distance_field(context, mask, width, height); - let output_texture = - context.create_render_texture(width, height, "gpu-mask-feather-output"); - let inside_view = sdf - .inside_texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let outside_view = sdf - .outside_texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let inside_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-mask-distance-inside-bind-group"), - layout: &self.inside_texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&inside_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(context.nearest_sampler()), - }, - ], - }); - let outside_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-mask-distance-outside-bind-group"), - layout: &self.outside_texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&outside_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(context.nearest_sampler()), - }, - ], - }); - let uniform_buffer = context - .device() - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("gpu-mask-distance-uniform-buffer"), - contents: bytemuck::bytes_of(&DistanceUniformBuffer { - resolution: [width as f32, height as f32], - feather_half: feather / 2.0, - _padding: 0.0, - }), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - let uniform_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-mask-distance-uniform-bind-group"), - layout: &self.uniform_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: uniform_buffer.as_entire_binding(), - }], - }); - let mut encoder = context - .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("gpu-mask-distance-command-encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("gpu-mask-distance-render-pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &output_view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); - render_pass.set_pipeline(&self.distance_pipeline); - render_pass.set_vertex_buffer(0, context.fullscreen_quad().slice(..)); - render_pass.set_bind_group(0, &inside_bind_group, &[]); - render_pass.set_bind_group(1, &outside_bind_group, &[]); - render_pass.set_bind_group(2, &uniform_bind_group, &[]); - render_pass.draw(0..6, 0..1); - } - - context.queue().submit([encoder.finish()]); - output_texture - } -} diff --git a/rust/crates/gpu/src/sdf_pipeline.rs b/rust/crates/gpu/src/sdf_pipeline.rs deleted file mode 100644 index d2612fe1..00000000 --- a/rust/crates/gpu/src/sdf_pipeline.rs +++ /dev/null @@ -1,307 +0,0 @@ -use bytemuck::{Pod, Zeroable}; -use wgpu::util::DeviceExt; - -use crate::{GPU_TEXTURE_FORMAT, context::GpuContext}; - -const FULLSCREEN_SHADER_SOURCE: &str = include_str!("shaders/fullscreen.wgsl"); -const JFA_INIT_SHADER_SOURCE: &str = include_str!("shaders/jfa_init.wgsl"); -const JFA_STEP_SHADER_SOURCE: &str = include_str!("shaders/jfa_step.wgsl"); - -pub struct SignedDistanceFieldTextures { - pub inside_texture: wgpu::Texture, - pub outside_texture: wgpu::Texture, -} - -pub struct SdfPipeline { - texture_bind_group_layout: wgpu::BindGroupLayout, - uniform_bind_group_layout: wgpu::BindGroupLayout, - init_pipeline: wgpu::RenderPipeline, - step_pipeline: wgpu::RenderPipeline, -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct JfaInitUniformBuffer { - resolution: [f32; 2], - invert: f32, - _padding: f32, -} - -#[repr(C)] -#[derive(Clone, Copy, Pod, Zeroable)] -struct JfaStepUniformBuffer { - resolution: [f32; 2], - step_size: f32, - _padding: f32, -} - -impl SdfPipeline { - pub fn new(device: &wgpu::Device) -> Self { - let texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-sdf-texture-bind-group-layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let uniform_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-sdf-uniform-bind-group-layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("gpu-sdf-pipeline-layout"), - bind_group_layouts: &[ - Some(&texture_bind_group_layout), - Some(&uniform_bind_group_layout), - ], - immediate_size: 0, - }); - let vertex_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-sdf-fullscreen-shader"), - source: wgpu::ShaderSource::Wgsl(FULLSCREEN_SHADER_SOURCE.into()), - }); - let init_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-jfa-init-shader"), - source: wgpu::ShaderSource::Wgsl(JFA_INIT_SHADER_SOURCE.into()), - }); - let step_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-jfa-step-shader"), - source: wgpu::ShaderSource::Wgsl(JFA_STEP_SHADER_SOURCE.into()), - }); - let init_pipeline = create_pipeline( - device, - "gpu-jfa-init-pipeline", - &pipeline_layout, - &vertex_shader_module, - &init_shader_module, - ); - let step_pipeline = create_pipeline( - device, - "gpu-jfa-step-pipeline", - &pipeline_layout, - &vertex_shader_module, - &step_shader_module, - ); - - Self { - texture_bind_group_layout, - uniform_bind_group_layout, - init_pipeline, - step_pipeline, - } - } - - pub fn compute_signed_distance_field( - &self, - context: &GpuContext, - source_texture: &wgpu::Texture, - width: u32, - height: u32, - ) -> SignedDistanceFieldTextures { - SignedDistanceFieldTextures { - inside_texture: self.run_jfa(context, source_texture, width, height, false), - outside_texture: self.run_jfa(context, source_texture, width, height, true), - } - } - - fn run_jfa( - &self, - context: &GpuContext, - source_texture: &wgpu::Texture, - width: u32, - height: u32, - is_inverted: bool, - ) -> wgpu::Texture { - let ping_texture = context.create_render_texture(width, height, "gpu-jfa-ping-texture"); - let pong_texture = context.create_render_texture(width, height, "gpu-jfa-pong-texture"); - - self.run_pass( - context, - source_texture, - &ping_texture, - &self.init_pipeline, - bytemuck::bytes_of(&JfaInitUniformBuffer { - resolution: [width as f32, height as f32], - invert: if is_inverted { 1.0 } else { 0.0 }, - _padding: 0.0, - }), - ); - - let mut source_is_ping = true; - let steps = (width.max(height) as f32).log2().ceil() as u32; - for step_index in (0..steps).rev() { - let step_size = 2u32.pow(step_index).max(1); - let input_texture = if source_is_ping { - &ping_texture - } else { - &pong_texture - }; - let output_texture = if source_is_ping { - &pong_texture - } else { - &ping_texture - }; - self.run_pass( - context, - input_texture, - output_texture, - &self.step_pipeline, - bytemuck::bytes_of(&JfaStepUniformBuffer { - resolution: [width as f32, height as f32], - step_size: step_size as f32, - _padding: 0.0, - }), - ); - source_is_ping = !source_is_ping; - } - - if source_is_ping { - ping_texture - } else { - pong_texture - } - } - - fn run_pass( - &self, - context: &GpuContext, - input_texture: &wgpu::Texture, - output_texture: &wgpu::Texture, - pipeline: &wgpu::RenderPipeline, - uniform_buffer_bytes: &[u8], - ) { - let input_view = input_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let output_view = output_texture.create_view(&wgpu::TextureViewDescriptor::default()); - let texture_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-sdf-texture-bind-group"), - layout: &self.texture_bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&input_view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(context.nearest_sampler()), - }, - ], - }); - let uniform_buffer = context - .device() - .create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("gpu-sdf-uniform-buffer"), - contents: uniform_buffer_bytes, - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - let uniform_bind_group = context - .device() - .create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("gpu-sdf-uniform-bind-group"), - layout: &self.uniform_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: uniform_buffer.as_entire_binding(), - }], - }); - let mut encoder = context - .device() - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("gpu-sdf-command-encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("gpu-sdf-render-pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &output_view, - resolve_target: None, - depth_slice: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::WHITE), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - occlusion_query_set: None, - timestamp_writes: None, - multiview_mask: None, - }); - render_pass.set_pipeline(pipeline); - render_pass.set_vertex_buffer(0, context.fullscreen_quad().slice(..)); - render_pass.set_bind_group(0, &texture_bind_group, &[]); - render_pass.set_bind_group(1, &uniform_bind_group, &[]); - render_pass.draw(0..6, 0..1); - } - - context.queue().submit([encoder.finish()]); - } -} - -fn create_pipeline( - device: &wgpu::Device, - label: &'static str, - layout: &wgpu::PipelineLayout, - vertex_shader_module: &wgpu::ShaderModule, - fragment_shader_module: &wgpu::ShaderModule, -) -> wgpu::RenderPipeline { - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some(label), - layout: Some(layout), - vertex: wgpu::VertexState { - module: vertex_shader_module, - entry_point: Some("vertex_main"), - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::<[f32; 2]>() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }], - }], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: fragment_shader_module, - entry_point: Some("fragment_main"), - targets: &[Some(wgpu::ColorTargetState { - format: GPU_TEXTURE_FORMAT, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview_mask: None, - cache: None, - }) -} diff --git a/rust/crates/gpu/src/shader_registry.rs b/rust/crates/gpu/src/shader_registry.rs deleted file mode 100644 index b6af173e..00000000 --- a/rust/crates/gpu/src/shader_registry.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::collections::HashMap; - -use crate::{GPU_TEXTURE_FORMAT, GpuError}; - -const FULLSCREEN_SHADER_SOURCE: &str = include_str!("shaders/fullscreen.wgsl"); -const GAUSSIAN_BLUR_SHADER_SOURCE: &str = include_str!("shaders/gaussian_blur.wgsl"); -const BLIT_SHADER_SOURCE: &str = include_str!("shaders/blit.wgsl"); -pub const GAUSSIAN_BLUR_SHADER_ID: &str = "gaussian-blur"; - -pub struct ShaderRegistry { - effect_texture_bind_group_layout: wgpu::BindGroupLayout, - effect_uniform_bind_group_layout: wgpu::BindGroupLayout, - effect_pipelines: HashMap<String, wgpu::RenderPipeline>, - blit_pipeline: wgpu::RenderPipeline, -} - -impl ShaderRegistry { - pub fn new(device: &wgpu::Device) -> Self { - let effect_texture_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-effect-texture-bind-group-layout"), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Texture { - multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), - count: None, - }, - ], - }); - let effect_uniform_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: Some("gpu-effect-uniform-bind-group-layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - }); - - let vertex_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-fullscreen-shader"), - source: wgpu::ShaderSource::Wgsl(FULLSCREEN_SHADER_SOURCE.into()), - }); - let gaussian_blur_shader_module = - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-gaussian-blur-shader"), - source: wgpu::ShaderSource::Wgsl(GAUSSIAN_BLUR_SHADER_SOURCE.into()), - }); - let blit_shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("gpu-blit-shader"), - source: wgpu::ShaderSource::Wgsl(BLIT_SHADER_SOURCE.into()), - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("gpu-effect-pipeline-layout"), - bind_group_layouts: &[ - Some(&effect_texture_bind_group_layout), - Some(&effect_uniform_bind_group_layout), - ], - immediate_size: 0, - }); - let blit_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("gpu-blit-pipeline-layout"), - bind_group_layouts: &[Some(&effect_texture_bind_group_layout)], - immediate_size: 0, - }); - let gaussian_blur_pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("gpu-gaussian-blur-pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &vertex_shader_module, - entry_point: Some("vertex_main"), - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::<[f32; 2]>() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }], - }], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &gaussian_blur_shader_module, - entry_point: Some("fragment_main"), - targets: &[Some(wgpu::ColorTargetState { - format: GPU_TEXTURE_FORMAT, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview_mask: None, - cache: None, - }); - let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("gpu-blit-pipeline"), - layout: Some(&blit_pipeline_layout), - vertex: wgpu::VertexState { - module: &vertex_shader_module, - entry_point: Some("vertex_main"), - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::<[f32; 2]>() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - format: wgpu::VertexFormat::Float32x2, - offset: 0, - shader_location: 0, - }], - }], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &blit_shader_module, - entry_point: Some("fragment_main"), - targets: &[Some(wgpu::ColorTargetState { - format: GPU_TEXTURE_FORMAT, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview_mask: None, - cache: None, - }); - - let effect_pipelines = HashMap::from([( - GAUSSIAN_BLUR_SHADER_ID.to_string(), - gaussian_blur_pipeline, - )]); - - Self { - effect_texture_bind_group_layout, - effect_uniform_bind_group_layout, - effect_pipelines, - blit_pipeline, - } - } - - pub fn get_effect_pipeline(&self, shader: &str) -> Result<&wgpu::RenderPipeline, GpuError> { - self.effect_pipelines - .get(shader) - .ok_or_else(|| GpuError::UnknownEffectShader { - shader: shader.to_string(), - }) - } - - pub fn effect_texture_bind_group_layout(&self) -> &wgpu::BindGroupLayout { - &self.effect_texture_bind_group_layout - } - - pub fn effect_uniform_bind_group_layout(&self) -> &wgpu::BindGroupLayout { - &self.effect_uniform_bind_group_layout - } - - pub fn blit_pipeline(&self) -> &wgpu::RenderPipeline { - &self.blit_pipeline - } -} diff --git a/rust/crates/gpu/src/shaders/gaussian_blur.wgsl b/rust/crates/gpu/src/shaders/gaussian_blur.wgsl deleted file mode 100644 index 6c8d311d..00000000 --- a/rust/crates/gpu/src/shaders/gaussian_blur.wgsl +++ /dev/null @@ -1,34 +0,0 @@ -struct VertexOutput { - @builtin(position) position: vec4f, - @location(0) tex_coord: vec2f, -} - -struct EffectUniforms { - resolution: vec2f, - direction: vec2f, - scalars: vec4f, -} - -@group(0) @binding(0) var input_texture: texture_2d<f32>; -@group(0) @binding(1) var input_sampler: sampler; -@group(1) @binding(0) var<uniform> uniforms: EffectUniforms; - -@fragment -fn fragment_main(input: VertexOutput) -> @location(0) vec4f { - let texel_size = vec2f(1.0, 1.0) / uniforms.resolution; - let sigma = uniforms.scalars.x; - let step_size = uniforms.scalars.y; - - var color = vec4f(0.0, 0.0, 0.0, 0.0); - var total_weight = 0.0; - - for (var index = -30; index <= 30; index = index + 1) { - let position = f32(index) * step_size; - let weight = exp(-(position * position) / (2.0 * sigma * sigma)); - let sample_uv = input.tex_coord + (texel_size * uniforms.direction * position); - color = color + textureSample(input_texture, input_sampler, sample_uv) * weight; - total_weight = total_weight + weight; - } - - return color / total_weight; -} diff --git a/rust/crates/gpu/src/shaders/jfa_distance.wgsl b/rust/crates/gpu/src/shaders/jfa_distance.wgsl deleted file mode 100644 index e65a7023..00000000 --- a/rust/crates/gpu/src/shaders/jfa_distance.wgsl +++ /dev/null @@ -1,50 +0,0 @@ -struct VertexOutput { - @builtin(position) position: vec4f, - @location(0) tex_coord: vec2f, -} - -struct DistanceUniforms { - resolution: vec2f, - feather_half: f32, - _padding: f32, -} - -@group(0) @binding(0) var inside_texture: texture_2d<f32>; -@group(0) @binding(1) var inside_sampler: sampler; -@group(1) @binding(0) var outside_texture: texture_2d<f32>; -@group(1) @binding(1) var outside_sampler: sampler; -@group(2) @binding(0) var<uniform> uniforms: DistanceUniforms; - -fn decode_seed(encoded: vec4f) -> vec2f { - let x = floor(encoded.r * 255.0 + 0.5) * 256.0 + floor(encoded.g * 255.0 + 0.5); - let y = floor(encoded.b * 255.0 + 0.5) * 256.0 + floor(encoded.a * 255.0 + 0.5); - return vec2f(x, y); -} - -fn is_no_seed(encoded: vec4f) -> bool { - return encoded.r > 0.99 && encoded.g > 0.99 && encoded.b > 0.99 && encoded.a > 0.99; -} - -@fragment -fn fragment_main(input: VertexOutput) -> @location(0) vec4f { - let pixel_coord = floor(input.tex_coord * uniforms.resolution); - let inside_encoded = textureSample(inside_texture, inside_sampler, input.tex_coord); - let outside_encoded = textureSample(outside_texture, outside_sampler, input.tex_coord); - - let has_inside = !is_no_seed(inside_encoded); - let has_outside = !is_no_seed(outside_encoded); - let distance_to_inside = select( - 100000.0, - distance(pixel_coord, decode_seed(inside_encoded)), - has_inside, - ); - let distance_to_outside = select( - 100000.0, - distance(pixel_coord, decode_seed(outside_encoded)), - has_outside, - ); - let signed_distance = distance_to_outside - distance_to_inside; - let alpha = smoothstep(-uniforms.feather_half, uniforms.feather_half, signed_distance); - - return vec4f(alpha, alpha, alpha, alpha); -} diff --git a/rust/crates/gpu/src/shaders/jfa_init.wgsl b/rust/crates/gpu/src/shaders/jfa_init.wgsl deleted file mode 100644 index 29aa38c7..00000000 --- a/rust/crates/gpu/src/shaders/jfa_init.wgsl +++ /dev/null @@ -1,35 +0,0 @@ -struct VertexOutput { - @builtin(position) position: vec4f, - @location(0) tex_coord: vec2f, -} - -struct JfaInitUniforms { - resolution: vec2f, - invert: f32, - _padding: f32, -} - -@group(0) @binding(0) var input_texture: texture_2d<f32>; -@group(0) @binding(1) var input_sampler: sampler; -@group(1) @binding(0) var<uniform> uniforms: JfaInitUniforms; - -fn encode_seed(seed: vec2f) -> vec4f { - let x_hi = floor(seed.x / 256.0); - let x_lo = seed.x - (x_hi * 256.0); - let y_hi = floor(seed.y / 256.0); - let y_lo = seed.y - (y_hi * 256.0); - return vec4f(x_hi / 255.0, x_lo / 255.0, y_hi / 255.0, y_lo / 255.0); -} - -@fragment -fn fragment_main(input: VertexOutput) -> @location(0) vec4f { - let mask = textureSample(input_texture, input_sampler, input.tex_coord).r; - let is_seed = select(mask > 0.5, mask < 0.5, uniforms.invert > 0.5); - - if (is_seed) { - let pixel_coord = floor(input.tex_coord * uniforms.resolution); - return encode_seed(pixel_coord); - } - - return vec4f(1.0, 1.0, 1.0, 1.0); -} diff --git a/rust/crates/gpu/src/shaders/jfa_step.wgsl b/rust/crates/gpu/src/shaders/jfa_step.wgsl deleted file mode 100644 index e20d7fb0..00000000 --- a/rust/crates/gpu/src/shaders/jfa_step.wgsl +++ /dev/null @@ -1,75 +0,0 @@ -struct VertexOutput { - @builtin(position) position: vec4f, - @location(0) tex_coord: vec2f, -} - -struct JfaStepUniforms { - resolution: vec2f, - step_size: f32, - _padding: f32, -} - -@group(0) @binding(0) var input_texture: texture_2d<f32>; -@group(0) @binding(1) var input_sampler: sampler; -@group(1) @binding(0) var<uniform> uniforms: JfaStepUniforms; - -fn decode_seed(encoded: vec4f) -> vec2f { - let x = floor(encoded.r * 255.0 + 0.5) * 256.0 + floor(encoded.g * 255.0 + 0.5); - let y = floor(encoded.b * 255.0 + 0.5) * 256.0 + floor(encoded.a * 255.0 + 0.5); - return vec2f(x, y); -} - -fn encode_seed(seed: vec2f) -> vec4f { - let x_hi = floor(seed.x / 256.0); - let x_lo = seed.x - (x_hi * 256.0); - let y_hi = floor(seed.y / 256.0); - let y_lo = seed.y - (y_hi * 256.0); - return vec4f(x_hi / 255.0, x_lo / 255.0, y_hi / 255.0, y_lo / 255.0); -} - -fn is_no_seed(encoded: vec4f) -> bool { - return encoded.r > 0.99 && encoded.g > 0.99 && encoded.b > 0.99 && encoded.a > 0.99; -} - -@fragment -fn fragment_main(input: VertexOutput) -> @location(0) vec4f { - let pixel_coord = floor(input.tex_coord * uniforms.resolution); - let texel_size = vec2f(1.0, 1.0) / uniforms.resolution; - - var best_distance = 10000000000.0; - var best_seed = vec2f(65535.0, 65535.0); - - for (var y = -1; y <= 1; y = y + 1) { - for (var x = -1; x <= 1; x = x + 1) { - let offset = vec2f(f32(x), f32(y)) * uniforms.step_size; - let sample_uv = input.tex_coord + (offset * texel_size); - - if ( - sample_uv.x < 0.0 || - sample_uv.x > 1.0 || - sample_uv.y < 0.0 || - sample_uv.y > 1.0 - ) { - continue; - } - - let encoded = textureSample(input_texture, input_sampler, sample_uv); - if (is_no_seed(encoded)) { - continue; - } - - let seed = decode_seed(encoded); - let distance_to_seed = distance(pixel_coord, seed); - if (distance_to_seed < best_distance) { - best_distance = distance_to_seed; - best_seed = seed; - } - } - } - - if (best_distance < 1000000000.0) { - return encode_seed(best_seed); - } - - return vec4f(1.0, 1.0, 1.0, 1.0); -} |
