feat: add back legacy pacing
This commit is contained in:
@@ -986,6 +986,29 @@
|
||||
</switch>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="oZG-5Y-pw5" userLabel="framePacing Stack">
|
||||
<rect key="frame" x="420" y="1189" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Frame Pacing Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MaN-f7-I3g" userLabel="Frame Pacing Mode">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="2Dp-Qg-chi" userLabel="Frame Pacing Mode Selector">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||
<segments>
|
||||
<segment title="Legacy"/>
|
||||
<segment title="Queue"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="7VX-PK-miO"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
@@ -1014,9 +1037,11 @@
|
||||
<outlet property="externalDisplayModeSelector" destination="VWB-YB-Ptj" id="0ak-pc-evb"/>
|
||||
<outlet property="externalDisplayModeStack" destination="YBu-VX-wpN" id="72y-R6-fF7"/>
|
||||
<outlet property="fpsStack" destination="TuR-H8-e2H" id="zGQ-gR-P1i"/>
|
||||
<outlet property="framePacingModeSelector" destination="2Dp-Qg-chi" id="h8A-YF-SZP"/>
|
||||
<outlet property="framePacingStack" destination="oZG-5Y-pw5" id="vGd-hD-zTr"/>
|
||||
<outlet property="frameQueueSizeLabel" destination="Ce3-FM-vIh" id="90D-Tq-OGd"/>
|
||||
<outlet property="frameQueueSizeSlider" destination="oZa-8Y-icz" id="Jo1-Fx-UMw"/>
|
||||
<outlet property="frameQueueSizeStack" destination="MML-c1-D5j" id="uFG-Ch-nsK"/>
|
||||
<outlet property="frameQueueSizeStack" destination="MML-c1-D5j" id="82B-r7-Zbh"/>
|
||||
<outlet property="framerateSelector" destination="8hF-kb-2uV" id="dfT-hb-csE"/>
|
||||
<outlet property="graphOpacityStepper" destination="9VJ-Gj-nWG" id="c4x-HW-KXU"/>
|
||||
<outlet property="gyroModeSelector" destination="aa5-3y-I4d" id="FsX-kH-h7w"/>
|
||||
|
||||
@@ -1685,6 +1685,29 @@
|
||||
<constraint firstAttribute="width" constant="380" id="A4I-UV-heD"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="U0H-kh-QLS" userLabel="framePacing Stack">
|
||||
<rect key="frame" x="437" y="2081" width="380" height="56.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Frame Pacing Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Cvx-kQ-L3H" userLabel="Frame Pacing Mode">
|
||||
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="CC6-pu-Uoh" userLabel="Frame Pacing Mode Selector">
|
||||
<rect key="frame" x="0.0" y="25.5" width="380" height="32"/>
|
||||
<segments>
|
||||
<segment title="Legacy"/>
|
||||
<segment title="Queue"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="selectedSegmentTintColor" systemColor="linkColor"/>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="380" id="AUi-t1-5ej"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
@@ -1713,6 +1736,8 @@
|
||||
<outlet property="externalDisplayModeSelector" destination="Tue-Qh-8zP" id="YCO-TO-Dda"/>
|
||||
<outlet property="externalDisplayModeStack" destination="1Q7-WA-wrj" id="KLk-cJ-fa3"/>
|
||||
<outlet property="fpsStack" destination="aMv-7Q-yYg" id="JxU-wq-2Za"/>
|
||||
<outlet property="framePacingModeSelector" destination="CC6-pu-Uoh" id="HTB-6I-QgB"/>
|
||||
<outlet property="framePacingStack" destination="U0H-kh-QLS" id="M8w-wy-HI8"/>
|
||||
<outlet property="frameQueueSizeLabel" destination="r8X-jt-JfH" id="Tr9-eb-bIx"/>
|
||||
<outlet property="frameQueueSizeSlider" destination="thv-JE-onX" id="gKD-6c-5tw"/>
|
||||
<outlet property="frameQueueSizeStack" destination="IHx-FO-ctr" id="gob-Ea-Avh"/>
|
||||
@@ -1798,19 +1823,19 @@
|
||||
<image name="trash.fill" catalog="system" width="117" height="128"/>
|
||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
||||
<systemColor name="linkColor">
|
||||
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="secondarySystemGroupedBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemBrownColor">
|
||||
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.63529411764705879" green="0.51764705882352946" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemGray2Color">
|
||||
<color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
<systemColor name="systemTealColor">
|
||||
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color red="0.18823529411764706" green="0.69019607843137254" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -42,6 +42,10 @@ typedef NS_ENUM(NSInteger, GyroMode) {
|
||||
AlwaysController
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, FramePacingMode) {
|
||||
FramePacingModeLegacy,
|
||||
FramePacingModeQueue
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, SettingsMenuMode) {
|
||||
AllSettings,
|
||||
@@ -91,6 +95,7 @@ typedef NS_ENUM(NSInteger, SettingsMenuMode) {
|
||||
enableGraphs:(BOOL)enableGraphs
|
||||
graphOpacity:(NSInteger)graphOpacity
|
||||
renderingBackend:(NSInteger)renderingBackend
|
||||
framePacingMode:(NSInteger)framePacingMode
|
||||
backgroundSessionTimer:(NSInteger)backgroundSessionTimer;
|
||||
|
||||
- (NSArray*) getHosts;
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
enableGraphs:(BOOL)enableGraphs
|
||||
graphOpacity:(NSInteger)graphOpacity
|
||||
renderingBackend:(NSInteger)renderingBackend
|
||||
framePacingMode:(NSInteger)framePacingMode
|
||||
backgroundSessionTimer:(NSInteger)backgroundSessionTimer{
|
||||
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
@@ -145,6 +146,7 @@
|
||||
settingsToSave.enableGraphs = enableGraphs;
|
||||
settingsToSave.graphOpacity = [NSNumber numberWithInteger:graphOpacity];
|
||||
settingsToSave.renderingBackend = [NSNumber numberWithInteger:renderingBackend];
|
||||
settingsToSave.framePacingMode = [NSNumber numberWithInteger:framePacingMode];
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
@property (nonatomic, retain) NSNumber * frameQueueSize;
|
||||
@property (nonatomic, retain) NSNumber * graphOpacity;
|
||||
@property (nonatomic, retain) NSNumber * renderingBackend;
|
||||
@property (nonatomic, retain) NSNumber * framePacingMode;
|
||||
|
||||
- (id) initFromSettings:(Settings*)settings;
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
// iOS < 17 must use Balanced mode (AVSB renderer)
|
||||
self.renderingBackend = [NSNumber numberWithInteger:RENDER_AVSB];
|
||||
}
|
||||
self.framePacingMode = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"framePacingMode"]];
|
||||
|
||||
NSInteger _screenSize = [[NSUserDefaults standardUserDefaults] integerForKey:@"streamResolution"];
|
||||
switch (_screenSize) {
|
||||
@@ -129,6 +130,7 @@
|
||||
self.enableGraphs = settings.enableGraphs;
|
||||
self.graphOpacity = settings.graphOpacity;
|
||||
self.renderingBackend = settings.renderingBackend;
|
||||
self.framePacingMode = settings.framePacingMode;
|
||||
self.backgroundSessionTimer = settings.backroundSessionTimer;
|
||||
#endif
|
||||
self.uniqueId = settings.uniqueId;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<attribute name="enableYUV444" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="externalDisplayMode" optional="YES" attributeType="Integer 16" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="frameQueueSize" optional="YES" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="framePacingMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="gyroMode" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="gyroSensitivity" optional="YES" attributeType="Float" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
|
||||
@@ -306,29 +306,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"[ Toggle stats overlay & graphs ]" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切换统计覆盖层和图表 ]"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切換統計覆蓋層和圖表 ]"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切換統計覆蓋層和圖表 ]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"[ On-screen widget tool ]" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -375,6 +352,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"[ Toggle stats overlay & graphs ]" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切换统计覆盖层和图表 ]"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切換統計覆蓋層和圖表 ]"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "[ 切換統計覆蓋層和圖表 ]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"%@ is currently running" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -789,6 +789,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Balanced" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Ball: assumed touch point. Cross: indicator location" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2010,6 +2033,52 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Frames to Buffer" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "缓冲帧数"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Frames to buffer: %ld" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "缓冲帧数:%ld"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數:%ld"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數:%ld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"From the player community, to the player community." : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -2419,6 +2488,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Later" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍后"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍後"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍後"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Launch" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -3393,6 +3485,52 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Performance" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "性能"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Performance Graphs - Opacity: %ld%%" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "性能图表 - 不透明度:%ld%%"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能圖表 - 不透明度:%ld%%"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能圖表 - 不透明度:%ld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Peripherals" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -3745,6 +3883,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Quit Now" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Quit Running App" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -3837,98 +3998,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Quit Now" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "立即退出"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rendering mode change requires app restart" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重启应用以确保干净启动。您是否要立即退出应用?"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重新啟動應用程式以確保乾淨啟動。您是否要立即退出應用程式?"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重新啟動應用程式以確保乾淨啟動。您是否要立即退出應用程式?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Restart Required" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重启"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重新啟動"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重新啟動"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Later" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍后"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍後"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "稍後"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Read Widget Instruction" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -4044,6 +4113,52 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rendering Mode (Experimental)" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(实验性)"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(實驗性)"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(實驗性)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rendering mode change requires app restart" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重启应用以确保干净启动。您是否要立即退出应用?"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重新啟動應用程式以確保乾淨啟動。您是否要立即退出應用程式?"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "更改渲染模式需要重新啟動應用程式以確保乾淨啟動。您是否要立即退出應用程式?"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"renderingBackendStackTip" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -4073,6 +4188,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Restart Required" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重启"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重新啟動"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "需要重新啟動"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Resume App" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -5384,144 +5522,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Rendering Mode (Experimental)" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(实验性)"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(實驗性)"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "渲染模式(實驗性)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Frames to Buffer" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "缓冲帧数"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Frames to buffer: %ld" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "缓冲帧数:%ld"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數:%ld"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "緩衝影格數:%ld"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Performance Graphs - Opacity: %ld%%" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "性能图表 - 不透明度:%ld%%"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能圖表 - 不透明度:%ld%%"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能圖表 - 不透明度:%ld%%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Balanced" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "平衡"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Performance" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "性能"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "效能"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"闽ICP备17012590号-3A\n主办单位:福州创图信息技术有限公司" : {
|
||||
|
||||
}
|
||||
|
||||
@@ -55,6 +55,13 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
NSInteger _maxRefreshRate;
|
||||
RenderingBackend _renderingBackend;
|
||||
|
||||
// Frame pacing mode
|
||||
BOOL _useLegacyPacing;
|
||||
|
||||
// Legacy pacing frame counter and queue management
|
||||
NSInteger _simplePacingFrameCount;
|
||||
NSInteger _enqueuedSampleBuffers;
|
||||
|
||||
}
|
||||
|
||||
- (void)reinitializeDisplayLayer
|
||||
@@ -129,9 +136,15 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
|
||||
_frameQueue = [FrameQueue sharedInstance];
|
||||
[_frameQueue start];
|
||||
[_frameQueue setHighWaterMark:(int)[[dataMan getSettings].frameQueueSize integerValue]];
|
||||
// Check if we should use legacy pacing mode
|
||||
_useLegacyPacing = [[dataMan getSettings].framePacingMode integerValue] == FramePacingModeLegacy;
|
||||
|
||||
if (!_useLegacyPacing) {
|
||||
// Queue pacing mode uses FrameQueue
|
||||
_frameQueue = [FrameQueue sharedInstance];
|
||||
[_frameQueue start];
|
||||
[_frameQueue setHighWaterMark:(int)[[dataMan getSettings].frameQueueSize integerValue]];
|
||||
}
|
||||
|
||||
[self reinitializeDisplayLayer];
|
||||
|
||||
@@ -155,11 +168,17 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
if ([[dataMan getSettings].renderingBackend integerValue] == RENDER_AVSB) {
|
||||
// PACING_MODE_VSYNC:
|
||||
// Deliver 1 frame at each vsync interval. Ignores server pts timestamps.
|
||||
// Drop frames intelligently to maintain chosen queue size.
|
||||
_renderingBackend = RENDER_AVSB;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderModeAVSB:)];
|
||||
|
||||
if (_useLegacyPacing) {
|
||||
// Legacy pacing mode
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderModeLegacy:)];
|
||||
} else {
|
||||
// PACING_MODE_VSYNC:
|
||||
// Deliver 1 frame at each vsync interval. Ignores server pts timestamps.
|
||||
// Drop frames intelligently to maintain chosen queue size.
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderModeAVSB:)];
|
||||
}
|
||||
|
||||
if (@available(iOS 15.0, tvOS 15.0, *)) {
|
||||
_displayLink.preferredFrameRateRange = CAFrameRateRangeMake(self->_frameRate, self->_frameRate, self->_frameRate);
|
||||
@@ -176,7 +195,6 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
|
||||
|
||||
- (void)setupDecompressionSessionWithAttributes:(NSDictionary *)destinationPixelBufferAttributes {
|
||||
// This method is called from within synchronized block, so no additional sync needed here
|
||||
if (_decompressionSession != NULL) {
|
||||
VTDecompressionSessionInvalidate(_decompressionSession);
|
||||
CFRelease(_decompressionSession);
|
||||
@@ -242,7 +260,33 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
|
||||
|
||||
int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
|
||||
#pragma mark DisplayLink - Frame Pacing - Vsync with FrameQueue
|
||||
#pragma mark DisplayLink - Legacy Frame Pacing
|
||||
|
||||
// Legacy frame pacing method from upstream/Integration - direct polling without FrameQueue
|
||||
- (void)renderModeLegacy:(CADisplayLink *)link {
|
||||
VIDEO_FRAME_HANDLE handle;
|
||||
PDECODE_UNIT du;
|
||||
|
||||
while (LiPollNextVideoFrame(&handle, &du)) {
|
||||
LiCompleteVideoFrame(handle, DrSubmitDecodeUnit(du));
|
||||
|
||||
// Calculate the actual display refresh rate
|
||||
double displayRefreshRate = 1 / (link.targetTimestamp - link.timestamp);
|
||||
|
||||
// Only pace frames if the display refresh rate is >= 90% of our stream frame rate.
|
||||
// Battery saver, accessibility settings, or device thermals can cause the actual
|
||||
// refresh rate of the display to drop below the physical maximum.
|
||||
if (displayRefreshRate >= _frameRate * 0.9f) {
|
||||
// Keep one pending frame to smooth out gaps due to
|
||||
// network jitter at the cost of 1 frame of latency
|
||||
if (LiGetPendingVideoFrames() == 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark DisplayLink - Frame Pacing - Vsync with FrameQueue (Queue Pacing)
|
||||
|
||||
// This frame pacing method was inspired by the behavior of moonlight-qt's Pacer class, although it has evolved
|
||||
// a few additional features. Incoming frames from Sunshine are asynchronously processed into a queue by the VideoRecv thread.
|
||||
@@ -294,9 +338,11 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
// we missed a callback
|
||||
// Log(LOG_W, @"*** slow frametime %.3f ms", frametime * 1000.0);
|
||||
}
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:frametime * 1000.0];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:frametime * 1000.0];
|
||||
}
|
||||
});
|
||||
}
|
||||
lastTargetLocal = targetLocal;
|
||||
|
||||
@@ -364,7 +410,9 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
}
|
||||
- (void)cleanup
|
||||
{
|
||||
[_frameQueue stop];
|
||||
if (_frameQueue) {
|
||||
[_frameQueue stop];
|
||||
}
|
||||
|
||||
if (_renderingBackend == RENDER_AVSB) {
|
||||
[_displayLink invalidate];
|
||||
@@ -925,34 +973,82 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
|
||||
// Dispatch onto our higher priority queue
|
||||
dispatch_async(self->_vtq, ^{
|
||||
Frame *frame = nil;
|
||||
if (self->_renderingBackend == RENDER_AVSB) {
|
||||
frame = [[Frame alloc] initWithSampleBuffer:sampleBufferOut frameNumber:frameNumber frameType:frameType];
|
||||
if (self->_useLegacyPacing && self->_renderingBackend == RENDER_AVSB) {
|
||||
// Legacy pacing: Process all frames normally, but with immediate display timing
|
||||
|
||||
// Set presentation time to current time for immediate display
|
||||
CMTime targetTime = CMTimeMakeWithSeconds(CACurrentMediaTime(), NSEC_PER_SEC);
|
||||
CMSampleBufferSetOutputPresentationTimeStamp(sampleBufferOut, targetTime);
|
||||
|
||||
// Dispatch display layer operations to main thread to avoid CATransaction warnings
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (frameType == FRAME_TYPE_IDR) {
|
||||
// Ensure the layer is visible now
|
||||
self->_displayLayer.hidden = NO;
|
||||
|
||||
// Tell our parent VC to hide the progress indicator
|
||||
[self->_callbacks videoContentShown];
|
||||
}
|
||||
|
||||
if ([self->_displayLayer controlTimebase] == NULL) {
|
||||
// On first frame, set timebase
|
||||
CMTimebaseRef timebase = NULL;
|
||||
CMTimebaseCreateWithSourceClock(CFAllocatorGetDefault(), CMClockGetHostTimeClock(), &timebase);
|
||||
|
||||
CMTime pts = CMSampleBufferGetOutputPresentationTimeStamp(sampleBufferOut);
|
||||
CMTimebaseSetTime(timebase, pts);
|
||||
CMTimebaseSetRate(timebase, 1.0);
|
||||
|
||||
[self->_displayLayer setControlTimebase:timebase];
|
||||
if (timebase) {
|
||||
CFRelease(timebase);
|
||||
}
|
||||
Log(LOG_I, @"Setting timebase for legacy pacing to %d / %d", pts.value, pts.timescale);
|
||||
}
|
||||
|
||||
[self->_displayLayer enqueueSampleBuffer:sampleBufferOut];
|
||||
CFRelease(sampleBufferOut);
|
||||
});
|
||||
} else {
|
||||
frame = [[Frame alloc] initWithPixelBufffer:pixelBuffer frameNumber:frameNumber frameType:frameType pts:presentationTimestamp];
|
||||
[frame setFormatDesc:self->_formatDesc];
|
||||
}
|
||||
int framesDropped = [self->_frameQueue enqueue:frame withSlackSize:3];
|
||||
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
static PlotMetrics frameQueueMetrics = {};
|
||||
[[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_QUEUED_FRAMES value:[self->_frameQueue count] plotMetrics:&frameQueueMetrics];
|
||||
[self safeCopyMetricsTo:&self->_frameQueueMetrics from:&frameQueueMetrics];
|
||||
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_DROPPED value:framesDropped];
|
||||
|
||||
// It's important we capture host metrics on the incoming thread, as this frame object
|
||||
// may have been dropped by the above enqueue
|
||||
static CFTimeInterval lastHostFrame = 0.0f;
|
||||
if (lastHostFrame != 0) {
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_HOST_FRAMETIME value:(frame.pts - lastHostFrame) * 1000.0];
|
||||
// Queue pacing: use FrameQueue
|
||||
Frame *frame = nil;
|
||||
if (self->_renderingBackend == RENDER_AVSB) {
|
||||
frame = [[Frame alloc] initWithSampleBuffer:sampleBufferOut frameNumber:frameNumber frameType:frameType];
|
||||
} else {
|
||||
frame = [[Frame alloc] initWithPixelBufffer:pixelBuffer frameNumber:frameNumber frameType:frameType pts:presentationTimestamp];
|
||||
[frame setFormatDesc:self->_formatDesc];
|
||||
}
|
||||
lastHostFrame = frame.pts;
|
||||
int framesDropped = [self->_frameQueue enqueue:frame withSlackSize:3];
|
||||
|
||||
// Decode time is not graphed because it is marked as hidden, but we can use the same mechanism for the value used by stats
|
||||
static PlotMetrics decodeMetrics = {};
|
||||
[[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_DECODE value:(CACurrentMediaTime() - decodeStartTime) * 1000.0 plotMetrics:&decodeMetrics];
|
||||
[self safeCopyMetricsTo:&self->_decodeMetrics from:&decodeMetrics];
|
||||
// Capture metrics on main thread to avoid UIApplication thread warning
|
||||
int frameQueueCount = [self->_frameQueue count];
|
||||
CFTimeInterval capturedDecodeTime = CACurrentMediaTime() - decodeStartTime;
|
||||
CFTimeInterval hostFramePts = frame.pts;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
|
||||
static PlotMetrics frameQueueMetrics = {};
|
||||
[[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_QUEUED_FRAMES value:frameQueueCount plotMetrics:&frameQueueMetrics];
|
||||
[self safeCopyMetricsTo:&self->_frameQueueMetrics from:&frameQueueMetrics];
|
||||
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_DROPPED value:framesDropped];
|
||||
|
||||
// It's important we capture host metrics on the incoming thread, as this frame object
|
||||
// may have been dropped by the above enqueue
|
||||
static CFTimeInterval lastHostFrame = 0.0f;
|
||||
if (lastHostFrame != 0) {
|
||||
[[ImGuiPlots sharedInstance] observeFloat:PLOT_HOST_FRAMETIME value:(hostFramePts - lastHostFrame) * 1000.0];
|
||||
}
|
||||
lastHostFrame = hostFramePts;
|
||||
|
||||
// Decode time is not graphed because it is marked as hidden, but we can use the same mechanism for the value used by stats
|
||||
static PlotMetrics decodeMetrics = {};
|
||||
[[ImGuiPlots sharedInstance] observeFloatReturnMetrics:PLOT_DECODE
|
||||
value:capturedDecodeTime * 1000.0
|
||||
plotMetrics:&decodeMetrics];
|
||||
[self safeCopyMetricsTo:&self->_decodeMetrics from:&decodeMetrics];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1051,13 +1147,16 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
if (_renderingBackend == RENDER_METAL) {
|
||||
stats->renderingBackendString = [NSString stringWithFormat:@"Metal, colorspace: %@", [MetalVideoRenderer currentColorSpace]];
|
||||
} else {
|
||||
stats->renderingBackendString = @"AVSampleBuffer";
|
||||
NSString *pacingMode = _useLegacyPacing ? @"Legacy" : @"Queue";
|
||||
stats->renderingBackendString = [NSString stringWithFormat:@"AVSampleBuffer (%@ pacing)", pacingMode];
|
||||
}
|
||||
|
||||
dispatch_sync(_sq, ^{
|
||||
memcpy(&stats->decodeMetrics, &_decodeMetrics, sizeof(PlotMetrics));
|
||||
memcpy(&stats->frameQueueMetrics, &_frameQueueMetrics, sizeof(PlotMetrics));
|
||||
[_frameQueue.frameDropMetrics copyMetrics:&stats->frameDropMetrics];
|
||||
if (_frameQueue) {
|
||||
memcpy(&stats->frameQueueMetrics, &_frameQueueMetrics, sizeof(PlotMetrics));
|
||||
[_frameQueue.frameDropMetrics copyMetrics:&stats->frameDropMetrics];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1069,7 +1168,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||
static int lastTargetRate = 0;
|
||||
int targetRate = (int)_maxRefreshRate;
|
||||
|
||||
if (_maxRefreshRate <= 60 || _maxRefreshRate == 90) {
|
||||
if (_maxRefreshRate <= 60 || _maxRefreshRate == 90 || !_frameQueue) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
typedef enum {
|
||||
PACING_MODE_VSYNC,
|
||||
PACING_MODE_PTS
|
||||
} FramePacingMode;
|
||||
|
||||
typedef enum {
|
||||
PLOT_FRAMETIME = 0,
|
||||
PLOT_HOST_FRAMETIME,
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#import "IdManager.h"
|
||||
#import "ConnectionHelper.h"
|
||||
#import "LocalizationHelper.h"
|
||||
#import "Plot.h"
|
||||
#import "CustomEdgeSlideGestureRecognizer.h"
|
||||
#import "DataManager.h"
|
||||
#import "ThemeManager.h"
|
||||
@@ -1180,6 +1181,9 @@ static NSMutableSet* hostList;
|
||||
[settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.audioConfigStack];
|
||||
[settingsViewController setHidden:_settingsExpandedInStreamView forStack:settingsViewController.pipStack];
|
||||
[settingsViewController.renderingBackendSelector setEnabled:!_settingsExpandedInStreamView];
|
||||
// Enable frame pacing mode selector only if not in stream view AND not in performance mode
|
||||
BOOL shouldEnableFramePacing = !_settingsExpandedInStreamView && (settingsViewController.renderingBackendSelector.selectedSegmentIndex != RENDER_METAL);
|
||||
[settingsViewController.framePacingModeSelector setEnabled:shouldEnableFramePacing];
|
||||
}
|
||||
|
||||
- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position {
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *externalDisplayModeStack;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *localMousePointerModeStack;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *renderingBackendStack;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *framePacingStack;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *frameQueueSizeStack;
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *performanceGraphStack;
|
||||
|
||||
@@ -115,6 +116,7 @@
|
||||
@property (strong, nonatomic) IBOutlet UISwitch *enableGraphsSwitch;
|
||||
@property (strong, nonatomic) IBOutlet UIStepper *graphOpacityStepper;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *renderingBackendSelector;
|
||||
@property (strong, nonatomic) IBOutlet UISegmentedControl *framePacingModeSelector;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIStackView *backgroundSessionTimerStack;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *backgroundSessionTimerSlider;
|
||||
|
||||
@@ -560,6 +560,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
|
||||
[self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
|
||||
[self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" withInfoTag:NO withDynamicLabel:NO to:videoSection];
|
||||
[self addSetting:self.framePacingStack ofId:@"framePacingStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
|
||||
|
||||
// Only show Metal renderer option on iOS 17+ where CAMetalDisplayLink is available
|
||||
if (@available(iOS 17.0, *)) {
|
||||
@@ -1431,7 +1432,13 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
NSInteger renderingBackend = [currentSettings.renderingBackend integerValue];
|
||||
[self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend];
|
||||
[self.renderingBackendSelector addTarget:self action:@selector(renderingBackendChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP state based on current selection
|
||||
|
||||
NSInteger framePacingMode = [currentSettings.framePacingMode integerValue];
|
||||
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
|
||||
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
|
||||
[self framePacingModeChanged:self.framePacingModeSelector];
|
||||
|
||||
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP and frame pacing state based on current selection
|
||||
|
||||
[self.citrixX1MouseSwitch setOn:currentSettings.btMouseSupport];
|
||||
[self.optimizeGamesSwitch setOn: currentSettings.optimizeGames];
|
||||
@@ -1665,6 +1672,24 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
}
|
||||
|
||||
- (void)renderingBackendChanged:(UISegmentedControl *)sender {
|
||||
// Disable PiP toggle when Metal renderer is selected
|
||||
if (sender.selectedSegmentIndex == RENDER_METAL) {
|
||||
// Performance mode (Metal renderer) selected - disable PiP
|
||||
[self.pipSwitch setOn:NO animated:YES];
|
||||
[self.pipSwitch setEnabled:NO];
|
||||
// Set pacing method to Queue and disable selector
|
||||
[self.framePacingModeSelector setSelectedSegmentIndex:FramePacingModeQueue];
|
||||
[self.framePacingModeSelector setEnabled:NO];
|
||||
} else {
|
||||
// Balanced mode (AVSB renderer) - enable PiP toggle if iOS 15+
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[self.pipSwitch setEnabled:YES];
|
||||
} else {
|
||||
[self.pipSwitch setOn:NO];
|
||||
[self.pipSwitch setEnabled:NO];
|
||||
}
|
||||
[self.framePacingModeSelector setEnabled:YES];
|
||||
}
|
||||
|
||||
// Get the current settings to compare with the new selection
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
@@ -1706,19 +1731,22 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
[alertController addAction:quitAction];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
// Disable PiP toggle when Metal renderer is selected
|
||||
if (sender.selectedSegmentIndex == RENDER_METAL) {
|
||||
// Performance mode (Metal renderer) selected - disable PiP
|
||||
[self.pipSwitch setOn:NO animated:YES];
|
||||
[self.pipSwitch setEnabled:NO];
|
||||
}
|
||||
|
||||
- (void)framePacingModeChanged:(UISegmentedControl *)sender {
|
||||
|
||||
if (sender.selectedSegmentIndex == FramePacingModeLegacy) {
|
||||
// Legacy mode selected - disable frames to buffer and graph settings
|
||||
[self.frameQueueSizeSlider setEnabled:NO];
|
||||
[self.enableGraphsSwitch setOn:NO animated:YES];
|
||||
[self.enableGraphsSwitch setEnabled:NO];
|
||||
[self.graphOpacityStepper setEnabled:NO];
|
||||
} else {
|
||||
// Balanced mode (AVSB renderer) - enable PiP toggle if iOS 15+
|
||||
if (@available(iOS 15.0, *)) {
|
||||
[self.pipSwitch setEnabled:YES];
|
||||
} else {
|
||||
[self.pipSwitch setOn:NO];
|
||||
[self.pipSwitch setEnabled:NO];
|
||||
// Queue mode selected - enable frames to buffer and graph settings
|
||||
[self.frameQueueSizeSlider setEnabled:YES];
|
||||
[self.enableGraphsSwitch setEnabled:YES];
|
||||
if (self.enableGraphsSwitch.isOn) {
|
||||
[self.graphOpacityStepper setEnabled:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2265,6 +2293,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
|
||||
NSInteger audioConfig = [@[@2, @6, @8][[self.audioConfigSelector selectedSegmentIndex]] integerValue];
|
||||
NSInteger renderingBackend = [self.renderingBackendSelector selectedSegmentIndex];
|
||||
NSInteger framePacingMode = [self.framePacingModeSelector selectedSegmentIndex];
|
||||
NSInteger onscreenControls = [self.onScreenWidgetSelector selectedSegmentIndex];
|
||||
NSInteger keyboardToggleFingers = self.softKeyboardGestureSelector.selectedSegmentIndex == 3 ? 20 : self.softKeyboardGestureSelector.selectedSegmentIndex+3;
|
||||
NSInteger oscLayoutToolFingers = (uint16_t)self->oswLayoutFingers;
|
||||
@@ -2348,6 +2377,7 @@ BOOL isCustomResolution(int resolutionSelected) {
|
||||
enableGraphs:enableGraphs
|
||||
graphOpacity:_graphOpacity
|
||||
renderingBackend:renderingBackend
|
||||
framePacingMode:framePacingMode
|
||||
backgroundSessionTimer:backgroundSessionTimer];
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,66 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"2Dp-Qg-chi.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; 2Dp-Qg-chi.segmentTitles[0] = \"Legacy\"; ObjectID = \"2Dp-Qg-chi\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Legacy"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "旧版"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "舊版"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "舊版"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2Dp-Qg-chi.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; 2Dp-Qg-chi.segmentTitles[1] = \"Queue\"; ObjectID = \"2Dp-Qg-chi\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Queue"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2w1-Vw-GVy.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"HDR\"; ObjectID = \"2w1-Vw-GVy\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -2311,6 +2371,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"MaN-f7-I3g.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Frame Pacing Mode\"; ObjectID = \"MaN-f7-I3g\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Frame Pacing Mode"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "帧缓冲模式"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "幀緩衝模式"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "幀緩衝模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mm0-xf-9eh.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; Mm0-xf-9eh.segmentTitles[0] = \"Slide From Left Edge\"; ObjectID = \"Mm0-xf-9eh\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
@@ -1087,6 +1087,66 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CC6-pu-Uoh.segmentTitles[0]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; CC6-pu-Uoh.segmentTitles[0] = \"Legacy\"; ObjectID = \"CC6-pu-Uoh\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Legacy"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "旧版"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "舊版"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "舊版"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CC6-pu-Uoh.segmentTitles[1]" : {
|
||||
"comment" : "Class = \"UISegmentedControl\"; CC6-pu-Uoh.segmentTitles[1] = \"Queue\"; ObjectID = \"CC6-pu-Uoh\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Queue"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "佇列"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ci6-ak-aCa.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Custom Resolution\"; ObjectID = \"ci6-ak-aCa\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
@@ -1177,6 +1237,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Cvx-kQ-L3H.text" : {
|
||||
"comment" : "Class = \"UILabel\"; text = \"Frame Pacing Mode\"; ObjectID = \"Cvx-kQ-L3H\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Frame Pacing Mode"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "帧缓冲模式"
|
||||
}
|
||||
},
|
||||
"zh-Hant" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "幀緩衝模式"
|
||||
}
|
||||
},
|
||||
"zh-HK" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "幀緩衝模式"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"D9X-7p-9oc.title" : {
|
||||
"comment" : "Class = \"UIBarButtonItem\"; title = \"ExportAll\"; ObjectID = \"D9X-7p-9oc\";",
|
||||
"extractionState" : "extracted_with_value",
|
||||
|
||||
Reference in New Issue
Block a user