我有一个应用程序可以完成数千个循环填充,每个循环填充可以在每个帧中独立变化。 Quartz在这方面非常快,我可以在iPhone 6上获得大约30fps,下面的示例使用-Ofast,-Ounchecked。但是我想尽可能地优化它。
我尝试或考虑的事情:
1)绘制缓存的图像而不是填充椭圆:我尝试使用此技术的代码在示例中被注释掉,因为我希望它可以更快。我发现的是,对于示例中的填充尺寸大致相同,但随着填充变大,它比简单绘制要慢得多。 (这与我在尝试此操作时在Java中发现的内容相矛盾。)
2)填充图像后面的矩形蒙版:填充矩形比填充椭圆要快得多,但是当我向蒙版添加剪裁时,它的速度会慢一些。如果有人要我发布它(试图保持这一点),我有一些代码。
3)Metal API:根据我的阅读,我可以使用几何着色器在GPU上渲染缩放适当的圆形几何体。我担心这是GPU的最坏情况,因为所有顶点数据都可以在每个帧上独立变化。我对这有多快没有任何直觉,除非我确定这将是一个巨大的改进,否则它需要学习很多。
这是一个代表问题的测试UIView(我省略了颜色以使图像比特更简单)。它是用Swift编写的,但如果它有帮助我很乐意将部件移动到objc(我不认为Swift是这里的问题)。请注意,技术的相对性能因maxPartSize而异。我正在寻找任何可以加快速度的方法。感谢。
class TestView: UIView
{
var parts: [Part]!
var start:NSDate!
var drawCount = 0 // for fps counter
var image: UIImage! // for the image approach
let maxPartSize: CGFloat = 10.0
override func drawRect(screenRect: CGRect)
{
if ( self.start == nil) { self.start = NSDate() }
if ( self.parts == nil ) { setupParts(screenRect) }
var context = UIGraphicsGetCurrentContext()
for (var i=0; i<parts.count; i++) {
let part = parts[i]
var drawRect: CGRect = CGRectMake(CGFloat(part.x), CGFloat(part.y), CGFloat(part.size), CGFloat(part.size))
// Draw 2D
CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor)
CGContextSetAlpha(context, CGFloat(part.trans))
CGContextFillEllipseInRect(context, drawRect)
// Draw with image. (This is faster for small values of maxPartSize, much slower otherwise)
// CGContextSetAlpha(context, CGFloat(part.trans))
// CGContextDrawImage(context, drawRect, getImage().CGImage)
}
drawCount++
if ( drawCount%100 == 0 ) {
let fps: Double = Double(drawCount) / NSDate().timeIntervalSinceDate(start)
println("\(fps) fps")
}
}
func setupParts( rect: CGRect ) {
self.parts = []
let numParts = 2000
for(var i=0; i<numParts; i++ ) {
let x=randomFloat()*Float(rect.width)
let y=randomFloat()*Float(rect.height)
let size = randomFloat() * Float(maxPartSize)
let trans=randomFloat()
parts.append( Part( x:x, y:y, size:size, trans:trans) )
}
}
func getImage() -> UIImage
{
if ( self.image != nil ) { return self.image }
var size = CGSizeMake(maxPartSize, maxPartSize)
UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
var context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor)
CGContextFillEllipseInRect(context, CGRectMake( 0, 0, maxPartSize, maxPartSize) )
var image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.image = image;
return image
}
required init(coder: NSCoder) {
super.init(coder: coder)
startAnimating()
}
func refreshView() { self.setNeedsDisplay(); }
func startAnimating() {
var updater = CADisplayLink(target: self, selector: Selector("refreshView"))
updater.frameInterval = 1
updater.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes )
}
}
final class Part {
var x:Float, y:Float, size: Float, trans: Float
init( x:Float, y:Float, size:Float, trans:Float ) {
self.x = x; self.y = y; self.size = size; self.trans = trans
}
}
func randomFloat() -> Float { return Float( arc4random() ) / Float(UInt32.max) }