Write down that a redraw has to fit inside one batch
"Drawing does not persist across frames; redraw it every frame" was the advice, and it is not sufficient on its own. The host runs a fixed number of source lines and then presents, and presenting discards the drawing buffer -- so a run of drawing verbs longer than one budget is **torn**, not merely transient, and an `SSHAPE` at the end of it captures only what was issued since the present, over whatever the frame before left behind. Measured against the standalone frontend's 256: after synchronising to a jiffy edge, 220 lines of drawing survive a capture and 250 do not. There is no fix available that does not change what a host owns -- the budget is the host's and so is the present -- so this is documentation, in the three places it belongs. Chapter 13 beside the note it qualifies, with the numbers. Chapter 6 where `SSHAPE` is introduced, which is where a program meets it. Chapter 14 from the step loop's side, naming `AKBASIC_FRONTEND_STEPS_PER_FRAME` and explaining the one thing a script can do about it: `settime()` is called once a frame, so `TI#` changes on the first step of a batch and nowhere else, and spinning until it changes is the only frame synchronisation this dialect has. **Untested, deliberately.** Reproducing it needs the real frontend, a clock and a timing window, and a test that reproduced it would be reproducing a race. Recorded in TODO.md rather than left looking like an oversight. TODO.md section 9 item 5, struck as a documentation outcome. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -244,3 +244,11 @@ The graphics verbs draw straight to the renderer rather than into a display list
|
|||||||
anything drawn is overwritten by the text layer on the next frame. A program that wants
|
anything drawn is overwritten by the text layer on the next frame. A program that wants
|
||||||
its drawing to persist has to redraw it. This is recorded as a defect rather than a
|
its drawing to persist has to redraw it. This is recorded as a defect rather than a
|
||||||
design; see Chapter 13.
|
design; see Chapter 13.
|
||||||
|
|
||||||
|
**A redraw also has to fit inside one batch.** The host runs a fixed number of source
|
||||||
|
lines and then presents, and presenting throws the drawing buffer away — so a run of
|
||||||
|
drawing verbs longer than one batch is torn rather than merely transient, and an
|
||||||
|
`SSHAPE` at the end of it captures only the part issued since the present. Watching
|
||||||
|
`TI#` change is how a program finds the boundary; Chapter 13 has the measured numbers and
|
||||||
|
[Chapter 18](18-tutorial-breakout-artwork.md#step-4-find-the-frame-boundary) has a
|
||||||
|
routine that uses them.
|
||||||
|
|||||||
@@ -168,8 +168,22 @@ resume there.
|
|||||||
`akbasic_runtime_run(rt, n)` is `step()` in a `while` with a budget: at most `n` steps,
|
`akbasic_runtime_run(rt, n)` is `step()` in a `while` with a budget: at most `n` steps,
|
||||||
then return regardless. That bound is the only thing standing between a script containing
|
then return regardless. That bound is the only thing standing between a script containing
|
||||||
`10 GOTO 10` and your frame rate. `n <= 0` means unbounded, which is right for a test and
|
`10 GOTO 10` and your frame rate. `n <= 0` means unbounded, which is right for a test and
|
||||||
wrong for a game. The SDL frontend uses 256; the stdio driver uses 1, because it wants to
|
wrong for a game. The SDL frontend uses 256 (`AKBASIC_FRONTEND_STEPS_PER_FRAME`); the
|
||||||
refresh the clock between steps.
|
stdio driver uses 1, because it wants to refresh the clock between steps.
|
||||||
|
|
||||||
|
**That budget is visible to a script, and drawing is where it shows.** The frontend
|
||||||
|
presents the frame when `run()` comes back, and presenting discards the drawing buffer —
|
||||||
|
so a sequence of drawing verbs longer than one budget is cut in half by the present in
|
||||||
|
the middle of it, and an `SSHAPE` afterwards captures only what was issued since. Not a
|
||||||
|
transient artefact: a torn capture, over whatever the frame before left behind.
|
||||||
|
|
||||||
|
A script cannot read the budget, but it can *see* it: `akbasic_runtime_settime()` is
|
||||||
|
called once per frame by the host, so `TI#` changes on the first step of a batch and
|
||||||
|
nowhere else. Spinning until it changes is the only frame synchronisation this dialect
|
||||||
|
has, and it is what
|
||||||
|
[Chapter 18](18-tutorial-breakout-artwork.md#step-4-find-the-frame-boundary) is built on.
|
||||||
|
A host that gives the interpreter a larger budget makes more drawing fit; one that never
|
||||||
|
calls `settime()` takes the synchronisation away entirely.
|
||||||
|
|
||||||
Time comes in from outside. `akbasic_runtime_settime(rt, ms)` is how the interpreter
|
Time comes in from outside. `akbasic_runtime_settime(rt, ms)` is how the interpreter
|
||||||
learns what time it is; it reads no clock, because it owns no loop and must not block. A
|
learns what time it is; it reads no clock, because it owns no loop and must not block. A
|
||||||
|
|||||||
Reference in New Issue
Block a user