4

请参阅下面的编辑更新。原问题已修改!

我有一个使用 DX11 设备的工作窗口。当我尝试使用 Alt+Enter 进入全屏模式时,我的问题就出现了。如果窗口没有聚焦,我会得到一个调试输出,上面写着:

'MyGame.exe': Loaded 'C:\Windows\SysWOW64\D3D10SDKLayers.DLL', Cannot find or open the PDB file

然后是警告

DXGI Warning: IDXGISwapChain::Present: Fullscreen presentation inefficiencies incurred due to application not using IDXGISwapChain::ResizeBuffers appropriately, specifying a DXGI_MODE_DESC not available in IDXGIOutput::GetDisplayModeList, or not using DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH

我相当肯定 DX11 游戏不应该加载 D3D10SDKLayers.dll,尤其是在执行过程中。从 MSDN 文档中,我收集到这个 dll 是在设备创建时加载的: if a device is created with the appropriate layer flag, this DLL is loaded automatically. 因此,我检查了在执行过程中是否以某种方式调用了我的设备创建方法,但事实并非如此。我的游戏中只有 2 个位置存在设备创建,并且两个位置都没有被击中。编辑:检查 MSDN 后,似乎这个 dll 只是一个调试 dll,可能只是被加载以打印出警告本身,没有其他目的。

显式复制案例:

1)Alt+Enter 6次(3个全屏转换周期,双向,开始窗口),第7天加载dll并弹出警告。无论窗口聚焦如何,都会发生这种情况。

方法调用层次结构摘要(全屏显示):

1) ToggleFullscreen() - 我的方法,只有 Alt+Enter 调用的方法
2) ResizeTargetAndBuffers() - 我的方法,下面的子方法
3) DXGISwapChain->ResizeTarget(frontBufferDesc) 将前缓冲区大小调整为指定的 res
4) DXGISwapChain->GetFullscreenState () 确定全屏状态
5) DXGISwapChain->SetFullscreenState(TRUE, NULL) 进入全屏状态
6) ResizeDXGIBuffers(width, height, TRUE) 我的方法,调整后台缓冲区大小,子方法低于
7) DXGISwapChain->ResizeBuffers(count, width, height, format, flags) 来调整后台缓冲区的大小
8) DXGISwapChain->ResizeTarget(frontBufferDesc) 防止刷新率问题。RefreshRate 成员按照 MSDN 最佳实践归零。
9) DXGISwapChain->GetFullscreenState() 判断全屏状态

方法调用层次结构摘要(窗口化):

1) ToggleFullscreen() - 我的方法,仅由 Alt+Enter 调用的方法
2) ResizeTargetAndBuffers() - 我的方法,下面的子方法
3) DXGISwapChain->ResizeTarget(backBufferDesc) 将前缓冲区大小调整为指定的 res
4) DXGISwapChain->GetFullscreenState () 确定全屏状态
5) DXGISwapChain->SetFullscreenState(FALSE, NULL) 进入全屏状态
6) DXGISwapChain->ResizeTarget(backBufferDesc) 将前端缓冲区的大小调整为窗口的分辨率(帮助解决了一些分辨率问题)
7) ResizeDXGIBuffers(width, height , FALSE) 我的方法,调整后台缓冲区大小,子方法低于
8) DXGISwapChain->ResizeBuffers(count, width, height, format, flags) 调整后台缓冲区大小
9) DXGISwapChain->GetFullscreenState() 确定全屏状态

这件事的后果是相当严重的。我的捕获 Alt+Enter 的低级键盘钩子不再被调用,因此 Windows 能够进行自动 Alt+Enter 处理,这完全绕过了我的 ToggleFullscreen 方法并将窗口设置为桌面分辨率。这会导致缓冲区的大小错误(因为我没有设置它们,所以 windows 做了),导致效率低下警告,并弄乱了我的程序中的变量,这些变量不再正确了解缓冲区大小以及窗口是否是全屏的或不。

关于可能导致这种情况的任何想法?

PS如果您需要代码示例,请具体说明您想看到的内容,如果可能,我会尝试将其发布。我不能把整个代码清单。

编辑:设备创建代码如下。


         hr = D3D11CreateDevice(    pAdapter,
                                    driverType,
                                    NULL,
                                    rDeviceSettings.m_CreateFlags,
                                    &rDeviceSettings.m_eD3DDeviceFeatureLevel,
                                    1,
                                    D3D11_SDK_VERSION,
                                    &pGraphicsDevice,
                                    &eFeatureLevel,
                                    &pDeviceContextI
            );


            if ( FAILED( hr ) ) {
                pAdapter = NULL;
                // Remote desktop does not allow you to enumerate the adapter.  In this case, we let D3D11 do the enumeration.
                if ( driverType == D3D_DRIVER_TYPE_UNKNOWN ) { 
                    hr = D3D11CreateDevice( pAdapter,
                                            driverType,
                                            NULL,
                                            rDeviceSettings.m_CreateFlags,
                                            &rDeviceSettings.m_eD3DDeviceFeatureLevel,
                                            1,
                                            D3D11_SDK_VERSION,
                                            &pGraphicsDevice,
                                            &eFeatureLevel,
                                            &pDeviceContextI
                    );
                }

第一次调用成功率 99%,即当你不使用远程桌面时,所以我只关注它。我给它适配器,driverType 为 D3D_DRIVER_TYPE_HARDWARE,m_CreateFlags 为 D3D11_CREATE_DEVICE_DEBUG,m_eFeatureLevel 为 D3D_FEATURE_LEVEL_11_0。非常标准的调用,它总是成功的。

编辑更新 1:经过大量调试后,我发现当加载 dll 并弹出低效率警告时,会出现一些非常有趣的情况。它们在下面列出:

1) VS2010 调试器不再触发 key hook 中的断点。
2) 打印输出在键钩中不再起作用。
3) 如果窗口在之前可重新调整大小,则窗口可能变得不可调整大小
4) 窗口可能变得不可移动。
5)三个线程退出。

编辑更新 2:第一次编辑更新可能有不正确的假设;如果我找到它,我会删除它。事实证明,我的低级键钩子不再被调用(我认为,因为其中没有断点或打印语句),所以如果我的程序中的某些东西意外地取消注册,那么这将导致上述所有问题. 明天测试这个...

编辑更新 3:我不确定发生了什么。我在家用电脑和工作电脑上测试了同一个干净的项目,得到了不同的结果。在家里,我可以无限期地 Alt+Enter 没有任何问题发生,但在工作中 Alt+Enter 第七次导致不再调用 key hook 并且发生缓冲区问题。

编辑更新 4:更多测试(在工作中)。在第三次转换到窗口模式后,关键挂钩肯定会被删除。它根本不再在 key hook 方法内部打印,并且无论按下什么键,都不会触发断点。我想我将就此提出一个单独的问题,因为我上面描述的所有其他问题只是这个关键钩子没有调用 ToggleFullscreen() 的后果。作为参考,我在下面提供了关键挂钩代码。


LRESULT _stdcall MyClass::WindowsKeyHook( s32 nCode, WPARAM wParam, LPARAM lParam ) {
    printf("Key hook called, nCode: %d. ", nCode);
    if( nCode < 0 || nCode != HC_ACTION )  { // do not process message 
        return CallNextHookEx( MyClassVar.GetWindowsKeyHook(), nCode, wParam, lParam );
    }
    printf(" Key hook status ok.\n");

    BOOL bEatKeystroke = FALSE;
    KBDLLHOOKSTRUCT* p = ( KBDLLHOOKSTRUCT* )lParam;
    switch( wParam ) {
        //NOTE: Alt seems to be a system key when it is PRESSED, but a regular key when it is released...
        case WM_SYSKEYDOWN:
            if(p->vkCode == VK_MENU || p->vkCode == VK_LMENU || p->vkCode == VK_RMENU) {
                MyClassVar.SetAltPressed(TRUE);
            }
            if(MyClassVar.IsAltPressed() && p->vkCode == VK_RETURN) {
                bEatKeystroke = TRUE;
                MyClassVar.SetAltEnterUsed(TRUE);
                printf("Alt+Enter used.\n");
            }
            break;
        case WM_SYSKEYUP:
            //NOTE: releasing alt+enter causes a SYSKEYUP message with code 0x13: PAUSE key...
            break;
        case WM_KEYDOWN:
            break;
        case WM_KEYUP: {
            if(p->vkCode == VK_MENU || p->vkCode == VK_LMENU || p->vkCode == VK_RMENU) {
                MyClassVar.SetAltPressed(FALSE);
            }
            bEatKeystroke = ( !MyClassVar.IsShortcutKeysAllowed() &&
                                ( p->vkCode == VK_LWIN || p->vkCode == VK_RWIN ) );
            break;
        }
    }

    if( bEatKeystroke ) {
        return 1;
    }
    else {
        return CallNextHookEx( MyClassVar.GetWindowsKeyHook(), nCode, wParam, lParam );
    }
}

printf 语句显示,直到第 6 次 Alt+Enter 之后才调用键挂钩。这是第三次进入窗口模式。正如我之前所想的那样,我没有必要第四次全屏显示问题。MyClassVar 调用的所有方法都是内联的,以使键挂钩尽可能快地运行,因为我知道 Windows 键挂钩有超时。Alt+Enter 的实际处理由 MyClass 中的一个线程处理。

另外,有人可以使它不是社区维基吗?我认为这个问题太具体了,不能用作维基。它成为一个的唯一原因是我一直在通过编辑定期更新它。

4

1 回答 1

1

我通过完全取出钥匙钩解决了这个问题。处理 Alt+Enter 的正确方法是创建交换链,然后调用IDXGIFactory1::MakeWindowAssosciation( m_hWnd, DXGI_MWA_NO_ALT_ENTER). 然后,您可以使用 Windows 消息过程中的您自己的代码来处理 Alt+EnterWM_SYSKEYDOWNWM_KEYUP。我希望这对其他人有帮助!我花了很多时间让它工作,所以如果你很难让它在你自己的应用程序中工作,给我发消息,我会尽力帮助你!

于 2012-02-03T01:54:16.190 回答