diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 3e4d33473..67d616406 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -714,78 +714,6 @@ int KYTY_SYSV_ABI AgcUnknownGetFusedShaderSize(SizeAlign* dst, const Shader* fro return OK; } -int KYTY_SYSV_ABI AgcUnknownFuseShaderHalves(Shader* fused_result, const Shader* front, - const Shader* back, void* scratch_mem) { - PRINT_NAME(); - - LOGF("\t fused_result = 0x%016" PRIx64 "\n" - "\t front = 0x%016" PRIx64 "\n" - "\t back = 0x%016" PRIx64 "\n" - "\t scratch_mem = 0x%016" PRIx64 "\n", - reinterpret_cast(fused_result), reinterpret_cast(front), - reinterpret_cast(back), reinterpret_cast(scratch_mem)); - - EXIT_NOT_IMPLEMENTED(fused_result == nullptr); - EXIT_NOT_IMPLEMENTED(front == nullptr); - EXIT_NOT_IMPLEMENTED(back == nullptr); - - const auto front_type = static_cast(front->type); - const auto back_type = static_cast(back->type); - if (!((front_type == Prospero::ShaderBinaryType::kGsFront && - back_type == Prospero::ShaderBinaryType::kGsBack) || - (front_type == Prospero::ShaderBinaryType::kHsFront && - back_type == Prospero::ShaderBinaryType::kHsBack))) { - return GRAPHICS5_ERROR_INVALID_SHADER_HALVES; - } - - *fused_result = *back; - fused_result->type = static_cast(front_type == Prospero::ShaderBinaryType::kGsFront - ? Prospero::ShaderBinaryType::kGs - : Prospero::ShaderBinaryType::kHs); - - if (front->specials != nullptr && back->specials != nullptr) { - const auto front_stages = front->specials->vgt_shader_stages_en.value; - const auto back_stages = back->specials->vgt_shader_stages_en.value; - const auto mismatch_bit = - (front_type == Prospero::ShaderBinaryType::kGsFront ? (1u << 22u) : (1u << 21u)); - if (((front_stages ^ back_stages) & mismatch_bit) != 0) { - return GRAPHICS5_ERROR_INVALID_SHADER_HALVES; - } - } - - if (scratch_mem != nullptr && back->sh_registers != nullptr && back->num_sh_registers != 0) { - auto* sh_registers = static_cast(scratch_mem); - memcpy(sh_registers, back->sh_registers, - static_cast(back->num_sh_registers) * sizeof(ShaderRegister)); - fused_result->sh_registers = sh_registers; - } - - auto* fused_regs = fused_result->sh_registers; - const auto fused_reg_count = static_cast(fused_result->num_sh_registers); - const auto front_reg_count = static_cast(front->num_sh_registers); - - if (front_type == Prospero::ShaderBinaryType::kGsFront) { - for (uint32_t occurrence = 0; occurrence < 2; occurrence++) { - auto* dst = find_shader_register(fused_regs, fused_reg_count, - Pm4::SPI_SHADER_PGM_CHKSUM_GS, occurrence); - const auto* src = find_shader_register(front->sh_registers, front_reg_count, - Pm4::SPI_SHADER_PGM_CHKSUM_GS, occurrence); - if (dst != nullptr && src != nullptr) { - dst->value = src->value; - } - } - patch_shader_register_address(fused_regs, fused_reg_count, Pm4::SPI_SHADER_PGM_LO_ES, - reinterpret_cast(front->code)); - } else { - patch_shader_register_address(fused_regs, fused_reg_count, Pm4::SPI_SHADER_PGM_LO_LS, - reinterpret_cast(front->code)); - } - - fused_result->user_data = nullptr; - - return OK; -} - static void merge_shader_register_max_field(ShaderRegister* dst, const ShaderRegister* src, uint32_t shift, uint32_t mask) { const auto dst_field = (dst->value >> shift) & mask; @@ -796,10 +724,9 @@ static void merge_shader_register_max_field(ShaderRegister* dst, const ShaderReg dst->value |= field << shift; } -int KYTY_SYSV_ABI AgcUnknownNApJjpKNBl4(Shader* fused_result, const Shader* front, - const Shader* back, void* scratch_mem) { - PRINT_NAME(); - +static int fuse_shader_halves(Shader* fused_result, const Shader* front, + const Shader* back, void* scratch_mem, + bool recompute_shared_vgprs) { LOGF("\t fused_result = 0x%016" PRIx64 "\n" "\t front = 0x%016" PRIx64 "\n" "\t back = 0x%016" PRIx64 "\n" @@ -807,6 +734,10 @@ int KYTY_SYSV_ABI AgcUnknownNApJjpKNBl4(Shader* fused_result, const Shader* fron reinterpret_cast(fused_result), reinterpret_cast(front), reinterpret_cast(back), reinterpret_cast(scratch_mem)); + EXIT_NOT_IMPLEMENTED(fused_result == nullptr); + EXIT_NOT_IMPLEMENTED(front == nullptr); + EXIT_NOT_IMPLEMENTED(back == nullptr); + const auto front_type = static_cast(front->type); const auto is_gs = front_type == Prospero::ShaderBinaryType::kGsFront; const auto is_hs = front_type == Prospero::ShaderBinaryType::kHsFront; @@ -839,14 +770,12 @@ int KYTY_SYSV_ABI AgcUnknownNApJjpKNBl4(Shader* fused_result, const Shader* fron const auto front_reg_count = static_cast(front->num_sh_registers); const auto checksum_offset = is_gs ? Pm4::SPI_SHADER_PGM_CHKSUM_GS : Pm4::SPI_SHADER_PGM_CHKSUM_HS; - const auto* front_checksum0 = - find_shader_register(front->sh_registers, front_reg_count, checksum_offset, 0); - const auto* front_checksum1 = - find_shader_register(front->sh_registers, front_reg_count, checksum_offset, 1); - auto* fused_checksum0 = find_shader_register(fused_regs, fused_reg_count, checksum_offset, 0); - auto* fused_checksum1 = find_shader_register(fused_regs, fused_reg_count, checksum_offset, 1); - fused_checksum0->value = front_checksum0->value; - fused_checksum1->value = front_checksum1->value; + for (uint32_t occurrence = 0; occurrence < 2; occurrence++) { + const auto* src = find_shader_register(front->sh_registers, front_reg_count, + checksum_offset, occurrence); + auto* dst = find_shader_register(fused_regs, fused_reg_count, checksum_offset, occurrence); + dst->value = src->value; + } const auto rsrc1_offset = is_gs ? Pm4::SPI_SHADER_PGM_RSRC1_GS : Pm4::SPI_SHADER_PGM_RSRC1_HS; const auto rsrc2_offset = is_gs ? Pm4::SPI_SHADER_PGM_RSRC2_GS : Pm4::SPI_SHADER_PGM_RSRC2_HS; @@ -857,33 +786,51 @@ int KYTY_SYSV_ABI AgcUnknownNApJjpKNBl4(Shader* fused_result, const Shader* fron auto* fused_rsrc1 = find_shader_register(fused_regs, fused_reg_count, rsrc1_offset); auto* fused_rsrc2 = find_shader_register(fused_regs, fused_reg_count, rsrc2_offset); + if (recompute_shared_vgprs) { + const auto front_vgprs = ((front_rsrc1->value & 0x3fu) + 1u) * 4u; + const auto back_vgprs = ((fused_rsrc1->value & 0x3fu) + 1u) * 4u; + const auto front_total = front_vgprs + (front_rsrc2->value >> 28u) * 8u; + const auto back_total = back_vgprs + (fused_rsrc2->value >> 28u) * 8u; + const auto max_total = std::max(front_total, back_total); + // sceAgcFuseShaderHalves reallocates shared VGPRs; the older export takes the maximum. + const auto shared = std::max(front_vgprs, back_vgprs) >= max_total + ? 0u + : (max_total - std::min(front_total, back_total) + 7u) / 64u; + fused_rsrc2->value = (fused_rsrc2->value & 0x0fffffffu) | ((shared & 0xfu) << 28u); + } else { + merge_shader_register_max_field(fused_rsrc2, front_rsrc2, 28, 0x0fu); + } merge_shader_register_max_field(fused_rsrc1, front_rsrc1, 0, 0x3fu); - merge_shader_register_max_field(fused_rsrc2, front_rsrc2, 28, 0x0fu); if (is_gs) { merge_shader_register_max_field(fused_rsrc1, front_rsrc1, 29, 0x03u); merge_shader_register_max_field(fused_rsrc2, front_rsrc2, 16, 0x03u); - fused_rsrc2->value = - (fused_rsrc2->value & 0xf7ffffc1u) | (front_rsrc2->value & 0x0800003eu); fused_rsrc2->value = (fused_rsrc2->value & 0xfffbffffu) | (front_rsrc2->value & 0x00040000u); } else { merge_shader_register_max_field(fused_rsrc1, front_rsrc1, 28, 0x03u); - fused_rsrc2->value = - (fused_rsrc2->value & 0xf7ffffc1u) | (front_rsrc2->value & 0x0800003eu); } + fused_rsrc2->value = + (fused_rsrc2->value & 0xf7ffffc1u) | (front_rsrc2->value & 0x0800003eu); - const auto program_lo_offset = is_gs ? Pm4::SPI_SHADER_PGM_LO_ES : Pm4::SPI_SHADER_PGM_LO_LS; - auto* program_lo = find_shader_register(fused_regs, fused_reg_count, program_lo_offset); - const auto address = reinterpret_cast(front->code); - program_lo->value = static_cast(address >> 8u); - (program_lo + 1)->value &= 0xffffff00u; - (program_lo + 1)->value |= static_cast((address >> 40u) & 0xffu); - - fused_result->user_data = front->user_data; - + patch_shader_register_address(fused_regs, fused_reg_count, + is_gs ? Pm4::SPI_SHADER_PGM_LO_ES : Pm4::SPI_SHADER_PGM_LO_LS, + reinterpret_cast(front->code)); + fused_result->user_data = recompute_shared_vgprs ? nullptr : front->user_data; return OK; } +int KYTY_SYSV_ABI AgcUnknownFuseShaderHalves(Shader* fused_result, const Shader* front, + const Shader* back, void* scratch_mem) { + PRINT_NAME(); + return fuse_shader_halves(fused_result, front, back, scratch_mem, true); +} + +int KYTY_SYSV_ABI AgcUnknownNApJjpKNBl4(Shader* fused_result, const Shader* front, + const Shader* back, void* scratch_mem) { + PRINT_NAME(); + return fuse_shader_halves(fused_result, front, back, scratch_mem, false); +} + static constexpr int GRAPHICS5_ERROR_INVALID_PACKET = static_cast(0x8a6c000cu); enum class RegIndirectPacket : uint32_t { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 8aacf7296..f2cc3bf3c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -25523,6 +25523,69 @@ void CheckPm4DepthControlHighBits(RenderContext &renderer) { std::printf("[host] %-32s ok\n", "Pm4DepthControlHighBits"); } +void CheckAgcShaderFusion() { + struct Case { + bool gs; + uint32_t front1, front2, back1, back2, fused1, fused2, older2; + }; + // Register results from both exports in the supplied libSceAgc.sprx. + constexpr Case cases[]{ + {true, 0x600c0007, 0x00030010, 0x600c0007, 0x008b0000, + 0x600c0007, 0x008b0010, 0x008b0010}, // Captured volume ES + GS. + {true, 0x20ac0003, 0xf805004a, 0x40ec0007, 0x028b003e, + 0x40ec0007, 0x1a8f000a, 0xfa8f000a}, + {false, 0x20ac0003, 0xf805004a, 0x10ec0007, 0x028b003e, + 0x20ec0007, 0x1a8b000a, 0xfa8b000a}, + }; + ShaderUserData user_data{}; + ShaderSpecialRegs specials{}; + for (const auto &c : cases) { + const auto checksum = c.gs ? Pm4::SPI_SHADER_PGM_CHKSUM_GS : Pm4::SPI_SHADER_PGM_CHKSUM_HS; + const auto rsrc1 = c.gs ? Pm4::SPI_SHADER_PGM_RSRC1_GS : Pm4::SPI_SHADER_PGM_RSRC1_HS; + const auto rsrc2 = c.gs ? Pm4::SPI_SHADER_PGM_RSRC2_GS : Pm4::SPI_SHADER_PGM_RSRC2_HS; + const auto lo = c.gs ? Pm4::SPI_SHADER_PGM_LO_ES : Pm4::SPI_SHADER_PGM_LO_LS; + std::array front_regs{{ + {checksum, 0x1234}, {checksum, 0x5678}, {rsrc1, c.front1}, + {rsrc2, c.front2}, {lo, 0}, {lo + 1u, 0}}}; + const std::array back_regs{{ + {checksum, 0}, {checksum, 0}, {rsrc1, c.back1}, + {rsrc2, c.back2}, {lo, 0}, {lo + 1u, 0xabcdef00}}}; + Shader front{}; + front.user_data = &user_data; + front.code = reinterpret_cast(0x12580e91c00ull); + front.sh_registers = front_regs.data(); + front.specials = &specials; + front.type = static_cast(c.gs ? Prospero::ShaderBinaryType::kGsFront + : Prospero::ShaderBinaryType::kHsFront); + front.num_sh_registers = front_regs.size(); + for (const bool older : {false, true}) { + for (const bool in_place : {false, true}) { + auto registers = back_regs; + std::array scratch{}; + Shader back = front; + back.sh_registers = registers.data(); + back.type = static_cast(c.gs ? Prospero::ShaderBinaryType::kGsBack + : Prospero::ShaderBinaryType::kHsBack); + Shader fused{}; + const auto fuse = older ? Gen5::AgcUnknownNApJjpKNBl4 : Gen5::AgcUnknownFuseShaderHalves; + const auto result = fuse(&fused, &front, &back, in_place ? nullptr : scratch.data()); + const auto *regs = fused.sh_registers; + Require("AgcShaderFusion", "native fused registers", + result == 0 && regs == (in_place ? registers.data() : scratch.data()) && + regs[0].value == 0x1234 && regs[1].value == 0x5678 && + regs[2].value == c.fused1 && regs[3].value == (older ? c.older2 : c.fused2) && + regs[4].value == 0x2580e91c && regs[5].value == 0xabcdef01 && + fused.type == static_cast(c.gs ? Prospero::ShaderBinaryType::kGs + : Prospero::ShaderBinaryType::kHs) && + fused.user_data == (older ? &user_data : nullptr) && + (in_place || std::memcmp(registers.data(), back_regs.data(), sizeof(registers)) == 0), + "AGC lost front resources or changed the export's allocation/metadata behavior"); + } + } + } + std::printf("[host] %-32s ok\n", "AgcShaderFusion"); +} + struct AgcCommandBufferLayout { using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t, void *); @@ -26125,6 +26188,7 @@ int main(int argc, char **argv) { } if (argc == 2 && std::strcmp(argv[1], "--context-state-only") == 0) { VulkanHarness vulkan; + CheckAgcShaderFusion(); CheckPm4NativeTargetGeometryRegisters(vulkan.RuntimeRenderer()); CheckPm4PrivateAgcShaderRegisters(vulkan.RuntimeRenderer()); CheckPm4PrivateAgcUconfigRegisters(vulkan.RuntimeRenderer()); @@ -26323,6 +26387,7 @@ int main(int argc, char **argv) { CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer()); CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer()); + CheckAgcShaderFusion(); CheckAgcWaitPackets(vulkan.RuntimeRenderer()); CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());