cheap/expensive内存DC在Win中如何API?

How cheap/expensive is memory DC in Win API?

我正在开发一个程序,该程序将包含许多 DIB 位图(由 CreateDIBSection 创建)并且必须使用 Win API.

在其上绘制大量文本

为了在位图上绘制,Windows 需要由 CreateCompatibleDC 创建的设备上下文。

现在有两种方法:

  1. 我可以为每个位图创建一次 DC,用于绘图并在释放位图时将其删除。

  2. 或者只有当我需要绘制到位图时才创建DC,调用绘制函数并删除DC。

更好的方法是什么?我更喜欢第一个,因为调用更少 - 这将使我的代码更小,也更快一点。

但是为每个位图保留一个长期存在的 DC 是不是太昂贵了?

Edit1:该应用程序实际上是一个 GUI 工具包库,将来可以以不同且不可预测的方式使用,因此我需要一个平衡的决定,即最大可能的性能和最小的系统资源使用。

GDI objects 是有限的,无论是每个进程还是每个会话。您正在与同一会话中的所有其他进程 运行 竞争资源。考虑到这一点,您应该仅在需要时才使用 GDI 资源(您问题中的选项 2)。

Mark Russinovich 的博客文章 Pushing the Limits of Windows: USER and GDI Objects – Part 2 介绍了相当多的细节。概括要点,这里是 window 管理器对 GDI 资源的限制列表:

  • 每个进程 10.000 个 GDI 对象(默认值,可通过注册表项配置 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota)。
  • 每个用户会话 65.535 个 GDI 对象。
  • GDI 对象内存限制是页面缓冲池限制(参见 Pushing the Limits of Windows: Paged and Nonpaged Pool)。

这里有一个测试来检查调用 CreateCompatibleDC 需要多长时间。我发现每次调用平均需要 10 到 15 微秒。与 BitBlt 相比,这相对较快,特别是对于较大的图像。所以持有内存DC并没有多大优势。

case WM_PAINT:
{
    static HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"path.bmp", 
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    std::wostringstream oss;

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    auto start = std::chrono::system_clock::now();
    auto memdc = CreateCompatibleDC(hdc);
    oss << L"CreateCompatibleDC: " 
        << (std::chrono::system_clock::now() - start).count() / 10 << "\n";

    auto oldbitmap = SelectObject(memdc, hbitmap);

    start = std::chrono::system_clock::now();
    BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, memdc, 0, 0, SRCCOPY);
    oss << L"BitBlt: "
        << (std::chrono::system_clock::now() - start).count() / 10 << "\n";

    SelectObject(memdc, oldbitmap);
    DeleteDC(memdc);

    EndPaint(hwnd, &ps);

    OutputDebugString(oss.str().c_str());
    break;
}

Windows10 的结果:

24 位 5MB 位图的结果:

CreateCompatibleDC: 17 microseconds
BitBlt: 2500 microseconds

8 位 275kb 的结果:

CreateCompatibleDC: 12 microseconds
BitBlt: 500 microseconds