Trying to figure out why the View Controller using a MTKView and a Metal-backed CIContext runs at 1/3 of the speed of an GLKView + OpenGLES2-backed CIContext.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// MetalCoreImage | |
// | |
// Created by Joshua Sullivan on 4/21/16. | |
// Copyright © 2016 The Nerdery. All rights reserved. | |
// | |
import UIKit | |
import MetalKit | |
import CoreImage | |
class ViewController: UIViewController { | |
private struct FrameRate: CustomStringConvertible { | |
let single: Double | |
let average: Double | |
var description: String { | |
let formattedString = NSString(format: "single: %0.1f, average: %0.1f", single, average) | |
return "FrameRate(\(formattedString))" | |
} | |
} | |
private var renderRect: CGRect = CGRect.zero | |
private var lastTime: CFTimeInterval = 0.0 | |
private var timeAccumulator: CFTimeInterval = 0.0 | |
private var framesRendered: Double = 0.0 | |
private var rotation: CGFloat = 0.0 | |
private var metalDevice: MTLDevice! | |
private var context: CIContext! | |
private var blurFilter: CIFilter! | |
private var transformFilter: CIFilter! | |
private var commandQueue: MTLCommandQueue! | |
private var colorSpace = CGColorSpaceCreateDeviceRGB()! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
guard let device = MTLCreateSystemDefaultDevice() else { | |
debugPrint("Unable to create metal device. Aborting.") | |
self.view = UIView(frame: view.frame) | |
return | |
} | |
metalDevice = device | |
commandQueue = device.newCommandQueue() | |
let mView = self.view as! MTKView | |
mView.device = device | |
mView.delegate = self | |
mView.framebufferOnly = false | |
context = CIContext(MTLDevice: device, options: [kCIContextWorkingColorSpace : NSNull()]) | |
let checkers = CIFilter(name: "CICheckerboardGenerator")! | |
blurFilter = CIFilter(name: "CIGaussianBlur", withInputParameters: [kCIInputImageKey : checkers.outputImage!, kCIInputRadiusKey : 40.0]) | |
transformFilter = CIFilter(name: "CIAffineTransform", withInputParameters: ["inputImage" : blurFilter.outputImage!]) | |
// filter = checkers | |
} | |
private func calculateFrameRate() -> FrameRate { | |
let now = CACurrentMediaTime() | |
guard lastTime > 0.0 else { | |
lastTime = now | |
return FrameRate(single: 0.0, average: 0.0) | |
} | |
let dt = now - lastTime | |
timeAccumulator += dt | |
framesRendered += 1.0 | |
let averageTime = timeAccumulator / framesRendered | |
let averageFrameRate = 1.0 / averageTime | |
lastTime = now | |
return FrameRate(single: 1.0 / dt, average: averageFrameRate) | |
} | |
} | |
extension ViewController: MTKViewDelegate { | |
func drawInMTKView(view: MTKView) { | |
guard let currentDrawable = view.currentDrawable else { | |
debugPrint("No currentDrawable.") | |
return | |
} | |
rotation += CGFloat(M_PI / 480.0) | |
let blur = sin(rotation * 4.0) * 20.0 + 20.0 | |
blurFilter.setValue(blur, forKey: kCIInputRadiusKey) | |
let transform = CGAffineTransformMakeRotation(rotation) | |
let boxedTransform = NSValue(CGAffineTransform: transform) | |
transformFilter.setValue(boxedTransform, forKey: kCIInputTransformKey) | |
transformFilter.setValue(blurFilter.outputImage!, forKey: kCIInputImageKey) | |
let commandBuffer = commandQueue.commandBuffer() | |
let targetTexture = currentDrawable.texture | |
context.render(transformFilter.outputImage!, toMTLTexture: targetTexture, commandBuffer: commandBuffer, bounds: renderRect, colorSpace: colorSpace) | |
commandBuffer.presentDrawable(currentDrawable) | |
commandBuffer.commit() | |
let frameRate = calculateFrameRate() | |
debugPrint(frameRate) | |
} | |
func mtkView(view: MTKView, drawableSizeWillChange size: CGSize) { | |
renderRect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// OGLCoreImage | |
// | |
// Created by Joshua Sullivan on 4/21/16. | |
// Copyright © 2016 The Nerdery. All rights reserved. | |
// | |
import UIKit | |
import GLKit | |
import CoreImage | |
class ViewController: UIViewController { | |
private struct FrameRate: CustomStringConvertible { | |
let single: Double | |
let average: Double | |
var description: String { | |
let formattedString = NSString(format: "single: %0.1f, average: %0.1f", single, average) | |
return "FrameRate(\(formattedString))" | |
} | |
} | |
lazy private var glContext: EAGLContext = { | |
return EAGLContext(API: .OpenGLES2) | |
}() | |
lazy private var context: CIContext = { | |
[unowned self] in | |
return CIContext(EAGLContext: self.glContext, options: [kCIContextWorkingColorSpace : NSNull()]) | |
}() | |
private var lastTime: CFTimeInterval = 0.0 | |
private var timeAccumulator: CFTimeInterval = 0.0 | |
private var framesRendered: Double = 0.0 | |
private var rotation: CGFloat = 0.0 | |
private var transformFilter: CIFilter! | |
private var blurFilter: CIFilter! | |
private var displayLink: CADisplayLink! | |
private let scaleTransform: CGAffineTransform = { | |
let scale = UIScreen.mainScreen().nativeScale | |
return CGAffineTransformMakeScale(scale, scale) | |
}() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view, typically from a nib. | |
guard let glView = self.view as? GLKView else { | |
assertionFailure("view isn't a GLKView.") | |
return | |
} | |
glView.context = glContext | |
glView.delegate = self | |
let checkers = CIFilter(name: "CICheckerboardGenerator")! | |
blurFilter = CIFilter(name: "CIGaussianBlur", withInputParameters: [kCIInputImageKey : checkers.outputImage!, kCIInputRadiusKey : 40.0]) | |
transformFilter = CIFilter(name: "CIAffineTransform", withInputParameters: ["inputImage" : blurFilter.outputImage!]) | |
displayLink = CADisplayLink(target: self, selector: #selector(displayLinkTick(_:))) | |
displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: kCFRunLoopCommonModes as String) | |
} | |
@objc private func displayLinkTick(link: CADisplayLink) { | |
rotation += CGFloat(M_PI / 480.0) | |
let blur = sin(rotation * 4.0) * 20.0 + 20.0 | |
blurFilter.setValue(blur, forKey: kCIInputRadiusKey) | |
let transform = CGAffineTransformMakeRotation(rotation) | |
let boxedTransform = NSValue(CGAffineTransform: transform) | |
transformFilter.setValue(boxedTransform, forKey: kCIInputTransformKey) | |
transformFilter.setValue(blurFilter.outputImage!, forKey: kCIInputImageKey) | |
(self.view as! GLKView).display() | |
debugPrint("frame rate:", calculateFrameRate()) | |
} | |
private func calculateFrameRate() -> FrameRate { | |
let now = CACurrentMediaTime() | |
guard lastTime > 0.0 else { | |
lastTime = now | |
return FrameRate(single: 0.0, average: 0.0) | |
} | |
let dt = now - lastTime | |
timeAccumulator += dt | |
framesRendered += 1.0 | |
let averageTime = timeAccumulator / framesRendered | |
let averageFrameRate = 1.0 / averageTime | |
lastTime = now | |
return FrameRate(single: 1.0 / dt, average: averageFrameRate) | |
} | |
} | |
extension ViewController: GLKViewDelegate { | |
func glkView(view: GLKView, drawInRect rect: CGRect) { | |
let renderRect = CGRectApplyAffineTransform(rect, scaleTransform) | |
context.drawImage(transformFilter.outputImage!, inRect: renderRect, fromRect: renderRect) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment