2015-10-20 40 views
6

我只是偶然發現了這個小惱人的行爲,同時在示例程序中添加了全屏支持。DirectX11 Swapchain和窗口失去全屏狀態

創建一個全屏窗口可以工作,但只要我在包含全屏窗口的輸出中移動任何窗口(來自另一個應用程序),窗口就會自動切換回窗口。

有什麼辦法來防止這種行爲(所以全屏窗口不會回到窗口)?

作爲參考,這是一個小的獨立示例(因此可以輕鬆地複製問題)。

此外,如果這是有用的,我在Windows 8.1上運行。

我已經試圖改變WindowAssociationFlags和SwapChainFlags,都沒有成功,像使用的,而不是丟棄FlipSequential

SharpDX.DXGI.Factory2 factory = new SharpDX.DXGI.Factory2(); 
SharpDX.DXGI.Adapter adapter = factory.GetAdapter(0); 

var renderForm1 = new RenderForm("Form 1"); 
factory.MakeWindowAssociation(renderForm1.Handle, SharpDX.DXGI.WindowAssociationFlags.IgnoreAll); 

Device device = new Device(adapter, DeviceCreationFlags.BgraSupport); 

SharpDX.DXGI.SwapChainDescription sd = new SharpDX.DXGI.SwapChainDescription() 
{ 
    BufferCount = 2, 
    ModeDescription = new SharpDX.DXGI.ModeDescription(0, 0, new SharpDX.DXGI.Rational(50, 1), SharpDX.DXGI.Format.R8G8B8A8_UNorm), 
    IsWindowed = true, 
    OutputHandle = renderForm1.Handle, 
    SampleDescription = new SharpDX.DXGI.SampleDescription(1,0), 
    SwapEffect = SharpDX.DXGI.SwapEffect.Discard, 
    Usage = SharpDX.DXGI.Usage.RenderTargetOutput, 
    Flags = SharpDX.DXGI.SwapChainFlags.None 
}; 

var swapChain1 = new SharpDX.DXGI.SwapChain(factory, device, sd); 

renderForm1.Left = 1922; //Just hardcoded here to move window to second screen 
renderForm1.Width = 1920; 
renderForm1.Height = 1080; 
renderForm1.FormBorderStyle = FormBorderStyle.None; 

swapChain1.SetFullscreenState(true, null); 
swapChain1.ResizeBuffers(2, 1920, 1080, SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.DXGI.SwapChainFlags.AllowModeSwitch); 

var resource = Texture2D.FromSwapChain<Texture2D>(swapChain1, 0); 
var renderView = new RenderTargetView(device, resource); 

RenderLoop.Run(renderForm1,() => 
{ 
    device.ImmediateContext.ClearRenderTargetView(renderView, new SharpDX.Color4(1, 0, 0, 1)); 
    swapChain1.Present(1, SharpDX.DXGI.PresentFlags.None); 
}); 

編輯: 我也嘗試了C++的樣品(剛剛拍攝的DirectX11基礎教程微軟和添加全屏切換),這導致了相同的行爲,所以這不是SharpDX的特定問題。

我查看了消息循環,一旦發生這種情況,第一個全屏模式將變回窗口狀態,並且我收到一條WM_DISPLAYCHANGE消息)。

回答

3

這聽起來像預期的行爲。如果您有全屏「獨佔」模式交換鏈並且關聯的窗口失去焦點,系統會自動將應用程序從全屏模式切換回窗口模式,設計爲

對於單個顯示器,只要您的應用程序的窗口大小足以填滿顯示屏,它就會工作。用戶不能使用鼠標來改變窗口的焦點,而且它需要像ALT + TAB那樣來切換焦點。

對於多個顯示器,這是一個真正的問題。如果您在另一臺顯示器上單擊另一個窗口,應用程序將失去焦點,並且全屏模式會再次切換。還有一些限制會阻止您在多臺顯示器上設置全屏「獨佔」模式。

此外,在Windows Vista或更高版本中,'獨佔'模式的概念是一種幻想:無論如何GPU總是共享的。無論是全屏還是窗口化交換鏈,「重點」應用都會獲得優先權。

你爲一個完整的畫面風格體驗三種選擇一個Windows桌面應用程序:

  1. 使用傳統的全屏幕「獨佔」模式,大小以填充顯示,與設置顯示模式沿窗這可能不是用戶通常爲Windows設置的內容。這裏你有IsWindowed = false
  2. 您設置窗口大小以填滿整個顯示屏(即最大化)。您可以使用窗口樣式來確保該窗口沒有框架,從而獲得全屏風格的體驗(WS_POPUP)。這裏您有IsWindowed = true,您應該確保設置DXGI_MWA_NO_ALT_ENTER以避免DXGI嘗試帶您使用1個案例。
  3. 您可以使用與IsWindowed = true一樣的2和無邊界窗口大小來匹配屏幕,但是您將顯示模式更改爲系統默認值以外的值。這通常被稱爲「假全屏」。每次退出應用程序時,顯示模式都會改回。

1所有問題都有我們剛纔描述的多任務和焦點問題。 2和3允許系統通知和其他彈出窗口顯示在遊戲中,而不是強制模式切換。 2和3在多顯示器設置中也可以更好地工作,您可以在一臺顯示器上播放遊戲,並在另一臺顯示器上使用其他應用程序。對於多任務處理的大多數人來說,喜歡具有邊框邊框的經典窗口風格。

Windows Store UWP全屏模式的概念基本上與上面的2相同。您無法使用UWP更改顯示模式。

調試全屏設置非常具有挑戰性。使用多個監視器,2和3可以在另一個屏幕上與調試器一起工作。對於真正的全屏獨佔模式,真正唯一的選擇是使用另一臺PC的遠程調試。

1和3的另一個問題是,您可以將顯示模式設置爲不會與顯示器同步,從而使用戶無法使用UI且無法退出系統。理想情況下,通過正確的驅動程序設置,DXGI枚舉列表不包含不受支持的模式,但它是需要注意的。因此,用於選擇顯示模式的用戶界面應該有一個超時時間,並且如果顯示模式在將來的某個時間點未能同步,則應該確保有一種合理的方法來用鍵盤中止應用程序。使用現有的顯示模式,就像我們在上面2中做的那樣,始終是最安全的選擇。

使用上述全屏獨佔模式(1)的主要原因是試圖獲得後置緩衝器/前置緩衝器的'翻轉'而不是'blit'。對於大多數現代系統來說,這是微不足道的性能差異。經歷使用它的痛苦的另一個原因是SLI/Crossfire多GPU渲染轉向單個顯示器。還需要其他一些優化才能真正實現該場景的運行,並且它非常適合。您應該查看詳細信息的供應商優化指南。

大多數現代遊戲默認使用假全屏而不是全屏「獨家」模式。它們提供了使用真正的窗口模式的能力,因爲許多用戶希望能夠在播放時進行多任務(如在線查看提示,使用即時消息或外部語音聊天等)。希望支持針對SLI/Crossfire調諧的高性能遊戲的AAA Windows桌面遊戲將提供全屏「獨家」模式,但這需要一些工作才能充分發揮作用,並且需要比DXGI代碼更多的工作。

DXGI OverviewDirectX Graphics Infrastructure (DXGI): Best Practices

+0

感謝您的好奇心(思考windows8.1或windows 10),你會遇到同樣的行爲使用DirectX9全屏(不是我打算回滾到它,但好奇它)嗎? – catflier

+0

我認爲在模擬Direct3D 9全屏模式方面存在一些怪癖 - 請記住,「丟失的設備」方案在Windows Vista +上並不存在,因此此行爲也被模擬 - 但我相信它基本上同樣的情況。 –

+0

默認情況下,在dx9中它實際上更糟糕,因爲只要它失去了焦點,它就會隱藏自身,但至少它可以很容易被欺騙,所以現在似乎唯一的方法將是共享資源回到它... – catflier

2

多次嘗試和考驗後,這裏是我用了不同的解決方法,但並不理想,但一切都弄好比獲得一個模式改變更好。

1 /強制光標在全屏窗口的中間,用鍵盤快捷鍵重新獲得控制權。 這並不理想,因爲我們的部分運行時我們無法做任何事情,但至少可以防止意外的「災難點擊」。它也不妨礙鍵盤交互。

2 /使用具有共享紋理的DX9渲染器。DX9 Swapchain可以將其父窗口設置爲桌面,因此在移動到其他位置時不會失去焦點。 將焦點放在頂部的窗口顯示移動時可見的小邊框,但這比失去一切更容易接受。 沒有將來的證據,但猜測將保持實際一段時間。

3 /留在Windows 7和禁用DWM服務:

在Windows 8不工作了,但在我的使用情況,因爲大多數媒體公司我工作仍然在Windows 7中,它一直是一個有效的解決方案至少5至10年。

4 /強制DX11窗口上前景

基本上連續地調用SetForegroundWindow避免另一個窗口取得焦點。

5 /在演示級別阻止模式切換。

由於我的應用我介紹的時候出現,我用下面的程序(之前叫至今)訪問

- 獲得前臺窗口句柄(使用GetForegroundWindow),如果前景手柄是我們的全屏窗口,只是打電話像平常一樣。

如果前景句柄不是我們的全屏窗口,請執行以下操作。請注意,不需要可視性檢查,因爲即使是不可見的重疊窗口也會導致全屏丟失! (嚴重的是,這太糟糕了......)

- 如果我們的前景窗口與顯示器重疊,請進行驗證: 請致電GetWindowRect獲取邊界,並與顯示器位置進行交集。

或者,使用DXGI_PRESENT_TEST標誌調用交換鏈上的存在。如果窗口是重疊的,當前呼叫將返回DXGI_STATUS_OCCLUDED

如果窗口重疊,或者隱藏,或在另一臺顯示器移動它(任何地方,所以它不重疊): ShowWindowSetWindowPos是aperfect適合這個任務。

在循環中重複該測試呈現調用,直到它不返回被遮擋的狀態(這很重要,因爲Windows可能沒有立即處理消息);一旦遮擋標誌消失,像平常一樣調用Present。