为何我的Bitmap Render Target占用内存过高(窗口Resize时)
问题分析与解决方案
核心问题
你在每次WM_PAINT消息触发时都调用pDCRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget)创建新的BitmapRenderTarget,虽然最后调用了Release,但窗口调整大小时会频繁触发WM_PAINT,短时间内大量创建销毁资源会导致内存峰值飙升,甚至可能因资源释放不及时出现隐性内存泄漏。
优化方案
- 复用BitmapRenderTarget:仅在窗口初始化或尺寸改变时创建/更新BitmapRenderTarget,而非每次绘制都重复创建。
- 处理
WM_SIZE消息:窗口大小变化时,销毁旧的BitmapRenderTarget,根据新尺寸创建新的实例。 - 复用固定资源:像画刷这类重复使用的绘制资源,改为全局初始化复用,避免每次绘制都创建销毁。
修改后的完整代码
#include <windows.h> #include <d2d1.h> #pragma comment(lib, "d2d1") // 全局变量 ID2D1Factory* pFactory = nullptr; ID2D1DCRenderTarget* pDCRenderTarget = nullptr; ID2D1BitmapRenderTarget* pBitmapRenderTarget = nullptr; ID2D1SolidColorBrush* pBlueBrush = nullptr; // 复用画刷 ID2D1Factory* GetFactory() { if (pFactory == nullptr) { D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory); } return pFactory; } // 初始化/更新BitmapRenderTarget void UpdateBitmapRenderTarget(HWND hwnd) { if (pBitmapRenderTarget) { pBitmapRenderTarget->Release(); pBitmapRenderTarget = nullptr; } RECT rc; GetClientRect(hwnd, &rc); D2D1_SIZE_F size = D2D1::SizeF(rc.right - rc.left, rc.bottom - rc.top); pDCRenderTarget->CreateCompatibleRenderTarget(size, &pBitmapRenderTarget); // 初始化复用画刷 if (!pBlueBrush && pBitmapRenderTarget) { pBitmapRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue), &pBlueBrush); } } // 窗口过程 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_SIZE: // 窗口尺寸变化时更新BitmapRenderTarget if (pDCRenderTarget) { UpdateBitmapRenderTarget(hwnd); } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); // 用BeginPaint规范处理绘制流程 RECT rc; GetClientRect(hwnd, &rc); pDCRenderTarget->BindDC(hdc, &rc); // 首次绘制时初始化BitmapRenderTarget if (!pBitmapRenderTarget) { UpdateBitmapRenderTarget(hwnd); } // 在BitmapRenderTarget上执行绘制 pBitmapRenderTarget->BeginDraw(); pBitmapRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White)); pBitmapRenderTarget->FillRectangle(D2D1::RectF(50, 50, 200, 200), pBlueBrush); HRESULT hr = pBitmapRenderTarget->EndDraw(); if (SUCCEEDED(hr)) { // 将位图绘制到DC渲染目标 ID2D1Bitmap* pBitmap = nullptr; pBitmapRenderTarget->GetBitmap(&pBitmap); if (pBitmap) { pDCRenderTarget->BeginDraw(); pDCRenderTarget->DrawBitmap(pBitmap, D2D1::RectF(0, 0, rc.right, rc.bottom)); pDCRenderTarget->EndDraw(); pBitmap->Release(); } } EndPaint(hwnd, &ps); // 必须调用EndPaint结束绘制 break; } case WM_DESTROY: PostQuitMessage(0); // 释放所有资源 if (pBlueBrush) pBlueBrush->Release(); if (pBitmapRenderTarget) pBitmapRenderTarget->Release(); if (pDCRenderTarget) pDCRenderTarget->Release(); if (pFactory) pFactory->Release(); break; case WM_CLOSE: DestroyWindow(hwnd); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } // 程序入口 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { const wchar_t CLASS_NAME[] = L"Direct2DDoubleBufferingWindow"; WNDCLASS wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); HWND hwnd = CreateWindowEx( 0, CLASS_NAME, L"Direct2D Double Buffering", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr); if (!hwnd) return -1; // 创建DC渲染目标 D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)); GetFactory()->CreateDCRenderTarget(&rtProps, &pDCRenderTarget); ShowWindow(hwnd, nCmdShow); // 初始化BitmapRenderTarget UpdateBitmapRenderTarget(hwnd); MSG msg = {}; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }
关键优化说明
- 把BitmapRenderTarget的创建逻辑封装到
UpdateBitmapRenderTarget函数,仅在窗口初始化和WM_SIZE消息触发时执行,避免重复创建资源。 - 将蓝色画刷改为全局复用,减少不必要的资源创建销毁开销。
- 使用
BeginPaint和EndPaint替代GetDC,符合Windows窗口绘制规范,确保WM_PAINT消息被正确处理。 - 增加HRESULT结果检查,避免渲染失败后的无效操作。
内容的提问来源于stack exchange,提问作者Abdulnoor Ali
相关产品推荐
相关产品推荐

