Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -939,15 +939,17 @@ const HostImport* importRunAt(const Context& context, HostImports& state, Import
if (sameRun(failed->second, run)) return nullptr;
failures.erase(failed);
}
// The imports this one replaces overlap it; none may be held by a build, and the import must fit the budget (with
// what may be evicted) before any of them is retired.
auto first = state.imports.lower_bound(base);
if (first != state.imports.begin() && std::prev(first)->second.base + std::prev(first)->second.bytes > base) --first;
std::uint64_t replaced = 0;
// A join may replace imports this build already looked up: it runs before any region confirms an import
// (UploadFinish's pre-pass re-resolves every pointer after it), and a retired import stays alive through the
// hold (KeepThroughHold). Refusing it there would keep the run copied for good.
const auto held = [&](const HostImport& entry) { return !joined && pointerHeld(state, entry); };
const auto held = [&](const HostImport& entry) {
if (joined || !pointerHeld(state, entry)) return false;
const bool expandedWindow = run.ranges.size() == 1 && entry.ranges.size() == 1 && entry.range == run.ranges.front() && base <= entry.base && base + bytes >= entry.base + entry.bytes && bytes > entry.bytes;
return !expandedWindow || Recorder::Active() == nullptr;
};
for (auto it = first; it != state.imports.end() && it->first < base + bytes; ++it) {
if (held(it->second)) return nullptr;
replaced += it->second.bytes;
Expand Down
36 changes: 36 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/HostImportRetireHold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,41 @@ struct ActiveRecorder {
}
};

void WindowPromotionOutlivesTheHold() {
struct RestoreWindow {
~RestoreWindow() { SetHostImportRangeWindow(std::uint64_t{64} << 20u); }
} restore;
SetHostImportRangeWindow(Alignment);
Heap heap;
ActiveRecorder active;
auto& recorder = *active.recorder;
const auto context = mockContext();
VkBuffer retired = VK_NULL_HANDLE;
{
std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex());
const auto* taken = HostImportFor(context, heap.Address(), 4096);
Expect(taken != nullptr && taken->bytes == Alignment, "the first access did not import one window");
if (taken == nullptr) return;
retired = taken->buffer;
const auto* whole = HostImportFor(context, heap.Address(), RangeBytes);
Expect(whole != nullptr && whole->base == heap.Address() && whole->bytes == RangeBytes && whole->buffer != retired, "a held import window could not be promoted to its whole registered range");
if (whole == nullptr) return;
Expect(Live(retired) && taken->buffer == retired && taken->bytes == Alignment, "promotion invalidated the held window or its entry");
recorder.Commands();
recorder.Submit();
recorder.Sync();
Expect(Live(retired), "a sync inside the promoting hold destroyed its old window");
recorder.Commands();
recorder.Submit();
}
{
std::lock_guard gpu(AgcDriver::GuestMemory::GpuMutex());
Expect(Live(retired), "the old window died while its hold's later batch was in flight");
recorder.Sync();
}
Expect(!Live(retired), "the old window survived every batch of the promoting hold");
}

void IdleRetireOutlivesTheHold() {
Heap heap;
ActiveRecorder active;
Expand Down Expand Up @@ -334,6 +369,7 @@ int main() {
setenv("APS5_WRITE_WATCH_IMPORTS", "unwatch", 1);
#endif
try {
WindowPromotionOutlivesTheHold();
IdleRetireOutlivesTheHold();
BusyRetireOutlivesALaterBatch();
} catch (const std::exception& error) {
Expand Down
Loading