diff --git a/dxvk.conf b/dxvk.conf index 2b68daf7c..368686a0d 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -203,6 +203,14 @@ # d3d11.zeroWorkgroupMemory = False +# Resource size limit for implicit discards, in kilobytes. For small staging +# resources mapped with MAP_WRITE, DXVK will sometimes allocate new backing +# storage in order to avoid GPU synchronization, so setting this too high +# may cause memory issues, setting it to 0 disables the feature. + +# d3d11.maxImplicitDiscardSize = 256 + + # Sets number of pipeline compiler threads. # # Supported values: diff --git a/src/d3d11/d3d11_context_imm.cpp b/src/d3d11/d3d11_context_imm.cpp index 2e3c46194..518227220 100644 --- a/src/d3d11/d3d11_context_imm.cpp +++ b/src/d3d11/d3d11_context_imm.cpp @@ -7,8 +7,6 @@ constexpr static uint32_t MinFlushIntervalUs = 750; constexpr static uint32_t IncFlushIntervalUs = 250; constexpr static uint32_t MaxPendingSubmits = 6; -constexpr static VkDeviceSize MaxImplicitDiscardSize = 256ull << 10; - namespace dxvk { D3D11ImmediateContext::D3D11ImmediateContext( @@ -16,6 +14,7 @@ namespace dxvk { const Rc& Device) : D3D11DeviceContext(pParent, Device, DxvkCsChunkFlag::SingleUse), m_csThread(Device, Device->createContext()), + m_maxImplicitDiscardSize(pParent->GetOptions()->maxImplicitDiscardSize), m_videoContext(this, Device) { EmitCs([ cDevice = m_device, @@ -393,7 +392,7 @@ namespace dxvk { auto buffer = pResource->GetBuffer(); auto sequenceNumber = pResource->GetSequenceNumber(); - if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= MaxImplicitDiscardSize) { + if (MapType != D3D11_MAP_READ && !MapFlags && bufferSize <= m_maxImplicitDiscardSize) { SynchronizeCsThread(sequenceNumber); bool hasWoAccess = buffer->isInUse(DxvkAccess::Write); @@ -514,11 +513,9 @@ namespace dxvk { // Don't implicitly discard large buffers or buffers of images with // multiple subresources, as that is likely to cause memory issues. - VkDeviceSize bufferSize = pResource->CountSubresources() == 1 - ? pResource->GetMappedSlice(Subresource).length - : MaxImplicitDiscardSize; + VkDeviceSize bufferSize = pResource->GetMappedSlice(Subresource).length; - if (bufferSize >= MaxImplicitDiscardSize) { + if (bufferSize >= m_maxImplicitDiscardSize || pResource->CountSubresources() > 1) { // Don't check access flags, WaitForResource will return // early anyway if the resource is currently in use doFlags = DoWait; diff --git a/src/d3d11/d3d11_context_imm.h b/src/d3d11/d3d11_context_imm.h index 9e9f73295..e96396a3d 100644 --- a/src/d3d11/d3d11_context_imm.h +++ b/src/d3d11/d3d11_context_imm.h @@ -125,6 +125,7 @@ namespace dxvk { uint64_t m_eventCount = 0ull; uint32_t m_mappedImageCount = 0u; + VkDeviceSize m_maxImplicitDiscardSize = 0ull; dxvk::high_resolution_clock::time_point m_lastFlush = dxvk::high_resolution_clock::now(); diff --git a/src/d3d11/d3d11_options.cpp b/src/d3d11/d3d11_options.cpp index dac86465d..2fb14557f 100644 --- a/src/d3d11/d3d11_options.cpp +++ b/src/d3d11/d3d11_options.cpp @@ -25,6 +25,11 @@ namespace dxvk { this->syncInterval = config.getOption("dxgi.syncInterval", -1); this->tearFree = config.getOption("dxgi.tearFree", Tristate::Auto); + int32_t maxImplicitDiscardSize = config.getOption("d3d11.maxImplicitDiscardSize", 256); + this->maxImplicitDiscardSize = maxImplicitDiscardSize >= 0 + ? VkDeviceSize(maxImplicitDiscardSize) << 10 + : VkDeviceSize(~0ull); + this->constantBufferRangeCheck = config.getOption("d3d11.constantBufferRangeCheck", false) && DxvkGpuVendor(devInfo.core.properties.vendorID) != DxvkGpuVendor::Amd; diff --git a/src/d3d11/d3d11_options.h b/src/d3d11/d3d11_options.h index 40a76acaa..d931f5f92 100644 --- a/src/d3d11/d3d11_options.h +++ b/src/d3d11/d3d11_options.h @@ -92,6 +92,9 @@ namespace dxvk { /// Limit frame rate int32_t maxFrameRate; + /// Limit discardable resource size + VkDeviceSize maxImplicitDiscardSize; + /// Defer surface creation until first present call. This /// fixes issues with games that create multiple swap chains /// for a single window that may interfere with each other.