NSOpenGLView and CVDisplayLink, no default frame buffer - cocoa

I have an NSOpenGLView and OpenGL code that works with an NSTimer running in the main loop (calling setNeedsDisplay and drawRect). I would like to use a CVDisplayLink, so I can get a better frame-rate without overdriving the timer. I copied most of the code from apple's OSXGLEssentials example. The display link starts and the callback runs, but nothing is actually draw on screen. glGetError returns GL_INVALID_FRAMEBUFFER_OPERATION.
glCheckFramebufferStatus returns GL_FRAMEBUFFER_UNDEFINED for GL_FRAMEBUFFER, GL_DRAW_FRAMEBUFFER and GL_READ_FRAMEBUFFER.
Info from the documentation:
GL_FRAMEBUFFER_UNDEFINED is returned if target is the default
framebuffer, but the default framebuffer does not exist.
Here are the relevant bits of code:
- (void)awakeFromNib {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, // Core Profile !
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAAllowOfflineRenderers,
0
};
NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:format shareContext: nil];
// [context setView: self];
[self setPixelFormat: format];
[self setOpenGLContext: context];
}
- (void)prepareOpenGL {
[super prepareOpenGL];
NSOpenGLContext* context = [self openGLContext];
[context makeCurrentContext];
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;
[context setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
MyDisplay_setup();
MyDisplay_initScene(_bounds.size.width, _bounds.size.height);
CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
CVDisplayLinkSetOutputCallback(displayLink, &displayLinkCallback, (__bridge void *)self);
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(displayLink, [context CGLContextObj], cglPixelFormat);
CVDisplayLinkStart(displayLink);
}
static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime,
CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) {
#autoreleasepool {
[(__bridge MyView*)displayLinkContext redraw];
}
return kCVReturnSuccess;
}
- (void)redraw {
NSOpenGLContext* context = [self openGLContext];
[context makeCurrentContext];
CGLLockContext([context CGLContextObj]);
MyDisplay_drawScene();
CGLFlushDrawable([context CGLContextObj]);
CGLUnlockContext([context CGLContextObj]);
}

This is an old question, but this problem still persists, so here's my answer. For reference, i don't use Xcode at all, i write code in Vim and compile with Clang, so this is the default behaviour, and nothing to do with the IB. I use only the NSOpenGLView, NSOpenGLContext, and CGDisplayLink for rendering. I have a MacBook Pro (Retina, 15-inch, Mid 2014) running macOS Sierra.
While debugging i found that NSOpenGLContext's view property returned nil for the first few frames after starting the display link. This was enough to corrupt the context if you did any rendering (other than glClear) while the view wasn't attached, and caused the same GL_FRAMEBUFFER_UNDEFINED error.
The easiest way to solve this, i found, was to assign the NSOpenGLView to its NSOpenGLContext after creation like this:
NSOpenGLView *view = ...;
view.openglContext.view = view;
I'm baffled that, apparently, it's necessary to do this even though the NSOpenGLContext is created by the NSOpenGLView, but there it is.

The trick is to open the View Effects inspector and uncheck the parent View in the Core Animation Layer section.

Related

CIFilter (gausianBlurr) with Animator Creates Memory Leaks

I ma trying to create a simple slide menu.
I created an NSView and Applied Gaussian Blurr to it's CALayer
//Menu initWithFrame method.
m_blur = [CIFilter filterWithName:#"CIGaussianBlur"];
[m_blur setDefaults];
[m_blur setValue: [NSNumber numberWithFloat:7] forKey:#"inputRadius"];
m_backgroundFiltersArray = [NSArray arrayWithObject:m_blur];
- (void)setVisible:(BOOL)bVisible
{
#autoreleasepool
{
if (!bVisible)
{
[self.layer setBackgroundFilters:nil];
[self.layer.superlayer setBackgroundFilters:nil];
}
else
{
[self.layer setBackgroundFilters:m_backgroundFiltersArray];
//Also Update the Driver's Parameters here.
}
}
[self setNeedsDisplay:YES];
}
In my App main View I am calling the menu like this
In init with Frame
m_menuView = [[MenuView alloc]initWithFrame:NSMakeRect(-fcMenuWidth, 0, fcMenuWidth, 446.0)];
[m_menuView setDelegate:self];
[self addSubview:m_menuView];
- (void)summonMenu
{
if(!m_bMenuUp)
{
m_bMenuUp = YES;
[m_menuView setVisible:YES];
[[m_menuView animator] setFrame:NSMakeRect(0, 0, fcMenuWidth, self.frame.size.height)];
}
}
- (void)hideMenu
{
if (m_bMenuUp)
{
m_bMenuUp = NO;
[m_menuView cancelPopUpButtons];
[[m_menuView animator] setFrame:NSMakeRect(-fcMenuWidth, 0, fcMenuWidth, self.frame.size.height)];
[m_menuView setVisible:NO];
}
}
However Each time I hide or summon the Menu
I have some little memory leak.
I tried to profile it in Instruments -- it tells about OpenGL context
That has about 768bytes leak each time.
I played around with functions
And discovered that when I am calling m_menuView setFrame: without the .animator property it all works fine and no leaks occurs.
Also if I disable CIFilter and use animator it works fine.
Can someone enlighten me how can I work this round please?

EAGLView transparency frames per second

I have a Cocos2d project and I want a constant background throughout the app. In the applicationDidFinishLaunching method of its delegate, I have replaced the line:
[viewController setView:glView];
with
[[viewController view] addSubview:glView];
because I have added subviews to the RootViewController's view in it's initWithNib, and those changes are lost if the view is replaced with glView.
I have also changed the pixelFormat of glView from kEAGLColorFormatRGB565 to kEAGLColorFormatRGBA8. When I make that change, glView becomes transparent and I can see through it, but the fps drops dramatically. If I don't make that change, the view doesn't become transparent, but I don't see the huge drop in fps. I'm talking about a significant drop in fps, from 59.0-60.0 to about 35.0-42.0.
I am using this code right below the addSubview line above to make the view transparent:
glClearColor(0, 0, 0, 0);
director.openGLView.backgroundColor = [UIColor clearColor];
director.openGLView.opaque = NO;
The last two lines are the culprits; commenting them out (both, not just one) causes the large drop in fps, while commenting out the glClearColor line has no effect on fps.
The whole applicationDidFinishLaunching method looks like this:
- (void) applicationDidFinishLaunching:(UIApplication*)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if(![CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
CCDirector *director = [CCDirector sharedDirector];
// Init the View Controller
viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
viewController.wantsFullScreenLayout = YES;
// Create the EAGLView manually
// 1. Create a RGB565 format. Alternative: RGBA8
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
EAGLView *glView = [EAGLView viewWithFrame:[window bounds]
pixelFormat:kEAGLColorFormatRGBA8
depthFormat:0
];
// attach the openglView to the director
[director setOpenGLView:glView];
if(![director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
#if GAME_AUTOROTATION == kGameAutorotationUIViewController
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
#else
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
#endif
[director setAnimationInterval:1.0/60];
[director setDisplayFPS:YES];
// make the OpenGLView a child of the view controller
[[viewController view] addSubview:glView];
//***make glView transparent***
glClearColor(0, 0, 0, 0);
director.openGLView.backgroundColor = [UIColor clearColor];
director.openGLView.opaque = NO;
// make the View Controller a child of the main window
[window addSubview:viewController.view];
[window makeKeyAndVisible];
// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
// You can change anytime.
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
// Removes the startup flicker
[self removeStartupFlicker];
// Run the intro Scene
[[CCDirector sharedDirector] runWithScene:[MainMenu scene]];
}
Any ideas as to why this is happening? I can provide more code if need be.
If you're testing this on a 1st or 2nd generation device, the drop in framerate is to be expected. Nothing you can do about it. These devices are heavily fillrate-limited, and a transparent 32-bit GL view is just asking too much of the device.
If this happens on a 3rd or even 4th generation device, then there's got to be something wrong but I couldn't begin to tell what that might be.
If you're testing the performance on the Simulator, don't. It's irrelevant.

Apply a CIFilter background filter when host window is transparent

I want to replicate the background of dock Stacks in grid and list mode. The background is translucent black with a blur effect:
Example of dock stack in grid mode http://www.thecustommac.com/wp-content/uploads/2009/09/stack-highlight.jpg
The problem is that [CALayer backgroundFilters] only applies to content in the window, the filters are not applied to content in other windows. Here's my code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//make window transparent
self.window.backgroundColor = [NSColor clearColor];
[self.window setOpaque:NO];
[self.window setHasShadow:NO];
[self.window setStyleMask:NSBorderlessWindowMask];
//make the content view layer hosting
CALayer *rootLayer = [CALayer layer];
[[self.window contentView] setLayer:rootLayer];
[[self.window contentView] setWantsLayer:YES];
//blur the background contents - NOT WORKING!
[rootLayer setBackgroundColor:CGColorCreateGenericGray(0.0, .716)];
CIFilter *blurFilter = [CIFilter filterWithName:#"CIGaussianBlur"];
[blurFilter setDefaults];
[rootLayer setBackgroundFilters:[NSArray arrayWithObject: blurFilter]];
}
I can't think of how else to achieve this effect. (I've taken a look at the Display Services to see if there are any useful functions but I can't see any.)
Any ideas?
There is private API available this. Here is sample code by Rob Keniger:
In 10.5 you can add any core image filter to a window using the
private function 'CGSAddWindowFilter'.
typedef void * CGSConnectionID;
extern OSStatus CGSNewConnection(const void **attr, CGSConnectionID *id);
- (void)enableBlurForWindow:(NSWindow *)window
{
CGSConnectionID _myConnection;
uint32_t __compositingFilter;
int __compositingType = 1; // Apply filter to contents underneath the window, then draw window normally on top
/* Make a new connection to CoreGraphics, alternatively you could use the main connection*/
CGSNewConnection(NULL , &_myConnection);
/* The following creates a new CoreImage filter, then sets its options with a dictionary of values*/
CGSNewCIFilterByName (_myConnection, (CFStringRef)#"CIGaussianBlur", &__compositingFilter);
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:3.0] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(_myConnection, __compositingFilter, (CFDictionaryRef)optionsDict);
/* Now just switch on the filter for the window */
CGSAddWindowFilter(_myConnection, [window windowNumber], __compositingFilter, __compositingType );
}
The filters are not being used by the root layer:
From the docs:
/* An array of filters that are applied to the background of the layer.
* The root layer ignores this property. Animatable. */
#property(copy) NSArray *backgroundFilters;
Your code has some error, here is the code that just works:
typedef void * CGSConnection;
extern OSStatus CGSNewConnection(const void **attributes, CGSConnection * id);
-(void)enableBlurForWindow:(NSWindow *)window {
CGSConnection thisConnection;
NSUInteger compositingFilter;
NSInteger compositingType = 1 << 0;
// Make a new connection to Core Graphics
CGSNewConnection(NULL, &thisConnection);
// Create a Core Image filter and set it up
CGSNewCIFilterByName(thisConnection, (CFStringRef)#"CIGaussianBlur", &compositingFilter);
NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:2] forKey:#"inputRadius"];
CGSSetCIFilterValuesFromDictionary(thisConnection, compositingFilter, (__bridge CFDictionaryRef)options);
// Apply the filter to the window
CGSAddWindowFilter(thisConnection, [window windowNumber], compositingFilter, compositingType);
}
Then, use it:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[_window setOpaque:NO];
[_window setBackgroundColor: [NSColor colorWithCalibratedHue:0.568 saturation:0.388 brightness:0.941 alpha:0.6]];
[self enableBlurForWindow:_window];
}
https://github.com/dsns/GaussianBlur_NSWindow_os_x_mavericks
its looks like crutch code for mavericks app
but it working

NSOpenGLView drawRect does not get called

I am currently trying to create a simple cocoa NSWindow programmatically instead of using Interface builder (I have got my reasons to do so). this is a quick test:
int main(int argc, char** argv){
NSWindow *mainwin;
CocoaGLView *mainview;
NSRect scr_frame;
unsigned int style_mask;
NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
[NSApplication sharedApplication];
scr_frame= NSMakeRect(100, 100, 400, 400);
style_mask=NSClosableWindowMask|NSMiniaturizableWindowMask|
NSResizableWindowMask|NSTitledWindowMask;
scr_frame=[NSWindow contentRectForFrameRect:scr_frame
styleMask:style_mask];
mainwin=[[NSWindow alloc]
initWithContentRect:scr_frame
styleMask:style_mask
backing:NSBackingStoreBuffered
defer:NO];
[mainwin makeKeyAndOrderFront:nil];
[mainwin setTitle:#"Visible screen window"];
mainview=[[CocoaGLView alloc] initWithFrame:scr_frame];
[mainwin setContentView:mainview];
[mainview display];
[mainwin setReleasedWhenClosed:YES];
[pool drain];
[NSApp run];
return 0;
}
CocoaGLView is derived from NSOpenGLView and looks like this:
#interface CocoaGLView : NSOpenGLView {
//some stuff
}
- (id) initWithFrame: (NSRect) frameRect;
- (void)setFrameSize:(NSSize) aSize;
- (void)drawRect:(NSRect) aRect;
#end
it generally works. I can see the window. I can even see the openGL things I draw inside CocoaGLViews drawRect function, but that function unfortunatelly only gets called once, what am I missing?
Why would you expect it to be called more than once? A view is asked to draw when the OS thinks that its content is no longer valid. If you want the OpenGL view to be drawn periodically, then you need to set up a timer that sends setNeedsDisplay: messages to your view.

Changing NSApplicationIcon across a running application?

I'd like to adjust the NSApplicationIcon image that gets shown automatically in all alerts to be something different than what is in the app bundle.
I know that it's possible to set the dock icon with [NSApplication setApplicationIconImage:] -- but this only affects the dock, and nothing else.
I'm able to work around this issue some of the time: I have an NSAlert *, I can call setIcon: to display my alternate image.
Unfortunately, I have a lot of nibs that have NSImageView's with NSApplicationIcon, that I would like to affect, and it would be a hassle to create outlets and put in code to change the icon. And for any alerts that I'm bringing up with the BeginAlert... type calls (which don't give an NSAlert object to muck with), I'm completely out of luck.
Can anybody think of a reasonable way to globally (for the life of a running application) override the NSApplicationIcon that is used by AppKit, with my own image, so that I can get 100% of the alerts replaced (and make my code simpler)?
Swizzle the [NSImage imageNamed:] method? This method works at least on Snow Leopard, YMMV.
In an NSImage category:
#implementation NSImage (Magic)
+ (void)load {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// have to call imageNamed: once prior to swizzling to avoid infinite loop
[[NSApplication sharedApplication] applicationIconImage];
// swizzle!
NSError *error = nil;
if (![NSImage jr_swizzleClassMethod:#selector(imageNamed:) withClassMethod:#selector(_sensible_imageNamed:) error:&error])
NSLog(#"couldn't swizzle imageNamed: application icons will not update: %#", error);
[pool release];
}
+ (id)_sensible_imageNamed:(NSString *)name {
if ([name isEqualToString:#"NSApplicationIcon"])
return [[NSApplication sharedApplication] applicationIconImage];
return [self _sensible_imageNamed:name];
}
#end
With this hacked up (untested, just wrote it) jr_swizzleClassMethod:... implementation:
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
#if OBJC_API_VERSION >= 2
Method origMethod = class_getClassMethod(self, origSel_);
if (!origMethod) {
SetNSError(error_, #"original method %# not found for class %#", NSStringFromSelector(origSel_), [self className]);
return NO;
}
Method altMethod = class_getClassMethod(self, altSel_);
if (!altMethod) {
SetNSError(error_, #"alternate method %# not found for class %#", NSStringFromSelector(altSel_), [self className]);
return NO;
}
id metaClass = objc_getMetaClass(class_getName(self));
class_addMethod(metaClass,
origSel_,
class_getMethodImplementation(metaClass, origSel_),
method_getTypeEncoding(origMethod));
class_addMethod(metaClass,
altSel_,
class_getMethodImplementation(metaClass, altSel_),
method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getClassMethod(self, origSel_), class_getClassMethod(self, altSel_));
return YES;
#else
assert(0);
return NO;
#endif
}
Then, this method to illustrate the point:
- (void)doMagic:(id)sender {
static int i = 0;
i = (i+1) % 2;
if (i)
[[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameBonjour]];
else
[[NSApplication sharedApplication] setApplicationIconImage:[NSImage imageNamed:NSImageNameDotMac]];
// any pre-populated image views have to be set to nil first, otherwise their icon won't change
// [imageView setImage:nil];
// [imageView setImage:[NSImage imageNamed:NSImageNameApplicationIcon]];
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:#"Shazam!"];
[alert runModal];
}
A couple of caveats:
Any image view already created must have setImage: called twice, as seen above to register the image changing. Don't know why.
There may be a better way to force the initial imageNamed: call with #"NSApplicationIcon" than how I've done it.
Try [myImage setName:#"NSApplicationIcon"] (after setting it as the application icon image in NSApp).
Note: On 10.6 and later, you can and should use NSImageNameApplicationIcon instead of the string literal #"NSApplicationIcon".

Resources