diff --git a/dxvk.conf b/dxvk.conf index b1e9be58d..308f58fe3 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -267,6 +267,15 @@ # d3d11.zeroWorkgroupMemory = False +# Clears mapped memory to zero when suballocated memory is freed. This will +# drastically increase CPU overhead and should only be used as a last resort +# if a game does not properly initialize mapped buffers on its own. +# +# Supported values: True, False + +# dxvk.zeroMappedMemory = 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 diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index 4d31bbdf0..0b8c07176 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -2,6 +2,8 @@ #include #include +#include "../util/util_bit.h" + #include "dxvk_device.h" #include "dxvk_memory.h" #include "dxvk_sparse.h" @@ -1254,9 +1256,17 @@ namespace dxvk { allocation->m_address = address; allocation->m_size = size; - if (chunk.memory.mapPtr) + if (chunk.memory.mapPtr) { allocation->m_mapPtr = reinterpret_cast(chunk.memory.mapPtr) + offset; + if (unlikely(m_device->config().zeroMappedMemory)) { + // Some games will not write mapped buffers and will break if + // there is any stale data stored within. Clear when the allocation is + // freed, so that subsequent allocations will receive cleared buffers. + allocation->m_flags.set(DxvkAllocationFlag::ClearOnFree); + } + } + if (chunk.memory.buffer) { allocation->m_buffer = chunk.memory.buffer; allocation->m_bufferOffset = offset; @@ -1322,6 +1332,11 @@ namespace dxvk { void DxvkMemoryAllocator::freeAllocation( DxvkResourceAllocation* allocation) { + if (allocation->m_flags.test(DxvkAllocationFlag::ClearOnFree)) { + if (allocation->m_mapPtr) + bit::bclear(allocation->m_mapPtr, allocation->m_size); + } + if (allocation->m_flags.test(DxvkAllocationFlag::CanCache)) { // Return cacheable allocations to the shared cache allocation->destroyBufferViews(); @@ -1523,6 +1538,9 @@ namespace dxvk { "\n size: ", memory.size, " bytes")); } + if (m_device->config().zeroMappedMemory) + bit::bclear(memory.mapPtr, memory.size); + Logger::debug(str::format("Mapped memory region 0x", std::hex, reinterpret_cast(memory.mapPtr), " - 0x", reinterpret_cast(memory.mapPtr) + memory.size - 1u)); diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 0866991ed..fbdaa6acf 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -465,12 +465,23 @@ namespace dxvk { * \brief Resource allocation flags */ enum class DxvkAllocationFlag : uint32_t { + /// Allocation owns the given VkDeviceMemory allocation + /// and is not suballocated from an existing chunk. OwnsMemory = 0, + /// Allocation owns a dedicated VkBuffer object rather + /// than the global buffer for the parent chunk, if any. OwnsBuffer = 1, + /// Allocation owns a VkImage object. OwnsImage = 2, + /// Allocation can use an allocation cache. CanCache = 3, + /// Allocation can be relocated for defragmentation. CanMove = 4, + /// Allocation is imported from an external API. Imported = 5, + /// Memory must be cleared to zero when the allocation + /// is freed. Only used to work around app bugs. + ClearOnFree = 6, }; using DxvkAllocationFlags = Flags; diff --git a/src/dxvk/dxvk_options.cpp b/src/dxvk/dxvk_options.cpp index b00eb3b60..9d7d4d54a 100644 --- a/src/dxvk/dxvk_options.cpp +++ b/src/dxvk/dxvk_options.cpp @@ -13,6 +13,7 @@ namespace dxvk { hud = config.getOption("dxvk.hud", ""); tearFree = config.getOption("dxvk.tearFree", Tristate::Auto); hideIntegratedGraphics = config.getOption ("dxvk.hideIntegratedGraphics", false); + zeroMappedMemory = config.getOption ("dxvk.zeroMappedMemory", false); deviceFilter = config.getOption("dxvk.deviceFilter", ""); } diff --git a/src/dxvk/dxvk_options.h b/src/dxvk/dxvk_options.h index ab77aa987..7f64ddcb7 100644 --- a/src/dxvk/dxvk_options.h +++ b/src/dxvk/dxvk_options.h @@ -42,6 +42,9 @@ namespace dxvk { // incorrectly assume monitor layouts. bool hideIntegratedGraphics = false; + /// Clears all mapped memory to zero. + bool zeroMappedMemory = false; + // Device name std::string deviceFilter; };