PDA

View Full Version : OpenGL flickering on window resizing




BehindTimes
Feb 5, 2013, 01:19 AM
This is really bothering me. I have an OpenGL window subclassed from NSOpenGLView and rendering via the display link. If I resize the window via the zoom button, the view flickers like crazy until the resize is complete (typically a white window when I'm filling the screen with black). All other reshapes seem to work as expected except for the zoom button. (FYI, It is double buffered).

If I force a redraw of the frame in the reshape, everything appears smooth, but that kills the performance of trying to resize the window.

I also tried Apple's GLEssentials from the help docs, and saw this problem in there too.

*EDIT*

OK, resizing the window normally also has flickers too.



chrono1081
Feb 5, 2013, 12:20 PM
This is really bothering me. I have an OpenGL window subclassed from NSOpenGLView and rendering via the display link. If I resize the window via the zoom button, the view flickers like crazy until the resize is complete (typically a white window when I'm filling the screen with black). All other reshapes seem to work as expected except for the zoom button. (FYI, It is double buffered).

If I force a redraw of the frame in the reshape, everything appears smooth, but that kills the performance of trying to resize the window.

I also tried Apple's GLEssentials from the help docs, and saw this problem in there too.

*EDIT*

OK, resizing the window normally also has flickers too.

My OpenGL skills are a bit rusty so this may not be entirely correct but I believe there is a flag somewhere to set when drawing the OpenGL window to enable double buffering which will prevent the flickering.

I hope someone with more knowledge can answer you but this may be what you want to look in to.

EDIT: Oops I noticed you said this happens when resizing. My suggestion may not be of much help.

BehindTimes
Feb 5, 2013, 02:14 PM
My OpenGL skills are a bit rusty so this may not be entirely correct but I believe there is a flag somewhere to set when drawing the OpenGL window to enable double buffering which will prevent the flickering.

I hope someone with more knowledge can answer you but this may be what you want to look in to.

EDIT: Oops I noticed you said this happens when resizing. My suggestion may not be of much help.

Everything was working well when I was just using a timer. It was mainly due to switching to the core video display link as suggested by Apple's guidelines when reshaping the window became an issue. I've tried even replacing NSOpenGLView with NSView and still getting the flickering. It seems that during the reshape method, the callback is not exactly being hit. I wasn't able to capture that in the video, but there were often times when I grabbed the window and resized that I could go several seconds without the buffer being filled.

https://www.youtube.com/watch?v=IK06sbIAGIQ

2 mins displaying the issue, and the last 30 seconds using Apple's own example which demonstrates the same issue.

Starfox
Feb 6, 2013, 08:38 PM
I'm having the same issue with a double-buffered context. Weird.

Sayer
Feb 7, 2013, 01:37 PM
Are you drawing on the main thread? The sample code GLEssentials shows how to do things the *right* way and it was resizing smoothly for me.

No one can see what you are actually doing without you posting some code you are running and seeing the flickering.

BehindTimes
Feb 7, 2013, 09:35 PM
Are you drawing on the main thread? The sample code GLEssentials shows how to do things the *right* way and it was resizing smoothly for me.

No one can see what you are actually doing without you posting some code you are running and seeing the flickering.

No, I am not drawing to the main thread. Rendering on the main thread, everything works fine. It's when rendering on the separate thread things blow up. GLEssentials also has this problem, as I've stated up above. The CVDisplayLinkRef callback is not being hit during a window resize.

But, here's some simple code which is failing (minus functions which have nothing to do with the rendering):


- (NSOpenGLPixelFormat *) createPixelFormat
{
NSOpenGLPixelFormatAttribute attr[] =
{
//NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFAAccelerated,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 16,
0
};

return [(NSOpenGLPixelFormat *)[NSOpenGLPixelFormat alloc]
initWithAttributes:attr];
}

- (id)initWithFrame:(NSRect)frame
{
NSOpenGLPixelFormat *pixelFormat;
pixelFormat = [self createPixelFormat];
self = [super initWithFrame:frame pixelFormat:pixelFormat];
if (self)
{
m_bRedraw = false;
}

return self;
}

- (BOOL) isOpaque
{
return YES;
}

- (BOOL) isFlipped
{
return YES;
}

CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
CVReturn result = [(__bridge OpenGLHelper*)displayLinkContext getFrameForTime:outputTime];
return result;
}

- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
{
@autoreleasepool
{
if ([self lockFocusIfCanDraw])
{
NSRect curFrame = [self frame];
[self drawFrame: curFrame];
[self unlockFocus];
}
}
return kCVReturnSuccess;
}

- (void) prepareOpenGL
{
[super prepareOpenGL];

// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;

[[self openGLContext] makeCurrentContext];

[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];

CGLLockContext([[self openGLContext] CGLContextObj]);

glEnable( GL_TEXTURE_2D );
glShadeModel( GL_SMOOTH );
glEnable(GL_DEPTH_TEST);
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

CGLUnlockContext([[self openGLContext] CGLContextObj]);

// Create a display link capable of being used with all active displays
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);

// Set the renderer output callback function
CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void *)(self));

// Set the display link for the current renderer
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, cglContext, cglPixelFormat);

// Activate the display link
CVDisplayLinkStart(displayLink);

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillTerminate:)
name:NSApplicationWillTerminateNotification
object:nil];
}

- (void) SetResizeRedraw: (bool) redraw
{
m_bRedraw = redraw;
}

- (void) reshape
{
NSRect rect;
//[super reshape];
CGLLockContext([[self openGLContext] CGLContextObj]);

[[self openGLContext] makeCurrentContext];
[[self openGLContext] update];
rect = [self bounds];
glViewport(0, 0, rect.size.width, rect.size.height);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, rect.size.width, rect.size.height, 0, 0, 100.0f);

CGLUnlockContext([[self openGLContext] CGLContextObj]);

//if (!CVDisplayLinkIsRunning(displayLink))
// Will work if this is uncommented, but kills performance
/*if(m_bRedraw)
{
[self drawFrame:[self frame]];
}*/
}

- (void)drawRect:(NSRect)dirtyRect
{
if (!CVDisplayLinkIsRunning(displayLink))
{
[self drawFrame:dirtyRect];
}
}

- (void) drawFrame:(NSRect)dirtyRect
{
CGLLockContext([[self openGLContext] CGLContextObj]);

[[self openGLContext] makeCurrentContext];

glClearColor(0, 0, 0, 255);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

CGLFlushDrawable([[self openGLContext] CGLContextObj]);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}

axxxum
Feb 8, 2013, 03:27 AM
From the back of my head since it's been a while I used OpenGL, but in your code I see you explicitly call the frame-update yourself; the proper way however is to let the OpenGL-threading mechanism handle that, i.e. whenever you want the frame updated, put in code '[self setNeedsDisplay:YES];' (self being the instance of the class derived from a View-superclass).

This will indicate to graphics-thread scheduler that your display needs updating (duh!) and whenever the time is ready, it will do so, without flickering.

The ADC-docs I'm sure, contain a more precise and clear formulation of this :)

axxxum
Feb 8, 2013, 04:14 AM
From the back of my head since it's been a while I used OpenGL, but in your code I see you explicitly call the frame-update yourself; the proper way however is to let the OpenGL-threading mechanism handle that, i.e. whenever you want the frame updated, put in code '[self setNeedsDisplay:YES];' (self being the instance of the class derived from a View-superclass).

This will indicate to graphics-thread scheduler that your display needs updating (duh!) and whenever the time is ready, it will do so, without flickering.

The ADC-docs I'm sure, contain a more precise and clear formulation of this :)

Nevermind, just looked into the GLEssentials sample-code, it's not yer vanilla-OpenGL stuff, as I assumed above.

Hem Dutt
Feb 8, 2013, 05:39 AM
Hi,

I tried following code in my reshape function :

-(void)reshape
{
NSOpenGLContext *context = [self openGLContext];
[context makeCurrentContext];

// must lock GL context because display link is threaded
CGLLockContext([context CGLContextObj]);


//glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);

[context flushBuffer];

CGLUnlockContext([context CGLContextObj]);
}

The code worked and flickering stopped without much overhead.

You must be calling another subroutine in your drawFrame method for your animation (assuming this because I did not found any subroutine for animation in the code) which you have not specified in the forum and the overhead must be because of that subroutine when you are calling drawFrame from within reshape method.

BehindTimes
Feb 8, 2013, 07:03 AM
Hi,

I tried following code in my reshape function :

-(void)reshape
{
NSOpenGLContext *context = [self openGLContext];
[context makeCurrentContext];

// must lock GL context because display link is threaded
CGLLockContext([context CGLContextObj]);


//glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);

[context flushBuffer];

CGLUnlockContext([context CGLContextObj]);
}

The code worked and flickering stopped without much overhead.

You must be calling another subroutine in your drawFrame method for your animation (assuming this because I did not found any subroutine for animation in the code) which you have not specified in the forum and the overhead must be because of that subroutine when you are calling drawFrame from within reshape method.

It will contain a bit more in the future, but I'm trying to resolve the simple issues first before the program becomes too massive. Embed the OpenGL view in a corner of a window containing a horizontal NSSplitView and a vertical NSSplitView. Try moving the bars now, and you should see slow down. In the video I posted, I just threw together a very simple animation (we're talking 2 textured polygons moving across a plane), and moving the bar dropped the frame rate to under 10 FPS.