diff options
Diffstat (limited to 'src/renderer.rs')
| -rw-r--r-- | src/renderer.rs | 160 |
1 files changed, 142 insertions, 18 deletions
diff --git a/src/renderer.rs b/src/renderer.rs index 155c318..c6f1973 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,4 +1,11 @@ -use crate::uniform::UniformCache; +use std::error::Error; +use std::str::FromStr; + +use rosc::OscType; +use wesl::{Mangler, ModulePath}; + +use crate::uniform::{UniformCache, UniformError}; +use crate::wesl::ShaderSources; const CANVAS_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; @@ -30,6 +37,24 @@ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { } "; +#[derive(Debug, thiserror::Error)] +pub enum ShaderError { + #[error("error resolving module")] + InvalidModulePath, + + #[error("error setting uniform '{0}': {1}")] + UniformError(String, crate::uniform::UniformError), + + #[error("uniform '{0}' not found")] + UniformNotFound(String), + + #[error("error binding resource to '{0}': {1}")] + BindError(String, crate::uniform::BindError), + + #[error("error compiling shader: {0}")] + CompileError(#[from] wesl::Error), +} + pub struct Renderer { canvas: wgpu::Texture, canvas_view: wgpu::TextureView, @@ -37,6 +62,7 @@ pub struct Renderer { vertex_bgl: wgpu::BindGroupLayout, vertex_bg: wgpu::BindGroup, render_pipeline: wgpu::RenderPipeline, + shader_sources: ShaderSources, uniforms: UniformCache, } @@ -102,11 +128,19 @@ impl Renderer { ); let mut uniforms = UniformCache::new(); + let mut shader_sources = ShaderSources::default(); + shader_sources.set_module(wesl::ModulePath::new_root(), DEFAULT_FRAGMENT); + + let initial_fragment = shader_sources + .compile(&wesl::ModulePath::new_root()) + .expect("default shader"); + log::info!("frag source:\n {initial_fragment}"); + let render_pipeline = build_pipeline( device, &vertex_module, &vertex_bgl, - DEFAULT_FRAGMENT, + &initial_fragment, &mut uniforms, ) .expect("default shader"); @@ -118,6 +152,7 @@ impl Renderer { vertex_bgl, vertex_bg, render_pipeline, + shader_sources, uniforms, } } @@ -126,24 +161,115 @@ impl Renderer { &self.canvas } - pub fn canvas_view(&self) -> &wgpu::TextureView { - &self.canvas_view - } - pub fn uniforms(&mut self) -> &mut UniformCache { &mut self.uniforms } - pub fn load_shader(&mut self, device: &wgpu::Device, source: &str) -> Result<(), String> { - let pipeline = build_pipeline( + pub fn set_uniform( + &mut self, + module: &str, + item: &str, + rest: &[&str], + args: &[OscType], + ) -> Result<(), ShaderError> { + let path = ModulePath::from_str(module).map_err(|_| ShaderError::InvalidModulePath)?; + let name = self.shader_sources.mangler().mangle(&path, item); + + let mut uref = self + .uniforms + .get(&name) + .ok_or(ShaderError::UniformNotFound(name))?; + + let lift_err = |e| ShaderError::UniformError(item.to_string(), e); + + for component in rest.iter() { + uref = uref.field(component).map_err(lift_err)?; + } + + let scalar = uref.leaf_scalar().map_err(lift_err)?; + match scalar.kind { + naga::ScalarKind::Float => args + .iter() + .map(|a| match a { + OscType::Float(f) => Ok(*f), + OscType::Double(d) => Ok(*d as f32), + OscType::Int(i) => Ok(*i as f32), + OscType::Bool(b) => Ok(if *b { 1.0 } else { 0.0 }), + _ => Err(UniformError::TypeMismatch), + }) + .collect::<Result<Vec<_>, _>>() + .and_then(|values| uref.set_f32(&values)), + naga::ScalarKind::Sint => args + .iter() + .map(|a| match a { + OscType::Int(i) => Ok(*i), + OscType::Float(f) => Ok(*f as i32), + OscType::Double(d) => Ok(*d as i32), + OscType::Bool(b) => Ok(if *b { 1 } else { 0 }), + _ => Err(UniformError::TypeMismatch), + }) + .collect::<Result<Vec<_>, _>>() + .and_then(|values| uref.set_i32(&values)), + naga::ScalarKind::Uint => args + .iter() + .map(|a| match a { + OscType::Int(i) => Ok(*i as u32), + OscType::Float(f) => Ok(*f as u32), + OscType::Double(d) => Ok(*d as u32), + OscType::Bool(b) => Ok(if *b { 1 } else { 0 }), + _ => Err(UniformError::TypeMismatch), + }) + .collect::<Result<Vec<_>, _>>() + .and_then(|values| uref.set_u32(&values)), + _ => Err(UniformError::TypeMismatch), + } + .map_err(lift_err) + } + + pub fn set_binding( + &mut self, + device: &wgpu::Device, + module: &str, + item: &str, + target: &str, + ) -> Result<(), ShaderError> { + let path = ModulePath::from_str(module).map_err(|_| ShaderError::InvalidModulePath)?; + let name = self.shader_sources.mangler().mangle(&path, item); + + if let Some(id) = target.strip_prefix("/texture/") { + self.uniforms.bind_texture(device, &name, id) + } else if let Some(id) = target.strip_prefix("/sampler/") { + self.uniforms.bind_sampler(device, &name, id) + } else { + Err(crate::uniform::BindError::ResourceNotFound( + target.to_string(), + )) + } + .map_err(|e| ShaderError::BindError(name, e)) + } + + pub fn load_module(&mut self, module_name: &str, source: &str) -> Result<(), ShaderError> { + let path = ModulePath::from_str(module_name).map_err(|_| ShaderError::InvalidModulePath)?; + self.shader_sources.set_module(path, source); + Ok(()) + } + + pub fn compile( + &mut self, + device: &wgpu::Device, + module_name: &str, + ) -> Result<(), Box<dyn Error>> { + let path = ModulePath::from_str(module_name).map_err(|_| ShaderError::InvalidModulePath)?; + let fragment_source = self.shader_sources.compile(&path)?; + log::info!("frag source:\n {fragment_source}"); + + self.render_pipeline = build_pipeline( device, &self.vertex_module, &self.vertex_bgl, - source, + &fragment_source, &mut self.uniforms, )?; - self.render_pipeline = pipeline; - log::info!("shader loaded"); Ok(()) } @@ -189,16 +315,14 @@ fn build_pipeline( vertex_bgl: &wgpu::BindGroupLayout, fragment_source: &str, cache: &mut UniformCache, -) -> Result<wgpu::RenderPipeline, String> { - let module = naga::front::wgsl::parse_str(fragment_source) - .map_err(|e| format!("WGSL parse error: {e}"))?; +) -> Result<wgpu::RenderPipeline, Box<dyn Error>> { + let module = naga::front::wgsl::parse_str(fragment_source)?; naga::valid::Validator::new( naga::valid::ValidationFlags::all(), naga::valid::Capabilities::all(), ) - .validate(&module) - .map_err(|e| format!("WGSL validation error: {e}"))?; + .validate(&module)?; cache.refresh(module, device); cache.rebuild_bind_group(device); @@ -225,13 +349,13 @@ fn build_pipeline( layout: Some(&pipeline_layout), vertex: wgpu::VertexState { module: vertex_module, - entry_point: Some("vs_main"), + entry_point: None, buffers: &[], compilation_options: Default::default(), }, fragment: Some(wgpu::FragmentState { module: &fragment_module, - entry_point: Some("fs_main"), + entry_point: None, targets: &[Some(wgpu::ColorTargetState { format: CANVAS_FORMAT, blend: Some(wgpu::BlendState::REPLACE), |
