shader: support quad tessellation domains and factor routing

This commit is contained in:
Çağatay Güçlü
2026-10-28 22:09:39 +01:00
committed by nmzik
parent a8e3c29b2e
commit 128c938514
2 changed files with 49 additions and 25 deletions
@@ -30,11 +30,13 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
if (kind == Attribute::Factor) {
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
const auto index = inst.Arg(1).U32() / 4u;
const auto outer = index < 3u;
EXIT_NOT_IMPLEMENTED(index >= 4u);
const bool quad = tess.domain == 2u;
const auto outer_count = quad ? 4u : 3u;
const bool outer = index < outer_count;
EXIT_NOT_IMPLEMENTED(index >= (quad ? 6u : 4u));
state.builder.AddFunction(spv::OpAccessChain, TypePointer(state, storage, TypeF32(state)),
pointer, outer ? variable : state.tess_inner_variable,
ConstantU32(state, outer ? index : index - 3u));
ConstantU32(state, outer ? index : index - outer_count));
return pointer;
}
auto address = ctx.Arg(inst, 1);
@@ -128,8 +130,8 @@ void DefineTessellationInterfaces(EmitterState& state) {
void DefineTessellationExecutionModes(EmitterState& state) {
const auto& tess = state.input_info.vertex->tess;
// CxVgtTfParam: triangles, integer or fractional-odd partitioning, clockwise output.
EXIT_NOT_IMPLEMENTED(tess.domain != 1u ||
// Triangle or quad domain, integer or fractional-odd partitioning, clockwise output.
EXIT_NOT_IMPLEMENTED((tess.domain != 1u && tess.domain != 2u) ||
(tess.partitioning != 0u && tess.partitioning != 2u) ||
tess.output_topology != 2u);
state.builder.RequireCapability(spv::CapabilityTessellation);
@@ -137,7 +139,9 @@ void DefineTessellationExecutionModes(EmitterState& state) {
state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeOutputVertices,
tess.output_control_points);
} else {
state.builder.AddExecutionMode(state.main_func, spv::ExecutionModeTriangles);
state.builder.AddExecutionMode(state.main_func, tess.domain == 1u
? spv::ExecutionModeTriangles
: spv::ExecutionModeQuads);
state.builder.AddExecutionMode(
state.main_func, tess.partitioning == 0u ? spv::ExecutionModeSpacingEqual
: spv::ExecutionModeSpacingFractionalOdd);
+38 -18
View File
@@ -47109,8 +47109,10 @@ bool HasPatchInterface(const std::string &assembly, const std::string &storage,
std::string::npos;
}
void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride, u32 domain = 1) {
using namespace ShaderRecompiler;
const bool quad = domain == 2;
const u32 control_points = quad ? 4 : 3;
const bool multiplied_stride = hs_stride == 112;
const u32 local_address = multiplied_stride ? 18 : 21;
const u32 control_point = multiplied_stride ? 30 : 34;
@@ -47145,12 +47147,12 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
control.push_back(ls_stride);
AppendVop3(&control, 0x143, output_address, 255u, Vgpr(relative_patch),
Vgpr(0));
control.push_back(3 * hs_stride);
control.push_back(control_points * hs_stride);
AppendVop3(&control, 0x143, input_address, 255u, Vgpr(relative_patch),
Vgpr(1));
control.push_back(3 * ls_stride);
control.push_back(control_points * ls_stride);
} else {
control.push_back(EncodeSopk(0x00, 106, 3 * hs_stride));
control.push_back(EncodeSopk(0x00, 106, control_points * hs_stride));
control.push_back(EncodeVop2(0x0b, 2, 249u, 1));
control.push_back(EncodeVop2Sdwa(106, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1));
AppendVop3(&control, 0x346, output_address, Vgpr(control_point), InlineU32(7),
@@ -47169,7 +47171,7 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
AppendVMovLiteral(&control, 4, 0x42280000u);
AppendVMovLiteral(&control, 5, 0x422c0000u);
control.push_back(EncodeVopc(0xd4, InlineU32(1), control_point));
control.push_back(EncodeVop2(0x1a, 4, InlineU32(4), relative_patch));
control.push_back(EncodeVop2(0x0b, 4, InlineU32(quad ? 24 : 16), relative_patch));
control.push_back(EncodeVop2(0x1a, 6, InlineU32(6), relative_patch));
control.push_back(EncodeVop2(0x26, 5, 255u, 6));
control.push_back(0x7fc0);
@@ -47178,6 +47180,12 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
}
control.push_back(EncodeMubuf0(0x1e));
control.push_back(EncodeMubuf1(0, 2, 4, 4));
if (quad) {
AppendVMovLiteral(&control, 6, std::bit_cast<u32>(5.0f));
AppendVMovLiteral(&control, 7, std::bit_cast<u32>(6.0f));
control.push_back(EncodeMubuf0(0x1d, 16));
control.push_back(EncodeMubuf1(6, 2, 4, 4));
}
for (u32 i = 0; i < 4; i++) {
control.push_back(EncodeMubuf0(0x1c, i * 16));
control.push_back(EncodeMubuf1(i, 2, 5, 2));
@@ -47185,7 +47193,7 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
AppendEnd(&control);
AppendVop3(&evaluation, 0x169, 24, 255u, Vgpr(7));
evaluation.push_back(3 * hs_stride);
evaluation.push_back(control_points * hs_stride);
evaluation.push_back(EncodeMubuf0(0x0d, 28));
evaluation.push_back(EncodeMubuf1(10, 2, 24, 4));
evaluation.push_back(EncodeMubuf0(0x0d, 2 * hs_stride + 28));
@@ -47194,9 +47202,9 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
evaluation.push_back(EncodeExp1(5, 6, 10, 12));
AppendEnd(&evaluation);
ShaderTessellationInputInfo tess{.input_control_points = 3,
.output_control_points = 3,
.domain = 1,
ShaderTessellationInputInfo tess{.input_control_points = control_points,
.output_control_points = control_points,
.domain = domain,
.partitioning = 2,
.output_topology = 2};
AnalyzeTessellationPrograms(local, control, tess);
@@ -47216,9 +47224,24 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
Require(name, "offchip component addressing",
ds.addresses[Attribute::EvaluationInput] == std::set<u32>{28, 2 * hs_stride + 28},
"TES control point and component offsets must survive lowering");
Require(name, "triangle factor layout",
hs.addresses[Attribute::Factor] == std::set<u32>{0, 4, 8, 12},
"the four packed guest factors must retain their order");
Require(name, "factor layout",
hs.addresses[Attribute::Factor] ==
(quad ? std::set<u32>{0, 4, 8, 12, 16, 20} : std::set<u32>{0, 4, 8, 12}),
"packed guest factors must retain their order");
const auto factor_access = [&](const char *builtin, u32 index) {
const auto end = hs.assembly.find(std::string(" BuiltIn ") + builtin);
Require(name, "factor builtin", end != std::string::npos, "missing tessellation factor");
const auto begin = hs.assembly.rfind('%', end);
const auto variable = hs.assembly.substr(begin, end - begin);
return hs.assembly.find(" " + variable + " %uint_" + std::to_string(index) + "\n") !=
std::string::npos;
};
Require(name, "outer and inner factor routing",
factor_access("TessLevelOuter", 2) &&
factor_access("TessLevelOuter", 3) == quad &&
factor_access("TessLevelInner", 0) &&
factor_access("TessLevelInner", 1) == quad,
"edge factors must precede the domain's interior factors");
Require(name, "predicated patch addresses",
hs.addresses[Attribute::PatchOutput] == std::set<u32>{0x7fc0, 0x7fd0, 0x7fe0, 0x7ff0},
"CP0 patch stores must use the address assigned on their active predicate");
@@ -47234,16 +47257,12 @@ void CheckTessellationProgram(const char *name, u32 ls_stride, u32 hs_stride) {
hs.assembly.find("OpMemoryModel Logical GLSL450") != std::string::npos &&
hs.assembly.find("OpControlBarrier %uint_2 %uint_4 %uint_0") != std::string::npos,
"HS barriers must match GLSL450 control point synchronization semantics");
Require(name, "factor builtins",
hs.assembly.find("TessLevelOuter") != std::string::npos &&
hs.assembly.find("TessLevelInner") != std::string::npos,
"triangle factors require outer[4] and inner[2] Vulkan builtins");
Require(name, "native domain and winding",
ds.assembly.find("TessCoord") != std::string::npos &&
ds.assembly.find("Triangles") != 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,
"TES must preserve guest triangle domain, partitioning, and winding");
"TES must preserve guest domain, partitioning, and winding");
std::printf("[host] %-32s ok\n", name);
}
@@ -47418,6 +47437,7 @@ void CheckTessellationPatchConstants() {
void CheckTessellationPrograms() {
CheckTessellationProgram("TessellationShiftedStride", 124, 128);
CheckTessellationProgram("TessellationMultipliedStride", 108, 112);
CheckTessellationProgram("TessellationQuadFactors", 36, 128, 2);
CheckTessellationPatchConstants();
}