AdaptiveHeadless<'Frame> Type
Runs an adaptive program with explicit frame stepping.
The runner owns the frame boundary. Each
AdaptiveHeadless.Step:
(1) applies cross-thread posts, drains the next-frame lane and the
pre-step lane (subscription events, e.g. input) at the frame boundary,
then compares the program's subscription projection against the attached
table,
(2) writes the current game time into the time root (once, or once per
fixed step when AdaptiveProgram is set),
(3) runs the program's Update phase,
(4) drains the intent queue until empty — posted work runs in post
order, and work posted during the drain runs in the same drain (after
each sub-step's Update under fixed-step),
(5) forces the frame builder — the frame's projections recompute exactly
once if any of their dependencies moved, and not at all otherwise — and
(6) notifies the observers with the forced frame. Draw code reads the
returned frame: reads are O(1) until the next write.
Before the first step, the runner drains the intent queue once at startup,
right after Init returns and before the first frame is forced: work
Init posted through its context runs before the first frame, like
the Cmd returned from the MVU init function.
The adaptive graph is confined to the thread that creates it: no locks,
allocation-free steady state. The runner
creates the graph lazily on the first user — the thread that first calls
Step/StepN/StepUntil/Run, or the dedicated game
thread of RunAsync. All graph work then happens on that thread.
Cross-thread writes go through cval.Post — drained by
Posting.pump at the start of every step — and external work goes
through AdaptiveContext.Intents (the post
lane is thread-safe; the runner drains it after Update).
Constructors
| Constructor |
Description
|
Full Usage:
AdaptiveHeadless(program, ?width, ?height, ?context, ?profiler)
Parameters:
AdaptiveProgram<'Frame>
?width : int
?height : int
?context : GameContext
?profiler : FrameProfiler
Returns: AdaptiveHeadless<'Frame>
|
|
Instance members
| Instance member |
Description
|
Full Usage:
this.Dispose
|
Detach all subscriptions, dispose program disposables and observers, and clean up resources. |
Full Usage:
this.Frame
Returns: 'Frame
|
The last forced frame. Valid after the first AdaptiveHeadless.Step.
|
|
Total elapsed virtual time.
|
Full Usage:
this.Post
Parameters:
unit -> unit
|
Thread-safe external injection: posts work to the post lane, where it
runs on the owner thread at the next post drain, in post order, drained
until empty, before the frame is forced. For work posted after
initialization, that drain follows the next step's
|
Full Usage:
this.Run
Parameters:
TimeSpan
-
Tick interval (e.g. TimeSpan.FromMilliseconds(16) for 60fps).
?ct : CancellationToken
-
Optional cancellation token to stop the loop early.
Returns: (GameTime * 'Frame) seq
A sequence of (GameTime * 'Frame) snapshots, paced by the interval.
|
Run the simulation synchronously, yielding each frame as a sequence.
Uses a spin-wait with
This advances the runner's internal state. Do not mix with
|
Full Usage:
this.RunAsync
Parameters:
TimeSpan
-
Tick interval.
?ct : CancellationToken
-
Optional cancellation token to stop the loop.
Returns: IAsyncEnumerable<StepOutcome<'Frame>>
An async sequence of StepOutcome snapshots: the game time and the forced frame of each step.
|
Run the simulation asynchronously, yielding each frame as an async enumerable.
The world loop runs on a dedicated background game thread — the graph is
confined to its creating thread, and async continuation threads are not it.
The async enumerable is a consumer of that thread: it receives the
outcomes the loop produces, in order. The
val outcome: obj
The consumer renders the packed frame — it never touches the world's
graph. Background work started with
IntentQueue.postTask / postAsync
runs off the world thread, and its completion posts back into the world's
intent queue (thread-safe) and runs at the next post drain on the game
thread.
At most one enumerator may consume a runner at a time: a second
This advances the runner's internal state. Do not mix with
|
Full Usage:
this.ShouldQuit
Returns: bool
|
Whether the runner has received an exit request.
|
Advance the program by one frame and return the forced frame.
When AdaptiveProgram is set, the
frame delta is converted into zero or more fixed-size steps: the time root
is written,
This mutates the runner's internal state (time root, program roots, frame).
Do not mix
|
|
Full Usage:
this.StepN
Parameters:
int
-
Number of frames to run.
elapsed : TimeSpan
-
Frame delta per step.
Returns: 'Frame
|
Advance the simulation by N frames and return the last forced frame.
This mutates the runner's internal state. Do not mix with
|
Full Usage:
this.StepUntil
Parameters:
'Frame -> bool
-
Condition to check after each frame.
elapsed : TimeSpan
-
Frame delta per step.
?maxFrames : int
-
Safety limit to prevent infinite loops.
Returns: bool
True if predicate was met, false if maxFrames was reached.
|
Advance until a predicate on the forced frame returns true.
This mutates the runner's internal state. Do not mix with
|
Mibo