Making OpenGL coexist with Core Animation on macOS

人盡茶涼 提交于 2019-12-21 23:25:04

问题


Is it possible for a layer-backed NSView to correctly do OpenGL rendering in drawRect:, or is it necessary to use NSOpenGLLayer/CAOpenGLLayer? Until recently, I haven't needed to worry about that, I just used a non-layer-backed view. But as stated in the AppKit release notes for 10.14:

Windows in apps linked against the macOS 10.14 SDK are displayed using Core Animation when the app is running in macOS 10.14.

To show how this breaks my rendering, I put together a sample app, with code shown below. If the project is run on an OS before Mojave, or is built using an SDK before 10.14, then the view shows solid red that stays that way if the window is resized. But if the project is build using the 10.14 SDK and run under macOS 10.14, then as the window is resized, the view flickers madly, and when you stop resizing, the view may or may not show anything.

Here's the project folder. Also, here is the app, built under High Sierra but with two instances of the view, one marked layer-backed in the nib.

-- AppDelegate.h --

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (strong) NSOpenGLPixelFormat* pixelFormat;

@end

-- AppDelegate.m --

#import "AppDelegate.h"

@interface AppDelegate ()

@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate

- (instancetype) init
{
    self = [super init];
    if (self != nil)
    {
        NSOpenGLPixelFormatAttribute    attribs[] =
        {
            NSOpenGLPFADoubleBuffer,
            NSOpenGLPFAAccelerated,
            NSOpenGLPFAColorSize,
            32,
            0
        };
        _pixelFormat = [[NSOpenGLPixelFormat alloc]
            initWithAttributes: attribs];
        NSLog(@"Got pixel format %@", _pixelFormat);
    }
    return self;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [self.window makeKeyAndOrderFront: self];
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
}

@end

-- MyGLView.h --

#import <Cocoa/Cocoa.h>

@class AppDelegate;

@interface MyGLView : NSView

@property (weak) IBOutlet AppDelegate*  appDelegate;

@end

-- MyGLView.m --

#import "MyGLView.h"
#import "AppDelegate.h"
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>

@interface MyGLView ()

@property (strong) NSOpenGLContext* openGLContext;

@end

@implementation MyGLView

- (void) awakeFromNib
{
    self.openGLContext = [[NSOpenGLContext alloc]
        initWithFormat: self.appDelegate.pixelFormat shareContext: nil];
    self.openGLContext.view = self;

    [[NSNotificationCenter defaultCenter]
        addObserver: self
        selector: @selector(resized:)
        name: NSViewFrameDidChangeNotification
        object: self];
}

- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver: self];
}

- (void) resized: (NSNotification*) note
{
    [self.openGLContext update];
}

- (void) drawRect:(NSRect) dirtyRect
{
    [self.openGLContext makeCurrentContext];

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

    [self.openGLContext flushBuffer];
}

@end

Update:

It's looking as if what I wanted to do can't be done. So my choices are to use NSOpenGLView, which uses a private class _NSOpenGLViewBackingLayer for its layer, or use NSOpenGLLayer.

I find NSOpenGLLayer confusing because although it seems to be rendering on screen, it's actually rendering to an FBO. And even if you start with a pixel format that supports double-buffering, you're really doing single-buffered rendering. In contrast, NSOpenGLView appears to render to the main frame buffer, double-buffered if you like.

来源:https://stackoverflow.com/questions/54485810/making-opengl-coexist-with-core-animation-on-macos

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!