Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

kamy

macrumors member
Original poster
Jul 27, 2011
44
0
Hi Mac Experts,

I'm really tearing my head out on this one!

I'm subclassing NSOpenGLView to do some animations on the view.
When i say animations, i mean that am moving some images from left to right of the view and so on.

This is what i do

a) Initialise the OpenGL system
Code:
- (id)initWithFrame:(NSRect)frame
{
    NSOpenGLPixelFormatAttribute attrs[] = {
        
        NSOpenGLPFANoRecovery, // Enable automatic use of OpenGL "share" contexts.
        NSOpenGLPFAColorSize, 24,
        NSOpenGLPFAAlphaSize, 8,
        NSOpenGLPFADepthSize, 16,
        NSOpenGLPFADoubleBuffer,
        NSOpenGLPFAAccelerated,
        0
    };
    // Create our pixel format.
    NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
    self = [super initWithFrame:frame pixelFormat:pixelFormat];
    
  
    return self;
}

// Synchronize buffer swaps with vertical refresh rate
- (void)prepareOpenGL
{
    GLint swapInt = 1;
    [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
    
}


b) Setup the Timers on Start
Code:
// Put our timer in -awakeFromNib, so it can start up right from the beginning
-(void)awakeFromNib
{
 if( gameTimer != nil )
        [gameTimer invalidate];
    
    gameTimer = [NSTimer timerWithTimeInterval:0.02   //time interval
                                        target:self
                                      selector:@selector(timerFired:)
                                      userInfo:nil
                                       repeats:YES];
    
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize*/
}


// Timer callback method
- (void)timerFired:(id)sender
{
    //The timer fires this method every second
    currentTime ++;

    // All we do here is tell the display it needs a refresh
    [self setNeedsDisplay:YES];
}

c) Animate my stuff in drawRect
Code:
- (void)drawRect:(NSRect)rect
{
    [self animateFrame:rect];
    
    // the correct way to do double buffering is this:
    [[self openGLContext] flushBuffer];
}

d) The animateFrame method just draws images in various rectangle positions
Code:
    [curImage drawInRect:targetRect
                fromRect:sourceRect
               operation:NSCompositeSourceOver
                fraction:1.0f];

------------------------------------------------------------------------
So here is the problem

When i start the app - i can see that time timer is being fired, drawRect is being invoked and the images are being drawn.

However i can only see the animation of the images, when i drag the window and move the window..
When the window is still the images just stay frozen.
When i move the window - i see the images are moving...

Ideally i want the images to move even if the window is static!

Can someone shed light on what is going on here or what have i missed?

Appreciate some help.
Thanks in advance!
KamyFC
 
When i start the app - i can see that time timer is being fired, drawRect is being invoked and the images are being drawn.

Ideally i want the images to move even if the window is static!

Can someone shed light on what is going on here or what have i missed?
Thanks for posting good info. So many here post generalizations and expect specific help.

Without seeing more code it is difficult to assess exactly but have you looked into NSWindow's viewsNeedDisplay: method?
 
I think the problem may lie in this code:
Code:
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize*/
I'm pretty sure that a single NSTimer can only be added to a single run-loop, and not more than once to that run-loop.

I suspect that adding a timer to a run-loop removes it from all other run-loops it was previously added to. The result would be that all previous add-to-run-loop operations are canceled, and only the last one is effective. This is consistent with your observed behavior, i.e. animation only occurs while tracking, and the tracking mode is the last mode used.

You need to add the timer only once, with a single mode value representing all modes you want the timer to fire in. Use NSRunLoopCommonModes as the mode (see NSRunLoop class reference), which you can add other modes to as needed (assuming the default set of modes in NSRunLoopCommonModes is insufficient).

A simple way to test the above hypothesis is to reverse the order of the two Obj-C statements. That is, replace with this:
Code:
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize*/
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSDefaultRunLoopMode];
If the program animates only while idle and stops animating while tracking, then that's consistent with the above explanation.

The next test would be to add the timer only once, use NSRunLoopCommonModes for the mode, and ensure tracking is added to the set of common modes.
 
I suspect that adding a timer to a run-loop removes it from all other run-loops it was previously added to.
Good catch. I was reading too fast and skipped that code.

FWIW: Apple docs says NSEventTrackingRunLoopMode should only be used for modal tracking events like a mouse drag loop. Depending on how elaborate/intense the animation is( and to take advantage of multiple cores ), maybe consider GCD instead of timers. The timers and runloop are set in awakeFromNib:, so the calls to currentRunLoop: would be on the main thread. Everything on the main thread can work for brief animations but the UI might become unresponsive for more intense work. YMMV.
 
Solved using NSView

Hey guys,

thanks for the replies.

I solved it after using NSView. I removed NSOpenGLView and used NSView.
I can see the animations.

----------

I think the problem may lie in this code:
Code:
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize*/
I'm pretty sure that a single NSTimer can only be added to a single run-loop, and not more than once to that run-loop.

I suspect that adding a timer to a run-loop removes it from all other run-loops it was previously added to. The result would be that all previous add-to-run-loop operations are canceled, and only the last one is effective. This is consistent with your observed behavior, i.e. animation only occurs while tracking, and the tracking mode is the last mode used.

You need to add the timer only once, with a single mode value representing all modes you want the timer to fire in. Use NSRunLoopCommonModes as the mode (see NSRunLoop class reference), which you can add other modes to as needed (assuming the default set of modes in NSRunLoopCommonModes is insufficient).

A simple way to test the above hypothesis is to reverse the order of the two Obj-C statements. That is, replace with this:
Code:
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSEventTrackingRunLoopMode]; //Ensure timer fires during resize*/
    [[NSRunLoop currentRunLoop] addTimer:gameTimer
                                 forMode:NSDefaultRunLoopMode];
If the program animates only while idle and stops animating while tracking, then that's consistent with the above explanation.

The next test would be to add the timer only once, use NSRunLoopCommonModes for the mode, and ensure tracking is added to the set of common modes.



I shall try in my other project, where am using an NSOpenGLView. Good information. Grazie!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.