mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-09 01:00:51 +00:00
856c838def
* In a previous update in 2021 many copyright ranges were truncated accidentally, and some files have been copy-pasted with wrong years. These dates have been fixed based on git history and original copyright messages.
1055 lines
31 KiB
C++
1055 lines
31 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018-2026 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#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<std::string> &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;
|
|
reftest.DefaultTriVS = NULL;
|
|
reftest.DefaultTriPS = NULL;
|
|
reftest.DefaultTriVB = 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;
|
|
|
|
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");
|
|
|
|
CreateDefaultInputLayout(vsblob);
|
|
|
|
ID3D11VertexShaderPtr vs = CreateVS(vsblob);
|
|
ID3D11PixelShaderPtr ps = CreatePS(psblob);
|
|
|
|
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});
|
|
|
|
IASetVertexBuffer(vb, sizeof(DefaultA2V), 0);
|
|
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
ctx->IASetInputLayout(defaultLayout);
|
|
|
|
ctx->VSSetShader(vs, NULL, 0);
|
|
ctx->PSSetShader(ps, NULL, 0);
|
|
|
|
RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});
|
|
|
|
ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL);
|
|
|
|
setMarker("Color Draw");
|
|
ctx->Draw(3, 0);
|
|
|
|
Present();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
REGISTER_TEST();
|