feat(framework): add Octane as the third first-class framework - #203
Draft
doodlewind wants to merge 1 commit into
Draft
feat(framework): add Octane as the third first-class framework#203doodlewind wants to merge 1 commit into
doodlewind wants to merge 1 commit into
Conversation
Octane (React's programming model, compiled) joins Solid and Vue Vapor: universal-driver renderer over the native tree, full 15-component surface, use-prefixed frame hooks, app.octane.tsx variants for all 8 demos, playground in-browser compilation, byte-exact PPSSPP goldens, and a three-framework PPSSPP benchmark (docs/bench/) per the PR #6 methodology. The PSP host gains an arena-pressure GC; framework=octane builds stub octane/profiling to dodge a WeakMap pinning pathology in the pinned QuickJS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Octane (https://github.com/octanejs/octane — React's programming model, compiled; by Dominic Gannaway) joins Solid and Vue Vapor as a first-class PocketJS framework. This is the React-model path PR #6 concluded was impossible with original React: Octane's universal client target compiles JSX + hooks into static host plans + dynamic slot batches executed by a
UniversalHostDriver— no DOM, no VDOM — which maps directly onto the nativeui.*tree.What's in here
Adapter (
framework/src/*octane*)renderer-octane.ts— the "pocket" universal driver: host command batches (create/update/insert/move/remove/destroy) applied to the shared NodeMirror arena viaapplyProps;#texthosts throughcreateTextNode/replaceText; portals as a driver capability minting overlay hosts transactionally. Re-exports the host-neutral universal ABI (octane/universal/native— QuickJS-safe).components-octane.tsx— the full 15-component surface (View/Text/Image/Sprite as hand-lowered universal plans; Screen/Focusable/FocusScope/FocusGrid/ActionHandler/Portal/Modal/ActionBar/Grid/Lazy/Gallery as Octane function components).frame-octane.tsx/lifecycle-octane.ts—useFrame,useButtonPress,useSpriteAnimation(use-prefixed: the Octane compiler slot-keys custom hooks by theuse[A-Z]call-site convention; non-hook names silently collide their internal slots).index-octane.ts— render/mount; the frame handler drains Octane's microtask renders inside the frame viaflushUniversalSync, so state commits land in the same tick as their input.Compiler/build
jsx-plugin.ts: thirdFRAMEWORKSentry,OCTANE_RENDERER_DESCRIPTOR, octane compile branch (collector pass on pristine source →octane/compileruniversal lowering), generalizedframeworkVariantPath(app.octane.tsxsiblings), cache-key includes the octane version.pocket.jsonschema ("framework": "octane"), package exports./octane/*, tsconfig paths.The memory hunt (the fun part) — octane demos initially died mid-run on PPSSPP (
InternalError: out of memorywith megabytes of arena free, opaquenullexceptions, wedged frame loops). A scratch cargo probe pinned to the exact libquickjs-sys revision (eval the real bundle + stubui+ frame loop + heap histograms + agc_decrefroot dump) unpicked a three-layer cause:trackedComponentsWeakMap is written even afterprofiler.stop()— with per-render closures as values.wm.set(key, {back: key})never collects; bellard master has the same code).Fixes, in order: framework=octane builds alias
octane/profilingto a no-op stub (framework/src/octane-profiling-stub.ts— also removes per-render profiler work from the frame budget); the PSP frame loop gained an arena-pressure GC (JS_RunGCwhen a frame leaves the bump pointer >256 KiB past the last collection — steady-state Solid/Vapor guests never trigger it); and the Octane ports keep always-animating state in leaf components, committing state on visible steps (music'sEqualizer/ProgressLinesplit, stats'StatTiles). Along the way the PSP host's exception logger learned to print tag + message + stack (anullexception is QuickJS's no-room-for-the-Error-object throw), and one solid golden (hero.boot, a single background pixel ±2) was re-baselined — it predates this branch; this is the first full solid e2e since the #149-era goldens.Result: all three frameworks pass byte-exact PPSSPP e2e, and the arena-pressure GC keeps every demo alive through its capture/bench window. What it does not do is make frequent re-rendering free on this engine revision: each Octane re-render still retains a residue the collector cannot reclaim (~80–115 KB/frame measured across the per-frame-state demos), so an always-animating screen has a session memory horizon — the stats bench window ends at 16.87 MiB of a 17.06 MiB arena, 196 KiB from the wall. The growing live set also inflates the engine's own auto-GC time, which dominates those demos' per-frame numbers in the benchmark below. The real cure is a quickjs-rs GC repair + repin, filed as follow-up; Solid, Vue Vapor, and Octane apps without per-frame state are unaffected.
Demos — all 8 Vue Vapor demos got
app.octane.tsxtwins (hero, cards, stats, library, settings, notifications, music, gallery), sim-verified text/pixel parity against the vapor variants, PPSSPP byte-exact goldens intests/goldens/psp-octane/(7 bench-table demos), plus newtests/goldens/psp-vue-vapor/coverage.Playground + docs — third framework toggle with in-browser octane compilation (pure-JS compiler, no WASM shim needed),
pg/runtime-octane.jsbundle, import-map entries;frameworks.mdreworked for three frameworks, octane fences added to all 29:::framework-codeblocks, README/DESIGN/RUNTIMES/home/INTEGRATION updated.Benchmark —
tools/bench-ppsspp.tsregained the PR #6 cross-engine axis:--frameworks=solid,vue-vapor,octane, per-(app, framework) grouping, geomean-vs-baseline ratios with optional--bootstrapCIs;tools/bench-chart.tsrenders the matrix SVG. Full three-framework results below.Benchmark (PPSSPP software renderer, per PR #6 methodology)
7 samples per (app, framework) cell across the 7 bench demos, deterministic PPSSPP headless (PPSSPP
676724ee5e, base3c14b47), geomean-vs-Solid ratios with mulberry32-seeded 5000-resample bootstrap 95% CIs. Full report + chart indocs/bench/(three-frameworks-ppsspp-2026-07-29.{md,json,svg,png}); raw samples reproducible via the specs table intools/bench-ppsspp.ts, and the report is rebuildable from a raw dump with the new--from-raw=<raw.jsonl>mode.Average frame work by app:
Reading the 15.6×: it is not Octane's render cost — it is the engine pathology above compounding. In the demos with per-frame state (hero, stats, notifications, music, and to a lesser degree library/settings) each tick's re-render grows the unreclaimable live set, and the engine's auto-GC walks it every collection, so avg frame work is dominated by in-engine GC (stats: 254 ms of its 280 ms is
avg_js, with another 22 ms in the arena-pressure GC bucket).cards— the one demo with no per-frame state — shows Octane's true steady-state cost: 9.66 ms, 1.86× Solid, still inside the 16.7 ms 60 fps budget. Boot/eval/bundle are engine-pathology-free and land essentially on top of Vue Vapor. These numbers are honest to the current pinned engine; they should collapse toward thecardsratio once the quickjs-rs GC repair lands.React status: unchanged from PR #6 — original React still has no viable PSP path; Octane is the React programming model that does run, measured above.
Authoring notes (also in docs/frameworks.md)
setState(a same-frame handler write would be clobbered by a stale read).stylewhose value changes across renders (unchanged values diff away and are safe).mount(App)— JSX in a call-argument arrow is a fail-closed universal-target error.Validation
bunx tsc --noEmitcleanbun run testgreen end-to-end (includes the newtests/octane-smoke.test.ts+hero --framework=octanebuild in the chain)tests/goldens/psp/,tests/goldens/psp-vue-vapor/(new),tests/goldens/psp-octane/(new);hero.bootre-baselined (single background pixel ±2, pre-dates this branch)docs/bench/bun site/build.tsgreen — playground ships currentpg/runtime.js/pg/runtime-vue-vapor.js/pg/runtime-octane.js🤖 Generated with Claude Code