HLSL: Added support for tessellation control/evaluation shaders#2604
HLSL: Added support for tessellation control/evaluation shaders#2604xen2 wants to merge 1 commit intoKhronosGroup:mainfrom
Conversation
45525a9 to
86cba34
Compare
|
It seems to work well enough on my use cases (tested with D3D11 on various shaders incl flat & PN, with and without adjacent edge average displacement. |
|
I need to get to this at some later point when I actually have time ... 1ksloc PRs of new difficult features takes a ton of energy and time to digest. |
|
No problem, I understand it is a big feature and it might take time to get improved/merged. |
86cba34 to
0eccc4d
Compare
|
FYI I am trying to rewrite it in a better way (getting rid of state machine, working directly at SPIR-V level, all changes local to If I succeed I will close this PR and open a new one. |
733fac5 to
b9d3f3e
Compare
|
I changed how the TCS split works. Old approach
New approach Work at the SPIR-V level instead:
Result
Each pass is just the existing As a result, it should be much more straightforward to review. Also added some simple unit test as bonus. |
|
It seems some tests are failing, I will investigate. edit: fixed now; however, still reviewing another issue (it works on my shaders but not after I run |
b9d3f3e to
d2318db
Compare
Emit HLSL hull (tesc) and domain (tese) shaders from Vulkan-style
SPIR-V where per-CP and patch-constant work share one entry point
gated by `if (gl_InvocationID == 0)` (the dxc/glslang pattern).
The hull entry is split into tesc_main + tesc_main_patch so
[patchconstantfunc] can point at the patch half. Done via in-place
block mutation: pre-scan for the gate header, OpControlBarrier and
per-CP OpFunctionCall; for each pass truncate the header's ops and
redirect its terminator to the merge block (per-CP) or the
then-block (patch), run emit_block_chain, restore state.
Also adds HS/DS I/O struct emission, builtin lowering for
SV_TessFactor / SV_InsideTessFactor / SV_DomainLocation /
SV_OutputControlPointID / InvocationId, a patch-constant wrapper
that marshals InputPatch/OutputPatch into globals, and [domain] /
[partitioning] / [outputtopology] attribute emission with sensible
defaults when execution modes are absent (e.g. TCS compiled without
a linked TES).
Includes shaders-hlsl/{tesc,tese}/basic.{tesc,tese} reference tests.
d2318db to
8bf4b0e
Compare
Adds hull shader (tesc) and domain shader (tese) code generation to the SPIRV-Cross HLSL backend.
This PR is built on top of #2603 which should be first merged in.
I also keep it as draft as I am still testing it in our project and refining it, but so far it worked well on several tessellation shaders our engine generates.
Please give me your thought regarding the general approach, would it be OK to merge it?
Hull shader (tesc) — function splitting
HLSL requires hull shader work split into a per-control-point entry point and a separate patch constant function, but SPIR-V expresses both as a single
main(). The compiler splits them using a redirected emission approach: rather than writing to the main output stream,emit_instruction()writes to one of two capture buffers (tesc_per_cp_bufferortesc_patch_buffer) based on aTescEmitPhasestate machine:OpAccessChainusinggl_InvocationIDis seen (output writes begin).OpFunctionCallto the patch constant function, or anOpStoreto a tess level/patch variable.if (gl_InvocationID == 0)construct; the closing brace is stripped and subsequent output is discarded.OpControlBarrieris suppressed entirely (hull shaders synchronize implicitly).After emission, the two buffers are assembled into
tesc_main()andtesc_main_patch(). A separatepatch_constant()wrapper callstesc_main_patch()and writes tess levels to theSPIRV_Cross_PatchConstantreturn struct.Note: The function splitting relies on the GLSL-to-SPIR-V compiler wrapping patch constant work in an
if (gl_InvocationID == 0) { ... }guard block. This is a limitation, but it is the pattern consistently emitted by both glslang and DXC when compiling GLSL/HLSL tessellation shaders to SPIR-V, so in practice it covers all real-world inputs.Domain shader (tese)
Nothing special, entry point signature:
[domain(...)] main(SPIRV_Cross_PatchConstant pc, const OutputPatch<SPIRV_Cross_Input, N> patch, float3/float2 domain : SV_DomainLocation).Patch-decorated inputs copy from
pc; per-CP inputs copy frompatch[i]in a loop.Other
SPIRV_Cross_PatchConstant: shared struct between tesc/tese with patch-decorated vars + tess level builtins (SV_TessFactor/SV_InsideTessFactor), sized by domain (tri: 3/1, quad: 4/2, isoline: 2/0).get_tesc_tese_flags(): merges execution mode flags from the other tessellation stage, since SPIR-V allows domain/spacing/winding modes on either entry point.input_cp_count: derived fromBuiltInPositioninput array size, with fallback to scanning other per-CP input arrays, thenoutput_vertices. Maybe there is a better way?SPIRV_Cross_PatchConstantgl_TessCoordassigned fromSV_DomainLocationat entry.