I am creating a Cocoa application in Xcode 6, and the application uses an OpenGLView. The draw method in my extension of NSOpenGLView is getting called repeatedly, but I am not sure at which rate it is being called or a way to set the rate.
Is there a default "framerate" for NSOpenGLView, and is there a way to change it?
Apple has a technote describing how to drive an OpenGL rendering loop. The answer is to use a CoreVideo display link (CVDisplayLink), it will call a callback during the blanking interval.
Generally in any window system, the window is not redrawn on a periodic schedule; it only happens in response to events that cause a "damaged" or "dirty" state.
The number of things that cause this "damaged" state has gotten a lot smaller in recent years thanks to compositing window managers (OS X uses one such window manager). It used to happen whenever a window moved over top it, but in modern window managers it only happens during/after a resize event or when the window is moved.
As you would expect, Cocoa's documentation says the same thing:
- update
Called by Cocoa when the view’s window moves or when the view itself moves or is resized.
Owen Taylor writes an excellent blog; this diagram illustrates what may happen:
If a compositor isn’t redrawing immediately when it receives damage from a client, but is waiting a bit for more damage, then it’s possible it might wait too long and miss the vertical reblank entirely. Then the frame rate could drop way down, even if there was plenty of CPU and GPU available.
Related
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'm trying to prevent my rendering from stopping when my window is out of focus or resizing. In addition to the resizing, if I resize my window smaller, then bigger again anything that wasn't visible when it was smaller is now black. Is there any way to fix this?
There are really two distinct things going on here. The moving/resizing problem is caused by the windows DefWindowProc function which applications use to handle messages that aren't explicitly handled by the application itself. In the case of moving or resizing, it blocks, creating a new message queue to handle most messages, but there are a few that it will still dispatch to the application's main event queue, like WM_TIMER. You can find lots more information in this answer.
The second issue is that your program only "owns" the pixels inside your window, and only those that are not covered up by other windows. When you make the window smaller, the pixels at the edge need to be overwritten to show the window border or whatever's behind the window. When the window is made bigger, some drivers automatically set the newly acquired pixels to black, while others leave them at whatever they were before (usually part of the window border). The OS doesn't remember if those pixels had some specific color the last time the window was that size, because most of the time the program doesn't care. Instead, windows sends a WM_PAINT message to indicate that the application should redraw the window. Your program should either directly handle this, or use a library like GLFW that abstracts it. In addition, you need to handle resize events by calling glViewport with the new size of the window.
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!
We're making a user-space device driver for OS X that moves the cursor using Quartz Events, and we ran into a problem when games — especially ones that run in a windowed mode — can't properly capture the mouse pointer (= contain/keep it within the boundaries of their windows). For example, it would go outside the game window and click on the desktop or nearby inactive applications.
We could fix this if only we could detect when an active application calls CGAssociateMouseAndMouseCursorPosition.
How would you do this? Any ideas are appreciated.
I dont know if this can help you
There is an option called Focus Follows Mouse
Focus Follows Mouse - The Mouse pointer will grab automatically change focus to a new window inisde this one app if you mouse over it, instead of having to click a window to get focus, then clicking to do something.
http://wineskin.urgesoftware.com/tiki-index.php?page=Manual+4.6+Advanced+-+Options
I have written a few different mouse logical layers (for bridging different input devices, etc.). I have found that hooking into the OS level WM_INPUT event is a sure way of getting very real-time mouse position information. There is also a less rigorous solution of just polling the mouse data you need from one of Windows' very primitive DLLs. They are lightning fast. You could poll on a 10ms timer and never see performance loss on a modern machine.
I am trying to make a simple application in which there is a empty red rectangle and whenever the mouse is moved over the upper half border of the rectangle the cursor will become closed hand.
I started with selecting the foundation command line project.Made a transparent NSWindow and embedded a NSView in it with the rectangle, made window to accept mouse moved events(by method: -setAcceptsMouseMovedEvents). I have overridden -canBecomeKeyWindow and -canBecomeMainWindow window to return YES. But somehow none of the -mouseMoved events are being received by NSView.
When I put the same code by making a cocoa application project and creating my window in -applicationDidFinishLaunching method , my view was able to receive -mouseMoved events.
why is it not receiving mouse moved events when I use foundation command line utility project ?
I have also observed that whenever I make a window(carbon or cocoa) through foundation cmd line utility project , the window doesn't become key even on clicking the title bar.On clicking the title bar color remains light grey instead of becoming dark grey. Why is this happening?
I have overridden -canBecomeKeyWindow and -canBecomeMainWindow of NSwindow to return YES.
I would agree with what Joshua has already said. Any application that is going to show a user interface, be it a faceless background process or one which shows up in the Dock, should be in the form of an application bundle, not a plain old Mach-O executable like the Foundation tool template will create.
Also, there are reasons why views do not respond to mouseMoved: events by default:
Mouse moved events can quickly flood the event queue
There is generally little reason to use mouseMoved:, as tracking areas are
far more effective and efficient.
A while back, I wrote a little test app that demonstrates the differences between these 2 approaches:
Moving your mouse around the upper view for roughly 20 seconds results in 1000 events, while in the lower view, which uses tracking areas, less than 50.
Sample GitHub project: https://github.com/NSGod/MouseMoved-vs-TrackingAreas
Again, as Joshua mentioned, it would be helpful if you could describe what you're trying to accomplish. If your app needs to be a background app (LSUIElement == 1), and present an interface without appearing in the Dock, then there are ways to do that (as Josh mentioned, a command-line, non-bundled app is not the way).
You have no event loop to detect events and pass them to your window because your program does not start an NSApplication. See the main.m file of a typical Cocoa application.
It might be helpful to describe what you're trying to accomplish by taking this approach. My guess is you're building a daemon but want a GUI interface to manage the otherwise "headless" daemon. That or you're building a new login management system. In either case, there are specific ways to do both and this isn't it. :-)