Skip to content

Instantly share code, notes, and snippets.

@JoshuaSullivan
Last active April 21, 2016 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshuaSullivan/fc20cac681681c620130894aa26b3902 to your computer and use it in GitHub Desktop.
Save JoshuaSullivan/fc20cac681681c620130894aa26b3902 to your computer and use it in GitHub Desktop.
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.
//
// 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)
}
}
//
// 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