mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
Merge pull request #6566 from TheAggie/perf/agc-draw-fewer-allocations
perf(agc): fewer heap allocations per recorded draw
This commit is contained in:
5 files changed
+67
-31
No files matched your search
@@ -323,12 +323,14 @@ public:
|
||||
// the GPU copies out of an import (gpuCopy) notes its read itself when the copy is recorded.
|
||||
// For the recorder's read tracking (ShaderResources::MarkGpuWrites); nothing once committed.
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> InPlaceReads() const;
|
||||
void InPlaceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& out) const;
|
||||
struct SplitReads {
|
||||
std::shared_ptr<const std::vector<std::pair<std::uint64_t, std::uint64_t>>> space;
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> own;
|
||||
};
|
||||
std::optional<SplitReads> SplitInPlaceReads() const;
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> DeviceReads() const;
|
||||
void DeviceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& out) const;
|
||||
|
||||
private:
|
||||
struct Region {
|
||||
|
||||
@@ -234,8 +234,10 @@ public:
|
||||
// ranges, the regions read in place, and the storage images with whether each is written.
|
||||
const std::vector<std::pair<std::uint64_t, std::uint64_t>>& GpuWrites() const { return guestMemory.Writes(); }
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> InPlaceReads() const { return guestMemory.InPlaceReads(); }
|
||||
void InPlaceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& out) const { guestMemory.InPlaceReads(out); }
|
||||
std::vector<std::pair<VkImage, bool>> StorageImages() const;
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> DeviceReads() const { return guestMemory.DeviceReads(); }
|
||||
void DeviceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& out) const { guestMemory.DeviceReads(out); }
|
||||
bool ReadsImage(const StorageTexture* image) const;
|
||||
bool HasCachedStorageImages() const;
|
||||
bool HoldsReleasedTexture() const;
|
||||
@@ -257,6 +259,7 @@ public:
|
||||
// (their count and size remain): a compute template then serves dispatches whose constants
|
||||
// differ, and the hit refreshes its data buffers with the dispatch's words (RefreshData).
|
||||
static std::vector<std::uint32_t> ContentKey(const CompiledShader& shader, bool dataWords = true, bool movableBuffers = false);
|
||||
static void AppendContentKey(const CompiledShader& shader, bool dataWords, bool movableBuffers, std::vector<std::uint32_t>& key);
|
||||
static bool DiffContentKeys(std::span<const std::uint32_t> previous, std::span<const std::uint32_t> key, bool draw, ContentKeyDiff& diff);
|
||||
// Records the shader's ShaderData and FlattenedSrt words into this object's data buffers
|
||||
// (vkCmdUpdateBuffer, a transfer write the caller's pre-dispatch barrier makes visible; a
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/GuestAllocations.hpp"
|
||||
#include "Optimization/include/Optimization/ShaderStageInputInfo.hpp"
|
||||
#include "ThreadOwned.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
@@ -764,7 +765,9 @@ bool MovableBuffers() {
|
||||
}
|
||||
|
||||
ResourceCache::Key DrawResourceKey(const Context& context, std::span<const CompiledShader> shaders, const ColorTarget& target, std::uint32_t colorAttachments, std::uint64_t indexAddress, std::uint64_t indexBytes, bool ranges) {
|
||||
ResourceCache::Key key{0xffffffffu};
|
||||
ResourceCache::Key key;
|
||||
key.reserve(256);
|
||||
key.push_back(0xffffffffu);
|
||||
const auto append64 = [&](std::uint64_t value) {
|
||||
key.push_back(static_cast<std::uint32_t>(value));
|
||||
key.push_back(static_cast<std::uint32_t>(value >> 32u));
|
||||
@@ -772,9 +775,10 @@ ResourceCache::Key DrawResourceKey(const Context& context, std::span<const Compi
|
||||
append64(reinterpret_cast<std::uint64_t>(context.device));
|
||||
key.push_back(static_cast<std::uint32_t>(shaders.size()));
|
||||
for (const auto& shader : shaders) {
|
||||
const auto part = ShaderResources::ContentKey(shader, true, MovableBuffers());
|
||||
key.push_back(static_cast<std::uint32_t>(part.size()));
|
||||
key.insert(key.end(), part.begin(), part.end());
|
||||
const auto at = key.size();
|
||||
key.push_back(0);
|
||||
ShaderResources::AppendContentKey(shader, true, MovableBuffers(), key);
|
||||
key[at] = static_cast<std::uint32_t>(key.size() - at - 1);
|
||||
}
|
||||
key.push_back(colorAttachments);
|
||||
if (ranges) {
|
||||
@@ -1911,7 +1915,10 @@ void recordDraw(const Context& context, const State& state, const Pm4::DrawParam
|
||||
const bool capture = CaptureInputsEnabled();
|
||||
const bool meshIndirect = state.stages.mesh && args != nullptr;
|
||||
const bool addressBased = resources.HoldsLease();
|
||||
auto passReads = addressBased ? std::vector<std::pair<std::uint64_t, std::uint64_t>>{} : resources.DeviceReads();
|
||||
thread_local std::vector<std::pair<std::uint64_t, std::uint64_t>>* passReadsSlot = nullptr;
|
||||
auto& passReads = ShaderRecompiler::ThreadOwned(passReadsSlot);
|
||||
passReads.clear();
|
||||
if (!addressBased) resources.DeviceReads(passReads);
|
||||
if (gpuIndirect) {
|
||||
passReads.emplace_back(args->arguments, args->arguments + args->RangeBytes());
|
||||
if (args->countIndirect) passReads.emplace_back(args->countAddress, args->countAddress + 4);
|
||||
|
||||
@@ -4965,9 +4965,9 @@ std::uint64_t GuestBufferMemory::inPlaceEnd(const Region& region) {
|
||||
return region.direct != nullptr && region.runCut ? std::min(region.end, region.direct->base + region.direct->bytes) : region.end;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::InPlaceReads() const {
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> result;
|
||||
if (!uploaded || committed) return result;
|
||||
void GuestBufferMemory::InPlaceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& result) const {
|
||||
result.clear();
|
||||
if (!uploaded || committed) return;
|
||||
if (space != nullptr) {
|
||||
for (const auto& region : space->base) {
|
||||
if (region.direct != nullptr) result.emplace_back(region.begin, region.end);
|
||||
@@ -4976,6 +4976,11 @@ std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::InPlaceR
|
||||
for (const auto& region : regions) {
|
||||
if (region.direct != nullptr) result.emplace_back(region.begin, inPlaceEnd(region));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::InPlaceReads() const {
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> result;
|
||||
InPlaceReads(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4988,9 +4993,9 @@ std::optional<GuestBufferMemory::SplitReads> GuestBufferMemory::SplitInPlaceRead
|
||||
return split;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::DeviceReads() const {
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> result;
|
||||
if (!uploaded || committed) return result;
|
||||
void GuestBufferMemory::DeviceReads(std::vector<std::pair<std::uint64_t, std::uint64_t>>& result) const {
|
||||
result.clear();
|
||||
if (!uploaded || committed) return;
|
||||
if (space != nullptr) {
|
||||
for (const auto& region : space->base) {
|
||||
if (region.direct != nullptr || region.mirror != nullptr) result.emplace_back(region.begin, region.end);
|
||||
@@ -4999,6 +5004,11 @@ std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::DeviceRe
|
||||
for (const auto& region : regions) {
|
||||
if (region.direct != nullptr || region.mirror != nullptr) result.emplace_back(region.begin, region.end);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> GuestBufferMemory::DeviceReads() const {
|
||||
std::vector<std::pair<std::uint64_t, std::uint64_t>> result;
|
||||
DeviceReads(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -2468,9 +2468,14 @@ void VisitContentKey(const CompiledShader& shader, bool dataWords, bool movableB
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> ShaderResources::ContentKey(const CompiledShader& shader, bool dataWords, bool movableBuffers) {
|
||||
Require(shader.program != nullptr, "missing compiled shader");
|
||||
std::vector<std::uint32_t> key;
|
||||
std::size_t words = 5;
|
||||
AppendContentKey(shader, dataWords, movableBuffers, key);
|
||||
return key;
|
||||
}
|
||||
|
||||
void ShaderResources::AppendContentKey(const CompiledShader& shader, bool dataWords, bool movableBuffers, std::vector<std::uint32_t>& key) {
|
||||
Require(shader.program != nullptr, "missing compiled shader");
|
||||
std::size_t words = key.size() + 5;
|
||||
const auto addWords = [&](std::size_t count) {
|
||||
if (count > key.max_size() - words) throw std::length_error("resource content key exceeds vector limits");
|
||||
words += count;
|
||||
@@ -2484,7 +2489,6 @@ std::vector<std::uint32_t> ShaderResources::ContentKey(const CompiledShader& sha
|
||||
}
|
||||
key.reserve(words);
|
||||
VisitContentKey(shader, dataWords, movableBuffers, [&](std::uint32_t word) { key.push_back(word); });
|
||||
return key;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -5144,7 +5148,9 @@ std::size_t ShaderResources::DrawUploadLimit() {
|
||||
|
||||
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();
|
||||
thread_local std::vector<std::pair<std::uint64_t, std::uint64_t>>* readsSlot = nullptr;
|
||||
auto& reads = ShaderRecompiler::ThreadOwned(readsSlot);
|
||||
guestMemory.InPlaceReads(reads);
|
||||
const auto maxSnapshotBytes = maxDrawSnapshotBytes;
|
||||
const auto uploadLimit = DrawUploadLimit();
|
||||
std::size_t totalSnapshotBytes = 0;
|
||||
@@ -5236,35 +5242,43 @@ std::shared_ptr<ShaderResources::DrawBindings> ShaderResources::PrepareDrawBindi
|
||||
}
|
||||
if (selected.empty()) return {};
|
||||
Require(context.descriptorCache != nullptr, "draw snapshots require a descriptor cache");
|
||||
std::array<std::vector<VkDescriptorSetLayoutBinding>, 2> descriptions;
|
||||
std::array<std::map<VkDescriptorType, std::uint32_t>, 2> counts;
|
||||
std::array<bool, 2> described{};
|
||||
thread_local std::array<std::vector<VkDescriptorPoolSize>, 2>* countsSlot = nullptr;
|
||||
auto& counts = ShaderRecompiler::ThreadOwned(countsSlot);
|
||||
for (auto& sizes : counts) sizes.clear();
|
||||
for (const auto& binding : bindings) {
|
||||
const auto set = setOf(binding);
|
||||
descriptions[set].push_back(binding.layout);
|
||||
counts[set][binding.layout.descriptorType] += (binding.flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) != 0 ? std::max(1u, static_cast<std::uint32_t>(binding.imageAllocations.size())) : binding.layout.descriptorCount;
|
||||
described[set] = true;
|
||||
const auto count = (binding.flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) != 0 ? std::max(1u, static_cast<std::uint32_t>(binding.imageAllocations.size())) : binding.layout.descriptorCount;
|
||||
if (count == 0) continue;
|
||||
auto& sizes = counts[set];
|
||||
const auto held = std::find_if(sizes.begin(), sizes.end(), [&](const VkDescriptorPoolSize& entry) { return entry.type == binding.layout.descriptorType; });
|
||||
if (held != sizes.end()) held->descriptorCount += count;
|
||||
else sizes.push_back({binding.layout.descriptorType, count});
|
||||
}
|
||||
const auto sizesOf = [&](std::size_t set) {
|
||||
std::vector<VkDescriptorPoolSize> sizes;
|
||||
for (const auto& [type, count] : counts[set]) {
|
||||
if (count != 0) sizes.push_back({type, count});
|
||||
}
|
||||
return sizes;
|
||||
};
|
||||
for (auto& sizes : counts) std::sort(sizes.begin(), sizes.end(), [](const VkDescriptorPoolSize& a, const VkDescriptorPoolSize& b) { return a.type < b.type; });
|
||||
const auto sizesOf = [&](std::size_t set) { return std::span<const VkDescriptorPoolSize>(counts[set]); };
|
||||
const auto variableOf = [&](std::size_t set) { return tableBindings[set] != std::numeric_limits<std::size_t>::max() ? std::optional<std::uint32_t>(std::max(1u, static_cast<std::uint32_t>(bindings[tableBindings[set]].imageAllocations.size()))) : std::nullopt; };
|
||||
result->cache = context.descriptorCache;
|
||||
if (!descriptions[0].empty() || splitSets) {
|
||||
if (described[0] || splitSets) {
|
||||
result->allocation = result->cache->AllocateRewritten(_layout, sizesOf(0), updateAfterBind, variableOf(0));
|
||||
result->layout = _layout;
|
||||
Require(result->allocation.set != VK_NULL_HANDLE, "draw snapshot descriptor allocation failed");
|
||||
}
|
||||
if (!descriptions[1].empty() || splitSets) {
|
||||
if (described[1] || splitSets) {
|
||||
result->fragmentAllocation = result->cache->AllocateRewritten(fragment.layout, sizesOf(1), updateAfterBind, variableOf(1));
|
||||
result->fragmentLayout = fragment.layout;
|
||||
Require(result->fragmentAllocation.set != VK_NULL_HANDLE, "draw snapshot descriptor allocation failed");
|
||||
}
|
||||
auto buffers = descriptorBuffers;
|
||||
auto writes = descriptorWrites;
|
||||
std::vector<std::size_t> snapshotIndices(allocations.size(), selected.size());
|
||||
thread_local std::vector<VkDescriptorBufferInfo>* buffersSlot = nullptr;
|
||||
auto& buffers = ShaderRecompiler::ThreadOwned(buffersSlot);
|
||||
buffers = descriptorBuffers;
|
||||
thread_local std::vector<VkWriteDescriptorSet>* writesSlot = nullptr;
|
||||
auto& writes = ShaderRecompiler::ThreadOwned(writesSlot);
|
||||
writes = descriptorWrites;
|
||||
thread_local std::vector<std::size_t>* indicesSlot = nullptr;
|
||||
auto& snapshotIndices = ShaderRecompiler::ThreadOwned(indicesSlot);
|
||||
snapshotIndices.assign(allocations.size(), selected.size());
|
||||
for (std::size_t index = 0; index < selected.size(); ++index) snapshotIndices[selected[index]] = index;
|
||||
auto nextWrite = writes.begin();
|
||||
for (const auto& binding : bindings) {
|
||||
|
||||
Reference in new issue
Block a user