Refresh a NSOpenGLView within a loop without letting go of the main runloop in Cocoa - cocoa

I am building an Cocoa/OpenGL app, for periods of about 2 second at a time, I need to control every video frame as well as writing to a digital IO device.
If after I make the openGL calls I let go of the main thread (like if I make the openGL calls inside a timer fire-method with an interval of like 0.01 Sec) openGLview is refreshed with every call to glFinish().
But If I instead keep the main thread busy like in a 2 second long while loop, openGl calls won't work (surprisingly the first call to glFinish() would work but the rest won't).
The documentation says that glFinish should block the thread until the gl commands are executed.
Can anybody please help me understand what is going on here or provide a solution to this problem.
To make it clear, I want to present 200 frames one after another without missing a frame and marking each frame refresh by writing to a digital IO port (I don't have a problem with this) all on Snow Leopard.

This is not quite my department - pretty vanilla NSOpenGLView user myself - but from the Mac OpenGL docs it looks like you might want to use a CVDisplayLink (Q&A1385) for this. Even if that won't do it, the other stuff there should probably help.
EDIT
I've only done some basic testing on this, but it looks like you can do what you want as long as you first set the correct OpenGL context and then swap buffers after each frame (assuming you're using a double buffered context):
// inside an NSOpenGLView subclass, somewhere outside the usual drawing loop
- (void) drawMultipleFrames
{
// it might be advisable to also do a [self lockFocus] here,
// although it seems to work without that in my simple tests
[[self openGLContext] makeCurrentContext];
// ... set up common OpenGL state ...
for ( i = 0; i < LOTS_OF_FRAMES; ++i )
{
// ... draw your frame ...
glFinish();
glSwapAPPLE();
}
// unlockFocus here if locked earlier
}
I previously tried using [[self openGLContext] flushBuffer] at the end of each frame instead -- that doesn't need glSwapAPPLE but doesn't block like glFinish so you might get frames trampling over one another. This seems to work OK with other apps, runs in the background etc, but of course YMMV.

Related

Drawing to an NSView while inside an event handler

I've tried everything I can think of, but no luck.
I have a project that is essentially an 'interpreter' of a programming language. So events get passed to the interpreter loop, the interpreted code does whatever (in this case, updates a memory bitmap), then the interpreter loop returns and the event handler returns. Eventually the app's drawRect gets called and the memory bitmap gets drawn to the NSView. This is all fine MOST of the time.
But... there's a few situations where the interpreted 'code' wants to cause a short animation, and does that by updating the memory bitmap, usleep()'ing for a few milliseconds, updating the memory bitmap, usleep()'ing, etc. The animation takes a little less than a second, so the thread-blockage shouldn't be an issue.
The problem is that NONE of the animation shows, and the screen doesn't get updated until after the interpreted 'code' ends, and the event returns.
The sleep function that gets invoked when the interpreted code indicates it wants to sleep looks like this:
void KSleep(DWORD tm) {
if( [pView lockFocusIfCanDraw] ) {
inSleep = true;
[pView setNeedsDisplay:YES];
[pView display];
[pView drawRect:NSMakeRect(0, 0, pView.frame.size.width, pView.frame.size.height)];
[pView unlockFocus];
}
usleep(tm*1000);
}
'inSleep' is a global variable I set up for testing purposes, 'pView' is a global NSView* to the window's only view. NOTE: Yes, some of the above code is redundant, I'm just including it to show that I've tried numerous combinations of trying to indicate to the OS that the view is dirty and to update it. None of them have worked.
The drawRect code (removing all of the code that does the run-of-the-mill blitting of the bitmap) looks like this:
-(void)drawRect:(CGRect)rect {
CGContextRef context = [[NSGRaphicsContext currentContext] graphicsPort];
CGContextSaveGState(context);
if( inSleep ) CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
else CGContextSetRGBFillColor(context, 1.0, 1.0, 0.0, 1.0);
CGContextFillRect(context, CGRectMake(0,0,200,200));
CGContextRestoreGState(context);
}
So:
1) The -mouseDown() event occurs and it calls the interpreter with the event.
2) The interpreted code draws to the bitmap (I'm ignoring that here, as it's not important to the working or not working of the screen update), and invokes 'sleep.'
3) The interpreter, seeing the 'sleep' invocation, calls Ksleep() (above).
4) Ksleep locks the focus, which seems to create a context, as without that, the debugger spit out warnings of a 0x0 context during the drawRect() function, and with the the lockFocus it does not and appears to have a valid context value.
5) Ksleep flags the view as needing an update and calls (varyingly) 'display' and/or 'drawRect', etc.
6) The drawRect routine DOES get control (breakpoints indicate that everything is working okay in that regard). 'inSleep' IS set correctly. It steps through everything in drawRect as expected. But NOTHING shows on the display, until...
7) drawRect returns to Ksleep, the sleep times out, the interpreter keeps interpreting, the interpreted code does more drawing and more sleeps, about 10 times (thus repeating steps 2-7 about 10 times).
From the time the program starts until the mouse action causes the 'animation' attempt, a yellow rectangle is drawn in the view. Once the 'animation'-causing mouse click occurs, NOTHING gets updated in the window until the animation completely finishes (even though drawRect IS executing multiple times throughout that animation attempt), THEN the rectangle turns blue. But breakpoints show that execution IS passing through the drawRect (with 'inSleep' true) each time the KSleep() routine is called.
Is this a thread thing? (The program is not explicitly creating any threads.)
I'm not particularly looking for suggestions for how to avoid the animation/KSleep structure, I realize that's not the preferred Macos method of doing things, but this is an attempt to port an old project from elsewhere, and modifying the 'interpreted' code to avoid this is not a possibility.
Thanks for any ideas or suggestions.
The solution I found was to place ALL of the interpreter code into a second thread, freeing up the main thread to be able to update the display. Even though the drawRect routine was running and drawing, it wasn't getting flushed to the display until after the current "command interpretation" cycle completed and returned. This page has a fair amount of information on the subject:
https://developer.apple.com/library/content/technotes/tn2133/_index.html
Additionally, all of the 'invalidate' calls/statements had to be saved/buffered into a common "smallest rectangle that includes all of the invalidates", and then that invalidate done AFTER the "command interpretation" returned AND/OR before a sleep() was done.
PITA.

What is the recommended frequency for UI changes?

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.

Correct way to drive Main Loop in Cocoa

I'm writing a game that currently runs in both Windows and Mac OS X. My main game loop looks like this:
while(running)
{
ProcessOSMessages(); // Using Peek/Translate message in Win32
// and nextEventMatchingMask in Cocoa
GameUpdate();
GameRender();
}
Thats obviously simplified a bit, but thats the gist of it. In Windows where I have full control over the application, it works great. Unfortunately Apple has their own way of doing things in Cocoa apps.
When I first tried to implement my main loop in Cocoa, I couldn't figure out where to put it so I created my own NSApplication per this post. I threw my GameFrame() right in my run function and everything worked correctly.
However, I don't feel like its the "right" way to do it. I would like to play nicely within Apple's ecosystem rather than trying to hack a solution that works.
This article from apple describes the old way to do it, with an NSTimer, and the "new" way to do it using CVDisplayLink. I've hooked up the CVDisplayLink version, but it just feels....odd. I don't like the idea of my game being driven by the display rather than the other way around.
Are my only two options to use a CVDisplayLink or overwrite my own NSApplication? Neither one of those solutions feels quite right.
I am curious to see if anyone who has actually done this cares to weigh in, but here is my understanding:
Apple pushes the CVDisplayLink solution over doing a loop on the main thread that uses -nextEventMatchingMask:untilDate:inMode:dequeue: because, I think, it provides better responsiveness for UI controls. This may not be relevant for full-screen games. (Note: You don't need to replace NSApplication to use that form of game loop.) I think the main potential issue with using CVDisplayLink is that it will only run one frame in advance and it does this determination early, which is even stronger than vertical sync. On the plus side, it might improve latency.
Other solutions include decoupling rendering from game logic and running game logic periodically on the main thread and rendering on the CVDisplayLink thread. I would probably only recommend this, however, if you run into issues with the game-driven-by-display paradigm.
You don't necessarily have to make your own NSApplication based class or use CVDisplayLink to get around the fact that an app's runloop is hidden from you in Cocoa.
You could just create a thread and have your run loop in there instead.
For what it's worth though, I just use CVDisplayLink.
I'm sticking something up here to revive this question...mainly out of portability. I found from studying the OLC Pixel Game Engine, that it works with a do{}while loop and std::chrono to check the timing of the frame to calculate fElapsed Time. Below is some code I wrote to do the same thing. It also adds a makeup portion, to govern the framerate from shooting above a certain value, in this case, 60 FPS.
c++ code
int maxSpeedMicros = 16700;
float fTimingBelt; //used to calculate fElapsedTime for internal calls.
std::chrono::steady_clock::time_point timingBelt[2];
bool engineRunning = false; //always have it true, until the engine stops.
bool isPaused = false;
do {
timingBelt[1] = std::chrono::steady_clock::now();
fTimingBelt = std::chrono::duration_cast<std::chrono::microseconds>(timingBelt[1] - timingBelt[0]).count() * 0.000001;
if (isPaused) {
do {
std::this_thread::sleep_for (std::chrono::milliseconds(100));
timingBelt[1] = std::chrono::steady_clock::now();
} while (isPaused);
}
timingBelt[0] = std::chrono::steady_clock::now();
// do updating stuff here.
timingBelt[1] = std::chrono::steady_clock::now();
int frameMakeup = std::chrono::duration_cast<std::chrono::microseconds>(timingBelt[1] - timingBelt[0]).count();
if (frameMakeup < maxSpeedMicros) {
int micros = maxSpeedMicros - frameMakeup;
std::this_thread::sleep_for (std::chrono::microseconds(micros));
}
} while (engineRunning);
However, that code was in direct conflict with Cocoa's event driven model.
Custom main application loop in cocoa
So as a bandaid, I commented out the whole loop, and created a new method that runs one iteration of the loop. I then implemented this in my AppDelegate:
Objective C Code
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
engine->resetTimer();
[NSTimer scheduledTimerWithTimeInterval:0.016666666667 target:self selector:#selector(engineLoop) userInfo:nil repeats:YES];
}
-(void) engineLoop { //Let's handle this by the engine object. That's too complicated!
engine->updateState();
[glView update]; //Since the engine is doing all of its drawing to a GLView
[[glView openGLContext] flushBuffer];
}
Still to do is adjust the tolerance of the timer object. Apple Developer documentation states that if a timer object misses the next window, it will wait for the next frame time. However, a tolerance allows it to shift the timing of future events to make smoother framerate transitions and better use of CPU power.
So at this point I am open to suggestions and input about what others have done to make more portable code. I am planning on a boolean argument in the constructor of the engine named "eventDriven" and if false, will start its own game loop thread, then split out the top event loop to call an "engineUpdate" method that handles all of the code that can be event driven. Then in the case of building on an event driven system, the delegate can just construct the engine with a engineUpdate = TRUE and have their events drive the gameUpdate.
Has anyone done this? and if so, how does it perform cross platform?

Anyone know why nextEventMatchingMask:untilDate:inMode:dequeue: take many ms to return an event?

In a OS X game calling this was recommended as the way to get keyboard and mouse events.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
if(!event) break;
processevent(event);
...
}
[pool release];
which is called in the games main loop (its cross platform).
Since the most recent versions of OSX 10.5.X this call is suddenly taking many milliseconds per event when there is an event available, and the game' frame rate is affected any time an event appears. If there are multiple events it can take as long as 10ms per frame on a slower mac.
Anyone have a clue as to why this is? Or what I can do alternatively to get events without impacting the game so much?
I tried managing the mouse events myself by getting the mouse position manually and when it gets close to the edge of the screen warping it to the center, but that causes a hitch in the motion (only when the cursor is hidden of course).
Other alternatives might be getting stuff from the HID manager,which we already do for joysticks, but HID is not terribly clear.
The faster the mac the more these hitches from getting events are noticeable.
I think you need to release and re-allocate the autorelease pool inside your loop: as you have the loop all the autoreleased items are just building up and never being flushed.
Offhand, I don't know why the method is taking so long to return. That's worth investigating on the cocoa-dev list or another Apple forum resource. My guess is that managing the events yourself is a bad idea — AppKit is optimized for that, and you can safely bet it will be a lot faster than thrown-together custom code.
However, there is something you can do to keep it from affecting your game: put it in a separate thread. This is a suggested approach to keep your UI from freezing up during a long method call. Apple has published an Introduction to Threading programming guide that can help you get up to speed with the critical concepts you'd need.
I think you have to use an actual value in the untilDate argument, like [NSDate distantFuture] or [NSDate distantPast]. The function will block until an event is available in the former case, while it will return immediately with a nil event in the latter case.
I learned this from the GLFW source code.

Updating the progress indicator while downloading firmware to the device

I am developing a cocoa application which downloads firmware to the device. The progress of downloading is showed using NSProgressIndicator. I call the -incrementBy: method of NSProgressIndicator after DeviceRequestTO method in a while loop. But the progress indicator gets updated only after the entire firmware is written to the device. It shows 100% completion at one go itself. So I added the -displayIfNeeded method of NSView class. Now it shows progress smoothly but this too occurs after the firmware download is complete. How can I achieve the progress indication and write operation simultaneously?
Following is the code:
while(1)
{
int result = (*dev)->DeviceRequestTO(dev, &request);
printf("\nBlocks Written Successfully: %d",DfuBlockCnt);
[refToSelf performSelectorOnMainThread:#selector(notifyContent)
withObject:nil
waitUntilDone:NO];
}
//In main thread
- (void)notifyContent{
[dnldIndicator incrementBy:1];
[self displayIfNeeded];
}
The method you need to call is setNeedsDisplay:, not displayIfNeeded. The latter means “send yourself display if somebody has sent you setNeedsDisplay:YES”. If you don't do that last part, the view doesn't know it should display, and displayIfNeeded will do nothing.
And once you add the setNeedsDisplay: message, you may be able to cut out the displayIfNeeded message, as the framework sends that message to the window (and, hence, to all its views) periodically anyway.
Your code looks exactly like some that I use for updating UIProgressIndicators and NSProgressIndicators on the Mac and iPhone, code that works perfectly for me. I'm assuming, like menumachine, that your while loop exists on a background thread (created using performSelectorInBackground:withObject: or NSThread's detachNewThreadSelector:toTarget:withObject:).
Are the minValue and maxValue of the progress indicator set correctly (0 and 100 or whatever your scale is)?
How frequently do updates occur? Maybe you're sending too many events too quickly and the UI is not having a chance to update properly.
This code should work, as far as I can tell.

Resources