NSOpenGLView - View not refreshing until i move the window...

Discussion in 'Mac Programming' started by kamy, Nov 16, 2014.

  1. kamy macrumors member

    Joined:
    Jul 27, 2011
    #1
    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
     
  2. briloronmacrumo macrumors 6502

    briloronmacrumo

    Joined:
    Jan 25, 2008
    Location:
    USA
    #2
    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?
     
  3. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #3
    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.
     
  4. briloronmacrumo macrumors 6502

    briloronmacrumo

    Joined:
    Jan 25, 2008
    Location:
    USA
    #4
    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.
     
  5. kamy thread starter macrumors member

    Joined:
    Jul 27, 2011
    #5
    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 shall try in my other project, where am using an NSOpenGLView. Good information. Grazie!
     

Share This Page