- reduce pressure curve tool tips

- fixed crash in pressure curve tool drawing stage
- initial pencil move force tweak
- frame queue tweaks demo
This commit is contained in:
True Zhuanjia
2026-02-06 15:27:29 +08:00
parent fa32d157ca
commit fa164f0e02
20 changed files with 445 additions and 107 deletions
+7 -1
View File
@@ -21,6 +21,7 @@ import UIKit
@objc public static var actionCancelled:Bool = false
@objc public static var alertController:UIAlertController = UIAlertController()
@objc public static var cancelButtonString:String = "Cancel"
@objc public static var autoCompletion:Bool = false
@objc class func showAlert(
in viewController: UIViewController,
@@ -51,7 +52,7 @@ import UIKit
confirmAction.isEnabled = false
if withCancel {alertController.addAction(cancelAction)}
if buttonTitle != "" {alertController.addAction(confirmAction)}
if buttonTitle != "" && !autoCompletion {alertController.addAction(confirmAction)}
if(countdown == 0) {
confirmAction.setValue(buttonTitle, forKey: "title")
@@ -72,6 +73,11 @@ import UIKit
timer.cancel()
confirmAction.isEnabled = true
confirmAction.setValue(buttonTitle, forKey: "title")
if autoCompletion {
autoCompletion = false
alertController.dismiss(animated: false)
return
}
} else {
confirmAction.setValue("\(remainingSeconds)", forKey: "title")
}
+49 -5
View File
@@ -1782,7 +1782,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="v6i-HA-XRx">
<rect key="frame" x="440" y="1405" width="380" height="56.5"/>
<rect key="frame" x="440" y="1545" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Switch Gyro by Controller" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="26O-xW-5EH">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
@@ -1990,6 +1990,46 @@
<constraint firstAttribute="height" constant="31" id="pV0-m0-1pz"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="A31-WC-As7">
<rect key="frame" x="441" y="1363" width="280" height="31"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable Timebase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cpg-mq-kYj" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="StG-nI-WpK">
<rect key="frame" x="150" y="0.0" width="132" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="130" id="lFV-eP-bfD"/>
</constraints>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="31" id="seO-0F-yIw"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="e0Q-XV-ydj">
<rect key="frame" x="441" y="1402" width="260" height="31"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Async Deueue" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="X0u-7f-F86" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="110" height="31"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="auR-cw-77D">
<rect key="frame" x="130" y="0.0" width="132" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="130" id="mZn-6A-4OO"/>
</constraints>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="31" id="UK8-ck-1zc"/>
</constraints>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
@@ -1997,6 +2037,8 @@
<connections>
<outlet property="appThemeSelector" destination="wi6-L4-KwS" id="mf9-YY-eyE"/>
<outlet property="appThemeStack" destination="Wf6-BA-Mdv" id="hlq-bB-S8N"/>
<outlet property="asyncFrameDequeueStack" destination="e0Q-XV-ydj" id="7TK-zc-BqI"/>
<outlet property="asyncFrameDequeueSwitch" destination="auR-cw-77D" id="SYx-KY-cqs"/>
<outlet property="audioConfigSelector" destination="iZL-dA-jk9" id="Yja-os-o6g"/>
<outlet property="audioConfigStack" destination="qnG-5F-2YY" id="kFX-VK-vuK"/>
<outlet property="audioEngineSelector" destination="85A-d5-pLE" id="Ads-EZ-Bpm"/>
@@ -2046,6 +2088,8 @@
<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="82B-r7-Zbh"/>
<outlet property="frameTimebaseStack" destination="A31-WC-As7" id="vcp-SE-PuI"/>
<outlet property="frameTimebaseSwitch" destination="StG-nI-WpK" id="yYY-DO-U1D"/>
<outlet property="framerateSelector" destination="8hF-kb-2uV" id="dfT-hb-csE"/>
<outlet property="fullColorRangeStack" destination="iPd-Rg-St7" id="iPd-Out-Let"/>
<outlet property="fullColorRangeSwitch" destination="iPd-Wt-Ch9" id="iPd-Out-Swt"/>
@@ -3106,7 +3150,7 @@
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
<image name="video.and.waveform" catalog="system" width="128" height="94"/>
<systemColor name="linkColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="secondarySystemGroupedBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -3115,13 +3159,13 @@
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemBrownColor">
<color red="0.67450980392156867" green="0.49803921568627452" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.63529411759999999" green="0.51764705879999995" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemGray3Color">
<color red="0.7803921568627451" green="0.7803921568627451" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.78039215689999997" green="0.78039215689999997" blue="0.80000000000000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemTealColor">
<color red="0.0" green="0.76470588235294112" blue="0.81568627450980391" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.18823529410000001" green="0.69019607839999997" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>
+56 -12
View File
@@ -1931,7 +1931,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="IHx-FO-ctr" userLabel="frameQueueSize Stack">
<rect key="frame" x="437" y="1973" width="380" height="89.5"/>
<rect key="frame" x="456" y="2063" width="380" height="89.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Frames to Buffer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="r8X-jt-JfH">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
@@ -2020,7 +2020,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" 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"/>
<rect key="frame" x="456" y="2171" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Frame Pacing" 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"/>
@@ -2344,7 +2344,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="qaV-ZR-fo7">
<rect key="frame" x="437" y="2464" width="380" height="55.5"/>
<rect key="frame" x="459" y="2763" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Slide Distance Threshold" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bsU-vw-dTl" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="380" height="16.5"/>
@@ -2364,7 +2364,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="kiR-q0-FQX" userLabel="hapticEngineStack">
<rect key="frame" x="437" y="2159" width="380" height="56.5"/>
<rect key="frame" x="459" y="2458" width="380" height="56.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Haptic Engine" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ldr-kv-d7n" userLabel="hapticEngine">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
@@ -2390,7 +2390,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="Wpg-TL-KJy" userLabel="appThemeStack">
<rect key="frame" x="437" y="2241" width="380" height="56.5"/>
<rect key="frame" x="459" y="2540" width="380" height="56.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Theme" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yF6-p8-b2s">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
@@ -2435,7 +2435,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="N0i-z6-WgC" userLabel="singleTapSensitivityStack">
<rect key="frame" x="437" y="2383" width="380" height="55.5"/>
<rect key="frame" x="459" y="2682" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Single Tap Sensitivity" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WzD-wo-Wum">
<rect key="frame" x="0.0" y="0.0" width="380" height="20.5"/>
@@ -2599,7 +2599,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="cNd-hI-JAS" userLabel="touchModeStack2">
<rect key="frame" x="437" y="2604" width="380" height="31"/>
<rect key="frame" x="459" y="2903" width="380" height="31"/>
<subviews>
<segmentedControl verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" apportionsSegmentWidthsByContent="YES" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="n8C-pt-nPE" userLabel="Touch Mode Selector">
<rect key="frame" x="0.0" y="0.0" width="380" height="32"/>
@@ -2682,7 +2682,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="xyO-r6-abA">
<rect key="frame" x="434" y="2708" width="300" height="31"/>
<rect key="frame" x="456" y="3007" width="300" height="31"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Controller → Mouse" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F0O-Ht-fW5" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="150" height="31"/>
@@ -2702,7 +2702,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="QuI-NM-qCU">
<rect key="frame" x="437" y="2838" width="380" height="55.5"/>
<rect key="frame" x="459" y="3137" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Curve Exponent" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AEN-EU-8LZ" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="380" height="16.5"/>
@@ -2722,7 +2722,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="jxy-UY-jFk">
<rect key="frame" x="437" y="2763" width="380" height="55.5"/>
<rect key="frame" x="459" y="3062" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Pointer Velocity" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="H3s-ud-yqh" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="380" height="16.5"/>
@@ -2807,7 +2807,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="Twt-JJ-ihg">
<rect key="frame" x="437" y="2919" width="380" height="55.5"/>
<rect key="frame" x="459" y="3218" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Left Stick Minimum Offset" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="whI-oR-V8o" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="380" height="16.5"/>
@@ -2827,7 +2827,7 @@
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="aQs-Ww-sPZ">
<rect key="frame" x="437" y="2993" width="380" height="55.5"/>
<rect key="frame" x="459" y="3292" width="380" height="55.5"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Right Stick Minimum Offset" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="q6s-4H-y9n" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="380" height="16.5"/>
@@ -2846,6 +2846,46 @@
<constraint firstAttribute="width" constant="380" id="tSR-LD-qKK"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="HCc-SK-NBk">
<rect key="frame" x="459" y="2252" width="280" height="31"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable Timebase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="P4N-i6-1zD" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="130" height="31"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bwf-wJ-EC2">
<rect key="frame" x="150" y="0.0" width="132" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="130" id="KUb-Mq-m9s"/>
</constraints>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="31" id="qzI-PN-1ml"/>
</constraints>
</stackView>
<stackView verifyAmbiguity="off" opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="dZ3-os-MKX">
<rect key="frame" x="459" y="2291" width="260" height="31"/>
<subviews>
<label verifyAmbiguity="off" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Async Deueue" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.64999997615814209" adjustsLetterSpacingToFitWidth="YES" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e1n-pw-04X" userLabel="uiLabel">
<rect key="frame" x="0.0" y="0.0" width="110" height="31"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="19e-9S-OgQ">
<rect key="frame" x="130" y="0.0" width="132" height="31"/>
<constraints>
<constraint firstAttribute="width" constant="130" id="hFO-BO-ck5"/>
</constraints>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="31" id="O8n-y9-TrQ"/>
</constraints>
</stackView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
@@ -2853,6 +2893,8 @@
<connections>
<outlet property="appThemeSelector" destination="pNz-vd-Sr9" id="nYB-7o-wkT"/>
<outlet property="appThemeStack" destination="Wpg-TL-KJy" id="Vhd-oB-Sy3"/>
<outlet property="asyncFrameDequeueStack" destination="dZ3-os-MKX" id="jfS-Q7-GAV"/>
<outlet property="asyncFrameDequeueSwitch" destination="19e-9S-OgQ" id="lgy-q2-16O"/>
<outlet property="audioConfigSelector" destination="hfR-K8-Ti4" id="4La-bI-jLq"/>
<outlet property="audioConfigStack" destination="Rld-CY-qzs" id="dmf-m8-sor"/>
<outlet property="audioEngineSelector" destination="pVq-EW-Dta" id="fcu-q8-UNI"/>
@@ -2900,6 +2942,8 @@
<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"/>
<outlet property="frameTimebaseStack" destination="HCc-SK-NBk" id="xts-bc-f0C"/>
<outlet property="frameTimebaseSwitch" destination="bwf-wJ-EC2" id="fwl-GA-bQ6"/>
<outlet property="framerateSelector" destination="dAN-Gy-Eg7" id="tMv-Va-PhO"/>
<outlet property="fullColorRangeStack" destination="fRg-Rn-St7" id="fRg-Out-Let"/>
<outlet property="fullColorRangeSwitch" destination="fRs-Wt-Ch9" id="fRs-Out-Let"/>
+2
View File
@@ -154,6 +154,8 @@ typedef NS_ENUM(NSInteger, PencilTickMode) {
controllerMousePointerVelocity:(CGFloat)controllerMousePointerVelocity
controllerMouseExpo:(CGFloat)controllerMouseExpo
controllerGyroSwitchMode:(NSInteger)controllerGyroSwitchMode
enableFrameTimebase:(BOOL)enableFrameTimebase
asyncFrameDequeue:(BOOL)asyncFrameDequeue
backgroundSessionTimer:(NSInteger)backgroundSessionTimer;
- (NSArray*) getHosts;
+4
View File
@@ -126,6 +126,8 @@
controllerMousePointerVelocity:(CGFloat)controllerMousePointerVelocity
controllerMouseExpo:(CGFloat)controllerMouseExpo
controllerGyroSwitchMode:(NSInteger)controllerGyroSwitchMode
enableFrameTimebase:(BOOL)enableFrameTimebase
asyncFrameDequeue:(BOOL)asyncFrameDequeue
backgroundSessionTimer:(NSInteger)backgroundSessionTimer{
__block Settings* settingsToSave = settings;
@@ -178,6 +180,8 @@
settingsToSave.backroundSessionTimer = [NSNumber numberWithInteger:backgroundSessionTimer];
settingsToSave.frameQueueSize = [NSNumber numberWithInteger:frameQueueSize];
settingsToSave.enableFrameTimebase = enableFrameTimebase;
settingsToSave.asyncFrameDequeue = asyncFrameDequeue;
settingsToSave.enableGraphs = enableGraphs;
settingsToSave.graphOpacity = [NSNumber numberWithInteger:graphOpacity];
settingsToSave.renderingBackend = [NSNumber numberWithInteger:renderingBackend];
+2
View File
@@ -74,6 +74,8 @@
@property (nonatomic) BOOL showKeyboardToolbar;
@property (nonatomic) BOOL unlockDisplayOrientation;
@property (nonatomic) BOOL enableGraphs;
@property (nonatomic) BOOL enableFrameTimebase;
@property (nonatomic) BOOL asyncFrameDequeue;
@property (nonatomic, retain) NSNumber * frameQueueSize;
@property (nonatomic, retain) NSNumber * graphOpacity;
@property (nonatomic, retain) NSNumber * renderingBackend;
+2
View File
@@ -91,6 +91,8 @@
self.enablePIP = settings.enablePIP;
self.fullColorRange = settings.fullColorRange;
self.frameQueueSize = settings.frameQueueSize;
self.enableFrameTimebase = settings.enableFrameTimebase;
self.asyncFrameDequeue = settings.asyncFrameDequeue;
self.playAudioOnPC = settings.playAudioOnPC;
self.redirectMic = settings.redirectMic;
self.useBuiltinMic = settings.useBuiltinMic;
+14 -3
View File
@@ -24,6 +24,10 @@ import UIKit
private var manualHoverFlag: Bool = false
private var autoHoverFlag: Bool = false
private(set) var pencilProEnabled: Bool = false
private var isFirstMove: Bool = false
private var moveEventIndex: Int64 = 0
private var initialMoveEventIndexLimit: Int64
private var touchBeganForce: Float = 0
@objc static private(set) var isDrawing: Bool = false
@objc static private(set) var pencilPausesNativeTouch: Bool = false
@@ -38,6 +42,7 @@ import UIKit
tickInterval = TimeInterval(settings.pencilTickIntervalUs.floatValue/1000000)
manualTick = settings.pencilTickMode.intValue == PencilTickMode.ManualTick.rawValue
pencilTickEnabled = settings.pencilTickMode.intValue != PencilTickMode.PencilTickDisabled.rawValue
initialMoveEventIndexLimit = UIScreen.main.maximumFramesPerSecond > 60 ? 4 : 2
super.init()
setupPressureLUT()
PencilHandler.shared = self
@@ -83,6 +88,8 @@ import UIKit
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let event = event else { return }
PencilHandler.isDrawing = true
// isFirstMove = true
moveEventIndex = 0
for touch in touches {
let coalesced = event.coalescedTouches(for: touch) ?? []
_ = self.sendStylusEvent(touchBatch: pencilTickEnabled ? coalesced : [touch])
@@ -95,6 +102,7 @@ import UIKit
let coalesced = event.coalescedTouches(for: touch) ?? []
_ = self.sendStylusEvent(touchBatch: pencilTickEnabled ? coalesced : [touch])
}
moveEventIndex += 1
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
@@ -173,22 +181,24 @@ import UIKit
var dispatchMoment: DispatchTime = .now()
for touch in touchBatch {
let point = touch.location(in: streamView)
let point = touch.preciseLocation(in: streamView)
let azimuth = touch.azimuthAngle(in: streamView)
let altitude = touch.altitudeAngle
let location = self.adjustCoordinatesForVideoArea(point: point)
let videoSize = self.getVideoAreaSize()
let normalizedLocation = CGPoint(x: location.x/videoSize.width, y: location.y/videoSize.height)
let force = Float(touch.force/touch.maximumPossibleForce)/sin(Float(altitude))
let targetForce = pressureCurveEnabled ? self.pressureLUT.value(at: force) : force
var targetForce = pressureCurveEnabled ? self.pressureLUT.value(at: force) : force
let eventType:UInt8
switch touch.phase {
case .began:
eventType = UInt8(LI_TOUCH_EVENT_DOWN)
touchBeganForce = targetForce
case .moved:
eventType = UInt8(manualHoverFlag ? LI_TOUCH_EVENT_HOVER : LI_TOUCH_EVENT_MOVE)
targetForce = moveEventIndex < initialMoveEventIndexLimit ? touchBeganForce : targetForce
case .ended:
eventType = UInt8(LI_TOUCH_EVENT_UP)
case .cancelled:
@@ -209,8 +219,9 @@ import UIKit
tickMoment += (manualTick ? tickInterval : touch.timestamp - previousTimeStamp)
}
let sendableForce = targetForce
DispatchQueue.global().asyncAfter(deadline: dispatchMoment + tickMoment + delay) {
LiSendPenEvent(eventType, UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), targetForce, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
LiSendPenEvent(eventType, UInt8(LI_TOOL_TYPE_PEN), 0, Float(normalizedLocation.x), Float(normalizedLocation.y), sendableForce, 0, 0, self.getRotation(fromAzimuthAngle: Float(azimuth)), self.getTilt(fromAltitudeAngle: Float(altitude)))
if eventType == UInt8(LI_TOUCH_EVENT_UP) {
PencilHandler.isDrawing = false
}
+52 -18
View File
@@ -412,12 +412,13 @@ final class PressureCurveView: UIView {
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if testStage == .drawingStage {
if pressures.isEmpty { return }
curve.polylinePoints = PressureCurve.generateLinearCurvePoints(from: pressures)
testStage = .curveStage
showGraph = true
let parentVC = parentViewController as! PressureCurveViewController
parentVC.setupNavigationBar()
parentVC.showCurveStageTips()
// parentVC.showCurveStageTips()
}
draggingIndex = nil
}
@@ -479,6 +480,18 @@ class PressureCurveViewController: UIViewController {
private var minPressure: CGFloat = 0
private var maxPressure: CGFloat = 1
private var isFirstLaunch: Bool = {
let key = "hasLaunchedPressureCurveTool"
let defaults = UserDefaults.standard
var launchedBefore = defaults.bool(forKey: key)
if !launchedBefore {
defaults.set(true, forKey: key)
return true
}
return false
}()
override func viewDidLoad() {
super.viewDidLoad()
@@ -532,6 +545,7 @@ class PressureCurveViewController: UIViewController {
}
override func viewDidAppear(_ animated: Bool) {
/*
AlertControllerUtil.cancelButtonString = SwiftLocalizationHelper.localizedString(forKey: "No")
AlertControllerUtil.showAlert(
in: self,
@@ -553,14 +567,16 @@ class PressureCurveViewController: UIViewController {
self.pressureRangeTest()
}
}
)
) */
if isFirstLaunch {
showCurveStageTips()
}
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self)
}
private func loadPersistedCurve() {
let oscProfileMan = OSCProfilesManager.sharedManager(CGRectZero)
let persistedCurvePoints = PressureCurve.importCurvePoints(oscProfileMan.getSelectedProfile().pressureCurvePoints)
@@ -625,8 +641,8 @@ class PressureCurveViewController: UIViewController {
title: SwiftLocalizationHelper.localizedString(forKey: "Pen Pressure Curve"),
message: SwiftLocalizationHelper.localizedString(forKey:"Adjust the pressure curve by adding or dragging the purple squares."),
withCancel: false,
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "OK"),
countdown: 0
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "This tip won't be shown again"),
countdown: 6
)
}
@@ -648,11 +664,13 @@ class PressureCurveViewController: UIViewController {
AlertControllerUtil.showAlert(
in: self,
title: SwiftLocalizationHelper.localizedString(forKey: "Pressure Range Test"),
message: SwiftLocalizationHelper.localizedString(forKey:"Draw freely and vary pen pressure naturally."),
withCancel: false,
message: SwiftLocalizationHelper.localizedString(forKey:"pressureRangeTestTip"),
withCancel: true,
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "OK"),
countdown: 0,
completion:{
if AlertControllerUtil.actionCancelled { return }
self.resetTapped()
self.curveView.showGraph = false
self.curveView.testStage = .drawingStage
self.curveView.setNeedsDisplay()
@@ -667,16 +685,32 @@ class PressureCurveViewController: UIViewController {
oscProfileMan.replaceSelectedProfile(with: selectedProfile, overwriteDefault: true)
DispatchQueue.main.async {
AlertControllerUtil.showAlert(
in: self,
title: SwiftLocalizationHelper.localizedString(forKey: "Pen Pressure Curve"),
message: SwiftLocalizationHelper.localizedString(forKey:"Pressure curve saved with current on-screen widget profile!"),
withCancel: false,
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "OK"),
countdown: 0,
completion: {
PencilHandler.shared?.setupPressureLUT()
})
if self.isFirstLaunch {
AlertControllerUtil.showAlert(
in: self,
title: SwiftLocalizationHelper.localizedString(forKey: "Pen Pressure Curve"),
message: SwiftLocalizationHelper.localizedString(forKey:"firstPressureCurvePersistTip"),
withCancel: false,
buttonTitle: SwiftLocalizationHelper.localizedString(forKey: "This tip won't be shown again"),
countdown: 11,
completion: {
PencilHandler.shared?.setupPressureLUT()
})
self.isFirstLaunch = false
}
else{
AlertControllerUtil.autoCompletion = true
AlertControllerUtil.showAlert(
in: self,
title: SwiftLocalizationHelper.localizedString(forKey: ""),
message: SwiftLocalizationHelper.localizedString(forKey:"Pressure curve saved"),
withCancel: false,
buttonTitle: "",
countdown: 1,
completion: {
PencilHandler.shared?.setupPressureLUT()
})
}
}
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24411" systemVersion="24G325" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24512" systemVersion="24G419" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="hidden" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
@@ -23,6 +23,7 @@
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="appTheme" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="asyncFrameDequeue" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="asyncNativeTouchPriority" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="audioConfig" attributeType="Integer 32" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
<attribute name="audioEngine" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
@@ -42,6 +43,7 @@
<attribute name="duckOtherApps" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="edgeSlidingSensitivity" optional="YES" attributeType="Float" defaultValueString="22" usesScalarValueType="NO" syncable="YES"/>
<attribute name="emulatedControllerType" optional="YES" attributeType="Integer 16" defaultValueString="2" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableFrameTimebase" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="enableGraphs" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="enablePinch" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
+85 -27
View File
@@ -2131,29 +2131,6 @@
}
}
},
"Draw freely and vary pen pressure naturally." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请随意绘制,笔压自然变化即可。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "請隨意繪製,筆壓自然變化即可。"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "請隨意繪製,筆壓自然變化即可。"
}
}
}
},
"Drift Correction" : {
"extractionState" : "manual",
"localizations" : {
@@ -2863,6 +2840,35 @@
}
}
},
"firstPressureCurvePersistTip" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Pressure curve saved with current on-screen widget profile. Check custom 'On Screen Widget' in 'Touch Control' section of the settings menu, or toolbox menu brought up by right screen edge slide gesture.\nWhen you switch widget profile, pressure curve will change too."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "笔压曲线已随屏显控件配置保存。见设置菜单‘触控分区’的’屏显控件’ 自定义选项, 或屏幕右边缘滑动手势调出的工具箱菜单。\n切换屏显控件配置时,笔压曲线也将随之变化。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "笔壓曲線已隨「螢幕小工具」配置儲存。請至設定選單「觸控分區」中的「螢幕小工具」自訂選項,或由螢幕右側邊緣滑動手勢呼叫的工具箱選單查看。\n切換「螢幕小工具」配置時,筆壓曲線也會隨之變化。"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "笔壓曲線已隨「螢幕小工具」配置儲存。請至設定選單「觸控分區」中的「螢幕小工具」自訂選項,或由螢幕右側邊緣滑動手勢呼叫的工具箱選單查看。\n切換「螢幕小工具」配置時,筆壓曲線也會隨之變化。"
}
}
}
},
"framePacingStackTip" : {
"extractionState" : "manual",
"localizations" : {
@@ -5558,25 +5564,25 @@
}
}
},
"Pressure curve saved with current on-screen widget profile!" : {
"Pressure curve saved" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "笔压曲线已随屏显控件配置保存!"
"value" : "笔压曲线已保存"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "筆壓曲線已隨螢幕小工具設定儲存!"
"value" : "筆壓曲線已儲存"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "筆壓曲線已隨螢幕小工具設定儲存!"
"value" : "筆壓曲線已儲存"
}
}
}
@@ -5627,6 +5633,35 @@
}
}
},
"pressureRangeTestTip" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "This will reset the pressure curve. To proceed with the test, tap 'OK' to draw freely and vary pen pressure naturally."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此测试将重置笔压曲线。 \n若继续测试,请点’确认’,并随意绘制。笔压自然变化即可。\n"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "此測試將重置筆壓曲線。\n若繼續測試,請點選「確認」,並隨意繪製即可,讓筆壓自然變化。"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "此測試將重置筆壓曲線。\n若繼續測試,請點選「確認」,並隨意繪製即可,讓筆壓自然變化。"
}
}
}
},
"Profile %@ duplicated from current layout" : {
"extractionState" : "manual",
"localizations" : {
@@ -7269,6 +7304,29 @@
}
}
},
"This tip won't be shown again" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "该提示将不再弹出"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "該提示將不再彈出"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "該提示將不再彈出"
}
}
}
},
"Tips" : {
"extractionState" : "manual",
"localizations" : {
+1 -1
View File
@@ -129,7 +129,7 @@
return;
}
CFTimeInterval timeout = (1.0f / _framerate) - _renderer.averageGPUTime;
Frame *frame = [_frameQueue dequeueWithTimeout:timeout];
Frame *frame = [_frameQueue dequeueWithTimeoutSync:timeout];
if (!_renderer.isStopping) {
// Only render if not paused
+81 -37
View File
@@ -57,6 +57,8 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
RenderingBackend _renderingBackend;
FramePacingMode _framePacingMode;
bool _enableTimebase;
bool _asyncFrameDequeue;
}
- (void)reinitializeDisplayLayer
@@ -132,12 +134,15 @@ extern int ff_isom_write_av1c(AVIOContext *pb, const uint8_t *buf, int size,
_parameterSetBuffers = [[NSMutableArray alloc] init];
DataManager* dataMan = [[DataManager alloc] init];
TemporarySettings* tempSettings = [dataMan getSettings];
_framePacingMode = [[dataMan getSettings].framePacingMode integerValue];
_framePacingMode = tempSettings.framePacingMode.integerValue;
_enableTimebase = tempSettings.enableFrameTimebase;
_asyncFrameDequeue = tempSettings.asyncFrameDequeue;
_frameQueue = [FrameQueue sharedInstance];
[_frameQueue start];
[_frameQueue setHighWaterMark:(int)[[dataMan getSettings].frameQueueSize integerValue]];
[_frameQueue setHighWaterMark:(int)[tempSettings.frameQueueSize integerValue]];
[self reinitializeDisplayLayer];
@@ -271,57 +276,96 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
CFTimeInterval deadline = link.targetTimestamp;
static CFTimeInterval lastTargetLocal = 0.0f;
CFTimeInterval dl0 = CACurrentMediaTime();
static int lateCallbacks = 0;
if (dl0 > deadline) {
// we already missed it, count how often this happens
lateCallbacks++;
return;
}
[self checkDisplayLayer];
static CFTimeInterval avgOverhead = 0.004f; // averaged each callback
CFTimeInterval waitFor = deadline - dl0 - avgOverhead;
if (waitFor < 0.001f) {
waitFor = 0.0f;
}
// Get the next frame or wait if necessary. If no frame arrives the previous one will be redisplayed automatically.
Frame *frame = [_frameQueue dequeueWithTimeout:waitFor];
if (frame) {
CFTimeInterval dl1 = CACurrentMediaTime();
LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
// the earliest they can be displayed due to double-buffering.
CFTimeInterval targetLocal = deadline + link.duration;
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
if(_asyncFrameDequeue){
[_frameQueue dequeueWithTimeout:waitFor completion:^(Frame *frame) {
if (frame) {
CFTimeInterval dl1 = CACurrentMediaTime();
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
// the earliest they can be displayed due to double-buffering.
CFTimeInterval targetLocal = deadline + link.duration;
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
#ifdef DISPLAYLINK_VERBOSE
Log(LOG_I, @"[%.3f] rendering frame %d, waitFor %.3f ms, overhead %.3f ms, lateCallbacks %d, queue size %d",
deadline, frame.frameNumber, waitFor * 1000.0, avgOverhead * 1000.0, lateCallbacks, [_frameQueue count]);
Log(LOG_I, @"[%.3f] rendering frame %d, waitFor %.3f ms, overhead %.3f ms, lateCallbacks %d, queue size %d",
deadline, frame.frameNumber, waitFor * 1000.0, avgOverhead * 1000.0, lateCallbacks, [_frameQueue count]);
#endif
// Update metrics
if (lastTargetLocal != 0) {
CFTimeInterval frametime = targetLocal - lastTargetLocal;
if (frametime > deadline - start + 0.0005f) {
// we missed a callback
// Log(LOG_W, @"*** slow frametime %.3f ms", frametime * 1000.0);
// Update metrics
if (lastTargetLocal != 0) {
CFTimeInterval frametime = targetLocal - lastTargetLocal;
if (frametime > deadline - start + 0.0005f) {
// 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];
}
}
lastTargetLocal = targetLocal;
// weighted moving average of how much time displayLink needs after dequeuing a frame.
// This is used to avoid overshooting a vsync by waiting too long.
const double alpha = 0.1f;
avgOverhead = ((CACurrentMediaTime() - dl1) * alpha) + (avgOverhead * (1.0 - alpha));
}
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateBackground) {
[[ImGuiPlots sharedInstance] observeFloat:PLOT_FRAMETIME value:frametime * 1000.0];
}];
}
else{
Frame *frame = [_frameQueue dequeueWithTimeoutSync:waitFor];
if (frame) {
CFTimeInterval dl1 = CACurrentMediaTime();
// LogOnce(LOG_I, @"Frame pacing: using AVSampleBufferDisplayLayer target %f Hz with %d FPS stream", 1.0f / (deadline - start), self->_frameRate);
// The system works best with properly timed video frames, which we time to the end of the next vsync period,
// the earliest they can be displayed due to double-buffering.
CFTimeInterval targetLocal = deadline + link.duration;
[self renderFrame:frame atTime:CMTimeMakeWithSeconds(targetLocal, NSEC_PER_SEC)];
#ifdef DISPLAYLINK_VERBOSE
Log(LOG_I, @"[%.3f] rendering frame %d, waitFor %.3f ms, overhead %.3f ms, lateCallbacks %d, queue size %d",
deadline, frame.frameNumber, waitFor * 1000.0, avgOverhead * 1000.0, lateCallbacks, [_frameQueue count]);
#endif
// Update metrics
if (lastTargetLocal != 0) {
CFTimeInterval frametime = targetLocal - lastTargetLocal;
if (frametime > deadline - start + 0.0005f) {
// 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];
}
}
lastTargetLocal = targetLocal;
// weighted moving average of how much time displayLink needs after dequeuing a frame.
// This is used to avoid overshooting a vsync by waiting too long.
const double alpha = 0.1f;
avgOverhead = ((CACurrentMediaTime() - dl1) * alpha) + (avgOverhead * (1.0 - alpha));
}
lastTargetLocal = targetLocal;
// weighted moving average of how much time displayLink needs after dequeuing a frame.
// This is used to avoid overshooting a vsync by waiting too long.
const double alpha = 0.1f;
avgOverhead = ((CACurrentMediaTime() - dl1) * alpha) + (avgOverhead * (1.0 - alpha));
}
}
@@ -361,7 +405,7 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
- (void)renderFrame:(Frame *)frame atTime:(CMTime)targetTime {
CMSampleBufferSetOutputPresentationTimeStamp(frame.sampleBuffer, targetTime);
if ([self->_displayLayer controlTimebase] == NULL) {
if (_enableTimebase && [self->_displayLayer controlTimebase] == NULL) {
// On first frame, set timebase to the initial presentation time.
// This will let us present frames using the local clock (vsync pacing) or
// the pts timestamps from the host.
+3 -1
View File
@@ -13,6 +13,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) int highWaterMark;
@property (nonatomic, readonly) int maxCapacity;
@property (atomic) BOOL paused;
- (void)dequeueWithTimeout:(CFTimeInterval)timeout
completion:(void (^)(Frame *frame))completion;
+ (instancetype)sharedInstance;
@@ -23,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
- (int)enqueue:(Frame *)frame;
- (int)enqueue:(Frame *)frame withSlackSize:(int)slack;
- (nullable Frame *)dequeue;
- (nullable Frame *)dequeueWithTimeout:(CFTimeInterval)timeout;
- (nullable Frame *)dequeueWithTimeoutSync:(CFTimeInterval)timeout;
- (CFTimeInterval)estimatedFramerate;
- (int)currentSoftCap;
- (void)waitForEnqueue;
+13 -1
View File
@@ -251,7 +251,19 @@
return frame;
}
- (Frame *)dequeueWithTimeout:(CFTimeInterval)timeout {
- (void)dequeueWithTimeout:(CFTimeInterval)timeout
completion:(void (^)(Frame *frame))completion {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
Frame *frame = [self dequeueWithTimeoutSync:timeout];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(frame);
}
});
});
}
- (Frame *)dequeueWithTimeoutSync:(CFTimeInterval)timeout {
CFTimeInterval start = CACurrentMediaTime();
CFTimeInterval deadline = start + timeout;
int round = 0;
@@ -1222,6 +1222,9 @@ static NSMutableSet* hostList;
// 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];
[settingsViewController.frameTimebaseSwitch setEnabled:shouldEnableFramePacing];
[settingsViewController.asyncFrameDequeueSwitch setEnabled:shouldEnableFramePacing];
// Disable mic switch if sunshine does not support mic redirection
[settingsViewController.redirectMicSwitch setEnabled:!_settingsExpandedInStreamView||streamFrameViewController.micStreamInitialized];
if(_settingsExpandedInStreamView && !streamFrameViewController.micStreamInitialized) [settingsViewController.redirectMicSwitch setOn:false];
@@ -243,6 +243,13 @@
@property (strong, nonatomic) IBOutlet UIStackView *pressureCurveStack;
@property (strong, nonatomic) IBOutlet UISwitch *pressureCurveSwitch;
@property (weak, nonatomic) IBOutlet UIStackView *frameTimebaseStack;
@property (weak, nonatomic) IBOutlet UISwitch *frameTimebaseSwitch;
@property (weak, nonatomic) IBOutlet UIStackView *asyncFrameDequeueStack;
@property (weak, nonatomic) IBOutlet UISwitch *asyncFrameDequeueSwitch;
@property (strong, nonatomic) IBOutlet UIStackView *testStack;
@@ -262,6 +269,8 @@
@property (weak, nonatomic) IBOutlet UIStackView *pencilPausesNativeTouchStack;
@property (weak, nonatomic) IBOutlet UISwitch *pencilPausesNativeTouchSwitch;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
@@ -794,6 +794,8 @@ BOOL isCustomResolution(int resolutionSelected) {
[self addSetting:self.pipStack ofId:@"pipStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
[self addSetting:self.framePacingStack ofId:@"framePacingStack" withInfoTag:YES withDynamicLabel:NO to:videoSection];
[self addSetting:self.frameQueueSizeStack ofId:@"frameQueueSizeStack" withInfoTag:NO withDynamicLabel:YES to:videoSection];
[self addSetting:self.frameTimebaseStack ofId:@"frameTimebaseStack" withInfoTag:NO withDynamicLabel:NO to:videoSection];
[self addSetting:self.asyncFrameDequeueStack ofId:@"asyncFrameDequeueStack" withInfoTag:NO withDynamicLabel:NO to:videoSection];
[videoSection addToParentStack:_parentStack];
// [videoSection setExpanded:NO];
@@ -1885,6 +1887,9 @@ BOOL isCustomResolution(int resolutionSelected) {
[self.framePacingModeSelector setSelectedSegmentIndex:framePacingMode];
[self.framePacingModeSelector addTarget:self action:@selector(framePacingModeChanged:) forControlEvents:UIControlEventValueChanged];
[self framePacingModeChanged:self.framePacingModeSelector];
[self.frameTimebaseSwitch setOn:self->tempSettings.enableFrameTimebase];
[self.asyncFrameDequeueSwitch setOn:self->tempSettings.asyncFrameDequeue];
[self renderingBackendChanged:self.renderingBackendSelector]; // Update PiP and frame pacing state based on current selection
@@ -2303,6 +2308,8 @@ BOOL isCustomResolution(int resolutionSelected) {
- (void)framePacingModeChanged:(UISegmentedControl *)sender {
// Hide frame queue size for Off and Legacy modes
[self setHidden:(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy) forStack:self.frameQueueSizeStack];
[self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.frameTimebaseStack];
[self setHidden:(sender.selectedSegmentIndex != FramePacingModeQueue) forStack:self.frameTimebaseStack];
if(sender.selectedSegmentIndex == FramePacingModeOff || sender.selectedSegmentIndex == FramePacingModeLegacy){
[self.enableGraphsSwitch setOn:NO];
@@ -3533,6 +3540,8 @@ BOOL isCustomResolution(int resolutionSelected) {
CGFloat controllerMousePointerVelocity = self.controllerMouseVelocitySlider.value;
CGFloat controllerMouseExpo = self.controllerMouseExpoSlider.value;
NSInteger controllerGyroSwitchMode = self.controllerGyroSwitchButtonSetter.selectedSegmentIndex;
BOOL enableFrameTimebase = self.frameTimebaseSwitch.isOn;
BOOL asyncFrameDequeue = self.asyncFrameDequeueSwitch.isOn;
NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value;
[dataMan saveSettings:currentSettings
@@ -3605,6 +3614,8 @@ BOOL isCustomResolution(int resolutionSelected) {
controllerMousePointerVelocity:controllerMousePointerVelocity
controllerMouseExpo:controllerMouseExpo
controllerGyroSwitchMode:controllerGyroSwitchMode
enableFrameTimebase:enableFrameTimebase
asyncFrameDequeue:asyncFrameDequeue
backgroundSessionTimer:backgroundSessionTimer];
}
+24
View File
@@ -2065,6 +2065,18 @@
}
}
},
"cpg-mq-kYj.text" : {
"comment" : "Class = \"UILabel\"; text = \"Enable Timebase\"; ObjectID = \"cpg-mq-kYj\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Enable Timebase"
}
}
}
},
"CpY-Sb-BDc.title" : {
"comment" : "Class = \"UIBarButtonItem\"; title = \"Import\"; ObjectID = \"CpY-Sb-BDc\";",
"extractionState" : "extracted_with_value",
@@ -5629,6 +5641,18 @@
}
}
},
"X0u-7f-F86.text" : {
"comment" : "Class = \"UILabel\"; text = \"Async Deueue\"; ObjectID = \"X0u-7f-F86\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Async Deueue"
}
}
}
},
"x9i-JN-FSn.text" : {
"comment" : "Class = \"UILabel\"; text = \"Roll factor\"; ObjectID = \"x9i-JN-FSn\";",
"extractionState" : "extracted_with_value",
+24
View File
@@ -2023,6 +2023,18 @@
}
}
},
"e1n-pw-04X.text" : {
"comment" : "Class = \"UILabel\"; text = \"Async Deueue\"; ObjectID = \"e1n-pw-04X\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Async Deueue"
}
}
}
},
"E7F-vx-pnM.text" : {
"comment" : "Class = \"UILabel\"; text = \"Frame Rate\"; ObjectID = \"E7F-vx-pnM\";",
"extractionState" : "extracted_with_value",
@@ -3955,6 +3967,18 @@
}
}
},
"P4N-i6-1zD.text" : {
"comment" : "Class = \"UILabel\"; text = \"Enable Timebase\"; ObjectID = \"P4N-i6-1zD\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Enable Timebase"
}
}
}
},
"PaG-9w-YxB.text" : {
"comment" : "Class = \"UILabel\"; text = \"Mute in Background\"; ObjectID = \"PaG-9w-YxB\";",
"extractionState" : "extracted_with_value",