diff --git a/src/graphics/guest_gpu/command_processor/commandProcessor.h b/src/graphics/guest_gpu/command_processor/commandProcessor.h index 1d64f45b5..382608bf2 100644 --- a/src/graphics/guest_gpu/command_processor/commandProcessor.h +++ b/src/graphics/guest_gpu/command_processor/commandProcessor.h @@ -27,6 +27,7 @@ struct IndirectDrawRegisters { uint32_t vertex_offset; uint32_t instance_offset; uint32_t index_offset; + uint32_t draw_index; }; class Pm4Execution { diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 532df5996..06a408e0e 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -1582,7 +1582,7 @@ KYTY_CP_OP_PARSER(CpOpDrawIndirect) { // The native indexed packet enables the high-half first-index destination in word 3. const IndirectDrawRegisters registers { buffer[1] & 0xffffu, buffer[2] & 0xffffu, - indexed && (buffer[2] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP}; + indexed && (buffer[2] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP, Pm4::SH_NOP}; cp.DrawIndirect(data_offset, registers, draw_initiator, indexed); @@ -1602,10 +1602,12 @@ KYTY_CP_OP_PARSER(CpOpDrawIndirectMulti) { const auto stride_in_bytes = buffer[7]; const auto draw_initiator = buffer[8]; const bool indexed = (cmd_id == 0xc0083800); - // Multi-draw moves the first-index enable to word 4; the destinations stay in 2/3. + // Multi-draw moves the first-index enable to word 4; the destinations stay in 2/3. Word 4 + // bit 31 enables the subdraw index, written to the register in its low half. const IndirectDrawRegisters registers { buffer[1] & 0xffffu, buffer[2] & 0xffffu, - indexed && (buffer[3] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP}; + indexed && (buffer[3] & (1u << 28u)) != 0 ? buffer[1] >> 16u : Pm4::SH_NOP, + (buffer[3] >> 31u) != 0 ? buffer[3] & 0xffffu : Pm4::SH_NOP}; if (count_indirect == 0) { count_addr = nullptr; diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 3837d504b..f52d24af4 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -789,7 +789,7 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters registers, uint32_t vertex_offset, uint32_t instance_offset, - uint32_t first_index) { + uint32_t first_index, uint32_t draw_index) { const auto write = [&](uint32_t location, uint32_t value) { if (location == Pm4::SH_NOP) { return; @@ -801,6 +801,7 @@ static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters write(registers.vertex_offset, vertex_offset); write(registers.instance_offset, instance_offset); write(registers.index_offset, first_index); + write(registers.draw_index, draw_index); } void CommandProcessor::DrawIndirect(uint32_t data_offset, IndirectDrawRegisters registers, @@ -839,7 +840,8 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun // while they are zero. const DrawIndirectArgs records {args_addr, draw_count, stride_in_bytes}; if (registers.vertex_offset == Pm4::SH_NOP && registers.instance_offset == Pm4::SH_NOP && - registers.index_offset == Pm4::SH_NOP && (!indexed || m_index_buffer_size != 0) && + registers.index_offset == Pm4::SH_NOP && registers.draw_index == Pm4::SH_NOP && + (!indexed || m_index_buffer_size != 0) && (indexed ? executor.DrawIndex(m_submit_id, CurrentBuffer(), {.index_count = m_index_buffer_size, .index_addr = IndexAddress(0), @@ -854,7 +856,7 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun const auto addr = args_addr + static_cast(i) * stride_in_bytes; if (!indexed) { const auto& args = *reinterpret_cast(addr); - PatchIndirectDrawOffsets(*this, registers, args.firstVertex, args.firstInstance, 0); + PatchIndirectDrawOffsets(*this, registers, args.firstVertex, args.firstInstance, 0, i); executor.DrawAuto(m_submit_id, CurrentBuffer(), {.vertex_count = args.vertexCount, .instance_count = args.instanceCount, @@ -866,7 +868,7 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun const auto& args = *reinterpret_cast(addr); PatchIndirectDrawOffsets(*this, registers, static_cast(args.vertexOffset), - args.firstInstance, args.firstIndex); + args.firstInstance, args.firstIndex, i); executor.DrawIndex(m_submit_id, CurrentBuffer(), {.index_count = std::min(args.indexCount, max_indices), .index_addr = IndexAddress(args.firstIndex), diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index aff171caf..c30356c3b 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -49964,6 +49964,13 @@ void CheckPm4DrawIndirectMultiPacket(RenderContext &renderer) { KYTY_PM4(10, Pm4::IT_DRAW_INDIRECT_MULTI, 0), 0, auto_packet[2], auto_packet[3], Pm4::SH_NOP, 2, 0, 0, 16, 2}; check_offsets(auto_multi_packet, hs, {53, 15}, "non-indexed multi-draw offsets"); + // Word 4 bit 31 has the CP write each subdraw's index, even with no offsets to patch. + shaders.SetHsUserSgpr(11, 77); + auto_multi_packet[2] = auto_multi_packet[3] = Pm4::SH_NOP; + auto_multi_packet[4] = (1u << 31u) | (Pm4::SPI_SHADER_USER_DATA_HS_0 + 11); + Require("Pm4DrawIndirectMulti", "multi-draw index", + execute(auto_multi_packet.data(), auto_multi_packet.size()) && hs[11] == 1, + "multi-draw did not write the last subdraw index"); arguments = {0, 1, 99, 101}; auto_packet[2] = Pm4::SH_NOP; check_offsets(auto_packet, hs, {53, 101}, "independent destinations");