From b88169c7dd04a922025820c25b1f19a86549583c Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 14 Nov 2025 20:24:33 +0100 Subject: [PATCH] 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. --- Model/Player/Backends/MPVClient.swift | 7 ++----- macOS/VideoLayer.swift | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index afda9c8e..0c84dfa1 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -666,11 +666,8 @@ final class MPVClient: ObservableObject { func glUpdate(_ ctx: UnsafeMutableRawPointer?) { let videoLayer = unsafeBitCast(ctx, to: VideoLayer.self) - videoLayer.client?.queue?.async { - if !videoLayer.isAsynchronous { - videoLayer.display() - } - } + // Request a redraw when MPV signals that new content is available + videoLayer.requestRedraw() } #else func getProcAddress(_: UnsafeMutableRawPointer?, _ name: UnsafePointer?) -> UnsafeMutableRawPointer? { diff --git a/macOS/VideoLayer.swift b/macOS/VideoLayer.swift index 2fc6576b..6ee3caa8 100644 --- a/macOS/VideoLayer.swift +++ b/macOS/VideoLayer.swift @@ -5,11 +5,15 @@ import OpenGL.GL3 final class VideoLayer: CAOpenGLLayer { var client: MPVClient! + private var needsRedraw = false + private let redrawQueue = DispatchQueue(label: "com.yattee.videolayer.redraw", qos: .userInteractive) override init() { super.init() autoresizingMask = [.layerWidthSizable, .layerHeightSizable] backgroundColor = NSColor.black.cgColor + // Enable asynchronous drawing for better performance + isAsynchronous = true } @available(*, unavailable) @@ -32,6 +36,8 @@ final class VideoLayer: CAOpenGLLayer { forLayerTime _: CFTimeInterval, displayTime _: UnsafePointer? ) { + needsRedraw = false + var i: GLint = 0 var flip: CInt = 1 var ditherDepth = 8 @@ -92,13 +98,23 @@ final class VideoLayer: CAOpenGLLayer { } override func display() { + // Mark as needing display without blocking + guard !needsRedraw else { return } + needsRedraw = true 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 layer: VideoLayer = unsafeBitCast(displayLinkContext, to: VideoLayer.self) 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) } return kCVReturnSuccess