sharpdx / SharpDX

SharpDX GitHub Repository
http://sharpdx.org
MIT License
1.69k stars 638 forks source link

D3D11 Buffer Memory Release #902

Closed kburgoyne closed 7 years ago

kburgoyne commented 7 years ago

I was pretty certain I was seeing a failure to release buffer memory when using SharpDX V3 with a Win8 Universal app. Since SharpDX was wanting to move on to V4, I thought it better to upgrade my app to Win10 UWP and SharpDX 4. I was still seeing the leak, so I just threw together a quick test app to focus specifically on the leak.

The following Gist contains the App.xaml.cs file from the test app:

https://gist.github.com/kburgoyne/75a069a14bad74eedad52769c126447a

Using VS2017 just create a blank UWP app. Steal the usings and SdxInit() from the gist and plug them into the new app's App.xaml.cs. Call SdxInit() from the start of OnLaunched().

I realize SdxInit() is doing more init stuff than it needs to, but I wanted to stick as close to the WindowsAppStore81/MiniCubeXaml example as possible.

The test simply inits DirectX and goes into a one million iteration loop creating and disposing a constant buffer. The same matrix constant buffer MiniCubeXaml creates. Just by watching the VS2017 Diagnostic Tools its easy to see the process memory consumption start running away. Given enough iterations there will eventually be an out of memory exception thrown.

Adding GC.Collect() inside the loop has no impact.

My experience chasing this under SharpDX V3 was that SharpDX's diagnostic object tracker would indicate that SharpDX didn't think it was holding onto any thing.

This seems like something somebody would have seen previously under V3, but games are generally designed to avoid buffer allocating and deallocating in order to avoid garbage collection. I only discovered the problem when I started making sure I was cleaning up all my allocations between game plays.

I realize this does raise the question of whether other people do not see the problem and it's unique to my system.

Axiverse commented 7 years ago

Possibly related to my issue #903, even if you call dispose, they may not be immediately cleaned up. Calling Device.ImmediateContext.Flush() will force the clean up.

https://stackoverflow.com/questions/44155133/directx11-com-object-with-0-references-not-released

Also you can use ReportLiveDeviceObjects to see the objects in DirectX memory. Just be sure to add the Debug flag to the Direct3D11 device construction parameters.

            DeviceDebug debug = Device3D11.QueryInterface<DeviceDebug>();
            debug.ReportLiveDeviceObjects(ReportingLevel.Detail);
            debug.Dispose();
kburgoyne commented 7 years ago

Thanks. d3dContext.Flush() does the trick. I'll keep your other code snippit in mind since I'm sure it'll come in handy at some point.

Something interesting:

If I do 100,000 iterations of creating and disposing constant buffers the size of Matrix, then d3dContext.Flush() works with the Diagnostic Tools showing the memory being freed.

If I do 1,000,000 iterations before doing d3dContext.Flush(), then Flush() gets lost in the weeds and never returns. The Diagnostic Tools show an initial partial freeing of memory (only about 10%) and then flat lines.

That last "bug" certainly has nothing to do with SharpDX. Doubtful any app would actually create and dispose 1,000,000 buffers without flushing in between, so it's an extreme case which either Microsoft, NVidia (my card) , or both never figured to test.

Thanks for the help!

kburgoyne commented 7 years ago

Additional notes pertaining to @Axiverse comment above regarding DeviceDebug and .ReportLiveDeviceObjects().

If the D3d11.DeviceCreationFlags.Debug flag is not included during device creation, the .QueryInterface() call will throw an E_NOINTERFACE.

The .ReportLiveDeviceObject() call will output to the Output window only if the Native Debugger is enabled in project settings. If only the Managed Debugger is enabled the report output will not make it to the Output window.