2012-05-08 13 views
9

Sto provando a eseguire rendering sincronizzati verticali in modo che venga eseguito esattamente un rendering per sincronizzazione verticale, senza saltare o ripetere alcun frame. Avrei bisogno di questo per funzionare con Windows 7 e (in futuro) Windows 8.Come eseguire esattamente un rendering per sincronizzazione verticale (senza ripetere, senza saltare)?

Fondamentalmente consisterebbe nel disegnare una sequenza di QUADS adatta allo schermo in modo che un pixel delle immagini originali corrisponda a 1: 1 a pixel sullo schermo. La parte di rendering non è un problema, sia con OpenGL o DirectX. Il problema è la sincronizzazione corretta.

Ho già provato ad utilizzare OpenGL, con l'estensione WGL_EXT_swap_control, disegnando e quindi chiamando

SwapBuffers(g_hDC); 
glFinish(); 

Ho provato tutte le combinazioni e le permutazioni di queste due istruzioni insieme a glFlush(), e non era affidabile.

Ho poi provato con Direct3D 10, disegnando e quindi chiamando

g_pSwapChain->Present(1, 0); 
pOutput->WaitForVBlank(); 

dove g_pSwapChain è un IDXGISwapChain* e pOutput è la IDXGIOutput* associato a tale SwapChain.

Entrambe le versioni, OpenGL e Direct3D, hanno lo stesso risultato: La prima sequenza di, ad esempio, 60 fotogrammi, non dura quello che dovrebbe (invece di circa 1000ms a 60hz, dura qualcosa come 1030 o 1050ms), i seguenti sembrano funzionare bene (circa 1000.40 ms), ma ogni tanto sembra saltare un frame. Faccio la misurazione con QueryPerformanceCounter.

Su Direct3D, provando un loop solo di WaitForVBlank, la durata di 1000 iterazioni è costantemente 1000.40 con poca variazione.

Quindi il problema qui non è sapere esattamente quando ciascuna delle funzioni chiamate restituisce e se lo scambio viene eseguito durante la sincronizzazione verticale (non prima, per evitare lo strappo).

Idealmente (se non sbaglio), per ottenere ciò che voglio, sarebbe eseguire un rendering, attendere fino all'avvio della sincronizzazione, scambiare durante la sincronizzazione, quindi attendere fino al termine della sincronizzazione. Come farlo con OpenGL o DirectX?

Edit: Un ciclo di prova di soli WaitForVSync 60x prende costantemente dal 1000.30ms a 1000.50ms. Lo stesso ciclo con Present(1,0) prima dello WaitForVSync, con nient'altro, nessun rendering, prende lo stesso tempo, ma a volte fallisce e prende 1017 ms, come se avesse ripetuto un frame. Non c'è rendering, quindi c'è qualcosa di sbagliato qui.

risposta

0

Durante la creazione di un dispositivo Direct3D, impostare il parametro PresentationInterval della struttura D3DPRESENT_PARAMETERS su D3DPRESENT_INTERVAL_DEFAULT.

+0

È disponibile anche in DX10? Sto creando il dispositivo con 'D3D10CreateDeviceAndSwapChain', che per quanto posso dire non ha alcun Parametro Presente. – slazaro

+0

Ho paura che DX10 non abbia questa opzione. Scusate, non sono molto interessato a DX10. – miloszmaki

+1

In realtà si desidera impostarlo anche: D3DPRESENT_INTERVAL_ONE ... e D3D10 esegue vsync attraverso il suo metodo attuale 'swapChain-> Present (vSync, 0);' – zezba9000

2

ho precedentemente provato ad utilizzare OpenGL, con l'estensione WGL_EXT_swap_control, disegnando e quindi chiamando

SwapBuffers(g_hDC); 
glFinish(); 

Tale glFinish() o glFlush è superflua. SwapBuffers implica un glFinish.

Potrebbe essere, che nelle impostazioni del driver grafico è impostato "forzare V-Blank/V-Sync off"?

+0

È impostato su qualsiasi cosa scelga ciascuna applicazione. I miei rendering eseguono più o meno la sincronizzazione con lo spazio vuoto verticale, ma il problema è che non è coerente, anche con rendering (soprattutto) vuoti. – slazaro

+0

@slazaro: come si misura questo? Voglio dire con un rendering vuoto non c'è nessun indicatore che si possa usare. Se si utilizza il timer di sistema, allora si dovrebbe essere consapevoli che questo sarà sempre jittering intorno l'intervallo V-Sync, mai esattamente colpendolo – datenwolf

+0

Con DirectX, quando ho solo un ciclo di 'WaitForVBlank', segna costantemente il stessa ora, con una piccola variazione, meno di 1 ms. D'altra parte, con un render e swapbuffer con vsync attivato, ogni render impiega circa 16.6ms, tranne alcuni che prendono due frame (~ 33.3ms). Dato che non spendo quel lungo rendering (un ciclo 60x senza vsync richiede 30ms), c'è qualche inaffidabilità, e penso che sia più di quanto possa essere incolpato sulla precisione dei timer. – slazaro

3

Ho lo stesso problema in DX11. Voglio garantire che il mio codice di rendering del frame abbia un multiplo esatto della frequenza di aggiornamento del monitor, per evitare la latenza del multi-buffering.

Basta chiamare pSwapChain->present(1,0) non è sufficiente. Ciò eviterà di lacerare in modalità a schermo intero, ma non attenderà il verificarsi della vblank. La chiamata corrente è asincrona e ritorna immediatamente se ci sono buffer di frame che rimangono da riempire. Quindi se il tuo codice di rendering produce molto rapidamente un nuovo fotogramma (ad esempio 10 ms per eseguire il rendering di tutto) e l'utente ha impostato il numero massimo di fotogrammi pre-renderizzati del driver su 4, verrà eseguito il rendering di quattro fotogrammi in anticipo rispetto a ciò che l'utente vede. Questo significa 4 * 16.7 = 67 ms di latenza tra l'azione del mouse e la risposta dello schermo, il che è inaccettabile. Nota che l'impostazione del driver vince, anche se la tua app ha richiesto pOutput->setMaximumFrameLatency(1), avrai 4 frame indipendentemente. Quindi, l'unico modo per non garantire il ritardo del mouse indipendentemente dall'impostazione del driver è che il ciclo di rendering attenda volontariamente fino al successivo intervallo di aggiornamento verticale, in modo da non utilizzare mai quei frameBuffer aggiuntivi.

IDXGIOutput::WaitForVBlank() è destinato a tale scopo. Ma non funziona! Quando chiamo il seguente

<render something in ~10ms> 
pSwapChain->present(1,0); 
pOutput->waitForVBlank(); 

e misuro il tempo necessario per la chiamata waitForVBlank() per tornare, sto vedendo che si alternano tra 6ms e 22ms, più o meno.

Come può accadere? In che modo waitForVBlank() può richiedere più tempo di 16,7 ms per essere completato? In DX9 abbiamo risolto questo problema utilizzando getRasterState() per implementare la nostra versione, molto più accurata di waitForVBlank. Ma quella chiamata era deprecata in DX11.

C'è un altro modo per garantire che il mio telaio è esattamente allineata con la frequenza di aggiornamento del monitor? C'è un altro modo per spiare la linea di scansione attuale come faceva GetRasterState?

-1

Se si esegue in modalità kernel o ring-0, è possibile tentare di leggere il bit 3 dallo VGA input register (03bah, 03dah). Le informazioni sono piuttosto vecchie ma, sebbene fosse hinted here che il bit avrebbe potuto cambiare posizione o essere obsoleto nella versione successiva di Windows 2000 e versioni successive, in realtà ne dubito. Il secondo link ha un codice sorgente molto vecchio che tenta di esporre il segnale vblank per le vecchie versioni di Windows. Non funziona più, ma in teoria la ricostruzione con l'ultimo SDK di Windows dovrebbe risolvere questo problema.

La parte difficile è la costruzione e la registrazione di un driver di periferica che espone queste informazioni in modo affidabile e quindi averlo scaricato dalla vostra applicazione.

1

Usiamo DX9 momento, e vogliono passare a DX11. Al momento utilizziamo GetRasterState() per sincronizzarci manualmente sullo schermo. Questo va via in DX11, ma ho scoperto che fare un dispositivo DirectDraw7 non sembra disturbare DX11. Quindi aggiungilo al tuo codice e dovresti essere in grado di ottenere la posizione di scanline.

IDirectDraw7* ddraw = nullptr; 
DirectDrawCreateEx(NULL, reinterpret_cast<LPVOID*>(&ddraw), IID_IDirectDraw7, NULL); 
DWORD scanline = -1; 
ddraw->GetScanLine(&scanline); 
1

In Windows 8.1 e Windows 10, è possibile utilizzare il DXGI 1.3 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Vedi MSDN. L'esempio qui è per le app di Windows 8 Store, ma dovrebbe essere adattabile anche agli swapchain di classe Win32.

Potete trovare questo video utile pure.

Problemi correlati