You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CAMetalLayer.nextDrawable耗时过长问题排查(附测试代码)

CAMetalLayer.nextDrawable() 异常高耗时问题复现

CAMetalLayer.nextDrawable() 本应是低耗时方法,但实际场景中偶尔会出现耗时超过8ms的情况,可通过以下步骤复现问题:

  • 复制下方Swift代码
  • 按照代码中viewDidLoad方法内的注释指引操作
  • 查看控制台日志输出
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()
        
        // 等待页面加载完成后,可执行以下操作:
        
        // 1. 下拉控制中心,控制台会持续输出大量日志
        
        // 2. 将应用切换到后台再切回前台,控制台会输出大量日志,且会持续较长时间
        
        // 3. 不进行任何操作,也可能会看到日志持续输出....
        //      点击屏幕后日志可能消失,但一段时间后可能再次出现....
    }
    
    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
        
        // 获取next drawable耗时超过5ms时,打印日志标记该方法耗时过长
        if (timeUsed > 0.005) {
            print("CAMetalLayer.nextDrawable take much time!! -> \(String(format: "%.2f", timeUsed * 1000)) ms")
        }
        frameDrawable.present()
    }
}

内容的提问来源于stack exchange,提问作者luckysmg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:01:05