Vsync causing input lag

Started by
14 comments, last by Zoner 13 years, 2 months ago
Hello everyone!

I'm working on a game in C++ using Direct3D and I'm having a problem with vertical sync.

When I have my presentation interval set to default (Vsync on) I'm getting input lag, that is, the controls get all sloppy and slow. However if i change the presentation interval to immediate i get no lag whatsoever.

What I think is going on is that IDirect3DDevice9::Present is stalling the application because it's waiting for vertical sync. I'm looking for a way of getting around this problem.

I've found out that the IDirect3DDevice9Ex device (which only works on vista and later systems) has a Present method which can be set to not wait for vertical sync and instead return an error message if the hardware is not ready. So the question is, how do I achieve this using the normal IDirect3DDevice9 device?
Advertisement

Hello everyone!

I'm working on a game in C++ using Direct3D and I'm having a problem with vertical sync.

When I have my presentation interval set to default (Vsync on) I'm getting input lag, that is, the controls get all sloppy and slow. However if i change the presentation interval to immediate i get no lag whatsoever.

What I think is going on is that IDirect3DDevice9::Present is stalling the application because it's waiting for vertical sync. I'm looking for a way of getting around this problem.

I've found out that the IDirect3DDevice9Ex device (which only works on vista and later systems) has a Present method which can be set to not wait for vertical sync and instead return an error message if the hardware is not ready. So the question is, how do I achieve this using the normal IDirect3DDevice9 device?


That's the entire point of VSYNC: to block Present until the next vertical refresh. Regular D3D9 doesn't have any means of querying for the when that refresh happens. You can try and make a good guess based on frame time if you want, but I'm not sure what you would plan to do with that extra time.

What I think is going on is that IDirect3DDevice9::Present is stalling the application because it's waiting for vertical sync. I'm looking for a way of getting around this problem.


Yes. That is how vsync works. So a couple things

1) It is weird you are getting input lag. Updating at 60 fps shouldn't be noticeable as lag. Are you actually "feeling" it or are you just, with timing, noticing that your input is delayed by 1/60th of a second? If the latter humans shouldn't be able to notice that, many games ship with VSYNC on and input in the main thread. So if you're getting more than 1/60th of a second of lag there is a bug elsewhere; perhaps you aren't processing input correctly. Something else to look out for would be a framerate spike where occasionally a frame takes a long time to render. Profiling is a good approach here.

2) If for some bizarre reason you actually *need* better than 1/60th of a second of response time (something that I don't think a keyboard can even deliver) you can put input processing in a separate thread.

-me

That's the entire point of VSYNC: to block Present until the next vertical refresh. Regular D3D9 doesn't have any means of querying for the when that refresh happens. You can try and make a good guess based on frame time if you want, but I'm not sure what you would plan to do with that extra time.


Alright. I was hoping there was a way around it but I guess not. Thanks for the quick reply!



Yes. That is how vsync works. So a couple things

1) It is weird you are getting input lag. Updating at 60 fps shouldn't be noticeable as lag. Are you actually "feeling" it or are you just, with timing, noticing that your input is delayed by 1/60th of a second? If the latter humans shouldn't be able to notice that, many games ship with VSYNC on and input in the main thread. So if you're getting more than 1/60th of a second of lag there is a bug elsewhere; perhaps you aren't processing input correctly. Something else to look out for would be a framerate spike where occasionally a frame takes a long time to render. Profiling is a good approach here.

2) If for some bizarre reason you actually *need* better than 1/60th of a second of response time (something that I don't think a keyboard can even deliver) you can put input processing in a separate thread.

-me


Yes, indeed it is. I have spent alot of time debugging this issue; I even rewrote half of the input system. And to answer your question: Yes, It's very noticeable. I'm controlling camera movement with the WASD keys and, for example, if you hold down one of them for a while and release, the camera can keep moving for a bit after you release the key. It's also almost impossible to perform delicate movements with the camera, as it seems the application only picks up short key presses some of the time, while some of the time it doesn't. Mouse movement controls the direction of the camera, and it gets somewhat sloppy aswell.

As I said in the OP, this only happens when I have presentation interval set to default. If i set it to immediate i get smooth input with no lag at all. This is why I was wondering if i could somehow prevent presenting if the device is still waiting for vertical sync.

An interesting note is that this only happens in fullscreen. In windowed mode i have no lag whatsoever even if i enable vsync.

As I see it, there's two explanations:

1) Input happens as the application is waiting for Present to finish, and this somehow causes lag. This is plausible because it only happens when Present is causing the application to wait. However, it doesn't make much sense, since the lag shouldn't be more than 1/60th of a second, like you said.

2) Something is wrong with my input system, which I realize now is much more likely. I'm using raw input, and whenever i capture the WM_INPUT message in my windows message loop, my input system processes the input. Could something be wrong with this model? Should i use some other technique?
Could you post your message loop?
Sounds like you are processing one message then moving on to rendering instead of emptying the input queue each frame.

Could you post your message loop?



Message Loop:


MSG msg;
ZeroMemory( &msg, sizeof( msg ) );
while( msg.message != WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else{
if(paused == true){Sleep(100);}
velocity->Run();
}
}




Message Processor:


LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_DESTROY:
{
PostQuitMessage( 0 );
return 0;
}
case WM_ACTIVATE:
{
if(lParam == SC_MINIMIZE){
paused = true;
}else if(lParam == SC_MAXIMIZE){
paused = false;
}
}
case WM_INPUT:
{
velocity->input->Get(lParam);
}
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}


Velocity is the class that encapsulates the entire game. The run method basically only checks if the device is lost, and if it is resets it, otherwise it renders.

lParam is passed to the class that handles input by the Get method.

If you can't find anything wrong here, feel free to request any other part of the code.

Sounds like you are processing one message then moving on to rendering instead of emptying the input queue each frame.


Wow, I can't believe I didn't think of that. That might just be the problem. I'm going to fix that and see if it does it.

Any idea why it only causes input lag when in fullscreen though?
Changed the message loop, however it did not fix the problem :/

Here's the new message loop:

MSG msg;
ZeroMemory( &msg, sizeof( msg ) );
while( msg.message != WM_QUIT )
{
while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

if(paused == true){Sleep(100);}
velocity->Run();
}
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.

This topic is closed to new replies.

Advertisement