257 lines
15 KiB
Markdown
257 lines
15 KiB
Markdown
# 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 / MSAA | Shadow distance | Volumetric/glow | Grass |
|
||
| --- | ---: | ---: | --- | --- |
|
||
| High | authored / authored 4× | authored (currently 180 m) | authored when renderer supports it | authored density/interactors |
|
||
| Balanced (default) | 0.85 cap / 2× cap | 120 m cap | authored ordinary lookdev; unsupported volumetric disabled | reduced spacing/interactors/update rate |
|
||
| Low | 0.70 cap / off | 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`](https://docs.godotengine.org/en/stable/classes/class_imagetexture.html#class-imagetexture-method-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 painted cloud panorama, 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](../assets/storybook/README.md).
|
||
|
||
The current humanoid cast uses the [modular woodland character kit](../assets/characters/README.md):
|
||
31 Blender parts, five animal heads, six authored examples and independently
|
||
configurable proportions, outfits and accessories. `CharacterAppearanceProfile`
|
||
resources are cosmetic. Meshes are shared; `Stout` morph weights and palette
|
||
materials are per actor. The reference traveler has 20,070 base triangles across
|
||
10 opaque surfaces; Godot imports LODs. The standalone
|
||
`tools/CharacterWorkshop.tscn` previews and saves profiles. Existing motion,
|
||
profession/inventory/death cues, and shader grass interaction remain owned by
|
||
their existing controllers. The appearance scenario verifies imported parts,
|
||
independent shape weights, stable identity, extreme proportions and cue clearance.
|
||
|
||
`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](benchmarks/README.md), [regional contract](REGIONAL_SIMULATION.md),
|
||
and [the build-in-public visual plan](BUILD_IN_PUBLIC_PLAN.md).
|
||
|
||
## Painterly environment pass — September 2026
|
||
|
||
The reference study highlighted flat cloud bands, primitive tree silhouettes,
|
||
sparse ground cover, opaque green water, and a sun arc offset from daytime
|
||
brightness. The current pass addresses those through shared presentation assets:
|
||
|
||
- The WorldEnvironment uses cool blue ambient fill, warm sunlight, restrained
|
||
glow and ordinary aerial fog. The solar arc now peaks at noon and the warm
|
||
transitions overlap dawn/dusk. An original painted cloud panorama supplies
|
||
layered cumulus shapes; a static sky shader remaps the artwork, blends its
|
||
seam/horizon, and tints it from the simulation clock. It adds no TIME-driven
|
||
cubemap updates or volumetric cloud pass. Generation provenance and the exact
|
||
prompt are in [the asset guide](../assets/painterly/README.md).
|
||
- Original Blender broadleaf crowns/trunks and layered conifers replace the
|
||
primitive foliage. Seventeen decorative edge trees form a woodland backdrop.
|
||
The four village tree placeholders now use `HarvestableTreePresentation`,
|
||
retaining their existing IDs, amounts, interaction points and resource count.
|
||
Full/low/depleted state and restored crowns still derive from real amounts.
|
||
- `PainterlyMeadow` creates at most 1,680 decorative flower clumps in four
|
||
MultiMesh batches, with cream daisies, pink cosmos, buttercups and lavender.
|
||
Local seeded drifts follow terrain and exclude houses, paths, work sites and
|
||
resource approach points. High/Balanced/Low expose at most 1,680/1,092/536
|
||
clumps, with shorter distance fading in cheaper profiles. Flowers share the
|
||
prevailing breeze; the existing grass and berry contact field remains intact.
|
||
Explicit white instance colors preserve the painted GLB palette when custom
|
||
instance data is enabled in Compatibility; native review caught and verified
|
||
that renderer-specific regression.
|
||
Flowers have no collision, harvesting, actor-contact deformation or saved
|
||
state. See [mesh sources and budgets](../assets/meadow/README.md).
|
||
- The blue/turquoise river follows actual centerline terrain heights, keeps
|
||
level cross-sections, and clips dry edges at the shoreline. The pool and its
|
||
outlet meet without overlapping coplanar transparent sheets. Broken foam,
|
||
subtle flowing strokes and painted reflection patches replace the hard ring
|
||
and featureless surface. These reflections are stylized procedural patches
|
||
plus ordinary sky specular response, not exact reflected scene geometry,
|
||
screen-space reflections, refraction or depth sampling. Water obeys scene
|
||
lighting at night and adds no per-frame scripts.
|
||
- MSAA follows the existing viewport ownership rules: High preserves authored
|
||
4×, Balanced caps at 2×, and Low disables it. Closing an inactive world cannot
|
||
overwrite the active world's antialiasing or an external viewport override.
|
||
|
||
The editable Blender sources live under `art/painterly/` and `art/meadow/`,
|
||
protected by `.gdignore`; runtime consumes portable GLBs. The broadleaf is
|
||
5,350 triangles / five surfaces and the conifer is 1,580 / two. All flower
|
||
clumps together are capped at 362,880 triangles / four surfaces before
|
||
quality and distance reduction. No scene collision, navigation bake, terrain
|
||
heightmap, simulation rule or save schema was changed.
|
||
|
||
`tools/capture_painterly_environment.gd -- --label=after` captures the running
|
||
main scene with paused simulation, fixed daytime presentation and hidden HUD,
|
||
including the normal gameplay camera, valley, lower village view and night.
|
||
The valley/near-village `painterly_before_*` and `painterly_after_*`
|
||
images are matched cameras; `after_gameplay` fixes the normal rig at its default
|
||
yaw/zoom with mouse input disabled, and `after_vista` is an additional review angle.
|
||
The tool also records static-camera profile samples in
|
||
`painterly_after_metrics.json`; wall-frame latency includes vsync and is not
|
||
isolated GPU time or weak-PC proof. Runtime camera defaults are unchanged.
|
||
|
||
Remaining visual work is primarily authored terrain/shoreline detail and the
|
||
blockout-like building/fortress forms. This pass improves atmosphere and
|
||
foliage without treating those assets as finished film-quality environments.
|
||
|
||
Validation for this pass: the full macOS Godot 4.7 gate passed, including all
|
||
headless scenarios, the Compatibility quality scenario, and 182 GUT tests /
|
||
2,279 assertions. Main-scene headless startup passed the gate's existing exact
|
||
diagnostic allowlist. Native Metal daytime/gameplay/valley/night images were
|
||
reviewed at 1600×900, with a separate native Compatibility capture confirming
|
||
colored petals after the instance-buffer fix. The presentation-quality scenario
|
||
also passed with native Compatibility, including white instance-color readback
|
||
(the headless dummy renderer cannot provide that readback).
|
||
The static M1 Max sample reports approximately 16.7 ms
|
||
median wall-frame time in all three profiles; see its JSON for p95, draw calls
|
||
and primitives. This includes vsync and does not establish GPU headroom.
|
||
|
||
Native renderer shutdown still reports the particle shader/material RID
|
||
retention previously documented in the storybook/foliage pass; the headless
|
||
allowlist was not expanded. Compatibility has different color/tonemapping and
|
||
its existing particle/texture shutdown diagnostics. Metal is the color
|
||
reference for the saved art-review images.
|
||
The native multi-world quality scenario additionally logged a transient macOS
|
||
OpenGL cubemap-unloadable diagnostic; the final main-scene captures did not.
|