From cdb64bfd4fb4d5312b2542a41a0070f25663166a Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 5 Oct 2026 23:15:20 +0400 Subject: [PATCH] graphics: share indirect draw handling and honor indexed sources --- src/graphics/guest_gpu/graphicsRun.cpp | 55 ++---------------------- src/libs/agc.cpp | 22 +++++++--- tests/ShaderRecompilerComputeTests.cpp | 58 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 57 deletions(-) diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 411095395..8e8cefb56 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -876,64 +876,15 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou } void CommandProcessor::DrawIndirect(uint32_t data_offset, uint32_t draw_initiator, bool indexed) { - EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u); - EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0); - - const auto* args_addr = - reinterpret_cast(m_draw_indirect_args_base_addr + data_offset); - - if (!indexed) { - DrawIndirectArgs args {}; - std::memcpy(&args, args_addr, sizeof(args)); - m_num_instances = args.instance_count; - DrawIndexAuto({.vertex_count = args.vertex_count_per_instance, - .instance_count = args.instance_count, - .first_vertex = args.start_vertex_location, - .first_instance = args.start_instance_location, - .offset_source = DrawOffsetSource::IndirectArgs}); - return; - } - - DrawIndexedIndirectArgs args {}; - std::memcpy(&args, args_addr, sizeof(args)); - - uint64_t index_size = 0; - switch (m_index_type_and_size) { - case 0: index_size = 2; break; - case 1: index_size = 4; break; - case 2: index_size = 1; break; - default: EXIT("unknown index_type_and_size: %u\n", m_index_type_and_size); - } - - auto* index_addr = reinterpret_cast( - m_index_base_addr + static_cast(args.start_index_location) * index_size); - - const uint32_t index_count = - (m_index_buffer_size != 0 ? std::min(args.index_count_per_instance, m_index_buffer_size) - : args.index_count_per_instance); - if (GraphicsRunDebugDumpEnabled() && index_count != args.index_count_per_instance) { - static std::atomic log_count {0}; - if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) { - LOGF("\t DrawIndexIndirect: clamped index_count from %" PRIu32 " to %" PRIu32 - " using INDEX_BUFFER_SIZE\n", - args.index_count_per_instance, index_count); - } - } - - m_num_instances = args.instance_count; - DrawIndex({.index_count = index_count, - .index_addr = index_addr, - .instance_count = args.instance_count, - .base_vertex = static_cast(args.base_vertex_location), - .first_instance = args.start_instance_location, - .offset_source = DrawOffsetSource::IndirectArgs}); + const auto args_size = indexed ? sizeof(DrawIndexedIndirectArgs) : sizeof(DrawIndirectArgs); + DrawIndirectMulti(data_offset, 1, nullptr, args_size, draw_initiator, indexed); } void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_count_or_count, const volatile uint32_t* count_addr, uint32_t stride_in_bytes, uint32_t draw_initiator, bool indexed) { - EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != 2u); + EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != (indexed ? 0u : 2u)); EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0); uint32_t draw_count = max_count_or_count; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 48a10a589..c4c4a8811 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -2529,7 +2529,6 @@ static uint64_t decode_indirect_modifier_patch_offsets(uint64_t modifier, bool i if (indexed && (low & 0x2u) != 0) { base_vtx_loc |= static_cast(sgpr_base + extract_modifier_bits(low, 14u, 5u)) << 16u; - base_vtx_loc |= 1ull << 59u; } return base_vtx_loc | (start_inst_loc << 32u); @@ -2787,13 +2786,15 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t dat EXIT_NOT_IMPLEMENTED(cmd == nullptr); - const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true); + // Single indexed draws store the index-offset enable in word 3, bit 28. + const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true) | + ((modifier & 0x2ull) << 59u); cmd[0] = KYTY_PM4(5, Pm4::IT_DRAW_INDEX_INDIRECT, 0u); cmd[1] = data_offset_in_bytes; cmd[2] = static_cast(patch_offsets); cmd[3] = static_cast(patch_offsets >> 32u); - cmd[4] = decode_indirect_draw_initiator(modifier); + cmd[4] = decode_draw_index_initiator(modifier); return cmd; } @@ -2914,19 +2915,30 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, EXIT_NOT_IMPLEMENTED(cmd == nullptr); + const auto low = static_cast(modifier); + const auto sgpr_base = indirect_modifier_sgpr_base(low); const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, true); const auto count_vaddr = reinterpret_cast(count_addr); + uint32_t draw_index_location = 0x280u; + if ((low & 0x8u) != 0) { + draw_index_location = sgpr_base + extract_modifier_bits(low, 24u, 5u); + } + // Multi indexed draws store the index-offset enable in word 4, bit 28. + const auto draw_control = draw_index_location | ((low & 0x10u) << 23u) | + ((low & 0x2u) << 27u) | ((count_indirect & 0x1u) << 30u) | + ((low & 0x8u) << 28u); + cmd[0] = KYTY_PM4(10, Pm4::IT_DRAW_INDEX_INDIRECT_MULTI, 0u); cmd[1] = data_offset_in_bytes; cmd[2] = static_cast(patch_offsets); cmd[3] = static_cast(patch_offsets >> 32u); - cmd[4] = (count_indirect & 1u) << 30u; + cmd[4] = draw_control; cmd[5] = max_count_or_count; cmd[6] = static_cast(count_vaddr) & ~0x3u; cmd[7] = static_cast(count_vaddr >> 32u); cmd[8] = stride_in_bytes; - cmd[9] = decode_indirect_draw_initiator(modifier); + cmd[9] = decode_draw_index_initiator(modifier); return cmd; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 7ef06c8d2..5163449be 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -39947,6 +39947,64 @@ void CheckPm4DrawIndirectMultiPacket(RenderContext &renderer) { packet_mismatch && !Gen5::AgcIsInternalDataPacket(0xc0017904u, invalid_payload), "indirect multi-draw packet differs from the expected stream"); + + constexpr uint32_t data_offset = 0x5c08; + constexpr uint32_t stride = 32; + std::array arguments{}; + const std::array first{0, 1, 3, 0xfffffffeu, 9}; + const std::array second{0, 2, 5, 7, 11}; + std::copy(first.begin(), first.end(), arguments.begin() + data_offset / 4); + std::copy(second.begin(), second.end(), arguments.begin() + (data_offset + stride) / 4); + std::array indices{}; + processor.BufferInit(); + processor.SetDrawIndirectArgsBaseAddress(reinterpret_cast(arguments.data())); + processor.SetIndexBaseAddress(reinterpret_cast(indices.data())); + processor.SetIndexType(0); + std::array captured{0xc0032500u, data_offset, 0x94, 0x95, 0}; + Require("Pm4DrawIndirectMulti", "captured indexed single execution", + execute(captured.data(), captured.size()), + "indexed indirect draw rejected native source-select zero"); + // Zero primitive counts still traverse CP argument decoding and renderer entry, + // while avoiding unrelated shader and attachment requirements. + for (const bool indexed : {false, true}) { + for (const bool multi : {false, true}) { + for (const uint64_t draw_modifier : {0ull, 0x100ull, 1ull << 32u, (1ull << 32u) | 0x100ull}) { + packet.fill(0); + dcb.cursor_up = packet.data(); + auto *buffer = reinterpret_cast(&dcb); + alignas(4) uint32_t draw_count = 3; + if (multi) { + emitted = indexed ? Gen5::AgcDcbDrawIndexIndirectMulti(buffer, data_offset, 1, 2, + &draw_count, stride, draw_modifier) + : Gen5::AgcDcbDrawIndirectMulti(buffer, data_offset, 1, 2, + &draw_count, stride, draw_modifier); + } else { + emitted = indexed ? Gen5::AgcDcbDrawIndexIndirect(buffer, data_offset, draw_modifier) + : Gen5::AgcDcbDrawIndirect(buffer, data_offset, draw_modifier); + } + const uint32_t begin = multi && !indexed ? 3 : 0; + const uint32_t words = multi ? (indexed ? 10 : 16) : 5; + const uint32_t opcode = multi ? (indexed ? 0xc0083800u : 0xc0082c00u) + : (indexed ? 0xc0032500u : 0xc0032400u); + const uint32_t initiator = (indexed ? 0 : 2) | + (draw_modifier == 0x100 ? 0x20 : 0); + Require("Pm4DrawIndirectMulti", "indexed/auto initiator emission", + emitted == packet.data() && dcb.cursor_up == packet.data() + words && + packet[begin] == opcode && packet[begin + 1] == data_offset && + packet[begin + (multi ? 9 : 4)] == initiator, + "indirect opcode, source select, or modifier bits differ from the native packet"); + Require("Pm4DrawIndirectMulti", "single/multi argument execution", + execute(packet.data(), words), + "indirect draw failed shared argument decoding or padded/count-limited traversal"); + if (multi) { + draw_count = 0; + Require("Pm4DrawIndirectMulti", "zero indirect count execution", + execute(packet.data(), words), "zero indirect draw count did not complete"); + } + } + } + } + renderer.GetCommandScheduler().Finish(); std::printf("[host] %-32s ok\n", "Pm4DrawIndirectMulti"); }