From b43bf9f7fb93ac3c49fa908432fdcbf08e048747 Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 29 Sep 2026 03:25:22 +0400 Subject: [PATCH] graphics: store vertex attribute bindings without duplicated arrays --- .../renderer/pipeline/pipelineCache.cpp | 22 +++---- src/graphics/host_gpu/renderer/renderDraw.cpp | 47 ++++++++------- src/graphics/shader/shader.cpp | 47 +++++---------- src/graphics/shader/shaderBindings.h | 14 ++--- tests/ShaderRecompilerComputeTests.cpp | 4 +- tests/shaderCfgTests.cpp | 60 ++++++++++++++++++- 6 files changed, 115 insertions(+), 79 deletions(-) diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 6d4179b97..9bb210fc8 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -761,24 +761,20 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( vs_input_info.resources_num > ShaderVertexInputInfo::RES_MAX); key.vertex_input.binding_count = static_cast(vs_input_info.buffers_num); key.vertex_input.attribute_count = static_cast(vs_input_info.resources_num); - uint32_t attributes_num = 0; for (int binding = 0; binding < vs_input_info.buffers_num; binding++) { const auto& buffer = vs_input_info.buffers[binding]; - EXIT_IF(buffer.attr_num < 0 || buffer.attr_num > ShaderVertexInputBuffer::ATTR_MAX); - attributes_num += static_cast(buffer.attr_num); - EXIT_IF(attributes_num > static_cast(vs_input_info.resources_num)); key.vertex_input.bindings[binding] = {.stride = buffer.stride, .instance = buffer.fetch_index != 0}; - for (int attribute = 0; attribute < buffer.attr_num; attribute++) { - const auto index = buffer.attr_indices[attribute]; - EXIT_IF(index < 0 || index >= vs_input_info.resources_num); - key.vertex_input.attributes[index] = { - .offset = buffer.attr_offsets[attribute], - .binding = static_cast(binding), - }; - } } - EXIT_IF(attributes_num != static_cast(vs_input_info.resources_num)); + for (int attribute = 0; attribute < vs_input_info.resources_num; attribute++) { + const auto binding = vs_input_info.resources_dst[attribute].buffer_index; + EXIT_IF(binding < 0 || binding >= vs_input_info.buffers_num); + key.vertex_input.attributes[attribute] = { + .offset = static_cast(vs_input_info.resources[attribute].Base48() - + vs_input_info.buffers[binding].addr), + .binding = static_cast(binding), + }; + } } if (auto iter = m_graphics_pipelines.find(key); iter != m_graphics_pipelines.end()) { diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 5a226bdf0..873595104 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -249,8 +249,8 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo for (int bi = 0; bi < vs_input_info.buffers_num; bi++) { const auto& b = vs_input_info.buffers[bi]; LOGF("DrawInputState[%u]: vb[%d] addr=0x%010" PRIx64 - " stride=%u records=%u fetch_index=%u attr_num=%d\n", - log_id, bi, b.addr, b.stride, b.num_records, b.fetch_index, b.attr_num); + " stride=%u records=%u fetch_index=%u\n", + log_id, bi, b.addr, b.stride, b.num_records, b.fetch_index); const auto* bytes = reinterpret_cast(b.addr); if (bytes != nullptr && b.stride != 0) { @@ -272,11 +272,13 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo raw[5], raw[6], raw[7], raw[8], flt[0], flt[1], flt[2], flt[3], flt[4], flt[5], flt[6], flt[7], flt[8]); - for (int ai = 0; ai < b.attr_num; ai++) { - const auto res_index = b.attr_indices[ai]; - const auto& r = vs_input_info.resources[res_index]; - const auto& rd = vs_input_info.resources_dst[res_index]; - const auto offset = b.attr_offsets[ai]; + for (int ai = 0; ai < vs_input_info.resources_num; ai++) { + const auto& r = vs_input_info.resources[ai]; + const auto& rd = vs_input_info.resources_dst[ai]; + if (rd.buffer_index != bi) { + continue; + } + const auto offset = static_cast(r.Base48() - b.addr); if (offset + 4u <= b.stride && r.Format() == Prospero::BufferFormat::k8_8_8_8UNorm) { uint32_t packed = 0; @@ -296,14 +298,16 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo } } - for (int ai = 0; ai < b.attr_num; ai++) { - const auto res_index = b.attr_indices[ai]; - const auto& r = vs_input_info.resources[res_index]; - const auto& rd = vs_input_info.resources_dst[res_index]; - LOGF("DrawInputState[%u]: attr[%d] res=%d offset=%u dst=v%d regs=%d fetch_index=%u " + for (int ai = 0; ai < vs_input_info.resources_num; ai++) { + const auto& r = vs_input_info.resources[ai]; + const auto& rd = vs_input_info.resources_dst[ai]; + if (rd.buffer_index != bi) { + continue; + } + LOGF("DrawInputState[%u]: attr[%d] offset=%u dst=v%d regs=%d fetch_index=%u " "sharp=%08" PRIx32 " %08" PRIx32 " %08" PRIx32 " %08" PRIx32 "\n", - log_id, ai, res_index, b.attr_offsets[ai], rd.register_start, rd.registers_num, - rd.fetch_index, r.fields[0], r.fields[1], r.fields[2], r.fields[3]); + log_id, ai, static_cast(r.Base48() - b.addr), rd.register_start, + rd.registers_num, rd.fetch_index, r.fields[0], r.fields[1], r.fields[2], r.fields[3]); } } } @@ -630,19 +634,22 @@ struct PreparedIndexBuffer { vk::IndexType type = vk::IndexType::eUint16; }; -static uint64_t VertexBufferDescriptorSize(const ShaderVertexInputBuffer& buffer, - const ShaderVertexInputInfo& info) { +static uint64_t VertexBufferDescriptorSize(int binding, const ShaderVertexInputInfo& info) { + const auto& buffer = info.buffers[binding]; if (buffer.stride != 0 || buffer.num_records == 0) { return static_cast(buffer.stride) * buffer.num_records; } uint64_t size = 0; - for (int i = 0; i < buffer.attr_num; i++) { - const auto& resource = info.resources[buffer.attr_indices[i]]; + for (int i = 0; i < info.resources_num; i++) { + if (info.resources_dst[i].buffer_index != binding) { + continue; + } + const auto& resource = info.resources[i]; // RDNA2 OOB_SELECT=2 only checks NumRecords != 0. A constant attribute still // fetches its entire format; NumRecords is not a byte count in this mode. const uint64_t extent = resource.OutOfBounds() == 2 - ? static_cast(buffer.attr_offsets[i]) + + ? resource.Base48() - buffer.addr + ShaderRecompiler::Format::GetFormatInfo(resource.Format()).byte_size : buffer.num_records; size = std::max(size, extent); @@ -679,7 +686,7 @@ static PreparedVertexBuffers AcquireVertexBuffers(CommandBuffer& b uint32_t range_count = 0; for (int i = 0; i < vs_input_info.buffers_num; i++) { const auto& vertex = vs_input_info.buffers[i]; - const auto size = VertexBufferDescriptorSize(vertex, vs_input_info); + const auto size = VertexBufferDescriptorSize(i, vs_input_info); sizes[i] = size; if (size == 0) { continue; diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 7bde696dd..e443d35f8 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -336,14 +336,15 @@ static void ShaderDetectBuffers(ShaderVertexInputInfo& info) { for (int ri = 0; ri < info.resources_num; ri++) { const auto& r = info.resources[ri]; - bool merged = false; - for (int bi = 0; bi < info.buffers_num; bi++) { + auto& destination = info.resources_dst[ri]; + int bi = 0; + for (; bi < info.buffers_num; bi++) { auto& b = info.buffers[bi]; uint64_t stride = b.stride; if (stride == r.Stride() && - b.fetch_index == static_cast(info.resources_dst[ri].fetch_index)) { + b.fetch_index == destination.fetch_index) { uint64_t rbase = r.Base48(); uint64_t base = std::min(rbase, b.addr); uint64_t offset1 = rbase - base; @@ -352,31 +353,20 @@ static void ShaderDetectBuffers(ShaderVertexInputInfo& info) { if (offset1 < stride && offset2 < stride) { EXIT_NOT_IMPLEMENTED(b.num_records != r.NumRecords()); b.addr = base; - EXIT_NOT_IMPLEMENTED(b.attr_num >= ShaderVertexInputBuffer::ATTR_MAX); - b.attr_indices[b.attr_num++] = ri; - merged = true; break; } } } - if (!merged) { + if (bi == info.buffers_num) { EXIT_NOT_IMPLEMENTED(info.buffers_num >= ShaderVertexInputInfo::RES_MAX); - int bi = info.buffers_num++; - info.buffers[bi].addr = r.Base48(); - info.buffers[bi].stride = r.Stride(); - info.buffers[bi].num_records = r.NumRecords(); - info.buffers[bi].fetch_index = info.resources_dst[ri].fetch_index; - info.buffers[bi].attr_num = 1; - info.buffers[bi].attr_indices[0] = ri; - } - } - - for (int bi = 0; bi < info.buffers_num; bi++) { - auto& b = info.buffers[bi]; - for (int ri = 0; ri < b.attr_num; ri++) { - b.attr_offsets[ri] = info.resources[b.attr_indices[ri]].Base48() - b.addr; + info.buffers[bi] = {.addr = r.Base48(), + .stride = r.Stride(), + .num_records = r.NumRecords(), + .fetch_index = destination.fetch_index}; + info.buffers_num++; } + destination.buffer_index = bi; } } @@ -907,8 +897,9 @@ void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info) { LOGF("\t\t register_start = %d\n" "\t\t registers_num = %d\n" - "\t\t fetch_index = %" PRIu32 "\n", - rd.register_start, rd.registers_num, rd.fetch_index); + "\t\t fetch_index = %" PRIu32 "\n" + "\t\t buffer_index = %d\n", + rd.register_start, rd.registers_num, rd.fetch_index, rd.buffer_index); LOGF("\t\t fields = %08" PRIx32 "%08" PRIx32 "%08" PRIx32 "%08" PRIx32 "\n", r.fields[3], r.fields[2], r.fields[1], r.fields[0]); LOGF("\t\t Base() = %" PRIx64 "\n" @@ -934,14 +925,8 @@ void ShaderDbgDumpInputInfo(const ShaderVertexInputInfo& info) { LOGF("\t\t addr = %" PRIx64 "\n" "\t\t stride = %" PRIu32 "\n" "\t\t num_records = %" PRIu32 "\n" - "\t\t fetch_index = %" PRIu32 "\n" - "\t\t attr_num = %" PRId32 "\n", - r.addr, r.stride, r.num_records, r.fetch_index, r.attr_num); - for (int j = 0; j < r.attr_num; j++) { - LOGF("\t\t attr_indices[%d] = %d\n" - "\t\t attr_offsets[%d] = %u\n", - j, r.attr_indices[j], j, r.attr_offsets[j]); - } + "\t\t fetch_index = %" PRIu32 "\n", + r.addr, r.stride, r.num_records, r.fetch_index); } } diff --git a/src/graphics/shader/shaderBindings.h b/src/graphics/shader/shaderBindings.h index a8d4d73a3..0543c2e67 100644 --- a/src/graphics/shader/shaderBindings.h +++ b/src/graphics/shader/shaderBindings.h @@ -174,15 +174,10 @@ struct ShaderSamplerResource { }; struct ShaderVertexInputBuffer { - static constexpr int ATTR_MAX = 32; - - uint64_t addr = 0; - uint32_t stride = 0; - uint32_t num_records = 0; - uint32_t fetch_index = 0; - int attr_num = 0; - int attr_indices[ATTR_MAX] = {0}; - uint32_t attr_offsets[ATTR_MAX] = {0}; + uint64_t addr = 0; + uint32_t stride = 0; + uint32_t num_records = 0; + uint32_t fetch_index = 0; }; struct ShaderVertexDestination { @@ -190,6 +185,7 @@ struct ShaderVertexDestination { int registers_num = 0; int attr_id = -1; uint32_t fetch_index = 0; + int buffer_index = 0; }; enum class ShaderStorageUsage { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index cf6c445ba..26ae5abcc 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -14062,9 +14062,7 @@ public: vertex.resources_num = 2; vertex.buffers_num = 1; vertex.buffers[0].stride = 6 * sizeof(float); - vertex.buffers[0].attr_num = 2; - vertex.buffers[0].attr_indices[1] = 1; - vertex.buffers[0].attr_offsets[1] = 2 * sizeof(float); + vertex.resources[1].UpdateAddress48(2 * sizeof(float)); for (uint32_t i = 0; i < 2; i++) { const auto format = i == 0 ? Prospero::BufferFormat::k32_32Float : packed_vertex_color ? Prospero::BufferFormat::k11_11_10Float diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 258c7cd8d..57bc08409 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1702,6 +1702,61 @@ void TestShaderStageBarriers() { "independent vertex invocations retained a workgroup barrier"); } +void TestVertexBufferGrouping() { + const uint32_t shader[] = {EncodeSopp(0x01)}; + std::array(AgcDirectResourceType::Last) + 1> offsets; + offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET); + offsets[static_cast(AgcDirectResourceType::PtrVertexBufferTable)] = 0; + offsets[static_cast(AgcDirectResourceType::PtrVertexAttribDescTable)] = 2; + ShaderUserData user_data{}; + user_data.direct_resource_count = offsets.size(); + user_data.direct_resource_offset = offsets.data(); + std::array semantics{}; + for (uint32_t i = 0; i < semantics.size(); i++) { + semantics[i].semantic = i; + semantics[i].hardware_mapping = i; + semantics[i].size_in_elements = 1; + } + ShaderMappedData mapped{}; + mapped.user_data = &user_data; + mapped.code_size_bytes = sizeof(shader); + mapped.input_semantics = semantics.data(); + mapped.num_input_semantics = semantics.size(); + HW::VertexShaderInfo regs{}; + regs.es_regs.data_addr = reinterpret_cast(shader); + regs.gs_regs.rsrc2.user_sgpr = 4; + ShaderMapUserData(regs.es_regs.data_addr, mapped); + std::array descriptors{}; + const std::array attributes{0, 1u | (1u << 26u), 2, 3}; + const std::array pointers{ + reinterpret_cast(descriptors.data()), + reinterpret_cast(attributes.data())}; + std::memcpy(regs.gs_user_sgpr.value, pointers.data(), sizeof(pointers)); + ShaderVertexInputInfo input{}; + for (const uint64_t base : {0x1000u, 0x3000u}) { + for (auto &descriptor : descriptors) { + descriptor.fields[1] = 16u << 16u; + descriptor.fields[2] = 8; + descriptor.UpdateAddress48(base); + } + descriptors[0].UpdateAddress48(base + 8); + descriptors[1].UpdateAddress48(base + 4); + descriptors[3].fields[1] = 0; // Constant attributes use their own buffer. + (void)PrepareProgram(regs, HW::Context{}, HW::UserConfig{}, input); + Check(input.resources_num == 4 && input.buffers_num == 3 && + input.resources_dst[0].buffer_index == 0 && + input.resources_dst[1].buffer_index == 1 && + input.resources_dst[2].buffer_index == 0 && + input.resources_dst[3].buffer_index == 2, + "interleaved vertex attributes lost their stride or instance-rate binding"); + Check(input.buffers[0].addr == base && input.buffers[1].addr == base + 4 && + input.buffers[1].fetch_index == 1 && input.buffers[2].stride == 0 && + input.resources[0].Base48() - input.buffers[0].addr == 8 && + input.resources[2].Base48() == input.buffers[0].addr, + "a later lower-address attribute or changed table left stale vertex offsets"); + } +} + void TestNggVertexEntryState() { using namespace ShaderRecompiler; // PPSA03309 shader 5a39eb2021a5d2c1: retain its NGG prologue and replace @@ -10571,13 +10626,11 @@ void TestNewShaderRecompilerClipDisabledPosition() { ShaderVertexInputInfo layout_a{}; layout_a.resources_num = 1; layout_a.buffers_num = 1; - layout_a.buffers[0].attr_num = 1; - layout_a.buffers[0].attr_indices[0] = 0; layout_a.buffers[0].stride = 16; auto layout_b = layout_a; layout_b.buffers[0].stride = 32; layout_b.buffers[0].fetch_index = 1; - layout_b.buffers[0].attr_offsets[0] = 4; + layout_b.resources[0].UpdateAddress48(4); Check(MakeStageStaticKey(layout_a) == MakeStageStaticKey(layout_b), "pipeline-only vertex layout fragmented the shader module cache key"); } @@ -13855,6 +13908,7 @@ int main() { TestDemandDrivenSpirvDeclarations(); TestNewShaderRecompilerSMovB32(); TestShaderStageBarriers(); + TestVertexBufferGrouping(); TestNggVertexEntryState(); TestNewShaderRecompilerClipDisabledPosition(); TestNewShaderRecompilerAuxPositionExports();