Improve video layer rendering on macOS

Refactored glUpdate to use requestRedraw method for better control. Added needsRedraw flag to prevent redundant display calls. Enabled asynchronous drawing on VideoLayer for improved performance. Modified displayLinkCallback to only report swap without triggering display to avoid flooding the main thread.
This commit is contained in:
Arkadiusz Fal
2025-11-14 20:24:33 +01:00
parent ddf997ee58
commit b88169c7dd
2 changed files with 19 additions and 6 deletions

View File

@@ -666,11 +666,8 @@ final class MPVClient: ObservableObject {
func glUpdate(_ ctx: UnsafeMutableRawPointer?) { func glUpdate(_ ctx: UnsafeMutableRawPointer?) {
let videoLayer = unsafeBitCast(ctx, to: VideoLayer.self) let videoLayer = unsafeBitCast(ctx, to: VideoLayer.self)
videoLayer.client?.queue?.async { // Request a redraw when MPV signals that new content is available
if !videoLayer.isAsynchronous { videoLayer.requestRedraw()
videoLayer.display()
}
}
} }
#else #else
func getProcAddress(_: UnsafeMutableRawPointer?, _ name: UnsafePointer<Int8>?) -> UnsafeMutableRawPointer? { func getProcAddress(_: UnsafeMutableRawPointer?, _ name: UnsafePointer<Int8>?) -> UnsafeMutableRawPointer? {

View File

@@ -5,11 +5,15 @@ import OpenGL.GL3
final class VideoLayer: CAOpenGLLayer { final class VideoLayer: CAOpenGLLayer {
var client: MPVClient! var client: MPVClient!
private var needsRedraw = false
private let redrawQueue = DispatchQueue(label: "com.yattee.videolayer.redraw", qos: .userInteractive)
override init() { override init() {
super.init() super.init()
autoresizingMask = [.layerWidthSizable, .layerHeightSizable] autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
backgroundColor = NSColor.black.cgColor backgroundColor = NSColor.black.cgColor
// Enable asynchronous drawing for better performance
isAsynchronous = true
} }
@available(*, unavailable) @available(*, unavailable)
@@ -32,6 +36,8 @@ final class VideoLayer: CAOpenGLLayer {
forLayerTime _: CFTimeInterval, forLayerTime _: CFTimeInterval,
displayTime _: UnsafePointer<CVTimeStamp>? displayTime _: UnsafePointer<CVTimeStamp>?
) { ) {
needsRedraw = false
var i: GLint = 0 var i: GLint = 0
var flip: CInt = 1 var flip: CInt = 1
var ditherDepth = 8 var ditherDepth = 8
@@ -92,13 +98,23 @@ final class VideoLayer: CAOpenGLLayer {
} }
override func display() { override func display() {
// Mark as needing display without blocking
guard !needsRedraw else { return }
needsRedraw = true
super.display() super.display()
CATransaction.flush() }
func requestRedraw() {
// Called from MPV's glUpdate callback - use setNeedsDisplay for efficient batching
DispatchQueue.main.async { [weak self] in
self?.setNeedsDisplay()
}
} }
let displayLinkCallback: CVDisplayLinkOutputCallback = { _, _, _, _, _, displayLinkContext -> CVReturn in let displayLinkCallback: CVDisplayLinkOutputCallback = { _, _, _, _, _, displayLinkContext -> CVReturn in
let layer: VideoLayer = unsafeBitCast(displayLinkContext, to: VideoLayer.self) let layer: VideoLayer = unsafeBitCast(displayLinkContext, to: VideoLayer.self)
if layer.client?.mpvGL != nil { if layer.client?.mpvGL != nil {
// Just report swap - don't trigger display here to avoid flooding main thread
mpv_render_context_report_swap(layer.client.mpvGL) mpv_render_context_report_swap(layer.client.mpvGL)
} }
return kCVReturnSuccess return kCVReturnSuccess