2

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)

A demo screenshot: screenshot

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() or draw() 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 calling videoView.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!

00007chl
  • 91
  • 1
  • 6
  • What flickers exactly, the entire view or what is within it - specifically does the view turn black during resize? – l'L'l Jul 11 '16 at 20:59
  • Follow-up question: what do you do, if anything, to avoid the possibility that you'll get a second (or a third, or a fourth) `update()` prior to completing the previous `videoView.drawRect()`, thereby creating a growing backlog? How long does it take you to draw a frame? – Tommy Jul 11 '16 at 21:51
  • @l'L'l I uploaded a screenshot. It does not turn black. – 00007chl Jul 12 '16 at 01:00
  • @Tommy Actually nothing. I don't exactly know how to deal with the external callback. – 00007chl Jul 12 '16 at 01:12
  • I've experienced the same type of thing myself; I'm not sure if there's going to be a solution using `OpenGL` that will keep the buffer completely free of garbage (even as cool as your example image is) without either blocking the drawing during resizing or having some sort of undesired flicker. – l'L'l Jul 12 '16 at 02:45

0 Answers0