diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 58f1a019..e00dd3c4 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -71,19 +71,6 @@ 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 | @@ -2370,10 +2357,10 @@ 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) { - // Keep the encoded offset for packet control values, and use the decoded offset only - // for register dispatch. + // Keep the encoded offset for packet control values, and use the normalized offset + // only for register dispatch. auto raw_cmd_offset = indirect_buffer[0]; - auto cmd_offset = DecodeIndirectCxRegisterOffset(raw_cmd_offset); + auto cmd_offset = NormalizeRegisterOffset(raw_cmd_offset); auto value = indirect_buffer[1]; if (HwCtxTrySetFakeRegister(cmd_offset, value)) { diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index 8a7bc250..7630a618 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -387,9 +387,6 @@ 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/libs/agc.cpp b/src/libs/agc.cpp index 6e0ada7b..fd83f597 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -1353,9 +1353,7 @@ static const ShaderSemantic* find_interpolant_output_semantic(const Shader* gs, } static void set_interpolant_register(ShaderRegister* regs, uint32_t index, uint32_t value) { - // 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].offset = Pm4::SPI_PS_INPUT_CNTL_0 + index; regs[index].value = value; } @@ -1407,6 +1405,96 @@ int KYTY_SYSV_ABI AgcCreateInterpolantMapping(ShaderRegister* regs, const Shader return OK; } +static uint32_t apply_interpolant_two_bit_field(uint32_t value, uint32_t field, + uint32_t shift) { + const auto mask = 0x3u << shift; + return (value & ~mask) | ((field & 0x3u) << shift); +} + +static uint32_t apply_interpolant_final_mask(uint32_t flags, uint32_t source, + uint32_t mask) { + flags = (flags & 0xffffffe0u) | ((mask >> 8u) & 0x1fu); + flags = (flags & 0xfffffbffu) | + ((source & 0x400000u) != 0 ? 0x400u : ((source >> 14u) & 0x400u)); + return flags; +} + +int KYTY_SYSV_ABI AgcCreateInterpolantMapping2(ShaderRegister* regs, const Shader* gs, + const Shader* ps) { + PRINT_NAME(); + + LOGF("\t regs = 0x%016" PRIx64 "\n" + "\t gs = 0x%016" PRIx64 "\n" + "\t ps = 0x%016" PRIx64 "\n", + reinterpret_cast(regs), reinterpret_cast(gs), + reinterpret_cast(ps)); + + EXIT_NOT_IMPLEMENTED(regs == nullptr); + EXIT_NOT_IMPLEMENTED(ps != nullptr && ps->num_input_semantics != 0 && + ps->input_semantics == nullptr); + + if (ps == nullptr || ps->num_input_semantics == 0) { + set_identity_interpolant_registers(regs, 0); + return OK; + } + + EXIT_NOT_IMPLEMENTED(gs == nullptr); + EXIT_NOT_IMPLEMENTED(gs->num_output_semantics != 0 && gs->output_semantics == nullptr); + + for (uint32_t i = 0; i < ps->num_input_semantics; i++) { + const auto source = shader_semantic_word(ps->input_semantics[i]); + auto mask_index = static_cast(gs->num_output_semantics); + + for (uint32_t j = 0; j < gs->num_output_semantics; j++) { + if (gs->output_semantics[j].semantic == static_cast(source)) { + mask_index = j; + break; + } + } + + const bool has_mask = mask_index < gs->num_output_semantics; + const auto mask = has_mask ? shader_semantic_word(gs->output_semantics[mask_index]) : 0u; + const auto mode = (source >> 20u) & 0x3u; + auto flags = 0u; + + if (mode == 0) { + flags = (((source >> 24u) & 0x1u) | (has_mask ? 0u : 1u)) << 5u; + flags = apply_interpolant_two_bit_field(flags, source >> 28u, 8u); + } else { + flags = ((source << 4u) & 0x03000000u) + 0x80000u; + + if (mode == 2) { + flags &= 0xffefffdfu; + flags |= has_mask ? ((~(mask & source) >> 16u) & 0x20u) : 0x20u; + flags = apply_interpolant_two_bit_field(flags, source >> 30u, 8u); + flags = apply_interpolant_two_bit_field(flags, source >> 30u, 21u); + } else { + if (has_mask) { + const auto masked = mask & source; + flags = (flags & 0xffffffdfu) | ((masked >> 15u) & 0x20u); + flags ^= 0x20u; + flags = (flags & 0xffefffffu) | ((~masked >> 1u) & 0x100000u); + flags = apply_interpolant_two_bit_field(flags, source >> 28u, 8u); + } else { + flags |= 0x100020u; + flags = apply_interpolant_two_bit_field(flags, source >> 28u, 8u); + } + flags = apply_interpolant_two_bit_field(flags, source >> 30u, 21u); + } + } + + flags = has_mask ? apply_interpolant_final_mask(flags, source, mask) + : (flags & 0xfffffbe0u); + set_interpolant_register(regs, i, flags); + } + + if (ps->num_input_semantics < 32u) { + set_identity_interpolant_registers(regs, ps->num_input_semantics); + } + + return OK; +} + int KYTY_SYSV_ABI AgcGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type) { PRINT_NAME(); diff --git a/src/libs/agc.h b/src/libs/agc.h index 0aa044ac..cebd4d63 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -66,6 +66,8 @@ int KYTY_SYSV_ABI AgcGetGsOversubscription(ShaderRegister* regs, const Shader* g float factor); int KYTY_SYSV_ABI AgcCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs, const Shader* ps); +int KYTY_SYSV_ABI AgcCreateInterpolantMapping2(ShaderRegister* regs, const Shader* gs, + const Shader* ps); int KYTY_SYSV_ABI AgcGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type); int KYTY_SYSV_ABI AgcGetDataPacketPayloadRange(MemoryRange* range, uint32_t* cmd, int type); int KYTY_SYSV_ABI AgcWriteDataPatchSetAddressOrOffset(uint32_t* cmd, uint64_t address_or_offset); diff --git a/src/libs/libAgcDriver.cpp b/src/libs/libAgcDriver.cpp index 1a09e368..7feb62d9 100644 --- a/src/libs/libAgcDriver.cpp +++ b/src/libs/libAgcDriver.cpp @@ -3,8 +3,6 @@ #include "libs/libs.h" #include "loader/symbolDatabase.h" -#include - namespace Libs { namespace LibGen5 { @@ -13,143 +11,6 @@ LIB_VERSION("Graphics5", 1, "Graphics5", 1, 1); namespace Gen5 = Graphics::Gen5; -template -static T ReadUnaligned(const uint8_t* ptr, uint32_t offset) { - T ret {}; - std::memcpy(&ret, ptr + offset, sizeof(T)); - return ret; -} - -static uint32_t BitExtract(uint32_t value, uint32_t start, uint32_t len) { - return (value >> start) & ((1u << len) - 1u); -} - -static void Graphics5UnknownDbWriteDefault(uint64_t* out, uint32_t first) { - constexpr uint64_t default_entry = 0x10000000ull; - - for (uint32_t i = first; i < 32u; i++) { - const auto low = static_cast(default_entry) + i; - const auto high = static_cast(default_entry >> 37u) * 32u + i; - out[i] = (static_cast(high) << 32u) | low; - } -} - -static uint32_t Graphics5UnknownDbApplyTwoBitField(uint32_t value, uint32_t field, uint32_t shift) { - const auto mask = 0x3u << shift; - - switch (field & 0x3u) { - case 0: return value & ~mask; - case 1: return (value & ~mask) | (0x1u << shift); - case 2: return (value & ~mask) | (0x2u << shift); - default: return value | mask; - } -} - -static uint32_t Graphics5UnknownDbApplyFinalMask(uint32_t flags, uint32_t src, - uint32_t mask_value) { - flags &= 0xffffffe0u; - flags |= BitExtract(mask_value, 8u, 5u); - flags &= 0xfffffbffu; - flags |= ((src & 0x400000u) != 0 ? 0x400u : ((src >> 14u) & 0x400u)); - return flags; -} - -static int KYTY_SYSV_ABI AgcUnknownDbOlWdppb4o(uint64_t arg0, uint64_t arg1, uint64_t arg2, - uint64_t arg3, uint64_t arg4, uint64_t arg5) { - PRINT_NAME(); - - (void)arg3; - (void)arg4; - (void)arg5; - - auto* out = reinterpret_cast(arg0); - - if (arg2 == 0) { - Graphics5UnknownDbWriteDefault(out, 0); - return 0; - } - - const auto* desc = reinterpret_cast(arg1); - const auto* src = reinterpret_cast(arg2); - - const auto count = ReadUnaligned(src, 0x50u); - - if (count == 0) { - Graphics5UnknownDbWriteDefault(out, 0); - return 0; - } - - const auto mask_count = ReadUnaligned(desc, 0x56u); - const auto* masks = reinterpret_cast(ReadUnaligned(desc, 0x38u)); - const auto* src_entries = - reinterpret_cast(ReadUnaligned(src, 0x30u)); - - for (uint32_t i = 0; i < count; i++) { - auto src_value = src_entries[i]; - auto mask_index = static_cast(mask_count); - - for (uint32_t j = 0; j < mask_count; j++) { - if ((static_cast(masks[j]) ^ static_cast(src_value)) == 0) { - mask_index = j; - break; - } - } - - const bool has_mask = (mask_index < mask_count); - const auto mode = (src_value >> 20u) & 0x3u; - auto flags = 0u; - - if (mode == 0) { - flags = (((src_value >> 24u) & 0x1u) | (has_mask ? 0u : 1u)) << 5u; - flags = Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 28u) & 0x3u, 8u); - } else { - const auto shifted_mode = (src_value << 4u) & 0x03000000u; - flags = shifted_mode + 0x80000u; - - if (mode == 2) { - flags &= 0xffefffdfu; - if (has_mask) { - flags |= ((~(masks[mask_index] & src_value) >> 16u) & 0x20u); - } else { - flags |= 0x20u; - } - flags = Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 30u) & 0x3u, 8u); - flags = Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 30u) & 0x3u, 21u); - } else { - if (has_mask) { - const auto masked = masks[mask_index] & src_value; - flags &= 0xffffffdfu; - flags |= (masked >> 15u) & 0x20u; - flags ^= 0x20u; - flags &= 0xffefffffu; - flags |= ((~masked >> 1u) & 0x100000u); - flags = - Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 30u) & 0x3u, 8u); - } else { - flags |= 0x100020u; - flags = - Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 28u) & 0x3u, 8u); - } - flags = Graphics5UnknownDbApplyTwoBitField(flags, (src_value >> 30u) & 0x3u, 21u); - } - } - - if (has_mask) { - flags = Graphics5UnknownDbApplyFinalMask(flags, src_value, masks[mask_index]); - } else { - flags &= 0xfffffbe0u; - } - - out[i] = (static_cast(flags) << 32u) | (0x10000000u + i); - } - - if (count < 32u) { - Graphics5UnknownDbWriteDefault(out, count); - } - - return 0; -} - LIB_DEFINE(InitAgcDriver_1) { PRINT_NAME_ENABLE(true); @@ -182,7 +43,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("qj7QZpgr9Uw", Gen5::AgcDcbContextStateOp); LIB_FUNC("H6vHS5cidSA", Gen5::AgcDcbContextStateOpGetSize); LIB_FUNC("BfBDZGbti7A", Gen5::AgcGetIsTrinityMode); - LIB_FUNC("dbOlWdppb4o", LibGen5::AgcUnknownDbOlWdppb4o); + LIB_FUNC("dbOlWdppb4o", Gen5::AgcCreateInterpolantMapping2); LIB_FUNC("F0ZXt5q0ZTA", Gen5::AgcDriverGetDefaultOwner); LIB_FUNC("F0Y42t-3e18", Gen5::AgcDriverInitResourceRegistration); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 71003363..2912e07a 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -5504,12 +5504,22 @@ void TestGraphicsCreateInterpolantMapping() { Check(Gen5::AgcCreateInterpolantMapping(regs, nullptr, nullptr) == 0, "null pixel shader interpolant mapping failed"); for (uint32_t i = 0; i < 32u; i++) { - Check(regs[i].offset == Pm4::CX_PS_SHADER_USAGE_BASE + i, + Check(regs[i].offset == Pm4::SPI_PS_INPUT_CNTL_0 + i, "identity interpolant register offset was unexpected"); Check(regs[i].value == i, "identity interpolant register value was unexpected"); } + ShaderRegister native_regs[32] = {}; + Check(Gen5::AgcCreateInterpolantMapping2(native_regs, nullptr, nullptr) == 0, + "native interpolant mapping identity path failed"); + for (uint32_t i = 0; i < 32u; i++) { + Check(native_regs[i].offset == Pm4::SPI_PS_INPUT_CNTL_0 + i, + "native interpolant register offset was unexpected"); + Check(native_regs[i].value == i, + "native interpolant register value was unexpected"); + } + ShaderSemantic gs_semantics[3] = {}; gs_semantics[0].semantic = 7; gs_semantics[0].hardware_mapping = 5; @@ -5523,7 +5533,7 @@ void TestGraphicsCreateInterpolantMapping() { gs.output_semantics = gs_semantics; gs.num_output_semantics = static_cast(std::size(gs_semantics)); - ShaderSemantic ps_semantics[4] = {}; + ShaderSemantic ps_semantics[6] = {}; ps_semantics[0].semantic = 7; ps_semantics[1].semantic = 8; ps_semantics[1].default_value = 2; @@ -5535,6 +5545,13 @@ void TestGraphicsCreateInterpolantMapping() { ps_semantics[3].is_f16 = 1; ps_semantics[3].default_value = 3; ps_semantics[3].default_value_hi = 2; + ps_semantics[4].semantic = 10; + ps_semantics[4].is_f16 = 2; + ps_semantics[4].default_value_hi = 3; + ps_semantics[5].semantic = 11; + ps_semantics[5].is_f16 = 1; + ps_semantics[5].default_value = 2; + ps_semantics[5].default_value_hi = 1; Shader ps{}; ps.input_semantics = ps_semantics; @@ -5550,9 +5567,25 @@ void TestGraphicsCreateInterpolantMapping() { "flat/custom interpolant mapping bits were unexpected"); Check(regs[3].value == 0x01580304u, "f16 interpolant mapping bits were unexpected"); - Check(regs[4].offset == Pm4::CX_PS_SHADER_USAGE_BASE + 4u && - regs[4].value == 4u, + Check(regs[6].offset == Pm4::SPI_PS_INPUT_CNTL_0 + 6u && + regs[6].value == 6u, "interpolant identity tail was not filled"); + + Check(Gen5::AgcCreateInterpolantMapping2(native_regs, &gs, &ps) == 0, + "native shader interpolant mapping failed"); + Check(native_regs[0].value == 0x00000005u && + native_regs[1].value == 0x00000220u && + native_regs[2].value == 0x0000052cu && + native_regs[3].value == 0x01580304u, + "native shader interpolant values were unexpected"); + Check(native_regs[4].offset == Pm4::SPI_PS_INPUT_CNTL_0 + 4u && + native_regs[4].value == 0x02680324u && + native_regs[5].offset == Pm4::SPI_PS_INPUT_CNTL_0 + 5u && + native_regs[5].value == 0x01380220u, + "native mode-specific interpolant values were unexpected"); + Check(native_regs[6].offset == Pm4::SPI_PS_INPUT_CNTL_0 + 6u && + native_regs[6].value == 6u, + "native interpolant identity tail was not filled"); } void TestNewShaderRecompilerNativeWideScalarMemoryIr() {