mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 21:22:53 +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)
|
||||
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).
|
||||
private let renderQueue = DispatchQueue(label: "stream.yattee.mpv.render", qos: .userInteractive)
|
||||
|
||||
@@ -501,8 +509,28 @@ final class MPVRenderView: UIView {
|
||||
// Cancel any pending resize
|
||||
pendingResizeWorkItem?.cancel()
|
||||
|
||||
// Always recreate framebuffer immediately to ensure video is properly sized
|
||||
// This is important during rotation animations
|
||||
// A continuous resize (corner layout panel toggles, rotation) changes
|
||||
// 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 {
|
||||
destroyFramebuffer()
|
||||
createFramebuffer()
|
||||
|
||||
Reference in New Issue
Block a user