请查看下面的编辑更新。原始问题已被修改!
我有一个使用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是在设备创建时加载的:如果使用适当的层标志创建设备,则自动加载此DLL
。因此,我检查了一下我的设备创建方法是否在执行过程中被调用,但它们没有被调用。我的游戏中只有两个位置存在设备创建,而且都没有被访问。
编辑:经过检查MSDN,似乎这个dll只是一个调试dll,并且可能只是为了打印出警告本身而加载,没有其他目的。
明确的复现情况:
1)使用Alt+Enter 6次(3个全屏过渡周期,双向,从窗口开始),在第7次时加载dll并弹出警告。无论是否聚焦于窗口,都会发生这种情况。
方法调用层次结构摘要(进入全屏模式):
1)ToggleFullscreen() - 我的方法,仅由Alt+Enter调用
2)ResizeTargetAndBuffers() - 我的方法,下面是子方法
3)DXGISwapChain->ResizeTarget(frontBufferDesc) 将前缓冲区调整为指定的分辨率
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) 避免刷新率问题。根据MSDN最佳实践,RefreshRate成员被清零。
9)DXGISwapChain->GetFullscreenState() 确定全屏状态
方法调用层次结构摘要(进入窗口模式):
1) ToggleFullscreen() - 这是我的方法,仅由Alt+Enter调用
2) ResizeTargetAndBuffers() - 这是我的方法,下面有子方法
3) DXGISwapChain->ResizeTarget(backBufferDesc)将前缓冲区的大小调整为指定的分辨率
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()以确定全屏状态
这将会产生相当严重的影响。我的低级键盘钩子不再被调用,所以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%,即当您未使用远程桌面时,因此我将仅关注它。我将其赋予适配器,驱动程序类型为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而没有任何问题发生,但是在工作中,第7次Alt+Enter导致关键钩子不再被调用并且缓冲区问题发生。
编辑更新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语句表明,键钩被调用直到第六次按下Alt+Enter之后。这是第三次转换到窗口模式。我之前认为必须第四次进入全屏模式才会出现问题,但实际上不需要。MyClassVar调用的所有方法都是内联的,以使键钩尽可能快地运行,因为我知道Windows键钩有一个超时时间。Alt+Enter的实际处理由MyClass中的一个线程处理。
此外,有人能使这不成为社区维基吗?我认为这个问题过于具体,不能作为维基使用。它变成维基的唯一原因是我经常进行编辑更新。