[d3d9] Unmap unused resources

This commit is contained in:
Robin Kertels 2022-02-14 00:08:01 +01:00 committed by Joshie
parent b4f432f1de
commit ea76bfd019
7 changed files with 100 additions and 4 deletions

View File

@ -560,3 +560,12 @@
# - True/False # - True/False
# dxvk.enableDebugUtils = False # dxvk.enableDebugUtils = False
# Memory limit for locked D3D9 textures
#
# How much virtual memory will be used for textures (in MB).
# 0 to disable the limit.
# THIS DOES NOT IMPACT ACTUAL MEMORY CONSUMPTION OR TEXTURE QUALITY.
# DO NOT CHANGE THIS UNLESS YOU HAVE A VERY GOOD REASON.
# d3d9.textureMemory = 100

View File

@ -89,6 +89,8 @@ namespace dxvk {
D3D9CommonTexture::~D3D9CommonTexture() { D3D9CommonTexture::~D3D9CommonTexture() {
if (m_size != 0) if (m_size != 0)
m_device->ChangeReportedMemory(m_size); m_device->ChangeReportedMemory(m_size);
m_device->RemoveMappedTexture(this);
} }
@ -488,7 +490,7 @@ namespace dxvk {
return D3D9_COMMON_TEXTURE_MAP_MODE_NONE; return D3D9_COMMON_TEXTURE_MAP_MODE_NONE;
#ifdef D3D9_ALLOW_UNMAPPING #ifdef D3D9_ALLOW_UNMAPPING
if (m_desc.Pool != D3DPOOL_DEFAULT) if (m_device->GetOptions()->textureMemory != 0 && m_desc.Pool != D3DPOOL_DEFAULT)
return D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE; return D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE;
#endif #endif

View File

@ -1,6 +1,7 @@
#include "d3d9_device.h" #include "d3d9_device.h"
#include "d3d9_annotation.h" #include "d3d9_annotation.h"
#include "d3d9_common_texture.h"
#include "d3d9_interface.h" #include "d3d9_interface.h"
#include "d3d9_swapchain.h" #include "d3d9_swapchain.h"
#include "d3d9_caps.h" #include "d3d9_caps.h"
@ -4201,6 +4202,7 @@ namespace dxvk {
pResource->GetBuffer(Subresource, !needsReadback); pResource->GetBuffer(Subresource, !needsReadback);
} }
// Don't use MapTexture here to keep the mapped list small while the resource is still locked.
mapPtr = pResource->GetData(Subresource); mapPtr = pResource->GetData(Subresource);
if (needsReadback) { if (needsReadback) {
@ -4298,6 +4300,8 @@ namespace dxvk {
pResource->SetLocked(Subresource, true); pResource->SetLocked(Subresource, true);
UnmapTextures();
const bool noDirtyUpdate = Flags & D3DLOCK_NO_DIRTY_UPDATE; const bool noDirtyUpdate = Flags & D3DLOCK_NO_DIRTY_UPDATE;
if ((desc.Pool == D3DPOOL_DEFAULT || !noDirtyUpdate) && !readOnly) { if ((desc.Pool == D3DPOOL_DEFAULT || !noDirtyUpdate) && !readOnly) {
if (pBox && MipLevel != 0) { if (pBox && MipLevel != 0) {
@ -4354,6 +4358,7 @@ namespace dxvk {
if (unlikely(!pResource->GetLocked(Subresource))) if (unlikely(!pResource->GetLocked(Subresource)))
return D3D_OK; return D3D_OK;
MapTexture(pResource, Subresource); // Add it to the list of mapped resources
pResource->SetLocked(Subresource, false); pResource->SetLocked(Subresource, false);
// Flush image contents from staging if we aren't read only // Flush image contents from staging if we aren't read only
@ -4380,6 +4385,7 @@ namespace dxvk {
pResource->SetNeedsReadback(Subresource, true); pResource->SetNeedsReadback(Subresource, true);
} }
UnmapTextures();
return D3D_OK; return D3D_OK;
} }
@ -4463,7 +4469,7 @@ namespace dxvk {
+ srcOffsetBlockCount.y * pitch + srcOffsetBlockCount.y * pitch
+ srcOffsetBlockCount.x * formatInfo->elementSize; + srcOffsetBlockCount.x * formatInfo->elementSize;
const void* mapPtr = pSrcTexture->GetData(SrcSubresource); const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource);
VkDeviceSize dirtySize = extentBlockCount.width * extentBlockCount.height * extentBlockCount.depth * formatInfo->elementSize; VkDeviceSize dirtySize = extentBlockCount.width * extentBlockCount.height * extentBlockCount.depth * formatInfo->elementSize;
D3D9BufferSlice slice = AllocStagingBuffer(dirtySize); D3D9BufferSlice slice = AllocStagingBuffer(dirtySize);
const void* srcData = reinterpret_cast<const uint8_t*>(mapPtr) + copySrcOffset; const void* srcData = reinterpret_cast<const uint8_t*>(mapPtr) + copySrcOffset;
@ -4489,7 +4495,7 @@ namespace dxvk {
} }
else { else {
const DxvkFormatInfo* formatInfo = lookupFormatInfo(pDestTexture->GetFormatMapping().FormatColor); const DxvkFormatInfo* formatInfo = lookupFormatInfo(pDestTexture->GetFormatMapping().FormatColor);
const void* mapPtr = pSrcTexture->GetData(SrcSubresource); const void* mapPtr = MapTexture(pSrcTexture, SrcSubresource);
// Add more blocks for the other planes that we might have. // Add more blocks for the other planes that we might have.
// TODO: PLEASE CLEAN ME // TODO: PLEASE CLEAN ME
@ -4522,6 +4528,7 @@ namespace dxvk {
image, dstLayers, image, dstLayers,
slice.slice); slice.slice);
} }
UnmapTextures();
} }
void D3D9DeviceEx::EmitGenerateMips( void D3D9DeviceEx::EmitGenerateMips(
@ -4643,6 +4650,7 @@ namespace dxvk {
pResource->SetMapFlags(Flags | oldFlags); pResource->SetMapFlags(Flags | oldFlags);
pResource->IncrementLockCount(); pResource->IncrementLockCount();
UnmapTextures();
return D3D_OK; return D3D_OK;
} }
@ -4675,7 +4683,8 @@ namespace dxvk {
pResource->DirtyRange().Clear(); pResource->DirtyRange().Clear();
TrackBufferMappingBufferSequenceNumber(pResource); TrackBufferMappingBufferSequenceNumber(pResource);
return D3D_OK; UnmapTextures();
return D3D_OK;
} }
@ -7223,4 +7232,61 @@ namespace dxvk {
return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1; return m_csChunk->empty() ? m_csSeqNum : m_csSeqNum + 1;
} }
void* D3D9DeviceEx::MapTexture(D3D9CommonTexture* pTexture, UINT Subresource) {
// Will only be called inside the device lock
void *ptr = pTexture->GetData(Subresource);
#ifdef D3D9_ALLOW_UNMAPPING
if (likely(pTexture->GetMapMode() == D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)) {
m_mappedTextures.insert(pTexture);
}
#endif
return ptr;
}
void D3D9DeviceEx::TouchMappedTexture(D3D9CommonTexture* pTexture) {
#ifdef D3D9_ALLOW_UNMAPPING
if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)
return;
D3D9DeviceLock lock = LockDevice();
m_mappedTextures.touch(pTexture);
#endif
}
void D3D9DeviceEx::RemoveMappedTexture(D3D9CommonTexture* pTexture) {
#ifdef D3D9_ALLOW_UNMAPPING
if (pTexture->GetMapMode() != D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE)
return;
D3D9DeviceLock lock = LockDevice();
m_mappedTextures.remove(pTexture);
#endif
}
void D3D9DeviceEx::UnmapTextures() {
// Will only be called inside the device lock
#ifdef D3D9_ALLOW_UNMAPPING
uint32_t mappedMemory = m_memoryAllocator.MappedMemory();
if (likely(mappedMemory < uint32_t(m_d3d9Options.textureMemory)))
return;
uint32_t threshold = (m_d3d9Options.textureMemory / 4) * 3;
auto iter = m_mappedTextures.leastRecentlyUsedIter();
while (m_memoryAllocator.MappedMemory() >= threshold && iter != m_mappedTextures.leastRecentlyUsedEndIter()) {
if (unlikely((*iter)->IsAnySubresourceLocked() != 0)) {
iter++;
continue;
}
(*iter)->UnmapData();
iter = m_mappedTextures.remove(iter);
}
#endif
}
} }

View File

@ -28,10 +28,13 @@
#include "d3d9_shader_permutations.h" #include "d3d9_shader_permutations.h"
#include <unordered_set>
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>
#include <unordered_map> #include <unordered_map>
#include "../util/util_lru.h"
namespace dxvk { namespace dxvk {
class D3D9InterfaceEx; class D3D9InterfaceEx;
@ -934,6 +937,10 @@ namespace dxvk {
return &m_memoryAllocator; return &m_memoryAllocator;
} }
void* MapTexture(D3D9CommonTexture* pTexture, UINT Subresource);
void TouchMappedTexture(D3D9CommonTexture* pTexture);
void RemoveMappedTexture(D3D9CommonTexture* pTexture);
private: private:
DxvkCsChunkRef AllocCsChunk() { DxvkCsChunkRef AllocCsChunk() {
@ -1142,6 +1149,8 @@ namespace dxvk {
D3D9CommonTexture* pResource, D3D9CommonTexture* pResource,
UINT Subresource); UINT Subresource);
void UnmapTextures();
uint64_t GetCurrentSequenceNumber(); uint64_t GetCurrentSequenceNumber();
Com<D3D9InterfaceEx> m_parent; Com<D3D9InterfaceEx> m_parent;
@ -1282,6 +1291,9 @@ namespace dxvk {
Direct3DState9 m_state; Direct3DState9 m_state;
#ifdef D3D9_ALLOW_UNMAPPING
lru_list<D3D9CommonTexture*> m_mappedTextures;
#endif
}; };
} }

View File

@ -73,6 +73,7 @@ namespace dxvk {
this->deviceLocalConstantBuffers = config.getOption<bool> ("d3d9.deviceLocalConstantBuffers", false); this->deviceLocalConstantBuffers = config.getOption<bool> ("d3d9.deviceLocalConstantBuffers", false);
this->allowDirectBufferMapping = config.getOption<bool> ("d3d9.allowDirectBufferMapping", true); this->allowDirectBufferMapping = config.getOption<bool> ("d3d9.allowDirectBufferMapping", true);
this->seamlessCubes = config.getOption<bool> ("d3d9.seamlessCubes", false); this->seamlessCubes = config.getOption<bool> ("d3d9.seamlessCubes", false);
this->textureMemory = config.getOption<int32_t> ("d3d9.textureMemory", 100) << 20;
// If we are not Nvidia, enable general hazards. // If we are not Nvidia, enable general hazards.
this->generalHazards = adapter != nullptr this->generalHazards = adapter != nullptr

View File

@ -157,6 +157,9 @@ namespace dxvk {
/// Don't use non seamless cube maps /// Don't use non seamless cube maps
bool seamlessCubes; bool seamlessCubes;
/// How much virtual memory will be used for textures (in MB).
int32_t textureMemory;
}; };
} }

View File

@ -90,6 +90,7 @@ namespace dxvk {
if (m_texture.IsManaged()) if (m_texture.IsManaged())
m_texture.SetAllNeedUpload(); m_texture.SetAllNeedUpload();
m_parent->TouchMappedTexture(&m_texture);
return D3D_OK; return D3D_OK;
} }
@ -174,6 +175,7 @@ namespace dxvk {
if (m_texture.IsManaged()) if (m_texture.IsManaged())
m_texture.SetAllNeedUpload(); m_texture.SetAllNeedUpload();
m_parent->TouchMappedTexture(&m_texture);
return D3D_OK; return D3D_OK;
} }
@ -267,6 +269,7 @@ namespace dxvk {
} }
} }
m_parent->TouchMappedTexture(&m_texture);
return D3D_OK; return D3D_OK;
} }