Plan the focus: Improving performance on the steamdeck. keep it concrete and simple. use or exte #294

Closed
opened 2026-09-09 23:52:26 +00:00 by viberfox-agent · 3 comments
Collaborator

The maintainer has set a focus for this lane, and it needs turning into a queue of work.

Improving performance on the steamdeck. keep it concrete and simple. use or extend harness tooling to find useful improvements.

This is a planning pass, not a build. Change no code and open no branch. What comes
back is a list, which the maintainer reorders and edits in the console before any of it
is filed — so propose the work, do not start it.

  1. Read before proposing. docs/direction.md for the goal and the seven values,
    CLAUDE.md for how this tree is built and what it already does, and whatever part of
    the code the focus names. Half of a good plan is noticing the thing is already there.
  2. Propose six to ten pieces of work. Each one a ticket a session could finish in a
    sitting, ordered so the earlier ones unblock the later ones. A title that says what
    changes, and a note saying where in the code it lands and how a machine would judge
    it — that last part is what this lane can land unattended.
  3. Leave out what it may not do. The wire protocol, database migrations, the CI
    workflows, tools/autopilot.ts itself and assetlinks.json are fenced: a ticket
    touching one is built and then parked for a person. Anything whose success is a
    matter of taste belongs to a person too.
  4. Post the plan in one comment on this ticket, as exactly this block:
```autopilot-plan
[{"title": "…", "note": "…"}, {"title": "…", "note": "…"}]
```

JSON, an array, title and note both strings. The autopilot reads the last such
block on this ticket and stores it as the queue; everything else in the comment is
ignored, so write whatever reasoning you want around it.
5. Then close this ticket. There is no branch to merge.

Decide it yourself. Nobody is watching this ticket, so a question asked here is a
ticket that stops. The values at the top of docs/direction.md settle the ambiguity, and
a piece of work you are unsure about is one to leave off the list rather than ask about.

Filed by the autopilot.

The maintainer has set a focus for this lane, and it needs turning into a queue of work. > Improving performance on the steamdeck. keep it concrete and simple. use or extend harness tooling to find useful improvements. **This is a planning pass, not a build.** Change no code and open no branch. What comes back is a list, which the maintainer reorders and edits in the console before any of it is filed — so propose the work, do not start it. 1. **Read before proposing.** `docs/direction.md` for the goal and the seven values, `CLAUDE.md` for how this tree is built and what it already does, and whatever part of the code the focus names. Half of a good plan is noticing the thing is already there. 2. **Propose six to ten pieces of work.** Each one a ticket a session could finish in a sitting, ordered so the earlier ones unblock the later ones. A title that says what changes, and a note saying where in the code it lands and how a machine would judge it — that last part is what this lane can land unattended. 3. **Leave out what it may not do.** The wire protocol, database migrations, the CI workflows, `tools/autopilot.ts` itself and `assetlinks.json` are fenced: a ticket touching one is built and then parked for a person. Anything whose success is a matter of taste belongs to a person too. 4. **Post the plan in one comment on this ticket**, as exactly this block: ```` ```autopilot-plan [{"title": "…", "note": "…"}, {"title": "…", "note": "…"}] ``` ```` JSON, an array, `title` and `note` both strings. The autopilot reads the last such block on this ticket and stores it as the queue; everything else in the comment is ignored, so write whatever reasoning you want around it. 5. **Then close this ticket.** There is no branch to merge. **Decide it yourself.** Nobody is watching this ticket, so a question asked here is a ticket that stops. The values at the top of `docs/direction.md` settle the ambiguity, and a piece of work you are unsure about is one to leave off the list rather than ask about. <sub>Filed by the autopilot. <!-- autopilot:focus-plan-c8fd6034-7 --></sub>
Author
Collaborator

🤖 Picked this up — working on branch perf/294-plan-focus-improving-performance in a fresh worktree on the VPS. I'll report back here when I'm done.

Label an issue agent:skip to keep me off it, or driven to keep it in a session you are driving.

🤖 Picked this up — working on branch `perf/294-plan-focus-improving-performance` in a fresh worktree on the VPS. I'll report back here when I'm done. <sub>Label an issue `agent:skip` to keep me off it, or `driven` to keep it in a session you are driving.</sub>
Author
Collaborator

The focus is the Steam Deck, and the shape of the answer is already set by what
landed on 2026-09-09: the Deck was the machine that settled the globe cloud shell —
71% of the GPU frame at 1280x720, the difference between 52 and 85 fps — after this
container had spent weeks unable to say anything about it. That result is the model.
It came from counts and from a real GPU, and it was only trustworthy because the
windows reported quiet, the fragment counts repeated to the unit, and the machine
was idle.

So the queue below is in three parts, and the order is the dependency order.

The instrument first (1-4). cargo perf today writes one sample per window to
/tmp/perf.json and has no way to repeat a run, no way to diff two runs, and one
global resolution. Every withdrawn finding in docs/notes/performance-suite.md is a
point sample differenced against a point sample; the survey records a within-group
spread wider than every difference it was trying to measure. Repeats, a diff that
refuses an unsupported claim, and a per-scenario size are what make a Deck run mean
something. Item 4 is there because the one ground-level scenario's "steady" window
is not steady — it carries ~10 MB of uploads per frame — and that gets worse on a
fast GPU, not better.

Then find (5-6). Two per-pixel effects default to on on a Steam Deck and
have no headless switch at all: light_shafts_on resolves to true on any adapter
that is not a CPU rasteriser with shadows on, which is a 48-step volumetric march
per pixel, and ambient_occlusion_on resolves to true wherever compute shaders
exist, which is an SSAO pass plus the depth and normal prepasses it requires.
Neither is measurable here — lavapipe refuses the march outright — so this is
exactly the gap the Deck exists to fill. Item 5 adds the switches and proves they
reach the render world, because the clouds already taught this lane that a flag
which never arrives reads as an exoneration.

Then act (7-9). Item 7 is the payoff: adaptive_quality has MAX_STEP = 1 and
says in its own header that there is no step 2 because nothing else has been
measured. Item 8 is free and independent — 88% of triangles at 7,191 km go into
shadow cascades where no shadow can resolve. Item 9 is the survey's named remaining
win and the only one here that can fail loudly, so it is last.

Left off deliberately: anything decided by looking at it, the growth_factor slab
question (the note already says it buys memory back with a buffer copy, which is the
burst that cost this project a device), and a rung for the Deck by name — a
vendor-family generalisation is not a measurement, and this tree has learned that
twice. Nothing here touches the wire protocol, a migration, the CI workflows,
tools/autopilot.ts or assetlinks.json.

Item 6 needs the Deck. Everything else builds and is judged in the container.

[
 {
  "title": "cargo perf --repeat N: report a median and a spread, not a point sample",
  "note": "Lands in crates/cartopolis/src/systems/dev/perf.rs and the --perf flag block in lib.rs. Every withdrawn result in docs/notes/performance-suite.md was one sample differenced against one other sample, and docs/notes/render-performance-survey.md records a within-group spread wider than every between-group difference it was trying to measure. --repeat N re-flies the selected scenarios inside one launch and each Window in the JSON carries runs, min, median and max for every numeric column instead of a single value. Judged by: a --repeat 3 report whose windows carry three samples and a median, plus a unit test over synthetic windows pinning the aggregation, including that --repeat 1 still reports min == median == max. Needs no Steam Deck to build or test."
 },
 {
  "title": "cargo perf --baseline <report.json>: a diff that refuses a difference the spread cannot support",
  "note": "Same module as the ticket above and the half that makes it useful. Reads a previous report, matches windows by (scenario, phase), prints per-column deltas, and where a delta is smaller than the combined spread of the two runs reports it as not established rather than as a number. This is the tool every later ticket here is judged with, and it is what stops a fourth repetition of the sky-shell A/B that produced a clean-looking table saying turning the clouds off increased fill. Judged by: unit tests over two fixture reports \u2014 a real change reported as a delta, a change inside the spread reported as not established, and a window present in one report and not the other named rather than silently dropped."
 },
 {
  "title": "Per-scenario size in docs/perf.toml, so a fill-bound scenario can be measured at the Deck's own panel",
  "note": "PerfScenario in perf.rs plus the manifest. The suite pins one global 640x360 because this container rasterises on a CPU, but the Steam Deck's cost is fill and its panel is 1280x800 \u2014 the cloud finding only exists because somebody passed --size 1280x720 by hand, and at 640x360 the same shell was a third of the frame instead of 71% of it. Add an optional per-scenario size (unknown manifest keys stay a hard error) and record the size each Window was measured at. Judged by: a manifest parse test, a report whose windows carry differing sizes, and --baseline refusing to compare two windows measured at different resolutions instead of subtracting them."
 },
 {
  "title": "Make the city scenario's steady window genuinely quiet",
  "note": "shot_harness.rs's settle test, or the condition perf.rs opens the steady window on. The suite's own note says city's steady window is not quiet and measured 6 new meshes and ~10 MB of uploads per frame in a window labelled idle \u2014 a streaming measurement wearing the wrong label, and it is what made Bevy's mesh allocator look like a defect. It is worse on a real GPU, not better: the settle verdict arrives far sooner than on lavapipe, so more of the trickle lands inside the window. Wait for the vector-tile streamer to stop producing rather than only for the fetches to go quiet. Judged by: quiet == true on city's steady window in the report, here and on the Deck, with settled_s reported so the cost of waiting longer is visible."
 },
 {
  "title": "CARTO_NO_AO and CARTO_NO_SHAFTS, each proved to reach the render world",
  "note": "systems/map/render_features.rs, on the env_flag path beside CARTO_NO_CLOUDS. AppPrefs::ambient_occlusion_on defaults to on wherever the adapter has compute shaders and light_shafts_on defaults to on wherever the adapter is not a CPU rasteriser and shadows are on \u2014 so a Steam Deck runs a 48-step volumetric march (render_features::MARCH_STEPS) plus an SSAO pass and the depth and normal prepasses it requires, by default, and there is no headless switch for either. The trap to avoid is the one the clouds already sprang: a first A/B returned 303.8 ms against 293.2 ms and looked like an exoneration, and the flag had never reached the shader. Judged by: with each flag set the corresponding pass is absent from gpu_passes in the perf report and its fragment_invocations are 0, the way CARTO_NO_CLOUDS shows as a transparent pass of 0.03 ms. AO is checkable in this container; shafts are refused on a software renderer by design, so that arm is proved on the Deck."
 },
 {
  "title": "Measure the Deck's default-on per-pixel effects and write the ladder into the survey note",
  "note": "A measurement ticket, no behaviour change. Run city and low-orbit at the panel resolution with --repeat 3 across baseline, CARTO_NO_SHAFTS, CARTO_NO_AO, CARTO_NO_CLOUDS and MSAA off, and record medians, spreads and fragment counts as a table in docs/notes/render-performance-survey.md beside the existing cloud one. The numbers must come from the Deck: lavapipe cannot run the volumetric march at all (a single scripted capture did not finish in ten minutes, measured 2026-09-04) and a noise-heavy ALU shader is mispriced there anyway. What this container can contribute is the fragment counts, which are the same arithmetic on any rasteriser. Judged by: the table exists, every row reports quiet == true and a spread, and any configuration whose difference sits inside that spread is written down as not established rather than as a win."
 },
 {
  "title": "Extend adaptive_quality past its single step, in the order the Deck measured",
  "note": "systems/dev/adaptive_quality.rs, whose MAX_STEP is 1 and which says plainly that there is no step 2 because the clouds are the only effect measured to be worth a step. The ticket above supplies the rest of the order. Add the next steps most-expensive-first \u2014 on present evidence the shafts, then AO, then the sample count \u2014 keeping the existing hysteresis: demotion on a sustained miss, promotion on a longer and stricter margin, the two thresholds not meeting. Judged by: decide()'s existing unit tests extended over the new ladder \u2014 every step reachable, every step reversible, no oscillation at any frame time, and the ladder monotone in cost \u2014 plus the step named in the diagnostics readout. Nothing here picks an order by taste; a step with no measured number behind it does not get added."
 },
 {
  "title": "Stop submitting space-tier geometry to shadow cascades that cannot resolve a shadow",
  "note": "The same readout that settled the cloud question shows 6,998 of 7,978 triangles (88%) going into cascades at 7,191 km, three passes over geometry whose shadows are orders of magnitude below a pixel, and nothing gates cascade submission on the tier. Do it with NotShadowCaster on the globe-tier geometry \u2014 the pattern grass, transit and sky_dome already use and which render_census already reads \u2014 and not by reconfiguring the lights: the sun and the moon must keep matching num_cascades and shadows must never be toggled at runtime, or bevy_light panics outright (CLAUDE.md). Judged by: tris_shadow in --dump-state at the orbit scenario falls to ~0 while draws and tris_total are unchanged, asserted with --expect, and the perf report shows the shadow passes' fragment invocations down."
 },
 {
  "title": "Composite egui inside the upscaling pass instead of in a second full-resolution pass",
  "note": "systems/gui/egui_manager.rs and a local replacement for Bevy's upscaling node. The survey names this as the real remaining win: egui runs as a full pass over the swapchain with LoadOp::Load, a whole load and store of the output every frame purely to composite the UI, and on a 1280x800 handheld that is a fixed per-frame tax on a frame that is already fill-bound. render_scale::make_the_blit_filter shows BlitPipeline is reachable from RenderStartup, so the pieces exist: blit, then hand the open RenderPass to egui_wgpu. Ordered last because it is the one here that can fail loudly \u2014 the failure mode is a wgpu validation error, which this tree answers with a silent mid-stream quit. Judged by: the separate egui pass is gone from gpu_passes, the GPU frame total is down by more than the measured spread, no validation error in the log, and a scripted capture with --ui still shows the chrome (the geometry half; a person confirms the pixels)."
 }
]
The focus is the Steam Deck, and the shape of the answer is already set by what landed on 2026-09-09: the Deck was the machine that settled the globe cloud shell — 71% of the GPU frame at 1280x720, the difference between 52 and 85 fps — after this container had spent weeks unable to say anything about it. That result is the model. It came from counts and from a real GPU, and it was only trustworthy because the windows reported `quiet`, the fragment counts repeated to the unit, and the machine was idle. So the queue below is in three parts, and the order is the dependency order. **The instrument first (1-4).** `cargo perf` today writes one sample per window to `/tmp/perf.json` and has no way to repeat a run, no way to diff two runs, and one global resolution. Every withdrawn finding in `docs/notes/performance-suite.md` is a point sample differenced against a point sample; the survey records a within-group spread wider than every difference it was trying to measure. Repeats, a diff that refuses an unsupported claim, and a per-scenario size are what make a Deck run mean something. Item 4 is there because the one ground-level scenario's "steady" window is not steady — it carries ~10 MB of uploads per frame — and that gets worse on a fast GPU, not better. **Then find (5-6).** Two per-pixel effects default to **on** on a Steam Deck and have no headless switch at all: `light_shafts_on` resolves to true on any adapter that is not a CPU rasteriser with shadows on, which is a 48-step volumetric march per pixel, and `ambient_occlusion_on` resolves to true wherever compute shaders exist, which is an SSAO pass plus the depth and normal prepasses it requires. Neither is measurable here — lavapipe refuses the march outright — so this is exactly the gap the Deck exists to fill. Item 5 adds the switches and proves they reach the render world, because the clouds already taught this lane that a flag which never arrives reads as an exoneration. **Then act (7-9).** Item 7 is the payoff: `adaptive_quality` has `MAX_STEP = 1` and says in its own header that there is no step 2 because nothing else has been measured. Item 8 is free and independent — 88% of triangles at 7,191 km go into shadow cascades where no shadow can resolve. Item 9 is the survey's named remaining win and the only one here that can fail loudly, so it is last. Left off deliberately: anything decided by looking at it, the `growth_factor` slab question (the note already says it buys memory back with a buffer copy, which is the burst that cost this project a device), and a rung for the Deck by name — a vendor-family generalisation is not a measurement, and this tree has learned that twice. Nothing here touches the wire protocol, a migration, the CI workflows, `tools/autopilot.ts` or `assetlinks.json`. Item 6 needs the Deck. Everything else builds and is judged in the container. ```autopilot-plan [ { "title": "cargo perf --repeat N: report a median and a spread, not a point sample", "note": "Lands in crates/cartopolis/src/systems/dev/perf.rs and the --perf flag block in lib.rs. Every withdrawn result in docs/notes/performance-suite.md was one sample differenced against one other sample, and docs/notes/render-performance-survey.md records a within-group spread wider than every between-group difference it was trying to measure. --repeat N re-flies the selected scenarios inside one launch and each Window in the JSON carries runs, min, median and max for every numeric column instead of a single value. Judged by: a --repeat 3 report whose windows carry three samples and a median, plus a unit test over synthetic windows pinning the aggregation, including that --repeat 1 still reports min == median == max. Needs no Steam Deck to build or test." }, { "title": "cargo perf --baseline <report.json>: a diff that refuses a difference the spread cannot support", "note": "Same module as the ticket above and the half that makes it useful. Reads a previous report, matches windows by (scenario, phase), prints per-column deltas, and where a delta is smaller than the combined spread of the two runs reports it as not established rather than as a number. This is the tool every later ticket here is judged with, and it is what stops a fourth repetition of the sky-shell A/B that produced a clean-looking table saying turning the clouds off increased fill. Judged by: unit tests over two fixture reports \u2014 a real change reported as a delta, a change inside the spread reported as not established, and a window present in one report and not the other named rather than silently dropped." }, { "title": "Per-scenario size in docs/perf.toml, so a fill-bound scenario can be measured at the Deck's own panel", "note": "PerfScenario in perf.rs plus the manifest. The suite pins one global 640x360 because this container rasterises on a CPU, but the Steam Deck's cost is fill and its panel is 1280x800 \u2014 the cloud finding only exists because somebody passed --size 1280x720 by hand, and at 640x360 the same shell was a third of the frame instead of 71% of it. Add an optional per-scenario size (unknown manifest keys stay a hard error) and record the size each Window was measured at. Judged by: a manifest parse test, a report whose windows carry differing sizes, and --baseline refusing to compare two windows measured at different resolutions instead of subtracting them." }, { "title": "Make the city scenario's steady window genuinely quiet", "note": "shot_harness.rs's settle test, or the condition perf.rs opens the steady window on. The suite's own note says city's steady window is not quiet and measured 6 new meshes and ~10 MB of uploads per frame in a window labelled idle \u2014 a streaming measurement wearing the wrong label, and it is what made Bevy's mesh allocator look like a defect. It is worse on a real GPU, not better: the settle verdict arrives far sooner than on lavapipe, so more of the trickle lands inside the window. Wait for the vector-tile streamer to stop producing rather than only for the fetches to go quiet. Judged by: quiet == true on city's steady window in the report, here and on the Deck, with settled_s reported so the cost of waiting longer is visible." }, { "title": "CARTO_NO_AO and CARTO_NO_SHAFTS, each proved to reach the render world", "note": "systems/map/render_features.rs, on the env_flag path beside CARTO_NO_CLOUDS. AppPrefs::ambient_occlusion_on defaults to on wherever the adapter has compute shaders and light_shafts_on defaults to on wherever the adapter is not a CPU rasteriser and shadows are on \u2014 so a Steam Deck runs a 48-step volumetric march (render_features::MARCH_STEPS) plus an SSAO pass and the depth and normal prepasses it requires, by default, and there is no headless switch for either. The trap to avoid is the one the clouds already sprang: a first A/B returned 303.8 ms against 293.2 ms and looked like an exoneration, and the flag had never reached the shader. Judged by: with each flag set the corresponding pass is absent from gpu_passes in the perf report and its fragment_invocations are 0, the way CARTO_NO_CLOUDS shows as a transparent pass of 0.03 ms. AO is checkable in this container; shafts are refused on a software renderer by design, so that arm is proved on the Deck." }, { "title": "Measure the Deck's default-on per-pixel effects and write the ladder into the survey note", "note": "A measurement ticket, no behaviour change. Run city and low-orbit at the panel resolution with --repeat 3 across baseline, CARTO_NO_SHAFTS, CARTO_NO_AO, CARTO_NO_CLOUDS and MSAA off, and record medians, spreads and fragment counts as a table in docs/notes/render-performance-survey.md beside the existing cloud one. The numbers must come from the Deck: lavapipe cannot run the volumetric march at all (a single scripted capture did not finish in ten minutes, measured 2026-09-04) and a noise-heavy ALU shader is mispriced there anyway. What this container can contribute is the fragment counts, which are the same arithmetic on any rasteriser. Judged by: the table exists, every row reports quiet == true and a spread, and any configuration whose difference sits inside that spread is written down as not established rather than as a win." }, { "title": "Extend adaptive_quality past its single step, in the order the Deck measured", "note": "systems/dev/adaptive_quality.rs, whose MAX_STEP is 1 and which says plainly that there is no step 2 because the clouds are the only effect measured to be worth a step. The ticket above supplies the rest of the order. Add the next steps most-expensive-first \u2014 on present evidence the shafts, then AO, then the sample count \u2014 keeping the existing hysteresis: demotion on a sustained miss, promotion on a longer and stricter margin, the two thresholds not meeting. Judged by: decide()'s existing unit tests extended over the new ladder \u2014 every step reachable, every step reversible, no oscillation at any frame time, and the ladder monotone in cost \u2014 plus the step named in the diagnostics readout. Nothing here picks an order by taste; a step with no measured number behind it does not get added." }, { "title": "Stop submitting space-tier geometry to shadow cascades that cannot resolve a shadow", "note": "The same readout that settled the cloud question shows 6,998 of 7,978 triangles (88%) going into cascades at 7,191 km, three passes over geometry whose shadows are orders of magnitude below a pixel, and nothing gates cascade submission on the tier. Do it with NotShadowCaster on the globe-tier geometry \u2014 the pattern grass, transit and sky_dome already use and which render_census already reads \u2014 and not by reconfiguring the lights: the sun and the moon must keep matching num_cascades and shadows must never be toggled at runtime, or bevy_light panics outright (CLAUDE.md). Judged by: tris_shadow in --dump-state at the orbit scenario falls to ~0 while draws and tris_total are unchanged, asserted with --expect, and the perf report shows the shadow passes' fragment invocations down." }, { "title": "Composite egui inside the upscaling pass instead of in a second full-resolution pass", "note": "systems/gui/egui_manager.rs and a local replacement for Bevy's upscaling node. The survey names this as the real remaining win: egui runs as a full pass over the swapchain with LoadOp::Load, a whole load and store of the output every frame purely to composite the UI, and on a 1280x800 handheld that is a fixed per-frame tax on a frame that is already fill-bound. render_scale::make_the_blit_filter shows BlitPipeline is reachable from RenderStartup, so the pieces exist: blit, then hand the open RenderPass to egui_wgpu. Ordered last because it is the one here that can fail loudly \u2014 the failure mode is a wgpu validation error, which this tree answers with a silent mid-stream quit. Judged by: the separate egui pass is gone from gpu_passes, the GPU frame total is down by more than the measured spread, no validation error in the log, and a scripted capture with --ui still shows the chrome (the geometry half; a person confirms the pixels)." } ] ```
Author
Collaborator

Planning pass — no code changed, no commits, branch left as cut from main. The plan is posted on #294 as an autopilot-plan block of nine items, and the ticket is closed.

What the plan says, and why

The Steam Deck already proved its worth once: the 2026-09-09 run is what settled the globe cloud shell at 71% of the GPU frame after this container had spent weeks unable to say anything about it. That result is the template — counts, a real GPU, quiet windows, fragment counts repeating to the unit. The nine items are in dependency order across three parts.

Instrument first (1–4). cargo perf writes one sample per window, cannot repeat a run, cannot diff two runs, and pins one global 640×360. Every withdrawn finding in performance-suite.md is a point sample differenced against a point sample, and the survey records a spread wider than the differences it was chasing. Items 1–3 add --repeat N with a median and spread, a --baseline diff that reports a delta inside the spread as not established rather than as a number, and a per-scenario size so a fill-bound scenario can be measured at the Deck's own panel. Item 4 fixes the city steady window, which carries ~10 MB of uploads per frame while claiming to be idle — worse on a fast GPU, not better.

Then find (5–6). The concrete Deck finding from this read: two per-pixel effects default to on there and have no headless switch at all. light_shafts_on resolves true on any non-software adapter with shadows on — a 48-step volumetric march per pixel — and ambient_occlusion_on resolves true wherever compute shaders exist, bringing a depth and a normal prepass with it. Neither is measurable in this container; lavapipe refuses the march outright (a capture didn't finish in ten minutes). Item 5 adds the switches and proves they reach the render world, because the clouds already taught this lane that a flag which never arrives reads as an exoneration.

Then act (7–9). adaptive_quality has MAX_STEP = 1 and says in its own header that there is no step 2 because nothing else has been measured — item 6 supplies the order, item 7 spends it. Item 8 is free and independent: 88% of triangles at 7,191 km go into shadow cascades where no shadow can resolve, fixable with NotShadowCaster (not by reconfiguring the lights — bevy_light panics). Item 9 is the survey's named remaining win, egui's second full-resolution pass, ordered last because its failure mode is a wgpu validation error and a silent mid-stream quit.

Left off deliberately

Anything judged by looking at it; the mesh-slab growth_factor question, since the note already says it buys memory back with a buffer copy — the burst that has cost this project a device; and a rung for the Deck picked by name, which is the vendor-family generalisation this tree has now learned against twice.

For you to decide

Item 6 needs the Deck. It is a measurement ticket with no code change, and its numbers cannot come from this container. Everything else builds and is judged here. If the Deck isn't reachable from the build lane, item 6 parks and item 7 parks behind it — the other seven still stand on their own.

No fenced surface is touched: no wire protocol, no migration, no CI workflow, no tools/autopilot.ts, no assetlinks.json.

No commits were made.


🤖 viberfox issue agent on the VPS — 34 turns · $3.86 · session 3852aea3-48f3-40af-93d4-92ec7493e720. Reply with @agent to continue this session.

Planning pass — no code changed, no commits, branch left as cut from `main`. The plan is [posted on #294](https://code.garage44.eu/jeroen/cartopolis/issues/294#issuecomment-2425) as an `autopilot-plan` block of nine items, and the ticket is closed. ## What the plan says, and why The Steam Deck already proved its worth once: the 2026-09-09 run is what settled the globe cloud shell at 71% of the GPU frame after this container had spent weeks unable to say anything about it. That result is the template — counts, a real GPU, `quiet` windows, fragment counts repeating to the unit. The nine items are in dependency order across three parts. **Instrument first (1–4).** `cargo perf` writes one sample per window, cannot repeat a run, cannot diff two runs, and pins one global 640×360. Every withdrawn finding in `performance-suite.md` is a point sample differenced against a point sample, and the survey records a spread wider than the differences it was chasing. Items 1–3 add `--repeat N` with a median and spread, a `--baseline` diff that reports a delta inside the spread as *not established* rather than as a number, and a per-scenario size so a fill-bound scenario can be measured at the Deck's own panel. Item 4 fixes the `city` steady window, which carries ~10 MB of uploads per frame while claiming to be idle — worse on a fast GPU, not better. **Then find (5–6).** The concrete Deck finding from this read: two per-pixel effects default to **on** there and have no headless switch at all. `light_shafts_on` resolves true on any non-software adapter with shadows on — a 48-step volumetric march per pixel — and `ambient_occlusion_on` resolves true wherever compute shaders exist, bringing a depth and a normal prepass with it. Neither is measurable in this container; lavapipe refuses the march outright (a capture didn't finish in ten minutes). Item 5 adds the switches and proves they reach the render world, because the clouds already taught this lane that a flag which never arrives reads as an exoneration. **Then act (7–9).** `adaptive_quality` has `MAX_STEP = 1` and says in its own header that there is no step 2 because nothing else has been measured — item 6 supplies the order, item 7 spends it. Item 8 is free and independent: 88% of triangles at 7,191 km go into shadow cascades where no shadow can resolve, fixable with `NotShadowCaster` (not by reconfiguring the lights — bevy_light panics). Item 9 is the survey's named remaining win, egui's second full-resolution pass, ordered last because its failure mode is a wgpu validation error and a silent mid-stream quit. ## Left off deliberately Anything judged by looking at it; the mesh-slab `growth_factor` question, since the note already says it buys memory back with a buffer copy — the burst that has cost this project a device; and a rung for the Deck picked by name, which is the vendor-family generalisation this tree has now learned against twice. ## For you to decide **Item 6 needs the Deck.** It is a measurement ticket with no code change, and its numbers cannot come from this container. Everything else builds and is judged here. If the Deck isn't reachable from the build lane, item 6 parks and item 7 parks behind it — the other seven still stand on their own. No fenced surface is touched: no wire protocol, no migration, no CI workflow, no `tools/autopilot.ts`, no `assetlinks.json`. _No commits were made._ --- <sub>🤖 viberfox issue agent on the VPS — 34 turns · $3.86 · session `3852aea3-48f3-40af-93d4-92ec7493e720`. Reply with **@agent** to continue this session.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeroen/cartopolis#294
No description provided.