diff --git a/naga/src/back/wgsl/writer.rs b/naga/src/back/wgsl/writer.rs index daf32a7116f..7a3f031b447 100644 --- a/naga/src/back/wgsl/writer.rs +++ b/naga/src/back/wgsl/writer.rs @@ -135,12 +135,6 @@ impl Writer { } pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult { - if !module.overrides.is_empty() { - return Err(Error::Unimplemented( - "Pipeline constants are not yet supported for this back-end".to_string(), - )); - } - self.reset(module); // Write all `enable` declarations @@ -172,6 +166,16 @@ impl Writer { } } + // Write all overrides + let mut overrides = module.overrides.iter().peekable(); + while let Some((handle, _)) = overrides.next() { + self.write_override(module, handle)?; + // Add extra newline for readability on last iteration + if overrides.peek().is_none() { + writeln!(self.out)?; + } + } + // Write all globals for (ty, global) in module.global_variables.iter() { self.write_global(module, global, ty)?; @@ -1205,6 +1209,9 @@ impl Writer { write_expression(self, value)?; write!(self.out, ")")?; } + Expression::Override(handle) => { + write!(self.out, "{}", self.names[&NameKey::Override(handle)])?; + } _ => unreachable!(), } @@ -1255,7 +1262,9 @@ impl Writer { |writer, expr| writer.write_expr(module, expr, func_ctx), )?; } - Expression::Override(_) => unreachable!(), + Expression::Override(handle) => { + write!(self.out, "{}", self.names[&NameKey::Override(handle)])?; + } Expression::FunctionArgument(pos) => { let name_key = func_ctx.argument_key(pos); let name = &self.names[&name_key]; @@ -1770,6 +1779,38 @@ impl Writer { Ok(()) } + /// Helper method used to write overrides + /// + /// # Notes + /// Ends in a newline + fn write_override( + &mut self, + module: &Module, + handle: Handle, + ) -> BackendResult { + let override_ = &module.overrides[handle]; + let name = &self.names[&NameKey::Override(handle)]; + + // Write @id attribute if present + if let Some(id) = override_.id { + write!(self.out, "@id({id}) ")?; + } + + // Write override declaration + write!(self.out, "override {name}: ")?; + self.write_type(module, override_.ty)?; + + // Write initializer if present + if let Some(init) = override_.init { + write!(self.out, " = ")?; + self.write_const_expression(module, init, &module.global_expressions)?; + } + + writeln!(self.out, ";")?; + + Ok(()) + } + // See https://github.com/rust-lang/rust-clippy/issues/4979. #[allow(clippy::missing_const_for_fn)] pub fn finish(self) -> W { @@ -1795,8 +1836,12 @@ impl TypeContext for WriterTypeContext<'_> { unreachable!("the WGSL back end should always provide type handles"); } - fn write_override(&self, _: Handle, _: &mut W) -> core::fmt::Result { - unreachable!("overrides should be validated out"); + fn write_override( + &self, + handle: Handle, + out: &mut W, + ) -> core::fmt::Result { + write!(out, "{}", self.names[&NameKey::Override(handle)]) } fn write_non_wgsl_inner(&self, _: &TypeInner, _: &mut W) -> core::fmt::Result { diff --git a/naga/src/front/glsl/ast.rs b/naga/src/front/glsl/ast.rs index 6b799305649..2c314aa602d 100644 --- a/naga/src/front/glsl/ast.rs +++ b/naga/src/front/glsl/ast.rs @@ -4,13 +4,14 @@ use core::fmt; use super::{builtins::MacroCall, Span}; use crate::{ AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle, - Interpolation, Literal, Sampling, StorageAccess, Type, UnaryOperator, + Interpolation, Literal, Override, Sampling, StorageAccess, Type, UnaryOperator, }; #[derive(Debug, Clone, Copy)] pub enum GlobalLookupKind { Variable(Handle), Constant(Handle, Handle), + Override(Handle, Handle), BlockSelect(Handle, u32), } diff --git a/naga/src/front/glsl/context.rs b/naga/src/front/glsl/context.rs index d650044992e..dd386e56f9b 100644 --- a/naga/src/front/glsl/context.rs +++ b/naga/src/front/glsl/context.rs @@ -211,6 +211,14 @@ impl<'a> Context<'a> { Some((v, ty)), ) } + GlobalLookupKind::Override(v, _ty) => { + let span = self.module.overrides.get_span(v); + ( + self.add_expression(Expression::Override(v), span)?, + false, + None, + ) + } }; let var = VariableReference { diff --git a/naga/src/front/glsl/parser.rs b/naga/src/front/glsl/parser.rs index 2eb3ec4b009..79001d8700f 100644 --- a/naga/src/front/glsl/parser.rs +++ b/naga/src/front/glsl/parser.rs @@ -436,6 +436,7 @@ impl DeclarationContext<'_, '_, '_> { let expr = match global { GlobalOrConstant::Global(handle) => Expression::GlobalVariable(handle), GlobalOrConstant::Constant(handle) => Expression::Constant(handle), + GlobalOrConstant::Override(handle) => Expression::Override(handle), }; Ok(self.ctx.add_expression(expr, meta)?) } diff --git a/naga/src/front/glsl/parser/declarations.rs b/naga/src/front/glsl/parser/declarations.rs index f0648267516..e3cb321c944 100644 --- a/naga/src/front/glsl/parser/declarations.rs +++ b/naga/src/front/glsl/parser/declarations.rs @@ -608,6 +608,7 @@ impl ParsingContext<'_> { kind: match global { GlobalOrConstant::Global(handle) => GlobalLookupKind::BlockSelect(handle, i), GlobalOrConstant::Constant(handle) => GlobalLookupKind::Constant(handle, ty), + GlobalOrConstant::Override(handle) => GlobalLookupKind::Override(handle, ty), }, entry_arg: None, mutable: true, diff --git a/naga/src/front/glsl/variables.rs b/naga/src/front/glsl/variables.rs index 0b21cde2f13..015cb36f57c 100644 --- a/naga/src/front/glsl/variables.rs +++ b/naga/src/front/glsl/variables.rs @@ -8,8 +8,8 @@ use super::{ }; use crate::{ AddressSpace, Binding, BuiltIn, Constant, Expression, GlobalVariable, Handle, Interpolation, - LocalVariable, ResourceBinding, Scalar, ScalarKind, ShaderStage, SwizzleComponent, Type, - TypeInner, VectorSize, + LocalVariable, Override, ResourceBinding, Scalar, ScalarKind, ShaderStage, SwizzleComponent, + Type, TypeInner, VectorSize, }; pub struct VarDeclaration<'a, 'key> { @@ -35,6 +35,7 @@ struct BuiltInData { pub enum GlobalOrConstant { Global(Handle), Constant(Handle), + Override(Handle), } impl Frontend { @@ -481,25 +482,69 @@ impl Frontend { (GlobalOrConstant::Global(handle), lookup) } StorageQualifier::Const => { - let init = init.ok_or_else(|| Error { - kind: ErrorKind::SemanticError("const values must have an initializer".into()), - meta, - })?; + // Check if this is a specialization constant with constant_id + let constant_id = qualifiers.uint_layout_qualifier("constant_id", &mut self.errors); + + if let Some(id) = constant_id { + // This is a specialization constant - convert to Override + let id: Option = match id.try_into() { + Ok(v) => Some(v), + Err(_) => { + self.errors.push(Error { + kind: ErrorKind::SemanticError( + format!( + "constant_id value {id} is too high (maximum is {})", + u16::MAX + ) + .into(), + ), + meta, + }); + None + } + }; - let constant = Constant { - name: name.clone(), - ty, - init, - }; - let handle = ctx.module.constants.append(constant, meta); + let override_handle = ctx.module.overrides.append( + Override { + name: name.clone(), + id, + ty, + init, + }, + meta, + ); - let lookup = GlobalLookup { - kind: GlobalLookupKind::Constant(handle, ty), - entry_arg: None, - mutable: false, - }; + let lookup = GlobalLookup { + kind: GlobalLookupKind::Override(override_handle, ty), + entry_arg: None, + mutable: false, + }; + + (GlobalOrConstant::Override(override_handle), lookup) + } else { + // Regular constant + let init = init.ok_or_else(|| Error { + kind: ErrorKind::SemanticError( + "const values must have an initializer".into(), + ), + meta, + })?; - (GlobalOrConstant::Constant(handle), lookup) + let constant = Constant { + name: name.clone(), + ty, + init, + }; + let handle = ctx.module.constants.append(constant, meta); + + let lookup = GlobalLookup { + kind: GlobalLookupKind::Constant(handle, ty), + entry_arg: None, + mutable: false, + }; + + (GlobalOrConstant::Constant(handle), lookup) + } } StorageQualifier::AddressSpace(mut space) => { match space { diff --git a/naga/src/proc/constant_evaluator.rs b/naga/src/proc/constant_evaluator.rs index 1e0c5ac15ab..9c036e2ae93 100644 --- a/naga/src/proc/constant_evaluator.rs +++ b/naga/src/proc/constant_evaluator.rs @@ -1189,8 +1189,9 @@ impl<'a> ConstantEvaluator<'a> { Behavior::Wgsl(WgslRestrictions::Const(_)) => { Err(ConstantEvaluatorError::OverrideExpr) } - Behavior::Glsl(_) => { - unreachable!() + // GLSL specialization constants (constant_id) become Override expressions + Behavior::Glsl(GlslRestrictions::Const | GlslRestrictions::Runtime(_)) => { + Ok(self.append_expr(expr, span, ExpressionKind::Override)) } }, ExpressionKind::Runtime => { diff --git a/naga/src/proc/namer.rs b/naga/src/proc/namer.rs index 127e346f3a1..9509d44cb4f 100644 --- a/naga/src/proc/namer.rs +++ b/naga/src/proc/namer.rs @@ -51,6 +51,7 @@ impl ExternalTextureNameKey { #[derive(Debug, Eq, Hash, PartialEq)] pub enum NameKey { Constant(Handle), + Override(Handle), GlobalVariable(Handle), Type(Handle), StructMember(Handle, u32), @@ -374,6 +375,21 @@ impl Namer { let name = self.call(label); output.insert(NameKey::Constant(handle), name); } + + for (handle, override_) in module.overrides.iter() { + let label = match override_.name { + Some(ref name) => name, + None => { + use core::fmt::Write; + // Try to be more descriptive about the override values + temp.clear(); + write!(temp, "override_{}", output[&NameKey::Type(override_.ty)]).unwrap(); + &temp + } + }; + let name = self.call(label); + output.insert(NameKey::Override(handle), name); + } } } diff --git a/naga/tests/in/glsl/spec-constant.frag b/naga/tests/in/glsl/spec-constant.frag new file mode 100644 index 00000000000..e51054fa017 --- /dev/null +++ b/naga/tests/in/glsl/spec-constant.frag @@ -0,0 +1,21 @@ +#version 450 + +// Specialization constants with constant_id layout qualifier +layout(constant_id = 0) const bool SPEC_CONST_BOOL = true; +layout(constant_id = 1) const int SPEC_CONST_INT = 42; +layout(constant_id = 2) const uint SPEC_CONST_UINT = 10u; +layout(constant_id = 3) const float SPEC_CONST_FLOAT = 3.14; + +layout(location = 0) out vec4 o_color; + +void main() { + float result = 0.0; + + if (SPEC_CONST_BOOL) { + result += float(SPEC_CONST_INT); + } + + result += float(SPEC_CONST_UINT) * SPEC_CONST_FLOAT; + + o_color = vec4(result, 0.0, 0.0, 1.0); +} diff --git a/naga/tests/in/glsl/spec-constant.toml b/naga/tests/in/glsl/spec-constant.toml new file mode 100644 index 00000000000..07bac2c0506 --- /dev/null +++ b/naga/tests/in/glsl/spec-constant.toml @@ -0,0 +1 @@ +targets = "WGSL" diff --git a/naga/tests/out/wgsl/glsl-spec-constant.frag.wgsl b/naga/tests/out/wgsl/glsl-spec-constant.frag.wgsl new file mode 100644 index 00000000000..d01c971099f --- /dev/null +++ b/naga/tests/out/wgsl/glsl-spec-constant.frag.wgsl @@ -0,0 +1,33 @@ +struct FragmentOutput { + @location(0) o_color: vec4, +} + +@id(0) override SPEC_CONST_BOOL: bool = true; +@id(1) override SPEC_CONST_INT: i32 = 42i; +@id(2) override SPEC_CONST_UINT: u32 = 10u; +@id(3) override SPEC_CONST_FLOAT: f32 = 3.14f; + +var o_color: vec4; + +fn main_1() { + var result: f32 = 0f; + + if SPEC_CONST_BOOL { + { + let _e7 = result; + result = (_e7 + f32(SPEC_CONST_INT)); + } + } + let _e10 = result; + result = (_e10 + (f32(SPEC_CONST_UINT) * SPEC_CONST_FLOAT)); + let _e14 = result; + o_color = vec4(_e14, 0f, 0f, 1f); + return; +} + +@fragment +fn main() -> FragmentOutput { + main_1(); + let _e1 = o_color; + return FragmentOutput(_e1); +}