请参阅下面的编辑更新。原问题已修改!
我有一个使用 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 中的一个线程处理。
另外,有人可以使它不是社区维基吗?我认为这个问题太具体了,不能用作维基。它成为一个的唯一原因是我一直在通过编辑定期更新它。