Drag Lag

simed

17-01-2012 11:03:47

When I drag a window around, it follows the mouse perfectly at high fps but at lower fps, it follows the mouse like it's on a spring. Other controls like sliders do the same.

The thing is, this effect becomes evident even at quite high fps... it's a bit visible at 45fps and really strong at 30fps. These are hardly unusual fps for a game to run at so I wondered if there is any way to configure this feature?

Thanks.

Altren

17-01-2012 14:32:45

Just checked my application and everything looks not bad. The problem is that you are doing too much logic between processing input and rendering frame. You should do that closer to render, if possible. Here's simple test I tried on MyGUI demos:
while (true)
{
::Sleep(50); // this is some game logic
Ogre::WindowEventUtilities::messagePump();

if (!mRoot->renderOneFrame())
break;
};

while (true)
{
Ogre::WindowEventUtilities::messagePump();
::Sleep(50); // this is some game logic

if (!mRoot->renderOneFrame())
break;
};
In first case everything fine, while in second case window is 50 milliseconds behind mouse. And in both cases we have same fps, i.e. make same amount of useful game logic.

simed

17-01-2012 16:06:33

My fps is 30 because of the rendering - my logic is virtually zero. If I turn off geometry fps goes to 500 and there is no 'lag'. Of course, I suppose MyGUI gets rendered last so it is the same effect. I imagine if you run your test with a bigger geometry or on a slower PC you'll get the same effect.

Does MyGUI somehow queue input events... the moved window is not just 1 frame behind, but when I stop moving mouse I can watch the window move for several frames to catch up. It's a nice effect but I wonder how it works and if it can be disabled so dragged elements 'lock' to the mouse?

Altren

17-01-2012 19:51:53

That's odd, and I guess that your problem might be in input system, that you use. Also there is little difference between running demo on old system and adding sleeps to reduce fps to a low value.
MyGUI never queue input - it store only last injected position and clicks.

simed

17-01-2012 20:37:18

Well when you introduce the sleep after message pump (input) and before render, you get the same issue. When my app is running at 30fps due to rendering, this is equivalent, no?

In your test, you said "window is 50 milliseconds behind mouse". If you repeat that test and drag a window, when you let go of the mouse do you see the window carry on moving until it "catches up"? If MyGUI isn't queuing anything, I wonder how that happens?

To be 100% sure... can you confirm MyGUI does no "drag speed" calculation - it simply sets position of dragged window where it thinks mouse cursor is? Does that mean it's actually Windows queuing the messages?!

FYI, I see this effect slightly even in the LayoutEditor... only slightly though.

Altren

17-01-2012 21:41:06

Well when you introduce the sleep after message pump (input) and before render, you get the same issue. When my app is running at 30fps due to rendering, this is equivalent, no?

In your test, you said "window is 50 milliseconds behind mouse". If you repeat that test and drag a window, when you let go of the mouse do you see the window carry on moving until it "catches up"? If MyGUI isn't queuing anything, I wonder how that happens?

To be 100% sure... can you confirm MyGUI does no "drag speed" calculation - it simply sets position of dragged window where it thinks mouse cursor is? Does that mean it's actually Windows queuing the messages?!

FYI, I see this effect slightly even in the LayoutEditor... only slightly though.

In my case window is always only one step behind, and it is impossible to make anything better that that. When I stop moving mouse and next frame is rendered window is in exact correct position.
And we don't use any "drag speed" or anything similar.

I know how possible and most common reason of behaviour you see - in messages dispatching if dispatch only single event per frame instead of dispatching all events you get delayed events. Something like that (in windows):
while (true)
{
MSG msg;
if ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) ) // <- you must use while loop here instead of peeking single message (i.e. replace "if" with "while")
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

if (!mRoot->renderOneFrame())
break;
};