From fdeea5204d92b665caee802f583ef6bc5cb81df0 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 9 Sep 2020 11:40:48 +0100 Subject: [PATCH] Refactor and optimise D3D11 refcounting implementation * In heavy D3D11 workloads the refcounting overhead especially during fast binding changes was significant. Refactoring the refcounting to work on a different model and deferring destruction of objects removes most of the overhead. --- renderdoc/driver/d3d11/d3d11_common.h | 77 +- renderdoc/driver/d3d11/d3d11_context.cpp | 97 +- renderdoc/driver/d3d11/d3d11_context.h | 25 +- renderdoc/driver/d3d11/d3d11_context_wrap.cpp | 2 + renderdoc/driver/d3d11/d3d11_debug.cpp | 72 +- renderdoc/driver/d3d11/d3d11_device.cpp | 253 ++-- renderdoc/driver/d3d11/d3d11_device.h | 190 ++- renderdoc/driver/d3d11/d3d11_device1_wrap.cpp | 6 +- renderdoc/driver/d3d11/d3d11_device3_wrap.cpp | 3 +- renderdoc/driver/d3d11/d3d11_device_wrap.cpp | 42 +- renderdoc/driver/d3d11/d3d11_hooks.cpp | 12 +- renderdoc/driver/d3d11/d3d11_renderstate.cpp | 158 +-- renderdoc/driver/d3d11/d3d11_renderstate.h | 47 +- renderdoc/driver/d3d11/d3d11_rendertext.cpp | 81 +- renderdoc/driver/d3d11/d3d11_resources.cpp | 47 +- renderdoc/driver/d3d11/d3d11_resources.h | 268 +++-- renderdoc/driver/d3d11/d3d11_video.cpp | 25 +- renderdoc/driver/d3d11/d3d11_video.h | 7 +- renderdoc/driver/d3d12/d3d12_common.h | 3 - util/test/demos/d3d11/d3d11_helpers.cpp | 44 +- util/test/demos/d3d11/d3d11_helpers.h | 30 +- .../test/demos/d3d11/d3d11_refcount_check.cpp | 1029 ++++++++++++++++- util/test/demos/d3d11/d3d11_test.cpp | 47 +- util/test/demos/d3d11/d3d11_test.h | 22 +- util/test/demos/d3d12/d3d12_helpers.cpp | 62 +- util/test/demos/d3d12/d3d12_helpers.h | 25 +- util/test/demos/d3d12/d3d12_large_buffer.cpp | 2 +- util/test/demos/d3d12/d3d12_test.cpp | 8 - util/test/demos/d3d12/d3d12_test.h | 23 +- util/test/demos/dx/d3d_helpers.h | 7 + util/test/demos/vk/vk_descriptor_reuse.cpp | 2 +- util/test/tests/D3D11/D3D11_Refcount_Check.py | 33 + 32 files changed, 1933 insertions(+), 816 deletions(-) create mode 100644 util/test/tests/D3D11/D3D11_Refcount_Check.py diff --git a/renderdoc/driver/d3d11/d3d11_common.h b/renderdoc/driver/d3d11/d3d11_common.h index 9bfff32b5..20f530a30 100644 --- a/renderdoc/driver/d3d11/d3d11_common.h +++ b/renderdoc/driver/d3d11/d3d11_common.h @@ -219,62 +219,33 @@ inline rdcstr GetDebugName(T *pObj) return rdcstr(); } -class RefCounter -{ -private: - IUnknown *m_pReal; - unsigned int m_iRefcount; - bool m_SelfDeleting; +#define SAFE_INTADDREF(p) \ + do \ + { \ + if(p) \ + { \ + IntAddRef(p); \ + } \ + } while((void)0, 0) -protected: - void SetSelfDeleting(bool selfDelete) { m_SelfDeleting = selfDelete; } - // used for derived classes that need to soft ref but are handling their - // own self-deletion - static void AddDeviceSoftref(WrappedID3D11Device *device); - static void ReleaseDeviceSoftref(WrappedID3D11Device *device); +#define SAFE_INTRELEASE(p) \ + do \ + { \ + if(p) \ + { \ + IntRelease(p); \ + (p) = NULL; \ + } \ + } while((void)0, 0) -public: - RefCounter(IUnknown *real, bool selfDelete = true) - : m_pReal(real), m_iRefcount(1), m_SelfDeleting(selfDelete) - { - } - virtual ~RefCounter() {} - unsigned int GetRefCount() { return m_iRefcount; } - ////////////////////////////// - // implement IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface( - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - __RPC__deref_out void **ppvObject); +#if ENABLED(RDOC_DEVEL) +#define ASSERT_REFCOUNT(refcount) \ + if(refcount < 0) \ + RDCERR("Unbalanced refcounting, refcount has dropped below 0"); +#else +#define ASSERT_REFCOUNT(refcount) +#endif - ULONG STDMETHODCALLTYPE AddRef() - { - InterlockedIncrement(&m_iRefcount); - return m_iRefcount; - } - ULONG STDMETHODCALLTYPE Release() - { - unsigned int ret = InterlockedDecrement(&m_iRefcount); - if(ret == 0 && m_SelfDeleting) - delete this; - return ret; - } - - unsigned int SoftRef(WrappedID3D11Device *device); - unsigned int SoftRelease(WrappedID3D11Device *device); -}; - -#define IMPLEMENT_IUNKNOWN_WITH_REFCOUNTER \ - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter::AddRef(); } \ - ULONG STDMETHODCALLTYPE Release() { return RefCounter::Release(); } \ - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) \ - { \ - return RefCounter::QueryInterface(riid, ppvObject); \ - } - -#define IMPLEMENT_IUNKNOWN_WITH_REFCOUNTER_CUSTOMQUERY \ - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter::AddRef(); } \ - ULONG STDMETHODCALLTYPE Release() { return RefCounter::Release(); } #define IMPLEMENT_FUNCTION_SERIALISED(ret, func, ...) \ ret func(__VA_ARGS__); \ template \ diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index 54bb55632..104cb0c80 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -37,37 +37,33 @@ WRAPPED_POOL_INST(WrappedID3D11CommandList); INT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::BeginEvent(LPCWSTR Name) { - if(m_Context) - return m_Context->PushMarker(0, Name); - - return -1; + return m_Context->PushMarker(0, Name); } INT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::EndEvent() { - if(m_Context) - return m_Context->PopMarker(); - - return -1; + return m_Context->PopMarker(); } void STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::SetMarker(LPCWSTR Name) { - if(m_Context) - return m_Context->SetMarker(0, Name); + return m_Context->SetMarker(0, Name); +} + +ULONG STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::AddRef() +{ + return m_Context->AddRef(); +} + +ULONG STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::Release() +{ + return m_Context->Release(); } HRESULT STDMETHODCALLTYPE WrappedID3DUserDefinedAnnotation::QueryInterface(REFIID riid, void **ppvObject) { - if(riid == __uuidof(ID3DUserDefinedAnnotation)) - { - *ppvObject = (ID3DUserDefinedAnnotation *)this; - AddRef(); - return S_OK; - } - - return E_NOINTERFACE; + return m_Context->QueryInterface(riid, ppvObject); } extern uint32_t NullCBOffsets[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; @@ -75,8 +71,7 @@ extern uint32_t NullCBCounts[D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT]; WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *realDevice, ID3D11DeviceContext *context) - : RefCounter(context), - m_pDevice(realDevice), + : m_pDevice(realDevice), m_pRealContext(context), m_ScratchSerialiser(new StreamWriter(1024), Ownership::Stream) { @@ -129,6 +124,8 @@ WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *real m_pRealContext->QueryInterface(__uuidof(ID3D11VideoContext2), (void **)&m_WrappedVideo.m_pReal2); } + m_UserAnnotation.SetContext(this); + m_NeedUpdateSubWorkaround = false; if(m_pRealContext) { @@ -177,12 +174,18 @@ WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *real m_CurDrawcallID = 1; m_MarkerIndentLevel = 0; - m_UserAnnotation.SetContext(this); m_CurrentPipelineState = new D3D11RenderState(D3D11RenderState::Empty); m_DeferredSavedState = NULL; m_DoStateVerify = IsCaptureMode(m_State); + // take a reference on the device, as with all ID3D11DeviceChild objects. + m_pDevice->AddRef(); + + // start with 1 ext ref and 0 int refs + m_ExtRef = 1; + m_IntRef = 0; + if(!context || GetType() == D3D11_DEVICE_CONTEXT_IMMEDIATE) { m_CurrentPipelineState->SetImmediatePipeline(m_pDevice); @@ -192,7 +195,6 @@ WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *real else { m_CurrentPipelineState->SetDevice(m_pDevice); - m_pDevice->SoftRef(); // we haven't actually marked active, but this makes the check much easier - just look at this // bool flag rather than "if immediate and not flagged" @@ -240,8 +242,11 @@ WrappedID3D11DeviceContext::~WrappedID3D11DeviceContext() void WrappedID3D11DeviceContext::GetDevice(ID3D11Device **ppDevice) { - *ppDevice = (ID3D11Device *)m_pDevice; - (*ppDevice)->AddRef(); + if(ppDevice) + { + *ppDevice = (ID3D11Device *)m_pDevice; + (*ppDevice)->AddRef(); + } } bool WrappedID3D11DeviceContext::HasNonMarkerEvents() @@ -1360,6 +1365,46 @@ void WrappedID3D11DeviceContext::ClearMaps() m_OpenMaps.clear(); } +void WrappedID3D11DeviceContext::IntAddRef() +{ + Atomic::Inc32(&m_IntRef); +} + +void WrappedID3D11DeviceContext::IntRelease() +{ + Atomic::Dec32(&m_IntRef); + ASSERT_REFCOUNT(m_IntRef); + // don't defer destruction of device contexts, delete them immediately. + if(m_IntRef + m_ExtRef == 0) + delete this; +} + +ULONG STDMETHODCALLTYPE WrappedID3D11DeviceContext::AddRef() +{ + // if we're about to create a new external reference on this object, add back our reference on + // the device + if(m_ExtRef == 0) + m_pDevice->AddRef(); + Atomic::Inc32(&m_ExtRef); + return (ULONG)m_ExtRef; +} + +ULONG STDMETHODCALLTYPE WrappedID3D11DeviceContext::Release() +{ + Atomic::Dec32(&m_ExtRef); + ASSERT_REFCOUNT(m_ExtRef); + // if we just released the last external reference on this object, release our reference on the + // device. + if(m_ExtRef == 0) + m_pDevice->Release(); + if(m_IntRef + m_ExtRef == 0) + { + delete this; + return 0; + } + return (ULONG)m_ExtRef; +} + HRESULT STDMETHODCALLTYPE WrappedID3D11DeviceContext::QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(IUnknown)) @@ -1443,7 +1488,7 @@ HRESULT STDMETHODCALLTYPE WrappedID3D11DeviceContext::QueryInterface(REFIID riid else if(riid == __uuidof(ID3DUserDefinedAnnotation)) { *ppvObject = (ID3DUserDefinedAnnotation *)&m_UserAnnotation; - m_UserAnnotation.AddRef(); + AddRef(); return S_OK; } else if(riid == __uuidof(ID3D11InfoQueue)) @@ -1461,7 +1506,7 @@ HRESULT STDMETHODCALLTYPE WrappedID3D11DeviceContext::QueryInterface(REFIID riid WarnUnknownGUID("ID3D11DeviceContext", riid); } - return RefCounter::QueryInterface(riid, ppvObject); + return RefCountDXGIObject::WrapQueryInterface(m_pRealContext, riid, ppvObject); } #pragma region Record Statistics diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index 7e046b0db..3c2e2fe78 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -69,14 +69,13 @@ struct MapIntercept class WrappedID3D11DeviceContext; // ID3DUserDefinedAnnotation -class WrappedID3DUserDefinedAnnotation : public RefCounter, public ID3DUserDefinedAnnotation +class WrappedID3DUserDefinedAnnotation : public ID3DUserDefinedAnnotation { public: - WrappedID3DUserDefinedAnnotation() : RefCounter(NULL), m_Context(NULL) {} + WrappedID3DUserDefinedAnnotation() : m_Context(NULL) {} void SetContext(WrappedID3D11DeviceContext *ctx) { m_Context = ctx; } - // doesn't need to soft-ref the device, for once! - IMPLEMENT_IUNKNOWN_WITH_REFCOUNTER_CUSTOMQUERY; - + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); virtual INT STDMETHODCALLTYPE BeginEvent(LPCWSTR Name); @@ -94,7 +93,7 @@ enum CaptureFailReason CaptureFailed_UncappedCmdlist, }; -class WrappedID3D11DeviceContext : public RefCounter, public ID3D11DeviceContext4 +class WrappedID3D11DeviceContext : public ID3D11DeviceContext4 { private: friend class WrappedID3D11DeviceContext; @@ -116,6 +115,10 @@ private: } }; + // we manually implement ID3D11DeviceChild instead of using WrappedID3D11DeviceChild because the + // device contexts can be special + int32_t m_ExtRef; + int32_t m_IntRef; std::set m_DeferredDirty; std::set m_DeferredReferences; @@ -325,14 +328,18 @@ public: int ThreadSafe_BeginEvent(uint32_t col, const wchar_t *name); int ThreadSafe_EndEvent(); + // internal addref/release + void IntAddRef(); + void IntRelease(); + ////////////////////////////// // implement IUnknown - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter::SoftRef(m_pDevice); } - ULONG STDMETHODCALLTYPE Release() { return RefCounter::SoftRelease(m_pDevice); } + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); ////////////////////////////// - // implement IDXGIDeviceChild + // implement ID3D11DeviceChild virtual HRESULT STDMETHODCALLTYPE SetPrivateData(REFGUID Name, UINT DataSize, const void *pData) { diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp index e400bf693..63aa51547 100644 --- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp @@ -5533,6 +5533,8 @@ void WrappedID3D11DeviceContext::Flush() m_EmptyCommandList = false; + m_pDevice->FlushPendingDead(); + SERIALISE_TIME_CALL(m_pRealContext->Flush()); if(IsActiveCapturing(m_State)) diff --git a/renderdoc/driver/d3d11/d3d11_debug.cpp b/renderdoc/driver/d3d11/d3d11_debug.cpp index e6fbe4717..51336e364 100644 --- a/renderdoc/driver/d3d11/d3d11_debug.cpp +++ b/renderdoc/driver/d3d11/d3d11_debug.cpp @@ -37,6 +37,23 @@ #include "data/hlsl/hlsl_cbuffers.h" +static void InternalRef(ID3D11DeviceChild *child) +{ + if(child) + { + // we don't want any of our internal resources to show up as external references but we do + // certainly want to keep them alive. + // To solve this, we add an internal refcount and remove the implicit external refcount. + // Removing the external refcount means the device will also have no refcount so it will still + // be destroyed at the same time regardless of these objects. The internal ref will keep them + // alive even if the user doesn't have any pointers to them and they aren't bound anywhere. + // Effectively you can think of this as the same as if they were bound to some unknown undefined + // pipeline slot + IntAddRef(child); + child->Release(); + } +} + D3D11DebugManager::D3D11DebugManager(WrappedID3D11Device *wrapper) { if(RenderDoc::Inst().GetCrashHandler()) @@ -142,31 +159,24 @@ void D3D11DebugManager::InitCommonResources() { CopyMSToArrayPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_CopyMSToArray", "ps_5_0"); - if(CopyMSToArrayPS) - m_pDevice->InternalRef(); + InternalRef(CopyMSToArrayPS); CopyArrayToMSPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_CopyArrayToMS", "ps_5_0"); - if(CopyArrayToMSPS) - m_pDevice->InternalRef(); + InternalRef(CopyArrayToMSPS); FloatCopyMSToArrayPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_FloatCopyMSToArray", "ps_5_0"); - if(FloatCopyMSToArrayPS) - m_pDevice->InternalRef(); + InternalRef(FloatCopyMSToArrayPS); FloatCopyArrayToMSPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_FloatCopyArrayToMS", "ps_5_0"); - if(FloatCopyArrayToMSPS) - m_pDevice->InternalRef(); + InternalRef(FloatCopyArrayToMSPS); DepthCopyMSToArrayPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_DepthCopyMSToArray", "ps_5_0"); - if(DepthCopyMSToArrayPS) - m_pDevice->InternalRef(); + InternalRef(DepthCopyMSToArrayPS); DepthCopyArrayToMSPS = shaderCache->MakePShader(multisamplehlsl.c_str(), "RENDERDOC_DepthCopyArrayToMS", "ps_5_0"); - if(DepthCopyArrayToMSPS) - m_pDevice->InternalRef(); + InternalRef(DepthCopyArrayToMSPS); MSArrayCopyVS = shaderCache->MakeVShader(hlsl.c_str(), "RENDERDOC_FullscreenVS", "vs_4_0"); - if(MSArrayCopyVS) - m_pDevice->InternalRef(); + InternalRef(MSArrayCopyVS); } else { @@ -191,7 +201,7 @@ void D3D11DebugManager::InitCommonResources() for(int i = 0; i < ARRAY_COUNT(PublicCBuffers); i++) { PublicCBuffers[i] = MakeCBuffer(PublicCBufferSize); - m_pDevice->InternalRef(); + InternalRef(PublicCBuffers[i]); rm->SetInternalResource(PublicCBuffers[i]); } @@ -327,25 +337,18 @@ void D3D11DebugManager::ShutdownResources() SAFE_RELEASE(PredicateDSV); - SAFE_RELEASE(CopyMSToArrayPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(CopyArrayToMSPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(FloatCopyMSToArrayPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(FloatCopyArrayToMSPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(DepthCopyMSToArrayPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(DepthCopyArrayToMSPS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(PixelHistoryUnusedCS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(PixelHistoryCopyCS); - m_pDevice->InternalRelease(); + // the objects we added with an internal ref, because they're used during capture, should also be + // released with an internal ref. + SAFE_INTRELEASE(CopyMSToArrayPS); + SAFE_INTRELEASE(CopyArrayToMSPS); + SAFE_INTRELEASE(FloatCopyMSToArrayPS); + SAFE_INTRELEASE(FloatCopyArrayToMSPS); + SAFE_INTRELEASE(DepthCopyMSToArrayPS); + SAFE_INTRELEASE(DepthCopyArrayToMSPS); + SAFE_INTRELEASE(MSArrayCopyVS); - SAFE_RELEASE(MSArrayCopyVS); - m_pDevice->InternalRelease(); + SAFE_RELEASE(PixelHistoryUnusedCS); + SAFE_RELEASE(PixelHistoryCopyCS); for(auto it = m_DiscardPatterns.begin(); it != m_DiscardPatterns.end(); it++) if(it->second) @@ -358,8 +361,7 @@ void D3D11DebugManager::ShutdownResources() for(int i = 0; i < ARRAY_COUNT(PublicCBuffers); i++) { - SAFE_RELEASE(PublicCBuffers[i]); - m_pDevice->InternalRelease(); + SAFE_INTRELEASE(PublicCBuffers[i]); } } diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 648d26481..60fec09fc 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -42,27 +42,19 @@ WRAPPED_POOL_INST(WrappedID3D11Device); WrappedID3D11Device *WrappedID3D11Device::m_pCurrentWrappedDevice = NULL; -void WrappedID3D11Device::NewSwapchainBuffer(IUnknown *backbuffer) -{ - WrappedID3D11Texture2D1 *wrapped = (WrappedID3D11Texture2D1 *)backbuffer; - - if(wrapped) - { - // keep ref as a 'view' (invisible to user) - wrapped->ViewAddRef(); - wrapped->Release(); - } -} - WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitParams params) - : m_RefCounter(realDevice, false), - m_SoftRefCounter(NULL, false), - m_pDevice(realDevice), - m_ScratchSerialiser(new StreamWriter(1024), Ownership::Stream) + : m_pDevice(realDevice), m_ScratchSerialiser(new StreamWriter(1024), Ownership::Stream) { if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->RegisterMemoryRegion(this, sizeof(WrappedID3D11Device)); + // if there's no other device, claim it! + if(m_pCurrentWrappedDevice == NULL) + m_pCurrentWrappedDevice = this; + + // start with a refcount of 1 + m_RefCount = 1; + m_SectionVersion = D3D11InitParams::CurrentVersion; uint32_t flags = WriteSerialiser::ChunkDuration | WriteSerialiser::ChunkTimestamp | @@ -90,13 +82,8 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara m_pDevice->QueryInterface(__uuidof(ID3D11Device5), (void **)&m_pDevice5); } - // refcounters implicitly construct with one reference, but we don't start with any soft - // references. - m_SoftRefCounter.Release(); - m_InternalRefcount = 0; - m_Alive = true; - m_DummyInfoQueue.m_pDevice = this; + m_WrappedInfoQueue.m_pDevice = this; m_DummyDebug.m_pDevice = this; m_WrappedDebug.m_pDevice = this; m_WrappedMultithread.m_pDevice = this; @@ -183,6 +170,11 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara m_pImmediateContext = new WrappedID3D11DeviceContext(this, context); + // add an internal reference to keep the immediate context always alive until we destroy it, and + // remove the implicit external ref from when any device child is created + m_pImmediateContext->IntAddRef(); + m_pImmediateContext->Release(); + m_pImmediateContext->GetScratchSerialiser().SetChunkMetadataRecording( m_ScratchSerialiser.GetChunkMetadataRecording()); @@ -192,6 +184,7 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara if(realDevice) { realDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&m_pInfoQueue); + realDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&m_WrappedInfoQueue.m_pReal); realDevice->QueryInterface(__uuidof(ID3D11Debug), (void **)&m_WrappedDebug.m_pDebug); realDevice->QueryInterface(__uuidof(ID3D11Multithread), (void **)&m_WrappedMultithread.m_pReal); realDevice->QueryInterface(__uuidof(ID3D11VideoDevice), (void **)&m_WrappedVideo.m_pReal); @@ -240,6 +233,7 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara // ATI workaround - these dlls can get unloaded and cause a crash. + /* if(GetModuleHandleA("aticfx32.dll")) LoadLibraryA("aticfx32.dll"); if(GetModuleHandleA("atiuxpag.dll")) @@ -258,6 +252,7 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara if(GetModuleHandleA("nvwgf2umx.dll")) LoadLibraryA("nvwgf2umx.dll"); + */ } WrappedID3D11Device::~WrappedID3D11Device() @@ -271,7 +266,7 @@ WrappedID3D11Device::~WrappedID3D11Device() for(auto it = m_CachedStateObjects.begin(); it != m_CachedStateObjects.end(); ++it) if(*it) - (*it)->Release(); + IntRelease(*it); m_CachedStateObjects.clear(); @@ -285,7 +280,8 @@ WrappedID3D11Device::~WrappedID3D11Device() SAFE_RELEASE(m_RealAnnotations); - SAFE_RELEASE(m_pImmediateContext); + m_pImmediateContext->IntRelease(); + m_pImmediateContext = NULL; for(auto it = m_SwapChains.begin(); it != m_SwapChains.end(); ++it) SAFE_RELEASE(it->second); @@ -312,6 +308,7 @@ WrappedID3D11Device::~WrappedID3D11Device() SAFE_DELETE(m_ResourceManager); SAFE_RELEASE(m_pInfoQueue); + SAFE_RELEASE(m_WrappedInfoQueue.m_pReal); SAFE_RELEASE(m_WrappedMultithread.m_pReal); SAFE_RELEASE(m_WrappedVideo.m_pReal); SAFE_RELEASE(m_WrappedVideo.m_pReal1); @@ -327,40 +324,40 @@ WrappedID3D11Device::~WrappedID3D11Device() RDCASSERT(WrappedID3D11Texture3D1::m_TextureList.empty()); } - delete m_Replay; + SAFE_DELETE(m_Replay); if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this); } -void WrappedID3D11Device::CheckForDeath() +HRESULT STDMETHODCALLTYPE DummyID3D11InfoQueue::QueryInterface(REFIID riid, void **ppvObject) { - if(!m_Alive) - return; - - if(m_RefCounter.GetRefCount() == 0) - { - RDCASSERT(m_SoftRefCounter.GetRefCount() >= m_InternalRefcount); - - // MEGA HACK - if(m_SoftRefCounter.GetRefCount() <= m_InternalRefcount || IsReplayMode(m_State)) - { - m_Alive = false; - delete this; - } - } + return m_pDevice->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE DummyID3D11InfoQueue::AddRef() { - m_pDevice->AddRef(); - return 1; + return m_pDevice->AddRef(); } ULONG STDMETHODCALLTYPE DummyID3D11InfoQueue::Release() { - m_pDevice->Release(); - return 1; + return m_pDevice->Release(); +} + +HRESULT STDMETHODCALLTYPE WrappedID3D11InfoQueue::QueryInterface(REFIID riid, void **ppvObject) +{ + return m_pDevice->QueryInterface(riid, ppvObject); +} + +ULONG STDMETHODCALLTYPE WrappedID3D11InfoQueue::AddRef() +{ + return m_pDevice->AddRef(); +} + +ULONG STDMETHODCALLTYPE WrappedID3D11InfoQueue::Release() +{ + return m_pDevice->Release(); } HRESULT STDMETHODCALLTYPE DummyID3D11Debug::QueryInterface(REFIID riid, void **ppvObject) @@ -370,43 +367,27 @@ HRESULT STDMETHODCALLTYPE DummyID3D11Debug::QueryInterface(REFIID riid, void **p ULONG STDMETHODCALLTYPE DummyID3D11Debug::AddRef() { - m_pDevice->AddRef(); - return 1; + return m_pDevice->AddRef(); } ULONG STDMETHODCALLTYPE DummyID3D11Debug::Release() { - m_pDevice->Release(); - return 1; + return m_pDevice->Release(); } HRESULT STDMETHODCALLTYPE WrappedD3D11Multithread::QueryInterface(REFIID riid, void **ppvObject) { - if(riid == __uuidof(IUnknown)) - { - *ppvObject = (IUnknown *)this; - AddRef(); - return S_OK; - } - if(riid == __uuidof(ID3D11Multithread)) - { - *ppvObject = (ID3D11Multithread *)this; - AddRef(); - return S_OK; - } - return E_NOINTERFACE; + return m_pDevice->QueryInterface(riid, ppvObject); } ULONG STDMETHODCALLTYPE WrappedD3D11Multithread::AddRef() { - m_pDevice->AddRef(); - return 1; + return m_pDevice->AddRef(); } ULONG STDMETHODCALLTYPE WrappedD3D11Multithread::Release() { - m_pDevice->Release(); - return 1; + return m_pDevice->Release(); } void STDMETHODCALLTYPE WrappedD3D11Multithread::Enter() @@ -693,8 +674,8 @@ HRESULT WrappedID3D11Device::QueryInterface(REFIID riid, void **ppvObject) } else if(riid == __uuidof(ID3D11Multithread)) { - AddRef(); *ppvObject = (ID3D11Multithread *)&m_WrappedMultithread; + m_WrappedMultithread.AddRef(); return S_OK; } else if(riid == ID3D11ShaderTraceFactory_uuid) @@ -727,8 +708,8 @@ HRESULT WrappedID3D11Device::QueryInterface(REFIID riid, void **ppvObject) { if(m_pInfoQueue) { - *ppvObject = m_pInfoQueue; - m_pInfoQueue->AddRef(); + *ppvObject = (ID3D11InfoQueue *)&m_WrappedInfoQueue; + m_WrappedInfoQueue.AddRef(); return S_OK; } else @@ -749,8 +730,8 @@ HRESULT WrappedID3D11Device::QueryInterface(REFIID riid, void **ppvObject) // return our wrapper if(m_WrappedDebug.m_pDebug) { - AddRef(); *ppvObject = (ID3D11Debug *)&m_WrappedDebug; + m_WrappedDebug.AddRef(); return S_OK; } else @@ -775,7 +756,7 @@ HRESULT WrappedID3D11Device::QueryInterface(REFIID riid, void **ppvObject) WarnUnknownGUID("ID3D11Device", riid); - return m_RefCounter.QueryInterface(riid, ppvObject); + return RefCountDXGIObject::WrapQueryInterface(m_pDevice, riid, ppvObject); } rdcstr WrappedID3D11Device::GetChunkName(uint32_t idx) @@ -1444,6 +1425,16 @@ void WrappedID3D11Device::ReplayLog(uint32_t startEventID, uint32_t endEventID, D3D11MarkerRegion::Set("!!!!RenderDoc Internal: Done replay"); } +void WrappedID3D11Device::NewSwapchainBuffer(IUnknown *backbuffer) +{ + WrappedID3D11Texture2D1 *wrapped = (WrappedID3D11Texture2D1 *)backbuffer; + + // add internal reference to keep this texture alive + SAFE_INTADDREF(wrapped); + // release the external reference + wrapped->Release(); +} + void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT QueueCount, IUnknown *const *ppPresentQueue, IUnknown **unwrappedQueues) @@ -1476,7 +1467,7 @@ void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT } } - wrapped11->ViewRelease(); + SAFE_INTRELEASE(wrapped11); } wrapped11 = NULL; @@ -1488,6 +1479,8 @@ void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapper *swapper, UINT SAFE_RELEASE(it->second); m_SwapChains.erase(it); } + + FlushPendingDead(); } template @@ -1633,6 +1626,88 @@ IDXGIResource *WrappedID3D11Device::WrapExternalDXGIResource(IDXGIResource *res) return (IDXGIResource *)voidRes; } +void WrappedID3D11Device::ReportDeath(ID3D11DeviceChild *obj) +{ + SCOPED_LOCK(m_D3DLock); + + m_DeadObjects.push_back(obj); + + // if we're shutting down, immediately flush the object as dead. This isn't super efficient but + // it's not the end of the world + if(m_RefCount == 0) + FlushPendingDead(); +} + +void WrappedID3D11Device::FlushPendingDead() +{ + SCOPED_LOCK(m_D3DLock); + + // to be safe, don't destroy anything while active capturing + if(IsActiveCapturing(m_State)) + return; + + D3D11ResourceManager *rm = GetResourceManager(); + + int pass = 0; + do + { + rdcarray objs; + objs.swap(m_DeadObjects); + + for(ID3D11DeviceChild *child : objs) + { + // only wrapped objects are reported for death + WrappedDeviceChild11 *wrapped = + (WrappedDeviceChild11 *)child; + + // is this object still dead? If so delete it, otherwise ignore it. + if(wrapped->GetIntRefCount() == 0 && wrapped->GetExtRefCount() == 0) + { + ResourceId id = wrapped->GetResourceID(); + + // clean up book-keeping + rm->RemoveWrapper(wrapped->GetReal()); + rm->ReleaseCurrentResource(id); + D3D11ResourceRecord *record = GetResourceManager()->GetResourceRecord(id); + if(record) + record->Delete(GetResourceManager()); + + if(GetResourceManager()->HasLiveResource(id)) + GetResourceManager()->EraseLiveResource(id); + + // this is a bit of a hack. the vtable for e.g. a wrapped blend state will have: + // + // [IUnknown][ID3D11DeviceChild][ID3D11BlendState] + // + // but a buffer will have + // + // [IUnknown][ID3D11DeviceChild][ID3D11Resource][ID3D11Buffer] + // + // If we have WrappedDeviceChild11 having a virtual descructor then because it's templated + // it will insert it in a variable location after all the I* functions. Even if we avoided + // that with another base class we'd then need two vtable pointers or we'd need to know the + // type to custom offset into the vtable (i.e. do the proper type cast to get the compiler + // to do that). + // + // Instead we hijack ID3D11DeviceChild's SetPrivateData with a custom GUID to get the object + // to delete itself. + wrapped->SetPrivateData(RENDERDOC_DeleteSelf, 0, NULL); + } + } + + objs.clear(); + + // loop again if m_DeadObjects has had some things added - e.g. if we destroyed a view and that + // caused a texture to be added to the dead objects list. Technically this isn't needed, but to + // keep behaviour consistent with the D3D runtime in testing we do this. It should only require + // at most three passes since we only have two layers of dependency (context free'd destroys a + // view, view free'd destroys a resource, resource free'd). + pass++; + if(pass > 3) + break; + } while(!m_DeadObjects.empty()); +} + void WrappedID3D11Device::SetMarker(uint32_t col, const wchar_t *name) { if(m_pCurrentWrappedDevice == NULL) @@ -2314,6 +2389,8 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapper *swapper, UINT SyncInterval, U if(IsBackgroundCapturing(m_State)) RenderDoc::Inst().Tick(); + FlushPendingDead(); + m_pImmediateContext->EndFrame(); m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame @@ -2406,13 +2483,14 @@ void WrappedID3D11Device::CachedObjectsGarbageCollect() { ID3D11DeviceChild *o = *it; - o->AddRef(); - if(o->Release() == 1) + // if there are no external references and only one internal reference then this object should + // have been deleted because that internal reference is the one we added at creation to keep the + // object alive. + if(GetExtRefCount(o) == 0 && GetIntRefCount(o) == 1) { auto eraseit = it; ++it; - o->Release(); - InternalRelease(); + IntRelease(o); m_CachedStateObjects.erase(eraseit); } else @@ -2420,6 +2498,8 @@ void WrappedID3D11Device::CachedObjectsGarbageCollect() ++it; } } + + FlushPendingDead(); } void WrappedID3D11Device::AddDeferredContext(WrappedID3D11DeviceContext *defctx) @@ -2590,29 +2670,6 @@ void WrappedID3D11Device::SetResourceName(ID3D11DeviceChild *pResource, const ch } } -void WrappedID3D11Device::ReleaseResource(ID3D11DeviceChild *res) -{ - ResourceId idx = GetIDForResource(res); - - // wrapped resources get released all the time, we don't want to - // try and slerp in a resource release. Just the explicit ones - if(!IsCaptureMode(m_State)) - { - if(GetResourceManager()->HasLiveResource(idx)) - GetResourceManager()->EraseLiveResource(idx); - return; - } - - SCOPED_LOCK(m_D3DLock); - - if(WrappedID3D11DeviceContext::IsAlloc(res)) - RemoveDeferredContext((WrappedID3D11DeviceContext *)res); - - D3D11ResourceRecord *record = GetResourceManager()->GetResourceRecord(idx); - if(record) - record->Delete(GetResourceManager()); -} - WrappedID3D11DeviceContext *WrappedID3D11Device::GetDeferredContext(size_t idx) { auto it = m_DeferredContexts.begin(); diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 30ceda8eb..10affe0e2 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -154,7 +154,7 @@ struct DummyID3D11InfoQueue : public ID3D11InfoQueue DummyID3D11InfoQueue() : m_pDevice(NULL) {} ////////////////////////////// // implement IUnknown - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { return E_NOINTERFACE; } + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); ULONG STDMETHODCALLTYPE AddRef(); ULONG STDMETHODCALLTYPE Release(); @@ -242,6 +242,152 @@ struct DummyID3D11InfoQueue : public ID3D11InfoQueue virtual BOOL STDMETHODCALLTYPE GetMuteDebugOutput() { return TRUE; } }; +// This one actually works and requires a special GUID to access. We only wrap it +// so we can keep the refcounting on our own device +struct WrappedID3D11InfoQueue : public ID3D11InfoQueue +{ + WrappedID3D11Device *m_pDevice; + ID3D11InfoQueue *m_pReal; + + WrappedID3D11InfoQueue() : m_pDevice(NULL) {} + ////////////////////////////// + // implement IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + ////////////////////////////// + // implement ID3D11InfoQueue + virtual HRESULT STDMETHODCALLTYPE SetMessageCountLimit(UINT64 MessageCountLimit) + { + return m_pReal->SetMessageCountLimit(MessageCountLimit); + } + virtual void STDMETHODCALLTYPE ClearStoredMessages() { return m_pReal->ClearStoredMessages(); } + virtual HRESULT STDMETHODCALLTYPE GetMessage(UINT64 MessageIndex, D3D11_MESSAGE *pMessage, + SIZE_T *pMessageByteLength) + { + return m_pReal->GetMessage(MessageIndex, pMessage, pMessageByteLength); + } + virtual UINT64 STDMETHODCALLTYPE GetNumMessagesAllowedByStorageFilter() + { + return m_pReal->GetNumMessagesAllowedByStorageFilter(); + } + virtual UINT64 STDMETHODCALLTYPE GetNumMessagesDeniedByStorageFilter() + { + return m_pReal->GetNumMessagesDeniedByStorageFilter(); + } + virtual UINT64 STDMETHODCALLTYPE GetNumStoredMessages() + { + return m_pReal->GetNumStoredMessages(); + } + virtual UINT64 STDMETHODCALLTYPE GetNumStoredMessagesAllowedByRetrievalFilter() + { + return m_pReal->GetNumStoredMessagesAllowedByRetrievalFilter(); + } + virtual UINT64 STDMETHODCALLTYPE GetNumMessagesDiscardedByMessageCountLimit() + { + return m_pReal->GetNumMessagesDiscardedByMessageCountLimit(); + } + virtual UINT64 STDMETHODCALLTYPE GetMessageCountLimit() + { + return m_pReal->GetMessageCountLimit(); + } + virtual HRESULT STDMETHODCALLTYPE AddStorageFilterEntries(D3D11_INFO_QUEUE_FILTER *pFilter) + { + return m_pReal->AddStorageFilterEntries(pFilter); + } + virtual HRESULT STDMETHODCALLTYPE GetStorageFilter(D3D11_INFO_QUEUE_FILTER *pFilter, + SIZE_T *pFilterByteLength) + { + return m_pReal->GetStorageFilter(pFilter, pFilterByteLength); + } + virtual void STDMETHODCALLTYPE ClearStorageFilter() { return m_pReal->ClearStorageFilter(); } + virtual HRESULT STDMETHODCALLTYPE PushEmptyStorageFilter() + { + return m_pReal->PushEmptyStorageFilter(); + } + virtual HRESULT STDMETHODCALLTYPE PushCopyOfStorageFilter() + { + return m_pReal->PushCopyOfStorageFilter(); + } + virtual HRESULT STDMETHODCALLTYPE PushStorageFilter(D3D11_INFO_QUEUE_FILTER *pFilter) + { + return m_pReal->PushStorageFilter(pFilter); + } + virtual void STDMETHODCALLTYPE PopStorageFilter() { return m_pReal->PopStorageFilter(); } + virtual UINT STDMETHODCALLTYPE GetStorageFilterStackSize() + { + return m_pReal->GetStorageFilterStackSize(); + } + virtual HRESULT STDMETHODCALLTYPE AddRetrievalFilterEntries(D3D11_INFO_QUEUE_FILTER *pFilter) + { + return m_pReal->AddRetrievalFilterEntries(pFilter); + } + virtual HRESULT STDMETHODCALLTYPE GetRetrievalFilter(D3D11_INFO_QUEUE_FILTER *pFilter, + SIZE_T *pFilterByteLength) + { + return m_pReal->GetRetrievalFilter(pFilter, pFilterByteLength); + } + virtual void STDMETHODCALLTYPE ClearRetrievalFilter() { return m_pReal->ClearRetrievalFilter(); } + virtual HRESULT STDMETHODCALLTYPE PushEmptyRetrievalFilter() + { + return m_pReal->PushEmptyRetrievalFilter(); + } + virtual HRESULT STDMETHODCALLTYPE PushCopyOfRetrievalFilter() + { + return m_pReal->PushCopyOfRetrievalFilter(); + } + virtual HRESULT STDMETHODCALLTYPE PushRetrievalFilter(D3D11_INFO_QUEUE_FILTER *pFilter) + { + return m_pReal->PushRetrievalFilter(pFilter); + } + virtual void STDMETHODCALLTYPE PopRetrievalFilter() { return m_pReal->PopRetrievalFilter(); } + virtual UINT STDMETHODCALLTYPE GetRetrievalFilterStackSize() + { + return m_pReal->GetRetrievalFilterStackSize(); + } + virtual HRESULT STDMETHODCALLTYPE AddMessage(D3D11_MESSAGE_CATEGORY Category, + D3D11_MESSAGE_SEVERITY Severity, D3D11_MESSAGE_ID ID, + LPCSTR pDescription) + { + return m_pReal->AddMessage(Category, Severity, ID, pDescription); + } + virtual HRESULT STDMETHODCALLTYPE AddApplicationMessage(D3D11_MESSAGE_SEVERITY Severity, + LPCSTR pDescription) + { + return m_pReal->AddApplicationMessage(Severity, pDescription); + } + virtual HRESULT STDMETHODCALLTYPE SetBreakOnCategory(D3D11_MESSAGE_CATEGORY Category, BOOL bEnable) + { + return m_pReal->SetBreakOnCategory(Category, bEnable); + } + virtual HRESULT STDMETHODCALLTYPE SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY Severity, BOOL bEnable) + { + return m_pReal->SetBreakOnSeverity(Severity, bEnable); + } + virtual HRESULT STDMETHODCALLTYPE SetBreakOnID(D3D11_MESSAGE_ID ID, BOOL bEnable) + { + return m_pReal->SetBreakOnID(ID, bEnable); + } + virtual BOOL STDMETHODCALLTYPE GetBreakOnCategory(D3D11_MESSAGE_CATEGORY Category) + { + return m_pReal->GetBreakOnCategory(Category); + } + virtual BOOL STDMETHODCALLTYPE GetBreakOnSeverity(D3D11_MESSAGE_SEVERITY Severity) + { + return m_pReal->GetBreakOnSeverity(Severity); + } + virtual BOOL STDMETHODCALLTYPE GetBreakOnID(D3D11_MESSAGE_ID ID) + { + return m_pReal->GetBreakOnID(ID); + } + virtual void STDMETHODCALLTYPE SetMuteDebugOutput(BOOL bMute) + { + return m_pReal->SetMuteDebugOutput(bMute); + } + virtual BOOL STDMETHODCALLTYPE GetMuteDebugOutput() { return m_pReal->GetMuteDebugOutput(); } +}; + // give every impression of working but do nothing. // Same idea as DummyID3D11InfoQueue above, a dummy interface so that users // expecting a ID3D11Debug don't get confused if we have turned off the debug @@ -295,6 +441,7 @@ private: WrappedD3D11Multithread m_WrappedMultithread; DummyID3D11InfoQueue m_DummyInfoQueue; + WrappedID3D11InfoQueue m_WrappedInfoQueue; DummyID3D11Debug m_DummyDebug; WrappedID3D11Debug m_WrappedDebug; WrappedID3D11VideoDevice2 m_WrappedVideo; @@ -302,10 +449,12 @@ private: ID3DUserDefinedAnnotation *m_RealAnnotations; int m_ReplayEventCount; - unsigned int m_InternalRefcount; - RefCounter m_RefCounter; - RefCounter m_SoftRefCounter; - bool m_Alive; + // the device only has one refcount, all device childs take precisely one when they have external + // references (if they lose their external references they release it) and when it reaches 0 the + // device is deleted. + int32_t m_RefCount; + + rdcarray m_DeadObjects; int32_t m_ChunkAtomic; @@ -430,8 +579,6 @@ public: void RemoveDeferredContext(WrappedID3D11DeviceContext *defctx); WrappedID3D11DeviceContext *GetDeferredContext(size_t idx); - void ReleaseResource(ID3D11DeviceChild *pResource); - ResourceId GetResourceID() { return m_ResourceID; } const DrawcallDescription *GetDrawcall(uint32_t eventId); ResourceDescription &GetResourceDesc(ResourceId id); @@ -557,15 +704,8 @@ public: virtual IDXGIResource *WrapExternalDXGIResource(IDXGIResource *res); ResourceId GetBackbufferResourceID() { return m_BBID; } - void InternalRef() { InterlockedIncrement(&m_InternalRefcount); } - void InternalRelease() { InterlockedDecrement(&m_InternalRefcount); } - void SoftRef() { m_SoftRefCounter.AddRef(); } - void SoftRelease() - { - m_SoftRefCounter.Release(); - CheckForDeath(); - } - void CheckForDeath(); + void ReportDeath(ID3D11DeviceChild *obj); + void FlushPendingDead(); //////////////////////////////////////////////////////////////// // Functions for D3D9 hooks to call into (D3DPERF api) @@ -576,12 +716,22 @@ public: ////////////////////////////// // implement IUnknown - ULONG STDMETHODCALLTYPE AddRef() { return m_RefCounter.AddRef(); } + ULONG STDMETHODCALLTYPE AddRef() + { + Atomic::Inc32(&m_RefCount); + return (ULONG)m_RefCount; + } ULONG STDMETHODCALLTYPE Release() { - unsigned int ret = m_RefCounter.Release(); - CheckForDeath(); - return ret; + Atomic::Dec32(&m_RefCount); + ASSERT_REFCOUNT(m_RefCount); + if(m_RefCount == 0) + { + FlushPendingDead(); + delete this; + return 0; + } + return (ULONG)m_RefCount; } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); diff --git a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp index 21ae8d1bd..089ae9e4b 100644 --- a/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device1_wrap.cpp @@ -147,8 +147,7 @@ HRESULT WrappedID3D11Device::CreateBlendState1(const D3D11_BLEND_DESC1 *pBlendSt { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } @@ -256,8 +255,7 @@ HRESULT WrappedID3D11Device::CreateRasterizerState1(const D3D11_RASTERIZER_DESC1 { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } diff --git a/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp index a0bd7b401..2bd90673e 100644 --- a/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device3_wrap.cpp @@ -819,8 +819,7 @@ HRESULT WrappedID3D11Device::CreateRasterizerState2(const D3D11_RASTERIZER_DESC2 { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } diff --git a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp index 6f8c7eb65..e36ec5bfe 100644 --- a/renderdoc/driver/d3d11/d3d11_device_wrap.cpp +++ b/renderdoc/driver/d3d11/d3d11_device_wrap.cpp @@ -210,6 +210,7 @@ HRESULT WrappedID3D11Device::CreateBuffer(const D3D11_BUFFER_DESC *pDesc, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Buffer(real, pDesc->ByteWidth, this); if(IsCaptureMode(m_State)) @@ -463,6 +464,7 @@ HRESULT WrappedID3D11Device::CreateTexture1D(const D3D11_TEXTURE1D_DESC *pDesc, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Texture1D(real, this); if(IsCaptureMode(m_State)) @@ -598,6 +600,7 @@ HRESULT WrappedID3D11Device::CreateTexture2D(const D3D11_TEXTURE2D_DESC *pDesc, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Texture2D1(real, this); if(IsCaptureMode(m_State)) @@ -732,6 +735,7 @@ HRESULT WrappedID3D11Device::CreateTexture3D(const D3D11_TEXTURE3D_DESC *pDesc, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Texture3D1(real, this); if(IsCaptureMode(m_State)) @@ -860,6 +864,7 @@ HRESULT WrappedID3D11Device::CreateShaderResourceView(ID3D11Resource *pResource, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11ShaderResourceView1(real, pResource, this); Chunk *chunk = NULL; @@ -1006,6 +1011,7 @@ HRESULT WrappedID3D11Device::CreateUnorderedAccessView(ID3D11Resource *pResource { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11UnorderedAccessView1(real, pResource, this); Chunk *chunk = NULL; @@ -1148,6 +1154,7 @@ HRESULT WrappedID3D11Device::CreateRenderTargetView(ID3D11Resource *pResource, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11RenderTargetView1(real, pResource, this); Chunk *chunk = NULL; @@ -1252,6 +1259,7 @@ HRESULT WrappedID3D11Device::CreateDepthStencilView(ID3D11Resource *pResource, { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11DepthStencilView(real, pResource, this); Chunk *chunk = NULL; @@ -1373,6 +1381,7 @@ HRESULT WrappedID3D11Device::CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC *p { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11InputLayout(real, this); if(IsCaptureMode(m_State)) @@ -1461,6 +1470,7 @@ HRESULT WrappedID3D11Device::CreateVertexShader(const void *pShaderBytecode, SIZ { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -1557,6 +1567,7 @@ HRESULT WrappedID3D11Device::CreateGeometryShader(const void *pShaderBytecode, S { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -1663,6 +1674,7 @@ HRESULT WrappedID3D11Device::CreateGeometryShaderWithStreamOutput( { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -1760,6 +1772,7 @@ HRESULT WrappedID3D11Device::CreatePixelShader(const void *pShaderBytecode, SIZE { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -1853,6 +1866,7 @@ HRESULT WrappedID3D11Device::CreateHullShader(const void *pShaderBytecode, SIZE_ { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -1948,6 +1962,7 @@ HRESULT WrappedID3D11Device::CreateDomainShader(const void *pShaderBytecode, SIZ { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -2044,6 +2059,7 @@ HRESULT WrappedID3D11Device::CreateComputeShader(const void *pShaderBytecode, SI { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Shader( real, ResourceId(), (const byte *)pShaderBytecode, BytecodeLength, this); @@ -2117,6 +2133,7 @@ bool WrappedID3D11Device::Serialise_CreateClassInstance(SerialiserType &ser, LPC } else { + FlushPendingDead(); wrapped = new WrappedID3D11ClassInstance(real, pClassLinkage, this); GetResourceManager()->AddLiveResource(pInstance, wrapped); @@ -2139,6 +2156,7 @@ ID3D11ClassInstance *WrappedID3D11Device::CreateClassInstance( { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11ClassInstance(*ppInstance, pClassLinkage, this); { @@ -2188,6 +2206,7 @@ bool WrappedID3D11Device::Serialise_GetClassInstance(SerialiserType &ser, LPCSTR } else { + FlushPendingDead(); wrapped = new WrappedID3D11ClassInstance(real, pClassLinkage, this); GetResourceManager()->AddLiveResource(pInstance, wrapped); @@ -2211,6 +2230,7 @@ ID3D11ClassInstance *WrappedID3D11Device::GetClassInstance(LPCSTR pClassInstance { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11ClassInstance(*ppInstance, pClassLinkage, this); { @@ -2275,6 +2295,7 @@ HRESULT WrappedID3D11Device::CreateClassLinkage(ID3D11ClassLinkage **ppLinkage) { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11ClassLinkage(real, this); if(IsCaptureMode(m_State)) @@ -2361,14 +2382,16 @@ HRESULT WrappedID3D11Device::CreateBlendState(const D3D11_BLEND_DESC *pBlendStat return ret; } + FlushPendingDead(); ID3D11BlendState *wrapped = new WrappedID3D11BlendState1(real, this); CachedObjectsGarbageCollect(); { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + // add an internal reference too just so if the application creates and destroys state objects + // at high frequency we keep it around. + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } @@ -2465,14 +2488,14 @@ HRESULT WrappedID3D11Device::CreateDepthStencilState(const D3D11_DEPTH_STENCIL_D return ret; } + FlushPendingDead(); ID3D11DepthStencilState *wrapped = new WrappedID3D11DepthStencilState(real, this); CachedObjectsGarbageCollect(); { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } @@ -2569,14 +2592,14 @@ HRESULT WrappedID3D11Device::CreateRasterizerState(const D3D11_RASTERIZER_DESC * return ret; } + FlushPendingDead(); ID3D11RasterizerState *wrapped = new WrappedID3D11RasterizerState2(real, this); CachedObjectsGarbageCollect(); { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } @@ -2673,14 +2696,14 @@ HRESULT WrappedID3D11Device::CreateSamplerState(const D3D11_SAMPLER_DESC *pSampl return ret; } + FlushPendingDead(); ID3D11SamplerState *wrapped = new WrappedID3D11SamplerState(real, this); CachedObjectsGarbageCollect(); { RDCASSERT(m_CachedStateObjects.find(wrapped) == m_CachedStateObjects.end()); - wrapped->AddRef(); - InternalRef(); + IntAddRef(wrapped); m_CachedStateObjects.insert(wrapped); } @@ -2756,6 +2779,7 @@ HRESULT WrappedID3D11Device::CreateQuery(const D3D11_QUERY_DESC *pQueryDesc, ID3 { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Query1(real, this); if(IsCaptureMode(m_State)) @@ -2842,6 +2866,7 @@ HRESULT WrappedID3D11Device::CreatePredicate(const D3D11_QUERY_DESC *pPredicateD { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Predicate(real, this); if(IsCaptureMode(m_State)) @@ -2923,6 +2948,7 @@ HRESULT WrappedID3D11Device::CreateCounter(const D3D11_COUNTER_DESC *pCounterDes { SCOPED_LOCK(m_D3DLock); + FlushPendingDead(); wrapped = new WrappedID3D11Counter(real, this); if(IsCaptureMode(m_State)) diff --git a/renderdoc/driver/d3d11/d3d11_hooks.cpp b/renderdoc/driver/d3d11/d3d11_hooks.cpp index 004a691a7..18d885795 100644 --- a/renderdoc/driver/d3d11/d3d11_hooks.cpp +++ b/renderdoc/driver/d3d11/d3d11_hooks.cpp @@ -225,9 +225,8 @@ private: dummyUsed = true; } - HRESULT ret = - real(pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, - pUsedSwapDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext); + HRESULT ret = real(pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, + SDKVersion, pUsedSwapDesc, ppSwapChain, ppDevice, pFeatureLevel, NULL); SAFE_RELEASE(dummydev); if(dummyUsed) @@ -263,12 +262,7 @@ private: *ppDevice = wrap; - if(ppImmediateContext) - { - if(*ppImmediateContext) - (*ppImmediateContext)->Release(); - wrap->GetImmediateContext(ppImmediateContext); - } + wrap->GetImmediateContext(ppImmediateContext); if(ppSwapChain && *ppSwapChain) *ppSwapChain = new WrappedIDXGISwapChain4( diff --git a/renderdoc/driver/d3d11/d3d11_renderstate.cpp b/renderdoc/driver/d3d11/d3d11_renderstate.cpp index b6852049d..46e50bf39 100644 --- a/renderdoc/driver/d3d11/d3d11_renderstate.cpp +++ b/renderdoc/driver/d3d11/d3d11_renderstate.cpp @@ -100,54 +100,54 @@ D3D11RenderState::~D3D11RenderState() void D3D11RenderState::ReleaseRefs() { - ReleaseRef(IA.IndexBuffer); - ReleaseRef(IA.Layout); + IntRelease(IA.IndexBuffer); + IntRelease(IA.Layout); for(UINT i = 0; i < D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT; i++) - ReleaseRef(IA.VBs[i]); + IntRelease(IA.VBs[i]); Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; for(int s = 0; s < 6; s++) { Shader *sh = stages[s]; - ReleaseRef(sh->Object); + IntRelease(sh->Object); for(UINT i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++) - ReleaseRef(sh->ConstantBuffers[i]); + IntRelease(sh->ConstantBuffers[i]); for(UINT i = 0; i < D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; i++) - ReleaseRef(sh->Samplers[i]); + IntRelease(sh->Samplers[i]); for(UINT i = 0; i < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; i++) - ReleaseRef(sh->SRVs[i]); + IntRelease(sh->SRVs[i]); for(UINT i = 0; i < D3D11_SHADER_MAX_INTERFACES; i++) - ReleaseRef(sh->Instances[i]); + IntRelease(sh->Instances[i]); sh++; } for(UINT i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++) - ReleaseRef(CSUAVs[i]); + IntRelease(CSUAVs[i]); for(UINT i = 0; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) - ReleaseRef(SO.Buffers[i]); + IntRelease(SO.Buffers[i]); - ReleaseRef(RS.State); + IntRelease(RS.State); - ReleaseRef(OM.BlendState); - ReleaseRef(OM.DepthStencilState); + IntRelease(OM.BlendState); + IntRelease(OM.DepthStencilState); for(UINT i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) - ReleaseRef(OM.RenderTargets[i]); + IntRelease(OM.RenderTargets[i]); for(UINT i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++) - ReleaseRef(OM.UAVs[i]); + IntRelease(OM.UAVs[i]); - ReleaseRef(OM.DepthView); + IntRelease(OM.DepthView); - ReleaseRef(Predicate); + IntRelease(Predicate); RDCEraseEl(IA); RDCEraseEl(VS); @@ -276,54 +276,54 @@ void D3D11RenderState::MarkReferenced(WrappedID3D11DeviceContext *ctx, bool init void D3D11RenderState::AddRefs() { - TakeRef(IA.IndexBuffer); - TakeRef(IA.Layout); + IntAddRef(IA.IndexBuffer); + IntAddRef(IA.Layout); for(UINT i = 0; i < D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT; i++) - TakeRef(IA.VBs[i]); + IntAddRef(IA.VBs[i]); Shader *stages[] = {&VS, &HS, &DS, &GS, &PS, &CS}; for(int s = 0; s < 6; s++) { Shader *sh = stages[s]; - TakeRef(sh->Object); + IntAddRef(sh->Object); for(UINT i = 0; i < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT; i++) - TakeRef(sh->ConstantBuffers[i]); + IntAddRef(sh->ConstantBuffers[i]); for(UINT i = 0; i < D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; i++) - TakeRef(sh->Samplers[i]); + IntAddRef(sh->Samplers[i]); for(UINT i = 0; i < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT; i++) - TakeRef(sh->SRVs[i]); + IntAddRef(sh->SRVs[i]); for(UINT i = 0; i < D3D11_SHADER_MAX_INTERFACES; i++) - TakeRef(sh->Instances[i]); + IntAddRef(sh->Instances[i]); sh++; } for(UINT i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++) - TakeRef(CSUAVs[i]); + IntAddRef(CSUAVs[i]); for(UINT i = 0; i < D3D11_SO_BUFFER_SLOT_COUNT; i++) - TakeRef(SO.Buffers[i]); + IntAddRef(SO.Buffers[i]); - TakeRef(RS.State); + IntAddRef(RS.State); - TakeRef(OM.BlendState); - TakeRef(OM.DepthStencilState); + IntAddRef(OM.BlendState); + IntAddRef(OM.DepthStencilState); for(UINT i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) - TakeRef(OM.RenderTargets[i]); + IntAddRef(OM.RenderTargets[i]); for(UINT i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++) - TakeRef(OM.UAVs[i]); + IntAddRef(OM.UAVs[i]); - TakeRef(OM.DepthView); + IntAddRef(OM.DepthView); - TakeRef(Predicate); + IntAddRef(Predicate); } D3D11RenderState::D3D11RenderState(WrappedID3D11DeviceContext *context) @@ -561,78 +561,6 @@ void D3D11RenderState::ApplyState(WrappedID3D11DeviceContext *context) const context->SetPredication(Predicate, PredicateValue); } -void D3D11RenderState::TakeRef(ID3D11DeviceChild *p) -{ - if(p) - { - p->AddRef(); - if(m_ImmediatePipeline) - { - if(WrappedID3D11RenderTargetView1::IsAlloc(p) || WrappedID3D11ShaderResourceView1::IsAlloc(p) || - WrappedID3D11DepthStencilView::IsAlloc(p) || WrappedID3D11UnorderedAccessView1::IsAlloc(p)) - m_pDevice->InternalRef(); - - m_pDevice->InternalRef(); - - // we can use any specialisation of device child here, as all that is templated - // is the nested pointer type. Saves having another class in the inheritance - // heirarchy :( - ((WrappedDeviceChild11 *)p)->PipelineAddRef(); - } - } -} - -void D3D11RenderState::ReleaseRef(ID3D11DeviceChild *p) -{ - if(p) - { - p->Release(); - if(m_ImmediatePipeline) - { - if(WrappedID3D11RenderTargetView1::IsAlloc(p) || WrappedID3D11ShaderResourceView1::IsAlloc(p) || - WrappedID3D11DepthStencilView::IsAlloc(p) || WrappedID3D11UnorderedAccessView1::IsAlloc(p)) - m_pDevice->InternalRelease(); - - m_pDevice->InternalRelease(); - - // see above - ((WrappedDeviceChild11 *)p)->PipelineRelease(); - } - } -} - -// these are overloads that take a buffer to skip the view IsAlloc checks - -void D3D11RenderState::TakeRef(ID3D11Buffer *p) -{ - if(p) - { - p->AddRef(); - if(m_ImmediatePipeline) - { - m_pDevice->InternalRef(); - - // see above - ((WrappedDeviceChild11 *)p)->PipelineAddRef(); - } - } -} - -void D3D11RenderState::ReleaseRef(ID3D11Buffer *p) -{ - if(p) - { - p->Release(); - if(m_ImmediatePipeline) - { - m_pDevice->InternalRelease(); - - // see above - ((WrappedDeviceChild11 *)p)->PipelineRelease(); - } - } -} - bool D3D11RenderState::IsRangeBoundForWrite(const ResourceRange &range) { for(UINT i = 0; i < D3D11_1_UAV_SLOT_COUNT; i++) @@ -707,7 +635,7 @@ void D3D11RenderState::UnbindRangeForWrite(const ResourceRange &range) { if(CSUAVs[i] && range.Intersects(GetResourceRange(CSUAVs[i]))) { - ReleaseRef(CSUAVs[i]); + IntRelease(CSUAVs[i]); CSUAVs[i] = NULL; } } @@ -716,7 +644,7 @@ void D3D11RenderState::UnbindRangeForWrite(const ResourceRange &range) { if(SO.Buffers[i] && range.Intersects(ResourceRange(SO.Buffers[i]))) { - ReleaseRef(SO.Buffers[i]); + IntRelease(SO.Buffers[i]); SO.Buffers[i] = NULL; } } @@ -725,7 +653,7 @@ void D3D11RenderState::UnbindRangeForWrite(const ResourceRange &range) { if(OM.RenderTargets[i] && range.Intersects(GetResourceRange(OM.RenderTargets[i]))) { - ReleaseRef(OM.RenderTargets[i]); + IntRelease(OM.RenderTargets[i]); OM.RenderTargets[i] = NULL; } } @@ -734,14 +662,14 @@ void D3D11RenderState::UnbindRangeForWrite(const ResourceRange &range) { if(OM.UAVs[i] && range.Intersects(GetResourceRange(OM.UAVs[i]))) { - ReleaseRef(OM.UAVs[i]); + IntRelease(OM.UAVs[i]); OM.UAVs[i] = NULL; } } if(OM.DepthView && range.Intersects(GetResourceRange(OM.DepthView))) { - ReleaseRef(OM.DepthView); + IntRelease(OM.DepthView); OM.DepthView = NULL; } } @@ -753,7 +681,7 @@ void D3D11RenderState::UnbindRangeForRead(const ResourceRange &range) if(IA.VBs[i] && range.Intersects(ResourceRange(IA.VBs[i]))) { // RDCDEBUG("Resource was bound on IA VB %u", i); - ReleaseRef(IA.VBs[i]); + IntRelease(IA.VBs[i]); IA.VBs[i] = NULL; } } @@ -761,7 +689,7 @@ void D3D11RenderState::UnbindRangeForRead(const ResourceRange &range) if(IA.IndexBuffer && range.Intersects(ResourceRange(IA.IndexBuffer))) { // RDCDEBUG("Resource was bound on IA IB"); - ReleaseRef(IA.IndexBuffer); + IntRelease(IA.IndexBuffer); IA.IndexBuffer = NULL; } @@ -776,7 +704,7 @@ void D3D11RenderState::UnbindRangeForRead(const ResourceRange &range) if(sh->ConstantBuffers[i] && range.Intersects(ResourceRange(sh->ConstantBuffers[i]))) { // RDCDEBUG("Resource was bound on %s CB %u", names[s], i); - ReleaseRef(sh->ConstantBuffers[i]); + IntRelease(sh->ConstantBuffers[i]); sh->ConstantBuffers[i] = NULL; } } @@ -803,7 +731,7 @@ void D3D11RenderState::UnbindRangeForRead(const ResourceRange &range) else { // RDCDEBUG("Unbinding."); - ReleaseRef(sh->SRVs[i]); + IntRelease(sh->SRVs[i]); sh->SRVs[i] = NULL; } } diff --git a/renderdoc/driver/d3d11/d3d11_renderstate.h b/renderdoc/driver/d3d11/d3d11_renderstate.h index 31cbe1cec..713611cff 100644 --- a/renderdoc/driver/d3d11/d3d11_renderstate.h +++ b/renderdoc/driver/d3d11/d3d11_renderstate.h @@ -94,12 +94,6 @@ struct D3D11RenderState ///////////////////////////////////////////////////////////////////////// // Utility functions to swap resources around, removing and adding refs - void TakeRef(ID3D11DeviceChild *p); - void ReleaseRef(ID3D11DeviceChild *p); - - void TakeRef(ID3D11Buffer *p); - void ReleaseRef(ID3D11Buffer *p); - template void ChangeRefRead(T *&stateItem, T *newItem) { @@ -110,7 +104,7 @@ struct D3D11RenderState // release the old item, which may destroy it but we won't use it again as we know is not the // same as the new item. - ReleaseRef(stateItem); + IntRelease(stateItem); // assign the new item, but don't ref it yet stateItem = newItem; @@ -123,37 +117,36 @@ struct D3D11RenderState } // finally we take the ref on the new item - TakeRef(stateItem); + IntAddRef(stateItem); } template void ChangeRefRead(T **stateArray, T *const *newArray, size_t offset, size_t num) { - // addref the whole array so none of it can be destroyed during processing - for(size_t i = 0; i < num; i++) - if(newArray[i]) - newArray[i]->AddRef(); - + // we don't really care if some objects temporarily hit 0 ext and int refcounts while changing + // hands. Consider the case: + // + // previous: [0] = A [1] = B + // bind: [0] = B [1] = A + // + // after processing slot 0, B has two refs (leftover [1] and updated [0]) and A has no refs. If + // A also has no external references this may look like it can be deleted, but all that will + // happen is the death will be reported to the device. By the time the device actually processes + // it the object will have an intref again. for(size_t i = 0; i < num; i++) ChangeRefRead(stateArray[offset + i], newArray[i]); - - // release the ref we added above - for(size_t i = 0; i < num; i++) - if(newArray[i]) - newArray[i]->Release(); } template void ChangeRefWrite(T *&stateItem, T *newItem) { - // don't do anything for redundant changes. This prevents the object from bouncing off refcount - // 0 during the changeover if it's only bound once, has no external refcount. + // don't do anything for redundant changes if(stateItem == newItem) return; // release the old item, which may destroy it but we won't use it again as we know is not the // same as the new item. We NULL it out so that it doesn't get unbound again below - ReleaseRef(stateItem); + IntRelease(stateItem); stateItem = NULL; // if we're not binding NULL, then unbind any other conflicting uses @@ -166,24 +159,14 @@ struct D3D11RenderState // now bind the new item and ref it stateItem = newItem; - TakeRef(stateItem); + IntAddRef(stateItem); } template void ChangeRefWrite(T **stateArray, T *const *newArray, size_t offset, size_t num) { - // addref the whole array so none of it can be destroyed during processing - for(size_t i = 0; i < num; i++) - if(newArray[i]) - newArray[i]->AddRef(); - for(size_t i = 0; i < num; i++) ChangeRefWrite(stateArray[offset + i], newArray[i]); - - // release the ref we added above - for(size_t i = 0; i < num; i++) - if(newArray[i]) - newArray[i]->Release(); } template diff --git a/renderdoc/driver/d3d11/d3d11_rendertext.cpp b/renderdoc/driver/d3d11/d3d11_rendertext.cpp index 5d5910b9a..f34631105 100644 --- a/renderdoc/driver/d3d11/d3d11_rendertext.cpp +++ b/renderdoc/driver/d3d11/d3d11_rendertext.cpp @@ -37,6 +37,16 @@ static Vec2f quadPos[] = { Vec2f(0.0f, 0.0f), Vec2f(1.0f, 0.0f), Vec2f(0.0f, 1.0f), Vec2f(1.0f, 1.0f), }; +// see d3d11_debug.cpp D3D11DebugManager::D3D11DebugManager for why we use this helper like this +static void InternalRef(ID3D11DeviceChild *child) +{ + if(child) + { + IntAddRef(child); + child->Release(); + } +} + D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) { if(RenderDoc::Inst().GetCrashHandler()) @@ -68,8 +78,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create font blendstate HRESULT: %s", ToStr(hr).c_str()); - if(BlendState) - m_pDevice->InternalRef(); + InternalRef(BlendState); rm->SetInternalResource(BlendState); D3D11_SAMPLER_DESC sampDesc; @@ -87,8 +96,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create linear sampler state HRESULT: %s", ToStr(hr).c_str()); - if(LinearSampler) - m_pDevice->InternalRef(); + InternalRef(LinearSampler); rm->SetInternalResource(LinearSampler); D3D11_TEXTURE2D_DESC desc; @@ -145,8 +153,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create debugTex HRESULT: %s", ToStr(hr).c_str()); - if(debugTex) - m_pDevice->InternalRef(); + InternalRef(debugTex); rm->SetInternalResource(debugTex); delete[] buf; @@ -156,11 +163,10 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create Tex HRESULT: %s", ToStr(hr).c_str()); - if(Tex) - m_pDevice->InternalRef(); + InternalRef(Tex); rm->SetInternalResource(Tex); - SAFE_RELEASE(debugTex); + SAFE_INTRELEASE(debugTex); Vec4f glyphData[2 * (numChars + 1)]; @@ -178,8 +184,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create font GlyphData HRESULT: %s", ToStr(hr).c_str()); - if(GlyphData) - m_pDevice->InternalRef(); + InternalRef(GlyphData); rm->SetInternalResource(GlyphData); for(int i = 0; i < numChars; i++) @@ -215,8 +220,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create font CBuffer HRESULT: %s", ToStr(hr).c_str()); - if(CBuffer) - m_pDevice->InternalRef(); + InternalRef(CBuffer); rm->SetInternalResource(CBuffer); cbufDesc.ByteWidth = (2 + FONT_MAX_CHARS) * sizeof(uint32_t) * 4; @@ -225,8 +229,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create font CharBuffer HRESULT: %s", ToStr(hr).c_str()); - if(CharBuffer) - m_pDevice->InternalRef(); + InternalRef(CharBuffer); rm->SetInternalResource(CharBuffer); rdcstr hlsl = GetEmbeddedResource(text_hlsl); @@ -239,14 +242,12 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) { VS = shaderCache->MakeVShader(hlsl.c_str(), "RENDERDOC_TextVS", "vs_4_0"); - if(VS) - m_pDevice->InternalRef(); + InternalRef(VS); rm->SetInternalResource(VS); PS = shaderCache->MakePShader(hlsl.c_str(), "RENDERDOC_TextPS", "ps_4_0"); - if(PS) - m_pDevice->InternalRef(); + InternalRef(PS); rm->SetInternalResource(PS); } else @@ -261,18 +262,15 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) VS = shaderCache->MakeVShader(hlsl.c_str(), "RENDERDOC_Text9VS", "vs_4_0_level_9_0", 1, inputs, &Layout); - if(VS) - m_pDevice->InternalRef(); + InternalRef(VS); rm->SetInternalResource(VS); - if(Layout) - m_pDevice->InternalRef(); + InternalRef(Layout); rm->SetInternalResource(Layout); PS = shaderCache->MakePShader(hlsl.c_str(), "RENDERDOC_TextPS", "ps_4_0_level_9_0"); - if(PS) - m_pDevice->InternalRef(); + InternalRef(PS); rm->SetInternalResource(PS); // these buffers are immutable because they're just replacing the fixed shader generation on @@ -290,8 +288,7 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) if(FAILED(hr)) RDCERR("Failed to create FL9 text PosBuffer HRESULT: %s", ToStr(hr).c_str()); - if(FL9Buffer) - m_pDevice->InternalRef(); + InternalRef(FL9Buffer); rm->SetInternalResource(FL9Buffer); } @@ -300,27 +297,17 @@ D3D11TextRenderer::D3D11TextRenderer(WrappedID3D11Device *wrapper) D3D11TextRenderer::~D3D11TextRenderer() { - SAFE_RELEASE(Tex); - m_pDevice->InternalRelease(); - SAFE_RELEASE(LinearSampler); - m_pDevice->InternalRelease(); - SAFE_RELEASE(BlendState); - m_pDevice->InternalRelease(); - SAFE_RELEASE(CBuffer); - m_pDevice->InternalRelease(); - SAFE_RELEASE(GlyphData); - m_pDevice->InternalRelease(); - SAFE_RELEASE(CharBuffer); - m_pDevice->InternalRelease(); - SAFE_RELEASE(VS); - m_pDevice->InternalRelease(); - SAFE_RELEASE(PS); - m_pDevice->InternalRelease(); + SAFE_INTRELEASE(Tex); + SAFE_INTRELEASE(LinearSampler); + SAFE_INTRELEASE(BlendState); + SAFE_INTRELEASE(CBuffer); + SAFE_INTRELEASE(GlyphData); + SAFE_INTRELEASE(CharBuffer); + SAFE_INTRELEASE(VS); + SAFE_INTRELEASE(PS); - SAFE_RELEASE(Layout); - m_pDevice->InternalRelease(); - SAFE_RELEASE(FL9Buffer); - m_pDevice->InternalRelease(); + SAFE_INTRELEASE(Layout); + SAFE_INTRELEASE(FL9Buffer); if(RenderDoc::Inst().GetCrashHandler()) RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this); diff --git a/renderdoc/driver/d3d11/d3d11_resources.cpp b/renderdoc/driver/d3d11/d3d11_resources.cpp index 77f82cb70..57dfc839b 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.cpp +++ b/renderdoc/driver/d3d11/d3d11_resources.cpp @@ -70,6 +70,12 @@ rdcarray WrappedID3DDeviceContextState::m_List; Threading::CriticalSection WrappedID3DDeviceContextState::m_Lock; const GUID RENDERDOC_ID3D11ShaderGUID_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct; +const GUID RENDERDOC_DeleteSelf = { + 0x1e4bf855, + 0xcc83, + 0x4b7a, + {0x91, 0x8a, 0xd6, 0x64, 0x56, 0x7c, 0xdd, 0x40}, +}; void WrappedShader::ShaderEntry::BuildReflection() { @@ -708,46 +714,6 @@ void GetDXTextureProperties(void *dxObject, ResourceFormat &fmt, uint32_t &width RDCERR("Getting DX texture properties for unknown/unhandled objects %p", dxObject); } -HRESULT STDMETHODCALLTYPE RefCounter::QueryInterface( - /* [in] */ REFIID riid, - /* [annotation][iid_is][out] */ - __RPC__deref_out void **ppvObject) -{ - return RefCountDXGIObject::WrapQueryInterface(m_pReal, riid, ppvObject); -} - -unsigned int RefCounter::SoftRef(WrappedID3D11Device *device) -{ - unsigned int ret = AddRef(); - if(device) - device->SoftRef(); - else - RDCWARN("No device pointer, is a deleted resource being AddRef()d?"); - return ret; -} - -unsigned int RefCounter::SoftRelease(WrappedID3D11Device *device) -{ - unsigned int ret = Release(); - if(device) - device->SoftRelease(); - else - RDCWARN("No device pointer, is a deleted resource being Release()d?"); - return ret; -} - -void RefCounter::AddDeviceSoftref(WrappedID3D11Device *device) -{ - if(device) - device->SoftRef(); -} - -void RefCounter::ReleaseDeviceSoftref(WrappedID3D11Device *device) -{ - if(device) - device->SoftRelease(); -} - WrappedID3DDeviceContextState::WrappedID3DDeviceContextState(ID3DDeviceContextState *real, WrappedID3D11Device *device) : WrappedDeviceChild11(real, device) @@ -763,7 +729,6 @@ WrappedID3DDeviceContextState::WrappedID3DDeviceContextState(ID3DDeviceContextSt WrappedID3DDeviceContextState::~WrappedID3DDeviceContextState() { SAFE_DELETE(state); - Shutdown(); { SCOPED_LOCK(WrappedID3DDeviceContextState::m_Lock); diff --git a/renderdoc/driver/d3d11/d3d11_resources.h b/renderdoc/driver/d3d11/d3d11_resources.h index de67ccecc..e6aef9869 100644 --- a/renderdoc/driver/d3d11/d3d11_resources.h +++ b/renderdoc/driver/d3d11/d3d11_resources.h @@ -74,32 +74,61 @@ bool CanQuery(base *b) return SUCCEEDED(check) && d != NULL; } -class TrackedResource -{ -public: - TrackedResource() { m_ID = ResourceIDGen::GetNewUniqueID(); } - ResourceId GetResourceID() { return m_ID; } -private: - TrackedResource(const TrackedResource &); - TrackedResource &operator=(const TrackedResource &); - - ResourceId m_ID; -}; - extern const GUID RENDERDOC_ID3D11ShaderGUID_ShaderDebugMagicValue; +extern const GUID RENDERDOC_DeleteSelf; template -class WrappedDeviceChild11 : public RefCounter, public NestedType2, public TrackedResource +class WrappedDeviceChild11 : public NestedType2 { +private: + ResourceId m_ID; + + ////////////////////////////////////////////////////////////////////////// + // D3D11's refcounting behaviour is incredibly messy, with several cycles possible: + // + // All ID3D11DeviceChild objects can query back the device. + // The device can query its immediate context + // Contexts can query objects currently bound to them + // Views can query out the resource they point to + // + // Adding to this, some games check the refcount on objects expecting it to be a certain value, + // which restricts how we can refcount. E.g. the immediate context can't have a refcount on its + // bound objects that applications can see, or some will break. + // + // By experimentation, all ID3D11DeviceChild objects that have a reference held by the application + // also hold one reference on the device. This means there's surprising behaviour where the device + // refcount can bounce up and down when child objects hit refcount 0 while still being alive + // (which is quite possible - bind a VB and then release it, its refcount is 0 but it's alive and + // will come back to 1 if you query it out again). This reference is externally visible so the + // ID3D11Device has more refcounts than the application "knows" about. + // + // All other references seem invisible to the application - views on the resource, context on + // bound objects, device on the immediate context. + // + // So we clone D3D's internal implementation of having an external ref (user facing) and an + // internal ref. Objects are only deleted when both are zero, and even then we defer destruction + // to avoid needing excessive extra refcounting when temporarily changing bindings. + // + // This also means the device is released if and only if its external ref count hits 0. That means + // that the user has no access to the device or any of its children so the whole cycle can be + // cleaned up + // + // See D3D11RenderState and the D3D11_Refcount_Check test. + + int32_t m_ExtRef; + int32_t m_IntRef; + protected: WrappedID3D11Device *m_pDevice; NestedType *m_pReal; - unsigned int m_PipelineRefs; WrappedDeviceChild11(NestedType *real, WrappedID3D11Device *device) - : RefCounter(real), m_pDevice(device), m_pReal(real), m_PipelineRefs(0) + : m_pDevice(device), m_pReal(real), m_ExtRef(1), m_IntRef(0) { - m_pDevice->SoftRef(); + m_ID = ResourceIDGen::GetNewUniqueID(); + + // start off with a strong reference on the device. + m_pDevice->AddRef(); bool ret = m_pDevice->GetResourceManager()->AddWrapper(this, real); if(!ret) @@ -108,20 +137,11 @@ protected: m_pDevice->GetResourceManager()->AddCurrentResource(GetResourceID(), this); } - void Shutdown() - { - m_pDevice->GetResourceManager()->RemoveWrapper(m_pReal); - m_pDevice->GetResourceManager()->ReleaseCurrentResource(GetResourceID()); - m_pDevice->ReleaseResource((NestedType *)this); - SAFE_RELEASE(m_pReal); - m_pDevice = NULL; - } - virtual ~WrappedDeviceChild11() { - // should have already called shutdown (needs to be called from child class to ensure - // vtables are still in place when we call ReleaseResource) - RDCASSERT(m_pDevice == NULL && m_pReal == NULL); + SAFE_RELEASE(m_pReal); + // we removed the reference to the device before releasing ourselves, so just NULL it out. + m_pDevice = NULL; } public: @@ -129,16 +149,55 @@ public: typedef NestedType1 InnerType1; typedef NestedType2 InnerType2; + ResourceId GetResourceID() { return m_ID; } NestedType *GetReal() { return m_pReal; } - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter::SoftRef(m_pDevice) - m_PipelineRefs; } + // internal addref/release + void IntAddRef() { Atomic::Inc32(&m_IntRef); } + void IntRelease() + { + Atomic::Dec32(&m_IntRef); + ASSERT_REFCOUNT(m_IntRef); + // due to deferred destruction, report our death but don't immediately delete ourselves. If + // we're still dead when the device reaps the list of deaths, we'll be deleted. + if(m_IntRef + m_ExtRef == 0) + m_pDevice->ReportDeath(this); + } + int32_t GetExtRefCount() { return m_ExtRef; } + int32_t GetIntRefCount() { return m_IntRef; } + ////////////////////////////// + // implement IUnknown + + ULONG STDMETHODCALLTYPE AddRef() + { + // if we're about to create a new external reference on this object, add back our reference on + // the device + if(m_ExtRef == 0) + m_pDevice->AddRef(); + Atomic::Inc32(&m_ExtRef); + return (ULONG)m_ExtRef; + } ULONG STDMETHODCALLTYPE Release() { - unsigned int piperefs = m_PipelineRefs; - return RefCounter::SoftRelease(m_pDevice) - piperefs; - } + Atomic::Dec32(&m_ExtRef); + ASSERT_REFCOUNT(m_ExtRef); - void PipelineAddRef() { InterlockedIncrement(&m_PipelineRefs); } - void PipelineRelease() { InterlockedDecrement(&m_PipelineRefs); } + WrappedID3D11Device *dev = m_pDevice; + + int32_t intRef = m_IntRef; + int32_t extRef = m_ExtRef; + + // report our own death first, so that if we're about to release the last external reference on + // the device below that we are ready to be cleaned up. + if(intRef + extRef == 0) + dev->ReportDeath(this); + + // if we just released the last external reference on this object, release our reference on the + // device. + if(extRef == 0) + dev->Release(); + + return (ULONG)extRef; + } HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(IUnknown)) @@ -260,7 +319,7 @@ public: return S_OK; } - return RefCounter::QueryInterface(riid, ppvObject); + return RefCountDXGIObject::WrapQueryInterface(m_pReal, riid, ppvObject); } ////////////////////////////// @@ -299,6 +358,12 @@ public: if(guid == RENDERDOC_ID3D11ShaderGUID_ShaderDebugMagicValue) return m_pDevice->SetShaderDebugPath(this, (const char *)pData); + if(guid == RENDERDOC_DeleteSelf) + { + delete this; + return S_OK; + } + if(guid == WKPDID_D3DDebugObjectName) { const char *pStrData = (const char *)pData; @@ -333,66 +398,52 @@ public: } }; +inline void IntAddRef(ID3D11DeviceChild *child) +{ + // assume it's wrapped, do a default cast with template parameters (the exact type doesn't matter) + if(child) + ((WrappedDeviceChild11 *)child)->IntAddRef(); +} + +inline void IntRelease(ID3D11DeviceChild *child) +{ + if(child) + ((WrappedDeviceChild11 *)child)->IntRelease(); +} + +inline int32_t GetIntRefCount(ID3D11DeviceChild *child) +{ + if(child) + return ((WrappedDeviceChild11 *)child)->GetIntRefCount(); + return -1; +} + +inline int32_t GetExtRefCount(ID3D11DeviceChild *child) +{ + if(child) + return ((WrappedDeviceChild11 *)child)->GetExtRefCount(); + return -1; +} + template class WrappedResource11 : public WrappedDeviceChild11 { private: - unsigned int m_ViewRefcount; // refcount from views (invisible to the end-user) - protected: #if ENABLED(RDOC_DEVEL) DescType m_Desc; #endif WrappedResource11(NestedType *real, WrappedID3D11Device *device) - : WrappedDeviceChild11(real, device), m_ViewRefcount(0) + : WrappedDeviceChild11(real, device) { #if ENABLED(RDOC_DEVEL) real->GetDesc(&m_Desc); #endif - - // we'll handle deleting on release, so we can check against m_ViewRefcount - RefCounter::SetSelfDeleting(false); } virtual ~WrappedResource11() {} public: - void ViewAddRef() - { - InterlockedIncrement(&m_ViewRefcount); - - RefCounter::AddDeviceSoftref(m_pDevice); - } - - void ViewRelease() - { - InterlockedDecrement(&m_ViewRefcount); - unsigned int extRefCount = RefCounter::GetRefCount(); - - WrappedID3D11Device *dev = m_pDevice; - - if(extRefCount == 0 && m_ViewRefcount == 0) - delete this; - - RefCounter::ReleaseDeviceSoftref(dev); - } - - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter::SoftRef(m_pDevice) - m_PipelineRefs; } - ULONG STDMETHODCALLTYPE Release() - { - unsigned int extRefCount = RefCounter::Release(); - unsigned int pipeRefs = m_PipelineRefs; - - WrappedID3D11Device *dev = m_pDevice; - - if(extRefCount == 0 && m_ViewRefcount == 0) - delete this; - - RefCounter::ReleaseDeviceSoftref(dev); - - return extRefCount - pipeRefs; - } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(ID3D11Resource)) @@ -459,8 +510,6 @@ public: { if(RenderDoc::Inst().IsReplayApp()) { - SCOPED_LOCK(m_pDevice->D3DLock()); - RDCASSERT(m_BufferList.find(GetResourceID()) == m_BufferList.end()); m_BufferList[GetResourceID()] = BufferEntry(this, byteLength); } @@ -477,15 +526,11 @@ public: virtual ~WrappedID3D11Buffer() { - SCOPED_LOCK(m_pDevice->D3DLock()); - if(RenderDoc::Inst().IsReplayApp()) { if(m_BufferList.find(GetResourceID()) != m_BufferList.end()) m_BufferList.erase(GetResourceID()); } - - Shutdown(); } virtual void STDMETHODCALLTYPE GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) @@ -520,8 +565,6 @@ public: { if(RenderDoc::Inst().IsReplayApp()) { - SCOPED_LOCK(m_pDevice->D3DLock()); - RDCASSERT(m_TextureList.find(GetResourceID()) == m_TextureList.end()); m_TextureList[GetResourceID()] = TextureEntry(this, type); } @@ -530,15 +573,11 @@ public: virtual ~WrappedTexture() { - SCOPED_LOCK(m_pDevice->D3DLock()); - if(RenderDoc::Inst().IsReplayApp()) { if(m_TextureList.find(GetResourceID()) != m_TextureList.end()) m_TextureList.erase(GetResourceID()); } - - Shutdown(); } }; @@ -648,7 +687,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11InputLayout() { Shutdown(); } + virtual ~WrappedID3D11InputLayout() {} }; class WrappedID3D11RasterizerState2 @@ -661,7 +700,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11RasterizerState2() { Shutdown(); } + virtual ~WrappedID3D11RasterizerState2() {} ////////////////////////////// // implement ID3D11RasterizerState @@ -702,8 +741,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11BlendState1() { Shutdown(); } - static bool IsState1(ID3D11BlendState *state) { return CanQuery(state); } + virtual ~WrappedID3D11BlendState1() {} ////////////////////////////// // implement ID3D11BlendState @@ -731,7 +769,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11DepthStencilState() { Shutdown(); } + virtual ~WrappedID3D11DepthStencilState() {} ////////////////////////////// // implement ID3D11DepthStencilState @@ -750,7 +788,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11SamplerState() { Shutdown(); } + virtual ~WrappedID3D11SamplerState() {} ////////////////////////////// // implement ID3D11SamplerState @@ -769,14 +807,12 @@ protected: : WrappedDeviceChild11(real, device), m_pResource(res), m_ResourceRange(this) { m_ResourceResID = GetIDForResource(m_pResource); - // cast is potentially invalid but functions in WrappedResource will be identical across each - ((WrappedID3D11Buffer *)m_pResource)->ViewAddRef(); + ::IntAddRef(m_pResource); } virtual ~WrappedView1() { - // cast is potentially invalid but functions in WrappedResource will be identical across each - ((WrappedID3D11Buffer *)m_pResource)->ViewRelease(); + ::IntRelease(m_pResource); m_pResource = NULL; } @@ -804,8 +840,10 @@ public: { RDCASSERT(m_pResource); if(pResource) + { *pResource = m_pResource; - m_pResource->AddRef(); + m_pResource->AddRef(); + } } ////////////////////////////// @@ -830,7 +868,7 @@ public: : WrappedView1(real, device, res) { } - virtual ~WrappedID3D11RenderTargetView1() { Shutdown(); } + virtual ~WrappedID3D11RenderTargetView1() {} ////////////////////////////// // implement ID3D11RenderTargetView1 virtual void STDMETHODCALLTYPE GetDesc1(D3D11_RENDER_TARGET_VIEW_DESC1 *pDesc1) @@ -856,7 +894,7 @@ public: : WrappedView1(real, device, res) { } - virtual ~WrappedID3D11ShaderResourceView1() { Shutdown(); } + virtual ~WrappedID3D11ShaderResourceView1() {} ////////////////////////////// // implement ID3D11ShaderResourceView1 virtual void STDMETHODCALLTYPE GetDesc1(D3D11_SHADER_RESOURCE_VIEW_DESC1 *pDesc1) @@ -882,7 +920,7 @@ public: : WrappedView1(real, device, res) { } - virtual ~WrappedID3D11DepthStencilView() { Shutdown(); } + virtual ~WrappedID3D11DepthStencilView() {} }; class WrappedID3D11UnorderedAccessView1 @@ -897,7 +935,7 @@ public: : WrappedView1(real, device, res) { } - virtual ~WrappedID3D11UnorderedAccessView1() { Shutdown(); } + virtual ~WrappedID3D11UnorderedAccessView1() {} ////////////////////////////// // implement ID3D11UnorderedAccessView1 virtual void STDMETHODCALLTYPE GetDesc1(D3D11_UNORDERED_ACCESS_VIEW_DESC1 *pDesc1) @@ -1034,7 +1072,7 @@ public: WrappedShader(device, origId, GetResourceID(), code, codeLen) { } - virtual ~WrappedID3D11Shader() { Shutdown(); } + virtual ~WrappedID3D11Shader() {} }; class WrappedID3D11Counter : public WrappedDeviceChild11 @@ -1046,7 +1084,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11Counter() { Shutdown(); } + virtual ~WrappedID3D11Counter() {} HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(ID3D11Asynchronous)) @@ -1078,7 +1116,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11Query1() { Shutdown(); } + virtual ~WrappedID3D11Query1() {} HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(ID3D11Asynchronous)) @@ -1122,7 +1160,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11Predicate() { Shutdown(); } + virtual ~WrappedID3D11Predicate() {} HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if(riid == __uuidof(ID3D11Asynchronous)) @@ -1159,12 +1197,7 @@ public: { SAFE_ADDREF(m_pLinkage); } - virtual ~WrappedID3D11ClassInstance() - { - SAFE_RELEASE(m_pLinkage); - Shutdown(); - } - + virtual ~WrappedID3D11ClassInstance() { SAFE_RELEASE(m_pLinkage); } ////////////////////////////// // implement ID3D11ClassInstance @@ -1174,8 +1207,8 @@ public: { if(ppLinkage) { - SAFE_ADDREF(m_pLinkage); *ppLinkage = m_pLinkage; + AddRef(); } } @@ -1214,7 +1247,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11ClassLinkage() { Shutdown(); } + virtual ~WrappedID3D11ClassLinkage() {} ////////////////////////////// // implement ID3D11ClassLinkage @@ -1313,7 +1346,6 @@ public: } // context isn't defined type at this point. - Shutdown(); } WrappedID3D11DeviceContext *GetContext() { return m_pContext; } @@ -1359,7 +1391,7 @@ public: : WrappedDeviceChild11(real, device) { } - virtual ~WrappedID3D11Fence() { Shutdown(); } + virtual ~WrappedID3D11Fence() {} HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { return WrappedDeviceChild11::QueryInterface(riid, ppvObject); diff --git a/renderdoc/driver/d3d11/d3d11_video.cpp b/renderdoc/driver/d3d11/d3d11_video.cpp index b48c12e9a..cce2cfbe3 100644 --- a/renderdoc/driver/d3d11/d3d11_video.cpp +++ b/renderdoc/driver/d3d11/d3d11_video.cpp @@ -1357,28 +1357,41 @@ void STDMETHODCALLTYPE WrappedID3D11VideoProcessorOutputView::GetResource( template Wrapped11VideoDeviceChild::Wrapped11VideoDeviceChild( NestedType *real, WrappedID3D11Device *device) - : RefCounter(real), m_pDevice(device), m_pReal(real) + : m_pDevice(device), m_pReal(real), m_ExtRef(1) { - m_pDevice->SoftRef(); + m_pDevice->AddRef(); } template Wrapped11VideoDeviceChild::~Wrapped11VideoDeviceChild() { SAFE_RELEASE(m_pReal); - m_pDevice = NULL; + // remove our device ref here as we don't have IntRef counter to keep the device alive when it + // hits ExtRef 0, which is when we'd normally release the device ref. + SAFE_RELEASE(m_pDevice); } template ULONG STDMETHODCALLTYPE Wrapped11VideoDeviceChild::AddRef() { - return RefCounter::SoftRef(m_pDevice); + if(m_ExtRef == 0) + m_pDevice->AddRef(); + Atomic::Inc32(&m_ExtRef); + return m_ExtRef; } template ULONG STDMETHODCALLTYPE Wrapped11VideoDeviceChild::Release() { - return RefCounter::SoftRelease(m_pDevice); + Atomic::Dec32(&m_ExtRef); + ASSERT_REFCOUNT(m_ExtRef); + // don't defer destruction of video objects, delete them immediately. + if(m_ExtRef == 0) + { + delete this; + return 0; + } + return (ULONG)m_ExtRef; } template @@ -1424,7 +1437,7 @@ Wrapped11VideoDeviceChild::QueryInterface(REFIID riid, return m_pDevice->QueryInterface(riid, ppvObject); } - return RefCounter::QueryInterface(riid, ppvObject); + return RefCountDXGIObject::WrapQueryInterface(m_pReal, riid, ppvObject); } template diff --git a/renderdoc/driver/d3d11/d3d11_video.h b/renderdoc/driver/d3d11/d3d11_video.h index d51ae0f4e..c72635b00 100644 --- a/renderdoc/driver/d3d11/d3d11_video.h +++ b/renderdoc/driver/d3d11/d3d11_video.h @@ -1033,8 +1033,13 @@ struct WrappedID3D11VideoContext2 : public ID3D11VideoContext2 // we don't want to depend on d3d11_resources.h in this header, so replicate // WrappedID3D11DeviceChild here (and it's simpler because these are pure pass-through wrappers) template -struct Wrapped11VideoDeviceChild : public RefCounter, public NestedType1 +struct Wrapped11VideoDeviceChild : public NestedType1 { +private: + // we don't need internal references because nothing holds internal references on these video + // objects + int32_t m_ExtRef; + protected: WrappedID3D11Device *m_pDevice; NestedType *m_pReal; diff --git a/renderdoc/driver/d3d12/d3d12_common.h b/renderdoc/driver/d3d12/d3d12_common.h index 17f5417f6..a919db353 100644 --- a/renderdoc/driver/d3d12/d3d12_common.h +++ b/renderdoc/driver/d3d12/d3d12_common.h @@ -361,9 +361,6 @@ struct D3D12CommandSignature rdcarray arguments; }; -#define IMPLEMENT_IUNKNOWN_WITH_REFCOUNTER_CUSTOMQUERY \ - ULONG STDMETHODCALLTYPE AddRef() { return RefCounter12::AddRef(); } \ - ULONG STDMETHODCALLTYPE Release() { return RefCounter12::Release(); } #define IMPLEMENT_FUNCTION_SERIALISED(ret, func, ...) \ ret func(__VA_ARGS__); \ template \ diff --git a/util/test/demos/d3d11/d3d11_helpers.cpp b/util/test/demos/d3d11/d3d11_helpers.cpp index dca03d8ae..55c190b18 100644 --- a/util/test/demos/d3d11/d3d11_helpers.cpp +++ b/util/test/demos/d3d11/d3d11_helpers.cpp @@ -144,7 +144,7 @@ static const UINT formatStrides[] = { 0, // DXGI_FORMAT_B4G4R4A4_UNORM }; -D3D11BufferCreator::D3D11BufferCreator(D3D11GraphicsTest *test) : m_Test(test) +D3D11BufferCreator::D3D11BufferCreator(ID3D11DevicePtr dev) : m_Dev(dev) { m_BufDesc.ByteWidth = 0; m_BufDesc.MiscFlags = 0; @@ -262,13 +262,13 @@ D3D11BufferCreator &D3D11BufferCreator::Size(UINT size) D3D11BufferCreator::operator ID3D11BufferPtr() const { ID3D11BufferPtr buf; - CHECK_HR(m_Test->dev->CreateBuffer(&m_BufDesc, m_Initdata.pSysMem ? &m_Initdata : NULL, &buf)); + CHECK_HR(m_Dev->CreateBuffer(&m_BufDesc, m_Initdata.pSysMem ? &m_Initdata : NULL, &buf)); return buf; } -D3D11TextureCreator::D3D11TextureCreator(D3D11GraphicsTest *test, DXGI_FORMAT format, UINT width, +D3D11TextureCreator::D3D11TextureCreator(ID3D11DevicePtr dev, DXGI_FORMAT format, UINT width, UINT height, UINT depth) - : m_Test(test) + : m_Dev(dev) { Format = format; Width = width; @@ -353,7 +353,7 @@ D3D11TextureCreator::operator ID3D11Texture1DPtr() const texdesc.Format = Format; ID3D11Texture1DPtr tex; - CHECK_HR(m_Test->dev->CreateTexture1D(&texdesc, NULL, &tex)); + CHECK_HR(m_Dev->CreateTexture1D(&texdesc, NULL, &tex)); return tex; } @@ -374,7 +374,7 @@ D3D11TextureCreator::operator ID3D11Texture2DPtr() const texdesc.SampleDesc.Quality = SampleDesc.Quality; ID3D11Texture2DPtr tex; - CHECK_HR(m_Test->dev->CreateTexture2D(&texdesc, NULL, &tex)); + CHECK_HR(m_Dev->CreateTexture2D(&texdesc, NULL, &tex)); return tex; } @@ -393,18 +393,18 @@ D3D11TextureCreator::operator ID3D11Texture3DPtr() const texdesc.Format = Format; ID3D11Texture3DPtr tex; - CHECK_HR(m_Test->dev->CreateTexture3D(&texdesc, NULL, &tex)); + CHECK_HR(m_Dev->CreateTexture3D(&texdesc, NULL, &tex)); return tex; } -D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Buffer *buf) - : m_Test(test), m_ViewType(viewType), m_ResType(ResourceType::Buffer), m_Res(buf) +D3D11ViewCreator::D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Buffer *buf) + : m_Dev(dev), m_ViewType(viewType), m_ResType(ResourceType::Buffer), m_Res(buf) { SetupDescriptors(); } -D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture1D *tex) - : m_Test(test), m_ViewType(viewType), m_Res(tex) +D3D11ViewCreator::D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture1D *tex) + : m_Dev(dev), m_ViewType(viewType), m_Res(tex) { D3D11_TEXTURE1D_DESC texdesc; tex->GetDesc(&texdesc); @@ -419,8 +419,8 @@ D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, I Format(texdesc.Format); } -D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture2D *tex) - : m_Test(test), m_ViewType(viewType), m_Res(tex) +D3D11ViewCreator::D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture2D *tex) + : m_Dev(dev), m_ViewType(viewType), m_Res(tex) { D3D11_TEXTURE2D_DESC texdesc; tex->GetDesc(&texdesc); @@ -443,8 +443,8 @@ D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, I Format(texdesc.Format); } -D3D11ViewCreator::D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture3D *tex) - : m_Test(test), m_ViewType(viewType), m_ResType(ResourceType::Texture3D), m_Res(tex) +D3D11ViewCreator::D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture3D *tex) + : m_Dev(dev), m_ViewType(viewType), m_ResType(ResourceType::Texture3D), m_Res(tex) { D3D11_TEXTURE3D_DESC texdesc; tex->GetDesc(&texdesc); @@ -776,7 +776,7 @@ D3D11ViewCreator::operator ID3D11ShaderResourceViewPtr() TEST_ASSERT(m_ViewType == ViewType::SRV, "Casting non-SRV ViewCreator to SRV"); ID3D11ShaderResourceViewPtr srv; - CHECK_HR(m_Test->dev->CreateShaderResourceView(m_Res, &desc.srv, &srv)); + CHECK_HR(m_Dev->CreateShaderResourceView(m_Res, &desc.srv, &srv)); return srv; } @@ -806,7 +806,7 @@ D3D11ViewCreator::operator ID3D11UnorderedAccessViewPtr() TEST_ASSERT(m_ViewType == ViewType::UAV, "Casting non-UAV ViewCreator to UAV"); ID3D11UnorderedAccessViewPtr uav; - CHECK_HR(m_Test->dev->CreateUnorderedAccessView(m_Res, &desc.uav, &uav)); + CHECK_HR(m_Dev->CreateUnorderedAccessView(m_Res, &desc.uav, &uav)); return uav; } @@ -816,7 +816,7 @@ D3D11ViewCreator::operator ID3D11RenderTargetViewPtr() TEST_ASSERT(m_ViewType == ViewType::RTV, "Casting non-RTV ViewCreator to RTV"); ID3D11RenderTargetViewPtr rtv; - CHECK_HR(m_Test->dev->CreateRenderTargetView(m_Res, &desc.rtv, &rtv)); + CHECK_HR(m_Dev->CreateRenderTargetView(m_Res, &desc.rtv, &rtv)); return rtv; } @@ -826,14 +826,12 @@ D3D11ViewCreator::operator ID3D11DepthStencilViewPtr() TEST_ASSERT(m_ViewType == ViewType::DSV, "Casting non-DSV ViewCreator to DSV"); ID3D11DepthStencilViewPtr dsv; - CHECK_HR(m_Test->dev->CreateDepthStencilView(m_Res, &desc.dsv, &dsv)); + CHECK_HR(m_Dev->CreateDepthStencilView(m_Res, &desc.dsv, &dsv)); return dsv; } -D3D11SamplerCreator::D3D11SamplerCreator(D3D11GraphicsTest *test) +D3D11SamplerCreator::D3D11SamplerCreator(ID3D11DevicePtr dev) : m_Dev(dev) { - m_Test = test; - m_Desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; m_Desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; m_Desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; @@ -852,6 +850,6 @@ D3D11SamplerCreator::D3D11SamplerCreator(D3D11GraphicsTest *test) D3D11SamplerCreator::operator ID3D11SamplerStatePtr() const { ID3D11SamplerStatePtr samp; - CHECK_HR(m_Test->dev->CreateSamplerState(&m_Desc, &samp)); + CHECK_HR(m_Dev->CreateSamplerState(&m_Desc, &samp)); return samp; } diff --git a/util/test/demos/d3d11/d3d11_helpers.h b/util/test/demos/d3d11/d3d11_helpers.h index 681871aa1..7f39fd826 100644 --- a/util/test/demos/d3d11/d3d11_helpers.h +++ b/util/test/demos/d3d11/d3d11_helpers.h @@ -99,7 +99,7 @@ struct D3D11GraphicsTest; class D3D11BufferCreator { public: - D3D11BufferCreator(D3D11GraphicsTest *test); + D3D11BufferCreator(ID3D11DevicePtr dev); D3D11BufferCreator &Vertex(); D3D11BufferCreator &Index(); @@ -134,7 +134,7 @@ public: operator ID3D11BufferPtr() const; private: - D3D11GraphicsTest *m_Test; + ID3D11DevicePtr m_Dev; D3D11_BUFFER_DESC m_BufDesc; D3D11_SUBRESOURCE_DATA m_Initdata = {}; @@ -143,8 +143,7 @@ private: class D3D11TextureCreator { public: - D3D11TextureCreator(D3D11GraphicsTest *test, DXGI_FORMAT format, UINT width, UINT height, - UINT depth); + D3D11TextureCreator(ID3D11DevicePtr dev, DXGI_FORMAT format, UINT width, UINT height, UINT depth); D3D11TextureCreator &Mips(UINT mips); D3D11TextureCreator &Array(UINT size); @@ -167,7 +166,7 @@ public: ID3D11Texture2DPtr Tex2D() const { return (ID3D11Texture2DPtr) * this; }; ID3D11Texture3DPtr Tex3D() const { return (ID3D11Texture3DPtr) * this; }; protected: - D3D11GraphicsTest *m_Test; + ID3D11DevicePtr m_Dev; UINT Width = 1; UINT Height = 1; @@ -185,7 +184,7 @@ protected: class D3D11SamplerCreator { public: - D3D11SamplerCreator(D3D11GraphicsTest *test); + D3D11SamplerCreator(ID3D11DevicePtr dev); D3D11SamplerCreator &AddressU(D3D11_TEXTURE_ADDRESS_MODE addr) { @@ -237,7 +236,7 @@ public: operator ID3D11SamplerStatePtr() const; protected: - D3D11GraphicsTest *m_Test; + ID3D11DevicePtr m_Dev; D3D11_SAMPLER_DESC m_Desc; }; @@ -245,10 +244,10 @@ protected: class D3D11ViewCreator { public: - D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Buffer *buf); - D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture1D *tex); - D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture2D *tex); - D3D11ViewCreator(D3D11GraphicsTest *test, ViewType viewType, ID3D11Texture3D *tex); + D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Buffer *buf); + D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture1D *tex); + D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture2D *tex); + D3D11ViewCreator(ID3D11DevicePtr dev, ViewType viewType, ID3D11Texture3D *tex); // common params D3D11ViewCreator &Format(DXGI_FORMAT format); @@ -275,7 +274,7 @@ public: private: void SetupDescriptors(); - D3D11GraphicsTest *m_Test; + ID3D11DevicePtr m_Dev; ID3D11ResourcePtr m_Res; ViewType m_ViewType; ResourceType m_ResType; @@ -298,13 +297,6 @@ private: UINT *firstSlice = NULL, *numSlices = NULL; }; -#define GET_REFCOUNT(val, obj) \ - do \ - { \ - obj->AddRef(); \ - val = obj->Release(); \ - } while(0) - #define CHECK_HR(expr) \ { \ HRESULT hr = (expr); \ diff --git a/util/test/demos/d3d11/d3d11_refcount_check.cpp b/util/test/demos/d3d11/d3d11_refcount_check.cpp index 1401f1ea7..6ebcb0b68 100644 --- a/util/test/demos/d3d11/d3d11_refcount_check.cpp +++ b/util/test/demos/d3d11/d3d11_refcount_check.cpp @@ -24,23 +24,973 @@ #include "d3d11_test.h" +typedef int(WINAPI *PFN_BEGIN_EVENT)(DWORD, WCHAR *); +typedef int(WINAPI *PFN_END_EVENT)(); + RD_TEST(D3D11_Refcount_Check, D3D11GraphicsTest) { static constexpr const char *Description = "Ensures that the device etc doesn't delete itself when there are still outstanding " "references, and also that it *does* delete itself when any cycle is detected."; + D3D11GraphicsTest reftest; + + void Prepare(int argc, char **argv) + { + reftest.headless = true; + + reftest.Prepare(argc, argv); + + D3D11GraphicsTest::Prepare(argc, argv); + } + +#define CHECK_REFCOUNT(obj, expected) \ + { \ + ULONG count = GetRefcount(obj); \ + if(count != expected) \ + { \ + if(!failed) \ + DEBUG_BREAK(); \ + failed = true; \ + TEST_WARN(#obj " has wrong reference count. Got %u expected %u", count, expected); \ + } \ + } + + bool IsICDLoaded() + { + if(rdoc || reftest.rdoc) + { + // renderdoc keeps driver DLLs around to avoid race condition bugs, so we can't check on those + // DLLs being unloaded. + // Instead we hack by calling D3D9's Begin/EndEvent. If there's no D3D11 device alive it + // always returns 0, otherwise it returns the nesting level minus 1. Since we don't have a + // device/context to force it to drain the annotation queue we take advantage of the nesting + // level starting at 0, so calling an unbalanced end() will return -1. + + static HMODULE d3d9 = LoadLibraryA("d3d9.dll"); + static PFN_BEGIN_EVENT begin = (PFN_BEGIN_EVENT)GetProcAddress(d3d9, "D3DPERF_BeginEvent"); + static PFN_END_EVENT end = (PFN_END_EVENT)GetProcAddress(d3d9, "D3DPERF_EndEvent"); + + // don't care about these being unbalanced + return end() != 0; + } + + // a bit of a hack but I don't know of a better way to test if the device was really destroyed + return GetModuleHandleA("nvwgf2um.dll") != NULL || GetModuleHandleA("nvwgf2umx.dll") != NULL || + GetModuleHandleA("atidxx32.dll") != NULL || GetModuleHandleA("atidxx64.dll") != NULL || + GetModuleHandleA("igd10iumd32.dll") != NULL || + GetModuleHandleA("igd10iumd64.dll") != NULL; + } + + bool HasMessages(ID3D11InfoQueue * infoQueue, const std::vector &haystacks) + { + std::string concat; + UINT64 num = infoQueue->GetNumStoredMessages(); + for(UINT64 i = 0; i < num; i++) + { + SIZE_T len = 0; + infoQueue->GetMessage(i, NULL, &len); + + char *msgbuf = new char[len]; + D3D11_MESSAGE *message = (D3D11_MESSAGE *)msgbuf; + + infoQueue->GetMessage(i, message, &len); + + if(message->Severity == D3D11_MESSAGE_SEVERITY_INFO) + concat += "INFO: "; + concat += message->pDescription; + concat += "\n"; + + delete[] msgbuf; + } + infoQueue->ClearStoredMessages(); + bool ret = true; + for(const std::string &haystack : haystacks) + ret &= (concat.find(haystack) != std::string::npos); + return ret; + } + + bool HasMessage(ID3D11InfoQueue * infoQueue, const std::string &haystack) + { + return HasMessages(infoQueue, {haystack}); + } + int main() { + // force a debug device + reftest.debugDevice = true; + + if(!reftest.Init()) + return 4; + + { + D3D_FEATURE_LEVEL features[] = {D3D_FEATURE_LEVEL_11_0}; + ULONG ret = 0; + UINT dummy[] = {16, 16, 16, 16, 16}; + + bool failed = false; + + static const GUID unwrappedID3D11InfoQueue__uuid = { + 0x3fc4e618, 0x3f70, 0x452a, {0x8b, 0x8f, 0xa7, 0x3a, 0xcc, 0xb5, 0x8e, 0x3d}}; + + // for the first device enable INFO for creation/destruction + ID3D11InfoQueue *infoQueue = NULL; + + // try first with renderdoc's GUID to get the unwrapped queue for testing against + reftest.dev.QueryInterface(unwrappedID3D11InfoQueue__uuid, &infoQueue); + + if(infoQueue == NULL) + reftest.dev.QueryInterface(__uuidof(ID3D11InfoQueue), &infoQueue); + + infoQueue->ClearStorageFilter(); + infoQueue->ClearRetrievalFilter(); + infoQueue->ClearStoredMessages(); + + ID3D11Debug *dbg = NULL; + reftest.dev.QueryInterface(__uuidof(ID3D11Debug), &dbg); + + // remove our references to everything but vb which we take locally + reftest.defaultLayout = NULL; + reftest.swap = NULL; + reftest.bbTex = NULL; + reftest.bbRTV = NULL; + reftest.dev1 = NULL; + reftest.dev2 = NULL; + reftest.dev3 = NULL; + reftest.dev4 = NULL; + reftest.dev5 = NULL; + reftest.ctx = NULL; + reftest.ctx1 = NULL; + reftest.ctx2 = NULL; + reftest.ctx3 = NULL; + reftest.ctx4 = NULL; + reftest.annot = NULL; + reftest.swapBlitVS = NULL; + reftest.swapBlitPS = NULL; + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + // reference counting behaviour is NOT CONTRACTUAL but some applications check for it anyway. + // this is particularly annoying when they're checking for implementation details, like + // whether a resource hits 0 refcount even if it's still bound somewhere, etc. + // The below refcounting behaviour was accurate for the D3D11 runtime at time of writing, and + // we check it against renderdoc which is based on emulating that behaviour enough to fit this + // test. + + // grab the device into a local pointer so we can AddRef / Release manually + ID3D11Device *localdev = reftest.dev; + localdev->AddRef(); + reftest.dev = NULL; + + ID3D11DeviceContext *localctx = NULL; + localdev->GetImmediateContext(&localctx); + + //////////////////////////////////////////////////////////// + // Create a VB and test basic 'child resource' <-> device refcounting + + ID3D11BufferPtr buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + + ID3D11Buffer *localvb = buf; + localvb->AddRef(); + buf = NULL; + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + // the device should have 5 references - localdev, localctx, localvb, dbg, and infoQueue + CHECK_REFCOUNT(localdev, 5); + + // the VB has one reference + CHECK_REFCOUNT(localvb, 1); + + // add 3 refs to the vertex buffer + localvb->AddRef(); + localvb->AddRef(); + localvb->AddRef(); + + // the device should still only have 5 references, localvb only holds one on the device + CHECK_REFCOUNT(localdev, 5); + + // but the VB has 4 references + CHECK_REFCOUNT(localvb, 4); + + localvb->Release(); + localvb->Release(); + localvb->Release(); + + CHECK_REFCOUNT(localdev, 5); + CHECK_REFCOUNT(localvb, 1); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + //////////////////////////////////////////////////////////// + // in spite of being cached, state objects should not refcount strangely (apart from + // duplicates sharing a pointer) + + { + D3D11_RASTERIZER_DESC rsdesc = {}; + + // ensure this isn't the default rasterizer state + rsdesc.CullMode = D3D11_CULL_BACK; + rsdesc.FillMode = D3D11_FILL_WIREFRAME; + rsdesc.DepthBias = 55; + + ID3D11RasterizerState *rs1 = NULL, *rs2 = NULL, *rs3 = NULL; + localdev->CreateRasterizerState(&rsdesc, &rs1); + + CHECK_REFCOUNT(localdev, 6); + CHECK_REFCOUNT(rs1, 1); + + // change the state, get a new object + rsdesc.DepthBias = 99; + localdev->CreateRasterizerState(&rsdesc, &rs2); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(rs1, 1); + CHECK_REFCOUNT(rs2, 1); + + // keep the same state, get the same object + localdev->CreateRasterizerState(&rsdesc, &rs3); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(rs1, 1); + CHECK_REFCOUNT(rs2, 2); + CHECK_REFCOUNT(rs3, 2); + + if(rs2 != rs3) + { + failed = true; + TEST_ERROR("Expected to get the same state object back"); + } + + rs1->Release(); + rs2->Release(); + rs3->Release(); + } + CHECK_REFCOUNT(localdev, 5); + + //////////////////////////////////////////////////////////// + // create a texture and check view <-> resource <-> device refcounting + + ID3D11Texture2DPtr tex = + D3D11TextureCreator(localdev, DXGI_FORMAT_BC1_UNORM, 128, 128, 1).SRV(); + + ID3D11Texture2D *localtex = tex; + localtex->AddRef(); + tex = NULL; + + // device has a new reference + CHECK_REFCOUNT(localdev, 6); + CHECK_REFCOUNT(localtex, 1); + + ID3D11ShaderResourceViewPtr srv = D3D11ViewCreator(localdev, ViewType::SRV, localtex); + + ID3D11ShaderResourceView *localsrv = srv; + localsrv->AddRef(); + srv = NULL; + + // the device has a new ref from the texture, AND from the SRV + CHECK_REFCOUNT(localdev, 7); + // the texture doesn't get a ref from the view + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + // release the texture. It is kept alive by the SRV, but the device refcount goes down too + localtex->Release(); + CHECK_REFCOUNT(localdev, 6); + CHECK_REFCOUNT(localtex, 0); + CHECK_REFCOUNT(localsrv, 1); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + { + ID3D11Texture2D *texretrieve = NULL; + ID3D11Resource *resretrieve = NULL; + localsrv->GetResource(&resretrieve); + resretrieve->QueryInterface(__uuidof(ID3D11Texture2D), (void **)&texretrieve); + ID3D11Texture2D *texcast = (ID3D11Texture2D *)resretrieve; + + if(texretrieve != localtex) + { + failed = true; + TEST_ERROR("Expected texture to come back identically"); + } + if(texcast != localtex) + { + failed = true; + TEST_ERROR("Expected texture to come back identically"); + } + + texretrieve->Release(); + resretrieve->Release(); + } + + localtex->AddRef(); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + localsrv->AddRef(); + localsrv->AddRef(); + localsrv->AddRef(); + + // external SRV references only apply to the SRV, not the texture or device. Same as any other + // ID3D11DeviceChild + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 4); + + localsrv->Release(); + localsrv->Release(); + localsrv->Release(); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + //////////////////////////////////////////////////////////// + // check refcounting on a deferred context + + ID3D11DeviceContext *localdefctx; + localdev->CreateDeferredContext(0, &localdefctx); + + // device gets another reference + CHECK_REFCOUNT(localdev, 8); + CHECK_REFCOUNT(localdefctx, 1); + + localdefctx->ClearState(); + + // bind the VB. Doesn't change any public refcounts + localdefctx->IASetVertexBuffers(0, 1, &localvb, dummy, dummy); + + CHECK_REFCOUNT(localdev, 8); + CHECK_REFCOUNT(localdefctx, 1); + CHECK_REFCOUNT(localvb, 1); + + // VB is now held alive by the defctx + localvb->Release(); + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localdefctx, 1); + CHECK_REFCOUNT(localvb, 0); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + { + ID3D11Buffer *vbretrieve = NULL; + localdefctx->IAGetVertexBuffers(0, 1, &vbretrieve, NULL, NULL); + + if(vbretrieve != localvb) + { + failed = true; + TEST_ERROR("Expected buffer to come back identically"); + } + + vbretrieve->Release(); + } + + localvb->AddRef(); + + CHECK_REFCOUNT(localdev, 8); + CHECK_REFCOUNT(localdefctx, 1); + CHECK_REFCOUNT(localvb, 1); + + localdefctx->Draw(0, 0); + + ID3D11CommandList *locallist = NULL; + localdefctx->FinishCommandList(FALSE, &locallist); + + infoQueue->ClearStoredMessages(); + + // extra refcount for the list, but otherwise unchanged + CHECK_REFCOUNT(localdev, 9); + CHECK_REFCOUNT(localdefctx, 1); + CHECK_REFCOUNT(locallist, 1); + CHECK_REFCOUNT(localvb, 1); + + ret = localdefctx->Release(); + + // this should release it + if(ret != 0) + { + failed = true; + TEST_ERROR("localdefctx still has outstanding references"); + } + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11Context")) + { + failed = true; + TEST_ERROR("Expected localdefctx to be really destroyed"); + } + + CHECK_REFCOUNT(localdev, 8); + CHECK_REFCOUNT(locallist, 1); + CHECK_REFCOUNT(localvb, 1); + + // the VB is held alive by the list now, though we can't retrieve it anymore + // we skip this test because although the runtime is smart enough to keep refs + // on the necessary objects, we aren't and we hope no-one actually takes advantage of this. + if(0) + { + localvb->Release(); + CHECK_REFCOUNT(localvb, 0); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + localvb->AddRef(); + CHECK_REFCOUNT(localvb, 1); + + if(HasMessage(infoQueue, "INFO: Destroy")) + { + failed = true; + TEST_ERROR("localvb should not have been destroyed"); + } + } + + CHECK_REFCOUNT(localdev, 8); + CHECK_REFCOUNT(locallist, 1); + CHECK_REFCOUNT(localvb, 1); + + ret = locallist->Release(); + localctx->Flush(); + + // this should release it + if(ret != 0) + { + failed = true; + TEST_ERROR("locallist still has outstanding references"); + } + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11CommandList")) + { + failed = true; + TEST_ERROR("Expected locallist to be really destroyed"); + } + + CHECK_REFCOUNT(localdev, 7); + + //////////////////////////////////////////////////////////// + // check that resources which are bound but don't have an external ref stay alive + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + infoQueue->ClearStoredMessages(); + + // another new device refcount + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localctx, 1); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + // binding doesn't change public refcounts + localctx->ClearState(); + localctx->IASetVertexBuffers(0, 1, &localvb, dummy, dummy); + localctx->PSSetShaderResources(0, 1, &localsrv); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localctx, 1); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + // but it means we can release things and they stay alive + localvb->Release(); + localtex->Release(); + localsrv->Release(); + localctx->Flush(); + + CHECK_REFCOUNT(localvb, 0); + CHECK_REFCOUNT(localtex, 0); + CHECK_REFCOUNT(localsrv, 0); + + if(HasMessage(infoQueue, "INFO: Destroy")) + { + failed = true; + TEST_ERROR("Expected nothing to be destroyed"); + } + + localvb->AddRef(); + localtex->AddRef(); + localsrv->AddRef(); + + CHECK_REFCOUNT(localdev, 7); + CHECK_REFCOUNT(localctx, 1); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + localctx->ClearState(); + localctx->Flush(); + + //////////////////////////////////////////////////////////// + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + ret = localvb->Release(); + localctx->Flush(); + + // this should release it + if(ret != 0) + { + failed = true; + TEST_ERROR("localvb still has outstanding references"); + } + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11Buffer")) + { + failed = true; + TEST_ERROR("Expected localvb to be really destroyed"); + } + + CHECK_REFCOUNT(localdev, 6); + + ret = localsrv->Release(); + localctx->Flush(); + + // this should release it + if(ret != 0) + { + failed = true; + TEST_ERROR("localsrv still has outstanding references"); + } + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11ShaderResourceView")) + { + failed = true; + TEST_ERROR("Expected localsrv to be really destroyed"); + } + + CHECK_REFCOUNT(localdev, 5); + + ret = localtex->Release(); + localctx->Flush(); + + // this should release it + if(ret != 0) + { + failed = true; + TEST_ERROR("localtex still has outstanding references"); + } + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11Texture2D")) + { + failed = true; + TEST_ERROR("Expected localtex to be really destroyed"); + } + + // the device should have 4 references - localdev, localctx, dbg and infoQueue + CHECK_REFCOUNT(localdev, 4); + + dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + + // ID3DUserDefinedAnnotation shares the context's refcount + { + CHECK_REFCOUNT(localctx, 1); + + ID3DUserDefinedAnnotationPtr annottest = localctx; + + if(annottest) + { + CHECK_REFCOUNT(localctx, 2); + ID3DUserDefinedAnnotation *localannot = annottest; + CHECK_REFCOUNT(localctx, 2); + CHECK_REFCOUNT(localannot, 2); + } + } + CHECK_REFCOUNT(localctx, 1); + + CHECK_REFCOUNT(localdev, 4); + + localctx->Release(); + localctx = NULL; + dbg->Release(); + dbg = NULL; + infoQueue->Release(); + infoQueue = NULL; + + // the device should only have this reference - localdev + CHECK_REFCOUNT(localdev, 1); + + bool before = IsICDLoaded(); + + ret = localdev->Release(); + + if(ret != 0) + { + failed = true; + TEST_ERROR("localdev still has outstanding references"); + } + + bool after = IsICDLoaded(); + + if(!before) + { + TEST_WARN("Couldn't detect ICD at all - unclear if device really was destroyed"); + } + else if(before && after) + { + failed = true; + TEST_ERROR("Device leaked - ICD dll stayed present"); + } + + /////////////////////////////////////////////////////////////////////////// + // test a device staying alive based on an unbound child resource + reftest.CreateDevice({}, NULL, features, D3D11_CREATE_DEVICE_DEBUG); + + localdev = reftest.dev; + localdev->AddRef(); + reftest.dev = NULL; + reftest.ctx = NULL; + CHECK_REFCOUNT(localdev, 1); + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + localvb = buf; + localvb->AddRef(); + buf = NULL; + + CHECK_REFCOUNT(localdev, 2); + CHECK_REFCOUNT(localvb, 1); + + localdev->Release(); + + CHECK_REFCOUNT(localdev, 1); + CHECK_REFCOUNT(localvb, 1); + + // release the device with the VB + before = IsICDLoaded(); + + ret = localvb->Release(); + + if(ret != 0) + { + failed = true; + TEST_ERROR("localvb still has outstanding references"); + } + + after = IsICDLoaded(); + + if(!before) + { + TEST_WARN("Couldn't detect ICD at all - unclear if device really was destroyed"); + } + else if(before && after) + { + failed = true; + TEST_ERROR("Device leaked - ICD dll stayed present"); + } + + /////////////////////////////////////////////////////////////////////////// + // test a device staying alive based on a *bound* child resource on the immediate context + reftest.CreateDevice({}, NULL, features, D3D11_CREATE_DEVICE_DEBUG); + + localdev = reftest.dev; + localdev->AddRef(); + reftest.dev = NULL; + reftest.ctx = NULL; + CHECK_REFCOUNT(localdev, 1); + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + localvb = buf; + localvb->AddRef(); + buf = NULL; + + CHECK_REFCOUNT(localdev, 2); + CHECK_REFCOUNT(localvb, 1); + + localdev->Release(); + + CHECK_REFCOUNT(localdev, 1); + CHECK_REFCOUNT(localvb, 1); + + localdev->GetImmediateContext(&localctx); + localctx->IASetVertexBuffers(0, 1, &localvb, dummy, dummy); + localctx->Flush(); + + localctx->Release(); + localctx = NULL; + + before = IsICDLoaded(); + + ret = localvb->Release(); + + if(ret != 0) + { + failed = true; + TEST_ERROR("localvb still has outstanding references"); + } + + after = IsICDLoaded(); + + if(!before) + { + TEST_WARN("Couldn't detect ICD at all - unclear if device really was destroyed"); + } + else if(before && after) + { + failed = true; + TEST_ERROR("Device leaked - ICD dll stayed present"); + } + + /////////////////////////////////////////////////////////////////////////// + // test a resource or view being destroyed when unbound + reftest.CreateDevice({}, NULL, features, D3D11_CREATE_DEVICE_DEBUG); + + localdev = reftest.dev; + localdev->AddRef(); + reftest.dev = NULL; + reftest.ctx = NULL; + CHECK_REFCOUNT(localdev, 1); + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + localvb = buf; + localvb->AddRef(); + buf = NULL; + + tex = D3D11TextureCreator(localdev, DXGI_FORMAT_BC1_UNORM, 128, 128, 1).SRV(); + localtex = tex; + localtex->AddRef(); + tex = NULL; + + srv = D3D11ViewCreator(localdev, ViewType::SRV, localtex); + localsrv = srv; + localsrv->AddRef(); + srv = NULL; + + CHECK_REFCOUNT(localdev, 4); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localtex, 1); + CHECK_REFCOUNT(localsrv, 1); + + localdev->GetImmediateContext(&localctx); + + // try first with renderdoc's GUID to get the unwrapped queue for testing against + localdev->QueryInterface(unwrappedID3D11InfoQueue__uuid, (void **)&infoQueue); + + if(infoQueue == NULL) + localdev->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&infoQueue); + + infoQueue->ClearStorageFilter(); + infoQueue->ClearRetrievalFilter(); + infoQueue->ClearStoredMessages(); + + CHECK_REFCOUNT(localdev, 6); + CHECK_REFCOUNT(localvb, 1); + + localctx->IASetVertexBuffers(0, 1, &localvb, dummy, dummy); + localctx->PSSetShaderResources(0, 1, &localsrv); + localctx->Flush(); + + localvb->Release(); + localtex->Release(); + localsrv->Release(); + localctx->Flush(); + + CHECK_REFCOUNT(localdev, 3); + CHECK_REFCOUNT(localvb, 0); + CHECK_REFCOUNT(localtex, 0); + CHECK_REFCOUNT(localsrv, 0); + + if(HasMessage(infoQueue, "INFO: Destroy")) + { + failed = true; + TEST_ERROR("Expected nothing to be destroyed"); + } + + localctx->ClearState(); + localctx->Flush(); + + CHECK_REFCOUNT(localdev, 3); + + if(!HasMessages(infoQueue, {"INFO: Destroy ID3D11Buffer", "INFO: Destroy ID3D11Texture2D", + "INFO: Destroy ID3D11ShaderResourceView"})) + { + failed = true; + TEST_ERROR("Expected buffer, texture and SRV to be destroyed on unbind"); + } + + localctx->Release(); + localctx = NULL; + infoQueue->Release(); + infoQueue = NULL; + + CHECK_REFCOUNT(localdev, 1); + + before = IsICDLoaded(); + + ret = localdev->Release(); + + if(ret != 0) + { + failed = true; + TEST_ERROR("localdev still has outstanding references"); + } + + after = IsICDLoaded(); + + if(!before) + { + TEST_WARN("Couldn't detect ICD at all - unclear if device really was destroyed"); + } + else if(before && after) + { + failed = true; + TEST_ERROR("Device leaked - ICD dll stayed present"); + } + + /////////////////////////////////////////////////////////////////////////// + // test that resources which temporarily bounce off 0 refcounts in a naive bind/unbind don't + // get destroyed. + reftest.CreateDevice({}, NULL, features, D3D11_CREATE_DEVICE_DEBUG); + + localdev = reftest.dev; + localdev->AddRef(); + reftest.dev = NULL; + reftest.ctx = NULL; + CHECK_REFCOUNT(localdev, 1); + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + localvb = buf; + localvb->AddRef(); + buf = NULL; + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + ID3D11Buffer *localvb2 = buf; + localvb2->AddRef(); + buf = NULL; + + buf = D3D11BufferCreator(localdev).Vertex().Data(DefaultTri); + ID3D11Buffer *localvb3 = buf; + localvb3->AddRef(); + buf = NULL; + + CHECK_REFCOUNT(localdev, 4); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localvb2, 1); + CHECK_REFCOUNT(localvb3, 1); + + localdev->GetImmediateContext(&localctx); + + // try first with renderdoc's GUID to get the unwrapped queue for testing against + localdev->QueryInterface(unwrappedID3D11InfoQueue__uuid, (void **)&infoQueue); + + if(infoQueue == NULL) + localdev->QueryInterface(__uuidof(ID3D11InfoQueue), (void **)&infoQueue); + + infoQueue->ClearStorageFilter(); + infoQueue->ClearRetrievalFilter(); + infoQueue->ClearStoredMessages(); + + CHECK_REFCOUNT(localdev, 6); + CHECK_REFCOUNT(localvb, 1); + CHECK_REFCOUNT(localvb2, 1); + CHECK_REFCOUNT(localvb3, 1); + + ID3D11Buffer *firstBuffers[] = {localvb, localvb2, localvb3}; + localctx->IASetVertexBuffers(0, 3, firstBuffers, dummy, dummy); + localctx->Flush(); + + localvb->Release(); + localvb2->Release(); + localvb3->Release(); + localctx->Flush(); + + CHECK_REFCOUNT(localdev, 3); + CHECK_REFCOUNT(localvb, 0); + CHECK_REFCOUNT(localvb2, 0); + CHECK_REFCOUNT(localvb3, 0); + + if(HasMessage(infoQueue, "INFO: Destroy")) + { + failed = true; + TEST_ERROR("Expected nothing to be destroyed"); + } + + // in a naive approach, when replacing localvb with localvb3 in slot 0, localvb is truly not + // referenced anywhere at all. This test ensures that we don't immediately destroy localvb + // when it's unbound from slot 0 because it's soon to be bound to slot 1. Note the same then + // happens with localvb2 which is temporarily reference-less when it's unbound from slot 1 + + ID3D11Buffer *secondBuffers[] = {localvb3, localvb, localvb2}; + localctx->IASetVertexBuffers(0, 3, secondBuffers, dummy, dummy); + localctx->Flush(); + + CHECK_REFCOUNT(localdev, 3); + CHECK_REFCOUNT(localvb, 0); + CHECK_REFCOUNT(localvb2, 0); + CHECK_REFCOUNT(localvb3, 0); + + if(HasMessage(infoQueue, "INFO: Destroy")) + { + failed = true; + TEST_ERROR("Expected nothing to be destroyed"); + } + + // clearing the state should still unbind and destroy the buffers + localctx->ClearState(); + localctx->Flush(); + + CHECK_REFCOUNT(localdev, 3); + + if(!HasMessage(infoQueue, "INFO: Destroy ID3D11Buffer")) + { + failed = true; + TEST_ERROR("Expected buffer, texture and SRV to be destroyed on unbind"); + } + + localctx->Release(); + localctx = NULL; + infoQueue->Release(); + infoQueue = NULL; + + CHECK_REFCOUNT(localdev, 1); + + before = IsICDLoaded(); + + ret = localdev->Release(); + + if(ret != 0) + { + failed = true; + TEST_ERROR("localdev still has outstanding references"); + } + + after = IsICDLoaded(); + + if(!before) + { + TEST_WARN("Couldn't detect ICD at all - unclear if device really was destroyed"); + } + else if(before && after) + { + failed = true; + TEST_ERROR("Device leaked - ICD dll stayed present"); + } + + /////////////////////////////////////////////////////////////////////////// + if(failed) + { + TEST_ERROR("Encountered refcounting errors, aborting test"); + return 5; + } + } + // initialise, create window, create device, etc if(!Init()) return 3; - ID3D11Debug *dbg = NULL; - dev.QueryInterface(__uuidof(ID3D11Debug), &dbg); - - dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); + ID3D11InfoQueuePtr infoQueue = dev; + if(infoQueue) + { + infoQueue->ClearStorageFilter(); + infoQueue->ClearRetrievalFilter(); + } + // run a normal test that we can capture from, so the checker can see that we got this far + // without failing ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0"); ID3DBlobPtr psblob = Compile(D3DDefaultPixel, "main", "ps_4_0"); @@ -51,6 +1001,28 @@ RD_TEST(D3D11_Refcount_Check, D3D11GraphicsTest) ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(DefaultTri); + // destroy backbuffer RTV + bbRTV = NULL; + ctx->Flush(); + + // save the backbuffer texture + ID3D11Texture2D *localbbtex = bbTex; + // release the backbuffer texture + bbTex = NULL; + ctx->Flush(); + + if(GetRefcount(localbbtex) != 0) + TEST_FATAL("backbuffer texture isn't 0 refcount!"); + + // get it back again + CHECK_HR(swap->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&bbTex)); + + if(bbTex != localbbtex) + TEST_FATAL("Expected backbuffer texture to be identical after obtaining it again"); + + // recreate the RTV + CHECK_HR(dev->CreateRenderTargetView(bbTex, NULL, &bbRTV)); + while(Running()) { ClearRenderTargetView(bbRTV, {0.2f, 0.2f, 0.2f, 1.0f}); @@ -66,59 +1038,12 @@ RD_TEST(D3D11_Refcount_Check, D3D11GraphicsTest) ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL); + setMarker("Color Draw"); ctx->Draw(3, 0); Present(); } - // remove our references to everything but vb which we take locally - defaultLayout = NULL; - vs = NULL; - ps = NULL; - swap = NULL; - bbTex = NULL; - bbRTV = NULL; - dev1 = NULL; - dev2 = NULL; - ctx = NULL; - ctx1 = NULL; - ctx2 = NULL; - annot = NULL; - - ID3D11Buffer *localvb = vb; - localvb->AddRef(); - vb = NULL; - - ID3D11Device *localdev = dev; - localdev->AddRef(); - dev = NULL; - - dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); - - ULONG ret = localvb->Release(); - - // this should release the VB - TEST_ASSERT(ret == 0, "localvb still has outstanding references"); - - GET_REFCOUNT(ret, localdev); - - // the device should only have at most 2 references - localdev and dbg - TEST_ASSERT(ret <= 2, "device has too many references"); - - dbg->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); - - dbg->Release(); - dbg = NULL; - - GET_REFCOUNT(ret, localdev); - - // the device should only have this reference - localdev - TEST_ASSERT(ret == 1, "device has too many references"); - - ret = localdev->Release(); - - TEST_ASSERT(ret == 0, "localdev still has outstanding references"); - return 0; } }; diff --git a/util/test/demos/d3d11/d3d11_test.cpp b/util/test/demos/d3d11/d3d11_test.cpp index d23289c8a..fdc8fb5b6 100644 --- a/util/test/demos/d3d11/d3d11_test.cpp +++ b/util/test/demos/d3d11/d3d11_test.cpp @@ -112,7 +112,7 @@ void D3D11GraphicsTest::Prepare(int argc, char **argv) if(dyn_D3D11CreateDevice) { D3D_FEATURE_LEVEL features[] = {D3D_FEATURE_LEVEL_11_0}; - hr = CreateDevice(adapters, NULL, features, 0); + hr = CreateDevice(NULL, NULL, features, 0); if(SUCCEEDED(hr)) { @@ -148,10 +148,7 @@ bool D3D11GraphicsTest::Init(IDXGIAdapterPtr pAdapter) if(headless) { - if(pAdapter != NULL) - hr = CreateDevice({pAdapter}, NULL, features, flags); - else - hr = CreateDevice(adapters, NULL, features, flags); + hr = CreateDevice(pAdapter, NULL, features, flags); if(FAILED(hr)) { @@ -182,10 +179,7 @@ bool D3D11GraphicsTest::Init(IDXGIAdapterPtr pAdapter) swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; swapDesc.Flags = 0; - if(pAdapter != NULL) - hr = CreateDevice({pAdapter}, &swapDesc, features, flags); - else - hr = CreateDevice(adapters, &swapDesc, features, flags); + hr = CreateDevice(pAdapter, &swapDesc, features, flags); if(FAILED(hr)) { @@ -222,23 +216,39 @@ GraphicsWindow *D3D11GraphicsTest::MakeWindow(int width, int height, const char return new Win32Window(width, height, title); } -HRESULT D3D11GraphicsTest::CreateDevice(const std::vector &adaptersToTry, - DXGI_SWAP_CHAIN_DESC *swapDesc, D3D_FEATURE_LEVEL *features, - UINT flags) +HRESULT D3D11GraphicsTest::CreateDevice(IDXGIAdapterPtr adapterToTry, DXGI_SWAP_CHAIN_DESC *swapDesc, + D3D_FEATURE_LEVEL *features, UINT flags) { HRESULT hr = E_FAIL; - for(size_t i = 0; i < adaptersToTry.size(); ++i) + + if(adapterToTry) { if(swapDesc) - hr = dyn_D3D11CreateDeviceAndSwapChain(adaptersToTry[i], D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, + hr = dyn_D3D11CreateDeviceAndSwapChain(adapterToTry, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, features, 1, D3D11_SDK_VERSION, swapDesc, &swap, &dev, NULL, &ctx); else - hr = dyn_D3D11CreateDevice(adaptersToTry[i], D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, features, - 1, D3D11_SDK_VERSION, &dev, NULL, &ctx); + hr = dyn_D3D11CreateDevice(adapterToTry, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, features, 1, + D3D11_SDK_VERSION, &dev, NULL, &ctx); if(SUCCEEDED(hr)) - break; + return hr; + } + else + { + for(size_t i = 0; i < adapters.size(); ++i) + { + if(swapDesc) + hr = dyn_D3D11CreateDeviceAndSwapChain(adapters[i], D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, + features, 1, D3D11_SDK_VERSION, swapDesc, &swap, + &dev, NULL, &ctx); + else + hr = dyn_D3D11CreateDevice(adapters[i], D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, features, 1, + D3D11_SDK_VERSION, &dev, NULL, &ctx); + + if(SUCCEEDED(hr)) + break; + } } // If it failed, try again on warp @@ -339,6 +349,9 @@ void D3D11GraphicsTest::Shutdown() dev1 = NULL; dev2 = NULL; dev = NULL; + + swapBlitVS = NULL; + swapBlitPS = NULL; } bool D3D11GraphicsTest::Running() diff --git a/util/test/demos/d3d11/d3d11_test.h b/util/test/demos/d3d11/d3d11_test.h index a197a7526..9099f4320 100644 --- a/util/test/demos/d3d11/d3d11_test.h +++ b/util/test/demos/d3d11/d3d11_test.h @@ -50,8 +50,8 @@ struct D3D11GraphicsTest : public GraphicsTest void Shutdown(); GraphicsWindow *MakeWindow(int width, int height, const char *title); - HRESULT CreateDevice(const std::vector &adaptersToTry, - DXGI_SWAP_CHAIN_DESC *swapDesc, D3D_FEATURE_LEVEL *features, UINT flags); + HRESULT CreateDevice(IDXGIAdapterPtr adapterToTry, DXGI_SWAP_CHAIN_DESC *swapDesc, + D3D_FEATURE_LEVEL *features, UINT flags); void PostDeviceCreate(); enum BufType @@ -87,40 +87,40 @@ struct D3D11GraphicsTest : public GraphicsTest void CreateDefaultInputLayout(ID3DBlobPtr vsblob); - D3D11BufferCreator MakeBuffer() { return D3D11BufferCreator(this); } + D3D11BufferCreator MakeBuffer() { return D3D11BufferCreator(dev); } D3D11TextureCreator MakeTexture(DXGI_FORMAT format, UINT width) { - return D3D11TextureCreator(this, format, width, 1, 1); + return D3D11TextureCreator(dev, format, width, 1, 1); } D3D11TextureCreator MakeTexture(DXGI_FORMAT format, UINT width, UINT height) { - return D3D11TextureCreator(this, format, width, height, 1); + return D3D11TextureCreator(dev, format, width, height, 1); } D3D11TextureCreator MakeTexture(DXGI_FORMAT format, UINT width, UINT height, UINT depth) { - return D3D11TextureCreator(this, format, width, height, depth); + return D3D11TextureCreator(dev, format, width, height, depth); } - D3D11SamplerCreator MakeSampler() { return D3D11SamplerCreator(this); } + D3D11SamplerCreator MakeSampler() { return D3D11SamplerCreator(dev); } template D3D11ViewCreator MakeSRV(T res) { - return D3D11ViewCreator(this, ViewType::SRV, res); + return D3D11ViewCreator(dev, ViewType::SRV, res); } template D3D11ViewCreator MakeRTV(T res) { - return D3D11ViewCreator(this, ViewType::RTV, res); + return D3D11ViewCreator(dev, ViewType::RTV, res); } template D3D11ViewCreator MakeDSV(T res) { - return D3D11ViewCreator(this, ViewType::DSV, res); + return D3D11ViewCreator(dev, ViewType::DSV, res); } template D3D11ViewCreator MakeUAV(T res) { - return D3D11ViewCreator(this, ViewType::UAV, res); + return D3D11ViewCreator(dev, ViewType::UAV, res); } std::vector GetBufferData(ID3D11Buffer *buf, uint32_t offset = 0, uint32_t len = 0); diff --git a/util/test/demos/d3d12/d3d12_helpers.cpp b/util/test/demos/d3d12/d3d12_helpers.cpp index 5b7164c04..a098144fe 100644 --- a/util/test/demos/d3d12/d3d12_helpers.cpp +++ b/util/test/demos/d3d12/d3d12_helpers.cpp @@ -199,7 +199,8 @@ D3D12_ROOT_PARAMETER1 tableParam(D3D12_SHADER_VISIBILITY vis, D3D12_DESCRIPTOR_R return ret; } -D3D12BufferCreator::D3D12BufferCreator(D3D12GraphicsTest *test) : m_Test(test) +D3D12BufferCreator::D3D12BufferCreator(ID3D12DevicePtr dev, D3D12GraphicsTest *test) + : m_Dev(dev), m_Test(test) { m_BufDesc.Alignment = 0; m_BufDesc.DepthOrArraySize = 1; @@ -260,20 +261,19 @@ D3D12BufferCreator::operator ID3D12ResourcePtr() const initialState = D3D12_RESOURCE_STATE_COPY_DEST; ID3D12ResourcePtr buf; - CHECK_HR(m_Test->dev->CreateCommittedResource(&m_HeapDesc, D3D12_HEAP_FLAG_NONE, &m_BufDesc, - initialState, NULL, __uuidof(ID3D12Resource), - (void **)&buf)); + CHECK_HR(m_Dev->CreateCommittedResource(&m_HeapDesc, D3D12_HEAP_FLAG_NONE, &m_BufDesc, initialState, + NULL, __uuidof(ID3D12Resource), (void **)&buf)); - if(m_Initdata) + if(m_Initdata && m_Test) m_Test->SetBufferData(buf, D3D12_RESOURCE_STATE_COMMON, (const byte *)m_Initdata, m_BufDesc.Width); return buf; } -D3D12TextureCreator::D3D12TextureCreator(D3D12GraphicsTest *test, DXGI_FORMAT format, UINT width, +D3D12TextureCreator::D3D12TextureCreator(ID3D12DevicePtr dev, DXGI_FORMAT format, UINT width, UINT height, UINT depth) - : m_Test(test) + : m_Dev(dev) { m_InitialState = D3D12_RESOURCE_STATE_COMMON; @@ -371,15 +371,15 @@ D3D12TextureCreator &D3D12TextureCreator::InitialState(D3D12_RESOURCE_STATES sta D3D12TextureCreator::operator ID3D12ResourcePtr() const { ID3D12ResourcePtr tex; - CHECK_HR(m_Test->dev->CreateCommittedResource(&m_HeapDesc, m_HeapFlags, &m_TexDesc, m_InitialState, - NULL, __uuidof(ID3D12Resource), (void **)&tex)); + CHECK_HR(m_Dev->CreateCommittedResource(&m_HeapDesc, m_HeapFlags, &m_TexDesc, m_InitialState, + NULL, __uuidof(ID3D12Resource), (void **)&tex)); return tex; } -D3D12ViewCreator::D3D12ViewCreator(D3D12GraphicsTest *test, ID3D12DescriptorHeap *heap, +D3D12ViewCreator::D3D12ViewCreator(ID3D12DevicePtr dev, ID3D12DescriptorHeap *heap, ID3D12DescriptorHeap *clearHeap, ViewType viewType, ID3D12Resource *res) - : m_Test(test), m_Type(viewType), m_Heap(heap), m_ClearHeap(clearHeap), m_Res(res) + : m_Dev(dev), m_Type(viewType), m_Heap(heap), m_ClearHeap(clearHeap), m_Res(res) { D3D12_RESOURCE_DESC resdesc = res->GetDesc(); D3D12_RESOURCE_DIMENSION dim = resdesc.Dimension; @@ -784,10 +784,10 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateCPU(ID3D12DescriptorHeap *he uint32_t descriptor) { static UINT increment[] = { - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV), 0, // D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV), }; D3D12_CPU_DESCRIPTOR_HANDLE cpu = heap->GetCPUDescriptorHandleForHeapStart(); @@ -797,17 +797,17 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateCPU(ID3D12DescriptorHeap *he if(m_Type == ViewType::DSV) { cpu.ptr += increment[D3D12_DESCRIPTOR_HEAP_TYPE_DSV] * descriptor; - m_Test->dev->CreateDepthStencilView(m_Res, &desc.dsv, cpu); + m_Dev->CreateDepthStencilView(m_Res, &desc.dsv, cpu); } else if(m_Type == ViewType::RTV) { cpu.ptr += increment[D3D12_DESCRIPTOR_HEAP_TYPE_RTV] * descriptor; - m_Test->dev->CreateRenderTargetView(m_Res, &desc.rtv, cpu); + m_Dev->CreateRenderTargetView(m_Res, &desc.rtv, cpu); } else if(m_Type == ViewType::CBV) { cpu.ptr += increment[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] * descriptor; - m_Test->dev->CreateConstantBufferView(&desc.cbv, cpu); + m_Dev->CreateConstantBufferView(&desc.cbv, cpu); } else if(m_Type == ViewType::SRV) { @@ -830,7 +830,7 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateCPU(ID3D12DescriptorHeap *he desc.srv.Shader4ComponentMapping = Shader4ComponentMapping; cpu.ptr += increment[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] * descriptor; - m_Test->dev->CreateShaderResourceView(m_Res, &desc.srv, cpu); + m_Dev->CreateShaderResourceView(m_Res, &desc.srv, cpu); } else if(m_Type == ViewType::UAV) { @@ -851,7 +851,7 @@ D3D12_CPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateCPU(ID3D12DescriptorHeap *he } cpu.ptr += increment[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] * descriptor; - m_Test->dev->CreateUnorderedAccessView(m_Res, NULL, &desc.uav, cpu); + m_Dev->CreateUnorderedAccessView(m_Res, NULL, &desc.uav, cpu); } return cpu; @@ -863,10 +863,10 @@ D3D12_GPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateGPU(ID3D12DescriptorHeap *he CreateCPU(heap, descriptor); static UINT increment[] = { - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV), 0, // D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), - m_Test->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV), + m_Dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV), }; D3D12_GPU_DESCRIPTOR_HANDLE gpu = heap->GetGPUDescriptorHandleForHeapStart(); @@ -881,7 +881,7 @@ D3D12_GPU_DESCRIPTOR_HANDLE D3D12ViewCreator::CreateGPU(ID3D12DescriptorHeap *he return gpu; } -D3D12PSOCreator::D3D12PSOCreator(D3D12GraphicsTest *test) : m_Test(test) +D3D12PSOCreator::D3D12PSOCreator(ID3D12DevicePtr dev) : m_Dev(dev) { GraphicsDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; GraphicsDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; @@ -963,7 +963,13 @@ D3D12PSOCreator &D3D12PSOCreator::InputLayout(const std::vectorDefaultInputLayout()); + const D3D12_INPUT_CLASSIFICATION vertex = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + static const std::vector defaultLayout = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, vertex, 0}, + {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, vertex, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, vertex, 0}, + }; + return InputLayout(defaultLayout); } D3D12PSOCreator &D3D12PSOCreator::StripRestart(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE stripCut) @@ -1011,13 +1017,13 @@ D3D12PSOCreator::operator ID3D12PipelineStatePtr() const ID3D12PipelineStatePtr pso; if(ComputeDesc.CS.BytecodeLength > 0) { - CHECK_HR(m_Test->dev->CreateComputePipelineState(&ComputeDesc, __uuidof(ID3D12PipelineState), - (void **)&pso)); + CHECK_HR(m_Dev->CreateComputePipelineState(&ComputeDesc, __uuidof(ID3D12PipelineState), + (void **)&pso)); } else { - CHECK_HR(m_Test->dev->CreateGraphicsPipelineState(&GraphicsDesc, __uuidof(ID3D12PipelineState), - (void **)&pso)); + CHECK_HR(m_Dev->CreateGraphicsPipelineState(&GraphicsDesc, __uuidof(ID3D12PipelineState), + (void **)&pso)); } return pso; } diff --git a/util/test/demos/d3d12/d3d12_helpers.h b/util/test/demos/d3d12/d3d12_helpers.h index 24f661ac4..6d3e8d6c0 100644 --- a/util/test/demos/d3d12/d3d12_helpers.h +++ b/util/test/demos/d3d12/d3d12_helpers.h @@ -75,7 +75,7 @@ struct D3D12GraphicsTest; class D3D12PSOCreator { public: - D3D12PSOCreator(D3D12GraphicsTest *test); + D3D12PSOCreator(ID3D12DevicePtr dev); D3D12PSOCreator &VS(ID3DBlobPtr blob); D3D12PSOCreator &HS(ID3DBlobPtr blob); @@ -104,13 +104,13 @@ public: D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDesc = {}; private: - D3D12GraphicsTest *m_Test; + ID3D12DevicePtr m_Dev; }; class D3D12BufferCreator { public: - D3D12BufferCreator(D3D12GraphicsTest *test); + D3D12BufferCreator(ID3D12DevicePtr dev, D3D12GraphicsTest *test); D3D12BufferCreator &UAV(); @@ -135,6 +135,7 @@ public: operator ID3D12ResourcePtr() const; private: + ID3D12DevicePtr m_Dev; D3D12GraphicsTest *m_Test; D3D12_RESOURCE_DESC m_BufDesc; @@ -145,8 +146,7 @@ private: class D3D12TextureCreator { public: - D3D12TextureCreator(D3D12GraphicsTest *test, DXGI_FORMAT format, UINT width, UINT height, - UINT depth); + D3D12TextureCreator(ID3D12DevicePtr dev, DXGI_FORMAT format, UINT width, UINT height, UINT depth); D3D12TextureCreator &Mips(UINT mips); D3D12TextureCreator &Array(UINT size); @@ -167,7 +167,7 @@ public: operator ID3D12ResourcePtr() const; protected: - D3D12GraphicsTest *m_Test; + ID3D12DevicePtr m_Dev; D3D12_RESOURCE_STATES m_InitialState; D3D12_RESOURCE_DESC m_TexDesc; @@ -178,8 +178,8 @@ protected: class D3D12ViewCreator { public: - D3D12ViewCreator(D3D12GraphicsTest *test, ID3D12DescriptorHeap *heap, - ID3D12DescriptorHeap *clearHeap, ViewType viewType, ID3D12Resource *res); + D3D12ViewCreator(ID3D12DevicePtr dev, ID3D12DescriptorHeap *heap, ID3D12DescriptorHeap *clearHeap, + ViewType viewType, ID3D12Resource *res); // common params D3D12ViewCreator &Format(DXGI_FORMAT format); @@ -229,7 +229,7 @@ public: private: void SetupDescriptors(ViewType viewType, ResourceType resType); - D3D12GraphicsTest *m_Test; + ID3D12DevicePtr m_Dev; ID3D12Resource *m_Res; ID3D12DescriptorHeap *m_Heap; ID3D12DescriptorHeap *m_ClearHeap; @@ -276,13 +276,6 @@ D3D12_INDIRECT_ARGUMENT_DESC drawArg(); D3D12_INDIRECT_ARGUMENT_DESC drawIndexedArg(); D3D12_INDIRECT_ARGUMENT_DESC dispatchArg(); -#define GET_REFCOUNT(val, obj) \ - do \ - { \ - obj->AddRef(); \ - val = obj->Release(); \ - } while(0) - #define CHECK_HR(expr) \ { \ HRESULT hr = (expr); \ diff --git a/util/test/demos/d3d12/d3d12_large_buffer.cpp b/util/test/demos/d3d12/d3d12_large_buffer.cpp index 25573ec32..c014da888 100644 --- a/util/test/demos/d3d12/d3d12_large_buffer.cpp +++ b/util/test/demos/d3d12/d3d12_large_buffer.cpp @@ -55,7 +55,7 @@ RD_TEST(D3D12_Large_Buffer, D3D12GraphicsTest) verts[indices[1]] = DefaultTri[1]; verts[indices[2]] = DefaultTri[2]; - range.End = vb->GetDesc().Width; + range.End = (SIZE_T)vb->GetDesc().Width; vb->Unmap(0, &range); diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index 75e20e34a..8f23f4fdd 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -301,14 +301,6 @@ bool D3D12GraphicsTest::Init() m_GPUSyncFence->SetName(L"GPUSync fence"); - const D3D12_INPUT_CLASSIFICATION vertex = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; - - m_DefaultInputLayout = { - {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, vertex, 0}, - {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, vertex, 0}, - {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, vertex, 0}, - }; - CHECK_HR(dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void **)&m_Alloc)); diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index 2f6411595..914551a23 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -75,7 +75,6 @@ struct D3D12GraphicsTest : public GraphicsTest ID3DBlobPtr Compile(std::string src, std::string entry, std::string profile); void WriteBlob(std::string name, ID3DBlobPtr blob, bool compress); - const std::vector &DefaultInputLayout() { return m_DefaultInputLayout; } void SetBlobPath(std::string name, ID3DBlobPtr &blob); void SetBlobPath(std::string name, ID3D12DeviceChild *shader); @@ -89,45 +88,45 @@ struct D3D12GraphicsTest : public GraphicsTest UINT NumStaticSamplers = 0, const D3D12_STATIC_SAMPLER_DESC *StaticSamplers = NULL); ID3D12CommandSignaturePtr MakeCommandSig(ID3D12RootSignaturePtr rootSig, const std::vector ¶ms); - D3D12PSOCreator MakePSO() { return D3D12PSOCreator(this); } - D3D12BufferCreator MakeBuffer() { return D3D12BufferCreator(this); } + D3D12PSOCreator MakePSO() { return D3D12PSOCreator(dev); } + D3D12BufferCreator MakeBuffer() { return D3D12BufferCreator(dev, this); } D3D12TextureCreator MakeTexture(DXGI_FORMAT format, UINT width) { - return D3D12TextureCreator(this, format, width, 1, 1); + return D3D12TextureCreator(dev, format, width, 1, 1); } D3D12TextureCreator MakeTexture(DXGI_FORMAT format, UINT width, UINT height) { - return D3D12TextureCreator(this, format, width, height, 1); + return D3D12TextureCreator(dev, format, width, height, 1); } D3D12TextureCreator MakeTexture(DXGI_FORMAT format, UINT width, UINT height, UINT depth) { - return D3D12TextureCreator(this, format, width, height, depth); + return D3D12TextureCreator(dev, format, width, height, depth); } template D3D12ViewCreator MakeCBV(T res) { - return D3D12ViewCreator(this, m_CBVUAVSRV, NULL, ViewType::CBV, res); + return D3D12ViewCreator(dev, m_CBVUAVSRV, NULL, ViewType::CBV, res); } template D3D12ViewCreator MakeSRV(T res) { - return D3D12ViewCreator(this, m_CBVUAVSRV, NULL, ViewType::SRV, res); + return D3D12ViewCreator(dev, m_CBVUAVSRV, NULL, ViewType::SRV, res); } template D3D12ViewCreator MakeRTV(T res) { - return D3D12ViewCreator(this, m_RTV, NULL, ViewType::RTV, res); + return D3D12ViewCreator(dev, m_RTV, NULL, ViewType::RTV, res); } template D3D12ViewCreator MakeDSV(T res) { - return D3D12ViewCreator(this, m_DSV, NULL, ViewType::DSV, res); + return D3D12ViewCreator(dev, m_DSV, NULL, ViewType::DSV, res); } template D3D12ViewCreator MakeUAV(T res) { - return D3D12ViewCreator(this, m_CBVUAVSRV, m_Clear, ViewType::UAV, res); + return D3D12ViewCreator(dev, m_CBVUAVSRV, m_Clear, ViewType::UAV, res); } std::vector GetBufferData(ID3D12ResourcePtr buffer, D3D12_RESOURCE_STATES state, @@ -227,8 +226,6 @@ struct D3D12GraphicsTest : public GraphicsTest ID3D12CommandQueuePtr queue; - std::vector m_DefaultInputLayout; - ID3D12FencePtr m_GPUSyncFence; HANDLE m_GPUSyncHandle = NULL; UINT64 m_GPUSyncCounter = 1; diff --git a/util/test/demos/dx/d3d_helpers.h b/util/test/demos/dx/d3d_helpers.h index c7dcf2e70..511606e60 100644 --- a/util/test/demos/dx/d3d_helpers.h +++ b/util/test/demos/dx/d3d_helpers.h @@ -44,6 +44,13 @@ COM_SMARTPTR(IDXGIAdapter); COM_SMARTPTR(IDXGISurface); COM_SMARTPTR(IDXGIResource); +template +ULONG GetRefcount(T *ptr) +{ + ptr->AddRef(); + return ptr->Release(); +} + std::vector FindD3DAdapters(IDXGIFactoryPtr factory, int argc, char **argv, bool &warp); diff --git a/util/test/demos/vk/vk_descriptor_reuse.cpp b/util/test/demos/vk/vk_descriptor_reuse.cpp index bd9fe37c1..60e6f7181 100644 --- a/util/test/demos/vk/vk_descriptor_reuse.cpp +++ b/util/test/demos/vk/vk_descriptor_reuse.cpp @@ -453,7 +453,7 @@ void main() { std::unique_lock scoped(doneLock); - while(threadsDone < threadCount) + while(threadsDone < (int)threadCount) doneCV.wait(scoped); threadsDone = 0; } diff --git a/util/test/tests/D3D11/D3D11_Refcount_Check.py b/util/test/tests/D3D11/D3D11_Refcount_Check.py new file mode 100644 index 000000000..381057b0f --- /dev/null +++ b/util/test/tests/D3D11/D3D11_Refcount_Check.py @@ -0,0 +1,33 @@ +import struct +import math +import renderdoc as rd +import rdtest + + +class D3D11_Refcount_Check(rdtest.TestCase): + demos_test_name = 'D3D11_Refcount_Check' + + def get_capture_options(self): + # Ensure we enable API validation since the test relies on being able to query it + ret = rd.CaptureOptions() + + ret.apiValidation = True + ret.debugOutputMute = False + + return ret + + + def check_capture(self): + draw = self.find_draw("Color Draw") + + self.check(draw is not None) + + draw = draw.next + + self.controller.SetFrameEvent(draw.eventId, False) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 0.5, 0.5, [0.0, 1.0, 0.0, 1.0]) + + rdtest.log.success("Captured loaded with color as expected") \ No newline at end of file