Skip to main content

render-perf

Guides performance optimization work on the Grida Canvas Rust rendering engine (grida crate). Use when asked to benchmark, profile, optimize, or analyze rendering performance — panning, zooming, compositing, caching, culling, or frame budgeting.

Quellinformationen

Repository
gridaco/nothing
Letzte Quellaktivität
17. Juli 2026 um 14:06
Erkannte Sprache von SKILL.md
Englisch
Sterne
54
Forks
3

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
render-perf
description
Guides performance optimization work on the Grida Canvas Rust rendering engine (grida crate). Use when asked to benchmark, profile, optimize, or analyze rendering performance — panning, zooming, compositing, caching, culling, or frame budgeting.
# Grida Canvas Engine — Performance Development Workflow and reasoning framework for render performance work on the Grida Canvas Rust engine. ## When to Use This Skill - Benchmarking canvas rendering - Optimizing frame time for any camera operation - Designing or modifying caching strategies - Analyzing benchmark results - Writing or auditing benchmarks - Investigating performance regressions --- ## How to Orient Yourself Before touching any code, build context by reading these sources in order: 1. **Read `crates/grida/AGENTS.md`** — crate conventions, test/check commands, benchmarking instructions. 2. **Read `docs/wg/feat-2d/optimization.md`** — the master optimization document. It describes every optimization strategy, measured costs, and the rendering pipeline. This is the single most important file. 3. **Browse `docs/wg/research/chromium/`** — research notes on how Chromium solves equivalent problems. Start with `index.md` for the map, then read whichever documents match the problem at hand. 4. **Read existing benchmarks** — look in `crates/grida/benches/` to understand what is already measured and how. 5. **Read `crates/grida_dev/src/main.rs`** — the `bench` and `bench-report` subcommands show how GPU benchmarks work with real `.grida` scene files. `bench-report` is the bulk mode that outputs JSON across all fixtures. Use `grep` and `glob` to discover the current state of code rather than relying on hardcoded paths. File locations shift as the engine evolves. ### Key discovery queries | What you need | How to find it | | ----------------------------------------- | --------------------------------------------------------------------------------------------------- | | The renderer entry point | `grep "struct Renderer" --include="*.rs"` in `crates/grida/src/` | | Camera change classification | `grep "enum CameraChangeKind" --include="*.rs"` | | How compositor cache works | `grep "struct LayerImage" --include="*.rs"` and read the containing module | | Promotion heuristics | `grep "fn should_promote" --include="*.rs"` | | Where zoom invalidation happens | `grep "zoom_changed\|mark_all_stale\|invalidate_all" --include="*.rs"` in `src/runtime/` | | Frame pipeline flow | Search for `fn queue\|fn flush\|fn draw\|fn frame` in the renderer file | | Benchmark fixture scenes | `--list-scenes` flag on `grida_dev bench`, or run `bench-report` on a directory | | Config toggles (compositing, atlas, etc.) | `grep "set_layer_compositing\|set_compositor_atlas\|set_interaction_render_scale" --include="*.rs"` | | Existing `.plan.md` proposals | `glob "docs/wg/feat-2d/*.plan.md"` | | Scene loading pipeline | `grep "fn load_scene" --include="*.rs"` in `src/runtime/scene.rs` | | Layout engine entry point | `grep "fn compute\b" --include="*.rs"` in `src/layout/engine.rs` | | Text measurement stats | `grep "ParagraphMeasureStats" --include="*.rs"` | | Skip-layout config | `grep "skip_layout" --include="*.rs"` in `src/runtime/` | | Load-bench CLI tool | Read `crates/grida_dev/src/bench/load_bench.rs` | --- ## The Benchmark Systems There are four complementary benchmarks. The bulk report is the recommended starting point for camera-motion work; the node-mutation bench is the go-to for invalidation-pipeline work; the single-scene bench and Criterion provide deeper investigation when needed. | Class | What moves between frames | Tool | Measures | | --------------- | ---------------------------------- | ---------------------------------- | ---------------------------------------------- | | Camera motion | `Camera2D` (pan/zoom) | `grida_dev bench` scenarios | draw + compositor + caches on stable scene | | Node mutation | Scene graph (translate / replace) | `grida_dev bench --translate` | `apply_changes` (invalidation pipeline) + draw | | Bulk regression | All scenes, camera motion | `grida_dev bench-report` (JSON) | per-scene PassStats across the fixture set | | Algorithmic | Synthetic grid, various operations | `cargo bench -p grida` (Criterion) | CPU-only cost with statistical CI | ### 1. Bulk benchmark report (`grida_dev bench-report`) Runs all scenes in all `.grida` files and outputs a compact **JSON report**. Use this to establish baselines, detect regressions across the full fixture set, and identify which scenes/stages are slowest. ```sh # All fixtures — recommended first step cargo run -p grida_dev --release -- bench-report ./fixtures/ --frames 100 --output baseline.json # Single file cargo run -p grida_dev --release -- bench-report ./fixtures/test-grida/bench.grida --frames 200 # Local fixtures for broader coverage cargo run -p grida_dev --release -- bench-report ./fixtures/local/ --frames 100 --output baseline-local.json ``` The JSON report contains per-scene results with: - `nodes`, `effects_nodes` — scene complexity - `fit_zoom` — the zoom level from `fit_camera_to_scene` - `pan` / `zoom` — legacy passes with full `PassStats` - `scenarios[]` — expanded scenario matrix (see below) - `errors[]` — files that failed to load Each `PassStats` contains: - `avg_us`, `fps`, `min_us`, `p50_us`, `p95_us`, `p99_us`, `max_us` — latency distribution - `queue_us`, `draw_us`, `mid_flush_us`, `compositor_us`, `flush_us` — per-stage breakdown - `settle_us` — cost of the stable (settle) frame after the pass ends Progress goes to stderr, JSON to stdout (or `--output path`). This keeps the JSON clean for programmatic consumption. ### 2. Single-scene GPU benchmark (`grida_dev bench`) Runs real scene data on the actual GPU backend (Metal/GL). This is the ground truth for "does the user experience improve?" ```sh # List scenes to pick the right one cargo run -p grida_dev --release -- bench ./fixtures/test-grida/bench.grida --list-scenes # Run a specific scene cargo run -p grida_dev --release -- bench ./fixtures/test-grida/bench.grida --scene <N> --frames 200 ``` **Always use `--release`.** Debug builds are 20-30x slower and produce meaningless data. The output reports legacy pan/zoom, then an expanded scenario matrix covering 20+ scenarios across multiple gesture types. Each scenario reports `min/p50/p95/p99/MAX` plus per-stage breakdown and settle cost. **Scenario types in the expanded matrix:** | Kind | Scenarios | What it tests | | ----------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `pan` | slow/fast × fit/zoomed | Linear back-and-forth panning | | `circle_pan` | small/large radius × fit/zoomed | Circular trackpad gesture (unpredictable edges) | | `zigzag` | fast (continuous) / slow (with pauses) × fit/zoomed | Diagonal reading pattern with direction changes | | `zoom` | slow/fast × around-fit/high | Zoom oscillation at different levels | | `pan_with_settle` | slow/fast × fit/zoomed | Pan with settle frames interleaved every 12 frames | | `zoom_with_settle` | slow/fast × fit/high | Zoom with settle frames interleaved every 12 frames — captures cache-cold spike after settle nukes zoom cache | | `baseline_nocache_zoom` | slow/fast × fit/high | Forces `stable=true` on every zoom frame — no-cache baseline measuring raw full-draw cost for A/B comparison | | `realtime` | fast/slow × fit/zoomed | **Real-time event loop simulation** with sleep, 240Hz tick thread, and settle countdown matching the native viewer | | `frameloop` | 16/50/80/120/200/300/500ms interval | **Real FrameLoop path** — the only bench that captures stable-frame jank during panning (see below) | | `frameloop_zoom` | 16/50/80/120/200/500ms interval | **Real FrameLoop path for zoom** — captures stable-frame intrusion during zoom gestures | | `resize` | alternating viewport sizes | `--resize` flag. Measures `resize()` + `redraw()` cost per cycle (layout rebuild + cache invalidation + repaint) | **SurfaceUI overlay measurement (`--overlay`):** By default, benchmarks measure content rendering only — the SurfaceUI overlay (frame titles, node badges, hit regions) is **not** included. Pass `--overlay` to include overlay drawing after each content flush, matching the real `Application::frame()` pipeline where `draw_and_flush_devtools_overlay()` runs after `Renderer::flush()`. ```sh # A/B test: content only vs content + overlay (MUST be sequential, never parallel) cargo run -p grida_dev --release -- bench ./fixtures/test-grida/L0.grida --scene 22 --frames 200 && \ cargo run -p grida_dev --release -- bench ./fixtures/test-grida/L0.grida --scene 22 --frames 200 --overlay # Bulk report with overlay cargo run -p grida_dev --release -- bench-report ./fixtures/ --frames 100 --overlay --output overlay.json ``` The overlay cost is opt-in because it is a devtools feature, not user content. Overlay cost scales with visible labeled nodes — viewport culling skips off-screen labels, so zoomed-in views are nearly free. At fit-zoom on large scenes (e.g. 500 labels visible), overlay adds ~1.8ms per frame (paragraph layout dominates). At typical editing zoom, the cost drops to ~190µs or less. | Question | Flag | | ---------------------------------------- | -------------- | | Is content rendering fast enough? | (no flag) | | Is the overlay adding visible latency? | `--overlay` | | Compare content-only vs content+overlay? | Run both, diff | The `realtime` scenarios use actual `thread::sleep()` between frames and simulate the native viewer's 240Hz tick thread + settle countdown. These produce frame timings that match what users actually see, including settle-induced frame drops at their natural frequency. The `frameloop` and `frameloop_zoom` scenarios go through the actual `FrameLoop.poll()` / `complete()` path — the same code path as `Application::frame()`. All other pan/zoom scenarios bypass `FrameLoop` and call `queue_unstable()` directly, which means they never produce stable frames mid-interaction. The `frameloop` scenarios sweep event intervals from 16ms (fast flick) to 500ms (discrete clicks) and reveal how `FrameLoop`'s stable-frame decisions affect the frame time distribution at each speed. Use these when investigating panning or zooming jank, adaptive timing, or pan/zoom image cache behavior. **Choosing scenes:** Use `--list-scenes` to see what's available. Pick scenes that stress the subsystem you're optimizing. For effects/caching work, look for scenes with high promoted-node counts. For culling work, look for scenes with many visible nodes but few effects. ### 3. Node mutation benchmark (`grida_dev bench --translate`) Measures the **invalidation pipeline cost** — not camera motion. Every frame mutates the scene graph (translates a target node), then runs `apply_changes` + `queue_unstable` + `flush`. This is the bench that captures real drag-a-node behavior, identical to what `Application::frame()` + `EditorDocument::apply_and_mark` exercise in the demo. ```sh # List scenes, pick a target cargo run -p grida_dev --release -- bench <path.grida> --list-scenes # Translate the first suitable root node for 200 frames cargo run -p grida_dev --release -- bench <path.grida> --translate first --frames 200 # Translate a specific node by id cargo run -p grida_dev --release -- bench <path.grida> --translate 42 --frames 200 ``` Output includes a separate line for **`apply_changes` timing** — this is the cost of running the classifier + dirty-set dispatch + partial rebuild, isolated from the draw phase. Treat this as the authoritative number for "is invalidation slow?" ``` apply_changes: avg=3720us p50=3288us p95=30714us MAX=30714us total frame: avg=65344us p50=62159us fps=15.3 queue: 1.3ms draw: 10ms mid_flush: 49ms compositor: 1µs flush: 7µs settle: 83ms ```
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen