microsoft / hlsl-specs

HLSL Specifications
MIT License
125 stars 34 forks source link

Deprecate SV_IsFrontFace as a geometry shader output #309

Open jenatali opened 1 year ago

jenatali commented 1 year ago

After several internal/offline discussions about this, I've come to the conclusion that this is the right change to make. I'm not sure how this particular "feature" came to be, but it seems that it was only ever supported by the compiler. Here's my case:

The D3D11 functional spec indicates that the only case where it is valid to output a "system generated value" is primitive ID, in the DXBC documentation for declaring an output system generated value.. It specifically uses "IsFrontFace" as an example of something that is invalid:

This may seem odd, because the System is supposed to "generate" a System Generated Value. But the purpose for this declaration is to allow a shader that inputs a System Generated Value to still be used in a scenario where a Shader Stage before it is activated, where the earlier Stage inputs the System Generated Value expected by the later stage. The earlier Stage can output the value to the later Stage (or it could make up its own value regardless of what the actual System Generated Value is), and pass that down to the later stage. It turns out the only System Generated Value this applies to is PrimitiveID, when passed from GS to PS. ... A System Generated Value cannot be output from a Stage that is before the place in the pipeline where the hardware would normally generate the value. e.g., a Geometry Shader cannot output "IsFrontFace", and a VS cannot output "PrimitiveID". The only stage that can sensibly output a System Generated Value is the Geometry Shader output of PrimitiveID to the Pixel Shader.

The only place in all of the D3D specs and documentation I can find where this feature is mentioned is on the HLSL semantics doc page: System-Value Semantic Description
SV_IsFrontFace Specifies whether a triangle is front facing. For lines and points, IsFrontFace has the value true. The exception is lines drawn out of triangles (wireframe mode), which sets IsFrontFace the same way as rasterizing the triangle in solid mode. Can be written to by the geometry shader, and read by the pixel shader.

There's no indication what the expected behavior should be, and given that SV_IsFrontFace is supposed to be a system-generated value, and not system-interpreted, it seems the only valid behavior would be for forwarding an already-generated value; but as discussed in the DXBC documentation linked above, the only stage which can receive this generated value is the pixel shader.

From skimming the WARP implementation, I see that it would not handle forwarding this value correctly. The SV_IsFrontFace value received in the pixel shader will always come from the rasterizer. Given that, plus some additional searching, I can find no test collateral which would attempt to validate behavior of outputting this value from a geometry shader. On top of all of this, a look at similar APIs across the industry reveal that neither GLSL nor SPIR-V (and by extension, GL and VK) have a similar construct.

So, this seems to have simply been a mistake, perhaps a simple copy/paste error in an old HLSL spec that was used to implement FXC's behavior, or a feature that was proposed but then was supposed to be cut, but FXC was never updated to remove it. This apparent mistake has carried forward into DXC. That said, I see no reason to continue supporting it, as it seems apparent that the hardware doesn't actually support this functionality. I propose that it should be invalid to specify this system value as a GS output, for future shader models at least, but I see no reason to continue to support this in current shader models either.

damyanp commented 3 months ago

To do here: