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, 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 } }