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 解决了这个问题!
我有点学习 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 解决了这个问题!