On D3D11 destroy swapchain-related resources immediately. Closes #3343

* During capture if ResizeBuffers is called we can't defer destruction to the
  end of the capture as it will cause the function to fail due to refcounting
  rules.
This commit is contained in:
baldurk
2024-06-06 14:47:01 +01:00
parent 1bdffb18a6
commit 80f49ea8e4
2 changed files with 77 additions and 41 deletions
+76 -41
View File
@@ -1743,6 +1743,9 @@ IUnknown *WrappedID3D11Device::WrapSwapchainBuffer(IDXGISwapper *swapper, DXGI_F
{
pTex = new WrappedID3D11Texture2D1((ID3D11Texture2D *)realSurface, this, TEXDISPLAY_UNKNOWN);
// this is only used to indicate that this texture is a backbuffer
pTex->m_RealDescriptor = new D3D11_TEXTURE2D_DESC();
SetDebugName(pTex, "Swap Chain Backbuffer");
D3D11_TEXTURE2D_DESC desc;
@@ -1825,6 +1828,36 @@ void WrappedID3D11Device::ReportDeath(ID3D11DeviceChild *obj)
{
SCOPED_LOCK(m_D3DLock);
ID3D11DeviceChild *texObj = obj;
ResourceRange range = ResourceRange::Null;
// backbuffers and their views are destroyed immediately to accommodate D3D's refcounting rules for
// backbuffers. For views, get the resource to check and for everything else check the object directly
if(WrappedID3D11ShaderResourceView1::IsAlloc(obj))
range = ResourceRange((WrappedID3D11ShaderResourceView1 *)obj);
else if(WrappedID3D11UnorderedAccessView1::IsAlloc(obj))
range = ResourceRange((WrappedID3D11UnorderedAccessView1 *)obj);
else if(WrappedID3D11RenderTargetView1::IsAlloc(obj))
range = ResourceRange((WrappedID3D11RenderTargetView1 *)obj);
else if(WrappedID3D11DepthStencilView::IsAlloc(obj))
range = ResourceRange((WrappedID3D11DepthStencilView *)obj);
if(range.GetResource() != NULL)
texObj = range.GetResource();
// if this is a wrapped 2D texture and is a backbuffer, destroy whatever object we were reporting
// the death of and stop
if(WrappedID3D11Texture2D1::IsAlloc(texObj))
{
WrappedID3D11Texture2D1 *tex = (WrappedID3D11Texture2D1 *)texObj;
if(tex->m_RealDescriptor)
{
DestroyDeadObject(obj);
return;
}
}
m_DeadObjects.push_back(obj);
// if we're shutting down or replaying, immediately flush the object as dead. This isn't super
@@ -1858,8 +1891,6 @@ void WrappedID3D11Device::FlushPendingDead()
if(IsActiveCapturing(m_State))
return;
D3D11ResourceManager *rm = GetResourceManager();
int pass = 0;
do
{
@@ -1867,45 +1898,7 @@ void WrappedID3D11Device::FlushPendingDead()
objs.swap(m_DeadObjects);
for(ID3D11DeviceChild *child : objs)
{
// only wrapped objects are reported for death
WrappedDeviceChild11<ID3D11DeviceChild> *wrapped =
(WrappedDeviceChild11<ID3D11DeviceChild> *)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);
}
}
DestroyDeadObject(child);
objs.clear();
@@ -1920,6 +1913,48 @@ void WrappedID3D11Device::FlushPendingDead()
} while(!m_DeadObjects.empty());
}
void WrappedID3D11Device::DestroyDeadObject(ID3D11DeviceChild *child)
{
D3D11ResourceManager *rm = GetResourceManager();
// only wrapped objects are reported for death
WrappedDeviceChild11<ID3D11DeviceChild> *wrapped = (WrappedDeviceChild11<ID3D11DeviceChild> *)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);
}
}
void WrappedID3D11Device::SetMarker(uint32_t col, const wchar_t *name)
{
if(m_pCurrentWrappedDevice == NULL)
+1
View File
@@ -821,6 +821,7 @@ public:
ResourceId GetBackbufferResourceID() { return m_BBID; }
void ReportDeath(ID3D11DeviceChild *obj);
void FlushPendingDead();
void DestroyDeadObject(ID3D11DeviceChild *child);
void Resurrect(ID3D11DeviceChild *obj);
////////////////////////////////////////////////////////////////