graphics: fix vertex and instance IDs for unpatched indirect draws

This commit is contained in:
nmzik committed 2026-10-28 04:33:03 +01:00
1 parent 1be1c6c4f9
commit a833e1b735
10 files changed
+72 -46

No files matched your search

+31 -18
View File
@@ -19739,9 +19739,9 @@ public:
"the draw's cull mode or front face did not reach the host GPU");
registers.SetModeControl({});
// Indirect records come from the GPU copy; the CPU backing holds zeros. In each pair the
// first record draws nothing and the second covers the target, through vertex offset 7
// for embedded fetches.
// Indirect records come from the GPU copy; the CPU backing holds zeros. Embedded
// fetches use vertex offset 7. Native shaders receive raw vertex/instance IDs when
// the packet does not patch either offset SGPR, even with nonzero record offsets.
{
constexpr uint64_t vertices_address = depth_address + 0x38000;
constexpr uint64_t args_address = vertices_address + 0x100;
@@ -19752,19 +19752,23 @@ public:
sizeof(positions));
constexpr uint32_t stride = 32;
const vk::DrawIndexedIndirectCommand indexed_records[]{
{3, 1, 0, 0, 5}, {3, 1, 0, 7, 5}, {0, 1, 0, 0, 0}, {3, 1, 0, 0, 0}};
{3, 1, 0, 0, 5}, {3, 1, 0, 7, 5}, {0, 1, 0, 0, 0}, {3, 1, 0, 0, 0},
{0, 1, 0, 0, 0}, {3, 1, 0, -7, 0}, {0, 1, 0, 0, 0}, {3, 1, 0, 0, 11}};
const vk::DrawIndirectCommand auto_records[]{{3, 1, 0, 5}, {3, 1, 7, 5}, {0, 1, 0, 0},
{3, 1, 0, 0}};
for (u32 i = 0; i < 4; i++) {
{3, 1, 0, 0}, {0, 1, 0, 0}, {3, 1, 13, 0},
{0, 1, 0, 0}, {3, 1, 0, 11}};
constexpr u32 records_per_kind = std::size(indexed_records);
static_assert(records_per_kind == std::size(auto_records));
for (u32 i = 0; i < records_per_kind; i++) {
std::memcpy(reinterpret_cast<void *>(args_address + i * stride), &indexed_records[i],
sizeof(indexed_records[i]));
std::memcpy(reinterpret_cast<void *>(args_address + (4 + i) * stride), &auto_records[i],
sizeof(auto_records[i]));
std::memcpy(reinterpret_cast<void *>(args_address + (records_per_kind + i) * stride),
&auto_records[i], sizeof(auto_records[i]));
}
// Larger than a caching page, so the arguments are cached instead of streamed.
(void)context.GetBufferCache().ObtainBuffer(
vertices_address, BufferCache::CACHING_PAGESIZE * 2, false);
const std::array<u32, 8 * stride / 4> zeros{};
const std::array<u32, 2 * records_per_kind * stride / 4> zeros{};
Require("GpuIndirectDraw", "CPU backing",
LibKernel::Memory::TryWriteBacking(args_address, zeros.data(), sizeof(zeros)),
"the CPU copy of the arguments could not be cleared");
@@ -19825,26 +19829,35 @@ public:
}
};
check("indexed embedded fetch", fetch_vs, true, 0);
check("auto embedded fetch", fetch_vs, false, 4);
check("auto embedded fetch", fetch_vs, false, records_per_kind);
check("indexed native fetch", vertex_address, true, 2);
check("auto native fetch", vertex_address, false, 6);
check("auto native fetch", vertex_address, false, records_per_kind + 2);
// Pipelines created from only their key and stage modules draw the same.
scheduler.Finish();
Require("PipelineFromKey", "vertex input pipelines",
PipelineCacheTestAccess::RecreateGraphicsPipelines(context.GetPipelineCache()) > 0,
"no cached graphics pipeline named only compiled modules");
check("recreated indexed embedded fetch", fetch_vs, true, 0);
check("recreated auto native fetch", vertex_address, false, 6);
check("recreated auto native fetch", vertex_address, false, records_per_kind + 2);
// Patched user SGPRs 0/1 read the records' base vertex 7 and first instance 5, not the
// table pointers they hold, and the index VGPRs exclude both bases.
check("indexed patched offsets", indirect_vs, true, 0, {0, 1});
check("auto patched offsets", indirect_vs, false, 4, {0, 1});
check("auto patched offsets", indirect_vs, false, records_per_kind, {0, 1});
// Slots 4/5 lie past USER_SGPR, so the tables survive; rewritten fetches keep Vulkan's
// vertex offset.
check("indexed patched embedded fetch", fetch_vs, true, 0, {4, 5});
// Patching one offset still leaves both bases out of the index VGPRs.
check("auto instance-only patch", vertex_address, false, 5,
check("auto instance-only patch", vertex_address, false, records_per_kind + 1,
{ShaderIndirectOffsets::None, 1});
// SH_NOP leaves the existing SGPR values intact; the native prolog adds these
// values once while the record's unrelated offsets are ignored. Check each base
// independently so a correct vertex offset cannot hide a broken instance offset.
shaders.SetGsUserSgpr(0, 7);
shaders.SetGsUserSgpr(1, 5);
check("indexed unpatched vertex offset", indirect_vs, true, 4);
check("auto unpatched vertex offset", indirect_vs, false, records_per_kind + 4);
check("indexed unpatched instance offset", indirect_vs, true, 6);
check("auto unpatched instance offset", indirect_vs, false, records_per_kind + 6);
}
// A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner.
@@ -47399,7 +47412,7 @@ void CheckEmbeddedFetchVertexOffset() {
vertex.tess = {.input_control_points = 3, .output_control_points = 3,
.ls_stride = 124, .hs_stride = 128, .domain = 1,
.partitioning = 2, .output_topology = 2};
vertex.fetch_embedded = true;
vertex.has_dynamic_vertex_fetch = true;
vertex.fetch_buffer_reg = 0;
vertex.fetch_attrib_reg = 2;
vertex.resources_num = 1;
@@ -47439,7 +47452,7 @@ void CheckEmbeddedFetchVertexOffset() {
const auto Resolve = [](const CompiledShader &result,
u32 index_offset) {
ShaderVertexInputInfo vertex;
vertex.fetch_embedded = true;
vertex.has_dynamic_vertex_fetch = true;
ShaderModule module;
module.info.user_data_base = result.program.user_data_base;
module.info.info.vertex_offset_sgpr = result.program.info.vertex_offset_sgpr;
@@ -47450,7 +47463,7 @@ void CheckEmbeddedFetchVertexOffset() {
const auto ResolveInstance = [](const CompiledShader &result) {
ShaderVertexInputInfo vertex;
vertex.fetch_embedded = true;
vertex.has_dynamic_vertex_fetch = true;
ShaderModule module;
module.info.user_data_base = result.program.user_data_base;
module.info.info.instance_offset_sgpr =
@@ -50344,7 +50357,7 @@ void CheckEmbeddedFetchLaneSpill() {
std::array<u32, 11> user_data{};
ShaderVertexInputInfo vertex;
vertex.fetch_embedded = true;
vertex.has_dynamic_vertex_fetch = true;
vertex.fetch_buffer_reg = 0;
vertex.resources_num = 1;
vertex.resources[0].fields[3] =
+2 -2
View File
@@ -1938,7 +1938,7 @@ void TestVertexBufferGrouping() {
Check(input.fetch_attrib_reg == 2, "a draw reread the vertex metadata of a mapped shader");
ShaderMapUserData(regs.es_regs.data_addr, mapped);
(void)PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input);
Check(!input.fetch_embedded, "remapping a shader kept its previous vertex metadata");
Check(!input.has_dynamic_vertex_fetch, "remapping a shader kept its previous vertex metadata");
}
void TestNggVertexEntryState() {
@@ -11575,7 +11575,7 @@ void TestEmbeddedFetchPreservesSharedScalarLoad() {
reinterpret_cast<uint64_t>(attributes.data())};
std::memcpy(user_data.data() + 12, tables, sizeof(tables));
ShaderVertexInputInfo input{};
input.fetch_embedded = true;
input.has_dynamic_vertex_fetch = true;
input.fetch_buffer_reg = 12;
input.fetch_attrib_reg = 14;
input.resources_num = 1;