mirror of
https://github.com/yattee/yattee.git
synced 2026-10-29 16:07:20 +00:00
Keep animated video resizes smooth on iOS
Every layout pass with a new size recreated the framebuffer and rendered synchronously on the render queue, blocking the main thread behind the in-flight render. Animated layout changes (corner layout panel toggles, rotation) dropped to a few frames per second. Size changes arriving in quick succession now let the layer stretch the last frame and recreate the framebuffer once the size settles; one-off resizes still recreate immediately.
This commit is contained in:
@@ -107,6 +107,14 @@ final class MPVRenderView: UIView {
|
|||||||
/// Pending resize work item (for debouncing)
|
/// Pending resize work item (for debouncing)
|
||||||
private var pendingResizeWorkItem: DispatchWorkItem?
|
private var pendingResizeWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
|
/// When the view size last changed away from the framebuffer size.
|
||||||
|
private var lastSizeChangeTime: CFTimeInterval = 0
|
||||||
|
|
||||||
|
/// Size changes closer together than this are one continuous resize
|
||||||
|
/// (animated layout change, rotation) and recreate the framebuffer only
|
||||||
|
/// once it settles.
|
||||||
|
private static let continuousResizeWindow: CFTimeInterval = 0.15
|
||||||
|
|
||||||
/// Dedicated queue for OpenGL rendering (off main thread).
|
/// Dedicated queue for OpenGL rendering (off main thread).
|
||||||
private let renderQueue = DispatchQueue(label: "stream.yattee.mpv.render", qos: .userInteractive)
|
private let renderQueue = DispatchQueue(label: "stream.yattee.mpv.render", qos: .userInteractive)
|
||||||
|
|
||||||
@@ -501,8 +509,28 @@ final class MPVRenderView: UIView {
|
|||||||
// Cancel any pending resize
|
// Cancel any pending resize
|
||||||
pendingResizeWorkItem?.cancel()
|
pendingResizeWorkItem?.cancel()
|
||||||
|
|
||||||
// Always recreate framebuffer immediately to ensure video is properly sized
|
// A continuous resize (corner layout panel toggles, rotation) changes
|
||||||
// This is important during rotation animations
|
// the size every animation frame. Recreating + rendering per step
|
||||||
|
// blocks main on renderQueue.sync (waiting out the in-flight render,
|
||||||
|
// then rendering again) and drops the animation to a few fps. Let the
|
||||||
|
// layer stretch the last frame meanwhile — the view keeps the video's
|
||||||
|
// aspect ratio — and recreate once the size settles. A one-off resize
|
||||||
|
// (e.g. handing the view to the mini player) still recreates now.
|
||||||
|
let now = CACurrentMediaTime()
|
||||||
|
let isContinuousResize = now - lastSizeChangeTime < Self.continuousResizeWindow
|
||||||
|
lastSizeChangeTime = now
|
||||||
|
if isContinuousResize {
|
||||||
|
MPVLogging.log("layoutSubviews: deferring framebuffer recreation (continuous resize)")
|
||||||
|
let workItem = DispatchWorkItem { [weak self] in
|
||||||
|
// Settled: the next layout pass recreates immediately
|
||||||
|
self?.lastSizeChangeTime = 0
|
||||||
|
self?.setNeedsLayout()
|
||||||
|
}
|
||||||
|
pendingResizeWorkItem = workItem
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + Self.continuousResizeWindow, execute: workItem)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
renderQueue.sync {
|
renderQueue.sync {
|
||||||
destroyFramebuffer()
|
destroyFramebuffer()
|
||||||
createFramebuffer()
|
createFramebuffer()
|
||||||
|
|||||||
Reference in New Issue
Block a user