FramePhasePllTest), covering normal cadence as well as extreme situations (frame-rate changes, long commits that span multiple frames, missed commit/presync deadlines, and loss/return of valid OpenXR timing).
FrameTiming invalid): reproduces the historical Choreographer behavior, including the errorMs budget that rejects 0 / sub-rate callbacks. The default frame rate (when the confirmed rate is unavailable) is FramePhasePll.Companion.DEFAULT_FRAME_RATE, matching the engine's historical 90Hz maximum update velocity.FrameTiming valid for FramePhasePll.Companion.LOCK_ON_VALID_FRAMES consecutive frames): drives an absolute schedule timeline so the realized lead (commit end -> next predicted-display grid line) converges to FramePhasePll.targetLeadUs using a slew-rate-limited proportional correction. The grid spacing is derived from the current confirmed refresh rate each frame, so dynamic frame-rate changes are tracked automatically. Only the grid phase comes from predictedDisplayTime, so any constant epoch offset between XrTime and the caller's clock is absorbed into FramePhasePll.targetLeadUs.xrWaitFrame return timing when available and targets commit completion midway between consecutive returns.class FramePhasePll(var targetLeadUs: Long = DEFAULT_TARGET_LEAD_US, var damping: Float = DEFAULT_DAMPING, var maxSlewUs: Long = DEFAULT_MAX_SLEW_US, var lockedScheduleMode: FramePhasePll.LockedScheduleMode = DEFAULT_LOCKED_SCHEDULE_MODE, var enabled: Boolean = true)
FramePhasePll
(
targetLeadUs
, damping
, maxSlewUs
, lockedScheduleMode
, enabled
)
|
Signature
constructor(targetLeadUs: Long = DEFAULT_TARGET_LEAD_US, damping: Float = DEFAULT_DAMPING, maxSlewUs: Long = DEFAULT_MAX_SLEW_US, lockedScheduleMode: FramePhasePll.LockedScheduleMode = DEFAULT_LOCKED_SCHEDULE_MODE, enabled: Boolean = true) Parameters targetLeadUs: Longdamping: FloatmaxSlewUs: LonglockedScheduleMode: FramePhasePll.LockedScheduleModeenabled: BooleanReturns FramePhasePll |
damping
: Float
[Get][Set] |
Fraction of the measured phase error corrected per frame (proportional gain).
Signature
var damping: Float |
enabled
: Boolean
[Get][Set] |
Master switch for phase-locking. When true (default) the loop phase-locks to the predicted-display clock where support is available (valid timing); it automatically stays in the Choreographer fallback where it is not. When false the loop never locks and behaves exactly like the historical free-running Choreographer schedule.
Signature
var enabled: Boolean |
lockedScheduleMode
: FramePhasePll.LockedScheduleMode
[Get][Set] |
Scheduling policy used after the loop phase-locks to OpenXR timing.
Signature
var lockedScheduleMode: FramePhasePll.LockedScheduleMode |
maxSlewUs
: Long
[Get][Set] |
Maximum schedule correction applied in a single frame, in microseconds (slew limit).
Signature
var maxSlewUs: Long |
mode
: FramePhasePll.Mode
[Get] |
Current loop mode.
Signature
var mode: FramePhasePll.Mode |
targetLeadUs
: Long
[Get][Set] |
Target lead (us) from commit end to the next predicted-display grid line.
Signature
var targetLeadUs: Long |
beginFrame
(
frameTimeNanos
, frameRate
, timing
, nowNs
)
|
Called at the start of every frame, before the tick.
Signature
fun beginFrame(frameTimeNanos: Long, frameRate: Float, timing: FrameTiming, nowNs: Long): FramePhasePll.BeginResult Parameters frameTimeNanos: Long
vsync timestamp (fallback) or nowNs (locked); CLOCK_MONOTONIC ns.
frameRate: Float
confirmed refresh rate (Hz); FramePhasePll.Companion.DEFAULT_FRAME_RATE is used if <= 0.
timing: FrameTiming
latest OpenXR predicted-display snapshot.
nowNs: Long
current monotonic time (ns); used to seed the locked timeline on transition.
Returns FramePhasePll.BeginResult |
endFrame
(
commitEndNs
, commitDurationNs
, frameRate
, timing
, nowNs
, tickToCommitDurationNs
)
|
Called after the tick in locked mode. Computes the delay before the next tick so the realized lead converges to FramePhasePll.targetLeadUs, with phase-preserving catch-up if the callback ran long or was delayed across one or more frame periods.
Signature
fun endFrame(commitEndNs: Long, commitDurationNs: Long, frameRate: Float, timing: FrameTiming, nowNs: Long, tickToCommitDurationNs: Long = commitDurationNs): FramePhasePll.ScheduleResult Parameters commitEndNs: Long
monotonic time (ns) at the end of commit().
commitDurationNs: Long
measured duration (ns) of commit().
frameRate: Float
confirmed refresh rate (Hz) for this frame (tracks dynamic changes).
timing: FrameTiming
OpenXR predicted-display snapshot for this frame.
nowNs: Long
current monotonic time (ns) when scheduling the next frame (>= commitEndNs).
tickToCommitDurationNs: Long
measured duration (ns) from tick start through commit end.
Returns FramePhasePll.ScheduleResult |
reset
()
|
Resets all PLL / drop-rejection state to the fallback bootstrap condition.
Signature
fun reset() |
COMMIT_SMOOTHING
: Float
[Get] |
Signature
const val COMMIT_SMOOTHING: Float = 0.1f |
DEFAULT_DAMPING
: Float
[Get] |
Signature
const val DEFAULT_DAMPING: Float = 0.25f |
DEFAULT_FRAME_RATE
: Float
[Get] |
Signature
const val DEFAULT_FRAME_RATE: Float = 90.0f |
DEFAULT_LOCKED_SCHEDULE_MODE
: FramePhasePll.LockedScheduleMode
[Get] |
Signature
val DEFAULT_LOCKED_SCHEDULE_MODE: FramePhasePll.LockedScheduleMode |
DEFAULT_MAX_SLEW_US
: Long
[Get] |
Signature
const val DEFAULT_MAX_SLEW_US: Long = 2000 |
DEFAULT_MINIMUM_TICK_DURATION_US
: Long
[Get] |
Signature
const val DEFAULT_MINIMUM_TICK_DURATION_US: Long = 2000 |
DEFAULT_TARGET_LEAD_US
: Long
[Get] |
Signature
const val DEFAULT_TARGET_LEAD_US: Long = 900 |
LOCK_ON_VALID_FRAMES
: Int
[Get] |
Signature
const val LOCK_ON_VALID_FRAMES: Int = 5 |
MAX_CATCHUP_FRAMES
: Int
[Get] |
Signature
const val MAX_CATCHUP_FRAMES: Int = 16 |
MAX_INTERVAL_FRACTION
: Float
[Get] |
Signature
const val MAX_INTERVAL_FRACTION: Float = 1.5f |
MIN_INTERVAL_FRACTION
: Float
[Get] |
Signature
const val MIN_INTERVAL_FRACTION: Float = 0.5f |
MIN_TICK_TO_COMMIT_DURATION_SAMPLES
: Int
[Get] |
Signature
const val MIN_TICK_TO_COMMIT_DURATION_SAMPLES: Int = 10 |
STALE_FRAMES
: Int
[Get] |
Signature
const val STALE_FRAMES: Int = 8 |
TICK_TO_COMMIT_DURATION_PERCENTILE
: Int
[Get] |
Signature
const val TICK_TO_COMMIT_DURATION_PERCENTILE: Int = 90 |
TICK_TO_COMMIT_DURATION_WINDOW_FRAMES
: Int
[Get] |
Signature
const val TICK_TO_COMMIT_DURATION_WINDOW_FRAMES: Int = 90 |
UNLOCK_ON_INVALID_FRAMES
: Int
[Get] |
Signature
const val UNLOCK_ON_INVALID_FRAMES: Int = 3 |
data class BeginResult(val shouldTick: Boolean, val mode: FramePhasePll.Mode, val modeChanged: Boolean)
BeginResult
(
shouldTick
, mode
, modeChanged
)
|
Signature
constructor(shouldTick: Boolean, mode: FramePhasePll.Mode, modeChanged: Boolean) Parameters shouldTick: Booleanmode: FramePhasePll.ModemodeChanged: BooleanReturns FramePhasePll.BeginResult |
mode
: FramePhasePll.Mode
[Get] |
Signature
val mode: FramePhasePll.Mode |
modeChanged
: Boolean
[Get] |
Signature
val modeChanged: Boolean |
shouldTick
: Boolean
[Get] |
Signature
val shouldTick: Boolean |
data class ScheduleResult(val delayNs: Long, val realizedLeadNs: Long, val correctionNs: Long, val smoothedCommitNs: Long, val skippedFrames: Int, val missedDeadline: Boolean, val nextGridLeadNs: Long = 0, val targetTickNs: Long = 0, val targetGridNs: Long = 0, val estimatedTickToCommitNs: Long = 0)
ScheduleResult
(
delayNs
, realizedLeadNs
, correctionNs
, smoothedCommitNs
, skippedFrames
, missedDeadline
, nextGridLeadNs
, targetTickNs
, targetGridNs
, estimatedTickToCommitNs
)
|
Signature
constructor(delayNs: Long, realizedLeadNs: Long, correctionNs: Long, smoothedCommitNs: Long, skippedFrames: Int, missedDeadline: Boolean, nextGridLeadNs: Long = 0, targetTickNs: Long = 0, targetGridNs: Long = 0, estimatedTickToCommitNs: Long = 0) Parameters delayNs: LongrealizedLeadNs: LongcorrectionNs: LongsmoothedCommitNs: LongskippedFrames: IntmissedDeadline: BooleannextGridLeadNs: LongtargetTickNs: LongtargetGridNs: LongestimatedTickToCommitNs: LongReturns FramePhasePll.ScheduleResult |
correctionNs
: Long
[Get] |
Schedule correction (ns) applied this frame (signed, slew-limited).
Signature
val correctionNs: Long |
delayNs
: Long
[Get] |
Delay (ns) from nowNs until the next tick should start. Always >= 0.
Signature
val delayNs: Long |
estimatedTickToCommitNs
: Long
[Get] |
Rolling p90 tick-start-to-commit-end duration used for midpoint scheduling.
Signature
val estimatedTickToCommitNs: Long = 0 |
missedDeadline
: Boolean
[Get] |
True when the commit ended after the grid line it was meant to precede (a miss).
Signature
val missedDeadline: Boolean |
nextGridLeadNs
: Long
[Get] |
Positive distance from commit end to the next OpenXR grid line.
Signature
val nextGridLeadNs: Long = 0 |
realizedLeadNs
: Long
[Get] |
Realized lead (ns): commit end to nearest grid line. Negative => commit landed late.
Signature
val realizedLeadNs: Long |
skippedFrames
: Int
[Get] |
Whole grid periods skipped to catch up after a long/late callback (0 when on time).
Signature
val skippedFrames: Int |
smoothedCommitNs
: Long
[Get] |
Low-pass-filtered commit duration (ns), for observability.
Signature
val smoothedCommitNs: Long |
targetGridNs
: Long
[Get] |
Absolute monotonic OpenXR timing line used as the next tick's scheduling anchor.
Signature
val targetGridNs: Long = 0 |
targetTickNs
: Long
[Get] |
Absolute monotonic time for the next tick target.
Signature
val targetTickNs: Long = 0 |
enum Mode : Enum<FramePhasePll.Mode>
| Member |
|---|
FALLBACK |
LOCKED |
enum LockedScheduleMode : Enum<FramePhasePll.LockedScheduleMode>
| Member |
|---|
PRE_SYNC |
WAITFRAME_MIDPOINT |