2012-05-08 22 views
9

Ich versuche vertikale synchronisierte Renderings zu machen, so dass pro vertikale Synchronisierung genau ein Rendering durchgeführt wird, ohne irgendwelche Frames zu überspringen oder zu wiederholen. Ich würde dies unter Windows 7 und (in Zukunft) Windows 8 benötigen.Wie man genau ein Rendering pro Vertikalsynchronisation durchführt (keine Wiederholung, kein Überspringen)?

Es würde im Grunde bestehen aus einer Sequenz von QUADS Zeichnung, die den Bildschirm so passen würde, dass ein Pixel aus den Originalbildern 1: 1 übereinstimmt Pixel auf dem Bildschirm. Der Rendering-Teil ist kein Problem, weder mit OpenGL noch mit DirectX. Das Problem ist die korrekte Synchronisierung.

ich zuvor versucht OpenGL verwenden, mit der WGL_EXT_swap_control Erweiterung, durch Ziehen und dann

SwapBuffers(g_hDC); 
glFinish(); 

Aufruf habe ich versucht, alle Kombinationen und Permutationen dieser beiden Befehle zusammen mit glFlush(), und es war nicht zuverlässig.

Ich habe dann versucht, mit Direct3D 10, durch Ziehen und dann

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

Aufruf wo g_pSwapChain ist ein IDXGISwapChain* und pOutput die IDXGIOutput* zu diesem SwapChain zugeordnet ist.

Beide Versionen, OpenGL und Direct3D, ergeben dasselbe: Die erste Sequenz von, sagen wir, 60 Frames hält nicht, was es sollte (statt etwa 1000ms bei 60Hz, dauert etwa 1030 oder 1050ms), die folgenden scheinen gut zu funktionieren (ungefähr 1000.40ms), aber hin und wieder scheint es einen Rahmen zu überspringen. Ich mache die Messung mit QueryPerformanceCounter.

Auf Direct3D, eine Schleife von nur WaitForVBlank versuchen, ist die Dauer von 1000 Iterationen konsistent 1000.40 mit wenig Variation.

Also das Problem hier ist nicht genau zu wissen, wann jede der Funktionen zurückgeben, und ob der Austausch während der vertikalen Synchronisierung erfolgt (nicht früher, um ein Reißen zu vermeiden).

Im Idealfall (wenn ich mich nicht irre), um zu erreichen, was ich will, wäre es ein Render durchzuführen, warten Sie, bis die Synchronisierung beginnt, während der Synchronisierung tauschen, warten Sie, bis die Synchronisierung abgeschlossen ist. Wie macht man das mit OpenGL oder DirectX?

Edit: Eine Testschleife nur WaitForVSync 60x konsequent von 1000.30ms zu 1000.50ms nimmt. Die gleiche Schleife mit Present(1,0) vor WaitForVSync, mit nichts anderem, kein Rendering, nimmt die gleiche Zeit, aber manchmal schlägt es fehl und dauert 1017ms, als ob ein Frame wiederholt wurde. Es gibt kein Rendering, also stimmt hier etwas nicht.

Antwort

0

Beim Erstellen eines Direct3D-Geräts den PresentationInterval-Parameter der D3DPRESENT_PARAMETERS-Struktur auf D3DPRESENT_INTERVAL_DEFAULT setzen.

+0

Ist das auch in DX10 verfügbar? Ich erstelle das Gerät mit 'D3D10CreateDeviceAndSwapChain', was, soweit ich weiß, keine Present-Parameter hat. – slazaro

+0

Ich fürchte, DX10 hat diese Option nicht. Tut mir leid, ich bin nicht sehr in DX10. – miloszmaki

+1

Eigentlich möchten Sie es auch setzen: D3DPRESENT_INTERVAL_ONE ... und D3D10 führt Vsync durch seine aktuelle Methode 'swapChain-> Present (vSync, 0);' – zezba9000

2

ich zuvor versucht OpenGL verwenden, mit der WGL_EXT_swap_control Erweiterung, durch Ziehen und dann

SwapBuffers(g_hDC); 
glFinish(); 

Das glFinish() aufgerufen wird oder glFlush ist überflüssig. SwapBuffers impliziert einen glFinish.

Könnte es sein, dass Sie in Ihren Grafiktreibereinstellungen "V-Blank/V-Sync deaktivieren" gewählt haben?

+0

Es ist festgelegt, was jede Anwendung wählt. Meine Renderings synchronisieren mehr oder weniger mit dem vertikalen Leerzeichen, aber das Problem ist, dass es nicht konsistent ist, selbst bei (meistens) leeren Renderings. – slazaro

+0

@slazaro: Wie misst man das? Ich meine, mit einem leeren Render gibt es keinen Indikator, den Sie verwenden könnten. Wenn Sie den System-Timer verwenden, sollten Sie beachten, dass dies immer um das V-Sync-Intervall herum wackelt und es niemals genau trifft. – datenwolf

+0

Mit DirectX, wenn ich nur eine Schleife von 'WaitForVBlank' habe, markiert es ständig die gleiche Zeit, mit einer kleinen Variation, weniger als 1ms. Auf der anderen Seite, mit einem Render-und Swap-Puffer mit vsync aktiviert, dauert jedes Rendern ca. 16,6 ms, mit Ausnahme von einigen, die zwei Frames (~ 33,3 ms) dauern. Angesichts der Tatsache, dass ich nicht so lange Rendering verbringe (eine 60x-Schleife ohne vsync dauert etwa 30ms), gibt es einige Unzuverlässigkeit, und ich denke, es ist mehr als kann man auf die Präzision der Timer verantwortlich gemacht werden. – slazaro

3

Ich habe das gleiche Problem in DX11. Ich möchte garantieren, dass mein Frame-Rendering-Code ein genaues Vielfaches der Aktualisierungsrate des Monitors einnimmt, um eine Latenzzeit mit mehreren Puffern zu vermeiden.

Nur anrufen pSwapChain->present(1,0) ist nicht ausreichend. Das verhindert das Zerreißen im Vollbildmodus, aber es wartet nicht auf das V-Blank. Der vorliegende Aufruf ist asynchron und kehrt sofort zurück, wenn noch Bildpuffer vorhanden sind, die gefüllt werden müssen. Wenn Ihr Render-Code also sehr schnell einen neuen Frame erzeugt (sagen wir 10 ms, um alles zu rendern) und der Benutzer "Maximale vorgerenderte Frames" des Treibers auf 4 gesetzt hat, werden Sie vier Frames vor dem, was der Benutzer sieht, rendern. Dies bedeutet 4 * 16,7 = 67 ms Latenz zwischen Mausaktion und Bildschirmantwort, was nicht akzeptabel ist. Beachten Sie, dass die Einstellung des Fahrers gewinnt - selbst wenn Ihre App nach pOutput->setMaximumFrameLatency(1) fragt, erhalten Sie trotzdem 4 Bilder. Die einzige Möglichkeit, unabhängig von der Treibereinstellung keine Mausverzögerung zu garantieren, besteht darin, dass die Renderschleife freiwillig bis zum nächsten vertikalen Aktualisierungsintervall wartet, sodass Sie diese zusätzlichen framebuffers nicht verwenden.

IDXGIOutput::WaitForVBlank() ist für diesen Zweck vorgesehen. Aber es funktioniert nicht! Wenn ich die folgende

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

nennen und ich messen die Zeit, die für die waitForVBlank() Anruf zurückzukehren, ich sehe es wechseln zwischen 6 ms und 22 ms, grob.

Wie kann das passieren? Wie könnte waitForVBlank() jemals länger als 16,7 ms dauern, um abzuschließen? In DX9 lösten wir dieses Problem mit getRasterState(), um unsere eigene, viel genauere Version von waitForVBlank zu implementieren. Aber dieser Aufruf war in DX11 veraltet.

Gibt es eine andere Möglichkeit zu garantieren, dass mein Bild genau mit der Aktualisierungsrate des Monitors ausgerichtet ist? Gibt es eine andere Möglichkeit, die aktuelle Scanline wie getRasterState auszuspionieren?

-1

Wenn Sie im Kernel-Modus oder Ring-0 laufen, können Sie versuchen, Bit 3 aus dem VGA input register (03bah, 03dah) zu lesen. Die Information ist ziemlich alt, aber obwohl es hinted here war, dass das Bit Standort geändert haben kann oder in einer späteren Version von Windows 2000 und höher veraltet sein kann, bezweifle ich das tatsächlich. Der zweite Link enthält einen sehr alten Quellcode, der versucht, das Vblank-Signal für alte Windows-Versionen verfügbar zu machen. Es läuft nicht mehr, aber in der Theorie sollte es mit dem neuesten Windows SDK neu erstellt werden.

Der schwierige Teil besteht darin, einen Gerätetreiber zu erstellen und zu registrieren, der diese Informationen zuverlässig verfügbar macht und sie dann von Ihrer Anwendung abruft.

1

Wir verwenden derzeit DX9 und möchten zu DX11 wechseln. Zurzeit verwenden wir GetRasterState(), um manuell mit dem Bildschirm zu synchronisieren. Das geht in DX11 weg, aber ich habe festgestellt, dass das Erstellen eines DirectDraw7-Geräts DX11 nicht zu stören scheint. Fügen Sie das einfach Ihrem Code hinzu und Sie sollten in der Lage sein, die Scanline-Position zu erhalten.

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

Unter Windows 8.1 und 10 Windows können Sie die Verwendung des DXGI 1.3 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT machen. Siehe MSDN. Das Beispiel ist für Windows 8 Store-Apps gedacht, sollte aber auch für Win32-Windows-Swapchains geeignet sein.

Sie können diese video auch nützlich finden.