Tips for Responsive GUI


#1

Hello, thanks for looking.

I am attempting to make my UI as smooth and responsive as possible and am looking for suggestions.

I’ve done some tests on the speed of repainting each of my OpenGL components and since it only takes about 20 microseconds, theoretically I should get close to 50,000 fps ignoring other overhead.
However, due to the latency of the windows message queue (perhaps) and other factors, dragging elements around the gui – vertices in this case – lag noticeably behind the cursor and do not feel responsive. I’m not using any timers, just mouse events to repaint.


#2

It’s obvious that the events provided by the system are not at the timing resolution you require. You need to smooth those values out, which means you need to update them more frequently than you get them. That means you will need some kind of timer/thread/loop driving it.
The easiest way to smooth out mouse movements is to generate your own ‘mouse position’ values, based on filtered interpolations of those suggested in the messages.
Here’s an example of one way to do such a thing…
e.g.

float filter = 0.1f; // [0 = slow, 1 = instant]
smoothedMouseX += (lastKnownActualX - smoothedMouseX) * filter;
smoothedMouseY += (lastKnownActualY - smoothedMouseY) * filter;

…calling this regularly (from a thread/timer/loop) will keep your ‘smoohedMouseX/y’ values up to date (filling in the gaps between messages), as long as you update the lastKnownActualX/y values when the messages are recieved.

That’s one way of making the actual movements smoother at least; the rate at which you choose to repaint depends on your requirements, really.


#3

Plus, your measurement of 20 microseconds is almost certainly rubbish. OpenGL is a command queue system, so all you’re actually measuring is the time needed to submit the commands to the OpenGL queue, unless you’re doing some very clever tricks - in which case 20 us sounds fishy.

Bear in mind, you only need 30-80 fps, depending on your refresh, anything more is a waste.

Meanwhile, you’re right in that you probably won’t get anywhere close to potential maximum frame rates using standard ‘needs repaint’ semantics. Most people end up splitting their ‘real-time’ needs from their ‘GUI’ needs, so neither holds up the other. That way, both can do what they do best - OpenGL drawing every display frame, and juce GUI updating partially as needed.

Bruce


#4

My mistake, but I would still expect rather exceptional performance from a modest couple of OGL components compared to the 300+ fps I can get from a maxed out 2nd generation pc game, UT2004 say, which is at the same time highly responsive to mouse movements. I know I must be missing something.

Haydxn: thanks for the event smoothing trick, I used that for camera movements in a snooker game but I fear that would make mouse input feel even less responsive than it does now.


#5

well, if the events provided by the OS are not smooth enough for you, you’ll get no improvements unless you interpolate them yourself :slight_smile: [that example is just about the most basic method i can think of]


#6

The thing is, the UI is lagging behind the cursor when I drag; it’s not that the cursor updates that are too infrequent. Have you played any pc games with vsync problems, makes the mouse feel like a controller? It feels like that. I just want the gui to be as responsive as the mouse movements themselves.

I attempted to use threading to assign the highest priority to the thread whose component is intercepting the mouse events, but I noticed no improvement.


#7

Wow that’s such an old thread, but I’m experiencing the exact same problem. More specifically this thing :

The way I do it is the way suggested in that thread : when my UI component (which inherits DragAndDropTarget) reveives the itemDragEnter/Move callbacks, it sends an update to the controller, which updates the GUI again (I do that because I have several components to move at once).

The gui->controller->gui path is very fast (<1ms) so I guess it shouldn’t be perceptible. And to update the gui, I simply call ‘setTopLeftPosition’ (I think that’s the way it’s done in the jucer, from what I’ve seen in the code)

I tried a few things, like calling repaint() from a Timer more frquently, but it didn’t help …

So I’m down to the conclusion that itemDragMove isn’t called often enough or do you see any other possible explanation?

I tried to use the mouseListenerMethods, but they don’t work either :

  • mouseDrag sends callbacks to the source component (I want them in the destination component)
  • mouseMove doesn’t send callbacks if a button is pressed …

There’s probably a better way to do this but I’m short of ideas, so if anyone knows what’s the exact cause of this problem and can suggest a solution, it will be greatly appreciated !


#8

Well, if it’s lagging behind, then your updates are coming too quickly. The acid test is whether you stop the mouse and the GUI ‘catches up’.

So you’ll need to do what Jules refers to as ‘coalesced’ updates, so when the GUI can update, it gets the latest value and discards any extra inbetween values that no longer matter.

Bruce


#9

Sorry, do you mean that if it’s the case (the GUI catches up), I have to discard the “inbetween” values ?


#10

Yes, if the GUI catches up, it means you’re essentially buffering values. For each GUI value you only really need as many values as you can manage to draw.

I encounter this frequently when controlling external devices - without throttling, I can send them way more positions than they can get to, so you end up with a backlog. The general principle would be to draw as often as possible/sensible, and at each point take the most current value (discarding any interim values). I think that generally happens in Juce, when you do - update value/position - trigger redraw, but it’s possible to circumvent that by accident, I’m sure.

Bruce


#11

Hi Bruce,

You were right, I did that and it seems to work much better. There’s still some lag but this might be my code’s fault. Gotta investigate.

For those interested in how such a thing works in practice, it’s quite easy indeed :

  • the controller updates some component internal variable which represents it’s position (for example a juce::Point representing the top-left position in the parents space)
  • in the parent component, I’ve got a Timer running with a callback which calls “setTopLeftPosition” on the children components, using the internal variable mentioned above

There’s no redrawing when the variable is updated, so it can be overwriten several times, without a repaint to happen. The timer period is something like 50 ms which allows to have both “kind of fluid” animation, not too much processor load, and no lag.

Gotta investigate more to see if everything is as good as it should be, test it under other configurations and stuff but it seems that so far so good.

What I really don’t understand is that, for example, the jucer doesn’t seem to need to use such “tricks” … :?: