diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index e0b359b..ea298c0 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -4441,6 +4441,11 @@ void GraphicsInitJmpTablesUcIndirect() { r.vertex_group_size = KYTY_PM4_GET(value, GE_CNTL, VERT_GRP_SIZE); cp.GetUcfg().SetGeControl(r); }; + g_hw_uc_indirect_func[Pm4::GE_PC_ALLOC] = [](KYTY_HW_UC_INDIRECT_ARGS) { + (void)cp; + (void)cmd_offset; + (void)value; + }; g_hw_uc_indirect_func[Pm4::GE_USER_VGPR_EN] = [](KYTY_HW_UC_INDIRECT_ARGS) { HW::GeUserVgprEn r; diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index afe0628..6ce5b53 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -1047,6 +1047,7 @@ constexpr uint32_t GE_USER_VGPR1 = 0x25C; constexpr uint32_t GE_USER_VGPR2 = 0x25D; constexpr uint32_t GE_USER_VGPR3 = 0x25E; constexpr uint32_t GE_STEREO_CNTL = 0x25F; +constexpr uint32_t GE_PC_ALLOC = 0x260; constexpr uint32_t GE_USER_VGPR_EN = 0x262; constexpr uint32_t GE_USER_VGPR_EN_EN_USER_VGPR1_SHIFT = 0; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index af23e8a..4919b76 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -1180,6 +1181,95 @@ int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegiste return OK; } +struct GsOccupancyLimits { + uint32_t vertex; + uint32_t export_count; +}; + +static GsOccupancyLimits get_gs_occupancy_limits(const Shader* gs, uint32_t vertex_capacity, + uint32_t export_capacity) { + const auto* onchip = + find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::VGT_GS_ONCHIP_CNTL); + const auto* subgroup = + find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::GE_NGG_SUBGRP_CNTL); + const auto* vs_out = + find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::SPI_VS_OUT_CONFIG); + const auto* cl_out = + find_shader_register(gs->cx_registers, gs->num_cx_registers, Pm4::PA_CL_VS_OUT_CNTL); + const auto* max_output = find_shader_register(gs->cx_registers, gs->num_cx_registers, + Pm4::GE_MAX_OUTPUT_PER_SUBGROUP); + + const uint32_t output_words = ((max_output->value & 0x3ffu) + 31u) >> 5u; + const uint32_t subgroup_work = + ((((onchip->value >> 11u) & 0x7ffu) * (subgroup->value & 0x1ffu)) + 31u) >> 5u; + uint32_t waves = std::max(subgroup_work, output_words); + if ((gs->specials->vgt_shader_stages_en.value & 0x00400000u) == 0) { + waves >>= 1u; + } + waves = std::max(waves, 1u); + + const uint32_t exports = 1u + ((cl_out->value >> 21u) & 1u) + ((cl_out->value >> 22u) & 1u) + + ((cl_out->value >> 23u) & 1u); + const uint32_t export_limit = (export_capacity / exports) * 4u; + const uint32_t vertex_limit = (vs_out->value & 0x80u) != 0 + ? 2048u + : vertex_capacity / (((vs_out->value >> 2u) & 0xfu) + 1u); + + return {waves * (vertex_limit / output_words), waves * (export_limit / output_words)}; +} + +int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs, + uint32_t budget, float factor) { + PRINT_NAME(); + + constexpr uint32_t FULL_PC_OVERSUBSCRIPTION = 0x7ffu; + constexpr uint32_t FULL_SH_OVERSUBSCRIPTION = 0x007f0000u; + + regs[0] = {Pm4::GE_PC_ALLOC, 0u}; + regs[1] = {Pm4::SPI_SHADER_PGM_RSRC4_GS, 0u}; + + if (budget == 0u) { + return OK; + } + if (budget == UINT32_MAX) { + regs[0].value |= FULL_PC_OVERSUBSCRIPTION; + regs[1].value |= FULL_SH_OVERSUBSCRIPTION; + return OK; + } + + const auto base = get_gs_occupancy_limits(gs, 1024u, 128u); + const auto expanded = get_gs_occupancy_limits(gs, 2048u, 382u); + const auto base_min = std::min(base.vertex, base.export_count); + const auto limit_shift = + (gs->specials->vgt_shader_stages_en.value & 0x00400000u) != 0 ? 5u : 6u; + const auto expanded_limit = + std::min({expanded.vertex, expanded.export_count, budget >> limit_shift, 1024u}); + const auto headroom = expanded_limit > base_min ? expanded_limit - base_min : 0u; + const auto target = static_cast( + std::fma(factor, static_cast(headroom), static_cast(base_min))); + + if (target <= base_min) { + return OK; + } + if (target < base.export_count) { + const auto range = std::max(expanded.vertex - base.vertex, 1u); + auto value = static_cast( + std::min((static_cast(target - base.vertex) << 10u) / range, 1024ull)); + value = std::max(value, 1u); + regs[0].value = (regs[0].value & ~FULL_PC_OVERSUBSCRIPTION) | + (((value << 1u) - 1u) & FULL_PC_OVERSUBSCRIPTION); + regs[1].value |= FULL_SH_OVERSUBSCRIPTION; + } else { + const auto range = std::max(expanded.export_count - base.export_count, 1u); + const auto value = static_cast( + std::min((static_cast(target - base.export_count) * 127u) / range, 127ull)); + regs[0].value |= FULL_PC_OVERSUBSCRIPTION; + regs[1].value = (regs[1].value & ~FULL_SH_OVERSUBSCRIPTION) | (value << 16u); + } + + return OK; +} + static uint32_t shader_semantic_word(const ShaderSemantic& semantic) { return ((semantic.semantic & 0xffu) << 0u) | ((semantic.hardware_mapping & 0xffu) << 8u) | ((semantic.size_in_elements & 0xfu) << 16u) | ((semantic.is_f16 & 0x3u) << 20u) | @@ -1466,7 +1556,7 @@ uint32_t* KYTY_SYSV_ABI GraphicsDcbContextStateOp(CommandBuffer* buf, uint32_t o buf->DbgDump(); - uint32_t* first = nullptr; + uint32_t* first = nullptr; const auto append = [&](uint32_t segment_size_dw) { auto* segment = buf->AllocateDW(segment_size_dw); if (segment == nullptr) { diff --git a/src/libs/agc.h b/src/libs/agc.h index 78a3d15..a9d18eb 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -56,6 +56,8 @@ int KYTY_SYSV_ABI GraphicsCreatePrimState(ShaderRegister* cx_regs, ShaderRegis const Shader* hs, const Shader* gs, uint32_t prim_type); int KYTY_SYSV_ABI GraphicsUpdatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_regs, uint32_t prim_type); +int KYTY_SYSV_ABI GraphicsGetGsOversubscription(ShaderRegister* regs, const Shader* gs, + uint32_t budget, float factor); int KYTY_SYSV_ABI GraphicsCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs, const Shader* ps); int KYTY_SYSV_ABI GraphicsGetDataPacketPayloadAddress(uint32_t** addr, uint32_t* cmd, int type); diff --git a/src/libs/libGraphicsDriver.cpp b/src/libs/libGraphicsDriver.cpp index ec9e807..b67cfd5 100644 --- a/src/libs/libGraphicsDriver.cpp +++ b/src/libs/libGraphicsDriver.cpp @@ -171,6 +171,7 @@ LIB_DEFINE(InitGraphicsDriver_1) { LIB_FUNC("vRoArM9zaIk", Gen5::GraphicsSetUcRegIndirectPatchAddRegisters); LIB_FUNC("D9sr1xGUriE", Gen5::GraphicsCreatePrimState); LIB_FUNC("Y3ymLfZ1384", Gen5::GraphicsUpdatePrimState); + LIB_FUNC("NKIzURsgV7I", Gen5::GraphicsGetGsOversubscription); LIB_FUNC("HV4j+E0MBHE", Gen5::GraphicsCreateInterpolantMapping); LIB_FUNC("V++UgBtQhn0", Gen5::GraphicsGetDataPacketPayloadAddress); LIB_FUNC("s+VGAMDQ0AQ", Gen5::GraphicsGetDataPacketPayloadRange);