diff options
| author | davli-nv <davli@nvidia.com> | 2025-08-05 10:55:52 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-05 17:55:52 +0000 |
| commit | 83675103a1a4fefde11b314aed26f4d37860efe7 (patch) | |
| tree | 136f597da62532f4b9361ff0d9e785d413fef2f1 | |
| parent | 2d775b54d2ab7772785c2196075d4c7c174407ab (diff) | |
Implement SPV_EXT_fragment_invocation_density (SPV_NV_shading_rate) (#8037)
* Implement SPV_EXT_fragment_invocation_density
-Adds semantics SV_FragSize and SV_FragInvocationCount and implements them for SPIRV and GLSL using the appropriate target builtins from extensions.
-Adds test case checking for expected target builtins from these semantics.
-For future work, could implement SV_FragSize using pixel shader input SV_ShadingRate for HLSL, and SV_FragInvocationCount needs research.
Fixes #7974
Generated with Claude Code
* address review feedback
https://github.com/shader-slang/slang/pull/8037#pullrequestreview-3084645845
* fixup format
* review feedback
https://github.com/shader-slang/slang/pull/8037#pullrequestreview-3086442819
| -rw-r--r-- | docs/user-guide/a2-01-spirv-target-specific.md | 4 | ||||
| -rw-r--r-- | docs/user-guide/a2-02-metal-target-specific.md | 2 | ||||
| -rw-r--r-- | docs/user-guide/a2-03-wgsl-target-specific.md | 2 | ||||
| -rw-r--r-- | source/slang/glsl.meta.slang | 3 | ||||
| -rw-r--r-- | source/slang/slang-emit-glsl.cpp | 8 | ||||
| -rw-r--r-- | source/slang/slang-emit-spirv.cpp | 17 | ||||
| -rw-r--r-- | source/slang/slang-ir-glsl-legalize.cpp | 18 | ||||
| -rw-r--r-- | tests/glsl/fragment-invocation-density-glsl.slang | 15 | ||||
| -rw-r--r-- | tests/spirv/fragment-invocation-density.slang | 25 |
9 files changed, 93 insertions, 1 deletions
diff --git a/docs/user-guide/a2-01-spirv-target-specific.md b/docs/user-guide/a2-01-spirv-target-specific.md index 05bf023df..0f64a5e1b 100644 --- a/docs/user-guide/a2-01-spirv-target-specific.md +++ b/docs/user-guide/a2-01-spirv-target-specific.md @@ -67,6 +67,8 @@ The system-value semantics are translated to the following SPIR-V code. | `SV_DomainLocation` | `BuiltIn TessCoord` | | `SV_DrawIndex`<sup>*</sup> | `Builtin DrawIndex` | | `SV_DeviceIndex` | `Builtin DeviceIndex` | +| `SV_FragInvocationCount` | `Builtin FragInvocationCountExt` | +| `SV_FragSize` | `Builtin FragSizeExt` | | `SV_GSInstanceID` | `BuiltIn InvocationId` | | `SV_GroupID` | `BuiltIn WorkgroupId` | | `SV_GroupIndex` | `BuiltIn LocalInvocationIndex` | @@ -95,7 +97,7 @@ The system-value semantics are translated to the following SPIR-V code. | `SV_VulkanInstanceID` | `BuiltIn InstanceIndex` | | `SV_VulkanVertexID` | `BuiltIn VertexIndex` | -*Note* that `SV_DrawIndex`, `SV_PointSize` and `SV_PointCoord` are Slang-specific semantics that are not defined in HLSL. +*Note* that `SV_DrawIndex`, `SV_FragInvocationCount`, `SV_FragSize`, `SV_PointSize` and `SV_PointCoord` are Slang-specific semantics that are not defined in HLSL. Also *Note* that `SV_InstanceID`/`SV_VertexID` counts all instances/vertices in a draw call, unlike how `InstanceIndex`/`VertexIndex` is relative to `BaseInstance`/`BaseVertex`. See [Using SV_InstanceID/SV_VertexID with SPIR-V target](#using-sv_instanceid-and-sv_vertexid-with-spir-v-target) diff --git a/docs/user-guide/a2-02-metal-target-specific.md b/docs/user-guide/a2-02-metal-target-specific.md index c0360efda..5d3e336db 100644 --- a/docs/user-guide/a2-02-metal-target-specific.md +++ b/docs/user-guide/a2-02-metal-target-specific.md @@ -29,6 +29,8 @@ The system-value semantics are translated to the following Metal attributes: | `SV_DepthGreaterEqual` | `[[depth(greater)]]` | | `SV_DepthLessEqual` | `[[depth(less)]]` | | `SV_DispatchThreadID` | `[[thread_position_in_grid]]` | +| `SV_FragInvocationCount` | `(Not supported)` | +| `SV_FragSize` | `(Not supported)` | | `SV_GroupID` | `[[threadgroup_position_in_grid]]` | | `SV_GroupThreadID` | `[[thread_position_in_threadgroup]]` | | `SV_GroupIndex` | Calculated from `SV_GroupThreadID` and group extents | diff --git a/docs/user-guide/a2-03-wgsl-target-specific.md b/docs/user-guide/a2-03-wgsl-target-specific.md index e8a918dc8..35aecd291 100644 --- a/docs/user-guide/a2-03-wgsl-target-specific.md +++ b/docs/user-guide/a2-03-wgsl-target-specific.md @@ -26,6 +26,8 @@ The system-value semantics are translated to the following WGSL code. | SV_DepthLessEqual | *Not supported* | | SV_DispatchThreadID | `@builtin(global_invocation_id)` | | SV_DomainLocation | *Not supported* | +| SV_FragInvocationCount | *Not supported* | +| SV_FragSize | *Not supported* | | SV_GSInstanceID | *Not supported* | | SV_GroupID | `@builtin(workgroup_id)` | | SV_GroupIndex | `@builtin(local_invocation_index)` | diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang index dfa858eca..782c09bb9 100644 --- a/source/slang/glsl.meta.slang +++ b/source/slang/glsl.meta.slang @@ -216,6 +216,9 @@ public in int gl_ViewportIndex : SV_ViewportArrayIndex; public in int gl_BaseVertex : SV_StartVertexLocation; public in int gl_BaseInstance : SV_StartInstanceLocation; +public in int gl_FragInvocationCountEXT : SV_FragInvocationCount; +public in int2 gl_FragSizeEXT : SV_FragSize; + // Override operator* behavior to compute algebric product of matrices and vectors. diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index eb71286a2..eb6d4c694 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -1246,6 +1246,14 @@ void GLSLSourceEmitter::_maybeEmitGLSLBuiltin(IRGlobalParam* var, UnownedStringS { _requireGLSLExtension(toSlice("GL_EXT_fragment_shading_rate_primitive")); } + else if (name == "gl_FragSizeEXT") + { + _requireGLSLExtension(toSlice("GL_EXT_fragment_invocation_density")); + } + else if (name == "gl_FragInvocationCountEXT") + { + _requireGLSLExtension(toSlice("GL_EXT_fragment_invocation_density")); + } else if (name == "gl_DrawID") { _requireGLSLVersion(460); diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp index da2620856..a5a77e148 100644 --- a/source/slang/slang-emit-spirv.cpp +++ b/source/slang/slang-emit-spirv.cpp @@ -6226,6 +6226,23 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex SpvBuiltInCullPrimitiveEXT, inst); } + else if (semanticName == "sv_fragsize") + { + requireSPIRVCapability(SpvCapabilityFragmentDensityEXT); + ensureExtensionDeclaration( + UnownedStringSlice("SPV_EXT_fragment_invocation_density")); + return getBuiltinGlobalVar(inst->getFullType(), SpvBuiltInFragSizeEXT, inst); + } + else if (semanticName == "sv_fraginvocationcount") + { + requireSPIRVCapability(SpvCapabilityFragmentDensityEXT); + ensureExtensionDeclaration( + UnownedStringSlice("SPV_EXT_fragment_invocation_density")); + return getBuiltinGlobalVar( + inst->getFullType(), + SpvBuiltInFragInvocationCountEXT, + inst); + } else if (semanticName == "sv_shadingrate") { requireSPIRVCapability(SpvCapabilityFragmentShadingRateKHR); diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index af14eaee0..dce85efd3 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -885,6 +885,24 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( name = "gl_PrimitiveShadingRateEXT"; } } + else if (semanticName == "sv_fragsize") + { + name = "gl_FragSizeEXT"; + context->requireGLSLVersion(ProfileVersion::GLSL_450); + context->requireGLSLExtension( + UnownedStringSlice::fromLiteral("GL_EXT_fragment_invocation_density")); + requiredType = builder->getVectorType( + builder->getBasicType(BaseType::Int), + builder->getIntValue(builder->getIntType(), 2)); + } + else if (semanticName == "sv_fraginvocationcount") + { + name = "gl_FragInvocationCountEXT"; + context->requireGLSLVersion(ProfileVersion::GLSL_450); + context->requireGLSLExtension( + UnownedStringSlice::fromLiteral("GL_EXT_fragment_invocation_density")); + requiredType = builder->getIntType(); + } else if (semanticName == "sv_startvertexlocation") { context->requireGLSLVersion(ProfileVersion::GLSL_460); diff --git a/tests/glsl/fragment-invocation-density-glsl.slang b/tests/glsl/fragment-invocation-density-glsl.slang new file mode 100644 index 000000000..7cee4620b --- /dev/null +++ b/tests/glsl/fragment-invocation-density-glsl.slang @@ -0,0 +1,15 @@ +//TEST:SIMPLE(filecheck=CHECK-GLSL): -target glsl -stage fragment -entry main + +// Test for EXT_fragment_invocation_density support (gl_FragSizeEXT and gl_FragInvocationCountEXT ) +import glsl; + +layout(location = 0) out highp vec4 FragColor; + +void main() +{ + // CHECK-GLSL: #extension GL_EXT_fragment_invocation_density : require + // CHECK-GLSL-DAG: gl_FragSizeEXT + // CHECK-GLSL-DAG: gl_FragInvocationCountEXT + + FragColor = vec4(gl_FragSizeEXT.x, gl_FragSizeEXT.y, gl_FragInvocationCountEXT, 1.0); +}
\ No newline at end of file diff --git a/tests/spirv/fragment-invocation-density.slang b/tests/spirv/fragment-invocation-density.slang new file mode 100644 index 000000000..f26b403ec --- /dev/null +++ b/tests/spirv/fragment-invocation-density.slang @@ -0,0 +1,25 @@ +//TEST:SIMPLE(filecheck=CHECK-SPIRV): -target spirv -stage fragment -entry main +//TEST:SIMPLE(filecheck=CHECK-GLSL): -target glsl -stage fragment -entry main + +// Test for SPV_EXT_fragment_invocation_density support (SV_FragSize and SV_FragInvocationCount) + +struct FragmentInput +{ + int2 fragmentSize : SV_FragSize; + int invocationsPerPixel : SV_FragInvocationCount; +} + +[shader("fragment")] +float4 main(FragmentInput input) : SV_Target +{ + // CHECK-SPIRV: OpCapability FragmentDensityEXT + // CHECK-SPIRV: OpExtension "SPV_EXT_fragment_invocation_density" + // CHECK-SPIRV-DAG: OpDecorate {{.*}} BuiltIn FragSizeEXT + // CHECK-SPIRV-DAG: OpDecorate {{.*}} BuiltIn FragInvocationCountEXT + + // CHECK-GLSL: #extension GL_EXT_fragment_invocation_density : require + // CHECK-GLSL-DAG: gl_FragSizeEXT + // CHECK-GLSL-DAG: gl_FragInvocationCountEXT + + return float4(input.fragmentSize.x, input.fragmentSize.y, input.invocationsPerPixel, 1.0); +}
\ No newline at end of file |
