【问题标题】:Redraw window using DirectX during move/resize在移动/调整大小期间使用 DirectX 重绘窗口
【发布时间】:2015-02-09 09:52:21
【问题描述】:

我已按照本教程进行操作,一切正常:http://www.braynzarsoft.net/index.php?p=InitDX11

结果是一个背景颜色不断变化的窗口。问题是在拖动窗口时颜色停止变化。我尝试将以下 case 语句(以各种组合)添加到 WndProc 回调,但无济于事:

case WM_ENTERSIZEMOVE:
  SetTimer(hwnd, 1, USER_TIMER_MINIMUM, NULL);
  return 0;

case WM_EXITSIZEMOVE:
  KillTimer(hwnd, 1);
  return 0;

case WM_TIMER:
  RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
  return 0;

case WM_PAINT:
  UpdateScene();
  DrawScene();
  return 0;

以上导致d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor) 出现异常,但我也尝试将WM_PAINT 案例合并到WM_TIMER 案例中,我得到的只是在自然窗口背景颜色和当前颜色之间闪烁DX 场景(闪烁的 DX 部分的颜色从未发生变化,无论我拖动窗口多久,它都保持不变)。

有什么建议吗?

【问题讨论】:

  • @Alex:不是这样的。问题不在于消息循环仅在消息队列为空时更新。问题是,在移动/调整窗口大小时,消息循环根本不运行。当移动/调整窗口大小时,代码进入一个模态(嵌套)循环,类似于模态对话框。在操作完成之前,外部消息循环不会运行。尽管RedrawWindow 调用中的RDW_UPDATENOW 标志更类似于消息循环行为,但代码应该可以正常工作。
  • @Alex:移动/调整大小期间的模态循环将很好地发送消息。它是在模态循环中不会运行的自定义循环实现。
  • 但是等等,定时器消息的优先级不是比绘制消息低吗?如果计时器正在触发但没有发送绘画消息,那么发生了什么?调整大小期间的内部模态循环是否与整个系统中的所有其他消息循环完全不同?
  • @andlabs:你从哪里得到的想法,没有生成绘画消息?
  • @IInspectable 我认为来自 Alex 的 cmets,但它们现在已被删除,因此我无法引用它们:/ 鉴于它如何描述背景,原始帖子中描述的行为似乎也暗示了这一点不变。

标签: winapi directx directx-11


【解决方案1】:

更好的选择是在调整大小时不绘制。一遍又一遍地调整后备缓冲区的大小通常没有多大价值。等到调整大小完成后再调整缓冲区的大小。

static bool s_in_sizemove = false;
static bool s_in_suspend = false;
static bool s_minimized = false;

case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);
    break;

case WM_SIZE:
    if (wParam == SIZE_MINIMIZED)
    {
        if (!s_minimized)
        {
            s_minimized = true;
            if (!s_in_suspend)
                OnSuspending();
            s_in_suspend = true;
        }
    }
    else if (s_minimized)
    {
        s_minimized = false;
        if (s_in_suspend)
            OnResuming();
        s_in_suspend = false;
    }
    else if ( !s_in_sizemove )
        OnWindowSizeChanged();
    break;

case WM_ENTERSIZEMOVE:
    s_in_sizemove = true;
    break;

case WM_EXITSIZEMOVE:
    s_in_sizemove = false;
    OnWindowSizeChanged();
    break;

case WM_GETMINMAXINFO:
    {
        auto info = reinterpret_cast<MINMAXINFO*>(lParam);
        info->ptMinTrackSize.x = 320;
        info->ptMinTrackSize.y = 200;
    }
    break;

您必须释放所有后备缓冲区和深度缓冲区引用并在 OnWindowSizedChange 中重新创建它们。

对于大多数“实时”图形应用程序,实际渲染是作为消息泵的一部分完成的:

// Main message loop
MSG msg = { 0 };
while (WM_QUIT != msg.message)
{
    if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else
    {
        Tick();
    }
}

这里Tick 处理计时器更新和渲染。

有关完整示例,请参阅Direct3D Win32 Game Visual Studio template

更新:如果“空白窗口”在调整大小期间困扰您,但您可以在调整大小期间使用 DXGI_SCALING_STRETCH 的默认行为,您可以将上面的 WM_PAINT 替换为:

case WM_PAINT:
    if (s_in_sizemove)
    {
        game->Tick();
    }
    else
    {
        hdc = BeginPaint(hWnd, &ps);
        EndPaint(hWnd, &ps);
    }
    break;

【讨论】:

    【解决方案2】:

    我有同样的问题。我认为解决方案会很复杂。然而,没有。那是关于消息池。 DX 应该使用相同的线程,实际上您在该循环中使用您的渲染(例如: myRender(){..} )在我的情况下 Frame(); 是我用于渲染的布尔可返回包含所有操作:

        MSG msg;
        bool done, result;
        
        // Initialize the message structure.
        ZeroMemory(&msg, sizeof(MSG));
        
        // Loop until there is a quit message from the window or the user.
        done = false;
        while (!done)
        {
            // Handle the windows messages.
            if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        
            }
        
            // If windows signals to end the application then exit out.
            if (msg.message == WM_QUIT)
            {
                done = true;
            }
            else
            {
                // Otherwise do the frame processing.
                result = Frame();
                if (!result)
                {
                    done = true;
                }
            }
        
        }
    

    当您调整大小时,您可以处理一些 WM_SIZEWM_MOVE 消息。在您的 LRESULT CALLBACK 句柄中:

                LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam) {
                    //...OTHERS...
                    if (umessage == WM_MOVE) {
        
                      /*Dont be confused ApplicationHandle is pointer of my SystemClass
                        which set ApplicationHandle = this in SystemClass.
                        to create a window/s and Graphics (contains all DirectX related operations).
                        Here GraphicsClass (contains all DX calls) initialized in
                        SystemClass as new class and SystemClass initialized as
                        new class at main.cpp in WINAPI. That makes a lot easier
                        for handling this kind of issues for me
        
                        In your case you will call your rendering loop instead Frame();*/
        
                      if (ApplicationHandle -> m_Graphics) {
                        RECT r;
                        GetClientRect(ApplicationHandle -> m_hwnd, & r);
                        ApplicationHandle -> m_Graphics -> clientSize = {
                          r.right - r.left,
                          r.bottom - r.top
                        };
                        //frame processing.
                        ApplicationHandle -> m_Graphics -> Frame();
                      }
                      if (umessage == WM_SIZING) {
                          if ((wparam == WMSZ_BOTTOM || wparam == WMSZ_RIGHT || wparam == WMSZ_BOTTOMRIGHT) && ApplicationHandle -> m_Graphics) {
                          /*IF WE DO NOT HANDLE wparam resize will be very laggy.*/
                            GetClientRect(ApplicationHandle -> m_hwndOWNER, & clientRect);
                            ApplicationHandle -> m_Graphics -> clientSize = {
                              clientRect.right - clientRect.left,
                              clientRect.bottom - clientRect.top
                            };
                            ApplicationHandle -> m_Graphics -> Frame();
        
                          }
                        }
                      }
                      //...OTHERS...
                    }
    

    这是结果视频:https://www.youtube.com/watch?v=vN_XPVRHuiw&feature=youtu.be

    即使您使用桌面窗口管理器 (WDM) 绘制自定义窗口,您也可以处理 WM_NCHITTEST 其他点击情况,因为只有在您调整大小时才会更新并移动,但如果您单击并按住标题或仅按住边框,则不会移动。

    此外,如果您已经在使用 DirectX,我不明白为什么要处理 WM_PAINT

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多