From 788f71822cacb736e6a95cabb4b29ee847f90e20 Mon Sep 17 00:00:00 2001 From: charlotte Date: Fri, 20 Jun 2025 17:06:33 -0700 Subject: [PATCH 1/7] Add support for wgsl directives. --- src/compose/error.rs | 22 ++- src/compose/mod.rs | 46 +++++-- src/compose/preprocess.rs | 153 +++++++++++++++++++++ src/compose/wgsl_directives.rs | 240 +++++++++++++++++++++++++++++++++ 4 files changed, 450 insertions(+), 11 deletions(-) create mode 100644 src/compose/wgsl_directives.rs diff --git a/src/compose/error.rs b/src/compose/error.rs index 8292d25..04fdce3 100644 --- a/src/compose/error.rs +++ b/src/compose/error.rs @@ -134,6 +134,18 @@ pub enum ComposerErrorInner { }, #[error("#define statements are only allowed at the start of the top-level shaders")] DefineInModule(usize), + #[error("Invalid WGSL directive '{directive}' at line {position}: {reason}")] + InvalidWgslDirective { + directive: String, + position: usize, + reason: String, + }, + #[error("Conflicting diagnostic directives for rule '{rule}': existing={existing_severity}, new={new_severity}")] + ConflictingDirectives { + rule: String, + existing_severity: String, + new_severity: String, + }, } struct ErrorSources<'a> { @@ -239,7 +251,8 @@ impl ComposerError { | ComposerErrorInner::OverrideNotVirtual { pos, .. } | ComposerErrorInner::GlslInvalidVersion(pos) | ComposerErrorInner::DefineInModule(pos) - | ComposerErrorInner::InvalidShaderDefDefinitionValue { pos, .. } => { + | ComposerErrorInner::InvalidShaderDefDefinitionValue { pos, .. } + | ComposerErrorInner::InvalidWgslDirective { position: pos, .. } => { (vec![Label::primary((), *pos..*pos)], vec![]) } ComposerErrorInner::WgslBackError(e) => { @@ -252,6 +265,13 @@ impl ComposerError { ComposerErrorInner::InconsistentShaderDefValue { def } => { return format!("{path}: multiple inconsistent shader def values: '{def}'"); } + ComposerErrorInner::ConflictingDirectives { + rule, + existing_severity, + new_severity, + } => { + return format!("{path}: conflicting diagnostic directives for rule '{rule}': existing={existing_severity}, new={new_severity}"); + } ComposerErrorInner::RedirectError(..) => ( vec![Label::primary((), 0..0)], vec![format!("override error")], diff --git a/src/compose/mod.rs b/src/compose/mod.rs index c1203b7..a9004a2 100644 --- a/src/compose/mod.rs +++ b/src/compose/mod.rs @@ -130,7 +130,7 @@ use indexmap::IndexMap; use naga::EntryPoint; use regex::Regex; use std::collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}; -use tracing::{debug, trace}; +use tracing::{debug, info, trace}; use crate::{ compose::preprocess::{PreprocessOutput, PreprocessorMetaData}, @@ -140,6 +140,9 @@ use crate::{ pub use self::error::{ComposerError, ComposerErrorInner, ErrSource}; use self::preprocess::Preprocessor; +pub use self::wgsl_directives::{ + DiagnosticDirective, EnableDirective, RequiresDirective, WgslDirectives, +}; pub mod comment_strip_iter; pub mod error; @@ -147,6 +150,7 @@ pub mod parse_imports; pub mod preprocess; mod test; pub mod tokenizer; +pub mod wgsl_directives; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Default)] pub enum ShaderLanguage { @@ -270,8 +274,6 @@ pub struct ComposableModuleDefinition { modules: HashMap, // used in spans when this module is included module_index: usize, - // preprocessor meta data - // metadata: PreprocessorMetaData, } impl ComposableModuleDefinition { @@ -580,11 +582,17 @@ impl Composer { language: ShaderLanguage, imports: &[ImportDefinition], shader_defs: &HashMap, + wgsl_directives: Option, ) -> Result { debug!("creating IR for {} with defs: {:?}", name, shader_defs); + let mut wgsl_string = String::new(); + if let Some(wgsl_directives) = &wgsl_directives { + info!("adding WGSL directives for {}", name); + wgsl_string = wgsl_directives.to_wgsl_string(); + } let mut module_string = match language { - ShaderLanguage::Wgsl => String::new(), + ShaderLanguage::Wgsl => wgsl_string, #[cfg(feature = "glsl")] ShaderLanguage::Glsl => String::from("#version 450\n"), }; @@ -657,6 +665,13 @@ impl Composer { start_offset, module_string.len() ); + info!( + "parsing {}: {}, header len {}, total len {}", + name, + module_string, + start_offset, + module_string.len() + ); let module = match language { ShaderLanguage::Wgsl => naga::front::wgsl::parse_str(&module_string).map_err(|e| { debug!("full err'd source file: \n---\n{}\n---", module_string); @@ -842,6 +857,7 @@ impl Composer { demote_entrypoints: bool, source: &str, imports: Vec, + wgsl_directives: Option, ) -> Result { let mut imports: Vec<_> = imports .into_iter() @@ -975,6 +991,7 @@ impl Composer { module_definition.language, &imports, shader_defs, + wgsl_directives, )?; // from here on errors need to be reported using the modified source with start_offset @@ -1376,6 +1393,7 @@ impl Composer { true, &preprocessed_source, imports, + None, ) .map_err(|err| err.into()) } @@ -1519,6 +1537,7 @@ impl Composer { name: module_name, mut imports, mut effective_defs, + cleaned_source, .. } = self .preprocessor @@ -1595,7 +1614,7 @@ impl Composer { let module_set = ComposableModuleDefinition { name: module_name.clone(), - sanitized_source: substituted_source, + sanitized_source: cleaned_source, file_path: file_path.to_owned(), language, effective_defs: effective_defs.into_iter().collect(), @@ -1650,7 +1669,13 @@ impl Composer { let sanitized_source = self.sanitize_and_set_auto_bindings(source); - let PreprocessorMetaData { name, defines, .. } = self + let PreprocessorMetaData { + name, + defines, + wgsl_directives, + cleaned_source, + .. + } = self .preprocessor .get_preprocessor_metadata(&sanitized_source, true) .map_err(|inner| ComposerError { @@ -1667,7 +1692,7 @@ impl Composer { let PreprocessOutput { imports, .. } = self .preprocessor - .preprocess(&sanitized_source, &shader_defs) + .preprocess(&cleaned_source, &shader_defs) .map_err(|inner| ComposerError { inner, source: ErrSource::Constructing { @@ -1734,7 +1759,7 @@ impl Composer { let definition = ComposableModuleDefinition { name, - sanitized_source: sanitized_source.clone(), + sanitized_source: cleaned_source.clone(), language: shader_type.into(), file_path: file_path.to_owned(), module_index: 0, @@ -1751,7 +1776,7 @@ impl Composer { imports, } = self .preprocessor - .preprocess(&sanitized_source, &shader_defs) + .preprocess(&cleaned_source, &shader_defs) .map_err(|inner| ComposerError { inner, source: ErrSource::Constructing { @@ -1770,6 +1795,7 @@ impl Composer { false, &preprocessed_source, imports, + Some(wgsl_directives), ) .map_err(|e| ComposerError { inner: e.inner, @@ -1855,7 +1881,7 @@ impl Composer { })?; } - // validation + if self.validate { let info = self.create_validator().validate(&naga_module); match info { diff --git a/src/compose/preprocess.rs b/src/compose/preprocess.rs index 1fbed3e..ebedf3c 100644 --- a/src/compose/preprocess.rs +++ b/src/compose/preprocess.rs @@ -6,6 +6,7 @@ use regex::Regex; use super::{ comment_strip_iter::CommentReplaceExt, parse_imports::{parse_imports, substitute_identifiers}, + wgsl_directives::{DiagnosticDirective, EnableDirective, RequiresDirective, WgslDirectives}, ComposerErrorInner, ImportDefWithOffset, ShaderDefValue, }; @@ -22,6 +23,9 @@ pub struct Preprocessor { import_regex: Regex, define_import_path_regex: Regex, define_shader_def_regex: Regex, + enable_regex: Regex, + requires_regex: Regex, + diagnostic_regex: Regex, } impl Default for Preprocessor { @@ -42,6 +46,10 @@ impl Default for Preprocessor { define_import_path_regex: Regex::new(r"^\s*#\s*define_import_path\s+([^\s]+)").unwrap(), define_shader_def_regex: Regex::new(r"^\s*#\s*define\s+([\w|\d|_]+)\s*([-\w|\d]+)?") .unwrap(), + enable_regex: Regex::new(r"^\s*enable\s+([^;]+)\s*;").unwrap(), + requires_regex: Regex::new(r"^\s*requires\s+([^;]+)\s*;").unwrap(), + diagnostic_regex: Regex::new(r"^\s*diagnostic\s*\(\s*([^,]+)\s*,\s*([^)]+)\s*\)\s*;") + .unwrap(), } } } @@ -52,6 +60,8 @@ pub struct PreprocessorMetaData { pub imports: Vec, pub defines: HashMap, pub effective_defs: HashSet, + pub wgsl_directives: WgslDirectives, + pub cleaned_source: String, } enum ScopeLevel { @@ -133,6 +143,145 @@ pub struct PreprocessOutput { } impl Preprocessor { + fn parse_enable_directive( + &self, + line: &str, + line_idx: usize, + ) -> Result, ComposerErrorInner> { + if let Some(cap) = self.enable_regex.captures(line) { + let extensions_str = cap.get(1).unwrap().as_str().trim(); + let extensions: Vec = extensions_str + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + + if extensions.is_empty() { + return Err(ComposerErrorInner::InvalidWgslDirective { + directive: line.to_string(), + position: line_idx, + reason: "No extensions specified".to_string(), + }); + } + + Ok(Some(EnableDirective { + extensions, + source_location: line_idx, + })) + } else { + Ok(None) + } + } + + fn parse_requires_directive( + &self, + line: &str, + line_idx: usize, + ) -> Result, ComposerErrorInner> { + if let Some(cap) = self.requires_regex.captures(line) { + let extensions_str = cap.get(1).unwrap().as_str().trim(); + let extensions: Vec = extensions_str + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(); + + if extensions.is_empty() { + return Err(ComposerErrorInner::InvalidWgslDirective { + directive: line.to_string(), + position: line_idx, + reason: "No extensions specified".to_string(), + }); + } + + Ok(Some(RequiresDirective { + extensions, + source_location: line_idx, + })) + } else { + Ok(None) + } + } + + fn parse_diagnostic_directive( + &self, + line: &str, + line_idx: usize, + ) -> Result, ComposerErrorInner> { + if let Some(cap) = self.diagnostic_regex.captures(line) { + let severity = cap.get(1).unwrap().as_str().trim().to_string(); + let rule = cap.get(2).unwrap().as_str().trim().to_string(); + + match severity.as_str() { + "off" | "info" | "warn" | "error" => {} + _ => { + return Err(ComposerErrorInner::InvalidWgslDirective { + directive: line.to_string(), + position: line_idx, + reason: format!( + "Invalid severity '{}'. Must be one of: off, info, warn, error", + severity + ), + }); + } + } + + Ok(Some(DiagnosticDirective { + severity, + rule, + source_location: line_idx, + })) + } else { + Ok(None) + } + } + + pub fn extract_wgsl_directives( + &self, + source: &str, + ) -> Result<(String, WgslDirectives), ComposerErrorInner> { + let mut directives = WgslDirectives::default(); + let mut cleaned_lines = Vec::new(); + let mut in_directive_section = true; + + for (line_idx, line) in source.lines().enumerate() { + let trimmed = line.trim(); + + if trimmed.is_empty() || trimmed.starts_with("//") { + cleaned_lines.push(line); + continue; + } + + if in_directive_section { + if let Some(enable) = self.parse_enable_directive(trimmed, line_idx)? { + cleaned_lines.push(""); + directives.enables.push(enable); + continue; + } else if let Some(requires) = self.parse_requires_directive(trimmed, line_idx)? { + cleaned_lines.push(""); + directives.requires.push(requires); + continue; + } else if let Some(diagnostic) = + self.parse_diagnostic_directive(trimmed, line_idx)? + { + cleaned_lines.push(""); + directives.diagnostics.push(diagnostic); + continue; + } else if !trimmed.starts_with("enable") + && !trimmed.starts_with("requires") + && !trimmed.starts_with("diagnostic") + { + in_directive_section = false; + } + } + + cleaned_lines.push(line); + } + + let cleaned_source = cleaned_lines.join("\n"); + Ok((cleaned_source, directives)) + } + fn check_scope<'a>( &self, shader_defs: &HashMap, @@ -379,6 +528,8 @@ impl Preprocessor { shader_str: &str, allow_defines: bool, ) -> Result { + let (shader_str, wgsl_directives) = self.extract_wgsl_directives(shader_str)?; + let mut declared_imports = IndexMap::default(); let mut used_imports = IndexMap::default(); let mut name = None; @@ -477,6 +628,8 @@ impl Preprocessor { imports: used_imports.into_values().collect(), defines, effective_defs, + wgsl_directives, + cleaned_source: shader_str.to_string(), }) } } diff --git a/src/compose/wgsl_directives.rs b/src/compose/wgsl_directives.rs new file mode 100644 index 0000000..74c1414 --- /dev/null +++ b/src/compose/wgsl_directives.rs @@ -0,0 +1,240 @@ +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableDirective { + pub extensions: Vec, + pub source_location: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RequiresDirective { + pub extensions: Vec, + pub source_location: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DiagnosticDirective { + pub severity: String, + pub rule: String, + pub source_location: usize, +} + +#[derive(Debug, Clone, Default)] +pub struct WgslDirectives { + pub enables: Vec, + pub requires: Vec, + pub diagnostics: Vec, +} + +impl WgslDirectives { + pub fn merge_with(&mut self, other: &WgslDirectives) -> Result<(), super::ComposerErrorInner> { + for enable in &other.enables { + for ext in &enable.extensions { + if !self.has_enable_extension(ext) { + self.enables.push(enable.clone()); + break; + } + } + } + + for requires in &other.requires { + for ext in &requires.extensions { + if !self.has_requires_extension(ext) { + self.requires.push(requires.clone()); + break; + } + } + } + + for diagnostic in &other.diagnostics { + if let Some(existing) = self.diagnostics.iter().find(|d| d.rule == diagnostic.rule) { + if existing.severity != diagnostic.severity { + return Err(super::ComposerErrorInner::ConflictingDirectives { + rule: diagnostic.rule.clone(), + existing_severity: existing.severity.clone(), + new_severity: diagnostic.severity.clone(), + }); + } + } else { + self.diagnostics.push(diagnostic.clone()); + } + } + + Ok(()) + } + + fn has_enable_extension(&self, extension: &str) -> bool { + self.enables + .iter() + .any(|enable| enable.extensions.contains(&extension.to_string())) + } + + fn has_requires_extension(&self, extension: &str) -> bool { + self.requires + .iter() + .any(|requires| requires.extensions.contains(&extension.to_string())) + } + + pub fn to_wgsl_string(&self) -> String { + let mut result = String::new(); + + let mut all_enables = HashSet::new(); + for enable in &self.enables { + all_enables.extend(enable.extensions.iter().cloned()); + } + if !all_enables.is_empty() { + let mut enables: Vec<_> = all_enables.into_iter().collect(); + enables.sort(); + result.push_str(&format!("enable {};\n", enables.join(", "))); + } + + let mut all_requires = HashSet::new(); + for requires in &self.requires { + all_requires.extend(requires.extensions.iter().cloned()); + } + if !all_requires.is_empty() { + let mut requires: Vec<_> = all_requires.into_iter().collect(); + requires.sort(); + result.push_str(&format!("requires {};\n", requires.join(", "))); + } + + for diagnostic in &self.diagnostics { + result.push_str(&format!( + "diagnostic({}, {});\n", + diagnostic.severity, diagnostic.rule + )); + } + + if !result.is_empty() { + result.push('\n'); // Add blank line after directives + } + + result + } + + pub fn is_empty(&self) -> bool { + self.enables.is_empty() && self.requires.is_empty() && self.diagnostics.is_empty() + } + + pub fn enable_extensions(&self) -> HashSet { + let mut extensions = HashSet::new(); + for enable in &self.enables { + extensions.extend(enable.extensions.iter().cloned()); + } + extensions + } + + pub fn requires_extensions(&self) -> HashSet { + let mut extensions = HashSet::new(); + for requires in &self.requires { + extensions.extend(requires.extensions.iter().cloned()); + } + extensions + } + + pub fn diagnostic_rules(&self) -> HashMap { + let mut rules = HashMap::new(); + for diagnostic in &self.diagnostics { + rules.insert(diagnostic.rule.clone(), diagnostic.severity.clone()); + } + rules + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_wgsl_directives_empty() { + let directives = WgslDirectives::default(); + assert!(directives.is_empty()); + assert_eq!(directives.to_wgsl_string(), ""); + } + + #[test] + fn test_wgsl_directives_to_string() { + let mut directives = WgslDirectives::default(); + directives.enables.push(EnableDirective { + extensions: vec!["f16".to_string(), "subgroups".to_string()], + source_location: 0, + }); + directives.requires.push(RequiresDirective { + extensions: vec!["readonly_and_readwrite_storage_textures".to_string()], + source_location: 0, + }); + directives.diagnostics.push(DiagnosticDirective { + severity: "warn".to_string(), + rule: "derivative_uniformity".to_string(), + source_location: 0, + }); + + let result = directives.to_wgsl_string(); + assert!(result.contains("enable f16, subgroups;")); + assert!(result.contains("requires readonly_and_readwrite_storage_textures;")); + assert!(result.contains("diagnostic(warn, derivative_uniformity);")); + } + + #[test] + fn test_merge_directives_no_conflict() { + let mut base = WgslDirectives::default(); + base.enables.push(EnableDirective { + extensions: vec!["f16".to_string()], + source_location: 0, + }); + + let other = WgslDirectives { + enables: vec![EnableDirective { + extensions: vec!["subgroups".to_string()], + source_location: 0, + }], + requires: vec![RequiresDirective { + extensions: vec!["readonly_and_readwrite_storage_textures".to_string()], + source_location: 0, + }], + diagnostics: vec![DiagnosticDirective { + severity: "warn".to_string(), + rule: "derivative_uniformity".to_string(), + source_location: 0, + }], + }; + + base.merge_with(&other).unwrap(); + + let enables = base.enable_extensions(); + assert!(enables.contains("f16")); + assert!(enables.contains("subgroups")); + + let requires = base.requires_extensions(); + assert!(requires.contains("readonly_and_readwrite_storage_textures")); + + let diagnostics = base.diagnostic_rules(); + assert_eq!( + diagnostics.get("derivative_uniformity"), + Some(&"warn".to_string()) + ); + } + + #[test] + fn test_merge_directives_conflict() { + let mut base = WgslDirectives::default(); + base.diagnostics.push(DiagnosticDirective { + severity: "warn".to_string(), + rule: "derivative_uniformity".to_string(), + source_location: 0, + }); + + let other = WgslDirectives { + enables: vec![], + requires: vec![], + diagnostics: vec![DiagnosticDirective { + severity: "error".to_string(), + rule: "derivative_uniformity".to_string(), + source_location: 0, + }], + }; + + let result = base.merge_with(&other); + assert!(result.is_err()); + } +} From a171478fdd6ff748a3585b4e852bd9f350ff0365 Mon Sep 17 00:00:00 2001 From: charlotte Date: Fri, 20 Jun 2025 17:08:27 -0700 Subject: [PATCH 2/7] Cleanup. --- src/compose/mod.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/compose/mod.rs b/src/compose/mod.rs index a9004a2..f9d3804 100644 --- a/src/compose/mod.rs +++ b/src/compose/mod.rs @@ -665,13 +665,6 @@ impl Composer { start_offset, module_string.len() ); - info!( - "parsing {}: {}, header len {}, total len {}", - name, - module_string, - start_offset, - module_string.len() - ); let module = match language { ShaderLanguage::Wgsl => naga::front::wgsl::parse_str(&module_string).map_err(|e| { debug!("full err'd source file: \n---\n{}\n---", module_string); @@ -1881,7 +1874,7 @@ impl Composer { })?; } - + // validation if self.validate { let info = self.create_validator().validate(&naga_module); match info { From c0fe09e2d356215303ad184d63737544a49e5491 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Fri, 20 Jun 2025 20:32:36 -0400 Subject: [PATCH 3/7] `dual_source_blending` test --- src/compose/test.rs | 28 +++++++++++++++++++ .../tests/dual_source_blending/blending.wgsl | 13 +++++++++ .../expected/wgsl_dual_source_blending.txt | 0 3 files changed, 41 insertions(+) create mode 100644 src/compose/tests/dual_source_blending/blending.wgsl create mode 100644 src/compose/tests/expected/wgsl_dual_source_blending.txt diff --git a/src/compose/test.rs b/src/compose/test.rs index 9985090..407ab15 100644 --- a/src/compose/test.rs +++ b/src/compose/test.rs @@ -5,6 +5,7 @@ mod test { use std::io::Write; use std::{borrow::Cow, collections::HashMap}; + use naga::valid::Capabilities; use wgpu::{ BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BufferDescriptor, BufferUsages, CommandEncoderDescriptor, ComputePassDescriptor, @@ -1557,4 +1558,31 @@ mod test { f32::from_le_bytes(view.try_into().unwrap()) } + + #[test] + fn wgsl_dual_source_blending() { + let mut composer = Composer::default(); + composer.capabilities.set(Capabilities::DUAL_SOURCE_BLENDING, true); + + let module = composer + .make_naga_module(NagaModuleDescriptor { + source: include_str!("tests/dual_source_blending/blending.wgsl"), + file_path: "tests/dual_source_blending/blending.wgsl", + ..Default::default() + }) + .unwrap(); + + let info = composer.create_validator().validate(&module).unwrap(); + let wgsl = naga::back::wgsl::write_string( + &module, + &info, + naga::back::wgsl::WriterFlags::EXPLICIT_TYPES, + ) + .unwrap(); + + let mut f = std::fs::File::create("wgsl_call_entrypoint.txt").unwrap(); + f.write_all(wgsl.as_bytes()).unwrap(); + drop(f); + output_eq!(wgsl, "tests/expected/wgsl_dual_source_blending.txt"); + } } diff --git a/src/compose/tests/dual_source_blending/blending.wgsl b/src/compose/tests/dual_source_blending/blending.wgsl new file mode 100644 index 0000000..ccb0e16 --- /dev/null +++ b/src/compose/tests/dual_source_blending/blending.wgsl @@ -0,0 +1,13 @@ +enable dual_source_blending; + +struct FragmentOutput { + @location(0) @blend_src(0) source0_: vec4, + @location(0) @blend_src(1) source1_: vec4, +} + +@fragment +fn fragment( + @builtin(position) frag_coord: vec4, +) -> FragmentOutput { + return FragmentOutput(vec4(frag_coord, 0.0), vec4(frag_coord, 1.0)); +} \ No newline at end of file diff --git a/src/compose/tests/expected/wgsl_dual_source_blending.txt b/src/compose/tests/expected/wgsl_dual_source_blending.txt new file mode 100644 index 0000000..e69de29 From 2c727fb34cb9d774de05619ecd5dc4ccb40166d3 Mon Sep 17 00:00:00 2001 From: Elabajaba Date: Fri, 20 Jun 2025 20:54:10 -0400 Subject: [PATCH 4/7] whoops --- src/compose/test.rs | 6 +++--- src/compose/tests/dual_source_blending/blending.wgsl | 4 ++-- .../tests/expected/wgsl_dual_source_blending.txt | 10 ++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/compose/test.rs b/src/compose/test.rs index 407ab15..c5abe67 100644 --- a/src/compose/test.rs +++ b/src/compose/test.rs @@ -1580,9 +1580,9 @@ mod test { ) .unwrap(); - let mut f = std::fs::File::create("wgsl_call_entrypoint.txt").unwrap(); - f.write_all(wgsl.as_bytes()).unwrap(); - drop(f); + // let mut f = std::fs::File::create("wgsl_dual_source_blending.txt").unwrap(); + // f.write_all(wgsl.as_bytes()).unwrap(); + // drop(f); output_eq!(wgsl, "tests/expected/wgsl_dual_source_blending.txt"); } } diff --git a/src/compose/tests/dual_source_blending/blending.wgsl b/src/compose/tests/dual_source_blending/blending.wgsl index ccb0e16..7a43569 100644 --- a/src/compose/tests/dual_source_blending/blending.wgsl +++ b/src/compose/tests/dual_source_blending/blending.wgsl @@ -9,5 +9,5 @@ struct FragmentOutput { fn fragment( @builtin(position) frag_coord: vec4, ) -> FragmentOutput { - return FragmentOutput(vec4(frag_coord, 0.0), vec4(frag_coord, 1.0)); -} \ No newline at end of file + return FragmentOutput(frag_coord, frag_coord); +} diff --git a/src/compose/tests/expected/wgsl_dual_source_blending.txt b/src/compose/tests/expected/wgsl_dual_source_blending.txt index e69de29..6058fb8 100644 --- a/src/compose/tests/expected/wgsl_dual_source_blending.txt +++ b/src/compose/tests/expected/wgsl_dual_source_blending.txt @@ -0,0 +1,10 @@ +enable dual_source_blending; +struct FragmentOutput { + @location(0) @blend_src(0) source0_: vec4, + @location(0) @blend_src(1) source1_: vec4, +} + +@fragment +fn fragment(@builtin(position) frag_coord: vec4) -> FragmentOutput { + return FragmentOutput(frag_coord, frag_coord); +} From 950f69b1a593e6d444a30504aca1bade7d38ad89 Mon Sep 17 00:00:00 2001 From: Charlotte McElwain Date: Sat, 21 Jun 2025 13:45:00 -0700 Subject: [PATCH 5/7] Change log level. --- src/compose/mod.rs | 4 ++-- src/compose/test.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compose/mod.rs b/src/compose/mod.rs index f9d3804..59f4aea 100644 --- a/src/compose/mod.rs +++ b/src/compose/mod.rs @@ -130,7 +130,7 @@ use indexmap::IndexMap; use naga::EntryPoint; use regex::Regex; use std::collections::{hash_map::Entry, BTreeMap, HashMap, HashSet}; -use tracing::{debug, info, trace}; +use tracing::{debug, trace}; use crate::{ compose::preprocess::{PreprocessOutput, PreprocessorMetaData}, @@ -588,7 +588,7 @@ impl Composer { let mut wgsl_string = String::new(); if let Some(wgsl_directives) = &wgsl_directives { - info!("adding WGSL directives for {}", name); + trace!("adding WGSL directives for {}", name); wgsl_string = wgsl_directives.to_wgsl_string(); } let mut module_string = match language { diff --git a/src/compose/test.rs b/src/compose/test.rs index c5abe67..0f37a1e 100644 --- a/src/compose/test.rs +++ b/src/compose/test.rs @@ -1562,7 +1562,9 @@ mod test { #[test] fn wgsl_dual_source_blending() { let mut composer = Composer::default(); - composer.capabilities.set(Capabilities::DUAL_SOURCE_BLENDING, true); + composer + .capabilities + .set(Capabilities::DUAL_SOURCE_BLENDING, true); let module = composer .make_naga_module(NagaModuleDescriptor { From 3878f7dc772e755a041fda934c61456af4b0bf46 Mon Sep 17 00:00:00 2001 From: Charlotte McElwain Date: Mon, 23 Jun 2025 14:44:22 -0700 Subject: [PATCH 6/7] Remove unused code. --- atomics.txt | 38 +++++++++ src/compose/error.rs | 13 ---- src/compose/wgsl_directives.rs | 137 +-------------------------------- 3 files changed, 39 insertions(+), 149 deletions(-) create mode 100644 atomics.txt diff --git a/atomics.txt b/atomics.txt new file mode 100644 index 0000000..23a94ad --- /dev/null +++ b/atomics.txt @@ -0,0 +1,38 @@ +var atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX: atomic; + +fn entry_pointX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX() -> f32 { + var y: u32; + + atomicStore((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 1u); + let _e3: u32 = atomicLoad((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX)); + y = _e3; + let _e7: u32 = atomicAdd((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 2u); + let _e8: u32 = y; + y = (_e8 + _e7); + let _e12: u32 = atomicSub((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 1u); + let _e13: u32 = y; + y = (_e13 + _e12); + let _e17: u32 = atomicMax((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 5u); + let _e18: u32 = y; + y = (_e18 + _e17); + let _e22: u32 = atomicMin((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 4u); + let _e23: u32 = y; + y = (_e23 + _e22); + let _e25: u32 = y; + let _e27: u32 = atomicExchange((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), _e25); + let _e28: u32 = y; + y = (_e28 + _e27); + let _e33: _atomic_compare_exchange_resultUint4_ = atomicCompareExchangeWeak((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 12u, 0u); + if _e33.exchanged { + let _e36: u32 = y; + y = (_e36 + _e33.old_value); + } + let _e38: u32 = y; + return f32(_e38); +} + +fn main() -> f32 { + let _e0: f32 = entry_pointX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX(); + return _e0; +} + diff --git a/src/compose/error.rs b/src/compose/error.rs index 04fdce3..185f41a 100644 --- a/src/compose/error.rs +++ b/src/compose/error.rs @@ -140,12 +140,6 @@ pub enum ComposerErrorInner { position: usize, reason: String, }, - #[error("Conflicting diagnostic directives for rule '{rule}': existing={existing_severity}, new={new_severity}")] - ConflictingDirectives { - rule: String, - existing_severity: String, - new_severity: String, - }, } struct ErrorSources<'a> { @@ -265,13 +259,6 @@ impl ComposerError { ComposerErrorInner::InconsistentShaderDefValue { def } => { return format!("{path}: multiple inconsistent shader def values: '{def}'"); } - ComposerErrorInner::ConflictingDirectives { - rule, - existing_severity, - new_severity, - } => { - return format!("{path}: conflicting diagnostic directives for rule '{rule}': existing={existing_severity}, new={new_severity}"); - } ComposerErrorInner::RedirectError(..) => ( vec![Label::primary((), 0..0)], vec![format!("override error")], diff --git a/src/compose/wgsl_directives.rs b/src/compose/wgsl_directives.rs index 74c1414..59dc0ef 100644 --- a/src/compose/wgsl_directives.rs +++ b/src/compose/wgsl_directives.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnableDirective { @@ -27,54 +27,6 @@ pub struct WgslDirectives { } impl WgslDirectives { - pub fn merge_with(&mut self, other: &WgslDirectives) -> Result<(), super::ComposerErrorInner> { - for enable in &other.enables { - for ext in &enable.extensions { - if !self.has_enable_extension(ext) { - self.enables.push(enable.clone()); - break; - } - } - } - - for requires in &other.requires { - for ext in &requires.extensions { - if !self.has_requires_extension(ext) { - self.requires.push(requires.clone()); - break; - } - } - } - - for diagnostic in &other.diagnostics { - if let Some(existing) = self.diagnostics.iter().find(|d| d.rule == diagnostic.rule) { - if existing.severity != diagnostic.severity { - return Err(super::ComposerErrorInner::ConflictingDirectives { - rule: diagnostic.rule.clone(), - existing_severity: existing.severity.clone(), - new_severity: diagnostic.severity.clone(), - }); - } - } else { - self.diagnostics.push(diagnostic.clone()); - } - } - - Ok(()) - } - - fn has_enable_extension(&self, extension: &str) -> bool { - self.enables - .iter() - .any(|enable| enable.extensions.contains(&extension.to_string())) - } - - fn has_requires_extension(&self, extension: &str) -> bool { - self.requires - .iter() - .any(|requires| requires.extensions.contains(&extension.to_string())) - } - pub fn to_wgsl_string(&self) -> String { let mut result = String::new(); @@ -115,30 +67,6 @@ impl WgslDirectives { pub fn is_empty(&self) -> bool { self.enables.is_empty() && self.requires.is_empty() && self.diagnostics.is_empty() } - - pub fn enable_extensions(&self) -> HashSet { - let mut extensions = HashSet::new(); - for enable in &self.enables { - extensions.extend(enable.extensions.iter().cloned()); - } - extensions - } - - pub fn requires_extensions(&self) -> HashSet { - let mut extensions = HashSet::new(); - for requires in &self.requires { - extensions.extend(requires.extensions.iter().cloned()); - } - extensions - } - - pub fn diagnostic_rules(&self) -> HashMap { - let mut rules = HashMap::new(); - for diagnostic in &self.diagnostics { - rules.insert(diagnostic.rule.clone(), diagnostic.severity.clone()); - } - rules - } } #[cfg(test)] @@ -174,67 +102,4 @@ mod tests { assert!(result.contains("requires readonly_and_readwrite_storage_textures;")); assert!(result.contains("diagnostic(warn, derivative_uniformity);")); } - - #[test] - fn test_merge_directives_no_conflict() { - let mut base = WgslDirectives::default(); - base.enables.push(EnableDirective { - extensions: vec!["f16".to_string()], - source_location: 0, - }); - - let other = WgslDirectives { - enables: vec![EnableDirective { - extensions: vec!["subgroups".to_string()], - source_location: 0, - }], - requires: vec![RequiresDirective { - extensions: vec!["readonly_and_readwrite_storage_textures".to_string()], - source_location: 0, - }], - diagnostics: vec![DiagnosticDirective { - severity: "warn".to_string(), - rule: "derivative_uniformity".to_string(), - source_location: 0, - }], - }; - - base.merge_with(&other).unwrap(); - - let enables = base.enable_extensions(); - assert!(enables.contains("f16")); - assert!(enables.contains("subgroups")); - - let requires = base.requires_extensions(); - assert!(requires.contains("readonly_and_readwrite_storage_textures")); - - let diagnostics = base.diagnostic_rules(); - assert_eq!( - diagnostics.get("derivative_uniformity"), - Some(&"warn".to_string()) - ); - } - - #[test] - fn test_merge_directives_conflict() { - let mut base = WgslDirectives::default(); - base.diagnostics.push(DiagnosticDirective { - severity: "warn".to_string(), - rule: "derivative_uniformity".to_string(), - source_location: 0, - }); - - let other = WgslDirectives { - enables: vec![], - requires: vec![], - diagnostics: vec![DiagnosticDirective { - severity: "error".to_string(), - rule: "derivative_uniformity".to_string(), - source_location: 0, - }], - }; - - let result = base.merge_with(&other); - assert!(result.is_err()); - } } From b3f5f3ab7f4b840f4afced5dedf8b1d64f64425e Mon Sep 17 00:00:00 2001 From: Charlotte McElwain Date: Mon, 23 Jun 2025 14:49:23 -0700 Subject: [PATCH 7/7] Rm. --- atomics.txt | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 atomics.txt diff --git a/atomics.txt b/atomics.txt deleted file mode 100644 index 23a94ad..0000000 --- a/atomics.txt +++ /dev/null @@ -1,38 +0,0 @@ -var atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX: atomic; - -fn entry_pointX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX() -> f32 { - var y: u32; - - atomicStore((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 1u); - let _e3: u32 = atomicLoad((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX)); - y = _e3; - let _e7: u32 = atomicAdd((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 2u); - let _e8: u32 = y; - y = (_e8 + _e7); - let _e12: u32 = atomicSub((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 1u); - let _e13: u32 = y; - y = (_e13 + _e12); - let _e17: u32 = atomicMax((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 5u); - let _e18: u32 = y; - y = (_e18 + _e17); - let _e22: u32 = atomicMin((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 4u); - let _e23: u32 = y; - y = (_e23 + _e22); - let _e25: u32 = y; - let _e27: u32 = atomicExchange((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), _e25); - let _e28: u32 = y; - y = (_e28 + _e27); - let _e33: _atomic_compare_exchange_resultUint4_ = atomicCompareExchangeWeak((&atomX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX), 12u, 0u); - if _e33.exchanged { - let _e36: u32 = y; - y = (_e36 + _e33.old_value); - } - let _e38: u32 = y; - return f32(_e38); -} - -fn main() -> f32 { - let _e0: f32 = entry_pointX_naga_oil_mod_XORSXG5C7NVXWI5LMMUX(); - return _e0; -} -