IDXGISwapChain::Present 内存泄漏

IDXGISwapChain::Present Memory Leak

我有点学习 Direct3D 并且有这个非常基本的代码,没有什么太花哨的。在 Visual Studio 调试期间,我注意到一个 constantly increasing memory usage,这有点奇怪。

根据我的观察,IDXGISwapChain::Present 会在每次帧更新时分配新的内存块。 Here is a difference between two memory snapshots,大约相差7秒。

我不知道这可能是什么原因,也许我忘了调用某种清算函数?而且我什至没有任何缓冲区或着色器或类似的东西,只有基本的 init 东西。我真的很想知道我怎么可能解决这个问题。这是完整的代码:

//==============================================================================
// main.cpp
// Copyright (c) Michael Loda. All rights reserved.
//==============================================================================

#if defined(DEBUG) || defined(_DEBUG)
#define ALC_DEBUG 1
#else
#define ALC_DEBUG 0
#endif


#include <crtdbg.h>

#define _WIN32_WINNT 0x0601
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <ShlObj.h>
#include <comdef.h>
#include <wrl.h>

#include <d3d11.h>
#include <DirectXMath.h>
#include <DirectXColors.h>


using Microsoft::WRL::ComPtr;


HWND g_WindowHandle = NULL;
LPCSTR g_WindowClassName = "WindowClass";

ComPtr<ID3D11Device> g_D3DDevice;
ComPtr<ID3D11DeviceContext> g_D3DDeviceContext;
ComPtr<IDXGISwapChain> g_D3DSwapChain;
ComPtr<ID3D11RenderTargetView> g_D3DRenderTargetView;
ComPtr<ID3D11Texture2D> g_D3DDepthStencil;
ComPtr<ID3D11DepthStencilView> g_D3DDepthStencilView;


LRESULT CALLBACK WindowProc(HWND p_Window, UINT p_Msg, WPARAM p_WParam, LPARAM p_LParam);


int APIENTRY WinMain(HINSTANCE p_Inst, HINSTANCE, LPSTR p_CmdLine, int p_Show)
{

#if ALC_DEBUG
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

    if (FAILED(CoInitializeEx(NULL, COINITBASE_MULTITHREADED)))
        return 1;



    HRESULT _Result = S_OK;


    UINT _DeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if ALC_DEBUG
    _DeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    if (FAILED(_Result = D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, _DeviceFlags, NULL, 0, D3D11_SDK_VERSION, g_D3DDevice.GetAddressOf(), NULL, g_D3DDeviceContext.GetAddressOf())))
        throw _Result; // TODO: Handle

#if ALC_DEBUG
    ComPtr<ID3D11Debug> _D3DDebug;
    if (FAILED(_Result = g_D3DDevice->QueryInterface(IID_PPV_ARGS(_D3DDebug.GetAddressOf()))))
        throw _com_error(_Result); // TODO: Handle
#endif

    WNDCLASSEXA _WindowClass = {};
    _WindowClass.cbSize = sizeof(_WindowClass);
    _WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    _WindowClass.lpfnWndProc = WindowProc;
    _WindowClass.cbClsExtra = 0;
    _WindowClass.cbWndExtra = 0;
    _WindowClass.hInstance = p_Inst;
    _WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION);
    _WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW);
    _WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    _WindowClass.lpszMenuName = NULL;
    _WindowClass.lpszClassName = g_WindowClassName;
    _WindowClass.hIconSm = LoadIconA(NULL, IDI_APPLICATION);

    if (!RegisterClassExA(&_WindowClass))
        throw; // TODO: Handle

    g_WindowHandle = CreateWindowExA(0, g_WindowClassName, "", WS_POPUP, 0, 0, 1280, 720, NULL, NULL, p_Inst, NULL);
    if (!g_WindowHandle)
        throw; // TODO: Handle


    {
        ComPtr<IDXGIDevice> _DXGIDevice;
        if (FAILED(_Result = g_D3DDevice->QueryInterface(IID_PPV_ARGS(_DXGIDevice.GetAddressOf()))))
            throw _Result; // TODO: Handle

        ComPtr<IDXGIAdapter> _DXGIAdapter;
        if (FAILED(_Result = _DXGIDevice->GetAdapter(_DXGIAdapter.GetAddressOf())))
            throw _Result; // TODO: Handle
        _DXGIDevice.Reset();

        ComPtr<IDXGIFactory> _DXGIFactory;
        if (FAILED(_Result = _DXGIAdapter->GetParent(IID_PPV_ARGS(_DXGIFactory.GetAddressOf()))))
            throw _Result; // TODO: Handle
        _DXGIAdapter.Reset();


        DXGI_SWAP_CHAIN_DESC _SwapChainDesc = {};
        _SwapChainDesc.BufferDesc.Width = 1280; // TODO: Change
        _SwapChainDesc.BufferDesc.Height = 720; // TODO: Change
        _SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60; // TODO: Change
        _SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
        _SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: SRGB?
        _SwapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        _SwapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_STRETCHED;
        _SwapChainDesc.SampleDesc.Count = 1; // TODO: Change
        _SwapChainDesc.SampleDesc.Quality = 0; // TODO: Change
        _SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        _SwapChainDesc.BufferCount = 1;
        _SwapChainDesc.OutputWindow = g_WindowHandle;
        _SwapChainDesc.Windowed = TRUE; // TODO: Fullscreen support
        _SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        _SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

        if (FAILED(_Result = _DXGIFactory->CreateSwapChain(g_D3DDevice.Get(), &_SwapChainDesc, g_D3DSwapChain.GetAddressOf())))
            throw _Result; // TODO: Handle


        if (FAILED(_Result = _DXGIFactory->MakeWindowAssociation(g_WindowHandle, DXGI_MWA_NO_ALT_ENTER)))
            throw _Result; // TODO: Handle


        _DXGIFactory.Reset();
    }

    {
        ComPtr<ID3D11Texture2D> _BackBuffer;
        if (FAILED(_Result = g_D3DSwapChain->GetBuffer(0, IID_PPV_ARGS(_BackBuffer.GetAddressOf()))))
            throw _Result; // TODO: Handle

        if (FAILED(_Result = g_D3DDevice->CreateRenderTargetView(_BackBuffer.Get(), NULL, g_D3DRenderTargetView.GetAddressOf())))
            throw _Result; // TODO: Handle
        _BackBuffer.Reset();


        D3D11_TEXTURE2D_DESC _Tex2DDesc = {};
        _Tex2DDesc.Width = 1280; // TODO: Change
        _Tex2DDesc.Height = 720; // TODO: Change
        _Tex2DDesc.MipLevels = 1;
        _Tex2DDesc.ArraySize = 1;
        _Tex2DDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        _Tex2DDesc.SampleDesc.Count = 1;
        _Tex2DDesc.SampleDesc.Quality = 0;
        _Tex2DDesc.Usage = D3D11_USAGE_DEFAULT;
        _Tex2DDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
        _Tex2DDesc.CPUAccessFlags = 0;
        _Tex2DDesc.MiscFlags = 0;

        if (FAILED(_Result = g_D3DDevice->CreateTexture2D(&_Tex2DDesc, NULL, g_D3DDepthStencil.GetAddressOf())))
            throw _Result; // TODO: Handle


        D3D11_DEPTH_STENCIL_VIEW_DESC _DSVDesc = {};
        _DSVDesc.Format = _Tex2DDesc.Format;
        _DSVDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        _DSVDesc.Texture2D.MipSlice = 0;
        _DSVDesc.Flags = 0;

        if (FAILED(_Result = g_D3DDevice->CreateDepthStencilView(g_D3DDepthStencil.Get(), &_DSVDesc, g_D3DDepthStencilView.GetAddressOf())))
            throw _Result; // TODO: Handle


        g_D3DDeviceContext->OMSetRenderTargets(1, g_D3DRenderTargetView.GetAddressOf(), g_D3DDepthStencilView.Get());

        D3D11_VIEWPORT _Viewport = { 0.0f, 0.0f, 1280.0f, 720.0f, 0.0f, 1.0f }; // TODO: Change
        g_D3DDeviceContext->RSSetViewports(1, &_Viewport);

    }


    ShowWindow(g_WindowHandle, p_Show);





    MSG _Msg = {};
    while (_Msg.message != WM_QUIT)
    {
        if (PeekMessageA(&_Msg, NULL, 0, 0, PM_REMOVE) != 0)
        {
            TranslateMessage(&_Msg);
            DispatchMessageA(&_Msg);
        }
        else
        {

            g_D3DDeviceContext->ClearRenderTargetView(g_D3DRenderTargetView.Get(), DirectX::Colors::MidnightBlue);
            g_D3DDeviceContext->ClearDepthStencilView(g_D3DDepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

            _Result = g_D3DSwapChain->Present(1, 0);
            if ((_Result == DXGI_ERROR_DEVICE_REMOVED) || (_Result == DXGI_ERROR_DEVICE_RESET))
            {
                // TODO: On device removed
            }
            else
            {
                if (FAILED(_Result))
                    throw _Result; // TODO: Handle
            }

        }
    }


    g_D3DDeviceContext->ClearState();

    g_D3DDepthStencilView.Reset();
    g_D3DDepthStencil.Reset();
    g_D3DRenderTargetView.Reset();
    g_D3DSwapChain.Reset();

    g_WindowHandle = NULL;

    UnregisterClassA(g_WindowClassName, p_Inst);

    g_D3DDeviceContext.Reset();
    g_D3DDevice.Reset();

#if ALC_DEBUG
    _D3DDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
    _D3DDebug.Reset();
#endif

    CoUninitialize();

    return (int)_Msg.wParam;
}


LRESULT CALLBACK WindowProc(HWND p_Window, UINT p_Msg, WPARAM p_WParam, LPARAM p_LParam)
{
    PAINTSTRUCT _PaintStruct = {};
    HDC _DC = NULL;

    switch (p_Msg)
    {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
        case WM_PAINT:
        {
            _DC = BeginPaint(p_Window, &_PaintStruct);
            EndPaint(p_Window, &_PaintStruct);
            return 0;
        }
        case WM_CLOSE:
        {
            // TODO: Improve this
            if (MessageBoxA(p_Window, "Are you sure you want to quit?", "", MB_YESNO | MB_ICONQUESTION) == IDYES)
                break;

            return 0;
        }
        default:
        {
            break;
        }
    }

    return DefWindowProcA(p_Window, p_Msg, p_WParam, p_LParam);
}

编辑:我想我会提到当我关闭 VSync(即将数字 0 而不是 1 传递给 Present 函数)时,增加的内存使用率更高更快。

将我的 AMD Radeon R9 M375 驱动程序从 17.6.2 更新到 17.10.3 解决了这个问题!