为什么在Windows XP中拔掉显示器后Direct3D无法恢复?

4

我遇到了一个有趣的 bug,但是一直找不到解决办法。在一个使用本地代码的窗口化 Direct3D9 程序中,我使用类似于以下的处理方式来处理设备丢失:

void MyClass::RecoverFromDeviceLost(LPDIRECT3DDEVICE9 deviceToRecover, D3DPRESENT_PARAMETERS devicePresentParams )
{


    HRESULT hr = deviceToRecover->TestCooperativeLevel();
    if(hr == D3DERR_DEVICELOST ) { 
        //Code to shutdown all D3DPOOL_DEFAULT allocated objects

    }else if(hr == D3DERR_DEVICENOTRESET){

        hr = deviceToRecover->Reset(&devicePresentParams);
        if(SUCCEEDED(hr))
        {
            //Code to rebuild all D3DPOOL_DEFAULT objects

        }

    }

}

这在Vista上运行良好,但在XP上似乎存在严重问题。如果监视器被拔掉或通过KVM切换到PC之外,我从未收到过 D3DERR_DEVICELOST。我收到的唯一的TestCooperativeLevel返回值是 D3DERR_DEVICENOTRESET。每次调用Reset都会给出一个D3DERR_INVALIDCALL。我尝试通过以下方式强制程序使用关闭代码:

...
else if(hr == D3DERR_DEVICENOTRESET){

        hr = deviceToRecover->Reset(&devicePresentParams);
        if(SUCCEEDED(hr))
        {
            //Code to rebuild all D3DPOOL_DEFAULT objects

        }else {
            //Duplicate of code to shutdown all D3DPOOL_DEFAULT objects
        }

    }
...

但是没有任何变化。这个问题似乎只影响Windows XP(到目前为止已在SP2、SP3上测试)。我正在使用2007年8月的DXSDK,目前无法更新。有人之前见过这个问题吗?或者有什么想法,为什么我不能重置我的设备?

更新: 我相信我已经找到了解决方案,但仍然对此问题中列出的第二个代码段的失败感到困惑。通过远程调试使DirectX调试运行时正常工作后,我意识到Reset函数一直失败的原因是存在未释放的资源。然而,完全相同的释放代码,按照答案所示应用,解决了这个问题。我确保程序在调用恢复函数之间没有创建D3DPOOL_DEFAULT对象。在Direct3D的结构中是否有什么可能导致像本问题中展示的代码段执行重置时出现问题的因素?

2个回答

3

我最终测试了一个使用DirectX进行图形处理的不同程序,只是想看看问题是否只出现在一个程序中。另一个应用程序在Windows XP中从监视器拔掉或KVM切换中恢复正常,没有出现任何问题。两个程序之间的主要区别在于工作的程序使用DXUT来管理Direct3d,而那个不工作的程序则全部手动管理。在查看DXUT源代码后,我注意到他们使用了一种单步设备恢复方法,该方法在返回D3DERR_DEVICENOTRESET值之前不依赖于D3DERR_DEVICELOST的返回值。以下代码似乎已经解决了这个问题:

void MyClass::RecoverFromDeviceLost(LPDIRECT3DDEVICE9 deviceToRecover, D3DPRESENT_PARAMETERS devicePresentParams )
{

    HRESULT hr = deviceToRecover->TestCooperativeLevel();
    if(hr == D3DERR_DEVICELOST ) { 
        //Device is lost and cannot be reset yet
        return;
    }


    //Code to shutdown all D3DPOOL_DEFAULT allocated objects

    hr=deviceToRecover->Reset(&devicePresentParams);
    if(SUCCEEDED(hr)){

        //Code to rebuild all D3DPOOL_DEFAULT objects

    }
}

如果显示器在较长一段时间内被拔掉(或KVM切换),此代码会产生多次重置的副作用。


我遇到了类似的情况,TestCooperativeLevel()返回D3DERR_DEVICENOTRESET,但调用Reset()会失败。当TestCooperativeLevel()返回D3DERR_DEVICELOST时,我释放了D3DPOOL_DEFAULT对象。解决方法是等待TestCooperativeLevel()返回D3DERR_DEVICENOTRESET; - Chris

2
前段时间,我开发了一个多显示器应用程序,出现了类似的症状。拔掉一个显示器并不会表现为丢失DX设备 - '设备'是一个操作系统级别的软件抽象,并且在拔掉显示器时不会丢失。
如果由于某种原因您需要在运行时检测显示器的拔插,即使使用Win32 API的EnumDisplayMonitors也不足够。如文章所述,该API查询一个驱动程序缓存,该缓存默认情况下仅在“启动、登录或打开显示属性控制面板”时更新。现在,我们只使用nVidia的,他们确实通过NvCplRefreshConnectedDevices(链接到NvCpl.dll以使用它)公开了强制缓存更新功能。无法确定ATI是否提供类似的功能,甚至是否需要。
更重要的是,您确定拔掉插头事件会强制您恢复资源吗?我猜不会。

你说得对,在Vista中,拔掉插头事件并不会强制恢复资源。我需要支持非NVIDIA显卡,但我会研究驱动程序缓存,看看是否有所发现。 - bsruth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接