Commit graph

809 commits

Author SHA1 Message Date
Admin
ec80213c4f Arcade sim: stop copying the world every tick (−99.93% bytes on terrain scenes)
step_world cloned two things per tick purely to dodge a borrow: the whole
Terrain (heights AND colors) and every static/kinematic Entity at 208 bytes
each. Terrain dominated — a 257^2 field is 1.3 MB/tick, 79 MB/s of memcpy at
60 Hz, on a world containing seven entities.

- Terrain: copy -> borrow, splitting the struct borrow the way the bottom of
  the same function already did
- Statics: 208-byte Entity -> 48-byte Solid. This one MUST stay a copy —
  movers sweep against kinematic poses from BEFORE this tick's integration
  and that ordering is load-bearing — but it only ever needed
  id/kind/pos/half/vel
- owner_pose: skip building the table when nothing is attached (most worlds)

  scene                ms/tick          B/tick
  demo                 0.002 -> 0.003   15,140 -> 4,796      (-68%)
  racing-ish (129)     0.007 -> 0.002   362,316 -> 8,576     (-98%)
  terrain 257          0.019 -> 0.001   1,323,964 -> 896     (-99.93%)
  large (500 static)   0.063 -> 0.056   591,386 -> 82,382    (-86%)
  stress (2000 static) 0.583 -> 0.457   2,353,936 -> 327,812 (-86%)

Result-neutrality proven, not assumed: new mover_golden.rs covers what
rigid_dynamics.rs doesn't reach (terrain cliffs/floors, sweeps, platform
carry, attach pin, projectile lifetimes, auto-face) and its golden hash is
identical before and after — verified by stashing only the source changes
and re-running, not by re-baselining. Also includes a test pinning the
pre-integration snapshot ordering, so a future "obvious" simplification that
reads live positions gets caught.

Leak check: 36,000 ticks (10 simulated minutes) of a busy world with
projectiles spawning and expiring — RSS flat at 3.8 MB, +0.4% drift.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 08:50:42 +02:00
Admin
4936c1b783 Arcade audio: sampled playback, mixer, and gameplay-driven emission
libs/game/audio (77 tests): WAV decoder (8/16/24/32-bit int, f32/f64,
total on malformed input), Ogg container with packet reassembly, sample
bank with on-demand decode + linear resample + LRU eviction and pinning so
a playing voice can't be evicted, and a 24-voice mixer with equal-power
pan, playback-rate pitch, click-free fades, priority+age voice stealing and
a limiter. Generation-tagged voice handles mean a stale handle cannot
retune a reused slot. The limiter test caught a real bug: peak was being
measured per-voice, but what clips is the SUM.

Emission is the point — sounds come from the engine observing gameplay,
not from script calls: Material/MaterialPair (order-independent, the softer
material names the sound), an impact curve mapping closing speed to
gain/pitch, and an AudioDirector with repeat-avoiding variant selection,
per-category volumes, per-pair cooldowns and a per-frame cap. A 200-contact
frame yields <=6 sounds and the cooldown map is proven not to leak.

Blocks now emit their own audio: car engine tracking revs, skid on lateral
slip, suspension thud on landing; character footsteps timed off the WALK
CYCLE rather than a timer, so feet and sound stay together when slowing;
jump/land scaled by fall speed; plane engine by throttle; lap and win
stings. RNG isolation proven: heavy audio work interleaved with world-rng
draws leaves the drawn sequence bit-identical to a silent run.

KNOWN GAP, reported rather than hidden: every Kenney audio pack is Ogg
Vorbis only (471 files, zero WAV), and the from-scratch Vorbis decoder is
NOT correct yet — setup header parses exactly, channels/rate/frame count
and envelope shape are right, but floor magnitudes come out ~75x low and
best correlation against an afconvert reference is 0.67. Two real bugs were
found and fixed en route (type-1 residue filled one codeword instead of the
partition; MDCT post-twiddle carried the pre-twiddle's 1/4 term). The
reference test is committed as #[ignore] with its measurements in the
message so it stays runnable. Ogg stays flagged unplayable in the asset
index; WAV and the --transcode path work today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 08:47:06 +02:00
Admin
623ee745e4 Arcade render: packed vertex formats — instance -27%, vertex -62%
Quest is vertex-bandwidth bound, so this is the measured headline:

  cube instance             176 B -> 128 B  (-27%)
  skinned character vertex   64 B ->  24 B  (-62%, re-uploaded EVERY frame)
  shadow mesh vertex         64 B ->  24 B  (-62%)

The Knight went 238 KB/frame -> 89 KB/frame: CPU skinning re-uploads the
whole buffer each frame, making it the largest recurring saving available.
Instance sizes are read from the compiled shader (RenderStats::
instance_floats), not counted by hand. The instance win was pure
duplication: sun_color/sun_sky/sun_ground/fog_color are identical for every
instance in a batch — 12 floats per cube — and moved to uniforms.
fog_density stayed per-instance because shadows switch it off individually.

Unblocked by adding geom.GameMeshVertex in draw/geometry_gen.rs and making
the existing pack_pair_f16/pack_unorm8x4 public, rather than writing a
second f16 rounding implementation that could drift from the first.

Three constraints found, worth keeping:
- Vertex attributes here are f32-ONLY. Compression means bit-packing into
  f32 lanes; unpack2f16/unpack4u8 are builtins on every backend
- Pod vertex structs need flat f32 fields, not Vec3f — std140 pads a vec3
  to 16 B and the repr(C) size assertion fails at runtime
- In the shader language `let` is immutable and helpers can't be forward-
  referenced, so the octahedral decode uses branchless step(0,v)*2-1: the
  sign() builtin returns 0 at 0, which would collapse the fold on
  axis-aligned normals

Tape BYTE_IDENTICAL; captures verified after each conversion (shadows
unchanged by packing, Knight correct with packed normals/UVs).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 08:41:31 +02:00
Admin
969cb2c3d9 Arcade: Kenney CC0 asset library with an AI-queryable index
75 models (5 KenneyNL starter kits, .glb, pinned commits + sha256, 2.8MB)
and 556 sounds (7 packs via kenney.nl content-hashed URLs, each zip
sha256-verified, 13MB). Nothing large enters git: dirs are gitignored, only
CREDITS.toml and .gitignore files are tracked.

The index is the point — an AI cannot use a library it can't name:
- id is kenney/racing/vehicle-truck-yellow, anchored to where the file
  LIVES, not its category, so retuning the category tree never invalidates
  a saved game
- Filename tokens are the floor; the value is two hand-curated alias tables
  (76 model rows, 116 audio FAMILY rows — Kenney's footstep_wood_000..004
  collapse to one family, so 556 files stay maintainable) spanning
  synonyms, kid vocabulary and misspellings (vehical, hosue, motercycle),
  function over identity ("something to hide behind"), colour/size/
  material, and theme, plus ~190 query-time synonym expansions
- AssetKind model/sound/music so a 30-second track can't be returned as a
  hit sound; GLB probe reads skins -> rigged, animations -> animated
- FIND_MODEL tool descriptor (provider-neutral plain data) + compact
  results; library_summary() is 469 chars for 632 entries and provably
  doesn't grow with the catalogue; resolve_or_explain() rejects
  hallucinated ids with near-misses; local_spawn() gives the local
  librarian a best match plus a confidence blending strength with margin

HONEST GAP: every Kenney audio pack is Ogg Vorbis only — no WAV exists
upstream — and this tree has no vorbis decoder. Sounds are indexed and
searchable but NOT playable: entries carry decodable:false, the agent JSON
emits playable:false so a game cannot fire a silent sound, and
--transcode converts via ffmpeg when present. A real decoder is the fix.

Three bugs found by testing, all fixed: sci-fi-sounds.zip ships a directory
with no owner-write bit so that pack alone silently extracted 0 of 73 files;
prepositions matched phrase aliases ("...at the roadworks" hit "something to
shoot at"), so function words must be dropped, not down-weighted; and exact
names lost to incidental aliases (a coin SOUND outranked the coin MODEL).
Miss list left visible at 2/52 rather than tuned away — both are defensible
answers against over-narrow expectations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 08:31:26 +02:00
Admin
a529923c13 Arcade render: CPU light bake, silhouette shadows, instance slimming
CPU light baker (bake.rs): per-static AO (5 face samples x 8 Fibonacci
rays), a sun-visibility term, and a trilinear probe lattice for moving
objects — all folded into instance colours the renderer already sends, so
zero extra bandwidth and zero GPU cost. Demo world, release: AO 15us,
sun 34us, probes 61us. The split is deliberate — AO is the expensive half
and is sun-independent, so a day/night cycle only pays the 34us. A ray
starting above the heightfield peak and heading up skips the terrain march
entirely: that early-out took the probe pass from 5.4ms to 61us.

Silhouette shadows (shadow_mesh.rs) replace the flat oriented quad: caster
points -> projection along the sun -> 2D convex hull -> fan triangulation,
which has no self-overlap and therefore cannot double-darken in an alpha
blend (the reason naive projected geometry bands). Draped over terrain
(vertices drop to ground height, long edges subdivide), soft rim from a
penumbra ring that widens with height, statics cached against (world edit,
sun position) — every shadow in a frame is ONE geometry, ONE draw call.
Z-fighting handled structurally: offset along the RECEIVER's normal with a
slope-scaled term (world-up slides the shadow on a slope), depth test on,
depth write off.

Instance stream 176B -> 128B (-27%), measured from the compiled shader:
sun_color/sun_sky/sun_ground/fog_color were 12 floats of identical data on
every cube and moved to uniforms. fog_density stays per-instance because
shadows switch it off individually.

RNG isolation is structural: GameWorld has no bake field and the ray set is
fixed, so there is no RNG here to share with the sim. Tape BYTE_IDENTICAL.
Deleted an unwired SDF-blob path and the dead project_box_shadow call site
rather than leaving two shadow implementations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 08:28:41 +02:00
Admin
bdbc946012 Arcade M6+M7: packaging/sharing with sandboxed installs, and the pretty pass
Committed together: both streams landed in libs/game/script, so splitting
them would produce two commits that don't compile.

M6 — packaging and sharing
- libs/zip_file gains a writer (store + deflate); real `unzip -t` validates
  our archives in an interop test. Packing is deterministic (fixed
  timestamps, sorted entries), so a package can be addressed by its own
  sha256 — which is what makes the registry's digest check mean anything
- libs/game/pkg: .arcade format (game.splash + manifest.toml + assets),
  total manifest parsing (attacker bytes always yield a Manifest or an
  error, never a panic; non-finite numbers refused rather than defaulted),
  registry client that verifies sha256 INSIDE download so tampered bytes
  never reach the extractor
- Hardened extraction: absolute paths, drive letters (C:x is absolute on
  Windows), UNC, backslashes, .., NUL/control chars, symlink members (via
  mode bits), duplicate names (the ambiguity IS the attack), declared-size
  caps checked before decompressing plus a post-decompress check, entry/
  total/archive caps, and a post-join re-check that the resolved parent is
  still inside the destination — which catches a pre-existing symlink the
  name test cannot see. 4000-round mutation fuzz with a canary file beside
  the destination; a 320 MB deflate bomb under 1 MB on the wire is refused
- Capability stripping rebinds fs/run/net to FRESH EMPTY OBJECTS rather
  than shadowing known verbs, so there is no hole the day someone adds one.
  Applied before the game handle is registered. Vacuity guard: an unstripped
  isolate genuinely reads a file, so the sandbox tests can't pass for
  unrelated reasons. Browser-installed games load Trust::Downloaded

M7 — pretty pass
- GameSun adopts draw::SceneSun (axis-converted: SceneSun is map-space
  y-south/z-up, games are y-up). Shaders compute hemisphere ambient +
  direct instead of each hardcoding its own split; defaults collapse the
  new formula to the old constants exactly, so unifying did not restyle
  existing games. write_into is the single write path — "one sun" is
  compiler-enforced
- Projected shadow geometry: the caster's silhouette along the sun, fitted
  in the sun's own (u,v) frame, so it stretches as the sun swings. Nearest
  N casters get projection, the rest blobs; one instance in the existing
  alpha batch, no extra pass. 0.6us for 24 casters
- Two pre-existing shadow bugs found via capture: the pipeline blends
  premultiplied, so unpremultiplied dark RGB ADDED light instead of
  removing it; and shadows were fogged, mixing them toward the bright
  horizon so a distant shadow came out lighter than the ground it darkened
- Particles are structurally isolated from the sim: GameWorld has no
  particle field and step_world has no particle code — the renderer owns
  simulation and its own RNG. particles_never_advance_the_world_rng
  interleaves particle verbs with real rand() draws over 32 rounds and
  asserts both the RNG state and the drawn stream are identical
- game.sfx_at with listener-relative gain/pan and a near-field ease so a
  sound at your feet doesn't flip channels; 2D verbs unchanged
- apps/arcade/BUDGETS.md: measured particle/sim costs, Quest columns marked
  as estimates (the real particle limit is fill rate, not CPU)

Tape probe BYTE_IDENTICAL. Not done: arcade has no audio backend, so
positional sound is implemented and tested but not audible there yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 03:27:09 +02:00
Admin
c042c06eba Arcade M5: multi-Claude co-editing — intent log, semantic rebase, soft leases
libs/game/coedit (zero deps, so merge logic is testable without a socket or
a VM). Not a CRDT: transactions are host-serialized into an append-only
generation history, and a conflict is answered by handing the author the new
base so THEY re-derive their intent.

- Transaction carries the author's whole intended file, not a patch: the
  diff against its declared base is derived host-side, so a stale or
  malformed patch can never be applied — and an AI writes whole files anyway
- diff3 over lines with LCS anchors. Merge::Conflict deliberately carries NO
  merged text: a half-merged game file that still parses is worse than an
  honest rejection. Conflict -> Rebase{new base + per-generation summary of
  what landed underneath}. An edit already present in the tip is refused as
  NoChange rather than appended as an empty generation
- Leases are advisory as designed: a test asserts a submit SUCCEEDS while
  another author holds the lease. They shape who chooses to edit; they never
  gate the log. TTL expiry means a crashed author cannot lock a region
- Wire: coedit is reliable-channel ONLY — a test signs a valid submission,
  sends it by datagram, and asserts it is ignored, so no datagram can
  rewrite the game. Every response is addressed, never broadcast
- Arcade bridge routes in exactly one place; local agent and remote authors
  share the queue and the rules, and a test asserts the local agent gets
  rebased identically when it loses a race. Remote players map to
  AuthorId(player+1) so a client holding player id 0 cannot impersonate the
  host's agent
- 200-round deterministic fuzz: 4 authors submit against deliberately stale
  bases, rebase, resubmit — asserting linear append-only numbering, no
  generation claiming a base from the future, and that replaying accepted
  diffs from generation 0 reproduces the head exactly

Two real bugs found building it: validate used `?` on the base lookup, so an
unknown base returned "no refusal" instead of UnknownBase; and
MAX_COEDIT_SOURCE (512 KiB) exceeded MAX_FRAME_BYTES (256 KiB), so the host
would have accepted a source it could never hand back inside a Rebase,
stranding the next author on an answer that never arrives. Both now 192 KiB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:45:31 +02:00
Admin
5817c6b0de Migrate gamemaker onto game_script: -2647 lines, tape still byte-identical
game_view.rs 4525 -> 1878. Deleted in one marker-verified cut: the 84-arm
game_dispatch chain, all arg/option/value helpers, spawn_entity/spawn_terrain
/spawn_car/spawn_plane/spawn_character/spawn_block_body/set_brain, the
GAME_API table, suggest_verb/edit_distance, and the duplicated CallbackTable.
register_game_handle now binds game_script's verb table; unknown verbs keep
the same hard-fail plus did-you-mean. Kept: run_tick and everything parity
depends on — tape input, gamepad poll, camera mailbox, perf channels, agent
RPC, save/log flush.

Rollback deliberately still gamemaker's hand-written WorldSnapshot, not
GameWorld::clone(). Clone is strictly safer and is what Arcade uses, but
eval_body is the parity-critical path and the snapshot is what the tape was
established against — swapping it deserves its own tape run, not a rider on
a 2700-line deletion.

Three pre-existing divergences fixed in game_script (sfx/beep/jingle predated
the 31-verb port and had drifted): beep's `to` defaulted to 0.0 instead of
freq, so every beep swept to silence; beep never read its wave option at all
(allowed but unused, and AudioRequest::Beep had no wave field); gain and
jingle ms were off. Audio drains host-side in run_tick and after a successful
eval so a startup jingle isn't held a frame; a failed eval discards its queue.

Regression the migration exposed: block verbs spawn through the shared box
path, which re-validated keys against the BOX allow-list, so game.car(...)
logged 8 bogus "unknown option" warnings per eval straight into the channel
the agent reads. Added spawn_entity_unchecked for the block path; a real typo
on game.box still warns. Racing fixture: 8 warnings -> 0.

Tape probe BYTE_IDENTICAL (re-run after the audio and warn changes too).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:43:08 +02:00
Admin
564c1b52dd Arcade M3 (part 1): MR/VR stage modes, XR input, settings panel, authoring inbox
Stage as presentation, never simulation. Stage{mode, origin, yaw, scale} is
applied as the scene draw list's view_transform uniform — one value per
frame, so sky/terrain/cubes/skinned characters all move together, cached
static slabs never invalidate (they hold stage-independent world transforms)
and per-instance cost is zero. The view matrix cannot carry this: in XR the
platform overwrites camera_view with the runtime's eye matrices every frame
(openxr_opengl.rs:20-25), discarding anything the app wrote.

- MrDiorama: world scaled onto an anchored slab, sky/fog/terrain-horizon
  suppressed (the room IS the environment), shadow-catcher quad so it looks
  planted. VrFullScale: 1:1, environment intact. Flat: unchanged
- stage_invariants.rs proves the design point: two worlds run 120 ticks, one
  switching flat->MR->VR->MR(new anchor)->flat mid-run, hashes bit-identical
  at every switch, with an assert_ne against a fresh world so the equality
  isn't vacuous. The stage has no API by which it could reach the world
- Stereo needed NO work, and rendering twice would have been wrong: XR uses
  single-pass GL_OVR_multiview2, so draw_pass.camera_view compiles to an
  indexed [VIEW_ID] lookup and the GPU rasterizes both eyes from one
  encoding. The existing draw path is already stereo-correct
- XR input maps controllers/pinch onto the same per-player InputState the
  net layer sends — an XR player is just another player to the sim
- Quest APK packages (arm64-v8a, passthrough/handtracking/anchor/colocation
  manifest bits) with no cargo_makepad changes
- Settings panel: provider/model pickers, masked key entry, pair button
  showing LAN URL + confirm code, active-tier status line
- Authoring inbox: keyless clients' Intent::Authoring queued for the host's
  agent, bounded and refusing with a reason — every entry eventually costs a
  paid call and it is filled by peers who hold the lobby key but are not
  trusted terminals

Pre-existing startup crash fixed: run_tick held a RefMut across
self.world.borrow() (`let _ = w` drops the reborrow, not the RefMut it came
from), aborting the demo path. Reproduced at HEAD with changes stashed.

Not wired, needs a device: passthrough is requested via
StageMode::wants_passthrough() but not handed to xr_passthrough (arcade has
no XR root widget yet); env-depth occlusion has a hook and no consumer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:32:22 +02:00
Admin
2160c28269 game_script: full verb parity with gamemaker (71 -> 102), pinned by test
Ports the 31 verbs game_script lacked — part, move_part, beam, attach,
detach, speed_mult, raycast, overlap_sphere, ground_peak, held, pressed,
axis, player_input, the cam_* readers and set_cam_* writers, save, load,
tone/tone_set/tone_stop, format, api, reset — verbatim, including the
details that bite: part's defaults and half-floor, move_part's only-given-
keys rule plus its leaves-the-static-slab redraw, attach's vec3-or-options
overload (vec3 parses first; the options path defaults to (0,1,0), not the
previous offset) with velocity zeroed, every documented clamp, raycast's
terrain-reports-as-minus-one convention, and save's strings-before-numeric
ordering (the numeric cast NaNs strings).

Audio stays host-installed: AudioRequest gained Tone/ToneSet/ToneStop/
StopAllTones with a ToneWave mirroring synth::Wave's parse fallbacks. tone()
must return an id synchronously, which a drained queue cannot do, so ids are
minted on Ctx and the host maps them to its own voices — script only ever
holds an opaque handle, so this is observationally identical. save/load
needed no hook at all: save_data lives on GameWorld and flushing was always
the host's job.

the_verb_surface_matches_gamemakers pins the count at 102. (Gamemaker's 98
match arms are 102 names — four are || aliases.) This unblocks migrating
gamemaker off its duplicate binding layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:29:34 +02:00
Admin
88183a924b Fix http_server dropping pipelined body bytes; add Intent::Authoring to game_net
from_tcp_stream wrapped the socket in a BufReader that was dropped on
return, so body bytes read ahead into it vanished and handle_post blocked
forever on bytes that no longer existed — one wedged thread per request
that sent headers and body in the same TCP segment. Browsers split the two,
which is why nothing noticed. The function now owns its buffer, reads to
\r\n\r\n, and returns the remainder alongside the headers for handle_post
to consume first. The websocket upgrade path had the identical exposure (a
frame pipelined with the upgrade was silently dropped) and consumes the
same prefix now; EOF mid-head returns instead of spinning to the 4096-line
guard. New tests cover headers+body in ONE write (the case that hung, with
the connection held open afterwards so a regression blocks rather than
passing on EOF), the split case, and a plain GET.

game_net: Intent::Authoring{text} + MAX_AUTHORING_TEXT so a keyless client
in a hosted room can route a creation request to the host's agent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:20:54 +02:00
Admin
cec4f5b380 Arcade M4: voice + AI tiers, game library, /pair key flow, and libs/game/script
- libs/game/script (new): the table-driven game.* binding layer game.md
  called for — 71 verbs in a HashMap built once per isolate, replacing
  gamemaker's 84-arm linear chain. spawn_entity/spawn_terrain ported
  verbatim so fixture terrain matches bit-for-bit; generation-tagged
  callback slots; streaming eval + hot reload. Rollback snapshot is
  GameWorld::clone() — M1a made the world Clone, and a clone cannot forget
  a field, which is the exact bug class that put next_id in M0r's fix list.
  (Gamemaker still runs its own copy; migrating it is a follow-up.)
- Capability tiers: Voice (VAD + Whisper + local judge) -> VoiceUnfiltered
  (push-to-talk, every utterance costs a call) -> Chatbox (typing is the
  gate). Text box in every tier, mic only above Chatbox. The chain sits
  behind the local-llm feature so Quest/mobile never link a backend they
  don't have
- Librarian: the local model is an optional override (None = no opinion),
  never a gate — a flaky or absent model can only sharpen a decision.
  Beneath it, deterministic word-overlap matching for load-by-description,
  restart, and manifest-clamped knob writes. Creative requests deliberately
  do NOT match an existing game (tested). Locally-answerable utterances are
  dropped before they reach the cloud
- /pair: self-contained page (no external URLs, asserted), 4-digit confirm
  code so a room of headsets can't take the wrong key, 0600 config-dir
  storage documented as NOT a keystore — Android/iOS must move to the
  platform one before shipping. Key never enters a log, package, or error
- Racing fixture evals through the new dispatch (28 entities) and renders;
  hot-reload rollback verified live — a bad verb reports with a suggestion
  and keeps the last good world

Found (platform, unfixed — out of this task's scope):
HttpServerHeaders::from_tcp_stream buffers past the headers into a
BufReader, so a body arriving in the same TCP segment is swallowed and
handle_post blocks forever on a body that's already gone. Browsers split
the two, which is why nothing noticed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 02:05:50 +02:00
Admin
a8427cda75 Arcade M2b: multiplayer — players in the sim, tiered replication, host/join
A room of devices now plays one game: host simulates, clients send input
and render replicated truth.

- libs/game/sim/player.rs: Players roster on GameWorld, slot 0 is always
  this device. Player 0's input stays in the world's original held/pressed/
  pad/cam_yaw fields and is mirrored into the roster, which is what keeps
  single-player numerics bit-identical (tape gate confirms). Ids are never
  reused, so a stale reference resolves to None, never to somebody else
- The camera-movement knot resolved: world.player_move(p) rotates that
  player's axes by THEIR cam_yaw, carried in their input packet. Player 0's
  branch is the original expression character-for-character (the f32 cos
  widened to f64 kept deliberately — tidying it would move the numbers)
- libs/game/session/replication.rs: Shared = pos/vel/size/kind/tag;
  Derived = facing/anim/scale/glow/blob shadows, recomputed client-side and
  costing zero wire bytes; Local = camera/audio/effects. Statics never enter
  the per-tick stream
- Protocol (additive, version unchanged): EntityDesc + Descriptors message
  splits rare reliable construction data from volatile unreliable state —
  without it a joiner sees poses for entities it cannot build. EntityDesc
  carries pos because statics never appear in the state stream (the
  late-joiner test caught ground arriving at the origin)
- Script: game.players/player_name/player_entity/player_input/bot/on_join/
  on_leave; blocks gained owner: PlayerId so a car reads its own driver
- Arcade: ARCADE_HOST=1 / ARCADE_JOIN=<addr>; clients skip world
  construction and don't simulate
- Racing wire volume, 6 players x 60Hz x 200 entities: 2400 pps,
  20.9 Mbit/s up (audit projected 74 Mbit for the XR stack). Asserted in
  racing_scenario_wire_volume_fits_a_living_room

Two more not-a-playable-default bugs, same class as M1b's rng-at-zero:
Entity::default() leaves gravity_scale 0 (weightless wheels) and ground
without friction gives no traction — only the DSL path filled these in.

255 tests green; tape probe BYTE_IDENTICAL; xr/arcade/gamemaker build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 01:21:44 +02:00
Admin
c13c867547 Arcade M1b: libs/game/blocks — car/character/plane, brains, race kit; racing game in 72 lines
The thesis proof: examples/gamemaker/resources/fixtures/racing.splash is a
complete playable racing game in 72 lines (12-corner oval from a waypoint
loop, 4 cars, gates, standings, restart) with no physics, no AI and no lap
bookkeeping in script. Blocks run engine-side at 60Hz: Blocks::pre_step
(intent -> motion) before step_world, post_step after; Blocks is Clone and
snapshots beside GameWorld so a failed eval rolls both back together.

- game.car (4 suspension raycasts on a box3d rigid chassis), game.character
  (drives the existing mover sweep + owns idle/walk/run blending),
  game.plane; game.drive/autodrive/speed
- Brains: game.wander/chase/patrol/caught — the fixture's hand-rolled AI,
  absorbed engine-side
- Race kit: spawnpoint, checkpoint, place, race, standings, lap/rank/
  finished, score/score_of (Shared-tier data, ready for replication)

raycast_vehicle audit (defects documented in car.rs, still live in xr):
libm sin/cos in steering (unreplicable), a wrong side-impulse denominator
(iaj.dot(iaj) where it should be (I^-1 aJ).aJ), and an unguarded division.
Kept the structure, replaced Bullet's friction solver with an arcade force
model: suspension acts at the contact point, grip and drive through the
centre of mass, steering as yaw torque — no lateral force can generate
roll, so it is stable by construction rather than by roll_influence fudge.

Engine bug fixed at the source: GameWorld::new() left rng at 0 and
xorshift64* is a fixed point at zero, so rand() returned 0 forever for any
world built through the sim API. reset_content seeds on every eval, which
hid it from gamemaker entirely; found by a wander brain that never left home.

Tape probe byte-identical; racing fixture evals clean and drives (AI follow
the line, gates bank in order).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:54:22 +02:00
Admin
e1d0500771 Arcade M2a: libs/game/net — host-authoritative LAN transport + micro_serde hardening
Harvest-and-rebuild from xr/src/net after the adversarial audit (verdict:
transport promote-with-fixes, authority model rewrite).

- Authority: explicit Host/Client roles. Client->host Join/Input/Intent/
  Leave/Ping; host->client Welcome/StateBatch/Event/Bye/Pong. No per-object
  authority field and no takeover messages — authority theft (audit H-11)
  is unrepresentable, not merely blocked
- Auth: self-contained SHA-256/HMAC; every datagram and frame is
  magic|version|sender|payload|mac, verified BEFORE any peer state is read
  or written. Closes seq-window poisoning, spoofed kick, address hijack
- Endpoints are pumped, not threaded: nothing blocks, so the connect-flood
  stall (H-5) cannot occur and a full session runs deterministically in one
  test process. The host only ever accepts, never initiates
- Harvested: LZ4 frame codec (check-before-allocate), partial-tail drain,
  budgeted read/write loops, MTU batching, peer/config shapes. Frame cap
  4MiB -> 256KiB now that XR alignment payloads are gone
- Per-entity sequencing (a stale datagram drops only its stale members),
  rejoin seq reset, player cap, peer timeout, snapshot-based mid-join
- Measured 6 clients x 60Hz x 200 entities: 2880 pps, 3.13 MB/s (~25 Mbit
  up) — Quest WiFi viable without delta encoding yet
- Hostile suite: one test per audit attack + 3000-mutation fuzz that must
  never panic or wedge the host. 24/24 green
- micro_serde: String::de_bin no longer panics on invalid UTF-8, checked
  offset arithmetic, Vec::de_bin rejects counts the buffer cannot back and
  never sizes allocation from the wire (11/11)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:33:16 +02:00
Admin
17263b6666 Arcade M1a: box3d as the dynamics layer (hybrid — movers keep the parity sweep)
- libs/game/sim/dynamics.rs: box3d world inside GameWorld, body mirror
  RECONCILED against the sorted entity list per tick (merge-walk), so
  retain/rollback/reset are correct by construction. Statics + kinematics
  mirrored (kinematics via target transforms so resting rigids inherit
  platform velocity); smooth terrain as a box3d heightfield
- New BodyKind::Rigid + body:"rigid" with density/friction/restitution;
  game.push = mass-scaled impulse; set_pos/set_vel detected via bit-exact
  pose caches (no new dispatch arms). Sphere rigids roll on real spheres
- Entity.orient quat read back per tick; renderer builds quat instance
  transforms for rigids (no shader change). Step order: mover sweep
  verbatim -> reconcile -> world_step(dt, 4) -> readback; rigid-free
  worlds skip the solver
- GameWorld stays Clone via box3d snapshot round-trip (bit-identical
  continuation proven by test)
- Determinism: double-run equality + golden hash 0xa8a2baf71e4a564f
  (aarch64, debug and release). Perf: 0.038 ms/tick for 100 movers +
  50 rigids + 65x65 terrain (budget 2 ms)
- Tape probe BYTE_IDENTICAL; box3d crate untouched; arcade demo gains a
  kicked crate stack (captures verified: settles upright, then topples
  with rotated resting poses)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 00:04:26 +02:00
Admin
ac5ed14285 Arcade M1c: skinned animated characters (glTF skins/clips, KayKit, CPU skinning)
- libs/game/render/skin.rs: GLB container + owned-JSON parser, dense
  accessors, multi-primitive skinned meshes, skin + inverse binds, full
  node-hierarchy palette, T/R/S clips (linear/step), nlerp blending.
  Hermetic tests via an in-code 2-joint GLB; real-asset test skips with a
  hint when the download hasn't run
- DrawGameSkinned shader (PbrVertex + albedo texture, terrain-style
  lighting); skinned batch draws between opaque and alpha passes.
  CPU skinning for now (sub-ms at 3716 verts): the uniform_buffer GPU path
  has zero in-tree runtime consumers — SkinnedModel::palette is the seam
  for the GPU swap
- apps/arcade: download_assets.sh (KayKit pinned commit + sha256,
  idempotent, dir gitignored, CC0 license note in-repo); Knight patrols
  the demo world with idle/walk blending, yaw follows path; captures
  verified (pose advances, depth-occludes correctly)
- renderer binary_search sites use the shared sorted-id helper from M0r

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:35:09 +02:00
Admin
d41b6e43ec Arcade M0r: parity-neutral sim internals cleanup
- Entity lookup O(log n): shared entity_index_sorted helper, single
  push_entity spawn path with ascending-id assert + per-tick sorted
  debug_assert — the sorted-Vec invariant is now enforced, not hoped
- WorldSnapshot captures next_id: failed-eval rollback can no longer mint
  colliding ids under surviving entities (review defect fixed)
- game.log buffered (1s/16KiB/eval-boundary flush), agent RPC poll gated
  on one .agent dir-mtime stat — no per-tick file I/O left
- Camera rig authoritative on GameWorld (orbit/chase state); widget keeps
  only device-input accumulation; mailbox drain order unchanged
- Per-tick cumulative script budget: with_instruction_limit reports
  consumption (incl. trap-wipe subtlety), gamemaker tick holds ONE 500k
  pool across on_tick + timers + touch events
- Tape probe verified BYTE-IDENTICAL vs pre-cleanup reference

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 23:34:45 +02:00
Admin
7bb0c3be34 Arcade M0 stage B: rendering extracted to libs/game/render + arcade live viewport
- libs/game/render: the 5 game draw shaders (script_mod registration),
  shape geometry + winding test, terrain mesh, static-slab instancing,
  draw_scene pass (sky/terrain/opaque/alpha) as GameRenderer over GameDraws
  (draw structs stay #[live] on the host widget so script theming works),
  CameraRig + scene_state per-view (multi-view ready), HUD + billboard
  label drawing. game_view.rs 4640 -> 3599 lines
- apps/arcade: first engine-only viewport (arcade_view.rs) — GameWorld
  built via the sim API with no script VM, 60Hz tick, orbit camera,
  offscreen pass composite; ARCADE_CAPTURE=<png> GPU-capture test hook
- Verified: sandbox3d fixture evals clean headless; arcade demo frame
  GPU-captured and visually checked; suites green. Sim untouched — stage-A
  tape byte-parity (probe.txt identical vs pre-refactor binary) stands

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:56:22 +02:00
Admin
f2228a4654 Arcade M0 stage A: gamemaker sim extracted to libs/game/sim + deterministic math + apps/arcade shell
- libs/game/math: cross-arch bit-deterministic f32 kernel (f64-internal
  fdlibm-style polynomial kernels, exactly-rounded IEEE ops only, zero
  platform-libm transcendentals). Parity golden hashes recorded on aarch64;
  equal hashes on every other target IS the determinism test
- libs/game/sim: the gamemaker world moved verbatim (entity/terrain/world/
  queries/step — float expression order preserved so tapes replay
  bit-identically). No Cx, no draw types, no ScriptObjectRef: script
  callbacks are CallbackSlots resolved through a generation-tagged
  host-side CallbackTable in game_view.rs; sim reset no longer touches audio
- examples/gamemaker: game_view.rs 5545 -> 4640 lines (script boundary,
  rendering, input devices, host I/O remain); per-tick args/input objects
  built via unchecked pushes + release_transient (flat-heap tick path)
- apps/arcade: Makepad Arcade app shell + workspace membership
- Verified: headless sandbox3d fixture (90 entities) boots end-to-end;
  gamemaker/sim/math/script-test suites green

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 22:28:06 +02:00
Admin
e61b9af7fc Fix all compile warnings across map crates (unused mut/import, key visibility)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:43 +02:00
Admin
627f0bb04b Map: app reads .mkmap shards — MkmapReader + TileArchiveReader, varint-KV index v2
libs/mbtile_reader gains the .mkmap consumer: root.mkidx parse (Hilbert
tile ids, root ranges -> brotli leaf directories -> shard/offset/len),
positioned shard reads, same get_metadata/get_tile_decoded surface as
MbtilesReader; TileArchiveReader sniffs the path so mbtiles_path can
point at either. Index format v2 drops JSON: the metadata section is now
varint KV like the leaves (writer + reader; no serde in the container).
Loader, zoom-range probe and the headless harness go through the enum;
bridge-dz/overlay sidecars stay mbtiles. Harness parity via shards:
worst AMS rz16 164.7ms, z12 native 51.3ms — identical to mbtiles. App
repointed to local/maps/europe-base-br.mkmap (111 shards + root.mkidx,
the exact bytes a CDN would serve).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
80d5967198 Map: Europe single-origin archive shipped — 51.8GB base + faces bake + .mkmap shards
Overnight run 2026-08-01: 9,380,390 tiles z2-14 in 3.2h, faces bake 1h
(+8GB, worker-side brotli q10), transmux to 99 shards all <510MB.
App repointed to europe-base-br-faces.mbtiles. nl-bridge-dz verified
byte-identical against the new base (deterministic feature indices).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
c7f5a44019 Tessellator: 5.8x fill tessellation + baked-strip fast path
- Sign probe was 57-76% of z9/z13 fill cost (point_in_fill_rule scans all
  contours per probe): exact bbox prefilter (always on, bit-identical) +
  trusted-winding map-only mode (holes take the exterior's sign; bowties
  and orphan slivers keep the probe). Sweep rework: persistent zero-alloc
  tessellator, events sorted once + cursor (BinaryHeap::pop was 40% of
  fill CPU), ~V events instead of 2V, direct index output. 4.4-7x per
  tile, worst per-fill 42.9ms -> 4.5ms, 6798/6798 fixture fills
  bit-identical in both modes. MAKEPAD_TESS_DUMP fixture capture +
  fixture_bench with per-stage split.
- Baked-fill fast path (v2-fills-1, flat mode): field-100 strip decode,
  per-triangle Sutherland-Hodgman clip to the fill overlap rect, edge AA
  via new Tessellator::fill_fringe_into over the deduped rings; 5% area
  guard falls back to runtime tess — and caught a real emitter bug
  (inverted ring winding on z13 water would have erased a water body).
  MAKEPAD_NO_BAKED_FILLS=1 kill switch; baked_fill_audit headless test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:42 +02:00
Admin
f0033ec8a5 Search: fuzzy transcription correction — settlements beat literal typos
Speech-to-text anglicizes Dutch names ("Harlem" for Haarlem). When the
literal token intersection surfaces no settlement, the query re-runs with
near-miss tokens (banded edit distance 1, 2 for long tokens; first letter
pinned, sorted-table range scan, capped) unioned per token — the tiered
scorer then ranks the town of Haarlem above an exactly-named minor POI
naturally (settlement tier + rank dwarf the exact-name bonus). NL
in-memory index only; the europe searchdb keeps literal matching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 17:48:41 +02:00
Kevin Boos
d6c3d5c322 cargo_makepad: don't include resources/android/ios on every platform (#1157)
* TextFlow: fix inline `<code>` spans sagging below the baseline

finish_row_center centered every walk by its own height, so a code run's
shorter walk got a larger downward shift than the surrounding prose under
`RowAlign.Center`. That undid TextFlow's baseline_shift: inline `<code>`
spans sat a few px below the line's baseline, and their descenders poked
out of the bottom of the code box.

* FinishedWalk now carries an optional `align_height` that text runs set
  to their line style's height, so every text run on a row receives the
  same centering shift and stays on the baseline. Also fixes sub- and
  superscripts drifting under `RowAlign.Center`.
* The per-style metrics probe now caches descenders too, since we need
  the full line height (ascender + descender) to compute `align_height`.

* Html/Markdown: make the fixed/code font size scale configurable

Replaces the hardcoded 0.85 `FIXED_FONT_SIZE_SCALE` consts with a
`fixed_font_size_scale` live property on TextFlow (default 0.85), so
apps can tune how much smaller `<code>` text renders than the prose.

* cargo_makepad: don't include `resources/android`/`ios` on every platform

Runtime asset bundling copied each crate's entire `resources/` tree into
every package, so `resources/android/` (manifest template, launcher icon
mipmaps) shipped as dead weight inside APK assets and Apple bundles, and
any `resources/ios/` content would ship on Android too. Those dirs are
platform-specific, so only their own platform should bundle them.

* Add `cp_all_skip_top()` to `makepad-shell`: like `cp_all()`, but skips
  top-level entries by exact name.
* Android (APK and AAB staging): bundle `resources/android/` minus the
  packaging inputs `AndroidManifest.xml.template` and `res/`, which
  already reach the package via the generated manifest and the aapt
  `res/` pipeline; skip `resources/ios/` entirely.
* Apple bundles: skip `resources/android/`, keep `resources/ios/`.
2026-08-01 10:26:31 +02:00
Admin
723b7928bb Route assistant: mmap LLM weights, fix batched-attention corruption, kokoro voice out
- ggml: read-only mmap module (unix-gated) + two-region Context (mapped
  weights / dirty caches) + segmented Metal buffer binding; llama loads
  GGUF weights as file-backed clean pages (jetsam-exempt) with owned-arena
  fallback (MAKEPAD_LLAMA_NO_MMAP=1). Route app dirty footprint 12GB -> 4-7GB;
  model load becomes lazy page-in; A/B byte-identical on 4B + 9B.
- llama: fix graph-cache keying corruption — a graph keyed wider than the
  KV cache corrupted attention for any prefill batch >= 2 (flash op reads
  permute-node dims baked at build; view reconfigure never reached the
  kernel; masks were written cache-narrow). Masks now always fill the full
  graph key width and graphs key by 1024-buckets; reconfigure path removed.
  Verified byte-exact vs per-length reference across batch 1/2/8/64/512,
  short+long prompts, mmap on/off, plus a two-session concurrency probe.
- voice: passive VoiceWaves no longer register the global audio-input
  callback (the invisible caption-bar wave stole mic audio — last
  registrant wins — and spawned duplicate whisper workers); whisper back
  to F16 default (voice Metal library has no quantized kernels; q5_0
  failed every GPU matmul); raw transcripts render immediately.
- route: kokoro TTS voice output (speaker toggle; streams reply sentences,
  announces nav maneuvers + arrival) with barge-in — voice activity on the
  mic stops playback instantly; dispatcher context 8k -> 32k (hybrid KV is
  12/48 layers, ~48KB/token); window caption bar suppressed under studio.
- llama-generate: --max-context/--prefill-batch-size + state fingerprints;
  new llama_concurrent_probe bin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:13:31 +02:00
Admin
676255dfc2 Land route app + platform geo/permission APIs, map nav layer, geodata radar
apps/route: AI trip planner on MapView — tool broker + local/cloud agent
dispatch, nav session + simulated drive, layers/theme state, DDG image
search, trip history, tilt-shift DOF layer (linear circle-of-confusion:
level = log2 of radius, constant growth rate, tilt raises only the
ceiling; tilt-shift on by default).

Platform: Cx geo location API (macOS/iOS/Android/web) + permission
plumbing, memory watchdog, headless build cfg. Map: nav layer M0 API,
landcover drape, bridge dz. Geodata: 250m dual-radar compositor
(radar_volume) + KNMI sync. Docs for the route/glass/blur/shiny work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
b297d4820e Bake building shadow geometry + terrain cast shadows (shiny.md T3)
Shadows are polygons, not passes: each building's roof ring is projected
along the sun's ground direction by height * shadow_len, and footprint +
projection + silhouette quads dissolve into ONE per-tile union (i_overlay
chunked, the road-union lesson) so overlaps never double-darken. Emitted
as material-6 ground fills in the ICON pass — in a city almost all
ground is road surface, and anything earlier gets painted over by the
street passes — with micro-depth 0.40, above the whole grounded road
ladder but below lifted deck bumps. The decal bakes full-dark and the
shader scales it by the live shadow_alpha uniform, so time-of-day fades
don't need a rebake. LOD gates: 3D mode, z>=14, and skip shadows under
2 px at bake magnification. Trees get soft contact-shadow discs.

Terrain: horizon-march cast shadows in the hillshade worker (17-step
exponential stride, ~15 ms per 512px region), skipped automatically
when regional relief is under 150 m. Hillshade + march light from the
one SceneSun via a new per-request sun/flag channel to the worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
3d25a9afa0 Add SceneSun + material channels + baked AO (shiny.md phase 1)
One global light (draw/src/scene_sun.rs): SceneSun POD shared by the
tile bake, ball lighting and terrain hillshade, replacing three
hardcoded sun rigs. ShinyConfig carries every shiny.md toggle; it rides
CompiledMapTheme so bake-flag flips reuse the style-epoch restyle and
stale tiles stay drawable (MapView::update_shiny).

T1: shape-0 geometry now carries its surface normal in param1/2 and a
material id in param3 (walls/roofs/water/canopy/green) — channels were
free, no vertex-format growth. DrawMapVector dispatches per-material
pixel effects behind uniform gates that default off (legacy frame when
off): T4 water noise sheen + sun glint, T4b building specular sheen
with heading-rotation highlight sweep, T5 canopy clump noise + rim and
green-area patchiness.

T2 (first slice): wall vertical AO gradient (ground-contact darkening)
and roof-edge parapet AO strips, baked at zero GPU cost behind bake_ao.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
6390434897 Vendor i_overlay polygon boolean crates into libs/
Inhouse i_overlay 7.0.3 and its i_float/i_shape/i_key_sort/i_tree deps
(used by the map road union mesh under the widgets 'maps' feature) as
workspace-excluded path crates, stripped of tests/locks/registry crud.
i_float's no_std libm dependency is removed by switching it to std
float math, so the maps feature no longer pulls any external crates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 00:10:52 +02:00
Admin
fff629bb3f map: bridge dz solver+AHN bake, base_dz transcode, road overlay unifier
Bridge elevation (bridge.md M1+M2): tools/map_tiles bridge-bake solves
per-vertex road/rail dz over the OSM graph (crossing clearance, grade
ramps, deck holds, junction consensus) with AHN DSM-DTM measured deck
profiles (BigTIFF reader in geodata, WGS84->RD), baked as bridge_dz +
per-base-tile base_dz (L/F/P join to exact renderer geometry). Renderer
joins dz through parse into TileWay.dz; strokes/arrows/fills lift off
their own profiles; tunnels never deck.

Road overlay unifier: painter's algorithm as geometry — per-way segment
rects + vertex discs, i_overlay top-down subtraction cascade into
DISJOINT faces (overlay_paint_groups), triangulated once; flat render is
pixel-equal to paint order (proven 0-diff on the abstract unit case) and
tilt cannot reorder it. Legacy strokes interleave by rank; plazas join
the cascade at true alpha. @roads 0/1 A/B toggle, @abtest headless
CPU-raster diff harness (1.50% vs 2D reference on the Raampoort tile),
@cam debug camera, bridge_eval.sh aerial-vs-render loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 10:42:24 +02:00
Admin
85515cea35 map: 3D terrain displacement, Europe-wide DEM, landcover drape, wind layer
Terrain gets real z in tilt mode: a displaced 288x216 surface mesh plus
per-vertex ground lift for all tile geometry (roads/fills/buildings/icons)
via vertex-stage sample_lod of a terrarium-packed elevation texture, with
labels riding the same ground through a lift-aware camera delta. Depth
domains rescale per frame to stay inside the -24 budget; road passes get
a relief-scaled clearance over the surface so cities keep correct
street-vs-building order. Regional zooms (<z14) drape landcover colors
into the hillshade (full-res MVT rasterization, treeline fade) instead of
lifting km-scale polygons that cannot follow relief.

Terrain build now covers EV-trip Europe (lat 43..58, lon -5..17, 8.8 GB
GLO-30 z6-12) with sign-safe Copernicus stems, transparent no-data, and
an Alpine rock/snow ramp. Terrain requests render 4096x3072 over ~3
viewports and only re-render on real movement.

Also: NOMADS GFS wind particle layer synced to the weather clock with 3D
altitude, rain cloud deck lift, stuck-tile fixes (expiring missing set,
decode-failure backoff, restyle placeholder purge), 78-degree tilt
ceiling, and park fill z-fight fix via widened micro-depth ranks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 10:42:24 +02:00
Admin
c0aeb25b0a map: rain radar overlay, flying 3D markers, Europe routing, geodata crate
- rain radar layer: pure-Rust KNMI HDF5 reader (superblock v0 walk,
  single-chunk deflate datasets, byte-exact vs h5py), ellipsoidal polar
  stereographic reprojection (20 m vs product corners), bilinear value
  sampling -> smooth banded isolines, textured quad overlay through the
  overlay camera, 25-frame nowcast animation; RadarSync polls at most
  once per 4 min through a disk-persisted gate and caches frames on disk
- makepad-geodata + makepad-tesla crates join the workspace (overlay
  builders, radar sync, NL open-data layers; transit routes now z7-14)
- Europe major-roads routing graph: nav-build --major-roads does a
  ways-first scan (5.7M ways / 46M nodes / 194 s / 971 MB) and the app
  falls back to it when a route leaves the regional graph — Amsterdam to
  Paris routes offline (501.8 km)
- 3D flying markers: chargers/POIs/stops ride thin stalks with DYNAMIC
  height (each pin clears its own building +8 m); labels, kW text,
  brand and tap zones all consume the baked per-marker lift; stalks and
  buildings grow together on the 2D->3D transition (per-tile flat->3D
  fade heights, no replay on zoom regens)
- markers depth-honest (small bias, buildings occlude them); phong-lit
  canopy/light spheres matching the buildings' NW sun; buildings tint by
  BAG age in 3D; district area tints (rank 60, alpha .32); transit line
  labels + stop names; follow-mode is an explicit attach/detach toggle;
  rotation release schedules the label re-place (no stuck upside-down
  labels after a fast spin)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 18:00:27 +02:00
Admin
1b0decc6c9 map: charger pin redesign, upright/billboard labels, 3D trees, exits
- Tesla-style droplet pins anchored at the tail tip (rotate around the
  exact site point in tilted views); red exclusively Tesla, amber DC,
  blue AC; Tesla pins show stall count, others peak kW
- in-pin text through the normal text renderer: billboard-anchored
  glyphs (anchor scales with the map, glyph size constant), exempt from
  label collision/repeat culling, no halo
- upright camera-delta labels: straightened labels (place names, POIs,
  brands, pin text) translate with the rotation gesture but stay
  horizontal — no more rotate-then-snap on regen
- per-icon zoom floors baked in vertex param4 + live icon_zoom uniform:
  stale deeper-bucket tiles never flash markers on zoom-out (param4 is
  lift-height only for non-icon shapes — icons stay on the ground)
- motorway exit labels (street_labels_points): carto-red name + ref
- 2D/3D mode: tilt gesture syncs app state (TiltChanged action), tilt
  release near-flat settles to exact 0, mode flip re-bakes tiles
  (extrusions appear/disappear without a zoom nudge)
- Simple 3D Buildings: building:part volumes with min_height bases;
  outlines containing parts flatten to footprints; famous buildings
  with tourism=attraction extrude instead of dying as attraction fills
- little 3D trees in tilt mode: crossed trunk quads + sphere-slice
  ball canopy (architecture-model proportions)
- searchdb/mbtiles support work: reach-based distance ranking, direct
  tile lookup + writer ordering

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 16:02:44 +02:00
Admin
dabbd0bbc6 llama: image-span prefill + M-RoPE — full VLM pipeline matches llama.cpp exactly
LlamaSession gains append_image_embeddings: precomputed vision embeddings
prefill through a second Embeddings-input graph spec sharing the same
cache tensors (SessionGraphParams grows an embeddings_input discriminant).
HybridDecodeBatchLayout gains an optional pre-expanded rope_positions
override, threaded to encode_rope_positions — cache indices and attention
masks stay linear while image tokens get qwen-vl 2D positions
[pos0, pos0+y, pos0+x, 0] and the span advances rope position by
max(w, h). Text after an image continues from the shifted position; pure
text paths are byte-identical to before (verified via llama-generate).

vlm-probe runs the whole thing: ppm -> vision tower -> chatml with
vision_start/end -> greedy. Output is token-for-token identical to
llama-mtmd-cli --temp 0 on both test images (radar scene description and
'A red circle.'). 229-token mixed prefill 0.98s, generation 27.9 tok/s
on the 9B UD-Q4_K_XL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 14:31:44 +02:00
Admin
56153ce02a llama: qwen3.5 vision tower on makepad-ggml — oracle parity achieved
New vision module: mmproj GGUF load (reuses the arch-agnostic loader),
exact-port preprocessing (calc_size_preserved_ratio, f64 bilinear resize to
match the reference binary's double promotion, block-major patch unfold),
and the 27-block ViT graph: patchify as two matmuls vs the flattened conv
weights, interpolated learned pos-embd (gpu bilinear+antialias, gathered to
block order), 2D vision rope, full bidirectional flash attention (f16 k/v,
f32 prec), layernorm composed as norm-mul-add, and the qwen3vl_merger
2-layer MLP into the LLM's 4096-dim space.

vlm-vision-probe validates against clip.cpp dumps: preproc bit-exact on
aligned images (1e-7), embeddings rms 3e-4..9e-4 / cosine 0.99999+ on all
three test images — inside the oracle's own flash-vs-composed spread
(rms 8.9e-4). 64-token encode 38ms, 192-token 103ms after graph compile.

Also fixes a stale DeltaNetRecurrentBlockSpec test initializer that broke
cargo test compilation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 14:23:26 +02:00
Admin
87959594a5 converse: conversational pipeline crate (speech + filter + llm), gamemaker on it
New makepad-converse crate: SpeechOutput/Playback moved out of gamemaker
(mix_into for custom audio callbacks, install_audio_output convenience);
TranscriptFilter trait + PassthroughFilter + FilterWorker thread (filters
built on the worker via factory since LlamaSession is not Send);
ConversePipeline wiring filter -> makepad_ai agent backend -> speech with a
ConverseAction stream, llm leg pluggable (claude-code / acp /
openai-compatible base_url for local servers). QwenFilter behind the
local-llm feature: chatml prompt, non-thinking prefill, greedy, SEND:/SKIP:
line protocol, fail-open parse; filter-repl bin for interactive testing.
Gamemaker's speech leg now uses the crate; its agent plumbing stays local.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:47:13 +02:00
Admin
478b711ce5 voice: pure-rust silero v5 vad + learned speech gate in window voice input
Ports the silero v5 16k branch to pure rust (minimal onnx-protobuf weight
extraction, hardcoded graph: stft-conv, 4x conv+relu, lstm cell, sigmoid
head), 512-sample chunks with 64-sample carried context. Validated against
onnxruntime to 2.3e-6 max diff (fixtures committed), ~425us/chunk release.
Model loads from repo-root silero_vad.onnx or MAKEPAD_VAD_MODEL. vad-test
bin for wav files. window_voice_input now gates packets on vad probability
(0.5 enter / 0.35 exit) and falls back to the rms gate when the model file
is missing — the log line says which gate is active.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:47:05 +02:00
Admin
4c8247cd01 ggml/llama: fix non-flat unary metal dispatch truncating rows past 1024 floats
Grid width for the non-flat unary path was ne01 while the kernel decomposes
tgpig.x into (ne0-chunk, row) — rows past ntg were silently dropped, masked
below the 32768-element flat-path threshold; silu on [8192, n] crossed it at
prefill batch 4 and corrupted qwen35 batched prefill. Grid is now
ne01 * ceil(ne0/nth). Adds metal unary/gated-delta-net/ssm_conv regression
tests, the llama-batch-probe bisect harness, prefill batch default 32, and
keeps the graph cache until a reserve-retry actually needs eviction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:46:57 +02:00
Admin
e66b64ddeb map: Europe searchdb, route elevation profile, rail/tram/water-label styling
- libs/map_nav searchdb: disk-backed all-of-Europe search index (120M docs,
  8GB, ~30ms cold queries via pread; external-sort builder, cell-sharded
  postings, rank heads, digit-token address filtering with street fallback);
  nav-build --searchdb two-pass pbf streaming; nav-probe dbsearch; app
  worker prefers local/maps/europe.searchdb over the places index
- examples/map elevation: dem.rs fetches AWS skadi SRTM tiles on demand
  (cached local/maps/dem/, offline after), median+220m smoothing and 2.5m
  climb hysteresis so radar rooftop noise doesn't fake ascent in flat
  cities; elev_graph.rs DrawVector profile panel above the route bar
- rail styling: heavy rail draws carto sleeper look (theme casing + white
  stripe dash) z14+; trams/metro stay a solid near-black line (shortbread
  tags trams rail=true — kind decides now); platform/station/dead railway
  values no longer draw as track linework
- water name labels: water_polygons_labels/water_lines_labels admitted
  through extraction, source ranks, and LABEL_CLASS_WATER colors; label
  layers excluded from stroke styling (double-drew as rivers)
- label fix: near-vertical reading-direction tie-break was inverted;
  vertical street names read bottom-to-top under any camera rotation
- cleanness: oneway arrows lighter+sparser, tree discs smaller

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 23:13:47 +02:00
Admin
9f067b5623 map: continent-wide place search with Google-style tiered ranking
score_search_hit: settlements are near-immune to distance ("brussels"
from Amsterdam means Bruxelles, not the closest Brusselsestraat),
POIs/streets keep strong local bias, and a number token signals
address intent. nav-build --places-only scans a pbf for settlement
nodes into a compact index (Europe: 1.31M places, 67MB, 60s); the app
merges it with the regional full index, deduping same-name near hits.

Route overlay decimation now measures against the last drawn point,
bounding the error at ~1.5px — pairwise skipping compounded and
visibly reshaped the route when zoomed out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 22:16:44 +02:00
Admin
9e7b3b8576 map: GPU re-expandable strokes — zoom-invariant widths on stale tiles
Stroke geometry now bakes the centerline anchor per vertex (svg
tessellator emits anchors; offsets ride in param1/2, width-growth
class in param3, shape_id+100 marks expand mode — the shared vertex
format is unchanged). The map vertex shader re-expands each stroke
with a per-class width correction (regular roads, thin paths/rails,
waterways, constant-px building outlines) computed from the tile's
styled bucket vs the live fractional view zoom.

Tiles rendered at a stale zoom bucket keep the exact widths a fresh
restyle would produce, through the whole gesture: no fat roads while
zooming in, no width snap when the rebuild lands, dash dots and
building outlines stay crisp. Fading outgoing generations carry their
own bucket so they correct too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 20:39:23 +02:00
Admin
f099c05750 map: Europe tile conversion pipeline + offline GPS navigator
Conversion tooling (tools/map_tiles): curated Shortbread base pyramid
from a VersaTiles planet archive (bbox extract, brotli->gzip transcode
parallelized per 256x256 block), all-tag native OSM pbf detail
converter, pbf audit, and nav-build/nav-probe producing the routing
graph + search index artifacts. download_map.sh orchestrates pinned
downloads and conversions. mbtile_reader writer now skips SQLite's
lock-byte page at byte offset 1 GiB — allocating through it corrupted
every database over 1 GiB ("2nd reference to page 16385"); the 31 GB
Europe conversion passes integrity checks.

Navigation (libs/map_nav, new): region.search place/POI/street/address
index (prefix autocomplete, NL/EN category synonyms, proximity
ranking) and region.graph routing graph (CSR directed edges,
car/bike/foot speeds, oneway, turn restrictions, snap grid, A*),
maneuver generation and the NavSession map-matching state machine;
26 unit tests.

MapView interaction layer: MapViewAction, camera API + animated
fly_to, overlay.rs (route polyline with traveled dimming, markers,
position puck), per-widget mbtiles_path override, archive
minzoom/maxzoom honored for tile requests, zoom-level cross-fade over
the previous level's imagery, floating panels win hit-testing.
examples/map is the navigator app: worker-thread search, Drive/Bike/
Walk routing, simulated turn-by-turn with banner, follow camera.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 20:13:21 +02:00
Admin
8d16165c01 mbtile_reader: direct tile lookup and mbtiles writer
Direct (zoom, column, row) tile queries so the map no longer scans an
entire zoom level per batch, plus a writer for producing mbtiles
archives.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 17:40:08 +02:00
Admin
d266571ab6 map: tile-seam overpaint fixes, label halos/colors/stability, z19
- per-end line caps (Tessellator::stroke_ends): butt at tile-clip cuts,
  round at true ends — road cap discs no longer stamp over neighbor
  tiles' tram tracks and roads at seams
- fill polygons clipped to their own tile square (Sutherland-Hodgman);
  a tile's MVT buffer fragments no longer overpaint the neighbor;
  building outlines skip segments running along the tile cut
- stroke clip padding 3px (under the generator buffer) so boundary
  cuts are detectable
- white label halos (8-offset underdraw, theme label_halo color)
- carto POI label colors (orange food, purple shops, brown culture,
  muted house numbers) from shortbread poi attributes
- label placement hysteresis across frames (no flicker while panning)
- street_polygons fill ranked below sites/parks (Bellamyplein-class
  plazas no longer cover the park inside them)
- max zoom 19 with width stops for z18/19

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 15:53:36 +02:00
Kevin Boos
d1efac1892 New unified kinetic scrolling. Vastly improve draw shaprness on low-res screens. Fix rendering and text perf issues on CPU+GPU (#1127)
* Fix rendering, gradient, sampling, etc issues on older GPUs

* Fix bug in `box_y` sdf function, which caused gradients to be split
  into two bands incorrectly. Mostly a problem on lower-res screens.
* Use per-texture filtering instead of GL sampler objects on Linux,
  especially for Mesa drivers that ignore min filter samplers.

This should help prevent blocky/pixellated things like emoji/avatars

* Improve rendering sharpness on low-DPI screens (icons, emoji, images)

On 1.0-DPI screens, emoji/SVG-icons/avatars were minified without
adequate sampling and SVG AA was sub-pixel, producing blocky/aliased
output. This reworks each path and adds optional full-window SSAA.

SVG icons (device-aware AA + round caps):
- draw_svg.rs/draw_vector.rs/render.rs: size the fill & stroke AA
  fringe and the curve-flatten tolerance in DEVICE pixels (≈constant
  regardless of icon size), so edges resolve via the analytic
  d/fwidth coverage and curves stay smooth at any scale; re-tessellate
  on scale change.
- triangulate.rs: thread the flatten tolerance through path fill/stroke.
- tessellate.rs: emit round caps as a solid disc (u=0.5) instead of a
  radial fade that collapsed to a square at small sizes.
- widgets/icon.rs: don't clip the Icon to its Fit bounds, so round
  caps that extend past the box render fully instead of being sheared.

Emoji:
- glyph_raster_image.rs/rasterizer.rs: rasterize color emoji near the
  on-screen size with an alpha-weighted box downscale (geometric-mean
  scale factor) instead of the font's native PNG strike.

Images / avatars (mipmaps):
- image_cache.rs/texture.rs/draw_list.rs/lib.rs: optionally emit a CPU
  mipmap chain (VecMipBGRAu8_32) for non-animated images so minified
  avatars/thumbnails sample cleanly. Env-gated MAKEPAD_IMAGE_MIPMAPS;
  default on for GL on Linux.
- metal.rs: real per-level mip upload. d3d11.rs/vulkan.rs/web_gl.rs:
  safe single-level fallback (no crash; real mips TODO).

Full-window supersampling (optional):
- window.rs: render the whole UI into an offscreen target at
  MAKEPAD_SUPERSAMPLE× device resolution and downscale-resolve into the
  window. Default 2×, env-tunable, 1× disables. Modeled on the existing
  GaussStack render-to-texture path.

* don't unconditionally enable supersampling SSAA of 2x by default

it's too expensive and too slow for most older devices

* cleanup, reduce comment verbosity

* improve SVG anti aliasing

* Windows: fix laggy/juddery scroll performance

- Pace the render loop to the display refresh using a DXGI frame-latency
  waitable object and present with vsync, replacing the free-spinning,
  uncapped Poll loop that caused uneven scroll cadence.
- Coalesce consecutive WM_MOUSEMOVE messages and paint once per loop pass
  to stop the judder when moving the mouse during fling deceleration.
- Cache get_dpi_factor() and the WM_NCHITTEST WindowDragQuery result to
  avoid per-mouse-move GetDeviceCaps syscalls and widget-tree hit-tests.
- Throttle XInput/DirectInput polling of empty/disconnected controller
  slots, which was stalling the UI thread.
- Rework the momentum fling to a native exponential model with a
  frame-interval EMA, and stop the tail auto-scroll from fighting an
  active fling/drag.
- D3D11: update the glyph atlas and image textures in place via
  UpdateSubresource instead of recreating them on every change, and
  spread D3D11 shader-object creation across frames.
- Slug atlas: only force a full re-layout on a width change; append rows
  on height growth.

* Windows: correctness fixes from review (off the scroll hot path)

- d3d11: close the DXGI frame-latency waitable HANDLE in Drop (it was
  leaked once per main-window lifecycle and the field comment was wrong);
  keep popup swap chains at frame-latency 1; track the waitable-swapchain
  flag for ResizeBuffers instead of inferring it from the handle; present
  without the vsync interval during a live resize.
- windows.rs: poll game input on the idle signal tick so a gamepad button
  can be serviced while the app is otherwise idle.
- win32_window / window: invalidate the WM_NCHITTEST / WindowDragQuery
  caches on window move and on a caption relayout, with a generation
  counter guarding against a reentrant invalidation being clobbered.
- windows_game_input: detect a controller already plugged in at launch via
  a one-shot full scan on the first poll, probe slot 0 (Player 1) first,
  and offset the DirectInput enumeration so it never stacks with the
  XInput probe.
- comment/doc corrections.

* Image cache: accept any Arc<D: AsRef<[u8]> + ?Sized> for async image data

The load_image_from_data_async family required Arc<Vec<u8>>, forcing callers
that already hold the bytes as Arc<[u8]> (e.g. a content-addressed media cache)
to copy the whole buffer via .to_vec() just to satisfy the type. Generalize the
data parameter to Arc<D> where D: AsRef<[u8]> + ?Sized, so those callers can pass
their existing Arc by refcount-clone with no byte copy. The decode path only ever
borrowed the bytes (&[u8]), so this is purely a signature relaxation; existing
Arc<Vec<u8>> callers are unaffected (D = Vec<u8>).

* Linux: GL glyph-atlas in-place texture update + X11/Wayland mouse-move coalescing

* Scroll: unified fling model + native trackpad momentum deceleration

Share one kinetic-scroll model between PortalList and ScrollBar (and thus
ScrollXView/ScrollYView/ScrollXYView) via a new widgets/src/scroll_motion.rs:

- Touch-drag flicks use an iOS-style exponential self-decay, frame-rate
  independent via a per-frame integrator with dt smoothing.
- Trackpad scrolling applies the OS momentum directly while fast (responsive,
  full native speed), then hands off to a gentler self-decaying tail once it
  slows past a threshold, so the deceleration is longer and smoother than the
  OS's short, choppy tail. Handoff is seeded at the current speed for a
  continuous transition; the seed is clamped against degenerate event timing.
- Add ScrollPhase to scroll events, mapped from NSEventPhase/momentumPhase on
  macOS and wl_pointer AxisStop on Wayland; None elsewhere (wheels/X11/Windows
  behave as before). MAKEPAD_RAW_TRACKPAD_MOMENTUM=1 bypasses the smoothed tail.
- A press catches an in-progress fling (stops the scroll, consumes the press so
  it doesn't also activate a child), matching iOS/Android/macOS.

* Shader codegen: prefix Metal/WGSL locals to avoid reserved-word collisions

The Metal/WGSL backends emitted user-declared shader locals verbatim, so a
local named after a reserved type keyword (e.g. `half`) produced invalid
shader source and failed to compile at runtime. Prefix them with `l_` like the
HLSL/GLSL backends already do.

* TextFlow: don't panic on unbalanced HTML close tags

end_code/end_quote unwrapped the area stack, so a stray `</pre>` or
`</blockquote>` in untrusted content (e.g. a chat message) panicked. Return
early instead, and drop a vestigial per-list-item area-stack push that leaked
an entry and could hand a stray close tag the wrong block's area.

* Scroll: expose fling decel, handoff threshold, & tail-decel as `#[live]` fields

This allows app devs to override the scroll feel per-widget in the DSL,
or globally by overriding the base widget's defaults — verified that a DSL
override takes effect.

* Windows: fix frame pacing, paste crash, and wheel input backlog

- Wait on the frame-latency waitable right before each window's vsync
  present instead of on every Paint, so input no longer stalls behind
  waits that have no matching present. Drain leftover credits after a
  live resize.
- Pasting when the clipboard has no text no longer panics.
- The poll loop now handles up to 32 messages (2 ms) per frame and
  merges consecutive mouse-wheel messages, so fast wheels can't build a
  backlog that keeps scrolling after the gesture ends. Sleep 1 ms when a
  frame presents nothing so animation polling doesn't spin a core.

* Image: don't build unused mip chains; fix stale images in recycled widgets

- Only build the CPU mip chain when a backend actually uploads it
  (Metal, behind its env var), and build it on the decode thread instead
  of the UI thread. Linux GL still gets its mipmaps via glGenerateMipmap
  and now retains less CPU memory per image.
- Recycled Image widgets no longer show the previous item's image or
  apply an old decode result. Placeholder textures set via set_texture
  (like blurhashes) stay visible while the real image decodes, and a
  failed load clears the widget instead of leaving old content up.

* Widgets: avoid needless caption redraws; cheaper PortalList height tracking

- Label::set_text does nothing when the text hasn't changed, and the
  window caption title is only synced when it actually changes, so mouse
  moves and animation ticks no longer redraw the whole window every
  event. The caption centering padding requests its own redraw now.
- PortalList records item heights only when new or changed, and only
  re-applies the default height after it drifts by half a pixel, so big
  lists don't walk every unmeasured item on every scroll frame.

* Text: cache layouts of long texts; stop cloning glyph outlines every frame

- The layout cache now accepts texts of any length (long messages and
  code blocks used to re-layout on every scroll frame). It is a real LRU
  with a byte budget on top of the entry cap, and texts drawn in the
  current frame are never evicted, so one heavy frame can't thrash the
  cache into a permanent miss cycle. The shaper cache is LRU now too.
- Glyph outlines are shared via Rc, so drawing a cached glyph no longer
  copies its command list, and outline complexity is computed once when
  the outline is built instead of every frame.

* Html: fix stale links/spans in recycled widgets and <details> renumbering

- set_text only rebuilds when the content actually changed, so a
  recycled link can't open the previous message's URL, and re-setting
  identical content keeps the user's <details> open/closed state.
- Custom widgets and <details> are keyed by their node index instead of
  a visit-order counter, so toggling a collapsed <details> can't
  renumber the widgets after it and rebind them to the wrong nodes.
  item_with_scope also recreates its widget when the template changes.

* Linux: fixed-distance wheel scrolling; Wayland frame-callback pacing

- Wheel scrolling moves a fixed 60 px per detent on X11 and Wayland
  instead of a timing-based guess that flipped between 12 px and 240 px
  depending on how events batched. Wayland reads real detent counts via
  AxisValue120 (wl_seat v9, with AxisDiscrete as the older fallback) and
  maps keymaps MAP_PRIVATE as v7+ requires. Touchpads are unchanged.
- Wayland frames are paced with wl_surface frame callbacks and swap
  interval 0, so redrawing a hidden or minimized window can no longer
  hang the whole app inside eglSwapBuffers (compositors withhold frame
  callbacks for hidden windows). Windows with a callback in flight skip
  presenting and stay dirty; X11 keeps vsync exactly as before.

* Text: bigger layout cache budget, reclaimed at the end of each frame

A maximal ~60 KB message lays out to roughly 4 MB of glyphs, so the 4 MB
budget couldn't hold even one alongside a normal screen. Raise it to
16 MB, and run eviction at the end of every frame so memory over the
budget is freed one frame after its content leaves the screen, instead
of lingering until some later layout happens to insert a new entry.

* Fix oversized uniform slices: the array lengths were in bytes, not f32 elements

* Wayland: flush buffered mouse motion before scroll events, and drop motion for closed windows

* GL: fall back to non-mipmapped filtering when glGenerateMipmap fails on strict GLES3 drivers

* Text: bucket emoji raster scales so zooming reuses atlas slots instead of re-decoding every step

* Image: add has_content() and record texture provenance on cache-hit loads too

* Scroll: native trackpad momentum, Chrome-model bounce; presses catch motion, never click children

* Dock: redraw the newly selected tab immediately when the active tab is closed

* Html: standard link colors with pressed precedence; skip re-parsing unchanged text; color setters

* Image: don't redraw on cache-hit loads of the already-bound image (per-draw reloaders looped forever)

* Scroll: log macOS momentum-end phase bits to check Cancelled (touch-cut) vs Ended (natural fade)

* Scroll: momentum state machine; flicks survive pagination; edge sentinels & once-per-frame actions

* Scroll: remove the MAKEPAD_SCROLL_DEBUG diagnostics

* Scroll: time-based fling velocity window; pointer fan-out guard; parked flings survive pagination

* PortalList: optional reached-start/end margins (Some(0) default); repositioning re-announces the edges

* Linux/Wayland: honor UI zoom across window resizes; scale caption bar with zoom

Keep the wayland-side window geom in native units so the zoomed dpi isn't
read back as "native" on the next Configure — UI zoom no longer resets or
flickers on maximize/tile. Also let the caption bar height scale with the
zoom (pin to native only on macOS, where the buttons are OS traffic lights).

* Windows: D3D11 fixes for drawlist mgmt

trying to help with the `new_batch` bugs

* draw_list.rs — `set_zbias` now returns whether it changed
* d3d11.rs — uploads draw_call_uniforms on the given condition:
  `uniforms_dirty || zbias_changed || buffer.is_none()`
  and the zbias advance is hoisted above the early-continues

* Scroll: hard flicks carry farther and boost on re-flick; iOS-style touch rubber band with per-edge bounce gating; Android fling spline behind a flag

* Splitter: redraw both pane subtrees on drag; cached views keep fresh fixed sizes in the dirty check

* iOS: re-deliver window-geometry changes dropped by re-entrant UIKit callbacks; Init only ever from the first draw

* final cleanup fixes. Ensure all examples, experiments, `studio` all work

* Image: skip re-parsing an SVG that is already shown, keyed on the caller's shared bytes

* CachedView: fix upside-down offscreen texture on GL/GL-ES

GL/GL-ES store offscreen FBOs bottom-up, unlike Metal/D3D/WGSL.
The DSL->script-shader migration switched the CachedView composite to plain
.sample() (non-flipping sample2d on GL), so cached views rendered
vertically mirrored on GL/GL-ES; Metal was fine on macOS.

Add a `sample_rt` script sampler (emits the V-flipping sample2d_rt on
GLSL, plain no-flip sample elsewhere) and use it in the CachedView and
CachedRoundedView composites.

* Fix `AdaptiveView::redraw()` to actually do something
2026-07-21 09:59:12 +02:00
Admin
d3d110df7b perf 2026-07-10 15:34:38 +02:00
Admin
59b7d2712f gamemaker: full engine round — chase camera rig, racing-game APIs, script stdlib math, real error line numbers
Engine (examples/gamemaker): chase camera rig (camera({chase}) — ease-behind
with mouse-wins/recenter authority), writable camera + look deltas, spatial
queries (raycast/overlap_sphere/ground_normal), save/load, sustained tones,
rot_y + collide:false spawnables, HUD slots/bars, terrain noise shaping +
height bands, per-shape instanced render batching with static slabs (3.2x),
error push-loop into the agent chat, unknown-verb/option diagnostics with
game.splash:line:col positions, streaming tail-statement finalization, quiet
toolbar UI, Shh voice hush, fable voice.

Platform: runtime vector methods (.length/.normalized/.dot/.cross), scalar+
vector lerp, TAU; ScriptVm error capture sink; window frame capture API; four
headless-JIT fixes (scalar casts, mat4 mul, Id-arg expansion, commuted
scalar-vec ops) with regression test stages. Widgets: single-line TextInput
baseline centering, TextFlow inline-code baseline alignment, glass button
corner_radius uniform. Voice/ggml Metal backends: debug logs behind
GGML_METAL_TRACE. splashgame.md: the runtime-loaded game API contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:20:03 +02:00
Admin
f2efe4af99 gamemaker prerequisites: makepad-tts library, TTS/whisper model downloader, aigame design docs
libs/tts was never tracked despite being a build dependency of the gamemaker
example. tools/download_tts.sh fetches the public upstream weights (HuggingFace
Kokoro-82M + whisper.cpp) and converts them locally with the in-repo stdlib-only
converter; model artifacts are gitignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 13:29:22 +02:00