You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 22:47:31