Drawing polygons using multithreading in C++

1.2k views Asked by At

I am drawing polygons (Polygon(dc, points, 3)) using in WM_PAINT event using C++. I have a big number of polygons so I am trying to implement multithreading. I am running VS2013 so I have included thread. I have created a function I want to run on a thread:

void anyFunс2(HDC dc, int index)
{
    POINT points[3];

    for (unsigned i = index; i < model->vertexFaces.size(); i += fs)
    {
        // Here we convert our points to Clip and Windowed Coordinates
        // and only then fetch the results
        if (Algorithms::FetchPolygons(&model->finalizedVertices[0], model->vertexFaces[i], points))
        {
            Polygon(dc, points, 3);
        }
    }
}

For instance I have three threads. I have designed the code the way where each thread renders every third element. For example first thread renders 0,3,6,9 polygons, second thread renders 1,4,7,10 and the final thread renders 2,5,8,11 polygons.

Here's my WM_PAINT event:

case WM_PAINT:
{
    // Get dc here
    hdc = GetDC(hWnd);

    // Create a backbuffer here 
    bdc = CreateCompatibleDC(hdc);

    // Get the screen dimensions
    RECT client;
    GetClientRect(hWnd, &client);

    // Create bitmap
    HBITMAP backBuffer = CreateCompatibleBitmap(hdc, client.right - client.left, client.bottom - client.top);

    // Release it, because we no longer need it
    hdc = NULL;
    ReleaseDC(hWnd, hdc);           

    // Select the back dc as a current one and specify the screen dimensions
    HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0, 25, 205));
    HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 55));

    SelectObject(bdc, hPen);
    SelectObject(bdc, hBrush);
    SelectObject(bdc, backBuffer);

    Rectangle(bdc, client.left, client.top, client.right, client.bottom);

    // Call some threads to draw polygons on our BDC
    for (unsigned i = 0; i < func_threads.size(); i++)
    {
        func_threads.at(i) = thread(anyFunс2, bdc, i);
    }

    // Wait until all threads finish their job
    for (unsigned i = 0; i < func_threads.size(); i++)
    {
        if (func_threads[i].joinable()) func_threads[i].join();
    }

    // Swap buffers     
    hdc = BeginPaint(hWnd, &ps);
    BitBlt(hdc, client.left, client.top, client.right, client.bottom, bdc, 0, 0, SRCCOPY);
    EndPaint(hWnd, &ps);

    // Delete all created objects from memory
    DeleteObject(backBuffer);
    DeleteObject(hBrush);
    DeleteObject(hPen);
    DeleteDC(bdc);
    break;
}

As you can see I run these threads in the loop. Then I have another loop where the Join() method is located for each thread. These threads draw polygons to the same HDC (I assume). After the main thread have finished waiting for all these threads it copies everything from the back buffer to the main one. However the problem is that the object is not fully drawn. I mean not all polygons are drawn. The link to the image is attached here. Please help me, why is it happening like that?!

2

There are 2 answers

1
Jerry Coffin On BEST ANSWER

The short answer is that GDI simply isn't designed to support drawing from multiple threads into the same DC simultaneously.

That leaves you with a few choices. The most direct would be to use PolyPolygon to draw all your polygons (or at least large numbers of them) in a single call. This seems particularly relevant in your case--from the looks of things, you're drawing lots of triangles, so a lot of the time taken is probably just the overhead to call the Polygon function, and not really time for Polygon to execute.

Another possibility would be to create a separate back-buffer for each thread to draw into, then use BitBlit to combine those together with (for example) an OR function, so you get the same overall effect as the original drawing.

The third (and probably best) way to support drawing a large number of polygons would be to switch from using GDI to using something like DirectX or OpenGL that's designed from the ground up to support exactly that.

0
James Rockford On

You can use CreateDIBsection() to draw shapes with multiple threads. Just have each thread draw pixels directly to the DIB. Then when all the threads are complete, you can bitBlt the DIB to the screen.