cocoa-cb: render on a dedicated dispatch queue

we rendered on the displaylink thread which wasn't the best idea. if
rendering took too long or was blocking it also blocked the displaylink
callback. when that happened new vsyncs were reported delayed or not at
all. consequently the mpv_render_context_report_swap function wasn't
called consistently and that could cause bad video playback. so the
rendering is moved to a dedicated dispatch queue. furthermore the update
callback starts a layer update directly instead of the displaylink
callback, making the rendering a bit more consistent.
This commit is contained in:
Akemi 2018-03-21 21:09:05 +01:00 committed by Kevin Mitchell
parent 9975835bde
commit 965ba23303
3 changed files with 36 additions and 28 deletions

View File

@ -27,21 +27,22 @@ class VideoLayer: CAOpenGLLayer {
} }
let videoLock = NSLock() let videoLock = NSLock()
let displayLock = NSLock()
var hasVideo: Bool = false var hasVideo: Bool = false
var neededFlips: Int = 0 var needsFlip: Bool = false
var canDrawOffScreen: Bool = false
var cglContext: CGLContextObj? = nil var cglContext: CGLContextObj? = nil
var surfaceSize: NSSize? var surfaceSize: NSSize?
enum Draw: Int { case normal = 1, atomic, atomicEnd } enum Draw: Int { case normal = 1, atomic, atomicEnd }
var draw: Draw = .normal var draw: Draw = .normal
var canDrawOffScreen: Bool = false let queue: DispatchQueue = DispatchQueue(label: "io.mpv.queue.draw")
var lastThread: Thread? = nil
var needsICCUpdate: Bool = false { var needsICCUpdate: Bool = false {
didSet { didSet {
if needsICCUpdate == true { if needsICCUpdate == true {
neededFlips += 1 update()
} }
} }
} }
@ -51,6 +52,7 @@ class VideoLayer: CAOpenGLLayer {
if inLiveResize { if inLiveResize {
isAsynchronous = true isAsynchronous = true
} }
update()
} }
} }
@ -92,9 +94,8 @@ class VideoLayer: CAOpenGLLayer {
pixelFormat pf: CGLPixelFormatObj, pixelFormat pf: CGLPixelFormatObj,
forLayerTime t: CFTimeInterval, forLayerTime t: CFTimeInterval,
displayTime ts: UnsafePointer<CVTimeStamp>?) { displayTime ts: UnsafePointer<CVTimeStamp>?) {
neededFlips = 0 needsFlip = false
canDrawOffScreen = Thread.current == lastThread canDrawOffScreen = true
lastThread = Thread.current
draw(ctx) draw(ctx)
} }
@ -197,36 +198,42 @@ class VideoLayer: CAOpenGLLayer {
let updateCallback: mpv_render_update_fn = { (ctx) in let updateCallback: mpv_render_update_fn = { (ctx) in
let layer: VideoLayer = MPVHelper.bridge(ptr: ctx!) let layer: VideoLayer = MPVHelper.bridge(ptr: ctx!)
layer.neededFlips += 1 layer.update()
} }
override func display() { override func display() {
displayLock.lock()
let isUpdate = needsFlip
super.display() super.display()
CATransaction.flush() CATransaction.flush()
if isUpdate {
if !cocoaCB.window.occlusionState.contains(.visible) &&
needsFlip && canDrawOffScreen
{
CGLSetCurrentContext(cglContext!)
draw(cglContext!)
} else if needsFlip {
update()
}
}
displayLock.unlock()
} }
func setVideo(_ state: Bool) { func setVideo(_ state: Bool) {
videoLock.lock() videoLock.lock()
hasVideo = state hasVideo = state
neededFlips = 0
videoLock.unlock() videoLock.unlock()
} }
func reportFlip() { func update() {
mpv.reportRenderFlip() queue.async {
videoLock.lock() self.videoLock.lock()
if !isAsynchronous && neededFlips > 0 && hasVideo { if !self.inLiveResize && self.hasVideo {
if !cocoaCB.window.occlusionState.contains(.visible) && self.needsFlip = true
neededFlips > 1 && canDrawOffScreen self.display()
{
CGLSetCurrentContext(cglContext!)
draw(cglContext!)
display()
} else {
display()
} }
self.videoLock.unlock()
} }
videoLock.unlock()
} }
} }

View File

@ -305,7 +305,7 @@ class Window: NSWindow, NSWindowDelegate {
} }
isAnimating = false isAnimating = false
cocoaCB.layer.neededFlips += 1 cocoaCB.layer.update()
cocoaCB.checkShutdown() cocoaCB.checkShutdown()
} }
@ -316,7 +316,7 @@ class Window: NSWindow, NSWindowDelegate {
endAnimation() endAnimation()
isInFullscreen = true isInFullscreen = true
cocoaCB.flagEvents(VO_EVENT_FULLSCREEN_STATE) cocoaCB.flagEvents(VO_EVENT_FULLSCREEN_STATE)
cocoaCB.layer.neededFlips += 1 cocoaCB.layer.update()
} }
func setToWindow() { func setToWindow() {
@ -327,7 +327,7 @@ class Window: NSWindow, NSWindowDelegate {
endAnimation() endAnimation()
isInFullscreen = false isInFullscreen = false
cocoaCB.flagEvents(VO_EVENT_FULLSCREEN_STATE) cocoaCB.flagEvents(VO_EVENT_FULLSCREEN_STATE)
cocoaCB.layer.neededFlips += 1 cocoaCB.layer.update()
} }
func getFsAnimationDuration(_ def: Double) -> Double{ func getFsAnimationDuration(_ def: Double) -> Double{

View File

@ -86,7 +86,7 @@ class CocoaCB: NSObject {
} else { } else {
layer.setVideo(true) layer.setVideo(true)
updateWindowSize() updateWindowSize()
layer.neededFlips += 1 layer.update()
} }
} }
@ -150,7 +150,7 @@ class CocoaCB: NSObject {
flagsOut: UnsafeMutablePointer<CVOptionFlags>, flagsOut: UnsafeMutablePointer<CVOptionFlags>,
displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in displayLinkContext: UnsafeMutableRawPointer?) -> CVReturn in
let ccb: CocoaCB = MPVHelper.bridge(ptr: displayLinkContext!) let ccb: CocoaCB = MPVHelper.bridge(ptr: displayLinkContext!)
ccb.layer.reportFlip() ccb.mpv.reportRenderFlip()
return kCVReturnSuccess return kCVReturnSuccess
} }
@ -160,7 +160,7 @@ class CocoaCB: NSObject {
CVDisplayLinkSetCurrentCGDisplay(link!, displayId) CVDisplayLinkSetCurrentCGDisplay(link!, displayId)
if #available(macOS 10.12, *) { if #available(macOS 10.12, *) {
CVDisplayLinkSetOutputHandler(link!) { link, now, out, inFlags, outFlags -> CVReturn in CVDisplayLinkSetOutputHandler(link!) { link, now, out, inFlags, outFlags -> CVReturn in
self.layer.reportFlip() self.mpv.reportRenderFlip()
return kCVReturnSuccess return kCVReturnSuccess
} }
} else { } else {
@ -454,6 +454,7 @@ class CocoaCB: NSObject {
func shutdown(_ destroy: Bool = false) { func shutdown(_ destroy: Bool = false) {
setCursorVisiblility(true) setCursorVisiblility(true)
layer.setVideo(false)
stopDisplaylink() stopDisplaylink()
uninitLightSensor() uninitLightSensor()
removeDisplayReconfigureObserver() removeDisplayReconfigureObserver()