From a32fe0a42ca02bd0a77ecf03827324e6e633e866 Mon Sep 17 00:00:00 2001 From: Evgeniy Kazarin Date: Sun, 18 Oct 2026 06:32:03 +0300 Subject: [PATCH 1/2] perf(agc): validate host imports through their registered owner --- .../Graphics/src/GuestBufferMemory.cpp | 24 ++++++++++++++++++- .../prx/libSceAgcDriver/tests/Recorder.cpp | 1 + .../tests/execution/HostImportWindows.cpp | 15 ++++++++---- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp index 1f7e3407ec7b..38c14c55feef 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp @@ -936,6 +936,23 @@ bool sameRange(const HostImport& entry, const GuestAllocations::Lease& lease) { return sameRun(entry, entry.joined ? joinedRun(lease, entry.base, entry.base + entry.bytes) : importRun(lease, entry.base)); } +bool singleRangeImportCurrent(const HostImport& entry) { + if (entry.joined || entry.unwatched || entry.ranges.size() != 1) return false; + const auto generation = GuestAllocations::GuestAllocationsGeneration_nid_postfix(); + const auto current = GuestAllocations::GuestAllocationsLookup_nid_postfix(entry.base, false); + if (current == nullptr || !current->readable || entry.ranges.front().owner_before(current) || current.owner_before(entry.ranges.front())) return false; + if (entry.base < current->address || entry.bytes > current->bytes || entry.base - current->address > current->bytes - entry.bytes) return false; + if (entry.bytes == current->bytes) { + if (current->address != 0) { + const auto previous = GuestAllocations::GuestAllocationsLookup_nid_postfix(current->address - 1, false); + if (previous != nullptr && continuesRun(*previous, *current)) return false; + } + const auto next = GuestAllocations::GuestAllocationsLookup_nid_postfix(current->address + current->bytes, false); + if (next != nullptr && continuesRun(*current, *next)) return false; + } + return generation == GuestAllocations::GuestAllocationsGeneration_nid_postfix(); +} + bool pointerHeld(const HostImports& state, const HostImport& entry) { return entry.lastUse >= state.buildFloor; } @@ -4613,7 +4630,12 @@ std::uint64_t HostImportSerial(const Context& context, std::uint64_t address, st std::lock_guard lock(state.mutex); // An import whose registered range changed must not be reused: reconcile first, as an upload // does (the walk only runs when the registry changed since the last one). - if (reconcile && GuestAllocations::GuestAllocationsGeneration_nid_postfix() != state.refreshedGeneration) static_cast(refreshImports(context, state, GuestAllocations::GuestAllocationsAcquire_nid_postfix())); + if (reconcile && GuestAllocations::GuestAllocationsGeneration_nid_postfix() != state.refreshedGeneration) { + const auto next = state.imports.upper_bound(address); + const auto existing = next != state.imports.begin() ? &std::prev(next)->second : nullptr; + const bool current = state.device == context.device && existing != nullptr && address >= existing->base && address + bytes <= existing->base + existing->bytes && singleRangeImportCurrent(*existing); + if (!current) refreshImports(context, state, GuestAllocations::GuestAllocationsAcquire_nid_postfix()); + } auto found = state.imports.upper_bound(address); if (found == state.imports.begin()) return 0; --found; diff --git a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp index 580996f9212f..e2559375b2f3 100644 --- a/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/Recorder.cpp @@ -1770,6 +1770,7 @@ void secondMappingImportTests(const Device& device) { const auto first = HostImportSerial(context, address, bytes, true); Require(first != 0 && HostImportSerial(context, address, bytes, true) == first, "singly mapped direct memory lost its import"); Require(sceKernelMapDirectMemory(&release.alias, page, 0x33, 0, release.physical, page) == 0, "map the first page a second time"); + Require(HostImportSerial(context, address, bytes, true) != first, "a second mapping kept a stale import serial before the next import lookup"); Require(HostImportFor(context, address, bytes) != nullptr, "direct memory was not imported again after a second mapping of its first page"); const auto second = HostImportSerial(context, address, bytes, true); Require(second != 0 && second != first, "the import of direct memory whose pages were replaced was kept"); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/HostImportWindows.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/HostImportWindows.cpp index 57396daef2a8..1a0b54402135 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/HostImportWindows.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/HostImportWindows.cpp @@ -20,6 +20,14 @@ #include #include +namespace AgcDriver { + +struct VulkanDeviceTestAccess { + static Graphics::Context ContextOf(const VulkanDevice& device) { return device.graphicsContext(); } +}; + +} + namespace { namespace Graphics = AgcDriver::Graphics; @@ -74,10 +82,7 @@ class GuestBlock { enum class Outcome : std::uint8_t { NoDevice, NoImport, Passed }; Graphics::Context Probe(const AgcDriver::VulkanDevice& device) { - Graphics::Context probe{}; - probe.device = device.Device(); - probe.hostImportAlignment = 1; - return probe; + return AgcDriver::VulkanDeviceTestAccess::ContextOf(device); } bool Fill(AgcDriver::VulkanDevice& device, std::uint64_t address) { @@ -113,11 +118,13 @@ Outcome WindowsFollowTheirRange() { Require(first != 0, "host import windows: the window at " + Hex(address) + " has no serial"); GuestBlock other(OtherBytes); + Require(Graphics::HostImportSerial(probe, address, ProbeBytes, true) == first, "host import windows: an unrelated mapping changed the import serial"); Require(Fill(*device, address), "host import windows: the window at " + Hex(address) + " cannot be filled after an unrelated registration"); Require(Graphics::HostImportSerial(probe, address, ProbeBytes, false) == first, "host import windows: the window at " + Hex(address) + " was dropped by an unrelated registration, although its range still owns it"); GuestAllocations::Mutation().Remove(guest.Data()); GuestAllocations::Mutation().Add(guest.Data(), BlockBytes, true, true, true); + Require(Graphics::HostImportSerial(probe, address, ProbeBytes, true) != first, "host import windows: a remapped range kept its stale import serial before the next fill"); Require(Fill(*device, address), "host import windows: the window at " + Hex(address) + " cannot be filled after its range was mapped again"); const auto second = Graphics::HostImportSerial(probe, address, ProbeBytes, false); Require(second != 0 && second != first, "host import windows: the window of the range mapped again kept the serial " + Hex(first)); From 7cea858cf57e40a5a2b50b2e2b0c109d8dd278f0 Mon Sep 17 00:00:00 2001 From: Evgeniy Kazarin Date: Sun, 18 Oct 2026 04:53:14 -0400 Subject: [PATCH 2/2] chore: record performance-series PR metadata Complete the publication checklist and series cross-links for #7466. No source or test changes.