1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 19:54:19 +01:00

[d3d11] Throttle resource uploads through staging buffer

This commit is contained in:
Philip Rebohle 2024-10-07 14:33:45 +02:00 committed by Philip Rebohle
parent d330718353
commit ec18dd7846
2 changed files with 48 additions and 25 deletions

View File

@ -10,7 +10,8 @@ namespace dxvk {
: m_parent(pParent),
m_device(pParent->GetDXVKDevice()),
m_context(m_device->createContext(DxvkContextType::Supplementary)),
m_stagingBuffer(m_device, StagingBufferSize) {
m_stagingBuffer(m_device, StagingBufferSize),
m_stagingSignal(new sync::Fence(0)) {
m_context->beginRecording(
m_device->createCommandList());
}
@ -25,7 +26,7 @@ namespace dxvk {
std::lock_guard<dxvk::mutex> lock(m_mutex);
if (m_transferCommands != 0)
FlushInternal();
ExecuteFlush();
}
void D3D11Initializer::InitBuffer(
@ -73,7 +74,7 @@ namespace dxvk {
counterSlice.offset(),
sizeof(zero), &zero);
FlushImplicit();
ExecuteFlush();
}
@ -85,12 +86,13 @@ namespace dxvk {
Rc<DxvkBuffer> buffer = pBuffer->GetBuffer();
if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) {
ThrottleAllocation(buffer->info().size);
auto stagingSlice = m_stagingBuffer.alloc(buffer->info().size);
std::memcpy(stagingSlice.mapPtr(0), pInitialData->pSysMem, stagingSlice.length());
m_transferMemory += buffer->info().size;
m_transferCommands += 1;
m_context->uploadBuffer(buffer,
stagingSlice.buffer(),
stagingSlice.offset());
@ -100,7 +102,7 @@ namespace dxvk {
m_context->initBuffer(buffer);
}
FlushImplicit();
ThrottleAllocation(0);
}
@ -151,6 +153,7 @@ namespace dxvk {
packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask);
}
ThrottleAllocation(dataSize);
stagingSlice = m_stagingBuffer.alloc(dataSize);
}
@ -168,7 +171,6 @@ namespace dxvk {
packedFormat, image->mipLevelExtent(mip), formatInfo->aspectMask);
m_transferCommands += 1;
m_transferMemory += mipSizePerLayer;
util::packImageData(stagingSlice.mapPtr(dataOffset),
pInitialData[index].pSysMem, pInitialData[index].SysMemPitch, pInitialData[index].SysMemSlicePitch,
@ -213,7 +215,7 @@ namespace dxvk {
}
}
FlushImplicit();
ThrottleAllocation(0);
}
@ -265,7 +267,7 @@ namespace dxvk {
m_context->initImage(image, subresources, VK_IMAGE_LAYOUT_PREINITIALIZED);
m_transferCommands += 1;
FlushImplicit();
ThrottleAllocation(0);
}
@ -274,24 +276,37 @@ namespace dxvk {
m_context->initSparseImage(pTexture->GetImage());
m_transferCommands += 1;
FlushImplicit();
ThrottleAllocation(0);
}
void D3D11Initializer::FlushImplicit() {
if (m_transferCommands > MaxTransferCommands
|| m_transferMemory > MaxTransferMemory)
FlushInternal();
void D3D11Initializer::ThrottleAllocation(VkDeviceSize allocationSize) {
DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics();
// If the amount of memory in flight exceeds the limit, stall the
// calling thread and wait for some memory to actually get released.
VkDeviceSize stagingMemoryInFlight = stats.allocatedTotal - m_stagingSignal->value();
if (stagingMemoryInFlight > MaxMemoryInFlight) {
ExecuteFlush();
m_stagingSignal->wait(stats.allocatedTotal - MaxMemoryInFlight);
} else if (m_transferCommands >= MaxCommandsPerSubmission || stats.allocatedSinceLastReset >= MaxMemoryPerSubmission) {
// Flush pending commands if there are a lot of updates in flight
// to keep both execution time and staging memory in check.
ExecuteFlush();
}
}
void D3D11Initializer::FlushInternal() {
void D3D11Initializer::ExecuteFlush() {
DxvkStagingBufferStats stats = m_stagingBuffer.getStatistics();
m_context->signal(m_stagingSignal, stats.allocatedTotal);
m_context->flushCommandList(nullptr);
m_transferCommands = 0;
m_transferMemory = 0;
m_stagingBuffer.reset();
m_transferCommands = 0;
}
@ -306,7 +321,7 @@ namespace dxvk {
if (mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_NONE
|| mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {
FlushInternal();
ExecuteFlush();
m_device->waitForResource(pResource->GetImage(), DxvkAccess::Write);
}

View File

@ -18,13 +18,20 @@ namespace dxvk {
* zero-initialization for buffers and images.
*/
class D3D11Initializer {
constexpr static size_t MaxTransferMemory = 32 * 1024 * 1024;
constexpr static size_t MaxTransferCommands = 512;
// Use a staging buffer with a linear allocator to service small uploads
constexpr static VkDeviceSize StagingBufferSize = 1ull << 20;
public:
// Maximum number of copy and clear commands to record before flushing
constexpr static size_t MaxCommandsPerSubmission = 512u;
// Maximum amount of staging memory to allocate before flushing
constexpr static size_t MaxMemoryPerSubmission = (env::is32BitHostPlatform() ? 12u : 48u) << 20;
// Maximum amount of memory in flight. If there are pending uploads while
// this limit is exceeded, further initialization will be stalled.
constexpr static size_t MaxMemoryInFlight = 3u * MaxMemoryPerSubmission;
D3D11Initializer(
D3D11Device* pParent);
@ -52,9 +59,9 @@ namespace dxvk {
Rc<DxvkContext> m_context;
DxvkStagingBuffer m_stagingBuffer;
Rc<sync::Fence> m_stagingSignal;
size_t m_transferCommands = 0;
size_t m_transferMemory = 0;
void InitDeviceLocalBuffer(
D3D11Buffer* pBuffer,
@ -75,8 +82,9 @@ namespace dxvk {
void InitTiledTexture(
D3D11CommonTexture* pTexture);
void FlushImplicit();
void FlushInternal();
void ThrottleAllocation(VkDeviceSize allocationSize);
void ExecuteFlush();
void SyncSharedTexture(
D3D11CommonTexture* pResource);