Files
gamedev-the-steward/docs/FEATURE_PRESENTATION_PERFORMANCE.md
T

9.2 KiB
Raw Blame History

Feature slice: presentation, relevance, and weak-PC performance

Presentation is deliberately replaceable and bounded. The simulation can run headless; loaded scenes provide geometry, navigation, animation, cues, and input. This guide records the current performance controls and the evidence we do and do not have.

Loaded-world boundary

ActiveWorldAdapter owns a context identity (world_id, location_id, context_id), a WorldTargetRegistry, loaded resource spatial index, and provider handles for resource/storage/activity/animal nodes. It is the only place where a simulation target is paired with loaded geometry. Multiple contexts must use separate ancestor containers; duplicate stable IDs are valid only when they belong to different explicit contexts.

WorldViewManager owns active NPC visual instances and forwards target/arrival/ navigation callbacks. NpcVisual, CreatureVisual, and AnimalNode follow authoritative positions. They do not own elapsed travel or saved quantities.

Relevance and navigation budgets

PresentationRelevancePolicy.select_detailed_visuals() rejects foreign world, context, or location candidates before ranking and returns at most 40 detailed visuals. Explicit pins include named/important people, player-affecting threats, situation/commitment participants, leaders, and unique owners. If mandatory pins exceed the cap, the policy fails closed instead of silently dropping identities. Decisions include score, distance, pin reasons, and rejection traces.

PresentationNavRequestBudget admits at most two new navigation requests per frame. It exposes pending/in-flight IDs and deterministic priority order. The budget is a pure policy contract today; wiring it into every production visual spawn is a next integration.

Jajce quality profiles

JajceWorld owns reversible High, Balanced, and Low presentation profiles with instance-local mutable environment/grass resources and viewport-scale ownership.

Profile 3D scale Shadow distance Volumetric/glow Grass
High authored authored (currently 180 m) authored when renderer supports it authored density/interactors
Balanced (default) 0.85 cap 120 m cap authored ordinary lookdev; unsupported volumetric disabled reduced spacing/interactors/update rate
Low 0.70 cap 80 m, orthogonal volumetric/glow/adjustment disabled; ordinary fog retained hidden/stopped, emitters off, shader interactors zero

UI remains at native resolution. High/Low switches restore captured authored values; viewport scale restoration is guarded by the current quality owner so a second world cannot clobber the active owner. Compatibility/mobile feature selection never forces unsupported volumetric fog on.

Grass trails and bush contact

GrassInteractionController samples the existing grass_interactors group: player, NPCs, hostile creatures and animals. The player keeps one slot; other visible actors are ranked by camera distance within the loaded actor container. High updates at 0.08 seconds with eight actors; Balanced uses 0.12 seconds and four. Low stops updates and immediately releases both grass and bushes.

FoliageTrailMap maintains a disposable 128×128 RGBA float texture covering a 64 m square around the camera. Swept footprints part grass to either side and lower its tips; the field recovers over eight seconds after contact. Sparse history is clipped to the moving window. The 256 KiB texture is reused with ImageTexture.update, at most 3.125 MiB/s of texture data at the High update rate. The vertex shader adds one field lookup per grass vertex; there are no blade collision bodies, per-blade scripts, render targets or added grass draw calls.

StylizedBerryPatch shares the field and contact positions between its leaf and berry MultiMeshes. Both bend in world space around the planted bush root, including under rotated/scaled parents, and spring back faster than grass. The five village berry placeholders now use this same amount-aware presentation as the existing riverbank bush. Contact does not change berries, resource amounts, navigation, targets or collision geometry.

The controller clears history after SimulationManager.state_restored, on quality changes and on world disposal. Missing/recreated actors, teleports over 3 m between samples and update gaps over 0.5 seconds start a new footprint rather than a connecting trail. Actor foot height gates deformation so bridge traffic does not flatten grass below. Trails are visual only and never enter saves. The exported actor, primary actor, foliage and simulation paths can be rebound when placing the controller in another loaded scene container.

tests/foliage_interaction_test.gd checks sweep direction, recovery, teleport rejection, camera movement, world isolation, restore/quality reset, the primary actor slot and shared leaf/fruit bindings. Run tools/capture_foliage.gd with the native renderer for before/contact/trail/recovery images and controller timing in docs/baselines/foliage_*. That capture freezes simulation and wind, and moves the player presentation through the actual meadow, checking the state checksum is unchanged. CPU update timing is not a GPU or weak-PC frame-rate guarantee. Native Metal and Compatibility both render the effect; existing particle-shader shutdown warnings (and four Compatibility texture leaks) also reproduce in the untouched pre-art baseline. The headless quality gate retains its exact allowlist.

Runtime hot paths already bounded

The woodland storybook art pass adds portable Blender animal meshes and an opaque 35-triangle, seven-blade grass tuft. The existing camera-local grass field and quality controls own its instance budget; four loaded path strips provide disposable grass-clearance data after material isolation. Animal appearance is derived from stable NPC IDs, shares mesh resources, and retains profession tint, velocity-driven motion, inventory cues and death through the existing presentation controllers. No saved state or navigation changes.

Characters and buildings use soft cel bands; tree crowns retain the shared breeze with painted color patches. A static procedural cloud sky, cooler ambient fill and warm sunlight follow the simulation day/night clock. The cloud shader adds no volumetric pass, outline pass or screen-space postprocess. See the asset contract and rebuild commands.

docs/baselines/storybook_*.png records the native Metal review. The accompanying storybook_render_metrics.json records a bounded local 1600×900 static-camera sample on Apple M1 Max with six NPCs and High/Balanced/Low settings. Wall-frame latency includes vsync and does not close the weak-PC evidence gap below.

  • Player interaction and villager inspection probes are throttled/event-driven; they no longer rebuild all candidate data every rendered frame.
  • LoadedResourceSpatialIndex bounds finite-resource discovery.
  • SimulationPopulationView gives stable-ID all/living/starving indexes once per tick.
  • WorldTargetRegistry gives O(1) target lookup and deterministic iteration.
  • Creature path failures retain the failed target and use bounded backoff.
  • Hostile hit flashes are health-change driven, not position-change driven.
  • Day/night, status, time dial, storage/debug labels, and grass updates run at human-readable rates or only on state changes.
  • Regional scheduled work uses a due-job heap rather than a permanent scan.

What the benchmarks prove

  • Simulation baseline 03: 600 full-fidelity data-only NPC/combatant records at about 90.6 ticks/s on the reviewed Apple M1 Max capture, with matching checksums across samples.
  • Loaded Resource Discovery 01: indexed 1,800-anchor queries preserve exact selection checksums while reducing resolution work dramatically.
  • Regional scale baseline 01: five settlements, 20 caravans, 2,000 named people, 50,000 aggregate residents, 2,025 jobs, budget/checksum parity.

These are headless/structural measurements. They do not prove 600 rendered visuals, a 30-day regional economy, or weak-PC GPU frame time.

Remaining evidence gap

The project still needs a recorded reference weak-PC active-world workload at a fixed gameplay camera, including High and Low p50/p95/p99 CPU/GPU frame time, draw calls/primitives, memory/VRAM, active detailed visuals, and navigation queue depth. The CI gate should keep structural profile assertions and the two-request policy; hardware measurements belong in a reviewed benchmark ledger, not brittle cross-machine CI thresholds.

Performance extension rules

  1. Measure a representative workload before optimizing.
  2. Keep simulation and presentation results checksum-identical across loaded, unloaded, and load/unload runs.
  3. Add indexes, dirty sets, analytical updates, or scheduled jobs before adding more per-frame scans.
  4. Keep allocations out of render-frame probes and avoid sorting when a stable bounded comparison is sufficient.
  5. Any new visual must declare relevance/pin behavior and obey the navigation request budget.

See the benchmark ledger, regional contract, and the build-in-public visual plan.