D3D9DeviceEx CheckDeviceState() 重置后返回 S_PRESENT_MODE_CHANGED

问题描述

当我将 D3D9Ex 设备重置为 S_PRESENT_MODE_CHANGED 状态(在我合上笔记本电脑的盖子时发生)时,设备状态一直处于 S_PRESENT_MODE_CHANGED 状态。

测试设置是:

  • Windows 10 PC 笔记本电脑
  • 连接 HDMI 的外部显示
  • 重复显示模式
  • 电源设置配置为关闭盖子时不进入待机状态

应用程序代码并不太花哨,只是简单地启动了一个窗口。在渲染循环中,它只是用淡蓝色清除窗口:

#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>

#pragma comment (lib,"d3d9.lib")

IDirect3D9Ex* d3d;
IDirect3DDevice9Ex* d3ddev;
int blue;

void initD3D(HWND hWnd);
void render_frame(HWND hWnd);
void cleanD3D(void);

LRESULT CALLBACK WindowProc(HWND hWnd,UINT message,WParaM wParam,LParaM lParam);

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdshow)
{
    HWND hWnd;
    WNDCLASSEX wc;
    ZeroMemory(&wc,sizeof(WNDCLASSEX));
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = (HBrush)COLOR_WINDOW;
    wc.lpszClassName = L"WindowClass";
    RegisterClassEx(&wc);
    hWnd = CreateWindowEx(NULL,L"WindowClass",L"Our First Direct3D Program",WS_OVERLAPPEDWINDOW,300,800,600,NULL,hInstance,NULL);
    ShowWindow(hWnd,nCmdshow);
    initD3D(hWnd);
    MSG msg;
    while (TRUE)
    {
        while (PeekMessage(&msg,PM_REMOVE))
        {
            TranslateMessage(&msg);
            dispatchMessage(&msg);
        }
        if (msg.message == WM_QUIT)
            break;
        render_frame(hWnd);
    }
    cleanD3D();
    return msg.wParam;
}

void cleanD3D(void)
{
    d3ddev->Release();
    d3d->Release();
}

LRESULT CALLBACK WindowProc(HWND hWnd,LParaM lParam)
{
    switch (message)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    } break;
    }

    return DefWindowProc(hWnd,message,wParam,lParam);
}

void initD3D(HWND hWnd)
{
    Direct3DCreate9Ex(D3D_SDK_VERSION,&d3d);
    D3DPRESENT_ParaMETERS d3dpp;
    ZeroMemory(&d3dpp,sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_disCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3d->CreateDeviceEx(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&d3ddev);
}

void render_frame(HWND hWnd)
{
    HRESULT state = d3ddev->CheckDeviceState(NULL);

    if (state == S_OK) {
        blue = (blue + 2) % 255;
    }
    else if (state == S_PRESENT_MODE_CHANGED) {
        D3DPRESENT_ParaMETERS d3dpp;

        ZeroMemory(&d3dpp,sizeof(d3dpp));
        d3dpp.Windowed = TRUE;
        d3dpp.SwapEffect = D3DSWAPEFFECT_disCARD;
        d3dpp.hDeviceWindow = hWnd;

        HRESULT resetRes = d3ddev->ResetEx(&d3dpp,NULL);
        blue = 255;
    }
    
    d3ddev->Clear(0,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,blue),1.0f,0);
    HRESULT presentResult = d3ddev->Present(NULL,NULL);   // displays the created frame on the screen
}

当我启动应用程序时,一切都按预期进行。当我合上笔记本电脑的盖子时,这会发生在 render_frame 函数中:

  • CheckDeviceState(NULL) 返回 S_PRESENT_MODE_CHANGED,所以我应该Reset() 设备(如 docs 中所述)
  • Reset() 在设备上制作成功
  • Present() 返回 S_OK
  • 屏幕正确地涂上了蓝色
  • 在下一帧渲染时,CheckDeviceState(NULL) 仍然返回 S_PRESENT_MODE_CHANGED,因此生成一个新的 Reset()。这不是预期的:Reset() 应该 .

一些进一步的信息:

  • 我尝试用创建全新设备来替换 Reset(),但失败并显示 E_OUTOFMEMORY 错误
  • 在盖子关闭调用 D3D9Ex 方法 GetAdapterMonitor() 返回 NULL

我如何检测这种情况?我应该怎么做才能保持应用程序正常运行且无需每次都重置设备?

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)