-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Vulkan support for SHADER_EARLY_DEPTH_TEST
and fix to conservative depth optimizations
#7676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
9463d45
9767039
f873559
6ec187d
468375a
2061d13
323c6d9
04dc8a1
59f0767
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -237,19 +237,27 @@ use crate::{FastIndexMap, NamedExpressions}; | |
|
||
pub use block::Block; | ||
|
||
/// Early fragment tests. | ||
/// Explicitly allows early depth/stencil tests. | ||
/// | ||
/// In a standard situation, if a driver determines that it is possible to switch on early depth test, it will. | ||
/// Normally, depth/stencil tests are performed after fragment shading. However, as an optimization, | ||
/// most drivers will move the depth/stencil tests before fragment shading if this does not | ||
/// have any observable consequences. This optimization is disabled under the following | ||
/// circumstances: | ||
/// - `discard` is called in the fragment shader. | ||
/// - The fragment shader writes to the depth buffer. | ||
/// - The fragment shader writes to any storage bindings. | ||
/// | ||
/// Typical situations when early depth test is switched off: | ||
/// - Calling `discard` in a shader. | ||
/// - Writing to the depth buffer, unless ConservativeDepth is enabled. | ||
/// When `EarlyDepthTest` is set, it is allowed to perform an early depth/stencil test even if the | ||
/// above conditions are not met. When [`EarlyDepthTest::Force`] is used, depth/stencil tests | ||
/// **must** be performed before fragment shading. | ||
/// | ||
/// To use in a shader: | ||
/// To force early depth/stencil tests in a shader: | ||
/// - GLSL: `layout(early_fragment_tests) in;` | ||
/// - HLSL: `Attribute earlydepthstencil` | ||
/// - SPIR-V: `ExecutionMode EarlyFragmentTests` | ||
/// - WGSL: `@early_depth_test` | ||
/// - WGSL: `@early_depth_test(force)` | ||
Comment on lines
+254
to
+258
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should also document the non-force case and what variants it has There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a link to |
||
/// | ||
/// This may also be enabled in a shader by specifying a [`ConservativeDepth`]. | ||
/// | ||
/// For more, see: | ||
/// - <https://www.khronos.org/opengl/wiki/Early_Fragment_Test#Explicit_specification> | ||
|
@@ -259,8 +267,24 @@ pub use block::Block; | |
#[cfg_attr(feature = "serialize", derive(Serialize))] | ||
#[cfg_attr(feature = "deserialize", derive(Deserialize))] | ||
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))] | ||
pub struct EarlyDepthTest { | ||
pub conservative: Option<ConservativeDepth>, | ||
pub enum EarlyDepthTest { | ||
/// Requires depth/stencil tests to be performed before fragment shading. | ||
/// | ||
/// This will disable depth/stencil tests after fragment shading, so discarding the fragment | ||
/// or overwriting the fragment depth will have no effect. | ||
Force, | ||
|
||
/// Allows an additional depth/stencil test to be performed before fragment shading. | ||
/// | ||
/// It is up to the driver to decide whether early tests are performed. Unlike `Force`, this | ||
/// does not disable depth/stencil tests after fragment shading. | ||
Allow { | ||
/// Specifies restrictions on how the depth value can be modified within the fragment | ||
/// shader. | ||
/// | ||
/// This may be taken into account when deciding whether to perform early tests. | ||
conservative: ConservativeDepth, | ||
}, | ||
} | ||
|
||
/// Enables adjusting depth without disabling early Z. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
god_mode = true | ||
targets = "SPIRV | GLSL" | ||
|
||
[glsl] | ||
version.Desktop = 420 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@fragment | ||
@early_depth_test(less_equal) | ||
fn main(@builtin(position) pos: vec4<f32>) -> @builtin(frag_depth) f32 { | ||
return pos.z - 0.1; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
god_mode = true | ||
targets = "SPIRV | GLSL" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@fragment | ||
@early_depth_test(force) | ||
fn main() -> @location(0) vec4<f32> { | ||
return vec4<f32>(0.4, 0.3, 0.2, 0.1); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#version 420 core | ||
layout (depth_less) out float gl_FragDepth; | ||
|
||
void main() { | ||
vec4 pos = gl_FragCoord; | ||
gl_FragDepth = (pos.z - 0.1); | ||
return; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#version 310 es | ||
|
||
precision highp float; | ||
precision highp int; | ||
|
||
layout(early_fragment_tests) in; | ||
layout(location = 0) out vec4 _fs2p_location0; | ||
|
||
void main() { | ||
_fs2p_location0 = vec4(0.4, 0.3, 0.2, 0.1); | ||
return; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
; SPIR-V | ||
; Version: 1.1 | ||
; Generator: rspirv | ||
; Bound: 17 | ||
OpCapability Shader | ||
%1 = OpExtInstImport "GLSL.std.450" | ||
OpMemoryModel Logical GLSL450 | ||
OpEntryPoint Fragment %11 "main" %6 %9 | ||
OpExecutionMode %11 OriginUpperLeft | ||
OpExecutionMode %11 DepthLess | ||
OpExecutionMode %11 DepthReplacing | ||
OpDecorate %6 BuiltIn FragCoord | ||
OpDecorate %9 BuiltIn FragDepth | ||
%2 = OpTypeVoid | ||
%3 = OpTypeFloat 32 | ||
%4 = OpTypeVector %3 4 | ||
%7 = OpTypePointer Input %4 | ||
%6 = OpVariable %7 Input | ||
%10 = OpTypePointer Output %3 | ||
%9 = OpVariable %10 Output | ||
%12 = OpTypeFunction %2 | ||
%13 = OpConstant %3 0.1 | ||
%11 = OpFunction %2 None %12 | ||
%5 = OpLabel | ||
%8 = OpLoad %4 %6 | ||
OpBranch %14 | ||
%14 = OpLabel | ||
%15 = OpCompositeExtract %3 %8 2 | ||
%16 = OpFSub %3 %15 %13 | ||
OpStore %9 %16 | ||
OpReturn | ||
OpFunctionEnd |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
; SPIR-V | ||
; Version: 1.1 | ||
; Generator: rspirv | ||
; Bound: 16 | ||
OpCapability Shader | ||
%1 = OpExtInstImport "GLSL.std.450" | ||
OpMemoryModel Logical GLSL450 | ||
OpEntryPoint Fragment %8 "main" %6 | ||
OpExecutionMode %8 OriginUpperLeft | ||
OpExecutionMode %8 EarlyFragmentTests | ||
OpDecorate %6 Location 0 | ||
%2 = OpTypeVoid | ||
%4 = OpTypeFloat 32 | ||
%3 = OpTypeVector %4 4 | ||
%7 = OpTypePointer Output %3 | ||
%6 = OpVariable %7 Output | ||
%9 = OpTypeFunction %2 | ||
%10 = OpConstant %4 0.4 | ||
%11 = OpConstant %4 0.3 | ||
%12 = OpConstant %4 0.2 | ||
%13 = OpConstant %4 0.1 | ||
%14 = OpConstantComposite %3 %10 %11 %12 %13 | ||
%8 = OpFunction %2 None %9 | ||
%5 = OpLabel | ||
OpBranch %15 | ||
%15 = OpLabel | ||
OpStore %6 %14 | ||
OpReturn | ||
OpFunctionEnd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the PR description you wrote that specifying both is invalid. But that's not documented here and not validated on parsing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This statement is true as written: the commonality between both variants is that it allows early depth tests in scenarios where the WGPU/WGLSL specs normally wouldn't permit it. Forcing an early depth test necessarily implies that an early depth test is allowed.
I think the fact that this is an enum should be sufficient documentation that the two variants are mutually exclusive?
Can you clarify what you mean by validation? It's not possible to specify both variants in WGSL using the
early_depth_test
attribute, since you have to either specifyforce
or a conservative depth. In the other shading languages, its technically not illegal (as far as I know) to both force early fragment tests and provide a conservative depth specifier, merely pointless.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ray marchers benefit immensely from being able to use both the
depth_greater
specifier and early depth tests, as they are incredibly expensively operations only executable in fragment shaders.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For ray marchers, you should only specify
depth_greater
and hope that the driver decides to perform early depth tests (which it probably will). If you force early depth tests, that will disable depth writes from the shader and give you incorrect results. See:Vulkan Spec:
Not as explicit, but says
EarlyFragmentTests
moves fragment shading after the depth test andDepthGreater
is only used to allow an additional depth test before fragment shading. Thus, when used in combination, there will be no depth test after shading to write out yourFragDepth
valueMetal Spec: 5.1.2