Optimize textures/buffers uploading on Vulkan with page allocator

This commit is contained in:
Wojtek Figat
2025-09-04 21:38:07 +02:00
parent cd22cd059d
commit 831fb0f442
10 changed files with 427 additions and 441 deletions

View File

@@ -972,133 +972,6 @@ void HelperResourcesVulkan::Dispose()
}
}
StagingManagerVulkan::StagingManagerVulkan(GPUDeviceVulkan* device)
: _device(device)
{
}
GPUBuffer* StagingManagerVulkan::AcquireBuffer(uint32 size, GPUResourceUsage usage)
{
// Try reuse free buffer
{
ScopeLock lock(_locker);
for (int32 i = 0; i < _freeBuffers.Count(); i++)
{
auto& freeBuffer = _freeBuffers[i];
if (freeBuffer.Buffer->GetSize() == size && freeBuffer.Buffer->GetDescription().Usage == usage)
{
const auto buffer = freeBuffer.Buffer;
_freeBuffers.RemoveAt(i);
return buffer;
}
}
}
// Allocate new buffer
auto buffer = _device->CreateBuffer(TEXT("Pooled Staging"));
if (buffer->Init(GPUBufferDescription::Buffer(size, GPUBufferFlags::None, PixelFormat::Unknown, nullptr, 0, usage)))
{
LOG(Warning, "Failed to create pooled staging buffer.");
return nullptr;
}
// Cache buffer
{
ScopeLock lock(_locker);
_allBuffers.Add(buffer);
#if !BUILD_RELEASE
_allBuffersAllocSize += size;
_allBuffersTotalSize += size;
_allBuffersPeekSize = Math::Max(_allBuffersTotalSize, _allBuffersPeekSize);
#endif
}
return buffer;
}
void StagingManagerVulkan::ReleaseBuffer(CmdBufferVulkan* cmdBuffer, GPUBuffer*& buffer)
{
ScopeLock lock(_locker);
if (cmdBuffer)
{
// Return to pending pool (need to wait until command buffer will be executed and buffer will be reusable)
auto& item = _pendingBuffers.AddOne();
item.Buffer = buffer;
item.CmdBuffer = cmdBuffer;
item.FenceCounter = cmdBuffer->GetFenceSignaledCounter();
}
else
{
// Return to pool
_freeBuffers.Add({ buffer, Engine::FrameCount });
}
// Clear reference
buffer = nullptr;
}
void StagingManagerVulkan::ProcessPendingFree()
{
ScopeLock lock(_locker);
// Find staging buffers that has been processed by the GPU and can be reused
for (int32 i = _pendingBuffers.Count() - 1; i >= 0; i--)
{
auto& e = _pendingBuffers[i];
if (e.FenceCounter < e.CmdBuffer->GetFenceSignaledCounter())
{
// Return to pool
_freeBuffers.Add({ e.Buffer, Engine::FrameCount });
_pendingBuffers.RemoveAt(i);
}
}
// Free staging buffers that has not been used for a few frames
for (int32 i = _freeBuffers.Count() - 1; i >= 0; i--)
{
auto& e = _freeBuffers.Get()[i];
if (e.FrameNumber + VULKAN_RESOURCE_DELETE_SAFE_FRAMES_COUNT < Engine::FrameCount)
{
auto buffer = e.Buffer;
// Remove buffer from lists
_allBuffers.Remove(buffer);
_freeBuffers.RemoveAt(i);
#if !BUILD_RELEASE
// Update stats
_allBuffersFreeSize += buffer->GetSize();
_allBuffersTotalSize -= buffer->GetSize();
#endif
// Release memory
buffer->ReleaseGPU();
Delete(buffer);
}
}
}
void StagingManagerVulkan::Dispose()
{
ScopeLock lock(_locker);
#if BUILD_DEBUG
LOG(Info, "Vulkan staging buffers peek memory usage: {0}, allocs: {1}, frees: {2}", Utilities::BytesToText(_allBuffersPeekSize), Utilities::BytesToText(_allBuffersAllocSize), Utilities::BytesToText(_allBuffersFreeSize));
#endif
// Release buffers and clear memory
for (auto buffer : _allBuffers)
{
buffer->ReleaseGPU();
Delete(buffer);
}
_allBuffers.Resize(0);
_pendingBuffers.Resize(0);
}
GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan* adapter)
: GPUDevice(RendererType::Vulkan, shaderProfile)
, _renderPasses(512)
@@ -1106,7 +979,7 @@ GPUDeviceVulkan::GPUDeviceVulkan(ShaderProfile shaderProfile, GPUAdapterVulkan*
, _layouts(4096)
, Adapter(adapter)
, DeferredDeletionQueue(this)
, StagingManager(this)
, UploadBuffer(this)
, HelperResources(this)
{
}
@@ -2088,8 +1961,8 @@ void GPUDeviceVulkan::DrawBegin()
// Flush resources
DeferredDeletionQueue.ReleaseResources();
StagingManager.ProcessPendingFree();
DescriptorPoolsManager->GC();
UploadBuffer.BeginGeneration(Engine::FrameCount);
#if VULKAN_USE_PIPELINE_CACHE
// Serialize pipeline cache periodically for less PSO hitches on next app run
@@ -2125,7 +1998,7 @@ void GPUDeviceVulkan::Dispose()
_renderPasses.ClearDelete();
_layouts.ClearDelete();
HelperResources.Dispose();
StagingManager.Dispose();
UploadBuffer.Dispose();
TimestampQueryPools.ClearDelete();
OcclusionQueryPools.ClearDelete();
SAFE_DELETE_GPU_RESOURCE(UniformBufferUploader);