diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 86021d0ccb..57a89fcfe8 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -3156,6 +3156,39 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI return S_OK; } +void WrappedID3D11Device::CachedObjectsGarbageCollect() +{ + // 4000 is a fairly arbitrary number, chosen to make sure this garbage + // collection kicks in as rarely as possible (4000 is a *lot* of unique + // state objects to have), while still meaning that we'll never + // accidentally cause a state object to fail to create because the app + // expects only N to be alive but we're caching M more causing M+N>4096 + if(m_CachedStateObjects.size() < 4000) return; + + // Now release all purely cached objects that have no external refcounts. + // This will thrash if we have e.g. 2000 rasterizer state objects, all + // referenced, and 2000 sampler state objects, all referenced. + + for(auto it=m_CachedStateObjects.begin(); it != m_CachedStateObjects.end();) + { + ID3D11DeviceChild *o = *it; + + o->AddRef(); + if(o->Release() == 1) + { + auto eraseit = it; + ++it; + o->Release(); + InternalRelease(); + m_CachedStateObjects.erase(eraseit); + } + else + { + ++it; + } + } +} + void WrappedID3D11Device::AddDeferredContext(WrappedID3D11DeviceContext *defctx) { RDCASSERT(m_DeferredContexts.find(defctx) == m_DeferredContexts.end()); diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index e15193b69b..1686ed0c22 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -228,6 +228,22 @@ class WrappedID3D11Device : public IFrameCapturer, public D3DDEVICEPARENT bool m_AppControlledCapture; set m_CachedStateObjects; + + // This function will check if m_CachedStateObjects is growing too large, and if so + // go through m_CachedStateObjects and release any state objects that are purely + // cached (refcount == 1). This prevents us from aggressively caching and running + // out of state objects (D3D11 has a max of 4096). + // + // This isn't the ideal solution as it means some Create calls will be slightly + // more expensive while they run this garbage collect, but it is the simplest. + // + // For cases where cached objects are repeatedly created and released this will + // rarely kick in - only in the case where a lot of unique state objects are + // created then released and never re-used. + // + // Must be called while m_D3DLock is held. + void CachedObjectsGarbageCollect(); + set m_DeferredContexts; map > m_LayoutDescs; map m_LayoutDXBC; diff --git a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp index b9d208ea54..f7d83a9143 100644 --- a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp @@ -116,6 +116,8 @@ HRESULT WrappedID3D11Device::CreateBlendState1(const D3D11_BLEND_DESC1 *pBlendSt ID3D11BlendState1 *wrapped = new WrappedID3D11BlendState1(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef(); @@ -190,6 +192,8 @@ HRESULT WrappedID3D11Device::CreateRasterizerState1(const D3D11_RASTERIZER_DESC1 ID3D11RasterizerState1 *wrapped = new WrappedID3D11RasterizerState1(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef(); diff --git a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp index 05af004577..c4b8acf3e0 100644 --- a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp @@ -2036,6 +2036,8 @@ HRESULT WrappedID3D11Device::CreateBlendState( ID3D11BlendState *wrapped = new WrappedID3D11BlendState(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef(); @@ -2109,6 +2111,8 @@ HRESULT WrappedID3D11Device::CreateDepthStencilState( ID3D11DepthStencilState *wrapped = new WrappedID3D11DepthStencilState(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef(); @@ -2182,6 +2186,8 @@ HRESULT WrappedID3D11Device::CreateRasterizerState( ID3D11RasterizerState *wrapped = new WrappedID3D11RasterizerState(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef(); @@ -2255,6 +2261,8 @@ HRESULT WrappedID3D11Device::CreateSamplerState( ID3D11SamplerState *wrapped = new WrappedID3D11SamplerState(real, this); + CachedObjectsGarbageCollect(); + { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); wrapped->AddRef();