Skip to content

Commit

Permalink
Enable CADisplayLink to run at a user-defined preferredFramesPerSecond
Browse files Browse the repository at this point in the history
Just a draft for testing at the moment.. if we want to include the way to set `preferredFramesPerSecond`, might make sense to expose some kind of API on `RiveView` or `RiveViewModel` to set on `CADisplayLink` when we create it for the animation loop.

In particular, the change to `Info.plist` below is what enables the display link to go to 120fps.

One caveat is that in setting `.preferredFramesPerSecond`, this API is marked as deprecated by Apple.. and still need to understand what the alternative is
https://developer.apple.com/documentation/quartzcore/cadisplaylink/1648421-preferredframespersecond

Diffs=
97b7622bc Enable CADisplayLink to run at a user-defined preferredFramesPerSecond (#6111)

Co-authored-by: Zachary Plata <[email protected]>
  • Loading branch information
zplata and zplata committed Oct 23, 2023
1 parent 9aeb729 commit 2b15111
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d65b239c5c6ce44c3b58cd71ff17721d874a4ab1
97b7622bcb0d99dc469706d9cf57cb03e34488de
2 changes: 1 addition & 1 deletion .rive_renderer
Original file line number Diff line number Diff line change
@@ -1 +1 @@
03f784cccda96fd5eee541e31bed4681904c0ed8
e40af7f67eb7654000a156614e462c9a31f69bb9
6 changes: 6 additions & 0 deletions Example-iOS/RiveExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@
E5A7874A27E115170056F24B /* energy_bar_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A7874727E115170056F24B /* energy_bar_example.riv */; };
E5A7874C27E1158E0056F24B /* prop_example.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A7874B27E1158E0056F24B /* prop_example.riv */; };
E5CD7D7127DC331900BFE5E2 /* SwiftMeshAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */; };
E5E87A012AE5A83800E7295F /* SwiftVariableFPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */; };
E5E87A022AE5A85E00E7295F /* SwiftVariableFPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */; };
F8772A872AD946D500AB5920 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9C73E9724FC471E00EF9516 /* AppDelegate.swift */; };
F8772A882AD946FD00AB5920 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 042C88822643D6B900E7DBB2 /* Main.storyboard */; };
F8772A892AD9470000AB5920 /* ExamplesMaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3357CA0280F42EC00F03B6F /* ExamplesMaster.swift */; };
Expand Down Expand Up @@ -419,6 +421,7 @@
E5A7874727E115170056F24B /* energy_bar_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = energy_bar_example.riv; sourceTree = "<group>"; };
E5A7874B27E1158E0056F24B /* prop_example.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = prop_example.riv; sourceTree = "<group>"; };
E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMeshAnimation.swift; sourceTree = "<group>"; };
E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftVariableFPS.swift; sourceTree = "<group>"; };
F8772A712AD945FC00AB5920 /* Preview.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Preview.app; sourceTree = BUILT_PRODUCTS_DIR; };
F8772A812AD945FE00AB5920 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F8772ADF2AD94A0500AB5920 /* Preview (macOS).app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Preview (macOS).app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -596,6 +599,7 @@
C9A84F342644931E0014D8E0 /* SwiftUI */ = {
isa = PBXGroup;
children = (
E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */,
04026DC327CE3ED6002B3DBF /* SwiftSimpleAnimation.swift */,
C9CB2F12264C92D200E7FF0D /* SwiftWidgets.swift */,
04026DC727CE3EE6002B3DBF /* SwiftLayout.swift */,
Expand Down Expand Up @@ -1110,6 +1114,7 @@
C3468E6227FDCBC6008652FD /* SimpleSlider.swift in Sources */,
C3C074EE28414F4600E8EB33 /* SwiftTestParityAnimSM.swift in Sources */,
83C89ACB29886ECB00044C17 /* StressTest.swift in Sources */,
E5E87A012AE5A83800E7295F /* SwiftVariableFPS.swift in Sources */,
C3357CA1280F42EC00F03B6F /* ExamplesMaster.swift in Sources */,
C324DB5628071EB80060589F /* RiveSwitch.swift in Sources */,
C3ECAC272817BE4600A81123 /* SwiftTouchEvents.swift in Sources */,
Expand All @@ -1133,6 +1138,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E5E87A022AE5A85E00E7295F /* SwiftVariableFPS.swift in Sources */,
F8772ACD2AD9471E00AB5920 /* SwiftMeshAnimation.swift in Sources */,
F8772AC22AD9471B00AB5920 /* BlendModes.swift in Sources */,
F8772AD72AD9472F00AB5920 /* SceneDelegate.swift in Sources */,
Expand Down
58 changes: 58 additions & 0 deletions Example-iOS/Source/Examples/SwiftUI/SwiftVariableFPS.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// SwiftVariableFPS.swift
// Example (iOS)
//
// Created by Zach Plata on 10/20/23.
// Copyright © 2023 Rive. All rights reserved.
//

import SwiftUI
import RiveRuntime

struct SwiftVariableFPS: DismissableView {
var dismiss: () -> Void = {}

private var stateChanger = RiveViewModel(fileName: "skills", stateMachineName: "Designer's Test")

var body: some View {
ScrollView{
VStack {
stateChanger.view()
.frame(height:200)

HStack{
Button("Prefer 30 fps") {
if #available(iOS 15.0, *) {
stateChanger.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 120, preferred: 30))
} else {
stateChanger.setPreferredFramesPerSecond(preferredFramesPerSecond: 30)
}
}
Button("Prefer 60 fps") {
if #available(iOS 15.0, *) {
stateChanger.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 120, preferred: 60))
} else {
stateChanger.setPreferredFramesPerSecond(preferredFramesPerSecond: 60)
}
}
Button("Prefer 120 fps") {
if #available(iOS 15.0, *) {
stateChanger.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 120, preferred: 120))
} else {
stateChanger.setPreferredFramesPerSecond(preferredFramesPerSecond: 120)
}
}
}

}
}.onAppear() {
if #available(iOS 15.0, *) {
stateChanger.setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange(minimum: 30, maximum: 120, preferred: 120))
} else {
stateChanger.setPreferredFramesPerSecond(preferredFramesPerSecond: 120)
}
}
}
}


3 changes: 2 additions & 1 deletion Example-iOS/Source/ExamplesMaster.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class ExamplesMasterTableViewController: UITableViewController {
("State Machine", typeErased(dismissableView: SwiftStateMachine())),
("Mesh Animation", typeErased(dismissableView: SwiftMeshAnimation())),
("Playing with Text", typeErased(dismissableView: TextInputView())),
("Rive Events", typeErased(dismissableView: SwiftEvents()))
("Rive Events", typeErased(dismissableView: SwiftEvents())),
("Variable FPS", typeErased(dismissableView: SwiftVariableFPS()))
]


Expand Down
2 changes: 2 additions & 0 deletions Example-iOS/Source/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
Expand Down
27 changes: 26 additions & 1 deletion Source/RiveView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@ open class RiveView: RiveRendererView {
setFPSCounterVisibility()
}

#if os(iOS)
/// Hints to underlying CADisplayLink the preferred FPS to run at
/// - Parameters:
/// - preferredFramesPerSecond: Integer number of seconds to set preferred FPS at
open func setPreferredFramesPerSecond(preferredFramesPerSecond: Int) {
if let displayLink = displayLinkProxy?.displayLink {
displayLink.preferredFramesPerSecond = preferredFramesPerSecond
}
}

/// Hints to underlying CADisplayLink the preferred frame rate range
/// - Parameters:
/// - preferredFrameRateRange: Frame rate range to set
@available(iOSApplicationExtension 15.0, *)
open func setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange) {
if let displayLink = displayLinkProxy?.displayLink {
displayLink.preferredFrameRateRange = preferredFrameRateRange
}
}
#endif

// MARK: - Controls

/// Starts the render loop
Expand Down Expand Up @@ -140,8 +161,12 @@ open class RiveView: RiveRendererView {
fpsCounter?.stopped()
}

private func timestamp() -> Double {
private func timestamp() -> CFTimeInterval {
#if os(iOS)
return displayLinkProxy?.displayLink?.targetTimestamp ?? Date().timeIntervalSince1970
#else
return Date().timeIntervalSince1970
#endif
}


Expand Down
22 changes: 21 additions & 1 deletion Source/RiveViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
fit: RiveFit = .contain,
alignment: RiveAlignment = .center,
autoPlay: Bool = true,
artboardName: String? = nil
artboardName: String? = nil,
preferredFramesPerSecond: Int? = nil
) {
self.fit = fit
self.alignment = alignment
Expand Down Expand Up @@ -177,6 +178,25 @@ open class RiveViewModel: NSObject, ObservableObject, RiveFileDelegate, RiveStat
didSet { riveView?.alignment = alignment }
}

#if os(iOS)
/// Hints to underlying CADisplayLink in RiveView (if created) the preferred FPS to run at
/// For more, see: https://developer.apple.com/documentation/quartzcore/cadisplaylink/1648421-preferredframespersecond
/// - Parameters:
/// - preferredFramesPerSecond: Integer number of seconds to set preferred FPS at
public func setPreferredFramesPerSecond(preferredFramesPerSecond: Int) {
riveView?.setPreferredFramesPerSecond(preferredFramesPerSecond: preferredFramesPerSecond)
}

/// Hints to underlying CADisplayLink in RiveView (if created) the preferred frame rate range
/// For more, see: https://developer.apple.com/documentation/quartzcore/cadisplaylink/3875343-preferredframeraterange
/// - Parameters:
/// - preferredFrameRateRange: Frame rate range to set
@available(iOSApplicationExtension 15.0, *)
public func setPreferredFrameRateRange(preferredFrameRateRange: CAFrameRateRange) {
riveView?.setPreferredFrameRateRange(preferredFrameRateRange: preferredFrameRateRange)
}
#endif

/// Starts the active Animation or StateMachine from it's last position. It will start
/// from the beginning if the active Animation has ended or a new one is provided.
/// - Parameters:
Expand Down

0 comments on commit 2b15111

Please sign in to comment.