mirror of https://github.com/mpv-player/mpv
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:
parent
9975835bde
commit
965ba23303
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue