Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,19 @@ jobs:
CARGO_BUILD_JOBS: '2'
run: python tools/ci/setup_windows_perry.py --out "$env:RUNNER_TEMP/bloom-perry" --github-env

- name: Native and WASM fixed lifecycle contract
shell: pwsh
run: python tools/ci/fixed_step_smoke.py

- name: Retain fixed lifecycle evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: fixed-game-lifecycle
path: target/ci/fixed-step
if-no-files-found: error
retention-days: 1

- name: Compile real browser startup fixture
shell: pwsh
run: python tools/ci/compile_web_game.py
Expand Down
59 changes: 59 additions & 0 deletions docs/evidence/fixed-game-lifecycle-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Fixed game lifecycle

The callback-only loop supplies variable delta and leaves timing and hook order
to each game. The new optional `runGameLifecycle` entry defines init, fixed
update, variable update, interpolated draw and one-time cleanup for native and
web. `FixedStepClock` provides bounded catch-up and explicit dropped-time
diagnostics. The original `runGame` API stays supported.

The pure TypeScript contract is compiled and executed with the qualified Perry
0.5.1220 profile both as a Windows executable and as actual WASM in its generated
runtime. Uniform and varied partitions each produce 50 ticks; a 6,000-frame
144 Hz sequence produces 2,500 ticks at 60 Hz. Checks cover catch-up/drop and
resume, interpolation, invalid settings/deltas, overflowing accumulation, very
small steps, optional hooks, initialization/disposal order, duplicate cleanup,
stop during fixed update and stop during variable update. Host-side controls
reject missing, duplicate, malformed and incorrect observations.

Initial failed runs are retained. Object-literal JSON reporting returned
undefined in WASM, so the fixture now reports the actual scalar observations
directly. `Number.isFinite` has a distinct HIR expression without a corresponding
WASM lowering in pinned Perry; an attempted runtime dispatch shim did not fix
that and was removed. A portable noncoercing arithmetic finite check now serves
the clock and existing quality/scene validators. Method syntax for stored
callbacks also went through named dispatch instead of the closure bridge.
Reading each callback into a local function reference fixes the native/WASM
observations without changing Perry. General JSON/exception compatibility is
not claimed.

A fresh installed candidate package compiles the public lifecycle fixture and
renders its exact 128x128 image on Radeon DX12 and Vulkan. Both captures match
all 16,384 pixels and PNG SHA-256
`8a509d87d3aa3fab96e0a9e2c67228e187f1bc0853cb4726aa5844799440f30e`.
Each records one init, nine updates/draws, two fixed ticks and one cleanup, with
valid interpolation. The fixture captures frame eight and exits after readback.
This lifecycle mode does not simulate Jolt; the existing scene/direct-2D modes
retain their independent physics acceptance. An older generic success message
in the first local log mentions Jolt; the mode-specific observations establish
the actual scope, and the harness message is corrected.

The same fixture compiles for web and a recording-FFI probe verifies all hooks,
13 fixed ticks across eight supplied 1/60-second frames, interpolation and the
explicit startup-fault control. That probe uses real Perry WASM/runtime but no
renderer. Hosted browser rendering of this revised fixture remains required.
The Windows job now runs the pure native/WASM contract and retains its evidence;
the installed native gate adds this lifecycle mode to its existing two modes.
The browser gate requires lifecycle counters in addition to the exact image.

The starter uses fixed simulation and previous/current interpolation. A fresh
installed CLI creates it with the default command and builds the unmodified
native template in 266.563 seconds. `npm start` builds/runs a bounded copy in
7.047 seconds: the 800x450 image shows the greeting and interpolated square,
the text asset loads, and cleanup runs once. Restoring the unmodified source
completes the full web build with its asset manifest. Seven installed runtime
and template sources match the candidate. This is local headless native and
web-build acceptance; full starter browser rendering is still unperformed.
Repository contracts and eight acceptance-control tests pass. Broader examples,
components, input/pause policy,
device-loss recovery, full starter browser assets/text and named-hardware
qualification remain separate. No issue is closed by these focused checks.
64 changes: 62 additions & 2 deletions docs/game-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,65 @@ Canonical examples use the palette's public uppercase names, such as
The installed native gate verifies cleanup runs exactly once after its physics
and capture fixture. Scheduler tests cover the asynchronous ordering, stop,
failure, re-entry and stale-callback behavior without claiming browser rendering.
Actual compiled-game browser startup, a complete init/update/fixed-update/draw
lifecycle and the one-command starter remain open under #142/#74.
The compiled-game gate at #170 passes real browser rendering and its explicit
startup-fault control. Full starter assets/text and all canonical example runtimes
remain open under #142/#74.

## Fixed game lifecycle

`runGameLifecycle(game, options?)` provides optional `init`, `fixedUpdate`,
`update` and `cleanup` hooks plus required `draw`. The starter uses this entry.
Hooks are closures or free functions without a bound `this`; keep shared state
in their lexical scope. The engine still owns begin/end drawing around each frame.

```ts
let previous = 0;
let position = 0;
runGameLifecycle({
init: () => initWindow(800, 450, 'Fixed simulation'),
fixedUpdate: (dt, tick) => {
previous = position;
position += 60 * dt;
},
draw: (alpha) => {
clearBackground(Colors.BLACK);
drawRect(previous + (position - previous) * alpha, 40, 20, 20, Colors.WHITE);
},
cleanup: () => closeWindow(),
});
```

Each frame runs zero or more fixed ticks, then one variable update, then one
draw. Tick numbers start at 1. Defaults are `fixedStepSeconds: 1 / 60`,
`maxFixedSteps: 8`, and `maxFrameSeconds: 0.25`. Variable update receives the
clamped frame delta. Fixed update receives the constant step; use it for physics
instead of feeding variable delta into a deterministic simulation. Draw receives
the remaining fraction of a tick, `alpha` in `[0, 1)`, for interpolation between
previous and current simulation state. Interpolation intentionally trails the
latest simulation by up to one tick.

The clock clamps long frame deltas and drops whole ticks left after the catch-up
limit, retaining only the fractional remainder. A background tab therefore cannot
trigger an unbounded catch-up loop. This policy slows simulation relative to
wall time during overload; it is not a networking or lockstep guarantee. Pause
and focus policy stay with the game. Resize behavior is unchanged, and resource
restoration after device loss is still separate.

`FixedStepClock` exposes `steps`, cumulative scheduled `ticks`, `alpha`, accepted
`deltaSeconds` and cumulative `droppedSeconds` for custom loops or diagnostics.
Its `advance(dt)` returns false for negative/nonfinite deltas or overflowing
accumulation, preserving accumulated time and interpolation. Per-frame steps
and accepted delta become zero on rejection. Settings require positive finite
times and an integer step limit from 1 through 1000. A `1e-9` relative tick
tolerance handles ordinary floating-point partition rounding; determinism here
means a fixed simulation step, not arbitrary cross-machine bitwise arithmetic.

Invalid settings make `runGameLifecycle` report an error and return false before
init. A second active lifecycle is also rejected. A valid start returns true;
native returns after shutdown, while web returns after scheduling. Calling
`closeWindow()` from init prevents frame scheduling. Calling it during fixed
update or variable update suppresses later hooks in that frame. Cleanup runs
once after normal shutdown; no update runs after disposal. As with `runGame`,
fatal process termination cannot promise cleanup. General Perry 0.5.1220 throw
propagation remains limited; the browser gate's explicit FFI error control does
not establish a general language exception guarantee.
17 changes: 11 additions & 6 deletions docs/starter.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,25 @@ executable. General long Windows project paths remain unqualified.

## Development and validation

The template separates init, variable update, draw and cleanup while using
`runGame` on both targets. See the [game-loop contract](game-loop.md). Edit,
stop and rerun to rebuild; automatic hot reload, fixed update and device-loss
recovery are not implemented by this command.
The template uses `runGameLifecycle` on both targets, with init, 60 Hz fixed
update, variable update, interpolated draw and cleanup. See the
[game-loop contract](game-loop.md). Edit, stop and rerun to rebuild; automatic
hot reload and device-loss recovery are not implemented by this command.

Local installed-package acceptance creates the project through the installed
The original #171 installed-package acceptance creates the project through the installed
npm command, builds the unmodified template natively, and completes the full
web build from that same source. A bounded copy adds capture/state/cleanup
observations for native execution. It renders the 800x450 greeting and square,
loads `assets/welcome.txt` and cleans up once on Radeon DX12. This does not prove
visible presentation, compiled-starter browser rendering or a clean machine's
visible presentation, the new fixed-lifecycle template, compiled-starter browser rendering or a clean machine's
shader DLL packaging.

The installed-package CI check verifies default creation from the exact packed
engine and rejects missing compilers and unsupported targets. Unit tests cover
manifest drift, existing-project preservation, asset inventory and the local
server. See [the retained acceptance report](evidence/windows-starter-cli-v1.md).

The fixed-lifecycle follow-up repeats fresh installed default creation, native
build and the bounded greeting/asset/cleanup run using the revised template,
then completes its full web build. See [the lifecycle evidence](evidence/fixed-game-lifecycle-v1.md).
The browser runtime still needs to qualify this complete starter.
52 changes: 23 additions & 29 deletions docs/windows-engine-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ first nine-scene Radeon evidence are in draft PR #154. Follow-up work starts at
| #127 Vulkan PT correctness | Three deterministic progressive and motion runs, both negative controls, finite intermediates, reset/lighting/rigid-motion checks, retained report | Canonical hardware gate, all four focused temporal tests, and CPU reference sanity check pass on Radeon/Vulkan; [report](evidence/issue-127-windows-vulkan-v1.md) and [raw evidence](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-155-windows-vulkan-20260910) published |
| #128 Windows image discrepancies | Identify the first incorrect stage or document a reviewed backend-specific baseline decision; rerun the full strict corpus and reproducibility checks | Cutout and surface corrections pass all nine Radeon images. At #159 source `d610d6a`, full runs 2 and 3 pass every configured check and reproduce 257 artifacts byte-identically with matching metadata and timing differences inside existing noise bounds. Earlier invalid runs retain their failures; named hardware acceptance remains separate |
| #135 / #149 temporal reconstruction | Enforced motion/producer/quality-preset corpus, representative scenes, fractional/native and frozen A/B timing, memory/resize checks, platform evidence | Device/resource, stationary SSGI, and profiler fixes are retained. The surface correction passes original HD startup limits and 154,720 analytic receiver checks on Vulkan, DX12, and hosted Metal; 93 local goldens pass, including lighting recovery. The full Radeon corpus passes twice. Wider representative scenes, frozen A/B performance, memory/resize, and platform acceptance remain open |
| #140 integration gates | Same required local/hosted lanes pass on exact source; release package startup and all-example evidence | #160 fixes silent Windows CI non-execution and MSVC PATH ordering. #161 passes the actual native engine build and all 20 native links locally and in hosted CI. #162 fixes the focused DX12 failures; #163 fixes camera-history reset. #164 passes all 22 hosted Tests jobs using an explicit FXC Windows lane. The underlying WARP/DXIL crash remains open. A separate layered-material correction passes the full local FXC shared suite and all 93 DXC/Vulkan goldens. Fresh installed headless scene/direct-2D rendering and cleanup pass through #169. Visible presentation and release packaging remain open. #170's initial Windows shared job again crashes despite FXC; serial mitigation awaits hosted qualification |
| #140 integration gates | Same required local/hosted lanes pass on exact source; release package startup and all-example evidence | #160 fixes silent Windows CI non-execution and MSVC PATH ordering. #161 passes the actual native engine build and all 20 native links locally and in hosted CI. #162 fixes the focused DX12 failures; #163 fixes camera-history reset. #164 passes all 22 hosted Tests jobs using an explicit FXC Windows lane. The underlying WARP/DXIL crash remains open. A separate layered-material correction passes the full local FXC shared suite and all 93 DXC/Vulkan goldens. Fresh installed headless scene/direct-2D rendering and cleanup pass through #169. Visible presentation and release packaging remain open. #170's initial Windows shared job again crashes despite FXC; the serial follow-up and #171 each pass all 22 hosted Tests jobs. The driver root cause remains open |
| #138 capability fallback | Actual constrained-adapter startup and relevant forced-tier corpus, truthful capability outputs | Existing implementation/evidence preserved; physical constrained-limit acceptance still needs proof |
| PR integration | Reviewable changes, passing required checks, full issue evidence, merge-ready rendering branch | #147 and the stacked fixes #154–#170 remain drafts; no merge performed |
| PR integration | Reviewable changes, passing required checks, full issue evidence, merge-ready rendering branch | #147 and the stacked fixes #154–#171 remain drafts; no merge performed |

## Engine work retained in scope

Expand Down Expand Up @@ -54,33 +54,27 @@ audit are saved in `tools/quality/out/windows-engine-plan/plan-requirements.json

## Current next steps

1. **Finish real compiled-game browser acceptance (#74/#142).**
The [compiled-game gate](evidence/compiled-web-startup-v1.md) catches a gap
in the earlier JavaScript-driven renderer check: Perry returned success for
unresolved imports and emitted a game without the engine calls. The corrected
preparer installs the exact checkout as a dependency, rejects unresolved
imports and inspects the actual WASM import table. A local recording-FFI probe
passes callback/cleanup and explicit startup-fault controls; hosted rendering
must still produce the exact frame. Perry's plain throw propagation remains
a separate limitation found during this work.
2. **Qualify the Windows CI mitigation (#140).**
All 22 Tests jobs passed through #169. #170's initial shared-library job then
hit an access violation despite FXC. The next attempt serializes the Windows
harness while retaining every assertion. The local serial library passes
489 tests with one existing ignored test; that does not establish the crash's
cause or qualify every helper on WARP. Physical Radeon DX12/DXC and Vulkan
image evidence remains distinct from hosted software rendering.
3. **Complete the starter and example experience (#142/#145).**
The installed web command works on Windows. Fresh native packages render
exact scene/direct-2D frames and simulate Jolt locally and in hosted CI.
[Shared cleanup, corrected example palettes and Pong pause replay](evidence/windows-game-cleanup-v1.md)
are [published at #169](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-starter-lifecycle-20260911).
All 20 canonical native examples link. The [starter command](starter.md)
creates a project from an installed package; its unmodified native/web builds
and bounded native greeting/asset/cleanup run pass locally. Default creation
now carries the exact engine archive, with hosted packaging checks pending.
All-example web/runtime acceptance, fixed updates, visible native presentation,
packaged DXC/DXIL and general Windows long-path support remain incomplete.
1. **Complete the starter and example runtime experience (#142/#74).**
[#170's real compiled game](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-compiled-web-20260911)
passes exact browser pixels, eight frames, one cleanup and its explicit startup
fault control. [#171's installed commands](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-starter-cli-20260911)
pass default project creation and setup-error controls in hosted CI. The full
starter's assets/text browser rendering and canonical example runtime matrix
remain open. All 20 native examples compile and link.
2. **Finish and qualify fixed lifecycle integration.**
The [fixed lifecycle candidate](evidence/fixed-game-lifecycle-v1.md) passes pure
native/WASM timing and hook-order contracts, plus exact installed rendering
and cleanup on Radeon DX12/Vulkan. Its revised installed starter passes native
build/render/asset/cleanup and the full web build locally.
New hosted checks require the lifecycle counters in native and browser games.
Pause/focus policy and device-loss recovery remain separate.
3. **Continue Windows integration and packaging (#140/#145).**
Both #170 and #171 pass all 22 hosted Tests jobs with a serial Windows harness.
The original access violations remain retained and their root cause unresolved.
Installed headless scene/direct-2D modes render exact frames, simulate Jolt and
clean up once. Visible presentation, packaged DXC/DXIL, clean-machine starter
setup and general Windows long paths remain incomplete. Physical Radeon
measurements and hosted software rendering remain distinct evidence.
4. **Complete wider graphics and performance acceptance.**
[Two strict full Radeon runs at #159](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-ssgi-surface-20260911)
pass all nine images and reproduce 257 artifacts byte-identically. Rerun
Expand Down
3 changes: 3 additions & 0 deletions scripts/ci-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,8 @@ run_component() {
tools/ci/compile_web_game.py \
tools/ci/compiled_web_smoke.py \
tools/ci/test_compiled_web_smoke.py \
tools/ci/fixed_step_smoke.py \
tools/ci/test_fixed_step_smoke.py \
tools/ci/test_compile_examples.py
"$python_cmd" -m unittest \
tools/quality/test_run.py \
Expand All @@ -300,6 +302,7 @@ run_component() {
tools/ci/test_web_smoke.py \
tools/ci/test_compile_examples.py \
tools/ci/test_compiled_web_smoke.py \
tools/ci/test_fixed_step_smoke.py \
-v
hr "visual metric and fault-engine tests"
cargo test --release --manifest-path tools/bloom-diff/Cargo.toml
Expand Down
Loading
Loading