2015-10-20 29 views
6

Mi sono appena imbattuto in questo piccolo comportamento fastidioso, mentre aggiungevo il supporto a schermo intero su un programma di esempio.DirectX11 Swapchain e finestra che perdono lo stato a schermo intero

La creazione di una finestra a schermo intero funziona, ma non appena si sposta una finestra (da un'altra applicazione) sull'output che contiene la finestra a tutto schermo, torna automaticamente a finestra.

C'è un modo per evitare questo comportamento (quindi la finestra a schermo intero non torna alle finestre)?

Come riferimento, questo è un piccolo esempio indipendente (quindi il problema può essere facilmente replicato).

Inoltre, se ciò è utile, sono in esecuzione su Windows 8.1.

Ho già provato a cambiare WindowAssociationFlags e SwapChainFlags, entrambi senza successo, stessa cosa che usare FlipSequential invece di Gettare

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); 
}); 

Edit: Ho anche provato un campione C++ (appena preso tutorial di base DirectX11 di Microsoft e aggiunto switch a schermo intero), questo porta allo stesso comportamento, quindi questo non è un problema specifico di SharpDX.

Ho guardato il ciclo dei messaggi, e una volta che questo si è verificato, la prima modalità a schermo intero è tornata a finestra, e ricevo un messaggio WM_DISPLAYCHANGE).

risposta

3

Sembra il comportamento previsto. Se si dispone di uno swapchain in modalità 'esclusiva' a schermo intero e la finestra associata perde la messa a fuoco, il sistema passa automaticamente l'applicazione dalla modalità a schermo intero alla modalità finestra dal modello.

Con un singolo monitor, funziona principalmente finché la finestra delle applicazioni è dimensionata per riempire il display. Gli utenti non possono usare il mouse per cambiare il focus della finestra e richiede qualcosa come ALT + TAB per cambiare lo stato attivo.

Con più monitor, è un vero problema. Se fai clic su un'altra finestra su un altro display, la tua app perde la messa a fuoco e la modalità a schermo intero viene nuovamente disattivata. Ci sono anche limitazioni che ti impediscono di impostare la modalità 'esclusiva' a schermo intero su più di un monitor.

Inoltre, in Windows Vista o versioni successive la nozione di modalità "esclusiva" è un'illusione: la GPU è sempre condivisa in ogni caso. L'applicazione "focus" ottiene la priorità sia che si tratti di uno schermo intero o di una catena di scambio con finestra.

Per un applicazioni desktop di Windows sono disponibili tre opzioni per una completa esperienza in stile schermo:

  1. utilizzare lo schermo tradizionale, piena modalità 'esclusivo' con una finestra di dimensioni per riempire lo schermo, insieme con l'impostazione della modalità di visualizzazione che potrebbe non essere ciò che l'utente ha impostato per Windows in generale. Qui hai IsWindowed = false.
  2. Si imposta la dimensione della finestra per riempire l'intero schermo (cioè ingrandito). È possibile utilizzare gli stili di Windows per garantire che la finestra non abbia frame che si traduce in un'esperienza di stile a schermo intero (WS_POPUP). Qui hai IsWindowed = true e dovresti assicurarti di impostare DXGI_MWA_NO_ALT_ENTER per evitare che DXGI provi a portarti a usare il caso 1.
  3. Si può fare lo stesso di 2 con IsWindowed = true e la finestra senza bordi dimensionata per abbinare lo schermo, ma si cambia la modalità di visualizzazione in qualcosa di diverso dal sistema predefinito.Questo è comunemente definito come "falso schermo intero". La modalità di visualizzazione viene ripristinata ogni volta che si esce dall'applicazione.

1 ha tutti i problemi con il multi-tasking e il focus che abbiamo appena descritto. 2 e 3 consentono di visualizzare notifiche di sistema e altri popup sul gioco e di non forzare un interruttore di modalità. 2 e 3 funzionano molto meglio anche nelle configurazioni multi-monitor in cui è possibile riprodurre il gioco su un display e utilizzare altre app su un altro display. Per il multitasking la maggior parte delle persone preferisce uno stile di finestra classico con un bordo di cornice.

Windows Store Le nozioni UWP della modalità a schermo intero sono fondamentalmente come 2 sopra. Non è possibile modificare la modalità di visualizzazione con una UWP.

Il debug di una configurazione a schermo intero è piuttosto impegnativo. Con più monitor, 2 e 3 possono funzionare con il tuo debugger sull'altro schermo. Per la vera modalità esclusiva a schermo intero, l'unica opzione è usare il debug remoto da un altro PC.

Un altro problema con 1 e 3 è che è possibile impostare la modalità di visualizzazione su qualcosa che non si sincronizzerà con il display lasciando all'utente un sistema senza UI e nessun modo per uscire. Idealmente con la corretta configurazione del driver, l'elenco di enumerazione DXGI non contiene modalità non supportate, ma è qualcosa di cui essere a conoscenza. Per questo motivo, l'interfaccia utente per la selezione di una modalità di visualizzazione dovrebbe avere un timeout e dovresti assicurarti che ci sia un modo ragionevole per interrompere l'applicazione con la tastiera se la modalità di visualizzazione non riesce a sincronizzarsi in futuro. Utilizzare la modalità di visualizzazione esistente come facciamo in 2 sopra è sempre l'opzione più sicura.

Il motivo principale per utilizzare la modalità esclusiva a schermo intero (1) sopra è cercare di ottenere "flip" anziché "blit" del backbuffer/frontbuffer. Per la maggior parte dei sistemi moderni, questa è una differenza di prestazioni trascurabile. L'altro motivo per cui è necessario provare ad usarlo è per il rendering multi-GPU SLI/Crossfire che va a un singolo display. Ci sono un certo numero di altre ottimizzazioni necessarie per far funzionare lo scenario, ed è piuttosto di nicchia. Dovresti cercare le guide di ottimizzazione del fornitore per i dettagli.

Nella maggior parte dei casi i giochi moderni utilizzano lo schermo intero falso piuttosto che la modalità "esclusiva" a schermo intero. Offrono la possibilità di utilizzare una modalità a finestra reale poiché molti utenti vogliono essere in grado di eseguire più task durante la riproduzione (come cercare suggerimenti online, usare chat IM o chat vocale esterna, ecc.). I giochi desktop Windows di AAA che vogliono supportare i giochi ad alte prestazioni per SLI/Crossfire offriranno una modalità 'esclusiva' a schermo intero, ma questo richiede un po 'di lavoro per funzionare pienamente e richiede più lavoro di un semplice codice DXGI.

Vedi DXGI Overview e DirectX Graphics Infrastructure (DXGI): Best Practices

+0

Grazie per la grande risposta dettagliata, per curiosità (pensando a windows8.1 o windows 10), si incontrerebbe lo stesso comportamento usando DirectX9 a schermo intero (non che io abbia intenzione di tornare indietro ad esso, ma curioso al riguardo)? – catflier

+0

Penso che ci siano alcune stranezze nel modo in cui viene emulata la modalità a schermo intero Direct3D 9 - ricorda che gli scenari di "dispositivo perso" non esistono realmente su Windows Vista +, quindi anche questo comportamento viene emulato--, ma credo che sia fondamentalmente la stessa situazione. –

+0

Per impostazione predefinita in dx9 è in realtà peggio, dal momento che perde lo stato attivo si nasconde, ma almeno può essere facilmente ingannato, quindi sembra che per ora l'unico modo sarà la risorsa condivisa ad esso ... – catflier

2

Dopo vari tentativi e prove, qui le diverse soluzioni alternative che ho usato, nessuno è l'ideale ma tutti sono in qualche modo meglio di ottenere un cambiamento di modalità.

1/Sposta il cursore al centro della finestra a schermo intero, con una scorciatoia da tastiera per ottenere nuovamente il controllo. Questo non è l'ideale dato che non possiamo fare nulla mentre la nostra parte è in esecuzione, ma almeno previene il "disastro" accidentale. Neanche impedisce l'interazione con la tastiera.

2/Utilizzare un renderer DX9 con una trama condivisa. DX9 Swapchain può avere la finestra madre impostata sul desktop, in modo da non perdere la concentrazione quando si passa a qualcos'altro. Avendo una finestra focalizzata in alto mostra i piccoli bordi visibili mentre la si muove, ma questo è un po 'più accettabile di perdere tutto. Non a prova di futuro, ma l'ipotesi rimarrà effettiva per un po '.

3/soggiorno su Windows 7 e disattivare il servizio DWM:

Non funziona in Windows 8 più, ma nel mio caso d'uso in quanto la maggior parte delle società di media per cui lavoro sono ancora in Windows 7, rimane una valida soluzione per almeno 5 a 10 anni.

4/Forza la finestra DX11 sul primo piano

Fondamentalmente continuamente chiamare SetForegroundWindow per evitare un'altra finestra per prendere fuoco.

5/Interruttore modalità di prevenzione a livello di presentazione.

Dato che su mia richiesta ho avuto accesso a quando si verifica presentazione, io uso la seguente procedura (prima di chiamare Presente)

-Get primo piano finestra maniglia (usando GetForegroundWindow), Se la maniglia di primo piano è la nostra finestra a schermo intero, basta chiama Presente come al solito.

Se la maniglia in primo piano non è la nostra finestra a schermo intero, eseguire quanto segue. Notare che il controllo della visibilità non è necessario, poiché anche una finestra invisibile sovrapposta causerà una perdita a schermo intero! (Sul serio, questo è solo così male ...)

-Verificare se la nostra finestra in primo piano si sovrappone con il monitor: Chiamata GetWindowRect per ottenere i limiti, ed eseguire all'incrocio con la posizione del monitor.

In alternativa, chiama Presente sullo swapchain con il flag DXGI_PRESENT_TEST. Se una finestra è sovrapposizione, la chiamata Presente tornerà DXGI_STATUS_OCCLUDED

Se una finestra si sovrappone, o nasconderlo o spostarlo in un altro monitor (da nessuna parte in modo che non si sovrapponga): ShowWindow e SetWindowPos sono aperfect adatta per questo compito.

Ripetere la chiamata presente del test in un ciclo finché non restituisce lo stato occluso (questo è importante, poiché Windows potrebbe non aver elaborato i messaggi immediatamente); Una volta che la bandiera occlusa è scomparsa, chiama Presente come al solito.