amerkoleci / Vortice.Windows

.NET bindings for Direct3D12, Direct3D11, WIC, Direct2D1, XInput, XAudio, X3DAudio, DXC, Direct3D9 and DirectInput.
MIT License
1.01k stars 73 forks source link

D3D11CreateDevice leaks objects according to ReportLiveObjects #399

Closed roy-t closed 1 year ago

roy-t commented 1 year ago

I was trying to verify if my code cleans-up properly after itself. After configuring the DXGI and Direct3D11 info queues. I notice that it seems impossible to clean up the ID3D11Device and Context.

It took me a while to drill down to a minimum example, but I believe even in this case I run into multiple objects that have not been cleaned up.

I'm on Windows 11, with Vortice.Direct3D11 v2.4.2 running on .NET 7.

using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.Direct3D11.Debug;
using Vortice.DXGI.Debug;
using static Vortice.Direct3D11.D3D11;
using static Vortice.DXGI.DXGI;

_ = D3D11CreateDevice(null, DriverType.Hardware, DeviceCreationFlags.Debug, null, out var device, out var context);

var dxgiDebug = DXGIGetDebugInterface1<IDXGIDebug>();
var dxgiInfoQueue = DXGIGetDebugInterface1<IDXGIInfoQueue>();
dxgiInfoQueue.PushEmptyStorageFilter(DebugAll);

var d3dDebug = device.QueryInterface<ID3D11Debug>();
var d3dInfoQueue = d3dDebug.QueryInterface<ID3D11InfoQueue>();
d3dInfoQueue.PushEmptyStorageFilter();

context.Dispose();
device.Dispose();

Console.WriteLine("===DXGI Report Live Objects===");
dxgiDebug.ReportLiveObjects(DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
var count = dxgiInfoQueue.GetNumStoredMessages(DebugAll);
for (var i = 0ul; i < count; i++)
{
    var message = dxgiInfoQueue.GetMessage(DebugAll, i);
    Console.WriteLine(message.Description);
}

Console.WriteLine("===D3D Report Live Device Objects===");
d3dDebug.ReportLiveDeviceObjects(ReportLiveDeviceObjectFlags.Detail | ReportLiveDeviceObjectFlags.IgnoreInternal);
for (var i = 0ul; i < d3dInfoQueue.NumStoredMessages; i++)
{
    var mesasge = d3dInfoQueue.GetMessage(i);
    Console.WriteLine(mesasge.Description);
}

Console.ReadLine();

This results in

===DXGI Report Live Objects===
Live IDXGIFactory at 0x00000141801F9120, Refcount: 2
        Live IDXGIAdapter at 0x000001418014FE40, Refcount: 1
Live ID3D11Device at 0x00000141821EDCA0, Refcount: 3
===D3D Report Live Device Objects===
Live ID3D11Device at 0x00000141821EDCA0, Refcount: 3
Live ID3D11Device at 0x00000141821EDCA0, Refcount: 3
amerkoleci commented 1 year ago

d3dDebug and d3dInfoQueue keeps 2 references on device, did you disposed them?

roy-t commented 1 year ago

Ahh, if I dispose the D3DDebug and D3DInfoQueue before I call dxgiDebug.ReportLiveObjects then it does not report any live objects. But of course then I can't call d3dDebug.ReportLiveDeviceObjects anymore.

I guess this by design. So I should just filter out the live device in my sanity checking?

For reference, this is what I changed it to:

using Vortice.Direct3D;
using Vortice.Direct3D11;
using Vortice.Direct3D11.Debug;
using Vortice.DXGI.Debug;
using static Vortice.Direct3D11.D3D11;
using static Vortice.DXGI.DXGI;

_ = D3D11CreateDevice(null, DriverType.Hardware, DeviceCreationFlags.Debug, null, out var device, out var context);

var dxgiDebug = DXGIGetDebugInterface1<IDXGIDebug>();
var dxgiInfoQueue = DXGIGetDebugInterface1<IDXGIInfoQueue>();
dxgiInfoQueue.PushEmptyStorageFilter(DebugAll);

var d3dDebug = device.QueryInterface<ID3D11Debug>();
var d3dInfoQueue = d3dDebug.QueryInterface<ID3D11InfoQueue>();
d3dInfoQueue.PushEmptyStorageFilter();

context.Dispose();
device.Dispose();

Console.WriteLine("===D3D Report Live Device Objects===");
d3dDebug.ReportLiveDeviceObjects(ReportLiveDeviceObjectFlags.Detail | ReportLiveDeviceObjectFlags.IgnoreInternal);
for (var i = 0ul; i < d3dInfoQueue.NumStoredMessages; i++)
{
    var mesasge = d3dInfoQueue.GetMessage(i);
    Console.WriteLine(mesasge.Description);
}

// Prints: Live ID3D11Device at 0x000001C9022A0C70, Refcount: 3

d3dDebug.Dispose();
d3dInfoQueue.Dispose();

Console.WriteLine("===DXGI Report Live Objects===");
dxgiDebug.ReportLiveObjects(DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
var count = dxgiInfoQueue.GetNumStoredMessages(DebugAll);
for (var i = 0ul; i < count; i++)
{
    var message = dxgiInfoQueue.GetMessage(DebugAll, i);
    Console.WriteLine(message.Description);
}

// Prints nothing

dxgiDebug.Dispose();
dxgiInfoQueue.Dispose();

Console.ReadLine();
amerkoleci commented 1 year ago

Yes, you can use the dxgi report part and ignore d3d11 one's

roy-t commented 1 year ago

Oh? Is everything in the DXGI report also in the D3D11 one? I didn't realize that.

amerkoleci commented 1 year ago

It is :) dxgi is the common part between d3d11 and D3D12

roy-t commented 1 year ago

OOoohh oopsie. Will update my code!