Merge pull request #6129 from fdfawef324/claude/agc-tessellation-refusal-fields

fix(agc): print the VGT_TF_PARAM and VGT_LS_HS_CONFIG fields of a refused tessellation draw
This commit is contained in:
Dean authored and GitHub committed 2026-10-19 02:54:51 +13:00
commit f834e249c4
2 files changed
+45 -1

No files matched your search

@@ -17,6 +17,7 @@
#include <span>
#include <array>
#include <sstream>
#include <string_view>
namespace AgcDriver::Graphics {
namespace {
@@ -50,6 +51,25 @@ std::string zeroMessage(std::uint32_t offset, std::uint32_t value, const char* n
return std::string(name) + " is unsupported" + detail;
}
std::string tessellationMessage(std::uint32_t config, std::uint32_t parameters, const ShaderRecompiler::TessellationConfiguration& tess) {
static constexpr std::array<std::string_view, 3> Types{"TESS_ISOLINE", "TESS_TRIANGLE", "TESS_QUAD"};
static constexpr std::array<std::string_view, 4> Partitionings{"PART_INTEGER", "PART_POW2", "PART_FRAC_ODD", "PART_FRAC_EVEN"};
static constexpr std::array<std::string_view, 4> Topologies{"OUTPUT_POINT", "OUTPUT_LINE", "OUTPUT_TRIANGLE_CW", "OUTPUT_TRIANGLE_CCW"};
std::ostringstream message;
const auto field = [&](std::string_view name, std::uint32_t value, std::span<const std::string_view> names) {
message << name << '=' << value;
if (value < names.size()) message << ' ' << names[value];
};
message << "VGT_TF_PARAM=0x" << std::hex << parameters << std::dec << " (";
field("TYPE", tess.domain, Types);
message << ", ";
field("PARTITIONING", tess.partitioning, Partitionings);
message << ", ";
field("TOPOLOGY", tess.outputTopology, Topologies);
message << "), VGT_LS_HS_CONFIG=0x" << std::hex << config << std::dec << " (NUM_PATCHES=" << tess.patchesPerGroup << ", HS_NUM_INPUT_CP=" << tess.inputControlPoints << ", HS_NUM_OUTPUT_CP=" << tess.outputControlPoints << "): only triangle and quad domains with integer, fractional-even or fractional-odd partitioning and triangle output are supported by the reference path";
return message.str();
}
void zero(const Registers& registers, std::uint32_t offset, std::uint32_t mask, const char* name, RegisterBank bank = RegisterBank::Context) {
const auto value = read(registers, offset, bank);
if ((value & mask) != 0) throw std::runtime_error(zeroMessage(offset, value, name));
@@ -915,7 +935,7 @@ ShaderStages DecodeShaderStages(const QueueState& queue, bool vertexWorkgroup) {
validate(tess.inputControlPoints != 0 && tess.inputControlPoints <= 32 && tess.outputControlPoints != 0 && tess.outputControlPoints <= 32, "invalid tessellation control-point counts");
validate(tess.patchesPerGroup != 0, "VGT_LS_HS_CONFIG.NUM_PATCHES is zero");
if ((parameters & ~0x000600ffu) != 0) Require(false, zeroMessage(0x2db, parameters, "VGT_TF_PARAM donut, factor detection, memory type or reserved state"));
validate((tess.domain == 1 || tess.domain == 2) && (tess.partitioning == 0 || tess.partitioning == 2 || tess.partitioning == 3) && (tess.outputTopology == 2 || tess.outputTopology == 3), "only triangle and quad domains with integer, fractional-even or fractional-odd partitioning and triangle output are supported by the reference path");
if (!((tess.domain == 1 || tess.domain == 2) && (tess.partitioning == 0 || tess.partitioning == 2 || tess.partitioning == 3) && (tess.outputTopology == 2 || tess.outputTopology == 3))) validate(false, tessellationMessage(config, parameters, tess).c_str());
result.minTessellationLevel = readFloat(queue.context, 0x287);
result.maxTessellationLevel = readFloat(queue.context, 0x286);
Require(result.minTessellationLevel >= 0.0f && result.minTessellationLevel <= 63.0f && result.minTessellationLevel <= result.maxTessellationLevel && result.maxTessellationLevel >= 1.0f, "tessellation level clamps need 0 <= VGT_HOS_MIN_TESS_LEVEL <= 63, VGT_HOS_MAX_TESS_LEVEL >= 1 and the minimum at most the maximum");
@@ -995,6 +995,29 @@ void ShaderStageTests() {
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "missing register");
}
void UnsupportedTessellationFieldTests() {
auto queue = makeState();
queue.context[0x2d5] = 0x0200210d;
queue.userConfig[0x242] = 9;
queue.userConfig[0x25b] = 0x1fe55;
queue.context[0x286] = 0x42800000;
queue.context[0x287] = 0x3f800000;
for (const auto [config, parameters, fields] : {
std::tuple{0xc355u, 0x40045u, "VGT_TF_PARAM=0x40045 (TYPE=1 TESS_TRIANGLE, PARTITIONING=1 PART_POW2, TOPOLOGY=2 OUTPUT_TRIANGLE_CW), VGT_LS_HS_CONFIG=0xc355 (NUM_PATCHES=85, HS_NUM_INPUT_CP=3, HS_NUM_OUTPUT_CP=3)"},
std::tuple{0x1033fu, 0x40066u, "VGT_TF_PARAM=0x40066 (TYPE=2 TESS_QUAD, PARTITIONING=1 PART_POW2, TOPOLOGY=3 OUTPUT_TRIANGLE_CCW), VGT_LS_HS_CONFIG=0x1033f (NUM_PATCHES=63, HS_NUM_INPUT_CP=3, HS_NUM_OUTPUT_CP=4)"},
std::tuple{0xc355u, 0x40048u, "VGT_TF_PARAM=0x40048 (TYPE=0 TESS_ISOLINE, PARTITIONING=2 PART_FRAC_ODD, TOPOLOGY=2 OUTPUT_TRIANGLE_CW)"},
std::tuple{0xc355u, 0x40029u, "(TYPE=1 TESS_TRIANGLE, PARTITIONING=2 PART_FRAC_ODD, TOPOLOGY=1 OUTPUT_LINE)"},
std::tuple{0xc355u, 0x4000du, "(TYPE=1 TESS_TRIANGLE, PARTITIONING=3 PART_FRAC_EVEN, TOPOLOGY=0 OUTPUT_POINT)"},
std::tuple{0xc355u, 0x4004bu, "(TYPE=3, PARTITIONING=2 PART_FRAC_ODD, TOPOLOGY=2 OUTPUT_TRIANGLE_CW)"},
std::tuple{0xc355u, 0x400f1u, "(TYPE=1 TESS_TRIANGLE, PARTITIONING=4, TOPOLOGY=7)"}}) {
queue.context[0x2d6] = config;
queue.context[0x2db] = parameters;
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "VGT_SHADER_STAGES_EN=0x200210d: VGT_TF_PARAM=0x");
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, fields);
expectFailure([&] { AgcDriver::Graphics::DecodeState(queue); }, "): only triangle and quad domains with integer, fractional-even or fractional-odd partitioning and triangle output are supported by the reference path");
}
}
void NggVertexWorkgroupTests() {
auto queue = makeState();
queue.context[0x2d5] = 0x12010;
@@ -8792,6 +8815,7 @@ int main(int argc, char** argv) {
depthCopyFormatTests();
quadPixelMaskTests();
ShaderStageTests();
UnsupportedTessellationFieldTests();
NggVertexWorkgroupTests();
inactiveVertexPointSizeTests();
PolygonPrimitiveTests();