Skip to content

Commit

Permalink
Add option make main thread rendering engine force display update on …
Browse files Browse the repository at this point in the history
…every frame (airbnb#2168)
  • Loading branch information
calda authored and Igor Moroz committed May 22, 2024
1 parent 177ba67 commit 437adef
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 13 deletions.
6 changes: 3 additions & 3 deletions Sources/Private/CoreAnimation/CoreAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ final class CoreAnimationLayer: BaseAnimationLayer {
}
}

/// The parent `LottieAnimationView` that manages this layer
weak var animationView: LottieAnimationView?
/// The parent `LottieAnimationLayer` that manages this layer
weak var lottieAnimationLayer: LottieAnimationLayer?

/// A closure that is called after this layer sets up its animation.
/// If the animation setup was unsuccessful and encountered compatibility issues,
Expand Down Expand Up @@ -316,7 +316,7 @@ final class CoreAnimationLayer: BaseAnimationLayer {
else { return }

if isAnimationPlaying == true {
animationView?.updateInFlightAnimation()
lottieAnimationLayer?.updateInFlightAnimation()
} else {
let currentFrame = currentFrame
removeAnimations()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
newFrame = floor(newFrame)
}
for animationLayer in animationLayers {
animationLayer.displayWithFrame(frame: newFrame, forceUpdates: false)
animationLayer.displayWithFrame(frame: newFrame, forceUpdates: forceDisplayUpdateOnEachFrame)
}
}

Expand All @@ -149,8 +149,16 @@ final class MainThreadAnimationLayer: CALayer, RootAnimationLayer {
/// The animatable Current Frame Property
@NSManaged var currentFrame: CGFloat

/// The parent `LottieAnimationView` that manages this layer
weak var animationView: LottieAnimationView?
/// The parent `LottieAnimationLayer` that manages this layer
weak var lottieAnimationLayer: LottieAnimationLayer?

/// Whether or not to use `forceDisplayUpdate()` when rendering each individual frame.
/// - The main thread rendering engine implements optimizations to decrease the amount
/// of properties that have to be re-rendered on each frame. There are some cases
/// where this can result in bugs / incorrect behavior, so we allow it to be disabled.
/// - Forcing a full render on every frame will decrease performance, and is not recommended
/// except as a workaround to a bug in the main thread rendering engine.
var forceDisplayUpdateOnEachFrame = false

var animationLayers: ContiguousArray<CompositionLayer>

Expand Down
2 changes: 1 addition & 1 deletion Sources/Private/RootAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import QuartzCore

/// A root `CALayer` responsible for playing a Lottie animation
protocol RootAnimationLayer: CALayer {
var animationView: LottieAnimationView? { get set }
var lottieAnimationLayer: LottieAnimationLayer? { get set }

var currentFrame: AnimationFrameTime { get set }
var renderScale: CGFloat { get set }
Expand Down
23 changes: 19 additions & 4 deletions Sources/Public/Animation/LottieAnimationLayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -703,9 +703,19 @@ public class LottieAnimationLayer: CALayer {
}
}

public var animationView: LottieAnimationView? {
set { rootAnimationLayer?.animationView = newValue }
get { rootAnimationLayer?.animationView }
/// Whether or not the Main Thread rendering engine should use `forceDisplayUpdate()`
/// when rendering each individual frame.
/// - The main thread rendering engine implements optimizations to decrease the amount
/// of properties that have to be re-rendered on each frame. There are some cases
/// where this can result in bugs / incorrect behavior, so we allow it to be disabled.
/// - Forcing a full render on every frame will decrease performance, and is not recommended
/// except as a workaround to a bug in the main thread rendering engine.
/// - Has no effect when using the Core Animation rendering engine.
public var mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame = false {
didSet {
(rootAnimationLayer as? MainThreadAnimationLayer)?.forceDisplayUpdateOnEachFrame
= mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame
}
}

/// Sets the lottie file backing the animation layer. Setting this will clear the
Expand Down Expand Up @@ -1085,6 +1095,8 @@ public class LottieAnimationLayer: CALayer {
return
}

animationLayer.lottieAnimationLayer = self

animationLayerDidLoad?(self, renderingEngine)

animationLayer.renderScale = screenScale
Expand All @@ -1098,13 +1110,16 @@ public class LottieAnimationLayer: CALayer {
}

fileprivate func makeMainThreadAnimationLayer(for animation: LottieAnimation) -> MainThreadAnimationLayer {
MainThreadAnimationLayer(
let mainThreadAnimationLayer = MainThreadAnimationLayer(
animation: animation,
imageProvider: imageProvider.cachedImageProvider,
textProvider: textProvider,
fontProvider: fontProvider,
maskAnimationToBounds: maskAnimationToBounds,
logger: logger)

mainThreadAnimationLayer.forceDisplayUpdateOnEachFrame = mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame
return mainThreadAnimationLayer
}

fileprivate func makeCoreAnimationLayer(for animation: LottieAnimation) -> CoreAnimationLayer? {
Expand Down
16 changes: 14 additions & 2 deletions Sources/Public/Animation/LottieAnimationView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,19 @@ open class LottieAnimationView: LottieAnimationViewBase {
lottieAnimationLayer.currentPlaybackMode
}

/// Whether or not the Main Thread rendering engine should use `forceDisplayUpdate()`
/// when rendering each individual frame.
/// - The main thread rendering engine implements optimizations to decrease the amount
/// of properties that have to be re-rendered on each frame. There are some cases
/// where this can result in bugs / incorrect behavior, so we allow it to be disabled.
/// - Forcing a full render on every frame will decrease performance, and is not recommended
/// except as a workaround to a bug in the main thread rendering engine.
/// - Has no effect when using the Core Animation rendering engine.
public var mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame: Bool {
get { lottieAnimationLayer.mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame }
set { lottieAnimationLayer.mainThreadRenderingEngineShouldForceDisplayUpdateOnEachFrame = newValue }
}

/// Sets the lottie file backing the animation view. Setting this will clear the
/// view's contents, completion blocks and current state. The new animation will
/// be loaded up and set to the beginning of its timeline.
Expand Down Expand Up @@ -805,11 +818,10 @@ open class LottieAnimationView: LottieAnimationViewBase {
self.setNeedsLayout()
}

lottieAnimationLayer.animationLayerDidLoad = { [weak self] lottieAnimationLayer, _ in
lottieAnimationLayer.animationLayerDidLoad = { [weak self] _, _ in
guard let self = self else { return }
self.invalidateIntrinsicContentSize()
self.setNeedsLayout()
lottieAnimationLayer.animationView = self
}
}

Expand Down

0 comments on commit 437adef

Please sign in to comment.