agc: merge resources from both shader stages

This commit is contained in:
nmzik committed 2026-09-05 07:39:30 +04:00
1 parent c7f7fd974b
commit 246212789f
2 files changed
+110 -98

No files matched your search

+45 -98
View File
@@ -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<uint64_t>(fused_result), reinterpret_cast<uint64_t>(front),
reinterpret_cast<uint64_t>(back), reinterpret_cast<uint64_t>(scratch_mem));
EXIT_NOT_IMPLEMENTED(fused_result == nullptr);
EXIT_NOT_IMPLEMENTED(front == nullptr);
EXIT_NOT_IMPLEMENTED(back == nullptr);
const auto front_type = static_cast<Prospero::ShaderBinaryType>(front->type);
const auto back_type = static_cast<Prospero::ShaderBinaryType>(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<uint8_t>(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<ShaderRegister*>(scratch_mem);
memcpy(sh_registers, back->sh_registers,
static_cast<size_t>(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<uint32_t>(fused_result->num_sh_registers);
const auto front_reg_count = static_cast<uint32_t>(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<uint64_t>(front->code));
} else {
patch_shader_register_address(fused_regs, fused_reg_count, Pm4::SPI_SHADER_PGM_LO_LS,
reinterpret_cast<uint64_t>(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<uint64_t>(fused_result), reinterpret_cast<uint64_t>(front),
reinterpret_cast<uint64_t>(back), reinterpret_cast<uint64_t>(scratch_mem));
EXIT_NOT_IMPLEMENTED(fused_result == nullptr);
EXIT_NOT_IMPLEMENTED(front == nullptr);
EXIT_NOT_IMPLEMENTED(back == nullptr);
const auto front_type = static_cast<Prospero::ShaderBinaryType>(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<uint32_t>(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<uint64_t>(front->code);
program_lo->value = static_cast<uint32_t>(address >> 8u);
(program_lo + 1)->value &= 0xffffff00u;
(program_lo + 1)->value |= static_cast<uint32_t>((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<uint64_t>(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<int>(0x8a6c000cu);
enum class RegIndirectPacket : uint32_t {
+65
View File
@@ -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<ShaderRegister, 6> front_regs{{
{checksum, 0x1234}, {checksum, 0x5678}, {rsrc1, c.front1},
{rsrc2, c.front2}, {lo, 0}, {lo + 1u, 0}}};
const std::array<ShaderRegister, 6> 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<const void *>(0x12580e91c00ull);
front.sh_registers = front_regs.data();
front.specials = &specials;
front.type = static_cast<uint8_t>(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<ShaderRegister, 6> scratch{};
Shader back = front;
back.sh_registers = registers.data();
back.type = static_cast<uint8_t>(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<uint8_t>(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());