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
This commit is contained in:
Matus Talcik
2020-08-31 20:34:38 +02:00
committed by GitHub
parent dc442343d6
commit b67739034e
11 changed files with 333 additions and 182 deletions

View File

@@ -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!(),
},
),

View File

@@ -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 {

View File

@@ -1020,111 +1020,116 @@ impl<W: Write> Writer<W> {
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<W: Write> Writer<W> {
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<W: Write> Writer<W> {
// 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);

View File

@@ -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) {

View File

@@ -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
}
)
);
}

View File

@@ -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![],
}
}

View File

@@ -15,6 +15,7 @@ pub enum Error {
UnsupportedExtInst(spirv::Word),
UnsupportedType(Handle<crate::Type>),
UnsupportedExecutionModel(spirv::Word),
UnsupportedExecutionMode(spirv::Word),
UnsupportedStorageClass(spirv::Word),
UnsupportedImageDim(spirv::Word),
UnsupportedImageFormat(spirv::Word),

View File

@@ -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<spirv::Word>,
@@ -300,6 +304,7 @@ pub struct Parser<I> {
lookup_sampled_image: FastHashMap<spirv::Word, LookupSampledImage>,
lookup_function_type: FastHashMap<spirv::Word, LookupFunctionType>,
lookup_function: FastHashMap<spirv::Word, Handle<crate::Function>>,
lookup_entry_point: FastHashMap<spirv::Word, EntryPoint>,
deferred_function_calls: Vec<DeferredFunctionCall>,
}
@@ -322,6 +327,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
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<I: Iterator<Item = u32>> Parser<I> {
generator,
}
});
let mut entry_points = Vec::new();
while let Ok(inst) = self.next_inst() {
use spirv::Op;
@@ -1412,7 +1417,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
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<I: Iterator<Item = u32>> Parser<I> {
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<I: Iterator<Item = u32>> Parser<I> {
Ok(())
}
fn parse_entry_point(
&mut self,
inst: Instruction,
entry_points: &mut Vec<EntryPoint>,
) -> 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<I: Iterator<Item = u32>> Parser<I> {
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<spirv::Word> = 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(())
}

View File

@@ -265,8 +265,12 @@ impl Parser {
fn get_shader_stage(word: &str) -> Result<crate::ShaderStage, Error<'_>> {
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)),
}
}

View File

@@ -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<ConservativeDepth>,
}
/// Enables adjusting depth without disabling early Z.
///
/// SPIR-V: ExecutionMode DepthGreater/DepthLess/DepthUnchanged
/// GLSL: layout (depth_<greater/less/unchanged/any>) 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<EarlyDepthTest>,
},
Compute {
local_size: (u32, u32, u32),
},
}
/// Class of storage for variables.

View File

@@ -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();
}