Merge pull request #1147 from alfalfa-star/perf/agc-staging-pool-heap-budget

perf(agc): size the device staging pool from the device-local heap
This commit is contained in:
Adrià 2026-10-10 11:28:29 +02:00 committed by GitHub
commit aad13ac11b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 13 deletions

View File

@ -43,14 +43,10 @@ struct BufferAllocation {
// the 256-byte copied-region buffers to fit, and those then missed on every build (81869 misses
// and 80850 evictions per run, each a Vulkan create or destroy under the pool mutex, 3.5 s per
// run). APS5_BUFFER_POOL_SHARED=1 keeps one tier as before.
//
// Device-local allocations (the detiler's scratch buffers and the staging shadows of written guest
// buffers, see GuestBufferMemory) are retained in a third tier with a budget of their own, video
// memory instead of pinned host memory: APS5_STAGING_POOL_MIB (default 512); 0 keeps them in the
// two host tiers as before.
class BufferPool {
public:
explicit BufferPool(const Context& context);
static VkDeviceSize DeviceBudget(const VkPhysicalDeviceMemoryProperties& memory);
~BufferPool();
BufferPool(const BufferPool&) = delete;
BufferPool& operator=(const BufferPool&) = delete;
@ -91,8 +87,7 @@ private:
// The tier a buffer of `capacity` and `properties` is retained in (the large one for everything
// when shared; the device tier for device-local memory while it has a budget).
Tier& tierFor(std::size_t capacity, VkMemoryPropertyFlags properties);
// The device tier's budget (APS5_STAGING_POOL_MIB), read once.
static VkDeviceSize DeviceBudget();
static bool DeviceTierEnabled();
static bool DeviceTiered(VkMemoryPropertyFlags properties);
static std::size_t NextCapacity(std::size_t capacity);
void destroy(const BufferAllocation& allocation) noexcept;

View File

@ -29,7 +29,7 @@ constexpr VkBufferUsageFlags deviceUsage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
BufferPool::BufferPool(const Context& context) : device(context.device), unmap(context.Function<PFN_vkUnmapMemory>("vkUnmapMemory")), destroyBuffer(context.Function<PFN_vkDestroyBuffer>("vkDestroyBuffer")), freeMemory(context.Function<PFN_vkFreeMemory>("vkFreeMemory")), maxSlots(std::max<std::size_t>(1, std::min<std::size_t>(MaxSlots(), context.limits.maxMemoryAllocationCount / 6u))) {
smallTier.budget = smallBudget;
largeTier.budget = budget;
deviceTier.budget = DeviceBudget();
deviceTier.budget = DeviceTierEnabled() ? DeviceBudget(context.memory) : 0;
}
BufferPool::~BufferPool() {
@ -40,12 +40,22 @@ BufferPool::~BufferPool() {
}
}
VkDeviceSize BufferPool::DeviceBudget() {
static const VkDeviceSize deviceBudget = [] {
bool BufferPool::DeviceTierEnabled() {
static const bool enabled = [] {
const char* value = std::getenv("APS5_STAGING_POOL_MIB");
return (value != nullptr ? std::strtoull(value, nullptr, 10) : 512ull) << 20u;
return value == nullptr || std::strtoull(value, nullptr, 10) != 0;
}();
return deviceBudget;
return enabled;
}
VkDeviceSize BufferPool::DeviceBudget(const VkPhysicalDeviceMemoryProperties& memory) {
static const char* value = std::getenv("APS5_STAGING_POOL_MIB");
if (value != nullptr) return static_cast<VkDeviceSize>(std::strtoull(value, nullptr, 10)) << 20u;
VkDeviceSize largest = 0;
for (std::uint32_t heap = 0; heap < memory.memoryHeapCount && heap < VK_MAX_MEMORY_HEAPS; ++heap) {
if ((memory.memoryHeaps[heap].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) largest = std::max(largest, memory.memoryHeaps[heap].size);
}
return std::max<VkDeviceSize>(VkDeviceSize{512} << 20u, largest / 8u);
}
void BufferPool::destroy(const BufferAllocation& allocation) noexcept {
@ -56,7 +66,7 @@ void BufferPool::destroy(const BufferAllocation& allocation) noexcept {
}
bool BufferPool::DeviceTiered(VkMemoryPropertyFlags properties) {
return (properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 && (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0 && DeviceBudget() != 0;
return (properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 && (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0 && DeviceTierEnabled();
}
std::size_t BufferPool::Capacity(std::size_t bytes, VkMemoryPropertyFlags properties) {

View File

@ -6,6 +6,7 @@
#include "prx/libSceAgcDriver/Graphics/include/TextureDetiler.hpp"
#include "prx/libSceAgcDriver/Graphics/include/VertexInput.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "prx/libSceAgcDriver/Graphics/include/BufferPool.hpp"
#include "prx/libSceAgcDriver/Graphics/include/ShaderInputState.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DccMetadata.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
@ -3730,6 +3731,19 @@ void meshIndexBufferTests() {
expectFailure([] { static_cast<void>(AgcDriver::Graphics::MeshIndexBufferDescriptor(AgcDriver::Pm4::DrawParameters{0, 3, 2, 1, 0, true})); }, "invalid mesh index buffer range");
}
void stagingPoolBudgetTests() {
if (std::getenv("APS5_STAGING_POOL_MIB") != nullptr) return;
VkPhysicalDeviceMemoryProperties memory{};
memory.memoryHeapCount = 2;
memory.memoryHeaps[0] = {VkDeviceSize{32} << 30u, 0};
memory.memoryHeaps[1] = {VkDeviceSize{16} << 30u, VK_MEMORY_HEAP_DEVICE_LOCAL_BIT};
Require(AgcDriver::Graphics::BufferPool::DeviceBudget(memory) == VkDeviceSize{2} << 30u, "the staging pool budget is not an eighth of the device-local heap");
memory.memoryHeaps[1].size = VkDeviceSize{2} << 30u;
Require(AgcDriver::Graphics::BufferPool::DeviceBudget(memory) == VkDeviceSize{512} << 20u, "the staging pool budget fell below 512 MiB");
memory.memoryHeapCount = 1;
Require(AgcDriver::Graphics::BufferPool::DeviceBudget(memory) == VkDeviceSize{512} << 20u, "a host heap sized the staging pool budget");
}
void meshArgumentTests() {
using AgcDriver::Graphics::MeshArguments;
using AgcDriver::Graphics::ResolveMeshArguments;
@ -3959,6 +3973,7 @@ int main() {
sampledBudgetReportTests();
meshArgumentTests();
meshIndexBufferTests();
stagingPoolBudgetTests();
validationTests();
vertexCopyTests();
highestDrawIndexTests();