From ec11f31aa6ece87cf45c8bf185defb078c568472 Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 28 Jul 2026 02:32:10 +0200 Subject: [PATCH] fix graphical bug (PPSA17221) --- .../command_processor/pm4Handlers.cpp | 27 +++++++++++++++---- src/graphics/guest_gpu/pm4.h | 3 +++ src/graphics/shader/shader.cpp | 4 ++- src/libs/agc.cpp | 6 ++--- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 89381e20a..5e4beadf7 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -65,10 +65,23 @@ constexpr uint32_t GcrKnownMask = GcrGl2MetadataInvalidate | GcrGl0V GcrGl2Writeback | GcrOrder012 | GcrOrder210; constexpr uint32_t RegisterSelectorMask = 0x70000000u; -uint32_t NormalizeRegisterOffset(uint32_t raw_offset) { - return (raw_offset & ~RegisterSelectorMask); +constexpr uint32_t NormalizeRegisterOffset(uint32_t raw_offset) { + return raw_offset & ~RegisterSelectorMask; } +// Indirect Cx descriptors retain their selector. Selector 1 offsets 0..31 address the +// SPI_PS_INPUT_CNTL register bank; ordinary context-register offsets remain unchanged. +constexpr uint32_t DecodeIndirectCxRegisterOffset(uint32_t raw_offset) { + const auto offset = NormalizeRegisterOffset(raw_offset); + return (raw_offset & RegisterSelectorMask) == Pm4::CX_PS_SHADER_USAGE_BASE && offset < 32u + ? Pm4::SPI_PS_INPUT_CNTL_0 + offset + : offset; +} + +static_assert(DecodeIndirectCxRegisterOffset(Pm4::CX_PS_SHADER_USAGE_BASE + 2u) == + Pm4::SPI_PS_INPUT_CNTL_0 + 2u); +static_assert(DecodeIndirectCxRegisterOffset(Pm4::DB_Z_INFO) == Pm4::DB_Z_INFO); + bool ReleaseMemGcrNeedsBarrier(uint32_t eop_event_type, uint32_t gcr_cntl) { return eop_event_type != 0x28u || (gcr_cntl & (GcrGl2MetadataInvalidate | GcrGl0VectorInvalidate | GcrGl1Invalidate | @@ -2321,14 +2334,18 @@ KYTY_CP_OP_PARSER(CpOpIndirectCxRegs) { EXIT("indirect CX registers have null address, num_regs = %" PRIu32 "\n", indirect_num_dw); } for (uint32_t i = 0; i < indirect_num_dw; i++, indirect_buffer += 2) { - auto cmd_offset = indirect_buffer[0]; - auto value = indirect_buffer[1]; + // Keep the encoded offset for packet control values, and use the decoded offset only + // for register dispatch. + auto raw_cmd_offset = indirect_buffer[0]; + auto cmd_offset = DecodeIndirectCxRegisterOffset(raw_cmd_offset); + auto value = indirect_buffer[1]; if (HwCtxTrySetFakeRegister(cmd_offset, value)) { continue; } - if (cmd_offset == 0xffffffffu) { + // The sentinel is an encoded descriptor value and must be checked before normalization. + if (raw_cmd_offset == 0xffffffffu) { static bool logged = false; if (!logged) { LOGF("\t temporary: skipping indirect CX sentinel pair offset = 0xffffffff, value " diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index d21636199..2d1fd5b09 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -385,6 +385,9 @@ constexpr uint32_t SPI_SHADER_POS_FORMAT = 0x1C3; constexpr uint32_t SPI_SHADER_Z_FORMAT = 0x1C4; constexpr uint32_t SPI_SHADER_COL_FORMAT = 0x1C5; +// Indirect Cx descriptor selector for the 32-entry PS input-control register bank. +constexpr uint32_t CX_PS_SHADER_USAGE_BASE = 0x10000000u; + constexpr uint32_t CB_BLEND0_CONTROL = 0x1E0; constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_SHIFT = 0; constexpr uint32_t CB_BLEND0_CONTROL_COLOR_SRCBLEND_MASK = 0x1F; diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 649714699..63dbf36b9 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -798,7 +798,9 @@ static void ShaderGetStaticInputInfoPS( ps_info = {}; - ps_info.input_num = sh.ps_in_control; + // SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control + // flags in the hardware state and extract only the input count here. + ps_info.input_num = sh.ps_in_control & 0x3fu; ps_info.ps_system_input_base = ShaderCalcPsSystemInputBase(sh); const uint32_t active_inputs = sh.ps_input_ena & sh.ps_input_addr; ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 2e3c04762..9ba7beb9d 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -1217,9 +1217,9 @@ static const ShaderSemantic* find_interpolant_output_semantic(const Shader* gs, } static void set_interpolant_register(ShaderRegister* regs, uint32_t index, uint32_t value) { - // The native implementation writes a descriptor offset (0x10000000 + index). Kyty stores - // decoded PM4 register offsets here because direct/custom indirect paths consume them. - regs[index].offset = Pm4::SPI_PS_INPUT_CNTL_0 + index; + // Interpolant mappings contain encoded Cx descriptors. Indirect PM4 ingestion resolves + // this selector and slot to the physical SPI_PS_INPUT_CNTL register. + regs[index].offset = Pm4::CX_PS_SHADER_USAGE_BASE + index; regs[index].value = value; }