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
7 changes: 7 additions & 0 deletions core/libs/prx/libSceAgcDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,13 @@ if(BUILD_TESTING)
set_tests_properties(agc_import_watch_other_family PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_import_settle_deferred COMMAND agc_driver_recorder_tests --deferred-settle-only)
add_test(NAME agc_import_settle_deferred_other_family COMMAND agc_driver_recorder_tests --deferred-settle-other-family-only)
add_test(NAME agc_import_settle_write_order COMMAND agc_driver_recorder_tests --settle-write-order-only)
add_test(NAME agc_import_settle_write_order_other_family COMMAND agc_driver_recorder_tests --settle-write-order-other-family-only)
add_test(NAME agc_import_settle_wait_failure COMMAND agc_driver_recorder_tests --settle-wait-failure-only)
set_tests_properties(agc_import_settle_write_order agc_import_settle_write_order_other_family agc_import_settle_wait_failure PROPERTIES
SKIP_RETURN_CODE 77 TIMEOUT 30
FAIL_REGULAR_EXPRESSION "SYNC-HAZARD-|VUID-"
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
add_test(NAME agc_import_settle_first_use COMMAND agc_driver_recorder_tests --settle-first-use-only)
add_test(NAME agc_import_settle_earlier_import COMMAND agc_driver_recorder_tests --settle-earlier-import-only)
add_test(NAME agc_import_settle_retire COMMAND agc_driver_recorder_tests --settle-retire-only)
Expand Down
5 changes: 5 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/include/BufferPool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class BufferPool {
std::size_t RetainedImages();
std::optional<BufferAllocation> Take(std::size_t bytes, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
void Put(const BufferAllocation& allocation) noexcept;
VkDeviceSize ReclaimIdleDevice(VkDeviceSize bytes, std::chrono::steady_clock::time_point now);
static bool SlabEligible(std::size_t capacity, VkDeviceSize alignment, VkDeviceSize size, VkDeviceSize atom);
static VkDeviceSize SlabBlockBytes(std::size_t capacity);
SlabSlot TakeSlot(const Context& context, std::uint32_t memoryType, std::size_t capacity, bool addressable);
Expand Down Expand Up @@ -168,6 +169,10 @@ class BufferPool {
std::uint64_t imageEvictions = 0;
std::list<Slot*> imageOrder;
std::uint64_t clock = 0;
std::optional<std::chrono::steady_clock::time_point> idleRead;
std::uint64_t idleClock = 0;
std::uint64_t reclaimBefore = 0;
std::uint64_t scannedIdleClock = 0;
AgcDriver::Mutex slabMutex;
std::unordered_map<std::uint64_t, Slab> slabs;
std::unordered_map<VkDeviceMemory, std::unique_ptr<SlabBlock>> slabBlocks;
Expand Down
50 changes: 50 additions & 0 deletions core/libs/prx/libSceAgcDriver/Graphics/src/BufferPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,56 @@ bool BufferPool::evictOldestImage(std::vector<BufferAllocation>& evicted) {
return true;
}

VkDeviceSize BufferPool::ReclaimIdleDevice(VkDeviceSize bytes, std::chrono::steady_clock::time_point now) {
if (bytes == 0) return 0;
const auto heaps = std::min<std::uint32_t>(memoryProperties.memoryHeapCount, VK_MAX_MEMORY_HEAPS);
if (std::count_if(memoryProperties.memoryHeaps, memoryProperties.memoryHeaps + heaps, [](const auto& heap) { return (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0; }) != 1) return 0;
std::vector<BufferAllocation> evicted;
{
std::lock_guard lock(mutex);
if (!idleRead) {
idleRead = now;
idleClock = clock;
return 0;
}
if (now < *idleRead) return 0;
if (now - *idleRead >= std::chrono::seconds(1)) {
reclaimBefore = idleClock;
idleClock = clock;
idleRead = now;
}
if (reclaimBefore <= scannedIdleClock) return 0;
VkDeviceSize selected = 0;
while (selected < bytes) {
auto oldest = deviceTier.free.end();
for (auto it = deviceTier.free.begin(); it != deviceTier.free.end(); ++it) {
if (it->second.empty()) continue;
const auto& slot = it->second.front();
if (slot.allocation.buffer == VK_NULL_HANDLE || slot.allocation.slab || slot.lastUse > reclaimBefore) continue;
if (oldest == deviceTier.free.end() || slot.lastUse < oldest->second.front().lastUse) oldest = it;
}
if (oldest == deviceTier.free.end()) {
scannedIdleClock = reclaimBefore;
break;
}
if (evicted.empty()) evicted.reserve(deviceTier.slots);
const auto allocation = oldest->second.front().allocation;
evicted.push_back(allocation);
selected += allocation.allocationBytes;
deviceTier.retainedBytes -= allocation.allocationBytes;
oldest->second.pop_front();
if (oldest->second.empty()) deviceTier.free.erase(oldest);
--deviceTier.slots;
++deviceTier.evictions;
}
}
VkDeviceSize released = 0;
for (const auto& allocation : evicted) released += destroy(allocation);
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
if (profile && released != 0) AgcDriver::ProfilePrint_nid_no_patch("[bufferpool] idle device reclaim: %llu bytes released for %llu bytes of storage-cache pressure\n", static_cast<unsigned long long>(released), static_cast<unsigned long long>(bytes));
return released;
}

std::size_t BufferPool::MaxSlots() {
// APS5_BUFFER_POOL_SLOTS=<n> bounds the retained allocations; 64 is the capacity the pool had
// before least-recently-used retention, for A/B runs.
Expand Down
33 changes: 6 additions & 27 deletions core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ HostImports& Imports() {

#ifdef _WIN32
void waitSettleBatch(const ImportSettle& settle, VkDevice device, const SettleBatch& batch) {
if (batch.busy && settle.waitForFences != nullptr) static_cast<void>(settle.waitForFences(device, 1, &batch.fence, VK_TRUE, 10'000'000'000ull));
if (batch.busy && settle.waitForFences != nullptr) Check(settle.waitForFences(device, 1, &batch.fence, VK_TRUE, 60'000'000'000ull), "vkWaitForFences (settle batch)");
}

bool settleBatchRunning(const ImportSettle& settle, VkDevice device, const SettleBatch& batch) {
Expand Down Expand Up @@ -987,7 +987,7 @@ const char* prepareSettleBatch(const Context& context, ImportSettle& settle, Set
}
if (batch.commands == VK_NULL_HANDLE) {
VkCommandBufferAllocateInfo allocate{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
allocate.commandPool = context.settlePool;
allocate.commandPool = context.pool;
allocate.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocate.commandBufferCount = 1;
if ((result = context.Function<PFN_vkAllocateCommandBuffers>("vkAllocateCommandBuffers")(context.device, &allocate, &batch.commands)) != VK_SUCCESS) {
Expand Down Expand Up @@ -1037,9 +1037,7 @@ void submitSettleBatch(const Context& context, HostImports& state) {
begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if ((result = context.Function<PFN_vkBeginCommandBuffer>("vkBeginCommandBuffer")(batch.commands, &begin)) != VK_SUCCESS) return fail("vkBeginCommandBuffer (settle batch)");
const auto copy = context.Resolved(&DeviceFunctions::cmdCopyBuffer, "vkCmdCopyBuffer");
const bool otherFamily = context.settleFamily != context.queueFamily;
const auto createBuffer = otherFamily ? context.Function<PFN_vkCreateBuffer>("vkCreateBuffer") : nullptr;
const auto bindBufferMemory = otherFamily ? context.Function<PFN_vkBindBufferMemory>("vkBindBufferMemory") : nullptr;
RecordMemoryBarrier(context, batch.commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
std::size_t consumed = 0;
for (; consumed < state.settleWanted.size() && batch.bases.size() < settleBatchCopies; ++consumed) {
const auto base = state.settleWanted[consumed];
Expand All @@ -1050,38 +1048,19 @@ void submitSettleBatch(const Context& context, HostImports& state) {
state.pendingSettles.erase(settle);
continue;
}
VkBuffer source = import->second.buffer;
if (otherFamily) {
VkExternalMemoryBufferCreateInfo external{VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT;
VkBufferCreateInfo aliasInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, &external};
aliasInfo.size = std::min<VkDeviceSize>(import->second.bytes, 65536);
aliasInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VkBuffer alias = VK_NULL_HANDLE;
if (createBuffer(context.device, &aliasInfo, nullptr, &alias) != VK_SUCCESS) {
state.pendingSettles.erase(settle);
continue;
}
if (bindBufferMemory(context.device, alias, import->second.memory, 0) != VK_SUCCESS) {
destroyBuffer(context.device, alias, nullptr);
state.pendingSettles.erase(settle);
continue;
}
batch.aliases.push_back(alias);
source = alias;
}
const VkBufferCopy region{0, (VkDeviceSize{index} * settleBatchCopies + batch.bases.size()) * 4u, 4};
copy(batch.commands, source, deviceSettle.batchTarget, 1, &region);
copy(batch.commands, import->second.buffer, deviceSettle.batchTarget, 1, &region);
settle->second = PendingSettle{SettlePhase::Submitted, index, context.device};
batch.bases.push_back(base);
}
state.settleWanted.erase(state.settleWanted.begin(), state.settleWanted.begin() + static_cast<std::ptrdiff_t>(consumed));
RecordMemoryBarrier(context, batch.commands, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_WRITE_BIT);
if ((result = context.Function<PFN_vkEndCommandBuffer>("vkEndCommandBuffer")(batch.commands)) != VK_SUCCESS) return fail("vkEndCommandBuffer (settle batch)");
if (batch.bases.empty()) return;
VkSubmitInfo submit{VK_STRUCTURE_TYPE_SUBMIT_INFO};
submit.commandBufferCount = 1;
submit.pCommandBuffers = &batch.commands;
if ((result = context.Function<PFN_vkQueueSubmit>("vkQueueSubmit")(context.settleQueue, 1, &submit, batch.fence)) != VK_SUCCESS) return fail("vkQueueSubmit (settle batch)");
if ((result = context.Function<PFN_vkQueueSubmit>("vkQueueSubmit")(context.queue, 1, &submit, batch.fence)) != VK_SUCCESS) return fail("vkQueueSubmit (settle batch)");
batch.busy = true;
state.settlesSubmitted += batch.bases.size();
}
Expand Down
30 changes: 20 additions & 10 deletions core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/PlaceholderImages.hpp"
#include "prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp"
#include "prx/libSceAgcDriver/Graphics/include/BufferPool.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Pipeline.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
Expand Down Expand Up @@ -572,7 +573,7 @@ std::uint64_t forcedCacheBudget() {
return forced;
}

std::uint64_t storageBudget(const Context& context) {
std::uint64_t storageBudget(const Context& context, std::uint64_t required = 0) {
const auto forced = forcedCacheBudget();
if (forced != 0) return forced;
if (context.memoryProperties2 == nullptr || context.physical == VK_NULL_HANDLE) return TextureCacheBudget(context.memory);
Expand All @@ -581,17 +582,26 @@ std::uint64_t storageBudget(const Context& context) {
VkPhysicalDevice device{VK_NULL_HANDLE};
std::chrono::steady_clock::time_point read{};
std::uint64_t budget{0};
std::uint64_t ceiling{0};
} cached;
const auto now = std::chrono::steady_clock::now();
std::lock_guard lock(cached.mutex);
if (cached.device == context.physical && now - cached.read < std::chrono::seconds(1)) return cached.budget;
VkPhysicalDeviceMemoryBudgetPropertiesEXT reported{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT};
VkPhysicalDeviceMemoryProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2};
properties.pNext = &reported;
context.memoryProperties2(context.physical, &properties);
cached.device = context.physical;
cached.read = now;
cached.budget = StorageTextureBudget(context.memory, &reported, SampledTextureMemory(), storageCacheBytes());
const auto read = [&] {
VkPhysicalDeviceMemoryBudgetPropertiesEXT reported{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT};
VkPhysicalDeviceMemoryProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2};
properties.pNext = &reported;
context.memoryProperties2(context.physical, &properties);
cached.device = context.physical;
cached.read = now;
cached.budget = StorageTextureBudget(context.memory, &reported, SampledTextureMemory(), storageCacheBytes());
cached.ceiling = cached.budget;
if (const auto heap = largestDeviceLocalHeap(context.memory); heap && reported.heapBudget[*heap] != 0) {
cached.ceiling = std::max<std::uint64_t>(512ull << 20u, std::min<std::uint64_t>(TextureCacheBudget(context.memory), reported.heapBudget[*heap] / 4u));
}
};
if (cached.device != context.physical || now - cached.read >= std::chrono::seconds(1)) read();
const auto needed = std::min(required, cached.ceiling);
if (needed > cached.budget && context.bufferPool != nullptr && context.bufferPool->ReclaimIdleDevice(needed - cached.budget, now) != 0) read();
return cached.budget;
}

Expand Down Expand Up @@ -1182,7 +1192,7 @@ std::shared_ptr<StorageTexture> cachedStorageTexture(const Context& context, std
static const bool keepNew = std::getenv("APS5_NO_KEEP_NEW_STORAGE") == nullptr;
if (auto* recorder = Recorder::Active(); keepNew && recorder != nullptr && GuestMemory::GpuMutex().HeldByThisThread() && recorder->Recording()) recorder->Keep(entry.texture);
entry.accounted = heldBytes(*entry.texture);
const auto budget = storageBudget(context);
const auto budget = storageBudget(context, cache.bytes + entry.accounted);
while (!cache.entries.empty() && cache.bytes + entry.accounted > budget) evicted.push_back(evictStorage(cache, std::prev(cache.entries.end())));
cache.bytes += entry.accounted;
texture = entry.texture;
Expand Down
Loading
Loading