From fb45627bb79562de6721862bbb76ac7fe3284f79 Mon Sep 17 00:00:00 2001 From: Alex <270190108+CouchRiv@users.noreply.github.com> Date: Sat, 10 Oct 2026 12:17:08 -0400 Subject: [PATCH] fix(agc): retain registered headers sharing shader code Keep each registered header and its prepared shader state instead of replacing every registration for the same code address. Resolve explicit ABIs by header and programs by type, preserving re-registration order. Co-Authored-By: GPT-6.1-Sol --- .../include/Driver/Shaders/ShaderRegistry.hpp | 4 +- .../src/Driver/Dispatch/Dispatch.cpp | 7 +- .../Execution/src/Driver/Draw/DrawDecode.cpp | 18 ++--- .../src/Driver/Draw/DrawRegisterKey.cpp | 4 +- .../src/Driver/Shaders/ShaderRegistry.cpp | 73 +++++++++++++++---- .../tests/execution/PreparedGraphics.cpp | 6 +- .../tests/execution/PreparedPublication.cpp | 56 ++++++++++++-- .../tests/execution/PreparedShaders.cpp | 63 ++++++++++++++++ 8 files changed, 185 insertions(+), 46 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp index 3ce4f820d..ac19eec56 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp +++ b/core/libs/prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -27,7 +28,7 @@ class VulkanDevice; namespace AgcDriver::DriverDetail { struct ShaderSnapshot; -using ShaderRegistry = std::map>; +using ShaderRegistry = std::map>>; struct PreparedShaderState { struct Entry { @@ -89,6 +90,7 @@ std::shared_ptr PrepareShaderWithDiagnosti std::uint64_t NullPixelProgramAddress(); ShaderSnapshot PrepareNullPixelProgram(const VulkanDevice& device); std::optional RegisteredFloatMode(const ShaderSnapshot& snapshot); +std::shared_ptr RegisteredProgram(const ShaderRegistry& registry, std::uint64_t address, std::initializer_list types); void PublishRegisteredShader(std::shared_ptr& registry, const std::shared_ptr& snapshot); void ResolvePreparedGraphics(const ShaderSnapshot& front, const std::shared_ptr& fragment, std::uint32_t primitiveType, const ShaderRecompiler::SpirvTarget& target); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp index cb71f04dd..dcfcc848f 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Dispatch/Dispatch.cpp @@ -16,12 +16,7 @@ namespace AgcDriver::DriverDetail { void Driver::dispatch(QueueState& queue, std::span packet, const Submission& submission, std::uint64_t indirectArguments) { PerformanceTimer timing("Driver.Dispatch"); const auto address = (static_cast(readRegister(queue.shader, 0x20c)) << 8u) | (static_cast(readRegister(queue.shader, 0x20d) & 0xffu) << 40u); - auto it = submission.shaders->upper_bound(address); - std::shared_ptr registeredShader; - if (it != submission.shaders->begin()) { - --it; - if (address - it->second->codeAddress < it->second->code.size() * sizeof(std::uint32_t)) registeredShader = it->second; - } + auto registeredShader = RegisteredProgram(*submission.shaders, address, {0}); if (!registeredShader) registeredShader = ReadRawComputeShader(address); const auto& snapshot = *registeredShader; require(snapshot.type == 0, "compute program refers to a non-compute shader"); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp index a34853b83..f4444a808 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawDecode.cpp @@ -28,13 +28,10 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const const auto prepare = [&](std::uint64_t address, std::uint8_t type, Stage stage, std::uint32_t rsrc2, std::uint32_t userDataBase) { const bool nullPixel = stage == Stage::Fragment && (address == 0 || pixelSkipped); if (nullPixel) address = NullPixelProgramAddress(); - auto it = registry.upper_bound(address); - require(it != registry.begin(), "graphics program does not belong to a registered shader"); - --it; - const auto& snapshot = *it->second; - require(address - snapshot.codeAddress < snapshot.code.size() * sizeof(std::uint32_t), "graphics program is outside registered shader code"); + const auto registered = RegisteredProgram(registry, address, {type}); + require(registered != nullptr, "graphics program does not belong to a compatible registered shader"); + const auto& snapshot = *registered; require((address - snapshot.codeAddress) % sizeof(std::uint32_t) == 0, "graphics entry point is not dword aligned"); - require(snapshot.type == type, "graphics program refers to an incompatible shader binary type"); if (!nullPixel) Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, rsrc2); const auto resources = nullPixel ? 0u : ReadGraphicsRegister(queue.shader, rsrc2); const auto userCount = ((resources >> 1u) & 0x1fu) | (((resources >> 27u) & 1u) << 5u); @@ -46,7 +43,7 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const 8, {}, {{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}}, - it->second, + registered, codeOffset }; for (std::uint32_t i = 0; i < userCount; ++i) { @@ -89,10 +86,9 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const append(0x0c8, 2, Stage::TessellationEvaluation, 0x08b, 0x08c, Role::Domain); } else if (graphics.stages.path == Graphics::ShaderPath::Geometry) { const auto frontAddress = programAddress(0xc8); - auto snapshot = registry.upper_bound(frontAddress); - require(snapshot != registry.begin(), "geometry front program is not registered"); - --snapshot; - const auto type = snapshot->second->type; + const auto snapshot = RegisteredProgram(registry, frontAddress, {2, 4}); + require(snapshot != nullptr, "geometry front program is not registered"); + const auto type = snapshot->type; require(type == 2 || type == 4, "invalid geometry front binary type"); append(0xc8, type, Stage::Mesh, 0x8b, 0x8c, Role::Main); initializeMerged(programs.back(), 0x82, type == 4); diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp index 36481c410..1759f417e 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp @@ -50,8 +50,8 @@ std::uint64_t Driver::drawRegisterKey(const QueueState& queue, const ShaderRegis continue; } --it; - mix(reinterpret_cast(it->second.get())); - mix(address - it->second->codeAddress); + for (const auto& snapshot : it->second) mix(reinterpret_cast(snapshot.get())); + mix(address - it->first); } return key; } diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp index 52a46d03b..29db99a99 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Shaders/ShaderRegistry.cpp @@ -207,20 +207,57 @@ bool SameHeader(const ShaderSnapshot& snapshot, const Shader* shader) { return std::memcmp(&used, ®istered, offsetof(Shader, num_sh_registers) + sizeof(Shader::num_sh_registers)) == 0; } +std::shared_ptr RegisteredHeader(const ShaderRegistry& registry, const Shader* shader) { + const auto found = registry.find(reinterpret_cast(const_cast(shader->code))); + if (found == registry.end()) return {}; + for (const auto& snapshot : found->second) { + if (snapshot->headerAddress == reinterpret_cast(shader)) return snapshot; + } + for (const auto& snapshot : found->second) { + if (SameHeader(*snapshot, shader)) return snapshot; + } + return {}; +} + +std::shared_ptr RegisteredProgram(const ShaderRegistry& registry, std::uint64_t address, std::initializer_list types) { + auto found = registry.upper_bound(address); + if (found == registry.begin()) return {}; + --found; + std::shared_ptr result; + bool registered = false; + for (const auto& snapshot : found->second) { + if (address - snapshot->codeAddress >= snapshot->code.size() * sizeof(std::uint32_t)) continue; + registered = true; + if (std::ranges::find(types, snapshot->type) == types.end()) continue; + require(result == nullptr || result->type == snapshot->type, "registered program has ambiguous shader binary types"); + result = snapshot; + } + require(!registered || result != nullptr, "registered program refers to an incompatible shader binary type"); + return result; +} + void PublishRegisteredShader(std::shared_ptr& registry, const std::shared_ptr& snapshot) { ShaderPreparationTransaction transaction; + auto published = snapshot; if (registry != nullptr) { const auto found = registry->find(snapshot->codeAddress); if (found != registry->end()) { - const auto& current = *found->second; - if (current.headerAddress == snapshot->headerAddress && current.type == snapshot->type && current.code == snapshot->code && current.header == snapshot->header) { - transaction.Commit(); - return; + for (const auto& current : found->second) { + if (current->headerAddress == snapshot->headerAddress && current->type == snapshot->type && current->code == snapshot->code && current->header == snapshot->header) { + if (current == found->second.back()) { + transaction.Commit(); + return; + } + published = current; + break; + } } } } auto next = registry != nullptr ? std::make_shared(*registry) : std::make_shared(); - next->insert_or_assign(snapshot->codeAddress, snapshot); + auto& headers = (*next)[snapshot->codeAddress]; + std::erase_if(headers, [&](const auto& current) { return current->headerAddress == snapshot->headerAddress || current->code != snapshot->code; }); + headers.push_back(published); registry = std::move(next); transaction.Commit(); } @@ -681,9 +718,10 @@ void Driver::ResolveGraphicsStagesAbi(std::span stages, std GuestMemory::CheckRange(shader, sizeof(Shader), alignof(Shader)); const auto address = reinterpret_cast(const_cast(shader->code)); require(registry != nullptr && registry->contains(address), "graphics ABI refers to an unregistered shader"); - if (owner == nullptr) owner = registry->at(address); - const auto& snapshot = *registry->at(address); - require(SameHeader(snapshot, shader), "graphics ABI refers to a replaced shader header"); + const auto selected = RegisteredHeader(*registry, shader); + require(selected != nullptr, "graphics ABI refers to a replaced shader header"); + if (owner == nullptr) owner = selected; + const auto& snapshot = *selected; require(snapshot.registeredState != nullptr, "registered shader state is missing"); const auto& registered = *snapshot.registeredState; for (const auto& [offset, value] : registered.shader) state.shader.insert_or_assign(offset, value); @@ -747,8 +785,8 @@ void Driver::ResolveShaderAbi(const Shader* shader, std::span(const_cast(shader->code)); require(shaders != nullptr && shaders->contains(address), "static ABI refers to an unregistered shader"); - snapshot = shaders->at(address); - require(SameHeader(*snapshot, shader), "static ABI refers to a replaced shader header"); + snapshot = RegisteredHeader(*shaders, shader); + require(snapshot != nullptr, "static ABI refers to a replaced shader header"); } require(snapshot->registeredState != nullptr, "registered shader state is missing"); QueueState state{}; @@ -809,8 +847,8 @@ void Driver::ResolveGraphicsAbi(const Shader* vertex, const Shader* pixel, std:: const auto lookup = [&](const Shader* shader) { const auto address = reinterpret_cast(const_cast(shader->code)); require(shaders != nullptr && shaders->contains(address), "rectangle ABI refers to an unregistered shader"); - const auto snapshot = shaders->at(address); - require(SameHeader(*snapshot, shader), "rectangle ABI refers to a replaced shader header"); + const auto snapshot = RegisteredHeader(*shaders, shader); + require(snapshot != nullptr, "rectangle ABI refers to a replaced shader header"); return snapshot; }; front = lookup(vertex); @@ -926,10 +964,13 @@ void Driver::RegisterShader(const Shader* shader) { if (shaders != nullptr) { const auto found = shaders->find(snapshot.codeAddress); if (found != shaders->end()) { - const auto& current = *found->second; - if (current.headerAddress == snapshot.headerAddress && current.type == snapshot.type && current.code == snapshot.code && current.header == snapshot.header) { - transaction.Commit(); - return; + for (const auto& current : found->second) { + if (current->headerAddress == snapshot.headerAddress && current->type == snapshot.type && current->code == snapshot.code && current->header == snapshot.header) { + const auto identical = current; + PublishRegisteredShader(shaders, identical); + transaction.Commit(); + return; + } } } } diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedGraphics.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedGraphics.cpp index eb1a511a1..e77a4d2e2 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedGraphics.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedGraphics.cpp @@ -80,7 +80,7 @@ void Check(AgcDriver::VulkanDevice& device, AgcDriver::Graphics::ShaderPath path fragment.snapshot->code.resize(64); fragment.snapshot->code.insert(fragment.snapshot->code.end(), pixelCode.begin(), pixelCode.end()); ShaderRegistry registry; - for (const auto* fixture : {&front, &back, &domain, &fragment}) registry.emplace(fixture->snapshot->codeAddress, fixture->snapshot); + for (const auto* fixture : {&front, &back, &domain, &fragment}) registry.emplace(fixture->snapshot->codeAddress, std::vector>{fixture->snapshot}); AgcDriver::QueueState queue{}; queue.context[0x8e] = 0xfu; queue.context[0x8f] = 0xfu; @@ -256,8 +256,8 @@ void NullPixelMatchesRegistration(AgcDriver::VulkanDevice& device) { const auto registered = null->prepared->entries; Require(!registered.empty(), "the null pixel program was not prepared at registration"); ShaderRegistry registry; - registry.emplace(front.snapshot->codeAddress, front.snapshot); - registry.emplace(null->codeAddress, null); + registry.emplace(front.snapshot->codeAddress, std::vector>{front.snapshot}); + registry.emplace(null->codeAddress, std::vector>{null}); AgcDriver::QueueState queue{}; queue.context[0x8e] = 0xfu; queue.context[0x8f] = 0xfu; diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedPublication.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedPublication.cpp index 99f9c45a3..5c5260c34 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedPublication.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedPublication.cpp @@ -1,4 +1,5 @@ #include "VulkanTestDevice.hpp" +#include "SceShaders.hpp" #include "prx/libSceAgcDriver/Execution/include/ShaderPreparation.hpp" #include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp" #include "Optimization/ResourceProgram.hpp" @@ -6,6 +7,7 @@ #include #include #include +#include #include #include @@ -106,6 +108,45 @@ void Transactions() { Require(!partial && front.prepared->entries.size() == 200 && pixel->prepared->entries.size() == 200, "concurrent group publication was partial or lost an update"); } +void HeaderSelection() { + const auto makeSnapshot = [](std::uint64_t headerAddress, std::uint8_t type) { + auto snapshot = std::make_shared(); + snapshot->codeAddress = 0x10000; + snapshot->headerAddress = headerAddress; + snapshot->type = type; + snapshot->code = {0xbf810000u}; + Shader header{}; + header.code = reinterpret_cast(snapshot->codeAddress); + header.type = type; + snapshot->header.resize(sizeof(Shader)); + std::memcpy(snapshot->header.data(), &header, sizeof(Shader)); + return snapshot; + }; + const auto vertex = makeSnapshot(0x20000, 2); + const auto compute = makeSnapshot(0x30000, 0); + std::shared_ptr registry; + PublishRegisteredShader(registry, vertex); + PublishRegisteredShader(registry, compute); + Require(RegisteredProgram(*registry, 0x10000, {2}) == vertex && RegisteredProgram(*registry, 0x10000, {0}) == compute, "shared code selected another registered type"); + Reject([&] { static_cast(RegisteredProgram(*registry, 0x10000, {1})); }); + Require(RegisteredProgram(*registry, 0x10100, {2}) == nullptr, "an out-of-range entry selected registered code"); + const auto copy = makeSnapshot(0x40000, 2); + PublishRegisteredShader(registry, copy); + Require(RegisteredProgram(*registry, 0x10000, {2}) == copy, "equivalent registered headers became ambiguous"); + PublishRegisteredShader(registry, makeSnapshot(vertex->headerAddress, 2)); + Require(RegisteredProgram(*registry, 0x10000, {2}) == vertex && registry->at(0x10000).size() == 3, "identical earlier registration did not restore its prepared snapshot as current"); + const auto replacement = makeSnapshot(copy->headerAddress, 2); + Shader changed; + std::memcpy(&changed, replacement->header.data(), sizeof(Shader)); + changed.target = 1; + std::memcpy(replacement->header.data(), &changed, sizeof(Shader)); + PublishRegisteredShader(registry, replacement); + Require(RegisteredProgram(*registry, 0x10000, {2}) == replacement, "same-type registration did not keep the latest program metadata"); + const auto geometry = makeSnapshot(0x60000, 4); + PublishRegisteredShader(registry, geometry); + Reject([&] { static_cast(RegisteredProgram(*registry, 0x10000, {2, 4})); }); +} + void Registration(AgcDriver::VulkanDevice& device) { const std::array code{0xbf810000u}; RecompileRequest request{{ShaderStage::Compute, 0x10000, code, 0, {}}, {32, 0, {}, ShaderComputeStageInfo{{1, 1, 1}, 0, {false, false, false}, false, 0, {}}, {}, {}, {}}, device.ComputeTarget(32), {0, 0, 0, 128}}; @@ -130,7 +171,7 @@ void Registration(AgcDriver::VulkanDevice& device) { for (unsigned iteration = 0; iteration < 40; ++iteration) { ShaderPreparationTransaction transaction; PublishRegisteredShader(registry, makeSnapshot()); - const auto current = registry->at(0x10000); + const auto current = registry->at(0x10000).back(); Require(current == original && current->prepared->rectangleRequested, "identical registration replaced a prepared snapshot"); auto& entries = transaction.Edit(*current).entries; entries.push_back({iteration + 1, handle}); @@ -160,11 +201,11 @@ void Registration(AgcDriver::VulkanDevice& device) { transaction.Commit(); } replacementWriter.get(); - Require(registry->at(0x10000) == replacement && submission->at(0x10000) == original, "replacement modified an in-flight registry"); + Require(registry->at(0x10000).back() == replacement && submission->at(0x10000).back() == original, "replacement modified an in-flight registry"); Require(original->prepared->entries.size() == 162 && replacement->prepared->entries.empty(), "stale preparation was published into a replacement snapshot"); original.reset(); - request.shader.code = submission->at(0x10000)->code; - const auto invocation = InvocationFor(*submission->at(0x10000), 0, request); + request.shader.code = submission->at(0x10000).back()->code; + const auto invocation = InvocationFor(*submission->at(0x10000).back(), 0, request); const auto capture = invocation.Capture({}); const auto result = invocation.Materialize(*capture); Require(result->variantId == GetPreparedArtifact(*handle).variantId, "old submission lost its prepared artifact"); @@ -172,19 +213,19 @@ void Registration(AgcDriver::VulkanDevice& device) { device.WaitIdle(); Require(!old.expired(), "in-flight submission released its snapshot early"); submission.reset(); - Require(old.expired(), "completed submission retained an obsolete snapshot"); + Require(!old.expired() && registry->at(0x10000).size() == 2, "registration discarded an earlier header for shared code"); Require(invocation.Materialize(*capture)->spirv.data() == result->spirv.data(), "prepared handle did not retain its artifact"); auto changed = makeSnapshot(); changed->headerAddress = replacement->headerAddress; changed->code[0] ^= 1; PublishRegisteredShader(registry, changed); - Require(registry->at(0x10000) == changed, "changed shader code was treated as identical"); + Require(registry->at(0x10000).back() == changed && registry->at(0x10000).size() == 1 && old.expired(), "changed shader code retained obsolete headers"); auto metadata = makeSnapshot(); metadata->headerAddress = changed->headerAddress; metadata->code = changed->code; metadata->header.push_back(std::byte{1}); PublishRegisteredShader(registry, metadata); - Require(registry->at(0x10000) == metadata, "changed shader metadata was treated as identical"); + Require(registry->at(0x10000).back() == metadata, "changed shader metadata was treated as identical"); } } @@ -192,6 +233,7 @@ void Registration(AgcDriver::VulkanDevice& device) { int main() { try { Transactions(); + HeaderSelection(); auto device = OpenVulkanTestDevice(); if (!device) return VulkanTestSkipped; Registration(*device); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp index 695192b8d..7d01e74a7 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/PreparedShaders.cpp @@ -6,6 +6,7 @@ #include "prx/libSceAgcDriver/Execution/include/Driver.hpp" #include "prx/libSceAgcDriver/Execution/include/ShaderPreparation.hpp" #include "prx/libSceAgcDriver/Submit/include/Acb.hpp" +#include "prx/libSceAgcDriver/Submit/include/Dcb.hpp" #include #include #include @@ -334,6 +335,67 @@ void UnsupportedTypeRegistration() { AgcDriverRegisterShader_nid_postfix(&header.shader); } +void SharedCodeHeaders() { + alignas(256) static const std::array code{0x7e0002ffu, 0, 0x7e0202ffu, 0, 0x7e0402ffu, 0, 0x7e0602ffu, 0x3f800000u, 0xf80008cfu, 0x03020100u, 0xbf810000u}; + struct Header { + Shader shader{}; + std::array registers{}; + ShaderSpecialRegs specials{}; + ShaderUserData users{}; + std::array context{{{0x2d5, 0x2000}}}; + } vertex, mesh; + const auto address = reinterpret_cast(code.data()); + for (auto* header : {&vertex, &mesh}) { + 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->shader.specials = &header->specials; + header->shader.user_data = &header->users; + header->specials.dispatch_modifier = 0x8000; + } + vertex.shader.cx_registers = vertex.context.data(); + vertex.shader.num_cx_registers = vertex.context.size(); + vertex.shader.type = 2; + vertex.registers = {{{0xc8, static_cast(address >> 8u)}, {0xc9, static_cast(address >> 40u)}, {0x8b, 0}, {0x8a, 0}, {0xca, 0}, {0xcb, 0}, {0xcc, 0}}}; + mesh.shader.type = 4; + mesh.registers = vertex.registers; + const std::array primitive{{{0x242, 4}}}; + AgcDriverRegisterShader_nid_postfix(&vertex.shader); + AgcDriverRegisterShader_nid_postfix(&mesh.shader); + AgcDriverResolveShaderAbi_nid_postfix(&vertex.shader, {}, primitive); + AgcDriverRegisterShader_nid_postfix(&mesh.shader); + Shader copy = vertex.shader; + copy.user_data = nullptr; + AgcDriverResolveShaderAbi_nid_postfix(©, {}, primitive); + copy.target ^= 1u; + ExpectFailure([&] { AgcDriverResolveShaderAbi_nid_postfix(©, {}, primitive); }, "replaced shader header"); + std::vector commands; + for (const auto reg : vertex.registers) commands.insert(commands.end(), {0xc0017600u, reg.offset, reg.value}); + for (const auto reg : vertex.context) commands.insert(commands.end(), {0xc0016900u, reg.offset, reg.value}); + commands.insert(commands.end(), {0xc0017600u, 0x8, 0, 0xc0016900u, 0xd, 0x00100010u, 0xc0016900u, 0x1c3, 4, 0xc0017900u, 0x242, 4, 0xc0012d00u, 3, 2}); + Packet packet{commands.data(), static_cast(commands.size()), 0, {}}; + for (unsigned iteration = 0; iteration < 2; ++iteration) { + Require(sceAgcDriverSubmitDcb(&packet) == 0, "shared-code draw submission failed"); + AgcDriverWaitIdle_nid_postfix(); + } + mesh.shader.type = 2; + mesh.shader.cx_registers = mesh.context.data(); + mesh.shader.num_cx_registers = mesh.context.size(); + AgcDriverRegisterShader_nid_postfix(&mesh.shader); + AgcDriverResolveShaderAbi_nid_postfix(&mesh.shader, {}, primitive); + AgcDriverResolveShaderAbi_nid_postfix(&vertex.shader, {}, primitive); + Require(sceAgcDriverSubmitDcb(&packet) == 0, "same-type registered header draw failed"); + AgcDriverWaitIdle_nid_postfix(); + AgcDriverRegisterShader_nid_postfix(&vertex.shader); + AgcDriverResolveShaderAbi_nid_postfix(&vertex.shader, {}, primitive); + Require(sceAgcDriverSubmitDcb(&packet) == 0, "re-registered earlier header draw failed"); + AgcDriverWaitIdle_nid_postfix(); +} + void RegistrationWithoutSpecials() { alignas(256) std::array code{0xbf810000u}; struct Header { @@ -521,6 +583,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(); + SharedCodeHeaders(); RegistrationWithoutSpecials(); Registration(argc == 2); std::cout << "prepared shader and transactional registration tests passed\n";