Winapi 在移动/调整大小期间使用DirectX重新绘制窗口

Winapi 在移动/调整大小期间使用DirectX重新绘制窗口,winapi,directx,directx-11,Winapi,Directx,Directx 11,我已经完成了本教程的学习,并已全部完成: 结果是一个背景颜色不断变化的窗口。问题是,当窗口被拖动时,颜色停止变化。我尝试将以下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

我已经完成了本教程的学习,并已全部完成:

结果是一个背景颜色不断变化的窗口。问题是,当窗口被拖动时,颜色停止变化。我尝试将以下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部分的颜色从未演变,无论我拖动窗口多长时间,它都保持不变)


有什么提示吗?

更好的选择是在调整缓冲区大小时不要绘制。反复调整backbuffer的大小通常没有多大价值。只需等到调整大小完成后再调整缓冲区大小

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;
此处
勾选
处理计时器更新和渲染

有关完整示例,请参见

更新:如果在调整大小期间“空白窗口”困扰您,但您对调整大小期间的默认行为
DXGI\u SCALING\u STRETCH
没有问题,您可以将上面的
WM\u PAINT
替换为:

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

我也遇到了同样的问题。我认为解决方案会很复杂。但是,不是。这是关于mesage pool的。DX应该使用相同的线程,事实上,您在该循环中使用了渲染(例如:myRender(){..}),在我的例子中,Frame();是我用于渲染的bool returnable,其中包含所有操作:

    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回调句柄中:

            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...
                }
以下是结果的视频:

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


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

@Alex:不是这样。问题不是消息循环只在消息队列为空时更新。问题是,在移动/调整窗口大小时,消息循环根本不运行。在移动/调整窗口大小时,代码输入一个模式(嵌套)循环,类似于模式对话框。在操作完成之前,外部消息循环将不会运行。代码应按发布的方式工作,尽管
重画窗口中的
RDW_UPDATENOW
标记与消息循环行为更为相似。@Alex:移动/调整大小期间的模式循环将发送消息。自定义循环实现在模式循环内不会运行。但是,等等,计时器消息的优先级不是比绘制消息低吗?如果计时器正在启动,但没有发送绘制消息,那么会发生什么?调整大小期间的内部模式循环与整个系统中的其他消息循环完全不同吗?@andlabs:你是从哪里得到这个想法的,没有生成任何绘画信息?@IIinspectable我想是从Alex的评论中可以看出的,但是现在它们被删除了,所以我不能引用它们:/鉴于它描述的背景没有改变,原始帖子中描述的行为似乎也暗示了这一点。
            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...
                }