见下面的编辑更新。原始问题已被修改!
我有一个使用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(计数,宽度,高度,格式,标志)以调整后台缓冲区的大小
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)将前缓冲区调整为窗口的res(帮助解决一些res问题)
7)ResizeDXGIBuffers(width,height,FALSE)我的方法,调整后面的缓冲区,下面的子方法
8)DXGISwapChain-> ResizeBuffers(计数,宽度,高度,格式,标志)以调整后台缓冲区的大小
9)DXGISwapChain-> GetFullscreenState()确定全屏状态
这种影响非常严重。我不再调用捕获Alt + Enter的低级键盘钩子,因此Windows可以进行自动Alt + Enter处理,完全绕过我的ToggleFullscreen方法并将窗口设置为桌面分辨率。这会导致缓冲区大小错误(因为我没有设置它们,windows确实如此),导致效率低下警告,并且弄乱程序中的变量,这些变量不再具有正确的缓冲区大小知识以及窗口是否正确全屏与否。
关于可能导致此问题的任何想法?
P.S。如果您需要代码示例,请具体说明您想要查看的内容,如果可能,我会尝试将其放入。我不能提出整个代码清单。
编辑:设备创建代码如下。
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调试器不再触发密钥钩子中的断点。
2)打印输出不再适用于钥匙钩
3)如果在之前重新调整大小,窗口可能变得不可调整大小
4)窗户可能变得不可移动
5)三个线程退出。
编辑更新2:第一次编辑更新可能有不正确的假设;如果我发现它,我会删除它。事实证明,我的低级键挂钩不再被调用(我认为,因为没有断点或打印语句在其中工作),所以如果我的程序中的某些东西意外取消注册,那么这将导致所有上述问题。明天测试......
编辑更新3:我不知道发生了什么事。我在家用电脑和工作电脑上测试了同样干净的项目,得到了不同的结果。在家里,我可以无限期地Alt + Enter而不会出现任何问题,但是在工作时Alt + Enter第7次导致不再调用键挂钩并且缓冲区问题发生。
编辑更新4:更多测试(在工作中)。在第3次转换到窗口模式后,肯定会删除键钩。它根本不再在键钩方法内打印,并且无论按下什么键,都不会触发断点。我想我将打开一个单独的问题,因为我上面描述的所有其他问题只是这个关键钩子没有调用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后才调用键挂钩。这是第3次转换为窗口模式。我没有必要像我之前想象的那样第四次全屏去问题。 MyClassVar调用的所有方法都是内联的,以使键钩尽可能快,因为我知道Windows键挂钩有一个超时。 Alt + Enter的实际处理由MyClass中的线程处理。
此外,有人可以使这不是社区维基吗?我认为这个问题太具体了,无法用作维基。它成为一个唯一的原因是我经常通过编辑更新它。
答案 0 :(得分:1)
我通过完全取出钥匙钩解决了这个问题。处理Alt + Enter的正确方法是创建交换链,然后调用IDXGIFactory1::MakeWindowAssosciation( m_hWnd, DXGI_MWA_NO_ALT_ENTER)
。然后,您可以使用WM_SYSKEYDOWN
和WM_KEYUP
从您的Windows邮件过程中使用自己的代码处理Alt + Enter。我希望这有助于其他人!我有一段时间让这个工作,所以如果你很难在自己的应用程序中工作,给我发消息,我会尽力帮助你!