c++ - Win32 window freezes after the first draw (directx 11)

1.1k Views Asked by At

I have a standard win32 window, that I draw on with D2D1. Everything is responsive and runs smoothly. My problem is as follows: After the window is created, it calls WM_PAINT once and then gets "stuck" waiting for any user input (ex. mouse move or click in the window area). As it receives the input, it runs without any further problems. While this doesn't necessarily render the program nonfunctional, it seems highly unprofessional and... ugly. Oh, and what I mean by "stuck" is that the background processes still run without problems, so the loop runs as intended - however, the WM_PAINT isn't called for some reason.

Here is the current code:

header file:

#define PROGRAM_FPS 30
#define GWINDOW_STYLE (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)

class Application
{
public:
    Application();
    ~Application();

    // Register the window class and call methods for instantiating drawing resources
    HRESULT Initialize();

    // Process and dispatch messages
    void RunMessageLoop();

    static inline void SafeRelease(IUnknown * _X)
    {
        if(_X != NULL)
            _X->Release();
    };

    HRESULT SetFullscreen(bool);

private:

    // Time (ms) between frames of the application
    // Initiation: m_AppFPS_Div( (DWORD)1000.0 / (DWORD)PROGRAM_FPS )
    const DWORD m_AppFPS_Div;

    // Initialize device-independent resources.
    HRESULT CreateDeviceIndependentResources();

    // Initialize device-dependent resources.
    HRESULT CreateDeviceResources();

    // Release device-dependent resource.
    void DiscardDeviceResources();

    // Draw content.
    HRESULT OnRender();

    HRESULT LoadBitmapFromFile(
        ID2D1RenderTarget*,
        PCWSTR,
        UINT,
        UINT,
        ID2D1Bitmap **
        );

    // The windows procedure.
    static LRESULT CALLBACK WndProc(
        HWND hWnd,
        UINT message,
        WPARAM wParam,
        LPARAM lParam
        );
    HWND m_hwnd;
    ID2D1Factory * m_pDirect2dFactory;
    ID2D1HwndRenderTarget * m_pRenderTarget;
    IWICImagingFactory *m_pIWICFactory;
    ID2D1SolidColorBrush * m_pWhiteBrush;
    ID2D1SolidColorBrush * m_pGrayBrush;
    ID2D1LinearGradientBrush * m_pLinearGradientBrush;
    ID2D1Bitmap *m_LoadingPicture;
};

parts of the .cpp file

void Application::RunMessageLoop()
{
    HACCEL hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_WIN32APP));
    DWORD screen_last_refresh = 0;
    MSG msg;
    for(bool applicationRunning = true; applicationRunning;)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if(msg.message == WM_QUIT)
            {
                applicationRunning = false;
            }

            if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        /*** HERE I RUN VARIOUS BACKGROUND PROCESSES ***/

        while((GetTickCount() - screen_last_refresh) < m_AppFPS_Div)
            Sleep(2);
        InvalidateRect(msg.hwnd, NULL, false);
        screen_last_refresh = GetTickCount();
    }
}

LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT result = 0;
    if (message == WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        Application *pApp = (Application *)pcs->lpCreateParams;

        ::SetWindowLongPtrW(hwnd, GWLP_USERDATA, PtrToUlong(pApp) );
        InvalidateRect(hwnd, NULL, false);
        result = 1;
    }
    else
    {
        Application *pApp = reinterpret_cast<Application *>(static_cast<LONG_PTR>(
            ::GetWindowLongPtrW(
                hwnd,
                GWLP_USERDATA
                )));

        bool wasHandled = false;

        if (pApp)
        {
            switch (message)
            {
            case WM_SIZE:
                {
                    UINT width = LOWORD(lParam);
                    UINT height = HIWORD(lParam);
                    pApp->OnResize(width, height);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DISPLAYCHANGE:
                {
                    InvalidateRect(hwnd, NULL, FALSE);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_PAINT:
                {
                    pApp->OnRender();
                    ValidateRect(hwnd, NULL);
                }
                result = 0;
                wasHandled = true;
                break;

            case WM_DESTROY:
                {
                    PostQuitMessage(0);
                }
                result = 1;
                wasHandled = true;
                break;
            }
        }

        if (!wasHandled)
        {
            result = DefWindowProc(hwnd, message, wParam, lParam);
        }
    }
    return result;
}

HRESULT Application::OnRender()
{
    HRESULT hr = S_OK;
    hr = CreateDeviceResources();
    if (SUCCEEDED(hr))
    {
        m_pRenderTarget->BeginDraw();
        m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

        /** HERE I HANDLE THE OPENING ANIMATION **/

        hr = m_pRenderTarget->EndDraw();
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }
    return hr;
}

So, I've tested it on 2 computers (both using Win 8.1) with VS 2012 Ultimate and VS 2013 Professional, run debug tests back and forth, stripping certain parts of the program, searched MDSN, Google and StackExchange - to no avail. This freeze also happens when I handle WM_COMMAND of a child window. There was also no such issue when I tried implementing GDI+, but it proved to be highly ineffective, hence the switch to DirectX. Aside from that, the program runs flawlessly. I hope I've provided enough information.

1

There are 1 best solutions below

2
On

You have not been clear about what you are doing to run "various background processes." That is probably where the "stuck" issue comes from. A suggestion is to move that out of the message loop. Instead, call SetTimer during window initialization and then do a bit of your background process work each time the WM_TIMER message comes in.

And, when you want a WM_PAINT to come in call InvalidateRect().