summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordavli-nv <davli@nvidia.com>2025-08-05 10:55:52 -0700
committerGitHub <noreply@github.com>2025-08-05 17:55:52 +0000
commit83675103a1a4fefde11b314aed26f4d37860efe7 (patch)
tree136f597da62532f4b9361ff0d9e785d413fef2f1
parent2d775b54d2ab7772785c2196075d4c7c174407ab (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.md4
-rw-r--r--docs/user-guide/a2-02-metal-target-specific.md2
-rw-r--r--docs/user-guide/a2-03-wgsl-target-specific.md2
-rw-r--r--source/slang/glsl.meta.slang3
-rw-r--r--source/slang/slang-emit-glsl.cpp8
-rw-r--r--source/slang/slang-emit-spirv.cpp17
-rw-r--r--source/slang/slang-ir-glsl-legalize.cpp18
-rw-r--r--tests/glsl/fragment-invocation-density-glsl.slang15
-rw-r--r--tests/spirv/fragment-invocation-density.slang25
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