2012-05-08 16 views
9

Tôi đang cố gắng thực hiện kết xuất được đồng bộ hóa theo chiều dọc sao cho chính xác một kết xuất được thực hiện trên mỗi lần đồng bộ hóa dọc, không bỏ qua hoặc lặp lại bất kỳ khung nào. Tôi sẽ cần điều này để làm việc trong Windows 7 và (trong tương lai) Windows 8.Cách thực hiện chính xác một lần hiển thị trên mỗi đồng bộ hóa dọc (không lặp lại, không bỏ qua)?

Về cơ bản nó sẽ vẽ một chuỗi QUADS phù hợp với màn hình sao cho pixel từ hình ảnh gốc khớp với 1: 1 a pixel trên màn hình. Phần hiển thị không phải là vấn đề, với OpenGL hoặc DirectX. Vấn đề là việc đồng bộ hóa chính xác.

tôi trước đây đã cố gắng sử dụng OpenGL, với phần mở rộng WGL_EXT_swap_control, bằng cách vẽ và sau đó gọi

SwapBuffers(g_hDC); 
glFinish(); 

Tôi đã thử tất cả kết hợp và hoán vị của hai hướng dẫn những cùng với glFlush(), và nó là không đáng tin cậy.

sau đó tôi thử với Direct3D 10, bằng cách vẽ và sau đó gọi

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

nơi g_pSwapChain là một IDXGISwapChain*pOutputIDXGIOutput* liên quan đến SwapChain đó.

Cả hai phiên bản, OpenGL và Direct3D, kết quả giống nhau: Chuỗi đầu tiên, có 60 khung hình, không kéo dài những gì cần (thay vì khoảng 1000ms ở 60Hz, kéo dài khoảng 1030 hoặc 1050ms), những cái sau đây dường như hoạt động tốt (khoảng 1000,40ms), nhưng bây giờ mọi thứ dường như bỏ qua một khung. Tôi thực hiện phép đo với QueryPerformanceCounter.

Trên Direct3D, thử một vòng lặp chỉ là WaitForVBlank, thời lượng 1000 lần lặp lại luôn là 1000,40 với ít biến thể.

Vì vậy, sự cố ở đây không biết chính xác khi nào mỗi hàm được gọi là trả về và liệu hoán đổi có được thực hiện trong quá trình đồng bộ hóa dọc hay không (không phải trước đó, để tránh xé).

Lý tưởng (nếu tôi không nhầm), để đạt được thứ tôi muốn, sẽ thực hiện một lần hiển thị, đợi cho đến khi quá trình đồng bộ bắt đầu, hoán đổi trong quá trình đồng bộ, sau đó đợi cho đến khi quá trình đồng bộ hoàn tất. Làm thế nào để làm điều đó với OpenGL hoặc DirectX?

Chỉnh sửa: Vòng lặp kiểm tra chỉ WaitForVSync 60x mất liên tục từ 1000.30ms đến 1000.50ms. Vòng lặp tương tự với Present(1,0) trước WaitForVSync, không có gì khác, không hiển thị, mất cùng một lúc, nhưng đôi khi không thành công và mất 1017ms, như thể đã lặp lại một khung. Không có kết xuất, vì vậy có gì đó sai ở đây.

Trả lời

0

Khi tạo thiết bị Direct3D, hãy đặt tham số PresentationInterval của cấu trúc D3DPRESENT_PARAMETERS thành D3DPRESENT_INTERVAL_DEFAULT.

+0

Điều đó cũng có sẵn trong DX10 không? Tôi đang tạo thiết bị với 'D3D10CreateDeviceAndSwapChain', mà theo như tôi có thể nói không có bất kỳ Tham số hiện tại nào. – slazaro

+0

Tôi sợ DX10 không có tùy chọn này. Xin lỗi, tôi không nhiều vào DX10. – miloszmaki

+1

Thực ra bạn cũng muốn đặt nó: D3DPRESENT_INTERVAL_ONE ... và D3D10 thực hiện vsync thông qua phương thức hiện tại 'swapChain-> Present (vSync, 0);' – zezba9000

2

tôi trước đây đã cố gắng sử dụng OpenGL, với phần mở rộng WGL_EXT_swap_control, bằng cách vẽ và sau đó gọi

SwapBuffers(g_hDC); 
glFinish(); 

Đó glFinish() hoặc glFlush là không cần thiết. SwapBuffers ngụ ý một glFinish.

Có thể nào, trong cài đặt trình điều khiển đồ họa của bạn, bạn đặt "buộc V-Blank/V-Sync tắt"?

+0

Được đặt thành bất kỳ ứng dụng nào mà mỗi ứng dụng chọn. Trình hiển thị của tôi thực hiện đồng bộ hóa nhiều hơn hoặc ít hơn cho trống theo chiều dọc, nhưng vấn đề là nó không nhất quán, ngay cả với (hầu hết) kết xuất trống. – slazaro

+0

@slazaro: Làm thế nào để bạn đo lường điều này? Tôi có nghĩa là với một render rỗng không có chỉ số bạn có thể sử dụng. Nếu bạn đang sử dụng bộ đếm thời gian hệ thống, thì bạn nên biết rằng điều này sẽ luôn xảy ra xung quanh khoảng thời gian V-Sync, không bao giờ chính xác nhấn nó – datenwolf

+0

Với DirectX, khi tôi chỉ có một vòng lặp 'WaitForVBlank', nó luôn đánh dấu đồng thời, với một biến thể nhỏ, nhỏ hơn 1ms. Mặt khác, với một render và swapbuffers với vsync được kích hoạt, mỗi render mất khoảng 16.6ms, ngoại trừ một số có hai khung (~ 33.3ms). Cho rằng tôi không chi tiêu mà rendering dài (một vòng 60x mà không vsync mất như 30ms), có một số không đáng tin cậy, và tôi nghĩ rằng nó nhiều hơn có thể được đổ lỗi về độ chính xác của bộ đếm thời gian. – slazaro

3

Tôi gặp vấn đề tương tự trong DX11. Tôi muốn đảm bảo rằng mã kết xuất khung của tôi lấy một bội số chính xác của tốc độ làm mới của màn hình, để tránh độ trễ đa bộ đệm.

Chỉ cần gọi pSwapChain->present(1,0) là không đủ. Điều đó sẽ ngăn chặn rách ở chế độ toàn màn hình, nhưng nó không chờ đợi cho vblank xảy ra. Các cuộc gọi hiện tại là không đồng bộ và nó trả về ngay lập tức nếu có bộ đệm khung còn lại để được lấp đầy. Vì vậy, nếu mã kết xuất của bạn đang tạo khung mới rất nhanh (nói 10ms để hiển thị mọi thứ) và người dùng đã đặt "Khung hình được hiển thị tối đa" của trình điều khiển thành 4, thì bạn sẽ hiển thị bốn khung trước những gì người dùng nhìn thấy. Điều này có nghĩa là 4 * 16.7 = 67ms độ trễ giữa hành động của chuột và phản hồi màn hình, điều này không thể chấp nhận được. Lưu ý rằng cài đặt của trình điều khiển thắng - ngay cả khi ứng dụng của bạn yêu cầu pOutput->setMaximumFrameLatency(1), bạn sẽ nhận được 4 khung bất kể. Vì vậy, cách duy nhất để đảm bảo không có độ trễ chuột bất kể cài đặt trình điều khiển là gì cho vòng lặp kết xuất của bạn để tự nguyện đợi cho đến khoảng thời gian làm mới dọc tiếp theo, để bạn không bao giờ sử dụng các khung hình thêm đó.

IDXGIOutput::WaitForVBlank() được thiết kế cho mục đích này. Nhưng nó không hoạt động! Khi tôi gọi là sau

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

và tôi đo thời gian cần thiết cho cuộc gọi waitForVBlank() trở lại, tôi nhìn thấy nó luân phiên giữa 6ms và 22ms, xấp xỉ.

Điều đó có thể xảy ra như thế nào? Làm thế nào có thể waitForVBlank() mất nhiều thời gian hơn 16,7ms để hoàn tất? Trong DX9, chúng tôi đã giải quyết vấn đề này bằng cách sử dụng getRasterState() để triển khai phiên bản waitForVBlank của chính chúng tôi, chính xác hơn nhiều. Nhưng cuộc gọi đó đã bị phản đối trong DX11.

Có cách nào khác để đảm bảo rằng khung của tôi được căn chỉnh chính xác với tốc độ làm mới của màn hình không? Có cách nào khác để theo dõi các scanline hiện tại như getRasterState được sử dụng để làm gì?

-1

Nếu bạn chạy trong chế độ lõi hoặc vòng-0, bạn có thể thử đọc bit 3 từ VGA input register (03bah, 03dah). Thông tin là khá cũ nhưng mặc dù nó là hinted here rằng bit có thể đã thay đổi vị trí hoặc có thể đã lỗi thời trong phiên bản mới hơn của Windows 2000 trở lên, tôi thực sự nghi ngờ điều này. Liên kết thứ hai có một số mã nguồn rất cũ cố gắng để lộ tín hiệu vblank cho các phiên bản Windows cũ. Nó không còn chạy, nhưng về lý thuyết xây dựng lại nó với Windows SDK mới nhất nên sửa lỗi này.

Phần khó khăn là xây dựng và đăng ký trình điều khiển thiết bị hiển thị thông tin này một cách đáng tin cậy và sau đó tìm nạp thông tin đó từ ứng dụng của bạn.

1

Hiện tại chúng tôi sử dụng DX9 và muốn chuyển sang DX11. Chúng tôi hiện đang sử dụng GetRasterState() để đồng bộ hóa theo cách thủ công với màn hình. Điều đó biến mất trong DX11, nhưng tôi thấy rằng việc tạo một thiết bị DirectDraw7 dường như không làm gián đoạn DX11. Vì vậy, chỉ cần thêm mã này vào mã của bạn và bạn sẽ có thể nhận được vị trí quét.

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

Trên Windows 8.1 và Windows 10, bạn có thể sử dụng DXGI 1.3 DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT. Xem MSDN. Mẫu ở đây là dành cho các ứng dụng Windows 8 Store, nhưng nó cũng phải thích ứng với các lớp trao đổi Win32 của Windows.

Bạn cũng có thể thấy điều này hữu ích video.

Các vấn đề liên quan