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
14 changes: 12 additions & 2 deletions core/libs/prx/libSceAgcDriver/Graphics/include/Recorder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,17 @@ class Recorder {
static constexpr std::size_t UnwrittenSnapshotEntries = 4096;
static constexpr std::size_t DrawInputBudget = std::size_t{1024} << 20u;
static constexpr std::size_t DrawInputEntries = 16384;
std::shared_ptr<Buffer> UnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp);
void NoteUnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, std::shared_ptr<Buffer> buffer);
std::shared_ptr<Buffer> UnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, VkDeviceSize* offset = nullptr);
void NoteUnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, std::shared_ptr<Buffer> buffer, VkDeviceSize offset = 0);
std::shared_ptr<Buffer> ReusableDrawSnapshot(std::uint64_t address, std::size_t bytes, SnapshotUse use = SnapshotUse::Storage, std::uint32_t* derived = nullptr, std::uint64_t generation = 0);
void KeepDrawSnapshot(std::uint64_t address, std::size_t bytes, std::uint64_t generation, std::uint64_t registryGeneration, std::shared_ptr<Buffer> buffer, SnapshotUse use = SnapshotUse::Storage, std::uint32_t derived = 0);
static constexpr std::size_t DrawUploadChunk = std::size_t{1} << 20u;
struct UploadSlice {
std::shared_ptr<Buffer> buffer;
VkDeviceSize offset = 0;
std::byte* bytes = nullptr;
};
UploadSlice DrawUpload(std::size_t bytes);
static constexpr std::size_t IndexRangeEntries = 16384;
static constexpr std::size_t KeptIndexRangeIndices = 4096;
bool KnownHighestIndex(std::uint64_t address, std::size_t bytes, SnapshotUse use, std::optional<std::uint32_t>& highest);
Expand Down Expand Up @@ -812,6 +819,8 @@ class Recorder {
std::uint64_t submissions = 0;
std::size_t inFlightKeptBytes = 0;
std::uint64_t writeNoteCount = 0;
std::shared_ptr<Buffer> drawUpload;
VkDeviceSize drawUploadUsed = 0;
// Command buffers and fences of completed batches, reused by later ones (hundreds of batches per
// frame would otherwise allocate and free their objects each time).
std::vector<std::pair<VkCommandBuffer, VkFence>> spare;
Expand All @@ -836,6 +845,7 @@ class Recorder {
struct UnwrittenSnapshot {
std::size_t bytes;
std::shared_ptr<Buffer> buffer;
VkDeviceSize offset = 0;
};
std::unordered_map<std::uint64_t, UnwrittenSnapshot> unwrittenSnapshots;
std::array<std::uint64_t, 3> unwrittenStamp{};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ class ShaderResources {
struct Snapshot {
std::uint64_t address;
std::shared_ptr<Buffer> buffer;
VkDeviceSize offset = 0;
std::size_t bytes = 0;
std::span<std::byte> Bytes() const { return buffer->Bytes().subspan(static_cast<std::size_t>(offset), bytes); }
};
DescriptorCache* cache = nullptr;
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
Expand All @@ -191,6 +194,7 @@ class ShaderResources {
static const char* MoveRefusalName(MoveRefusal refusal);
std::optional<std::vector<MovedBuffer>> MovedDispatchBuffers(const CompiledShader& shader, Recorder* recorder, std::size_t maxBytes, MoveProof& proof) const;
static std::size_t MaxDrawSnapshotBytes();
static std::size_t DrawUploadLimit();
void WriteBack();
// Deferred completion: MarkGpuWrites registers the results the recorded work leaves on the GPU
// (storage images stay there; buffer ranges are noted so CPU reads wait); WriteBackBuffers runs
Expand Down
6 changes: 3 additions & 3 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1707,15 +1707,15 @@ void captureInputs(const Context& context, Recorder& recorder, VkCommandBuffer c
};
if (bindings != nullptr) {
for (const auto& snapshot : bindings->snapshots) {
const auto bytes = snapshot.buffer->Bytes();
addSample(snapshot.address, bytes.size(), snapshot.buffer->Handle(), 0, bytes.data());
const auto bytes = snapshot.Bytes();
addSample(snapshot.address, bytes.size(), snapshot.buffer->Handle(), snapshot.offset, bytes.data());
}
}
for (const auto& [begin, end] : resources.InPlaceReads()) {
Require(end >= begin, "invalid capture input range");
const auto bytes = static_cast<std::size_t>(end - begin);
if (bytes == 0 || bytes > 512) continue;
if (bindings != nullptr && std::any_of(bindings->snapshots.begin(), bindings->snapshots.end(), [&](const auto& snapshot) { return snapshot.address < end && begin < snapshot.address + snapshot.buffer->Bytes().size(); })) continue;
if (bindings != nullptr && std::any_of(bindings->snapshots.begin(), bindings->snapshots.end(), [&](const auto& snapshot) { return snapshot.address < end && begin < snapshot.address + snapshot.bytes; })) continue;
if (resources.WritesOverlap(begin, bytes) || recorder.PendingWriteOverlaps(begin, bytes)) {
CaptureTrace::Log("input-skip draw=%llu batch=%llu address=%llx bytes=%zu reason=gpu-writer", draw, batch, static_cast<unsigned long long>(begin), bytes);
continue;
Expand Down
18 changes: 15 additions & 3 deletions core/libs/prx/libSceAgcDriver/Graphics/src/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2542,28 +2542,40 @@ void Recorder::eraseDrawSnapshot(std::map<DrawSnapshotKey, DrawSnapshot>::iterat
drawSnapshots.erase(entry);
}

Recorder::UploadSlice Recorder::DrawUpload(std::size_t bytes) {
const VkDeviceSize alignment = std::max<VkDeviceSize>(context.limits.minStorageBufferOffsetAlignment, 16);
auto offset = (drawUploadUsed + alignment - 1) & ~(alignment - 1);
if (drawUpload == nullptr || offset + bytes > drawUpload->Bytes().size()) {
drawUpload = std::make_shared<Buffer>(context, std::max(DrawUploadChunk, bytes), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
offset = 0;
}
drawUploadUsed = offset + bytes;
return {drawUpload, offset, drawUpload->Bytes().data() + offset};
}

void Recorder::retireUnwrittenSnapshots(const std::array<std::uint64_t, 3>& stamp) {
if (stamp == unwrittenStamp) return;
unwrittenSnapshots.clear();
unwrittenBytes = 0;
unwrittenStamp = stamp;
}

std::shared_ptr<Buffer> Recorder::UnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp) {
std::shared_ptr<Buffer> Recorder::UnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, VkDeviceSize* offset) {
retireUnwrittenSnapshots(stamp);
const auto found = unwrittenSnapshots.find(address);
if (found == unwrittenSnapshots.end() || found->second.bytes != bytes) return {};
if (offset != nullptr) *offset = found->second.offset;
return found->second.buffer;
}

void Recorder::NoteUnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, std::shared_ptr<Buffer> buffer) {
void Recorder::NoteUnwrittenDrawSnapshot(std::uint64_t address, std::size_t bytes, const std::array<std::uint64_t, 3>& stamp, std::shared_ptr<Buffer> buffer, VkDeviceSize offset) {
retireUnwrittenSnapshots(stamp);
if (const auto found = unwrittenSnapshots.find(address); found != unwrittenSnapshots.end()) {
unwrittenBytes -= found->second.bytes;
unwrittenSnapshots.erase(found);
}
if (unwrittenSnapshots.size() >= UnwrittenSnapshotEntries || bytes > DrawSnapshotBudget - unwrittenBytes) return;
unwrittenSnapshots.emplace(address, UnwrittenSnapshot{bytes, std::move(buffer)});
unwrittenSnapshots.emplace(address, UnwrittenSnapshot{bytes, std::move(buffer), offset});
unwrittenBytes += bytes;
}

Expand Down
45 changes: 32 additions & 13 deletions core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4950,10 +4950,19 @@ std::size_t ShaderResources::MaxDrawSnapshotBytes() {
return static_cast<std::size_t>(mib * 1024ull * 1024ull);
}

std::size_t ShaderResources::DrawUploadLimit() {
static const std::size_t limit = [] {
const char* value = std::getenv("APS5_DRAW_UPLOAD_MAX");
return value != nullptr ? static_cast<std::size_t>(std::strtoull(value, nullptr, 10)) : std::size_t{4096};
}();
return limit;
}

std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindings(Recorder& recorder, std::span<const MovedBuffer> moved, bool movedOnly) const {
if ((_set == VK_NULL_HANDLE && fragment.set == VK_NULL_HANDLE) || usesBda) return {};
const auto reads = guestMemory.InPlaceReads();
const auto maxSnapshotBytes = maxDrawSnapshotBytes;
const auto uploadLimit = DrawUploadLimit();
std::size_t totalSnapshotBytes = 0;
auto result = std::make_shared<DrawBindings>();
thread_local std::vector<std::size_t>* selectedSlot = nullptr;
Expand Down Expand Up @@ -4981,7 +4990,7 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
else movedWordBuffers[movedWordBufferNext++ % MovedWordBufferSlots] = std::move(entry);
}
selected.push_back(index);
result->snapshots.push_back({0, std::move(buffer)});
result->snapshots.push_back({0, std::move(buffer), 0, override->size});
continue;
}
std::uint64_t address = item.address;
Expand All @@ -5002,28 +5011,38 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
stamp = {GuestMemory::TrackerGeneration(), GuestMemory::CollectEpoch(), registryGeneration};
stamped = true;
}
auto buffer = recorder.UnwrittenDrawSnapshot(begin, bytes, stamp);
VkDeviceSize offset = 0;
auto buffer = recorder.UnwrittenDrawSnapshot(begin, bytes, stamp, &offset);
static const bool verifySnapshots = std::getenv("APS5_VERIFY_DRAW_SNAPSHOTS") != nullptr;
if (verifySnapshots && buffer != nullptr) {
static std::atomic<std::uint64_t> checked{0}, differing{0};
const auto fast = buffer->Bytes().subspan(static_cast<std::size_t>(offset), bytes);
const auto slow = recorder.ReusableDrawSnapshot(begin, bytes, Recorder::SnapshotUse::Storage, nullptr, GuestMemory::CollectWrites(begin, bytes));
const bool same = slow == buffer || (slow != nullptr && slow->Bytes().size() == buffer->Bytes().size() && std::memcmp(slow->Bytes().data(), buffer->Bytes().data(), buffer->Bytes().size()) == 0) || (slow == nullptr && std::memcmp(buffer->Bytes().data(), reinterpret_cast<const void*>(begin), bytes) == 0);
const bool same = slow == buffer || (slow != nullptr && slow->Bytes().size() == fast.size() && std::memcmp(slow->Bytes().data(), fast.data(), fast.size()) == 0) || (slow == nullptr && std::memcmp(fast.data(), reinterpret_cast<const void*>(begin), bytes) == 0);
if (!same) differing.fetch_add(1);
if (checked.fetch_add(1) % 200000 == 199999) std::fprintf(stderr, "[draw-snapshots] verify: %llu fast answers checked, %llu differing%c", static_cast<unsigned long long>(checked.load()), static_cast<unsigned long long>(differing.load()), 10);
}
if (buffer == nullptr) {
const auto generation = GuestMemory::CollectWrites(begin, bytes);
buffer = recorder.ReusableDrawSnapshot(begin, bytes, Recorder::SnapshotUse::Storage, nullptr, generation);
if (buffer == nullptr) {
buffer = std::make_shared<Buffer>(context, bytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
std::memcpy(buffer->Bytes().data(), reinterpret_cast<const void*>(begin), bytes);
recorder.KeepDrawSnapshot(begin, bytes, generation, registryGeneration, buffer);
const bool uploaded = bytes != 0 && bytes <= uploadLimit;
if (uploaded) {
auto slice = recorder.DrawUpload(bytes);
std::memcpy(slice.bytes, reinterpret_cast<const void*>(begin), bytes);
buffer = std::move(slice.buffer);
offset = slice.offset;
} else {
const auto generation = GuestMemory::CollectWrites(begin, bytes);
buffer = recorder.ReusableDrawSnapshot(begin, bytes, Recorder::SnapshotUse::Storage, nullptr, generation);
if (buffer == nullptr) {
buffer = std::make_shared<Buffer>(context, bytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
std::memcpy(buffer->Bytes().data(), reinterpret_cast<const void*>(begin), bytes);
recorder.KeepDrawSnapshot(begin, bytes, generation, registryGeneration, buffer);
}
}
if (GuestMemory::TrackerGeneration() == stamp[0]) recorder.NoteUnwrittenDrawSnapshot(begin, bytes, stamp, buffer);
else stamped = false;
if (GuestMemory::TrackerGeneration() != stamp[0]) stamped = false;
else if (!uploaded || stamp[1] != 0) recorder.NoteUnwrittenDrawSnapshot(begin, bytes, stamp, buffer, offset);
}
selected.push_back(index);
result->snapshots.push_back({begin, std::move(buffer)});
result->snapshots.push_back({begin, std::move(buffer), offset, bytes});
totalSnapshotBytes += bytes;
CaptureTrace::Log("draw-snapshot batch=%llu address=%llx bytes=%zu", static_cast<unsigned long long>(recorder.Submissions() + 1), static_cast<unsigned long long>(begin), bytes);
}
Expand Down Expand Up @@ -5072,7 +5091,7 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
const auto snapshotIndex = snapshotIndices[binding.allocations[element]];
if (snapshotIndex == selected.size()) continue;
const auto& snapshot = result->snapshots[snapshotIndex];
buffers[offset + element] = {snapshot.buffer->Handle(), 0, snapshot.buffer->Bytes().size()};
buffers[offset + element] = {snapshot.buffer->Handle(), snapshot.offset, snapshot.bytes};
}
}
}
Expand Down
Loading
Loading