From b67739034ebe48dc8f21dfd75a428c907ac46e6b Mon Sep 17 00:00:00 2001 From: Matus Talcik Date: Mon, 31 Aug 2020 20:34:38 +0200 Subject: [PATCH] Implement Execution Modes (#169) Add documentation, make clippy happy Compilation errors Add some more docs Few more compilation errors Changes based on the review glsl-new parser fix Set default local size to (0, 0, 0) final cleanup Last design New design --- examples/convert.rs | 26 +++-- src/back/glsl.rs | 15 +-- src/back/msl.rs | 183 ++++++++++++++++++----------------- src/back/spv/writer.rs | 8 +- src/front/glsl/mod.rs | 10 +- src/front/glsl_new/parser.rs | 90 +++++++++-------- src/front/spv/error.rs | 1 + src/front/spv/mod.rs | 116 +++++++++++++++++----- src/front/wgsl/mod.rs | 8 +- src/lib.rs | 50 +++++++++- tests/convert.rs | 8 +- 11 files changed, 333 insertions(+), 182 deletions(-) diff --git a/examples/convert.rs b/examples/convert.rs index 3e54b8ffb9..9707b6cb7a 100644 --- a/examples/convert.rs +++ b/examples/convert.rs @@ -91,7 +91,9 @@ fn main() { naga::front::glsl_new::parse_str( &input, "main".to_string(), - naga::ShaderStage::Fragment, + naga::ShaderStage::Fragment { + early_depth_test: None, + }, ) .unwrap(), ) @@ -104,7 +106,9 @@ fn main() { naga::front::glsl::parse_str( &input, "main".to_string(), - naga::ShaderStage::Fragment, + naga::ShaderStage::Fragment { + early_depth_test: None, + }, ) .unwrap(), ) @@ -115,8 +119,14 @@ fn main() { #[cfg(feature = "glsl")] "comp" => { let input = fs::read_to_string(&args[1]).unwrap(); - naga::front::glsl::parse_str(&input, "main".to_string(), naga::ShaderStage::Compute) - .unwrap() + naga::front::glsl::parse_str( + &input, + "main".to_string(), + naga::ShaderStage::Compute { + local_size: (0, 0, 0), + }, + ) + .unwrap() } #[cfg(feature = "deserialize")] "ron" => { @@ -209,8 +219,12 @@ fn main() { String::from("main"), match stage { "vert" => ShaderStage::Vertex, - "frag" => ShaderStage::Fragment, - "comp" => ShaderStage::Compute, + "frag" => ShaderStage::Fragment { + early_depth_test: None, + }, + "comp" => ShaderStage::Compute { + local_size: (0, 0, 0), + }, _ => unreachable!(), }, ), diff --git a/src/back/glsl.rs b/src/back/glsl.rs index d0652adc72..e5224c0b85 100644 --- a/src/back/glsl.rs +++ b/src/back/glsl.rs @@ -129,7 +129,7 @@ pub fn write<'a>(module: &'a Module, out: &mut impl Write, options: Options) -> .ok_or_else(|| Error::Custom(String::from("Entry point not found")))?; let func = &module.functions[entry_point.function]; - if entry_point.stage == ShaderStage::Compute { + if let ShaderStage::Compute { .. } = entry_point.stage { if (es && version < 310) || (!es && version < 430) { return Err(Error::Custom(format!( "Version {} doesn't support compute shaders", @@ -293,12 +293,13 @@ pub fn write<'a>(module: &'a Module, out: &mut impl Write, options: Options) -> } if let Some(interpolation) = global.interpolation { - if (entry_point.stage == ShaderStage::Fragment && global.class == StorageClass::Input) - || (entry_point.stage == ShaderStage::Vertex - && global.class == StorageClass::Output) - { - write!(out, "{} ", write_interpolation(interpolation)?)?; - } + match (entry_point.stage, global.class) { + (ShaderStage::Fragment { .. }, StorageClass::Input) + | (ShaderStage::Vertex, StorageClass::Output) => { + write!(out, "{} ", write_interpolation(interpolation)?)?; + } + _ => {} + }; } let block = match global.class { diff --git a/src/back/msl.rs b/src/back/msl.rs index eb3464f96d..da7045942f 100644 --- a/src/back/msl.rs +++ b/src/back/msl.rs @@ -1020,111 +1020,116 @@ impl Writer { LocationMode::VertexInput, LocationMode::Intermediate, ), - crate::ShaderStage::Fragment => ( + crate::ShaderStage::Fragment { .. } => ( "fragment", LocationMode::Intermediate, LocationMode::FragmentOutput, ), - crate::ShaderStage::Compute => { + crate::ShaderStage::Compute { .. } => { ("kernel", LocationMode::Uniform, LocationMode::Uniform) } }; let location_input_name = fun.name.or_index(InputStructIndex(fun_handle)); - if stage != crate::ShaderStage::Compute { - writeln!(self.out, "struct {} {{", location_input_name)?; - for ((handle, var), &usage) in - module.global_variables.iter().zip(&fun.global_usage) - { - if var.class != crate::StorageClass::Input - || !usage.contains(crate::GlobalUse::LOAD) + match stage { + crate::ShaderStage::Compute { .. } => { + writeln!(self.out, "struct {} {{", location_input_name)?; + for ((handle, var), &usage) in + module.global_variables.iter().zip(&fun.global_usage) { - continue; - } - // if it's a struct, lift all the built-in contents up to the root - let ty_handle = var.ty; - if let crate::TypeInner::Struct { ref members } = - module.types[ty_handle].inner - { - for (index, member) in members.iter().enumerate() { - if let crate::MemberOrigin::BuiltIn(built_in) = member.origin { - let name = member.name.or_index(MemberIndex(index)); - let ty_name = module.types[member.ty].name.or_index(member.ty); - write!(self.out, "\t{} {}", ty_name, name)?; - ResolvedBinding::BuiltIn(built_in) - .try_fmt_decorated(&mut self.out, ";\n")?; - } + if var.class != crate::StorageClass::Input + || !usage.contains(crate::GlobalUse::LOAD) + { + continue; } - } else if let Some(ref binding @ crate::Binding::Location(_)) = var.binding - { - let tyvar = TypedGlobalVariable { - module, - handle, - usage: crate::GlobalUse::empty(), - }; - let resolved = options.resolve_binding(binding, in_mode)?; - - write!(self.out, "\t")?; - tyvar.try_fmt(&mut self.out)?; - resolved.try_fmt_decorated(&mut self.out, ";\n")?; - } - } - writeln!(self.out, "}};")?; - writeln!(self.out, "struct {} {{", output_name)?; - for ((handle, var), &usage) in - module.global_variables.iter().zip(&fun.global_usage) - { - if var.class != crate::StorageClass::Output - || !usage.contains(crate::GlobalUse::STORE) - { - continue; - } - // if it's a struct, lift all the built-in contents up to the root - let ty_handle = var.ty; - if let crate::TypeInner::Struct { ref members } = - module.types[ty_handle].inner - { - for (index, member) in members.iter().enumerate() { - let name = member.name.or_index(MemberIndex(index)); - let ty_name = module.types[member.ty].name.or_index(member.ty); - match member.origin { - crate::MemberOrigin::Empty => {} - crate::MemberOrigin::BuiltIn(built_in) => { + // if it's a struct, lift all the built-in contents up to the root + let ty_handle = var.ty; + if let crate::TypeInner::Struct { ref members } = + module.types[ty_handle].inner + { + for (index, member) in members.iter().enumerate() { + if let crate::MemberOrigin::BuiltIn(built_in) = member.origin { + let name = member.name.or_index(MemberIndex(index)); + let ty_name = + module.types[member.ty].name.or_index(member.ty); write!(self.out, "\t{} {}", ty_name, name)?; ResolvedBinding::BuiltIn(built_in) .try_fmt_decorated(&mut self.out, ";\n")?; } - crate::MemberOrigin::Offset(_) => { - //TODO + } + } else if let Some(ref binding @ crate::Binding::Location(_)) = + var.binding + { + let tyvar = TypedGlobalVariable { + module, + handle, + usage: crate::GlobalUse::empty(), + }; + let resolved = options.resolve_binding(binding, in_mode)?; + + write!(self.out, "\t")?; + tyvar.try_fmt(&mut self.out)?; + resolved.try_fmt_decorated(&mut self.out, ";\n")?; + } + } + writeln!(self.out, "}};")?; + writeln!(self.out, "struct {} {{", output_name)?; + for ((handle, var), &usage) in + module.global_variables.iter().zip(&fun.global_usage) + { + if var.class != crate::StorageClass::Output + || !usage.contains(crate::GlobalUse::STORE) + { + continue; + } + // if it's a struct, lift all the built-in contents up to the root + let ty_handle = var.ty; + if let crate::TypeInner::Struct { ref members } = + module.types[ty_handle].inner + { + for (index, member) in members.iter().enumerate() { + let name = member.name.or_index(MemberIndex(index)); + let ty_name = module.types[member.ty].name.or_index(member.ty); + match member.origin { + crate::MemberOrigin::Empty => {} + crate::MemberOrigin::BuiltIn(built_in) => { + write!(self.out, "\t{} {}", ty_name, name)?; + ResolvedBinding::BuiltIn(built_in) + .try_fmt_decorated(&mut self.out, ";\n")?; + } + crate::MemberOrigin::Offset(_) => { + //TODO + } } } + } else { + let tyvar = TypedGlobalVariable { + module, + handle, + usage: crate::GlobalUse::empty(), + }; + write!(self.out, "\t")?; + tyvar.try_fmt(&mut self.out)?; + if let Some(ref binding) = var.binding { + let resolved = options.resolve_binding(binding, out_mode)?; + resolved.try_fmt_decorated(&mut self.out, "")?; + } + writeln!(self.out, ";")?; } - } else { - let tyvar = TypedGlobalVariable { - module, - handle, - usage: crate::GlobalUse::empty(), - }; - write!(self.out, "\t")?; - tyvar.try_fmt(&mut self.out)?; - if let Some(ref binding) = var.binding { - let resolved = options.resolve_binding(binding, out_mode)?; - resolved.try_fmt_decorated(&mut self.out, "")?; - } - writeln!(self.out, ";")?; } + writeln!(self.out, "}};")?; + writeln!(self.out, "{} {} {}(", em_str, output_name, fun_name)?; + let separator = separate(last_used_global.is_none()); + writeln!( + self.out, + "\t{} {} [[stage_in]]{}", + location_input_name, LOCATION_INPUT_STRUCT_NAME, separator + )?; } - writeln!(self.out, "}};")?; - writeln!(self.out, "{} {} {}(", em_str, output_name, fun_name)?; - let separator = separate(last_used_global.is_none()); - writeln!( - self.out, - "\t{} {} [[stage_in]]{}", - location_input_name, LOCATION_INPUT_STRUCT_NAME, separator - )?; - } else { - writeln!(self.out, "{} void {}(", em_str, fun_name)?; - } + _ => { + writeln!(self.out, "{} void {}(", em_str, fun_name)?; + } + }; for ((handle, var), &usage) in module.global_variables.iter().zip(&fun.global_usage) { @@ -1142,10 +1147,10 @@ impl Writer { LocationMode::VertexInput } (crate::ShaderStage::Vertex, crate::StorageClass::Output) - | (crate::ShaderStage::Fragment, crate::StorageClass::Input) => { + | (crate::ShaderStage::Fragment { .. }, crate::StorageClass::Input) => { LocationMode::Intermediate } - (crate::ShaderStage::Fragment, crate::StorageClass::Output) => { + (crate::ShaderStage::Fragment { .. }, crate::StorageClass::Output) => { LocationMode::FragmentOutput } _ => LocationMode::Uniform, @@ -1188,11 +1193,11 @@ impl Writer { // write down function body let has_output = match shader_stage { - Some(crate::ShaderStage::Vertex) | Some(crate::ShaderStage::Fragment) => { + Some(crate::ShaderStage::Vertex) | Some(crate::ShaderStage::Fragment { .. }) => { writeln!(self.out, "\t{} {};", output_name, OUTPUT_STRUCT_NAME)?; true } - Some(crate::ShaderStage::Compute) | None => false, + Some(crate::ShaderStage::Compute { .. }) | None => false, }; for (local_handle, local) in fun.local_variables.iter() { let ty_name = module.types[local.ty].name.or_index(local.ty); diff --git a/src/back/spv/writer.rs b/src/back/spv/writer.rs index 1c6c6957b0..1e5d44a22a 100644 --- a/src/back/spv/writer.rs +++ b/src/back/spv/writer.rs @@ -271,8 +271,8 @@ impl Writer { let exec_model = match entry_point.stage { crate::ShaderStage::Vertex => spirv::ExecutionModel::Vertex, - crate::ShaderStage::Fragment => spirv::ExecutionModel::Fragment, - crate::ShaderStage::Compute => spirv::ExecutionModel::GLCompute, + crate::ShaderStage::Fragment { .. } => spirv::ExecutionModel::Fragment, + crate::ShaderStage::Compute { .. } => spirv::ExecutionModel::GLCompute, }; instruction.add_operand(exec_model as u32); @@ -298,13 +298,13 @@ impl Writer { self.try_add_capabilities(exec_model.required_capabilities()); match entry_point.stage { crate::ShaderStage::Vertex => {} - crate::ShaderStage::Fragment => { + crate::ShaderStage::Fragment { .. } => { let execution_mode = spirv::ExecutionMode::OriginUpperLeft; self.try_add_capabilities(execution_mode.required_capabilities()); self.instruction_execution_mode(function_id, execution_mode) .to_words(&mut self.logical_layout.execution_modes); } - crate::ShaderStage::Compute => {} + crate::ShaderStage::Compute { .. } => {} } if self.writer_flags.contains(WriterFlags::DEBUG) { diff --git a/src/front/glsl/mod.rs b/src/front/glsl/mod.rs index 6bc39d63c6..f8fad62b5a 100644 --- a/src/front/glsl/mod.rs +++ b/src/front/glsl/mod.rs @@ -638,7 +638,7 @@ impl<'a> Parser<'a> { name: Some(name), class: match self.shader_stage { ShaderStage::Vertex => StorageClass::Output, - ShaderStage::Fragment => StorageClass::Input, + ShaderStage::Fragment { .. } => StorageClass::Input, _ => panic!(), }, binding: Some(Binding::BuiltIn(BuiltIn::Position)), @@ -1327,7 +1327,13 @@ mod tests { println!( "{:#?}", - parse_str(data, String::from("main"), crate::ShaderStage::Fragment) + parse_str( + data, + String::from("main"), + crate::ShaderStage::Fragment { + early_depth_test: None + } + ) ); } diff --git a/src/front/glsl_new/parser.rs b/src/front/glsl_new/parser.rs index c1c986d8eb..b74dc64a97 100644 --- a/src/front/glsl_new/parser.rs +++ b/src/front/glsl_new/parser.rs @@ -130,52 +130,56 @@ pomelo! { // expression variable_identifier ::= Identifier(v) { - if v.1.as_str() == "gl_Position" && - (extra.shader_stage == ShaderStage::Vertex || - extra.shader_stage == ShaderStage::Fragment) { - let h = extra.global_variables.fetch_or_append( - GlobalVariable { - name: Some(v.1.clone()), - class: match extra.shader_stage { - ShaderStage::Vertex => StorageClass::Output, - ShaderStage::Fragment => StorageClass::Input, - _ => StorageClass::Input, - }, - binding: Some(Binding::BuiltIn(BuiltIn::Position)), - ty: extra.types.fetch_or_append(Type { - name: None, - inner: TypeInner::Vector { - size: VectorSize::Quad, - kind: ScalarKind::Float, - width: 4, + let gl_position = if let ShaderStage::Vertex | ShaderStage::Fragment { .. } = extra.shader_stage { + if v.1.as_str() == "gl_Position" { + let h = extra.global_variables.fetch_or_append( + GlobalVariable { + name: Some(v.1.clone()), + class: match extra.shader_stage { + ShaderStage::Vertex => StorageClass::Output, + ShaderStage::Fragment { .. } => StorageClass::Input, + _ => StorageClass::Input, }, - }), - interpolation: None, - }, - ); - extra.lookup_global_variables.insert(v.1.clone(), h); - let expression = extra.context.expressions.append( - Expression::GlobalVariable(h) - ); - extra.context.lookup_global_var_exps.insert(v.1, expression); - ExpressionRule{ - expression, - statements: vec![], + binding: Some(Binding::BuiltIn(BuiltIn::Position)), + ty: extra.types.fetch_or_append(Type { + name: None, + inner: TypeInner::Vector { + size: VectorSize::Quad, + kind: ScalarKind::Float, + width: 4, + }, + }), + interpolation: None, + }, + ); + extra.lookup_global_variables.insert(v.1.clone(), h); + let expression = extra.context.expressions.append( + Expression::GlobalVariable(h) + ); + extra.context.lookup_global_var_exps.insert(v.1.clone(), expression); + + Some(expression) + } else { + None } } else { - // try global and local vars - let expression = - if let Some(local_var) = extra.context.lookup_local_var(&v.1) { - local_var - } else if let Some(global_var) = extra.context.lookup_global_var_exps.get(&v.1) { - *global_var - } else { - return Err(ErrorKind::UnknownVariable(v.0, v.1)) - }; - ExpressionRule{ - expression, - statements: vec![], - } + None + }; + + let expression = + if let Some(gl_position) = gl_position { + gl_position + } else if let Some(local_var) = extra.context.lookup_local_var(&v.1) { + local_var + } else if let Some(global_var) = extra.context.lookup_global_var_exps.get(&v.1) { + *global_var + } else { + return Err(ErrorKind::UnknownVariable(v.0, v.1)); + }; + + ExpressionRule { + expression, + statements: vec![], } } diff --git a/src/front/spv/error.rs b/src/front/spv/error.rs index 0488a83ef9..f9ab8fca35 100644 --- a/src/front/spv/error.rs +++ b/src/front/spv/error.rs @@ -15,6 +15,7 @@ pub enum Error { UnsupportedExtInst(spirv::Word), UnsupportedType(Handle), UnsupportedExecutionModel(spirv::Word), + UnsupportedExecutionMode(spirv::Word), UnsupportedStorageClass(spirv::Word), UnsupportedImageDim(spirv::Word), UnsupportedImageFormat(spirv::Word), diff --git a/src/front/spv/mod.rs b/src/front/spv/mod.rs index 0f77860061..5ef5be200c 100644 --- a/src/front/spv/mod.rs +++ b/src/front/spv/mod.rs @@ -29,7 +29,11 @@ use crate::{ use num_traits::cast::FromPrimitive; use std::{convert::TryInto, num::NonZeroU32}; -pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[spirv::Capability::Shader]; +pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[ + spirv::Capability::Shader, + spirv::Capability::CullDistance, + spirv::Capability::StorageImageExtendedFormats, +]; pub const SUPPORTED_EXTENSIONS: &[&str] = &[]; pub const SUPPORTED_EXT_SETS: &[&str] = &["GLSL.std.450"]; @@ -235,7 +239,7 @@ struct LookupFunctionType { #[derive(Debug)] struct EntryPoint { - exec_model: spirv::ExecutionModel, + stage: crate::ShaderStage, name: String, function_id: spirv::Word, variable_ids: Vec, @@ -300,6 +304,7 @@ pub struct Parser { lookup_sampled_image: FastHashMap, lookup_function_type: FastHashMap, lookup_function: FastHashMap>, + lookup_entry_point: FastHashMap, deferred_function_calls: Vec, } @@ -322,6 +327,7 @@ impl> Parser { lookup_sampled_image: FastHashMap::default(), lookup_function_type: FastHashMap::default(), lookup_function: FastHashMap::default(), + lookup_entry_point: FastHashMap::default(), deferred_function_calls: Vec::new(), } } @@ -1402,7 +1408,6 @@ impl> Parser { generator, } }); - let mut entry_points = Vec::new(); while let Ok(inst) = self.next_inst() { use spirv::Op; @@ -1412,7 +1417,7 @@ impl> Parser { Op::Extension => self.parse_extension(inst), Op::ExtInstImport => self.parse_ext_inst_import(inst), Op::MemoryModel => self.parse_memory_model(inst), - Op::EntryPoint => self.parse_entry_point(inst, &mut entry_points), + Op::EntryPoint => self.parse_entry_point(inst), Op::ExecutionMode => self.parse_execution_mode(inst), Op::Source => self.parse_source(inst), Op::SourceExtension => self.parse_source_extension(inst), @@ -1492,16 +1497,11 @@ impl> Parser { self.future_member_decor.clear(); } - module.entry_points.reserve(entry_points.len()); - for raw in entry_points { + module.entry_points.reserve(self.lookup_entry_point.len()); + for raw in self.lookup_entry_point.values() { module.entry_points.push(crate::EntryPoint { - stage: match raw.exec_model { - spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex, - spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment, - spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute, - other => return Err(Error::UnsupportedExecutionModel(other as u32)), - }, - name: raw.name, + stage: raw.stage, + name: raw.name.clone(), function: *self.lookup_function.lookup(raw.function_id)?, }); } @@ -1557,11 +1557,7 @@ impl> Parser { Ok(()) } - fn parse_entry_point( - &mut self, - inst: Instruction, - entry_points: &mut Vec, - ) -> Result<(), Error> { + fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error> { self.switch(ModuleState::EntryPoint, inst.op)?; inst.expect_at_least(4)?; let exec_model = self.next()?; @@ -1570,23 +1566,93 @@ impl> Parser { let function_id = self.next()?; let (name, left) = self.next_string(inst.wc - 3)?; let ep = EntryPoint { - exec_model, + stage: match exec_model { + spirv::ExecutionModel::Vertex => crate::ShaderStage::Vertex, + spirv::ExecutionModel::Fragment => crate::ShaderStage::Fragment { + early_depth_test: None, + }, + spirv::ExecutionModel::GLCompute => crate::ShaderStage::Compute { + local_size: (0, 0, 0), + }, + _ => return Err(Error::UnsupportedExecutionModel(exec_model as u32)), + }, name, function_id, variable_ids: self.data.by_ref().take(left as usize).collect(), }; - entry_points.push(ep); + self.lookup_entry_point.insert(function_id, ep); Ok(()) } fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error> { + use crate::ShaderStage; + use spirv::ExecutionMode; + self.switch(ModuleState::ExecutionMode, inst.op)?; inst.expect_at_least(3)?; - let _ep_id = self.next()?; - let _mode = self.next()?; - for _ in 3..inst.wc { - let _ = self.next()?; //TODO - } + + let ep_id = self.next()?; + let mode_id = self.next()?; + let args: Vec = self.data.by_ref().take(inst.wc as usize - 3).collect(); + + let ep: &mut EntryPoint = self + .lookup_entry_point + .get_mut(&ep_id) + .ok_or(Error::InvalidId(ep_id))?; + let mode = spirv::ExecutionMode::from_u32(mode_id) + .ok_or(Error::UnsupportedExecutionMode(mode_id))?; + + match ep.stage { + ShaderStage::Fragment { + ref mut early_depth_test, + } => { + match mode { + ExecutionMode::EarlyFragmentTests => { + if early_depth_test.is_none() { + *early_depth_test = Some(crate::EarlyDepthTest { conservative: None }); + } + } + ExecutionMode::DepthUnchanged => { + *early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::Unchanged), + }); + } + ExecutionMode::DepthGreater => { + *early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::GreaterEqual), + }); + } + ExecutionMode::DepthLess => { + *early_depth_test = Some(crate::EarlyDepthTest { + conservative: Some(crate::ConservativeDepth::LessEqual), + }); + } + ExecutionMode::DepthReplacing => { + // Ignored because it can be deduced from the IR. + } + ExecutionMode::OriginUpperLeft => { + // Ignored because the other option (OriginLowerLeft) is not valid in Vulkan mode. + } + _ => { + return Err(Error::UnsupportedExecutionMode(mode_id)); + } + }; + } + ShaderStage::Compute { ref mut local_size } => { + match mode { + ExecutionMode::LocalSize => { + *local_size = (args[0], args[1], args[2]); + } + _ => { + return Err(Error::UnsupportedExecutionMode(mode_id)); + } + }; + } + _ => { + return Err(Error::UnsupportedExecutionMode(mode_id)); + } + }; + Ok(()) } diff --git a/src/front/wgsl/mod.rs b/src/front/wgsl/mod.rs index ec89afe88a..efced204f8 100644 --- a/src/front/wgsl/mod.rs +++ b/src/front/wgsl/mod.rs @@ -265,8 +265,12 @@ impl Parser { fn get_shader_stage(word: &str) -> Result> { match word { "vertex" => Ok(crate::ShaderStage::Vertex), - "fragment" => Ok(crate::ShaderStage::Fragment), - "compute" => Ok(crate::ShaderStage::Compute), + "fragment" => Ok(crate::ShaderStage::Fragment { + early_depth_test: None, + }), + "compute" => Ok(crate::ShaderStage::Compute { + local_size: (0, 0, 0), + }), _ => Err(Error::UnknownShaderStage(word)), } } diff --git a/src/lib.rs b/src/lib.rs index 3e25f88850..b662046c23 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,48 @@ pub struct Header { pub generator: u32, } +/// Early fragment tests. In a standard situation if a driver determines that it is possible to +/// switch on early depth test it will. Typical situations when early depth test is switched off: +/// - Calling ```discard``` in a shader. +/// - Writing to the depth buffer, unless ConservativeDepth is enabled. +/// +/// SPIR-V: ExecutionMode EarlyFragmentTests +/// In GLSL: layout(early_fragment_tests) in; +/// HLSL: Attribute earlydepthstencil +/// +/// For more, see: +/// - https://www.khronos.org/opengl/wiki/Early_Fragment_Test#Explicit_specification +/// - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/sm5-attributes-earlydepthstencil +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +pub struct EarlyDepthTest { + conservative: Option, +} +/// Enables adjusting depth without disabling early Z. +/// +/// SPIR-V: ExecutionMode DepthGreater/DepthLess/DepthUnchanged +/// GLSL: layout (depth_) out float gl_FragDepth; +/// - ```depth_any``` option behaves as if the layout qualifier was not present. +/// HLSL: SV_Depth/SV_DepthGreaterEqual/SV_DepthLessEqual +/// +/// For more, see: +/// - https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt +/// - https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#system-value-semantics +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "deserialize", derive(Deserialize))] +pub enum ConservativeDepth { + /// Shader may rewrite depth only with a value greater than calculated; + GreaterEqual, + + /// Shader may rewrite depth smaller than one that would have been written without the modification. + LessEqual, + + /// Shader may not rewrite depth value. + Unchanged, +} + /// Stage of the programmable pipeline. #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize))] @@ -52,8 +94,12 @@ pub struct Header { #[allow(missing_docs)] // The names are self evident pub enum ShaderStage { Vertex, - Fragment, - Compute, + Fragment { + early_depth_test: Option, + }, + Compute { + local_size: (u32, u32, u32), + }, } /// Class of storage for variables. diff --git a/tests/convert.rs b/tests/convert.rs index ef3431f560..0e1f6b8694 100644 --- a/tests/convert.rs +++ b/tests/convert.rs @@ -114,7 +114,9 @@ fn convert_phong_lighting() { let module = load_glsl( "glsl_phong_lighting.frag", "main", - naga::ShaderStage::Fragment, + naga::ShaderStage::Fragment { + early_depth_test: None, + }, ); naga::proc::Validator::new().validate(&module).unwrap(); @@ -133,7 +135,9 @@ fn constant_expressions() { let module = load_glsl( "glsl_constant_expression.vert", "main", - naga::ShaderStage::Fragment, + naga::ShaderStage::Fragment { + early_depth_test: None, + }, ); naga::proc::Validator::new().validate(&module).unwrap(); }