Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ee42e69
Merge remote-tracking branch 'origin/pr-9533' into up/pub-a
JAB-dev Oct 29, 2026
0a32528
feat(agc): publish a written native multisample D32S8 surface to its …
JAB-dev Oct 29, 2026
0bfa952
fix(agc): publish native multisample depth bytes only where the guest…
JAB-dev Oct 29, 2026
94e65ca
test(agc): assert that depth publication keeps guest bytes and waits …
JAB-dev Oct 29, 2026
790e422
fix(agc): import a native multisample D32S8 target whose stencil plan…
JAB-dev Oct 29, 2026
bb085f7
Merge origin/main into up/pub-a
JAB-dev Oct 30, 2026
93673d8
Merge feat/agc-publish-native-d32s8-guest-planes into fix/agc-import-…
JAB-dev Oct 30, 2026
48b3aa3
Merge origin/main into up/pub-a
JAB-dev Oct 31, 2026
f2a0b8c
Merge feat/agc-publish-native-d32s8-guest-planes into fix/agc-import-…
JAB-dev Oct 31, 2026
ab5a323
Merge origin/main into up/pub-a
JAB-dev Oct 31, 2026
24869a3
fix(agc): check the guest buffers before a dispatch or draw takes its…
JAB-dev Oct 31, 2026
94de485
Merge feat/agc-publish-native-d32s8-guest-planes into fix/agc-import-…
JAB-dev Oct 31, 2026
6a773d8
Merge remote-tracking branch 'origin/main' into feat/agc-publish-nati…
JAB-dev Oct 31, 2026
a515279
Merge remote-tracking branch 'origin/main' into fix/agc-import-d32s8-…
JAB-dev Oct 31, 2026
e65995d
Merge remote-tracking branch 'origin/main' into feat/agc-publish-nati…
JAB-dev Nov 2, 2026
d621fa1
Merge branch 'feat/agc-publish-native-d32s8-guest-planes' into fix/ag…
JAB-dev Nov 2, 2026
f6e7798
docs(agc): drop guest publication of a written surface from the unimp…
JAB-dev Nov 2, 2026
474d598
Merge branch 'feat/agc-publish-native-d32s8-guest-planes' into fix/ag…
JAB-dev Nov 2, 2026
456c2ef
Merge remote-tracking branch 'origin/main' into feat/agc-publish-nati…
JAB-dev Nov 2, 2026
f49ecfc
Merge branch 'feat/agc-publish-native-d32s8-guest-planes' into fix/ag…
JAB-dev Nov 2, 2026
6e2b826
Merge remote-tracking branch 'origin/main' into feat/agc-publish-nati…
JAB-dev Nov 3, 2026
4767e11
Merge branch 'feat/agc-publish-native-d32s8-guest-planes' into fix/ag…
JAB-dev Nov 3, 2026
0180baf
Merge remote-tracking branch 'origin/main' into feat/agc-publish-nati…
JAB-dev Nov 3, 2026
a4cd5ea
Merge branch 'feat/agc-publish-native-d32s8-guest-planes' into fix/ag…
JAB-dev Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ void Write(std::uint64_t address, std::span<const std::byte> source, std::size_t
// guest writes made meanwhile to untouched bytes survive. Compares in 256-byte blocks.
void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original);
void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint32_t byteStoreLimit);
std::size_t WriteChangedIfHeld(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint64_t unchangedSince);
std::uint32_t ChangedByteStoreLimit();

// Write tracking. The guest arena is reserved with page write watching, so CollectWrites learns which
Expand Down Expand Up @@ -133,6 +134,7 @@ std::uint64_t UntrackedHint(std::uint64_t address, std::size_t bytes);
// the stamps collects make (dirty pages), not the MarkWritten stamps of the driver's own GPU label
// records, which share the title's label blocks. False when the arena is not write-watched.
bool UnchangedSinceCollected(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
bool UnchangedSinceLastCollect(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
// Per 64 KiB tracker block of [address, address + bytes) (block 0 holds `address`): `changed[k]`
// receives whether the block was stamped after generations[k] (UnchangedSince over that block
// alone), `cpu[k]`, when given, whether a collect stamped it after (a CPU store, as
Expand Down
105 changes: 83 additions & 22 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1417,9 +1417,28 @@ struct WriteTracker {
return false;
}

bool rangeUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t since) const {
bool rangeUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t since, bool walked = true) const {
if (refusedSince(address, address + bytes, since)) return false;
return unchanged(blockOf(address), blockOf(address + bytes - 1), since) || !storedOver(address, address + bytes, since, true);
return unchanged(blockOf(address), blockOf(address + bytes - 1), since) || !storedOver(address, address + bytes, since, walked);
}

bool cpuUnchanged(std::uint64_t address, std::size_t bytes, std::uint64_t generation) const {
const auto first = blockOf(address);
const auto last = blockOf(address + bytes - 1u);
const auto end = address + bytes;
for (auto block = first; block <= last; ++block) {
if (const auto found = cpuPages.find(block); found != cpuPages.end()) {
const auto begin = blockBegin(block);
const auto from = std::max(address, begin) - begin;
const auto to = std::min<std::uint64_t>(end, begin + WriteBlockBytes) - begin;
for (auto page = from / WritePageBytes; page <= (to - 1u) / WritePageBytes; ++page) {
if (found->second.pages[page] > generation) return false;
}
} else if (cpuStampOf(block) > generation) {
return false;
}
}
return true;
}

void stamp(std::uint64_t block, std::uint32_t stampGeneration, StampKind kind) {
Expand Down Expand Up @@ -2155,21 +2174,21 @@ static GuestAllocations::Lease::Ranges pinStoreRanges(std::uint64_t address, std
return pinned;
}

std::uint64_t storeOwn(std::uint64_t address, std::size_t bytes, const std::function<std::pair<std::uint64_t, std::uint64_t>()>& store) {
std::uint64_t storeOwn(std::uint64_t address, std::size_t bytes, const std::function<std::pair<std::uint64_t, std::uint64_t>()>& store, bool complete = true) {
if (bytes == 0) return 0;
const auto pinned = pinStoreRanges(address, bytes);
BreakProofs();
const auto stampStored = [](WriteTracker& tracker, std::pair<std::uint64_t, std::uint64_t> stored) -> std::uint64_t {
const auto stampStored = [complete](WriteTracker& tracker, std::pair<std::uint64_t, std::uint64_t> stored) -> std::uint64_t {
if (stored.second <= stored.first) return 0;
++tracker.generation;
if (!tracker.watched || !tracker.covers(stored.first, static_cast<std::size_t>(stored.second - stored.first))) {
tracker.noteUnstampedStore(stored.first, stored.second);
if (tracker.watched) tracker.stampWatchedPart(stored.first, stored.second, tracker.generation, true);
if (tracker.watched) tracker.stampWatchedPart(stored.first, stored.second, tracker.generation, complete);
return 0;
}
for (auto block = tracker.blockOf(stored.first); block <= tracker.blockOf(stored.second - 1); ++block) {
tracker.stamp(block, tracker.generation, StampKind::Driver);
tracker.noteDriverStore(block, stored.first, stored.second, tracker.generation, true);
tracker.noteDriverStore(block, stored.first, stored.second, tracker.generation, complete);
}
return tracker.generation;
};
Expand Down Expand Up @@ -2371,22 +2390,16 @@ bool UnchangedSinceCollected(std::uint64_t address, std::size_t bytes, std::uint
auto& tracker = Tracker();
const auto lock = lockTracker(tracker);
if (!tracker.watched || generation == 0 || bytes == 0 || !tracker.covers(address, bytes)) return false;
const auto first = tracker.blockOf(address);
const auto last = tracker.blockOf(address + bytes - 1);
const auto end = address + bytes;
for (auto block = first; block <= last; ++block) {
if (const auto found = tracker.cpuPages.find(block); found != tracker.cpuPages.end()) {
const auto begin = tracker.blockBegin(block);
const auto from = std::max(address, begin) - begin;
const auto to = std::min<std::uint64_t>(end, begin + WriteBlockBytes) - begin;
for (auto page = from / WritePageBytes; page <= (to - 1) / WritePageBytes; ++page) {
if (found->second.pages[page] > generation) return false;
}
} else if (tracker.cpuStampOf(block) > generation) {
return false;
}
}
return true;
return tracker.cpuUnchanged(address, bytes, generation);
}

bool UnchangedSinceLastCollect(std::uint64_t address, std::size_t bytes, std::uint64_t generation) {
if (bytes == 0u || bytes > std::numeric_limits<std::uint64_t>::max() - address) return false;
auto& tracker = Tracker();
const auto lock = lockTracker(tracker);
tracker.initialize();
if (!tracker.watched || generation == 0u || !tracker.covers(address, bytes)) return false;
return tracker.cpuUnchanged(address, bytes, generation) && tracker.rangeUnchanged(address, bytes, generation, false);
}

bool WrittenSince(std::uint64_t address, std::size_t bytes, std::uint64_t generation) {
Expand Down Expand Up @@ -3072,6 +3085,54 @@ void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std
});
}

static_assert(std::atomic_ref<std::uint64_t>::is_always_lock_free && std::atomic_ref<std::uint8_t>::is_always_lock_free);

std::size_t WriteChangedIfHeld(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint64_t unchangedSince) {
require(current.size() == original.size(), "write-back snapshot sizes differ");
BreakProofs();
if (current.empty()) return 0;
const ReadSiteScope site(ReadSite::Store);
FlushGpuWrites(address, current.size());
const TimedAccess timed(CounterChangedWrite, current.size());
auto* destination = reinterpret_cast<std::uint8_t*>(address);
const auto size = current.size();
std::size_t lost = 0;
storeOwn(address, size, [&] {
CheckRange(destination, size, 1, true);
if (unchangedSince != 0u) {
auto& tracker = Tracker();
if (!tracker.cpuUnchanged(address, size, unchangedSince) || tracker.refusedSince(address, address + size, unchangedSince)) {
lost = size;
return std::pair<std::uint64_t, std::uint64_t>{0, 0};
}
}
std::size_t firstChanged = size;
std::size_t lastChanged = 0;
const auto exchange = [&](std::size_t index) {
if (current[index] == original[index]) return;
auto expected = std::to_integer<std::uint8_t>(original[index]);
firstChanged = std::min(firstChanged, index);
lastChanged = std::max(lastChanged, index + 1);
if (!std::atomic_ref<std::uint8_t>(destination[index]).compare_exchange_strong(expected, std::to_integer<std::uint8_t>(current[index]))) ++lost;
};
std::size_t at = 0;
for (; at < size && lost == 0 && ((address + at) & 7u) != 0u; ++at) exchange(at);
for (; at + 8 <= size && lost == 0; at += 8) {
std::uint64_t wanted, held;
std::memcpy(&wanted, current.data() + at, 8);
std::memcpy(&held, original.data() + at, 8);
if (wanted == held) continue;
firstChanged = std::min(firstChanged, at);
lastChanged = std::max(lastChanged, at + 8);
if (std::atomic_ref<std::uint64_t>(*reinterpret_cast<std::uint64_t*>(destination + at)).compare_exchange_strong(held, wanted)) continue;
for (std::size_t index = at; index < at + 8; ++index) exchange(index);
}
for (; at < size && lost == 0; ++at) exchange(at);
return firstChanged < lastChanged ? std::pair<std::uint64_t, std::uint64_t>{address + firstChanged, address + lastChanged} : std::pair<std::uint64_t, std::uint64_t>{0, 0};
}, false);
return lost;
}

void Write(std::uint64_t address, std::span<const std::byte> source, std::size_t alignment) {
if (source.empty()) return;
// A store, as in WriteChanged.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5423,6 +5423,7 @@ void VulkanDevice::recordDispatch(RecordedDispatch& record) {
if (recorder.HasQueuedKeyStores() && (resources.HoldsLease() || recorder.AnyQueuedKeyStore(touches))) recorder.FlushKeyStores();
if (recorder.HasQueuedStores() && (resources.HoldsLease() || recorder.AnyQueuedStore(touches))) recorder.FlushStores();
static_cast<void>(Graphics::StorageTexture::StoreDrawnLinearTouching("dispatch access", touches, resources.HoldsLease()));
resources.RequireGuestBuffers();
std::shared_ptr<ScratchPool> pool;
if (const auto slotDwords = record.shader->program->scratchPoolSlotDwords; slotDwords != 0) {
const std::uint64_t groups = argumentImport != nullptr ? std::numeric_limits<std::uint64_t>::max() : static_cast<std::uint64_t>(record.x) * record.y * record.z;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ bool DirectDepthCopy(const Context& context, VkFormat depthFormat, VkImageAspect
void SetDirectDepthCopy(bool enabled);
std::uint64_t DirectDepthCopyCount();
std::uint64_t MultisampledDepthBytes_nid_no_patch(VkExtent2D extent, std::uint32_t bytesPerTexel, std::uint32_t samples);
void RequireNoResidentDepthRead_nid_no_patch(std::uint64_t address, std::size_t bytes, std::optional<std::pair<std::uint64_t, std::size_t>> htileClear = std::nullopt);
void RequireNoResidentDepthRead_nid_no_patch(std::uint64_t address, std::size_t bytes, std::optional<std::pair<std::uint64_t, std::size_t>> htileClear = std::nullopt, bool allowPublication = true);
void AppendHostWrittenDepthRanges(std::uint64_t address, std::size_t bytes, std::vector<std::pair<std::uint64_t, std::uint64_t>>& ranges);
enum class DepthPublicationStage { ReadCaptured, GuestCopied, BeforeCommit, PlaneCommitted, StencilPlaneRead };
void SetDepthPublicationHookForTest(void (*hook)(DepthPublicationStage));
bool HasResidentDepthGuards_nid_no_patch();
void RetireUnmappedDepth_nid_no_patch(std::uintptr_t address, std::size_t bytes);
void RequireNoNativeDepthFeedback_nid_no_patch(const Context& context, const DepthTarget& target, std::span<const std::shared_ptr<Texture>> sampled);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <span>
Expand Down Expand Up @@ -71,6 +72,7 @@ struct MultisampleColorAlias {
};

std::optional<MultisampleColorAlias> MultisampleColorTargetOver(std::uint64_t address, std::size_t bytes);
void WithMultisampleTargetsLockForTest(const std::function<void()>& inside);

std::uint64_t MultisampledColorBytes(const ColorTarget& target);
std::size_t MultisampledDccBytes(const ColorTarget& target);
Expand Down Expand Up @@ -105,6 +107,7 @@ void InstallResidentImageReadGuard_nid_no_patch();
void UpdateResidentImageUnmapObserver_nid_no_patch();
void ClaimMultisampleTargetMemory(std::uint64_t address, std::uint64_t bytes);
void GuardOneFragmentFmask(const Context& context, const ColorTarget& target);
bool HoldsMultisampleTargetsLock();
void RequireNoMultisampleTarget(std::uint64_t address, std::size_t bytes, const char* access);
void RequireNoMultisampleColorTarget(std::uint64_t address, std::size_t bytes, const char* access);
std::vector<std::pair<std::uint64_t, std::uint64_t>> HostOnlySampleRanges(std::uint64_t address, std::size_t bytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class ShaderResources {
VkDescriptorSetLayout FragmentLayout() const { return fragment.layout; }
bool SplitSets() const { return splitSets; }
PipelineSetLayouts SetLayouts() const { return {_layout, fragment.layout, splitSets, layoutKey, fragmentLayoutKey}; }
void RequireGuestBuffers() const { requireGuestBuffers(); }
void Bind(VkCommandBuffer commands, VkPipelineBindPoint bindPoint, VkPipelineLayout layout) const;
struct DrawBindings {
DrawBindings() = default;
Expand Down
Loading
Loading