Migrating OpenGL app from Swift 2.x to Swift 3

Hi,


I've a venerable app that I've coerced from the Apple example code GLEssentials in obj-c to Swift 2 and now I'm trying to move to Swift 3. It has a NSOpenGLView at it's core, the main renderer is still not in Swift (I know: I'll have to bite the bullet soon and migrate to Metal or suchlike), but everything else is.


Currently in OSX 10.12, with Xcode 8 the window is just blank. I've had to make some compiler-suggested changes to the main (Swift) OpenGLView class to get it to compile and run succesfully in Swift 3, and I feel this is where the problem lies as the renderer class appears to be running just fine; I can get it to report an FPS of 60 and am able to place breakpoints that do halt the code every frame. My OpenGLView class sets up as below, and this is where most of the new Swift 3 changes have taken place:


class BQSOpenGLView: NSOpenGLView {
    var m_renderer: OpenGLRenderer = OpenGLRenderer()
    var have_setup_renderer: Bool = false
    var displayLink: CVDisplayLink?
    var has_setup = false
    var is_fullscreen: Bool = false
    var grid_size: QSGridSize = QSMakeGridSize(1, 1)
    var backing_scalar_changed: Bool = false
    var backing_scalar: GLfloat = 0.0

    override func awakeFromNib() {
        backing_scalar = GLfloat((self.window?.backingScaleFactor)!)
        NotificationCenter.default.addObserver(self, selector: #selector(BQSOpenGLView.willEnterFullscreen(_:)), name: NSNotification.Name.NSWindowWillEnterFullScreen, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(BQSOpenGLView.willExitFullscreen(_:)), name: NSNotification.Name.NSWindowWillExitFullScreen, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(BQSOpenGLView.backingScaleChange(_:)), name: NSNotification.Name.NSWindowDidChangeBackingProperties, object: nil)
        let attributes : [NSOpenGLPixelFormatAttribute] = [
            NSOpenGLPixelFormatAttribute(NSOpenGLPFADoubleBuffer),
            NSOpenGLPixelFormatAttribute(NSOpenGLPFADepthSize),
            NSOpenGLPixelFormatAttribute(24),
            NSOpenGLPixelFormatAttribute(NSOpenGLPFASampleBuffers),
            NSOpenGLPixelFormatAttribute(1),
            NSOpenGLPixelFormatAttribute(NSOpenGLPFASamples),
            NSOpenGLPixelFormatAttribute(2),
            NSOpenGLPixelFormatAttribute(NSOpenGLPFAOpenGLProfile),
            NSOpenGLPixelFormatAttribute(NSOpenGLProfileVersion3_2Core),
            NSOpenGLPixelFormatAttribute(0)
        ]
        self.pixelFormat = NSOpenGLPixelFormat(attributes: attributes)
        self.wantsBestResolutionOpenGLSurface = true
        let context:NSOpenGLContext = NSOpenGLContext.init(format: self.pixelFormat!, share: nil)!
        CGLEnable(context.cglContextObj!, kCGLCECrashOnRemovedFunctions)
        self.openGLContext = context
    }

    override func prepareOpenGL() {
    super.prepareOpenGL()

        //  The callback function is called everytime CVDisplayLink says its time to get a new frame.
        func displayLinkOutputCallback(_ displayLink: CVDisplayLink, _ inNow: UnsafePointer<CVTimeStamp>, _ inOutputTime: UnsafePointer<CVTimeStamp>, _ flagsIn: CVOptionFlags, _ flagsOut: UnsafeMutablePointer<CVOptionFlags>, _ displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn {
            unsafeBitCast(displayLinkContext, to: BQSOpenGLView.self).renderFrame()
            return kCVReturnSuccess
        }
        self.wantsLayer = true
        self.wantsBestResolutionOpenGLSurface = true
        CVDisplayLinkCreateWithActiveCGDisplays(&displayLink)
        let success = CVDisplayLinkSetOutputCallback(displayLink!, displayLinkOutputCallback, Unmanaged.passUnretained(self).toOpaque())
        if success != kCVReturnSuccess {
            Swift.print ("No opengl")
        }
        CVDisplayLinkStart(displayLink!)
        has_setup = true
        NotificationCenter.default.post(name: Notification.Name(rawValue: kOPENGL_SETUP_NOTE), object: nil)
        self.backingScaleChange(nil)
    }
//..
}


Is there a new Swift 3 way of setting up OpenGL or is the above code correct and the problem somewhere else?


EDIT: the render function in the view sub-class is:


func renderFrame() { 
     self.openGLContext?.makeCurrentContext()
     CGLLockContext((self.openGLContext?.cglContextObj)!)
     if !have_setup_renderer { //init only when locked all the context
          self.backingScaleChange(nil)
          m_renderer = OpenGLRenderer.init(defaultFBO: 0, screenSize: (self.window?.screen?.frame.size)!, backingScalar: self.backing_scalar)
          have_setup_renderer = true
     }
     m_renderer.render()
     CGLFlushDrawable((self.openGLContext?.cglContextObj)!)
     CGLUnlockContext((self.openGLContext?.cglContextObj)!)
}

Replies

I've got OpenGL working in Swift 3 with version 2 and 3 render context. So, it is possible.


I'm not using NSOpenGLView but I set my views layer class to CAEAGLLayer.


Feel the pain going to Metal. Its zillions of shaders too. lol.


I initially had a blank OpenGL issue when going to Swift 3 / Xcode 8. But it was caused when I accidently set preferredFramesPerSecond incorrectly because frameInterval on the display link was depricated. Not sure that is your problem, though. Here is something similiar to what I'm doing:



displayLink = UIScreen.main.displayLink(withTarget: self, selector:#selector(BQSOpenGLView.renderFrame))
displayLink!.isPaused = false
//displayLink!.frameInterval = 1  <= depricated !
displayLink!.preferredFramesPerSecond = 60 //new probably default value
displayLink!.add(to: RunLoop.main, forMode:RunLoopMode.commonModes)


For kicks, if you set the clear color to blue and clear the buffer does the screen color change? If it changes to blue, I suppose that would imply the context is being set correct. Might be a place to start.


self.openGLContext has a flush too? What if you use that instead?


Hope that helps.

Thanks macNib,


My fault: I should have been specific about it being an OSX app. Unfortunately, there aren't OSX equivalents of preferredFramesPerSecond or CAEAGLLayer's. But good to know it isn't a Swift 3 issue!

It is possible to use OpenGL with Swift 3. I have a working example [here](https://github.com/00buggy00/SwiftOpenGLTutorials). I've looked over your code quickly and I don't see anything in particular that stands out, but you may find why you're not getting your expected renders when you compare what you've got with some working code.