I'm new to macOS development.
Currently I'm implementing a NSOpenGLView
with a third-party library.
- The
update
function is a callback from the library, which will be called when a new frame arrives. Since it's expected to return immediately, the drawing should be called asynchronously. - I tried to use
DispatchQueue.main.async()
here, however I found that it would block the main thread, e.g. there will be no animation when the container window is being resized. So I created a dispatch queue for drawing. - Now resizing is pretty smooth, however the view glitches seriously during resizing. I think the reason should be drawing from different thread (when resizing, the view is also redrawn from main thread), however I'm not sure the concrete reason.
- Glitches only happen while window bound is changing. During the resizing, if stop dragging for a while, no glitches observed.
(Update: The problem only happens if double buffering is enabled)
func update() {
videoView.glQueue.async {
videoView.drawRect()
}
}
class VideoView: NSOpenGLView {
lazy var glQueue: DispatchQueue = DispatchQueue(label: "gl", attributes: .serial)
override func draw(_ dirtyRect: NSRect) {
self.drawRect()
}
func drawRect() {
openGLContext?.lock()
openGLContext?.makeCurrentContext()
doDrawing()
openGLContext?.flushBuffer()
openGLContext?.unlock()
}
}
I tried for several solutions.
- Call
videoView.drawRect()
in main queue will totally block the resize. - Add lock to
update()
ordraw()
will eliminate glitches, but the UI thread is still very slow with choppy resizing.
class VideoView: NSOpenGLView {
override func update() {
// lock with NSLock / lock the context
super.update()
// unlock
}
}
- Check
window.inLiveResize
, and stop callingvideoView.drawRect()
if true. When resizing, the draw() function is called automatically. This approach works well with quite smooth resizing, however when pause dragging, the view will freeze since the LiveResize event is not finished.
func update() {
if !(videoView.window!.inLiveResize) {
videoView.glQueue.async {
videoView.drawRect()
}
}
}
I finally managed to figure out a workaround, but it's still imperfect and I'm sure it's not the right direction.
Please suggest whether I'm doing this in the right way, or better solution / best practice about drawing without blocking UI. Thanks!