Files
ARCADIA-Studio/harness/experiments/grid-cache-20260925/COLD-PAN.md
T

9.1 KiB
Raw Blame History

Cold construction and pan alternatives

Follow-up to the stationary-camera cache, for issue 44. Application source remains 336720559bcec511949caa8a42bee8a55c3822da, with evidence checkpoint 2ca84676ee4dc30846208881a27d90a901482085. These standalone experiments change no application code, dependencies, event handling or release. Native interaction remains paused; no result below establishes native FPS.

Question and method

The existing cache removes repeated construction at identical camera inputs but misses during pan/zoom. Can simpler geometry or renderer-level translation remove that cost without changing the grid's appearance? Use the same unoptimized Iced 0.14 WGPU dependencies and Windows environment recorded in context.json. Compile the new drivers with the same rustc/extern/linker arguments described in README, replacing the driver filename and output executable. No desktop window is created and no input is sent. Run cold_grid_probe with argument 20; phase drivers take no arguments.

The cold driver compares original circles, one reusable origin circle transformed for each fill, one path per column, and direct 1.15-unit squares. Every candidate also constructs one origin-circle template; this small common overhead is retained. Three warmups precede 20 changing-pan samples. Construction ends before geometry destruction, rendering and readback. Percentiles use sorted index ceil((n-1)*p). Fixed candidate ordering and uncontrolled OS load limit small timing comparisons.

Cold construction results

cold_grid_probe.rs produces grid-cold-01.log. Median milliseconds:

Canvas, zoom Original Reused circle Column batch Square
1900x660, 1.0 61.0564 55.3975 54.3594 3.5527
1900x660, 0.5 215.8919 162.3073 158.5968 7.5699
1440x900, 1.0 60.1683 43.3860 60.8198 2.0340
1440x900, 0.5 165.2049 133.3420 143.1489 6.8178

Both circle alternatives exactly match original pixels in all 12 sampled cases (four integer canvas/zoom pairs, three pan offsets, scale 1). This does not prove equivalence at fractional zoom, other DPI or other camera inputs. Squares match eight of those cases but differ at the third pan offset in each canvas/zoom pair: 4,266–13,184 changed pixels, maximum channel difference 7. Their different shape and area are an explicit appearance change, not a pixel-preserving optimization.

Reusing circles and column batches leave substantial cold cost; the experiment does not justify adding them to production as the solution to low FPS. The square alternative is much faster in this scope, but is not adopted without evaluating its appearance and the actual interaction result. The previous all-grid single path experiment remains rejected for its much higher construction cost.

Source explanation and independent review

A fresh read-only critic checked the probes and actual local Iced source. Each Path::circle allocates/builds a path and converts its arc into quadratic segments. Each WGPU Frame::fill allocates a boxed geometry builder and invokes Lyon fill tessellation. Same-color fills already share a solid mesh buffer: this is not one GPU draw call per dot. Frame translation transforms paths before tessellation, so path reuse alone does not reuse triangles.

Renderer-level with_translation can reuse cached triangles. Stock Canvas cannot attach a separate transform to each returned geometry; application adoption would need a small background widget or wrapper, with layout, clipping, lifecycle and interaction checks. Scaling the grid to handle zoom is unsuitable because the dot radius must remain fixed in screen units.

The critic also identified two important controls. Original opaque background geometry must not silently become screenshot-clear color: MSAA/alpha composition can change quantized pixels. Also, iced_wgpu::triangle::Storage::prepare rewrites cached mesh data when its transform changes. Construction/command-recording time therefore excludes real preparation/upload costs. The later probe measures a rendering/readback path separately; neither timer represents native presentation.

Primary sources: Iced 0.14 iced_graphics/src/geometry/path{,/builder}.rs, iced_wgpu/src/{geometry,triangle}.rs, iced_core/src/renderer.rs, and iced_widget/src/canvas.rs. The reviewer changed no files and did not execute the probes; execution and interpretation remain the parent's responsibility.

Renderer-translated phase cache

phase_grid_probe_v1.rs / grid-phase-01.log cache zero-phase circles once for each size/spacing pair, translate by pan.rem_euclid(step), and externally clip to the original viewport. The original nonnegative row/column indices and fixed screen-space radius are retained. Cache clip bounds include the radius margins. This first prototype uses screenshot clear color instead of opaque background geometry; its widespread one-level pixel differences are not accepted as parity. Its compiler emitted one duplicate Renderer-import warning, retained in the original driver. Later drivers remove that import and compile with -D warnings.

phase_grid_opaque_probe.rs / grid-phase-opaque-01.log add an oversized opaque background to the same cached geometry as the circles. Its bounds extend from (-step,-step) to (width+2*step,height+2*step), covering the viewport at every positive remainder without adding extra dot rows or columns. This directly compares against the unchanged legacy painter. A proposed separate-background control was not executed; no result is claimed for it.

Of 75 comparisons (five size/zoom pairs, five offsets, three DPI scales), 73 are byte-exact. All 60 cases at integer canvas sizes and spacing 14 or 24 are exact. At 301.5x211.25, zoom 0.73, pan (-13.25,9.75), scale 1 changes 12 pixels by at most one channel level; scale 2 changes one pixel by at most six levels. These are failures of exact parity and are preserved, not hidden behind a tolerance.

The targeted phase_grid_diagnostic.rs / grid-phase-diagnostic-01.log locates all 12 scale-1 differences at the rightmost physical column x=301. The scale-2 single-pixel difference does not recur. That diagnostic skips earlier large cases and their rendering/timing sequence: it does not exonerate the earlier failure or establish that every residual difference is at an edge.

The replay driver's first compilation failed with E0689 because the new control's Vector::new(-13.25,9.75) left its numeric type ambiguous before rem_euclid. An explicit Vector<f32> annotation corrects the experiment harness. No probe executed during that failed attempt; no application source changed.

phase_grid_replay.rs preserves the full opaque probe's rendering/timing sequence and adds coordinate diagnostics without extra renders until that sequence ends. grid-phase-replay-01.log reproduces both failures: the 12 right-edge pixels and one interior pixel at physical (149,263), where legacy RGBA is [240,243,246,255] and cached RGBA is [246,248,251,255]. The latter cannot be dismissed as viewport-edge rounding. Afterwards, all 12 same-renderer repeat controls (three repetitions of old/old and cached/cached at two scales) are byte-exact; the control cache builds once. This finite control does not establish universal determinism, but supports investigating differing geometry coverage instead of treating the reproduced pixel as random readback noise.

Cold construction plus command recording takes 68.42–173.72ms median for the four large viewports; cached translation takes 0.0008–0.0011ms. Each per-size/spacing cache builds exactly once through all pan comparisons and measurements. Zoom and resize through a single retained cache are not tested by these drivers. The labels containing submit mean renderer command recording, not GPU submission.

Two selected cases include construction, recording, backend preparation and GPU readback in a separate timer (two warmups, eight samples, sorted-index median):

Canvas, zoom Original p50 ms Phase cache p50 ms
1900x660, 1.0 259.9265 212.6536
1440x900, 0.5 337.2560 170.3780

These support a benefit within the headless path, but retain significant renderer and readback cost and variable outliers. They are not native frame times. Sample size, sequential ordering and uncontrolled machine load prohibit a precise general speedup claim. All driver/log hashes and execution mapping are in cold-pan-context.json.

The phase candidate removes the measured pan tessellation cost, but does not meet exact visual preservation. Do not adopt it as an invisible optimization. The small circle rewrites leave the main cost and squares change appearance. Further work should investigate the reproduced interior coverage difference or compare optimized dependency/build profiles before adding a custom widget. Any adopted rendering candidate must then verify a real mounted scene at a nonzero widget origin, foreground ordering, viewport clipping, pan/zoom/resize cache lifecycle and unchanged event behavior. Native interaction/FPS verification remains a separate required gate. No application implementation is changed by these experiments.