2012-05-08 19 views
9

Próbuję wykonać rendery synchronizowane w pionie, aby dokładnie jeden render był wykonywany na synchronizację w pionie, bez pomijania ani powtarzania żadnych ramek. Potrzebuję tego do pracy pod Windows 7 i (w przyszłości) Windows 8.Jak wykonać dokładnie jeden render na synchronizację w pionie (bez powtarzania, bez pomijania)?

Będzie to zasadniczo polegać na rysowaniu sekwencji QUADS, która pasowałaby do ekranu, tak aby piksel z oryginalnych zdjęć pasował do 1: 1 a piksel na ekranie. Część renderująca nie stanowi problemu, ani z OpenGL, ani z DirectX. Problem polega na prawidłowej synchronizacji.

ja wcześniej wypróbowane przy użyciu OpenGL, z rozszerzeniem WGL_EXT_swap_control, rysując, a następnie wywołanie

SwapBuffers(g_hDC); 
glFinish(); 

Próbowałem wszystkich kombinacji i permutacji tych dwóch instrukcji wraz z glFlush(), i nie było wiarygodne.

Następnie próbowałem z Direct3D 10, rysując, a następnie wywołanie

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

gdzie g_pSwapChain jest IDXGISwapChain* i pOutput jest IDXGIOutput* związane z tym SwapChain.

Obie wersje, OpenGL i Direct3D, dają takie same wyniki: Pierwsza sekwencja, powiedzmy 60 klatek, nie trwa tak, jak powinna (zamiast około 1000 ms przy 60 Hz trwa około 1030 lub 1050 ms), następujące wydają się działać dobrze (około 1000.40ms), ale od czasu do czasu zdaje się pomijać klatkę. Wykonuję pomiar za pomocą QueryPerformanceCounter.

W Direct3D, próbując pętli tylko WaitForVBlank, czas trwania 1000 iteracji jest zawsze 1000.40 z niewielką zmianą.

Problem polega więc na tym, że nie wiadomo dokładnie, kiedy każda z funkcji zwanych zwraca i czy zamiana jest wykonywana podczas synchronizacji pionowej (nie wcześniej, aby uniknąć łzawienia).

Idealnie (jeśli się nie mylę), aby osiągnąć to, co chcę, byłoby wykonać jeden render, poczekać, aż zacznie sync, swap podczas synchronizacji, a następnie poczekać, aż synchronizacja jest wykonywana. Jak to zrobić z OpenGL lub DirectX?

Edit: Pętla test tylko WaitForVSync 60x trwa niezmiennie od 1000.30ms do 1000.50ms. Ta sama pętla z Present(1,0) przed WaitForVSync, bez niczego innego, bez renderowania, zajmuje ten sam czas, ale czasami kończy się niepowodzeniem i zajmuje 1017 ms, jak gdyby po powtórzeniu ramki. Nie ma renderingu, więc coś jest nie tak.

Odpowiedz

0

Podczas tworzenia urządzenia Direct3D ustaw parametr PresentationInterval dla struktury D3DPRESENT_PARAMETERS na .

+0

Czy jest to również dostępne w DX10? Tworzę urządzenie z 'D3D10CreateDeviceAndSwapChain', które, o ile mogę powiedzieć, nie ma żadnych Present Presenters. – slazaro

+0

Obawiam się, że DX10 nie ma tej opcji. Przepraszam, nie jestem zbytnio w DX10. – miloszmaki

+1

Właściwie chcesz to również ustawić: D3DPRESENT_INTERVAL_ONE ... i D3D10 wykonuje operację vsync poprzez swoją obecną metodę 'swapChain-> Present (vSync, 0);' – zezba9000

2

ja wcześniej wypróbowane przy użyciu OpenGL, z rozszerzeniem WGL_EXT_swap_control, rysując, a następnie wywołanie

SwapBuffers(g_hDC); 
glFinish(); 

To glFinish() lub glFlush jest zbędny. SwapBuffers oznacza glFish.

Czy to możliwe, że w ustawieniach sterownika graficznego ustawiłeś "wymuszenie wyłączania V-Blank/V-Sync"?

+0

Jest ustawiony na dowolną aplikację. Moje renderowanie wykonuje mniej lub więcej synchronizacji z pionowym pustym miejscem, ale problem polega na tym, że nie jest on spójny, nawet w przypadku (w większości) pustych renderowań. – slazaro

+0

@slazaro: Jak to mierzyć? Chodzi mi o to, że przy pustym renderingu nie ma wskaźnika, którego można użyć. Jeśli używasz timera systemowego, powinieneś wiedzieć, że zawsze będzie to drgać wokół interwału V-Sync, nigdy dokładnie go nie uderzając – datenwolf

+0

Z DirectX, gdy mam tylko pętlę 'WaitForVBlank', konsekwentnie zaznacza w tym samym czasie, z niewielką zmianą, mniejszą niż 1 ms. Z drugiej strony, przy włączonym renderowaniu i swapbuforach z aktywowanym algorytmem odwzorowującym, każde renderowanie zajmuje około 16,6 ms, z wyjątkiem niektórych, które wymagają dwóch ramek (~ 33,3 ms). Biorąc pod uwagę, że nie spędzam tak długiego renderowania (pętla 60x bez odwrotnej synchronizacji zajmuje 30 ms), jest pewna niewiarygodność i myślę, że to więcej niż można obarczać winą precyzji timerów. – slazaro

3

Mam ten sam problem w DX11. Chcę zagwarantować, że mój kod renderujący klatki pobiera dokładną wielokrotność częstotliwości odświeżania monitora, aby uniknąć opóźnień w wielu buforach.

Po prostu wywołanie pSwapChain->present(1,0) nie jest wystarczające. To zapobiegnie rozerwaniu w trybie pełnoekranowym, ale nie czeka na pojawienie się vblanka. Obecne wywołanie jest asynchroniczne i zwraca się natychmiast, jeśli pozostały do ​​wypełnienia bufory ramki. Jeśli więc twój kod renderujący bardzo szybko tworzy nową klatkę (powiedzmy 10 ms, aby wyrenderować wszystko), a użytkownik ustawił "Maksymalnie wstępnie wyrenderowane klatki" na 4, to renderujesz cztery klatki przed tym, co zobaczy użytkownik. Oznacza to 4 * 16,7 = 67 ms opóźnienia między działaniem myszy a reakcją ekranu, co jest niedopuszczalne. Pamiętaj, że ustawienie kierowcy wygrywa - nawet jeśli Twoja aplikacja poprosi o pOutput->setMaximumFrameLatency(1), otrzymasz 4 ramki niezależnie. Tak więc jedynym sposobem na zagwarantowanie braku opóźnienia myszy bez względu na ustawienia sterownika jest dla pętli renderowania dobrowolne oczekiwanie na następny pionowy interwał odświeżania, aby nigdy nie używać tych dodatkowych frameBufferów.

IDXGIOutput::WaitForVBlank() jest przeznaczona do tego celu. Ale to nie działa! Kiedy zadzwonić pod następujące

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

i zmierzyć czas potrzebny na wywołanie waitForVBlank() wrócić, widzę jej zastępcę między 6 ms i 22ms, z grubsza.

Jak to się dzieje? W jaki sposób może zająć waitForVBlank() więcej niż 16,7 ms? W DX9 rozwiązaliśmy ten problem, używając getRasterState(), aby zaimplementować naszą własną, znacznie dokładniejszą wersję waitForVBlank. Ale to połączenie było przestarzałe w DX11.

Czy istnieje inny sposób zagwarantowania, że ​​moja ramka jest dokładnie dopasowana do częstotliwości odświeżania monitora? Czy istnieje inny sposób na śledzenie aktualnej linii skanowania, jak robił to getRasterState?

-1

Jeśli działasz w trybie jądra lub pierścień-0, możesz spróbować odczytać bit 3 z VGA input register (03bah, 03dah). Informacja jest dość stara, ale chociaż była to hinted here, że bit mógł zmienić lokalizację lub może być przestarzały w późniejszej wersji Windows 2000 i nowszych, faktycznie to wątpię. Drugi link ma bardzo stary kod źródłowy, który próbuje odsłonić sygnał vblank dla starych wersji Windows. To już nie działa, ale w teorii odbudowanie go najnowszym pakietem Windows SDK powinno to naprawić.

Trudną częścią jest budowanie i rejestrowanie sterownika urządzenia, który rzetelnie ujawnia te informacje, a następnie pobiera je z aplikacji.

1

Obecnie korzystamy z DX9 i chcemy przejść na DX11. Obecnie używamy GetRasterState() do ręcznej synchronizacji z ekranem. To znika w DX11, ale odkryłem, że tworzenie urządzenia DirectDraw7 nie zakłóca działania DX11. Po prostu dodaj to do kodu i powinieneś być w stanie uzyskać pozycję linii skanowania.

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

W Windows 8.1 i Windows 10, można skorzystać z następujących DXGI 1,3 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Zobacz MSDN. Przykład jest dla aplikacji Windows 8 Store, ale powinien również być dostosowywany do wymiany okien Win32 w klasie.

Może się przydać również ta video.