Proper way to run CADisplayLink on async background thread?

628 Views Asked by At

What is the proper way to make CADisplayLink callback functions run on a background thread? I'm creating the Display Link with:

let displayLink = CADisplayLink(target: self, selector: #selector(self.renderBackground))
        
if let displayLink = displayLink {
    displayLink.preferredFramesPerSecond = 30
    DispatchQueue.main.async {
        displayLink.add(to: .current, forMode: .common)
     }
}

This works, but when I add a breakpoint to the renderBackground function, it shows it is on the Main thread:

enter image description here

It appears to be on the main thread?

2

There are 2 best solutions below

1
On

A CADisplayLink is intended to provide callbacks synchronized with the screen refresh, which takes place on the main thread. It doesn't make sense to synchronize an event that occurs on the main thread with calls on another thread.

I suspect there is no way to do this.

0
On
DispatchQueue.main.async {
    displayLink.add(to: .current, forMode: .common)
}

CADisplayLink.add(to: .current, …) will schedule callbacks on the run loop from the current thread. Since you used DispatchQueue.main, the current thread at this point is still the main thread, so the callback keeps happening on the main thread.

You can get them on a background thread if you do the following:

  • launch a background thread (I'd not recommend using GCD/DispatchQueue for this as you'll keep this thread busy, hence preventing GCD to run at full capacity)
  • From this background thread: call displayLink.add(to: .current, forMode: .common)
  • From this background thread: use one of the RunLoop.run(…) methods on RunLoop.current for as long as you need to receive CADisplayLink updates.