summaryrefslogtreecommitdiffstats
path: root/src/renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer.rs')
-rw-r--r--src/renderer.rs160
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),