2

I am working on a program that will have many DIB bitmaps (created by CreateDIBSection) and will have to draw a lot of text on them using Win API.

In order to draw on the bitmap, Windows needs device context, created by CreateCompatibleDC.

And now here are two approaches:

  1. I can create the DC once per bitmap, using it for drawing and delete it when freeing the bitmap.

  2. Or I can create DC only when I need to draw to the bitmap, call the draw functions and delete the DC.

What is the better approach? I prefer the first, because of less calls - this will make my code much smaller and also a little bit faster.

But isn't it too expensive to hold a long living DC for every bitmap?

Edit1: The application is actually a GUI toolkit library that can be used in different and unpredictable way in the future, so I need a well balanced decision with maximal possible performance and minimal system resource usage.

johnfound
  • 6,857
  • 4
  • 31
  • 60
  • Common practice is to call "`GetDC()`" (or GetDCEx()), draw, then "`ReleaseDC()`". – paulsm4 Nov 02 '15 at 07:21
  • I don't need to draw on a window, so GetDC is not an option. CreateCompatibleDC is what need to be used. This is memory DC that is not associated with a window. – johnfound Nov 02 '15 at 07:23
  • @selbie Hm, maybe you mean #2? And I still don't feel like know the answer of the question. – johnfound Nov 02 '15 at 07:35
  • Oops - that's what I meant! Yes #2! – selbie Nov 02 '15 at 07:48
  • @selbie - So, your opinion is that the memory device context is expensive enough (memory, OS resources) to try to minimize the instances in the memory on the price of bunch of API calls: `CreateCompatibleDC`, 2xNx`SelectObject` and `DeleteDC` on every draw operation (even a changed pixel)? – johnfound Nov 02 '15 at 07:55
  • Maybe you could elaborate on what your application actually does? Because my answer will be different if you are something like a game or rendered running at 30fps vs showing still images that only change every few seconds. – selbie Nov 02 '15 at 07:59
  • @selbie - Question edited, but I don't think it will help a lot, because the application is actually a library and its use is not very clear now. – johnfound Nov 02 '15 at 08:23

2 Answers2

5

GDI objects are limited, both per process as well as per session. You are competing for resources with all other processes running in the same session. With that in mind, you should consume GDI resources only when needed (option 2 in your question).

Mark Russinovich's blog entry Pushing the Limits of Windows: USER and GDI Objects – Part 2 goes into a fair amount of detail. To sum up the gist, here is a list of limits that the window manager places on GDI resources:

  • 10.000 GDI objects per process (default value, configurable through the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota).
  • 65.535 GDI objects per user session.
  • GDI object memory limit is the paged pool limit (see Pushing the Limits of Windows: Paged and Nonpaged Pool).
SzieberthAdam
  • 3,999
  • 2
  • 23
  • 31
IInspectable
  • 46,945
  • 8
  • 85
  • 181
4

Here is a test to examine how long it take to call CreateCompatibleDC. I find on average it takes about 10 to 15 microseconds to make each call. This is relatively fast compared to BitBlt, specially for larger images. Therefore there is not much advantage in holding the memory 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;
}

Result on Windows 10:

Result for 24bit 5MB bitmap:

CreateCompatibleDC: 17 microseconds
BitBlt: 2500 microseconds

Result for 8bit 275kb:

CreateCompatibleDC: 12 microseconds
BitBlt: 500 microseconds

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77