NSOpenGLView not redrawing

Discussion in 'Mac Programming' started by tobeythorn, Aug 7, 2008.

  1. tobeythorn macrumors newbie

    Joined:
    Jun 30, 2008
    #1
    I've been trying to write a simple subclass of NSOpenGLView. However, the view never redraws unless I resize the window containing it. This has been driving me crazy for about a week and I've tried everything I can think of. My drawRect in my NSOpenGLView is below. I've set double buffer for the view in IB.

    Code:
    -(void) drawRect: (NSRect) rect	{
    	NSLog(@"%@", @"Draw Rect");
    	if(rootNode == nil)	{
    		rootNode = [ self createScene ];
    	}
    	
    	[[self openGLContext] makeCurrentContext];
    	
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    
    	glClearColor(0, 0, 0, 0);
    	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    	
    	id<NSObject> myRenderer = [ [ Renderer alloc ] init ];
    	[ myRenderer renderTree: rootNode ];
    
    	[ [ self openGLContext ] flushBuffer ];
    }
    
    My createScene method simply creates a scenegraph "node" that contains VBO's to make a triangle. [myRenderer renderTree: rootNode] just draws the node:

    Code:
    -(void) renderLeaf: (id<NSObject>) leaf	{
    		glPushMatrix();
    
    		glTranslatef(((float) rand())/(RAND_MAX), 0.5f, 0.0f);
    		glColor3f(((float) rand())/(RAND_MAX), 0.06f, 0.35f);
    	
    		glBindBufferARB(GL_ARRAY_BUFFER_ARB, [ leaf vboIDv ]);
    		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, [ leaf vboIDp ]);
    		glEnableClientState(GL_VERTEX_ARRAY);
    		glVertexPointer(3, GL_FLOAT, 0, 0);
    
    		glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0);
    		glDisableClientState(GL_VERTEX_ARRAY);
    
    		// bind with 0, so, switch back to normal pointer operation
    		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    		glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
    				
    		glPopMatrix();
    		
    	}
    Cocoa is supposed to be so easy, but it's starting to seems like it just obscures things that shouldn't be obscured, and without thorough explanation in the API.

    Anyway, many thanks if you can give me some suggestions to fix this problem.

    -Tobey
     
  2. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #2
    It might be down to the way you initialised your view.

    Don't know if this helps but this is how I initialise my view:-

    Code:
    
    - (id) initWithFrame: (NSRect) frame
    {
    	GLuint attribs[] = 
    	{
    		NSOpenGLPFANoRecovery,
    		NSOpenGLPFAWindow,
    		NSOpenGLPFAAccelerated,
    		NSOpenGLPFADoubleBuffer,
    		NSOpenGLPFAColorSize, 24,
    		NSOpenGLPFAAlphaSize, 8,
    		NSOpenGLPFADepthSize, 24,
    		NSOpenGLPFAStencilSize, 8,
    		NSOpenGLPFAAccumSize, 0,
    		0
    	} ;
    
    	NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; 
    	
    	if (!fmt)
    		NSLog(@"No OpenGL pixel format");
    
    	return self = [super initWithFrame:frame pixelFormat: [fmt autorelease]];
    }
    
    b e n
     
  3. tobeythorn thread starter macrumors newbie

    Joined:
    Jun 30, 2008
    #3
    Ben,
    I tried initializing like you suggested, but drawRect is still only happening once.
     
  4. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #4
    Ah, if you want the view to animate then you will need to set up a timer.

    b e n
     
  5. tobeythorn thread starter macrumors newbie

    Joined:
    Jun 30, 2008
    #5
    Well, everything I've read says that you just need to let the view know it needs updating at the end of drawRect ([ [ self openGLContext ] flushBuffer ];). I don't want to use a timer because I don't want locked framerates. Also, where would this timer go? InitWithFrame?
     
  6. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #6
    All that [ [ self openGLContext ] flushBuffer ] does is updates your view by copying the backbuffer to the front. It doesn't trigger another redraw on your view. It's up to you to force a redraw. You might want to do this when the scene has changed in some way, or you may want to do it on a regular basis using a timer. You can force a redraw by sending the view a setNeedsDisplay message, eg

    Code:
    [ self setNeedsDisplay: YES ] ;
    
    If you decide to add a timer then you would need something like this:-

    Code:
    - (void) drawRect: (NSRect) rect
    {
    	if ( ! animationTimer )
    		animationTimer=[ [ NSTimer scheduledTimerWithTimeInterval:0.017 target:self selector:@selector(animationTimerFired:) userInfo:nil repeats:YES ] retain ] ;
    ....
    ....
    ....
    }
    
    /**
     * Service the animation timer.
     */
    - (void) animationTimerFired: (NSTimer *) timer
    {
    	[ self setNeedsDisplay: YES ] ;
    }
    
    
    animationTimer would be an instance variable of your NSOpenGLView derived view class.

    b e n
     
  7. tobeythorn thread starter macrumors newbie

    Joined:
    Jun 30, 2008
    #7
    Ben,
    That's the thing, I've tried placing setNeedsDisplay at the end of my drawrect method, but with no success. It draws once fine, but unless i resize the window, or i awake it, it won't redraw.
     
  8. lazydog macrumors 6502a

    Joined:
    Sep 3, 2005
    Location:
    Cramlington, UK
    #8
    It's a bit of a mystery to me then! If you like post your code and I can try running it.

    b e n
     
  9. tobeythorn thread starter macrumors newbie

    Joined:
    Jun 30, 2008
    #9
    SOLVED:
    I decided to strip things down and make a non-openGL custom NSView to see if it would update correctly. It did not. After many many hours reading Apples documentation, i found this and this.


    Basically, the window should automatically redraw itself, but doesn't work right. I have no idea why. So instead, since I want to control updates manually in my game anyway, I do [ myWindow setAutodisplay:NO ]. However, it is still not enough to manually display the view. The containing window must be manually displayed afterwards.

    CONCLUSION: Apple needs to emphasize that the window needs to be updated, and not just its views in it's documentation. Apple also needs to update it's deprecated openGL examples and realize too that NSTimers are not suited for every application and provide some examples without. Lastly, Apple guides are good.
     
  10. jdiamond macrumors 6502

    Joined:
    Dec 17, 2008
    #10
    View only redraws on resize...

    I went through a similar problem, that also frustrated me for weeks, but had a different solution:

    Just make the OpenGLView single buffered.
    I tried it, and OS-X seems to have enough built in buffering that I see no flickering or tearing.

    P.S. Yeah, some of the OpenGL examples are out of date enough to crash the machine. But this is less a documentation issue, and more a failure on Apple's part to provide backwards compatibility.
     
  11. rossipoo macrumors regular

    Joined:
    Jun 7, 2009
    #11
    Setting needsDisplay to YES within the drawRect method would be terribly inefficient. Cocoa probably safeguards against this practice.

    Basically it works like this: when you change any variables that influence drawRect, you also need to set needsDisplay. If you are rotating a cube, then you need to set needsDisplay in the same place that you change the orientation variable. If something happens when you press X, then put setNeedsDisplay into your "X" code.

    It is common to set up a timer which sets needsDisplay 60 times per second for example, but it is not necessary.
     
  12. jdiamond macrumors 6502

    Joined:
    Dec 17, 2008
    #12
    Still something's fishy in all this...

    The behavior with double buffering confirms that setting needsDisplay of the OpenGLView is incomplete and causes some undesired side effects. Whether by design or a bug, it seems that only by telling the parent window of the OpenGLView to update will the full redraw of the view take place. Numerous programmers have noticed this problem, which is most apparent when trying to embed OpenGL into a Cocoa Gui app...
     
  13. Manderby macrumors 6502a

    Manderby

    Joined:
    Nov 23, 2006
    #13
    I have another solution

    I had the same problem as listed above. Any (NSOpenGLView or Custom) view is drawn once and then, it is not drawn again until the window containing it is resized or moved. It turned out, the double buffer was actually used but never swapped. In my case, all the solutions provided here did not succeeded. BUT! Now after approximately 20 hours of trial and error, search and despair, I found the solution. It's fairly simple. Add the following to your implementation if you have an NSOpenGLView subclass:

    Code:
    - (void) prepareOpenGL
    {
      GLint swapInt = 1;
      [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; // set to vbl sync
    }
    
    When using a custom NSView subclass just set this value right after the creation of the context. It looks like this swap interval is 0 by default since the new sdk.

    Hope this helps. Found this thread in the first place while searchich for a solution.
     
  14. CaptainFoxx macrumors newbie

    Joined:
    Sep 28, 2016
    #14
    This link seems to be about getting a function call every v sync and then triggering a render.
     
  15. teagls macrumors member

    Joined:
    May 16, 2013
    #15

    NSOpenGLView does not update on it's own and there is no setting to make it update automatically. The best way is to use a display link to sync with the refresh rate of the display.
     

Share This Page