Vsync causing input lag

Started by
14 comments, last by Zoner 13 years, 3 months ago

How do you get your input? It sounds like maybe you use unbuffered input, which means that the key have to be active (held down) in the precise instant that the key is queried. If this is the case, try switching to a buffered input method, for example using WM_KEYDOWN/WM_KEYUP.


I am using raw unbuffered input. Should that really matter though? When i trap the WM_INPUT message, the input object is notified that a key has been pressed and immediately notifies the camera object.

I will try using buffered input and see what the result is.
Advertisement
Something weird just happened. I just found that the problem goes away if i disable my second monitor. I'm running two monitors (one is a TV, actually), and my windows desktop extends over both monitors. I have noticed before that having two monitors enabled can cause some performance drops in games (nothing significant though, usually just a bit lower framerate) so i tried disabling the second monitor and, voila, the lag disappeared. Also, I had a friend try and run the game on his machine and he experienced no lag.

I'm still puzzled as to how having my windows desktop extended to a second monitor could cause my application to lag. Does direct3d behave differently when there's two monitors enabled? Since this problem only appears when running fullscreen, could it be that I need to tell direct3d which monitor to use?

EDIT: Could it be that two monitors is causing the application to wait for vsync on both monitors?
Bump.

Anyone have any info on this?

Bump.

Anyone have any info on this?


Windows is most likely copying your backbuffer to a shared surface ( possibly in system memory ), in order to keep the two desktops in sync. This will cause more buffering and slower perf.
Another thing you could be running into here is that if the CPU is running ahead of the GPU D3D will buffer up to 3 frames worth of graphics commands. This can give you some noticeable input lag, especially if you have a slow monitor refresh rate.

You can limit the number of frames it buffers by doing an occlusion query every frame, and reading the result one frame later (ideally two frames later if using SLI in AFR mode).

http://developer.nvi...archive.html#16 has some more details.
Always a fun topic, since its basically covered in landmines.

Guidelines:

Pump the entire message loop until it is empty, before rendering anything. This might mean you need to keep a copy of various messages (primarily mouse/keyboard input) into your own queues for processing later. If you have a seperate rendering thread then correctly handling things like WM_SIZE events can be rather exciting and complicated, since it is absolutely unsafe for the main thread to wait on a d3d rendering thread.

Rendering on a seperate thread is a good idea but you also are required to create the d3d device context on the main message thread if you ever need to run in fullscreen. However D3D uses SendMessage to do bookkeeping/mode switches from the rendering thread and its pretty easy to get the threads deadlocked (render thread asks for mode switch, main thread sees the message and tries to flush the rendering thread which is already waiting on the main thread etc). The code that calls Reset and Present should be be in functions handled by user based windows messages and run on the main thread via PostMessage. The ideal is to keep the message queue empty at all times. Present isnt technically required to be on the main thread but it can causes mode switches and send messages requiring syncronous handling on the main message thread and its easier to deal with (and avoid deadlocks) if you are already there.

Present can block, but its only going to do that if the maximum number of frames are already in the command queue and it has to wait on one of the older ones to be evicted. This results in a paradox where running at a balanced or less than optimal frame rate can have less input latency than running with the default 3 queued frames worth of stuff. If your game or render threads take a combined 30ms, and the GPU is running at 30fps, the input latency is going to be 60ms. But if your game/render threads only take 2 ms, and the GPU is still running at 30fps, you can get 90 ms of input latency, and should see some 86ms of time blocked in Present.

In DXGI the frame limiter is configurable through the API (1 to 16 frames). The main way to get this down to 2 or 1 in D3D9 when you don't have the DXGI API is to force a stall in the rendering thread, by either locking a rendertarget (ideally the previous frames target sometime at the start of the next frame), or spin-waiting on an d3d query result to be available. Neither approach makes the GPU vendors very happy but they kind of get myopic on total theoretical frame rate instead of user input latency concerns. To be fair at extremely high frame rates (60+) the latency issues should more or less dissolve.

Modern display devices have frame buffers and introduce their own latencies which are hard to deal with at times.

Sadly there is no way to use WaitForSingleObject for pending occlusion queries, and spin waiting on these can burn a lot of cycles. The best that can be done here is to make sure your app calls timeBeginPeriod(1) at startup so a Sleep(1) can be made to work in the spin.

If your input data is old you should consider ignoring it. This is absolutely requires being able to pump the main message loop nearly instantly so you can timestamp everything coming in. This also means that the game state should also be running on its own dedicated tread or threads separate from the main thread, leaving the message pump for the main HWND a lean mean dispatching machine.
http://www.gearboxsoftware.com/

This topic is closed to new replies.

Advertisement