fix(agc): defer registered compute shaders without specials to dispatch

A registered compute shader takes its wave size from its header's
specials (dispatch_modifier), and PrepareRegistered read them without a
null check. ps5-opengl registers compute headers without specials, since
it writes the specials pointer only for linked graphics stages, and
passes the wave size in the dispatch initiator instead. Registration
then threw "shader metadata is outside the registered header".

Such a shader is now deferred like the other shaders that can't be
prepared at registration, so it is prepared at its first dispatch with
the wave size from the initiator's CS_W32_EN.
This commit is contained in:
AlxcBTW authored and Dean Galvin committed 2026-10-10 21:20:29 +13:00
1 parent f6af6d11d3
commit f752ef4f2b
2 files changed
+29

No files matched your search

@@ -452,6 +452,10 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
if (userCount > 32) throw std::runtime_error("AGC driver: static user SGPR count exceeds the register bank");
if (stage == Stage::Compute) {
compute = Graphics::DecodeComputeStageInfo(state.shader, snapshot.header);
if (header.specials == nullptr) {
if (deferred != nullptr) return defer("its header has no specials to give its wave size");
return {};
}
const auto special = ReadHeaderArray(snapshot, header.specials, 1).front();
wave = (special.dispatch_modifier & 0x8000u) != 0 ? 32u : 64u;
} else if (stage == Stage::Fragment) {
@@ -334,6 +334,30 @@ void UnsupportedTypeRegistration() {
AgcDriverRegisterShader_nid_postfix(&header.shader);
}
void RegistrationWithoutSpecials() {
alignas(256) std::array<std::uint32_t, 1> code{0xbf810000u};
struct Header {
Shader shader{};
std::array<ShaderRegister, 7> registers{};
} header;
const auto address = reinterpret_cast<std::uintptr_t>(code.data());
header.shader.file_header = 0x34333231u;
header.shader.version = 0x18;
header.shader.header_size = sizeof(header);
header.shader.shader_size = sizeof(code);
header.shader.code = code.data();
header.shader.sh_registers = header.registers.data();
header.shader.num_sh_registers = header.registers.size();
header.registers = {{{0x20c, static_cast<std::uint32_t>(address >> 8u)}, {0x20d, static_cast<std::uint32_t>(address >> 40u)}, {0x207, 1}, {0x208, 1}, {0x209, 1}, {0x212, 0}, {0x213, 0}}};
AgcDriverRegisterShader_nid_postfix(&header.shader);
std::vector<std::uint32_t> commands;
for (const auto reg : header.registers) commands.insert(commands.end(), {0xc0017600u, reg.offset, reg.value});
for (const std::uint32_t initiator : {0x8041u, 0x41u}) commands.insert(commands.end(), {0xc0031500u, 1, 1, 1, initiator});
Packet packet{commands.data(), static_cast<std::uint32_t>(commands.size()), 0, {}};
sceAgcDriverSubmitAcb(0x20, &packet);
AgcDriverWaitIdle_nid_postfix();
}
void Registration(bool indirect) {
UnsupportedTypeRegistration();
alignas(256) std::array<std::uint32_t, 1> code{0xbf810000u};
@@ -496,6 +520,7 @@ int main(int argc, char** argv) {
Require(argc != 2 || std::string_view(argv[1]) != "--fail-before-registration", "injected failure before registration");
PrepareMultisampledStorage(*device);
device.reset();
RegistrationWithoutSpecials();
Registration(argc == 2);
std::cout << "prepared shader and transactional registration tests passed\n";
return 0;