I have a cocoa application window (NSWindow) which position on the screen should be updated frequently (depending on some calculation). As noticed in the documentation, UI changes should be made on the main thread:
void calculationThread()
{
while(true)
{
calculatePosition();
if(positionChanged)
{
dispatch_async(dispatch_get_main_queue(), ^{ setWindowPos(); });
}
}
}
void setWindowPos()
{
[window setFrame:_newFrame display:YES];
}
Now the problem I have is that the window movement is very slow and delayed. After making some profiling I see that the calculation process takes about 40mSec, meaning that I'm queueing up a backlog of UI updates 25 times a second.
I've read here that this might be faster than they can be processed and timer should be used to fire the changes every tenth of a second or so. But, wouldn't it be too slow for the human eye (I mean, in that case the movement wouldn't be delayed but would be lagged causing pretty much the same affect).
I will appreciate some knowledge sharing on this. Actually my main 2 questions are:
Are 25-30 UI updates per second really to much?
If yes, what is the recommended UI changes frequency?
The frequency at which a window can be moved around onscreen without problems will of course depend upon the speed of the user's machine, the video card they have, the size of the window, and probably a bunch of other factors. There is no single good answer to this. However, if you just drag a window around on your screen, you will notice that it can probably be moved very smoothly (unless your machine is very busy or very low on memory or something); I would not expect 25 times per second to produce a problem on a modern Mac. Not even close, in fact.
#RobNapier's points about Core Animation etc. are fine, but overstated I think; there is nothing inherently wrong with changing your UI using a timer or other periodic update if that is what you actually want to do. CoreAnimation is a toolkit for making some types of animation easier; using it is not required, and it is not suited to every problem. Similarly, if you want to make changes that are actually synched to screen refresh then CVDisplayLink is useful, but it doesn't really sound like that's what you want to do.
For your purposes, your basic approach seems fine, although I would suggest adding an NSDate check in order to skip updates if the previous update was less than, say, 1/60th of a second previous. After all, the calculation appears to take 40mSec on your machine, but it might be much faster on some other machine; you want to throttle your drawing to a reasonable rate just to be a good citizen.
So what is the problem, then? I suspect the issue might actually be your call [window setFrame:_newFrame display:YES]. If you look at Apple's docs for that method, they state "When YES the window sends a displayIfNeeded message down its view hierarchy, thus redrawing all views." Each time you call that method, then, you are not only moving your window (which I gather is your intention); you are redrawing all of the contents of the window, too, and that is slow. If you don't need to do that, then that is the overhead you need to eliminate. Call setFrameOrigin: or setFrameTopLeftPoint: instead (which make the semantics clear, that you are moving the window without resizing it or redrawing it), or perhaps just setFrame:display: passing NO instead of YES, and I'm guessing your performance problem will vanish.
If you do in fact need to redraw the window contents every time, then please edit the problem description to reflect that. In that case, the solution will have to involve profiling why your window drawing is slow, and figuring out ways to optimize that, which is an entirely different problem.
As you've discovered, you should never try to drive the UI from a tight loop. You should let the UI drive you. There are three primary tools for that.
For simple problems, AppKit is capable of moving windows around the screen. Just call [NSWindow setFrame:display:animate:]. You can override animationResizeTime: to modify the timing.
In many cases AppKit doesn't give enough control. In those case, the best tool is almost always Core Animation. You should tell the system using Core Animation how you where you want UI elements to wind up, and over what period and path, and let it do the work of getting them there. See the Core Animation Programming Guide for extensive documentation on how to use that. It focuses on animating CALayer, but the techniques are similar for NSWindow. You'll use [NSWindow setAnimations:] to add your animation. Look at the NSAnimatablePropertyContainer protocol (which NSWindow conforms to) for more information. For a simple sample project of animating NSWindow, see Just Say No from CIMGF.
In a few cases, you really do need to update the screen manually at the screen update frequency. I must stress how rare this situation is. In almost all cases, Core Animation is the correct tool. But in those rare case (some kinds of video for instance), you can use a CVDisplayLink to handle this. That will call you each time the screen would like to refresh, giving you an opportunity to update your content to match.
I develop audio plugins, which are run inside their hosts and work realtime. Each plugin has its own window with controls, which often contains some kind of analysis pane, a pretty big rectangle that gets repeatedly painted (e.g. 20-50x per second). This is all working well.
The trouble comes when the user adjusts a parameter - the plugin uses WM_MOUSEMOVE to track mouse movements and on each change calls ::InvalidateRect to make the relevant portion of the window be redrawn. If you move quickly enough, the window really gets quickly repainted, however there seems no time for the host and other windows to be redrawn and these usually perform some kind of analysis feedback too, so it is really not ideal.
No my questions:
1) Assuming the host and other window are using ::InvalidateRect too, why mine is prioritized?
2) How to make ::InvalidateRect not prioritized, meaning the window needs to be invalidated, but it may be later, the rest of the system must get time for their redrawing too.
Thanks in advance!
If any one has got an explanation for why does flickering occur on a UI (for windows form) when it incorporates more sub controls (like, data grid's, images etc).
Thanks in advance.
Flickering is usually a problem that has to do with intensive graphics operations or custom draws. The best way to solve it is to make the flickering controls double buffered. Since you don't give us much to go by, that would be an obvious first choice. You could also manually invalidate controls triggered by certain events.
Here's an article that should get you started:
Double Buffering Windows Forms
I have a ContentPresenter that switches between 2 states ("editor" and "viewer"). The states have different heights and different values of Content and ContentTemplate properties.
In one state change I resize the control by playing a Storyboard and in reaction to Storyboard.Completed event I change Content/Template properties.
It works basically ok, except I observed occasional flickering. When playing in debugger I observed that modifying above properties in Completed handler may cause strange screen effects (portions of the screen empty, extra shading, lines etc.) that the user observes as flickering.
However, whenever I postponed the ContentPresenter changes by using Dispatcher.BeginInvoke(), the problems seem to disappear. (More testing needed.)
Anyone has an explanation? On the web you'll find numerous code examples that perform UI changes in Storyboard.Completed handler, hence I find it a bit strange.
I suspect it may be more due to timing than needing to be on the UI thread. Try doing a Sleep for a second or so before changing the Content/Template properties instead of using Dispatcher.BeginInvoke(). If the flicker is gone then perhaps trying to change those properties while the Content was busy resizing itself during the storyboard was the culprit - perhaps assigning a fixed size or otherwise disabling the constant resizing during the storyboard would help. That's just a guess though, I'd have to see a sample to really figure it out. If you can make a small sample to reproduce it I'd gladly look at it.
We have a fairly complex GUI, so when certain windows are resized their Redraw() is set to false till the operation is completed. The problem with this is that if the OS "Show window content while dragging" setting is checked, when decreasing the window's size the windows behind it are not repainted. This means I have to force the repaint myself so the remains of the resized window are deleted. I have no problem getting the dimensions of the region that was uncovered. What I'm looking for is best way to cause all windows within that region to repaint their part.
Not being much of a GUI programmer, I can traverse the uncovered region and list the windows in it. Then, I can ask each one of them to repaint its part. But I'm quite certain there has to be a better way to do this...
It is worth mentioning the app is written in PowerBuilder. This means I can call whatever Win32 function I'd like, but have limited control over the GUI behavior and the message handling. If there's a better way to prevent the window's content resize from being visible, or there's a way to make a non-redrawn window clean after itself, I'd love to hear it (just have the limitations above in mind).
I'm curious what version of PowerBuilder you are working in? I do resizing all the time and never run into issues like you are describing.
Maybe you can lay out some more detail on why you need to set your redraws to false within the PowerBuilder environment.
Hope I can help.