shader: release descriptor dependencies

This commit is contained in:
nmzik
2026-09-27 19:37:32 +04:00
parent 421684e75f
commit 566beaa067
5 changed files with 180 additions and 83 deletions
+128 -71
View File
@@ -32009,6 +32009,61 @@ void CheckImageSamplerSpecialization() {
std::printf("[host] %-32s ok\n", "ImageSpecializationPipelineId");
}
void CheckResourcePlanHandoff() {
constexpr const char *name = "ResourcePlanHandoff";
using namespace ShaderRecompiler::IR;
for (const bool numeric_read : {false, true}) {
std::vector<u32> code{EncodeSmem0(0x02, 8), EncodeSmem1(0, 125),
EncodeSmem0(0x08, 12, 4), EncodeSmem1(0, 125)};
if (numeric_read) {
code.insert(code.end(), {EncodeSmem0(0x00, 13), EncodeSmem1(16, 125)});
AppendVop3(&code, 0x125u, 0, 12, 13); // v_add_nc_u32 v0, s12, s13
} else {
code.push_back(EncodeVop1(0x01, 0, 12));
}
code.insert(code.end(), {EncodeMubuf0(0x1c, 0, false, false),
EncodeMubuf1(0, 2, 0)});
AppendEnd(&code);
std::array<u32, 2> user_data{};
std::vector<u32> memory{0x1000u, 0u, 16u, 0x30000000u | DstSel(4, 5, 6, 7), 7u};
ShaderComputeInputInfo compute{};
ShaderRecompiler::CompileOptions options;
options.stage = ShaderType::Compute;
options.user_data = user_data;
options.input_info.compute = &compute;
auto translated = ShaderRecompiler::TranslateProgram(code, options);
auto plan = ExtractResourcePlan(translated.program);
ResourceSnapshot snapshot;
ResourceSpecialization specialization;
const SrtRuntime runtime{.user_data = user_data,
.read_memory = ReadTestMemory,
.userdata = &memory};
Require(name, "initial descriptors",
MaterializeResources(plan, runtime, snapshot, specialization) &&
snapshot.buffers.size() == 1 && snapshot.buffers[0].dwords[0] == 0x1000u,
"SRT buffer descriptor was not materialized");
auto compiled = ShaderRecompiler::CompileProgram(std::move(translated), options,
specialization);
ValidateSpirv(name, compiled.spirv);
const auto &bindings = compiled.program.bindings;
Require(name, "GPU data requirements",
bindings.user_data_registers.empty() && bindings.memory_offset_count == 1 &&
(FindBinding(bindings, DescriptorBindingKind::FlattenedSrt) != nullptr) ==
numeric_read,
"descriptor-only shader retained SRT uploads or live scalar data was removed");
memory[0] = 0x2000u;
memory[4] = 13u;
Require(name, "independent host plan",
MaterializeResources(plan, runtime, snapshot, specialization) &&
snapshot.buffers[0].dwords[0] == 0x2000u &&
(!numeric_read || snapshot.flattened_srt.back() == 13u),
"compiled shader cleanup invalidated or froze the host resource plan");
}
std::printf("[host] %-32s ok\n", name);
}
void CheckNativeImageDescriptorTypes() {
using ShaderRecompiler::IR::DescriptorBindingKind;
using ShaderRecompiler::IR::FirstImageBinding;
@@ -33217,7 +33272,7 @@ void CheckPm4NativeTargetGeometryRegisters(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", "Pm4NativeTargetGeometry");
}
void CheckPm4PrivateAgcShaderRegisters(RenderContext &renderer) {
void CheckPm4IndirectShaderRegisters(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
std::array<uint32_t, 24> registers{
@@ -33245,13 +33300,13 @@ void CheckPm4PrivateAgcShaderRegisters(RenderContext &renderer) {
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_GS] != nullptr &&
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_CHKSUM_HS] != nullptr &&
g_hw_sh_indirect_func[Pm4::SPI_SHADER_PGM_RSRC4_HS] != nullptr;
Require("Pm4PrivateAgcShaderRegisters", "private shader register stream",
Require("Pm4IndirectShaderRegisters", "indirect shader register stream",
handlers_present &&
processor.Process(execution, command) == Pm4ProcessResult::Complete,
"private AGC shader registers were rejected by SET_SH_REG_INDIRECT");
"shader registers were rejected by SET_SH_REG_INDIRECT");
const auto &vs = processor.GetShCtx().GetVs();
const auto &ps = processor.GetShCtx().GetPs().ps_regs;
Require("Pm4PrivateAgcShaderRegisters", "native state around front metadata",
Require("Pm4IndirectShaderRegisters", "native state around front metadata",
vs.es_regs.data_addr == 0x50025e500ull &&
vs.gs_regs.rsrc1.vgprs == 2u &&
vs.gs_regs.rsrc1.gs_vgpr_component_count == 3u &&
@@ -33260,10 +33315,10 @@ void CheckPm4PrivateAgcShaderRegisters(RenderContext &renderer) {
ps.data_addr == 0x500010400ull && ps.rsrc1.vgprs == 13u &&
ps.rsrc2.user_sgpr == 30u,
"GS-front metadata changed native GS state or prevented later PS writes");
std::printf("[host] %-32s ok\n", "Pm4PrivateAgcShaderRegisters");
std::printf("[host] %-32s ok\n", "Pm4IndirectShaderRegisters");
}
void CheckPm4PrivateAgcUconfigRegisters(RenderContext &renderer) {
void CheckPm4IndirectUconfigRegisters(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
std::array<uint32_t, 2> registers{
@@ -33278,11 +33333,11 @@ void CheckPm4PrivateAgcUconfigRegisters(RenderContext &renderer) {
Pm4Execution execution;
const bool handler_present =
g_hw_uc_indirect_func[Pm4::UC_PARAMETER_OVERSUBSCRIPTION] != nullptr;
Require("Pm4PrivateAgcUconfigRegisters", "private UCONFIG register stream",
Require("Pm4IndirectUconfigRegisters", "indirect UCONFIG register stream",
handler_present &&
processor.Process(execution, command) == Pm4ProcessResult::Complete,
"private AGC UCONFIG register was rejected by SET_UCONFIG_REG_INDIRECT");
std::printf("[host] %-32s ok\n", "Pm4PrivateAgcUconfigRegisters");
"UCONFIG register was rejected by SET_UCONFIG_REG_INDIRECT");
std::printf("[host] %-32s ok\n", "Pm4IndirectUconfigRegisters");
}
void CheckPm4DirectShaderRegisterFallback(RenderContext &renderer) {
@@ -33538,12 +33593,12 @@ void CheckPm4DepthRenderOverride(RenderContext &renderer) {
std::printf("[host] %-32s ok\n", name);
}
void CheckAgcShaderFusion() {
void CheckShaderFusion() {
struct Case {
bool gs;
uint32_t front1, front2, back1, back2, fused1, fused2, older2;
};
// Register results from both exports in the supplied libSceAgc.sprx.
// Expected register results for both shader-fusion variants.
constexpr Case cases[]{
{true, 0x600c0007, 0x00030010, 0x600c0007, 0x008b0000,
0x600c0007, 0x008b0010, 0x008b0010}, // Captured volume ES + GS.
@@ -33585,7 +33640,7 @@ void CheckAgcShaderFusion() {
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",
Require("ShaderFusion", "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) &&
@@ -33594,14 +33649,14 @@ void CheckAgcShaderFusion() {
: 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");
"shader fusion lost front resources or changed the export's allocation/metadata behavior");
}
}
}
std::printf("[host] %-32s ok\n", "AgcShaderFusion");
std::printf("[host] %-32s ok\n", "ShaderFusion");
}
struct AgcCommandBufferLayout {
struct CommandBufferLayout {
using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t,
void *);
@@ -33613,10 +33668,10 @@ struct AgcCommandBufferLayout {
void *user_data;
uint32_t reserved_dw;
};
static_assert(offsetof(AgcCommandBufferLayout, cursor_up) == 0x10);
static_assert(offsetof(AgcCommandBufferLayout, reserved_dw) == 0x30);
static_assert(offsetof(CommandBufferLayout, cursor_up) == 0x10);
static_assert(offsetof(CommandBufferLayout, reserved_dw) == 0x30);
void CheckAgcWaitPackets(RenderContext &renderer) {
void CheckPm4WaitPackets(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
constexpr auto packet_mismatch = static_cast<int>(0x8a6c000cu);
@@ -33628,18 +33683,18 @@ void CheckAgcWaitPackets(RenderContext &renderer) {
alignas(8) uint32_t label32 = 0x11223344u;
std::array<uint32_t, 16> packet32{};
AgcCommandBufferLayout dcb32{packet32.data(),
packet32.data() + packet32.size(),
packet32.data(),
packet32.data() + packet32.size(),
nullptr,
nullptr,
0};
CommandBufferLayout dcb32{packet32.data(),
packet32.data() + packet32.size(),
packet32.data(),
packet32.data() + packet32.size(),
nullptr,
nullptr,
0};
const auto address32 = reinterpret_cast<uint64_t>(&label32);
auto *emitted32 =
Gen5::AgcDcbWaitRegMem(reinterpret_cast<Gen5::CommandBuffer *>(&dcb32), 0,
3, 0, 0, &label32, label32, UINT32_MAX, 400);
Require("AgcWaitPackets", "native 32-bit packet",
Require("Pm4WaitPackets", "native 32-bit packet",
Gen5::AgcDcbWaitOnAddressGetSize(0) == 56 &&
emitted32 == packet32.data() &&
dcb32.cursor_up == packet32.data() + 14 &&
@@ -33656,14 +33711,14 @@ void CheckAgcWaitPackets(RenderContext &renderer) {
packet32[10] == 25u && packet32[11] == 0xc0017904u &&
packet32[12] == 0x342u && packet32[13] == 0xc8000000u &&
execute(packet32.data(), 14),
"32-bit waitOnAddress stream differs from native AGC");
"32-bit wait packet differs from the expected stream");
alignas(8) uint32_t patched_label32 = 0xaabbccddu;
const auto patched_address32 = reinterpret_cast<uint64_t>(&patched_label32);
packet32[6] |= 0x2u;
packet32[7] |= 0xa5a40000u;
Require(
"AgcWaitPackets", "native 32-bit patch",
"Pm4WaitPackets", "native 32-bit patch",
Gen5::AgcWaitRegMemPatchAddress(packet32.data(), &patched_label32) == 0 &&
Gen5::AgcWaitRegMemPatchReference(packet32.data(), patched_label32) ==
0 &&
@@ -33681,18 +33736,18 @@ void CheckAgcWaitPackets(RenderContext &renderer) {
alignas(8) uint64_t label64 = 0x1122334455667788ull;
std::array<uint32_t, 16> packet64{};
AgcCommandBufferLayout dcb64{packet64.data(),
packet64.data() + packet64.size(),
packet64.data(),
packet64.data() + packet64.size(),
nullptr,
nullptr,
0};
CommandBufferLayout dcb64{packet64.data(),
packet64.data() + packet64.size(),
packet64.data(),
packet64.data() + packet64.size(),
nullptr,
nullptr,
0};
const auto address64 = reinterpret_cast<uint64_t>(&label64);
auto *emitted64 =
Gen5::AgcDcbWaitRegMem(reinterpret_cast<Gen5::CommandBuffer *>(&dcb64), 1,
3, 0, 0, &label64, label64, UINT64_MAX, 400);
Require("AgcWaitPackets", "native 64-bit packet",
Require("Pm4WaitPackets", "native 64-bit packet",
Gen5::AgcDcbWaitOnAddressGetSize(1) == 64 &&
Gen5::AgcDcbWaitOnAddressGetSize(2) == 0 &&
emitted64 == packet64.data() &&
@@ -33712,14 +33767,14 @@ void CheckAgcWaitPackets(RenderContext &renderer) {
packet64[12] == 25u && packet64[13] == 0xc0017904u &&
packet64[14] == 0x342u && packet64[15] == 0xc8000000u &&
execute(packet64.data(), packet64.size()),
"64-bit waitOnAddress stream differs from native AGC");
"64-bit wait packet differs from the expected stream");
alignas(8) uint64_t patched_label64 = 0x11223344aabbccddull;
const auto patched_address64 = reinterpret_cast<uint64_t>(&patched_label64);
packet64[6] |= 0x5u;
packet64[7] |= 0x5a580000u;
Require(
"AgcWaitPackets", "native 64-bit patch",
"Pm4WaitPackets", "native 64-bit patch",
Gen5::AgcWaitRegMemPatchAddress(packet64.data(), &patched_label64) == 0 &&
Gen5::AgcWaitRegMemPatchReference(packet64.data(),
0xdeadbeefaabbccddull) == 0 &&
@@ -33740,18 +33795,18 @@ void CheckAgcWaitPackets(RenderContext &renderer) {
std::array<uint32_t, 16> invalid{};
const uint32_t short_payload = 0x342u;
Require(
"AgcWaitPackets", "invalid packet",
"Pm4WaitPackets", "invalid packet",
Gen5::AgcWaitRegMemPatchAddress(invalid.data(), &label32) ==
packet_mismatch &&
Gen5::AgcWaitRegMemPatchReference(invalid.data(), label32) ==
packet_mismatch &&
!Gen5::AgcIsInternalDataPacket(
KYTY_PM4(2, Pm4::IT_SET_UCONFIG_REG, 1), &short_payload),
"native wait patch accepted a packet without the AGC metadata prefix");
std::printf("[host] %-32s ok\n", "AgcWaitPackets");
"wait patch accepted a packet without the metadata prefix");
std::printf("[host] %-32s ok\n", "Pm4WaitPackets");
}
void CheckAgcDrawIndirectMultiPacket(RenderContext &renderer) {
void CheckPm4DrawIndirectMultiPacket(RenderContext &renderer) {
GraphicsInitJmpTables();
CommandProcessor processor(renderer, 0);
const auto execute = [&](uint32_t *packet, uint32_t size_dw) {
@@ -33771,19 +33826,19 @@ void CheckAgcDrawIndirectMultiPacket(RenderContext &renderer) {
0x00000022u, 0xc0017904u, 0x00000342u, 0xc6000000u};
std::array<uint32_t, 16> packet{};
AgcCommandBufferLayout dcb{packet.data(),
packet.data() + packet.size(),
packet.data(),
packet.data() + packet.size(),
nullptr,
nullptr,
0};
CommandBufferLayout dcb{packet.data(),
packet.data() + packet.size(),
packet.data(),
packet.data() + packet.size(),
nullptr,
nullptr,
0};
auto *emitted = Gen5::AgcDcbDrawIndirectMulti(
reinterpret_cast<Gen5::CommandBuffer *>(&dcb), 0x11223344u, 1u,
0x55667788u, count_address, 0xaabbccddu, modifier);
const uint32_t invalid_payload[]{0x342u, 0xc6000010u};
Require("AgcDrawIndirectMulti", "native packet",
Require("Pm4DrawIndirectMulti", "native packet",
emitted == packet.data() &&
dcb.cursor_up == packet.data() + packet.size() &&
packet == expected && execute(packet.data(), 3u) &&
@@ -33793,8 +33848,8 @@ void CheckAgcDrawIndirectMultiPacket(RenderContext &renderer) {
Gen5::AgcWaitRegMemPatchReference(packet.data(), 0x12345678u) ==
packet_mismatch &&
!Gen5::AgcIsInternalDataPacket(0xc0017904u, invalid_payload),
"drawIndirectMulti stream differs from native AGC");
std::printf("[host] %-32s ok\n", "AgcDrawIndirectMulti");
"indirect multi-draw packet differs from the expected stream");
std::printf("[host] %-32s ok\n", "Pm4DrawIndirectMulti");
}
void CheckPm4ContextStateOperations(RenderContext &renderer) {
@@ -33814,13 +33869,13 @@ void CheckPm4ContextStateOperations(RenderContext &renderer) {
const auto invoke = [&](ContextStateOperation operation) {
std::array<uint32_t, 32> packet{};
std::array<uint32_t, 6> segment_sizes{};
AgcCommandBufferLayout dcb{packet.data(),
packet.data() + packet.size(),
packet.data(),
packet.data() + packet.size(),
nullptr,
nullptr,
0};
CommandBufferLayout dcb{packet.data(),
packet.data() + packet.size(),
packet.data(),
packet.data() + packet.size(),
nullptr,
nullptr,
0};
const auto operation_value = static_cast<uint32_t>(operation);
size_t segment_count = 0;
switch (operation) {
@@ -34456,6 +34511,7 @@ int main(int argc, char **argv) {
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--shader-data-storage-only") == 0) {
CheckResourcePlanHandoff();
VulkanHarness vulkan;
RunCase(&vulkan, BufferOffsetsUsePackedLaneAndStorageFallback());
return 0;
@@ -34593,10 +34649,10 @@ int main(int argc, char **argv) {
}
if (argc == 2 && std::strcmp(argv[1], "--context-state-only") == 0) {
VulkanHarness vulkan;
CheckAgcShaderFusion();
CheckShaderFusion();
CheckPm4NativeTargetGeometryRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4IndirectShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4IndirectUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
@@ -34604,15 +34660,15 @@ int main(int argc, char **argv) {
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--agc-wait-only") == 0) {
if (argc == 2 && std::strcmp(argv[1], "--pm4-wait-only") == 0) {
VulkanHarness vulkan;
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckPm4WaitPackets(vulkan.RuntimeRenderer());
CheckPm4WaitResume(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--agc-draw-multi-only") == 0) {
if (argc == 2 && std::strcmp(argv[1], "--pm4-draw-multi-only") == 0) {
VulkanHarness vulkan;
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());
CheckPm4DrawIndirectMultiPacket(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--rewind-only") == 0) {
@@ -34820,6 +34876,7 @@ int main(int argc, char **argv) {
VulkanHarness vulkan;
#endif
CheckImageSamplerSpecialization();
CheckResourcePlanHandoff();
CheckNativeImageDescriptorTypes();
CheckClipControlDepthClipState();
CheckReferenceClockScale();
@@ -34830,17 +34887,17 @@ int main(int argc, char **argv) {
CheckPm4Predication(vulkan.RuntimeRenderer());
CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer());
CheckPm4NativeTargetGeometryRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4PrivateAgcUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4IndirectShaderRegisters(vulkan.RuntimeRenderer());
CheckPm4IndirectUconfigRegisters(vulkan.RuntimeRenderer());
CheckPm4DirectShaderRegisterFallback(vulkan.RuntimeRenderer());
CheckPm4GuardBandRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer());
CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer());
CheckPm4DepthControlHighBits(vulkan.RuntimeRenderer());
CheckPm4DepthRenderOverride(vulkan.RuntimeRenderer());
CheckAgcShaderFusion();
CheckAgcWaitPackets(vulkan.RuntimeRenderer());
CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer());
CheckShaderFusion();
CheckPm4WaitPackets(vulkan.RuntimeRenderer());
CheckPm4DrawIndirectMultiPacket(vulkan.RuntimeRenderer());
CheckPm4ContextStateOperations(vulkan.RuntimeRenderer());
CheckPm4IndirectControlFlow(vulkan.RuntimeRenderer());
CheckPm4WaitResume(vulkan.RuntimeRenderer());