diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterTessellation.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterTessellation.cpp index 54d25b8e3..d9ce3a8bf 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterTessellation.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterTessellation.cpp @@ -130,10 +130,10 @@ void DefineTessellationInterfaces(EmitterState& state) { void DefineTessellationExecutionModes(EmitterState& state) { const auto& tess = state.input_info.vertex->tess; - // Triangle or quad domain, integer or fractional-odd partitioning, clockwise output. + // Triangle or quad domain, integer or fractional-odd partitioning, triangle output. EXIT_NOT_IMPLEMENTED((tess.domain != 1u && tess.domain != 2u) || (tess.partitioning != 0u && tess.partitioning != 2u) || - tess.output_topology != 2u); + (tess.output_topology != 2u && tess.output_topology != 3u)); state.builder.RequireCapability(spv::CapabilityTessellation); if (state.program.stage == ShaderType::TessellationControl) { state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeOutputVertices, @@ -145,7 +145,9 @@ void DefineTessellationExecutionModes(EmitterState& state) { state.builder.AddExecutionMode( state.main_func, tess.partitioning == 0u ? spv::ExecutionModeSpacingEqual : spv::ExecutionModeSpacingFractionalOdd); - state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeVertexOrderCw); + state.builder.AddExecutionMode(state.main_func, tess.output_topology == 2u + ? spv::ExecutionModeVertexOrderCw + : spv::ExecutionModeVertexOrderCcw); } } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 202db0460..08eef6d86 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -47480,7 +47480,8 @@ bool HasPatchInterface(const std::string &assembly, const std::string &storage, std::string::npos; } -void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride, u32 domain = 1) { +void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride, u32 domain = 1, + u32 output_topology = 2) { using namespace ShaderRecompiler; const bool quad = domain == 2; const u32 control_points = quad ? 4 : 3; @@ -47584,7 +47585,7 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride, u3 .output_control_points = control_points, .domain = domain, .partitioning = 2, - .output_topology = 2}; + .output_topology = output_topology}; AnalyzeTessellationPrograms(local, control, tess); Require(name, "decoded interface", tess.ls_stride == ls_stride && tess.hs_stride == hs_stride, @@ -47639,7 +47640,10 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride, u3 ds.assembly.find("TessCoord") != std::string::npos && ds.assembly.find(quad ? "Quads" : "Triangles") != std::string::npos && ds.assembly.find("SpacingFractionalOdd") != std::string::npos && - ds.assembly.find("VertexOrderCw") != std::string::npos, + ds.assembly.find(output_topology == 2 ? "VertexOrderCw" : "VertexOrderCcw") != + std::string::npos && + ds.assembly.find(output_topology == 2 ? "VertexOrderCcw" : "VertexOrderCw") == + std::string::npos, "TES must preserve guest domain, partitioning, and winding"); std::printf("[host] %-32s ok\n", name); } @@ -47816,6 +47820,7 @@ void CheckTessellationPrograms() { CheckTessellationProgram("TessellationShiftedStride", 124, 128); CheckTessellationProgram("TessellationMultipliedStride", 108, 112); CheckTessellationProgram("TessellationQuadFactors", 36, 128, 2); + CheckTessellationProgram("TessellationCounterclockwiseQuads", 36, 128, 2, 3); CheckTessellationProgram("TessellationMaskedControlPoint", 32, 256, 2); CheckTessellationPatchConstants(); }