ios CAMetalLayer.nextDrawable需要花费大量时间,甚至超过8毫秒

wxclj1h5  于 2023-01-03  发布在  iOS
关注(0)|答案(1)|浏览(275)

CAMetalLayer.nextDrawable()应该不是一个非常耗时的方法,但有时候它往往会花费很多时间,甚至超过8ms
1.复制下面的代码
1.遵循viewDidLoad中的注解指南
1.参见日志打印

class TestVC: UIViewController {
    
    let metalLayer:CAMetalLayer = CAMetalLayer()
    
    var displayLink:CADisplayLink!
    
    var thread:Thread!
    
    var animationView:AnimationView!
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.displayLink.isPaused = true
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.displayLink.isPaused = false
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        print("ahhhaa")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .red
        
        let scale = UIScreen.main.scale
        metalLayer.frame = self.view.bounds
        metalLayer.drawableSize = CGSize(width: self.view.frame.width * scale, height: self.view.frame.height * scale)
        self.view.layer.addSublayer(metalLayer)
        self.addDisplayLinkInUITaskRunner()
        
        // Wait screen loading complete,and next,You can:
        
        // 1. Swipe down the control center, you will see a lot log print in console, and it will
        //                      constantly exists.
        
        // 2. Move the app to bg, and then move to fg, you will see a lot of log,
        //      it also will constantly exist for a long time.
        
        // 3. Just don't do anything, you might see it the log print constantly....
        //      And when you tap the screen, it may disappear, and After a while, you may see log again....
    }
    
    func addDisplayLinkInUITaskRunner() {
        self.thread = Thread(block: {
            RunLoop.current.add(NSMachPort(), forMode: .common)
            RunLoop.current.run()
        })
        self.thread.name = ""
        self.thread.start()
        
        self.perform(#selector(addDisplayLink), on: thread, with: nil, waitUntilDone: false)
    }
    
    @objc func addDisplayLink() {
        self.displayLink = CADisplayLink(target: self, selector: #selector(onDisplayLink))
        if #available(iOS 15.0, *) {
            self.displayLink.preferredFrameRateRange = .init(minimum: 60, maximum: 120, preferred: 120)
        } else {
            self.displayLink.preferredFramesPerSecond = 120
        }
        self.displayLink.add(to: .current, forMode: .common)
    }
    
    @objc private func onDisplayLink() {
        let startTime = CACurrentMediaTime()
        let frameDrawable = metalLayer.nextDrawable()!
        let timeUsed = CACurrentMediaTime() - startTime
        
        // If time used to get next drawble over 3ms,
        // we print it here to indicate this method take much time!
        if (timeUsed > 0.005) {
            print("CAMetalLayer.nextDrawable take much time!! -> \(String(format: "%.2f", timeUsed * 1000)) ms")
        }
        frameDrawable.present()
    }
}
83qze16e

83qze16e1#

如果displaySyncEnabled设置为true(默认值),它将等待下一个vsync来显示可绘制对象,这意味着你可能很快就用完可绘制对象,因此nextDrawable将等待直到有一个可用(或最多1秒)。
换句话说,由于您已经有可绘制对象排队等待显示,因此在一个可绘制对象真正可用之前,您无法获得另一个可绘制对象。
如果您确实希望尽快显示它们-将displaySyncEabled设置为false。然而,当前行为可能正是您所希望的,因为您很少从显示帧的速度快于显示器的刷新率中获得任何好处。

相关问题