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";