c++ - Win32 窗口在第一次绘制后冻结(directx 11)

Posted

技术标签:

【中文标题】c++ - Win32 窗口在第一次绘制后冻结(directx 11)【英文标题】:c++ - Win32 window freezes after the first draw (directx 11) 【发布时间】:2015-06-02 19:13:51 【问题描述】:

我有一个标准的 win32 窗口,我使用D2D1 绘制。一切都响应迅速并且运行顺利。我的问题如下:创建窗口后,它调用WM_PAINT 一次,然后“卡住”等待任何用户输入(例如鼠标移动或单击窗口区域)。当它接收到输入时,它运行没有任何进一步的问题。虽然这并不一定会使程序无法运行,但它似乎非常不专业并且...... 。哦,我所说的“卡住”是指后台进程仍然可以正常运行,因此循环按预期运行 - 但是,WM_PAINT 由于某种原因没有被调用。

这是当前代码:

头文件:

#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;
;

.cpp 文件的一部分

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;

所以,我已经在 2 台计算机(均使用 Win 8.1)上使用 VS 2012 Ultimate 和 VS 2013 Professional 对其进行了测试,来回运行调试测试,剥离程序的某些部分,搜索 MDSN、Google 和 StackExchange -徒劳无功。当我处理子窗口的WM_COMMAND 时,也会发生这种冻结。当我尝试实现 GDI+ 时也没有这样的问题,但事实证明它非常无效,因此切换到 DirectX。除此之外,该程序运行完美。我希望我已经提供了足够的信息。

【问题讨论】:

与其在主循环中使用InvalidateRect 触发渲染,不如直接调用OnRender 函数? @JonathanPotter 这就是我现在正在做的事情。关键是,一旦我移动鼠标或按键,这种方法就可以完美运行。我想知道为什么会这样。 【参考方案1】:

您还不清楚自己在做什么来运行“各种后台进程”。这可能就是“卡住”问题的来源。一个建议是将其移出消息循环。相反,在窗口初始化期间调用 SetTimer,然后在每次 WM_TIMER 消息进入时执行一些后台进程工作。

而且,当您想要 WM_PAINT 调用 InvalidateRect() 时。

【讨论】:

“各种后台进程”当然是指它们与这个问题无关。正如我所说,整个FOR 循环是正常工作的,所以InvalidateRect 仍然每秒被调用30 次,而WM_PAINT 没有被执行。逐步调试方法以及直接观察表明了这一点。应用程序的其余部分表现得好像没有任何问题,但拒绝绘图。 WM_PAINT 是低优先级消息。它一直保持到消息队列为空。如果您不断处理其他消息流,WM_PAINT 将不会进入。

以上是关于c++ - Win32 窗口在第一次绘制后冻结(directx 11)的主要内容,如果未能解决你的问题,请参考以下文章

位图未在 Win32 C++ 中绘制

Win32 窗口在几分钟后不绘制

Win32知识之窗口绘制.窗口第一讲

Win32 GUI c++ .bmp 图像未显示

“你好世界!”在 C++ 中冻结

win32绘制自定义类窗口导致绘制11个窗口的解决办法