feat: scale village queries and enrich Jajce center
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
```text
|
||||
SimulationManager tick
|
||||
-> ActionExecutionSystem advances needs and working progress
|
||||
-> SimulationPopulationView refreshes transient living/starving indexes
|
||||
-> VillageOpportunitySystem re-derives the current capable helper
|
||||
-> ActionSelectionSystem chooses an action when the NPC is idle
|
||||
-> SimulationManager stores the selected action
|
||||
@@ -25,6 +26,8 @@ SimulationManager tick
|
||||
- queries `RelationshipSystem` after personal survival/schedule overrides, so a
|
||||
trusted starving acquaintance can redirect ordinary work toward food
|
||||
gathering while the pantry is low;
|
||||
- consumes the manager's current `SimulationPopulationView` for that social
|
||||
query instead of rebuilding an all-NPC lookup for each idle decision;
|
||||
- accepts the current ephemeral `OpportunityHelperResult` and, only for its
|
||||
matching NPC, selects the reported ordinary gather/deposit action after
|
||||
urgent self-care and sleep/meal schedule branches;
|
||||
@@ -71,7 +74,10 @@ SimulationManager tick
|
||||
SimulationManager remains the orchestrator and event boundary. It owns
|
||||
simulation records, invokes the focused systems, stores selected actions and
|
||||
targets, and translates presentation callbacks into simulation transitions.
|
||||
It also publishes the latest `ActionSelectionResult` for presentation; the UI
|
||||
It also owns one disposable `SimulationPopulationView`, rebuilt at tick start
|
||||
and refreshed after each NPC advances so interleaved decision semantics remain
|
||||
unchanged.
|
||||
The manager publishes the latest `ActionSelectionResult` for presentation; the UI
|
||||
does not recompute decisions. Each idle selection re-derives the capable helper
|
||||
instead of consulting persisted assignment state. At completion it atomically
|
||||
pays any definition-backed stored-resource cost before applying the action
|
||||
@@ -128,6 +134,8 @@ simulation ticks.
|
||||
- stores the exact event ID as the trust cause and ignores replayed or older
|
||||
events;
|
||||
- selects trusted starving subjects deterministically for action selection;
|
||||
- uses a supplied per-tick population view when available, with a compatibility
|
||||
fallback for isolated callers;
|
||||
- does not choose actions, perform transactions, or format presentation text.
|
||||
|
||||
These collaborators are `RefCounted` rule services, not additional scene-tree
|
||||
|
||||
@@ -10,6 +10,7 @@ results.
|
||||
```text
|
||||
SimulationClock
|
||||
-> SimulationManager orchestrates one deterministic tick
|
||||
-> SimulationPopulationView indexes all/living/starving NPCs by stable ID
|
||||
-> ActionExecutionSystem advances needs and work
|
||||
-> VillageOpportunitySystem re-derives the current capable helper
|
||||
-> ActionSelectionSystem chooses an action
|
||||
@@ -42,6 +43,10 @@ would otherwise obscure that lifecycle:
|
||||
first without weakening its direct-source or one-hop checks;
|
||||
- `simulation/relationships/RelationshipSystem.gd` owns directed relationship
|
||||
queries, event-driven trust changes, and deterministic social tie-breaking;
|
||||
- `simulation/population/SimulationPopulationView.gd` owns a transient derived
|
||||
all/living/starving stable-ID index. The manager rebuilds it once at the tick
|
||||
boundary and refreshes it after each interleaved NPC update; relationship and
|
||||
action queries may read it, but it never enters save records;
|
||||
- `simulation/opportunities/VillageOpportunitySystem.gd` observes immutable
|
||||
event references plus current NPC/storage state, then owns the bounded shared
|
||||
open/resolved/invalidated lifecycle for the proven pantry-food and blocked-
|
||||
@@ -76,6 +81,7 @@ hard to read.
|
||||
| `simulation/events/` | Immutable event history and derived event queries |
|
||||
| `simulation/knowledge/` | Per-NPC knowledge of objective event IDs |
|
||||
| `simulation/relationships/` | Directed social consequences and relationship queries |
|
||||
| `simulation/population/` | Transient per-tick population query indexes |
|
||||
| `simulation/opportunities/` | Knowledge-gated unresolved-condition projections |
|
||||
| `simulation/state/` | Versioned, serializable mutable records |
|
||||
| `simulation/definitions/` | Stable IDs and immutable gameplay definitions |
|
||||
@@ -118,6 +124,12 @@ improving ownership.
|
||||
- Benchmark fixtures use ordinary simulation records and must round-trip
|
||||
through the current state schema. They may control workload setup, but must
|
||||
not add benchmark-only fields or branches to production saves and ticks.
|
||||
- Derived population indexes are disposable acceleration structures. NPC
|
||||
records remain authoritative, and rebuilding the view after initialization,
|
||||
restore, or a tick must produce the same decisions and checksum.
|
||||
- Camera-local grass, ambient butterflies, water animation, smoke, and foliage
|
||||
motion are presentation only. Grass consumes bounded real actor transforms;
|
||||
it does not write NPC positions or become persistent state.
|
||||
- Opportunity records reference stable NPC, storage, resource, trigger-event,
|
||||
and resolution-event IDs. Their generator may observe authoritative state
|
||||
and history, but it does not mutate the economy or command NPC behavior. The
|
||||
|
||||
@@ -412,6 +412,13 @@ coverage validates the texture contract, presentation nodes, terrain shape,
|
||||
stable resources, and navigation. Clip capture and a visual performance review
|
||||
remain presentation checkpoints rather than missing implementation.
|
||||
|
||||
`Jajce Center 02` adds the first reviewed composition pass beyond that baseline:
|
||||
the summit citadel and residential roof amphitheater read as one center, the
|
||||
waterfall feeds a Terrain3D-sampled turquoise ribbon, and bounded camera-local
|
||||
grass, conifers, richer deciduous silhouettes, and oversized butterflies add
|
||||
life without changing simulation authority. The matched review lives in
|
||||
[`baselines/JAJCE_CENTER_02.md`](baselines/JAJCE_CENTER_02.md).
|
||||
|
||||
### Exit condition
|
||||
|
||||
The lookdev scene can produce a compelling 10–20 second clip with no gameplay.
|
||||
@@ -755,14 +762,25 @@ Completed:
|
||||
NPC and event counts. The first reviewed capture sets a local 600-NPC target,
|
||||
records identical checksums across samples, and exposes repeated scarce-food
|
||||
population scans plus objective-event growth as the next measured work.
|
||||
33. Shared population query view: stable-ID all/living/starving indexes are
|
||||
built once per deterministic tick and refreshed through interleaved NPC
|
||||
updates. Baseline 02 preserves every checksum and improves the 600-NPC case
|
||||
by 23.3% without simulation LOD.
|
||||
34. `Jajce Center 02`: the reviewed matched capture strengthens the citadel,
|
||||
cascading homes, bridge, waterfall, terrain-following river, conifer/tree
|
||||
clusters, and ambient butterflies. Roughly 10,400 camera-local grass
|
||||
particles remain short and patchy and bend from real actor transforms.
|
||||
|
||||
Next:
|
||||
|
||||
1. Reuse one stable per-tick population view in the existing
|
||||
trusted-starving-subject query, preserve exact action/checksum behavior, and
|
||||
rerun simulation scaling baseline 01. Let the resulting profile determine
|
||||
whether the following slice needs another bounded query optimization, a
|
||||
spatial index, or the first active/abstract LOD contract.
|
||||
1. Measure and implement a loaded-resource spatial query through
|
||||
`ActiveWorldAdapter` for far-apart Terrain3D-authored finite sources. Keep
|
||||
simulation-owned resource records, stable IDs, current score/reachability,
|
||||
reservations, player parity, and unload/rebind behavior unchanged.
|
||||
2. Sculpt a real Terrain3D river channel and waterfall shelf before adding a
|
||||
broad foliage asset pass. Use Terrain3D particles for nearby grass and
|
||||
intentional instancing for tree/fern/rock masses; add livestock only with a
|
||||
real identity and interaction contract.
|
||||
|
||||
Do not start with GIS data, a full city, a large asset pack, or more NPC
|
||||
mechanics. The next proof is a beautiful stage for the systems that already
|
||||
|
||||
@@ -469,8 +469,10 @@ important history.
|
||||
|
||||
[Simulation scaling baseline 01](benchmarks/SIMULATION_SCALING_BASELINE_01.md)
|
||||
sets the first local target at 600 data-only full-fidelity NPCs while preserving
|
||||
deterministic state. Revisit that target when world presentation or LOD enters
|
||||
the measured workload.
|
||||
deterministic state. [Baseline 02](benchmarks/SIMULATION_SCALING_BASELINE_02.md)
|
||||
keeps identical final checksums after one reusable per-tick population view and
|
||||
improves that case from 65.84 to 85.81 ticks per second. Revisit the target when
|
||||
world presentation or LOD enters the measured workload.
|
||||
|
||||
## Milestone 9 — Player interaction and social agency
|
||||
|
||||
@@ -862,15 +864,35 @@ workload exclusions, checksums, and local 50-ticks-per-second reference target
|
||||
live in
|
||||
[Simulation scaling baseline 01](benchmarks/SIMULATION_SCALING_BASELINE_01.md).
|
||||
|
||||
The immediate next slice should build one bounded per-tick population view for
|
||||
the existing trusted-starving-subject query. That scarce-food path currently
|
||||
rebuilds an all-NPC lookup for each applicable idle decision, making it the
|
||||
first code-level candidate consistent with the measured superlinear growth.
|
||||
Preserve exact selection/checksum behavior and rerun the same ledger before
|
||||
choosing a spatial index or active/abstract LOD design.
|
||||
The first measured optimization is also complete. `SimulationPopulationView`
|
||||
builds stable-ID all/living/starving indexes once per tick, refreshes them after
|
||||
each interleaved NPC update, and serves relationship/action queries without
|
||||
entering saved state. Every baseline-02 checksum exactly matches baseline 01.
|
||||
The 600-NPC case falls from 15,187.74 to 11,654.10 microseconds per tick, a
|
||||
23.3% improvement, while small fixtures expose the expected fixed dictionary
|
||||
cost. See
|
||||
[Simulation scaling baseline 02](benchmarks/SIMULATION_SCALING_BASELINE_02.md).
|
||||
|
||||
The immediate next systems slice should now prove spatial discovery for the
|
||||
world vision: index loaded finite `ResourceNode` anchors through
|
||||
`ActiveWorldAdapter` so Terrain3D-authored sources can be placed much farther
|
||||
apart without an all-node scan for every resolution. Preserve stable IDs,
|
||||
current distance/risk/comfort/priority scoring, reachability, reservations,
|
||||
player parity, unload/rebind authority, and exact deterministic outcomes. First
|
||||
measure 18, 180, and 1,800 loaded candidates; do not choose a generic spatial
|
||||
framework or active/abstract LOD mode before that real consumer proves the
|
||||
contract.
|
||||
|
||||
Recently completed:
|
||||
|
||||
- Shared population query view: one transient per-tick all/living/starving
|
||||
index replaces repeated scarce-food relationship scans, preserves exact
|
||||
checksums, and improves the reviewed 600-NPC workload by 23.3%.
|
||||
- `Jajce Center 02`: matched before/after captures now document a larger summit
|
||||
citadel, cascading timber-plaster homes, terrain-following turquoise river,
|
||||
rock-framed waterfall, richer tree silhouettes, restrained conifer clusters,
|
||||
oversized butterflies, and short camera-local grass that bends around real
|
||||
player/NPC transforms without becoming simulation state.
|
||||
- Deterministic simulation scaling baseline: schema-valid full-fidelity
|
||||
fixtures, a repeatable CLI runner, and a fast headless regression now record
|
||||
population throughput, phase timing, serialized-state growth, history
|
||||
|
||||
+14
-5
@@ -2,7 +2,7 @@
|
||||
|
||||
> Agent-facing context for understanding the project quickly.
|
||||
>
|
||||
> Snapshot basis: repository state on July 14, 2026. Treat the code as the
|
||||
> Snapshot basis: repository state on July 16, 2026. Treat the code as the
|
||||
> source of truth when this document and the implementation differ.
|
||||
|
||||
See the [documentation map](README.md) for the authority and scope of each plan.
|
||||
@@ -193,12 +193,15 @@ study, and patrol target typed activity sites. The migration is documented in
|
||||
and GUT run through the local quality gate)
|
||||
- **Main scene:** `res://main.tscn`
|
||||
- **Terrain:** Terrain3D 1.0.2 is installed and enabled
|
||||
- **Jajce runtime:** reusable 512 m Terrain3D seed, greybox landmarks, stable
|
||||
ResourceNode placement, lookdev camera, and Terrain3D-derived runtime
|
||||
navigation with collision guardrails
|
||||
- **Jajce runtime:** reusable 512 m Terrain3D seed, stable ResourceNode
|
||||
placement, larger citadel/village-center composition, terrain-following river
|
||||
ribbon, reactive camera-local grass, and Terrain3D-derived runtime navigation
|
||||
with collision guardrails
|
||||
- **Lookdev baseline:** `Jajce Lookdev 01` is captured at
|
||||
`docs/baselines/jajce_lookdev_01.png` with
|
||||
`tools/capture_jajce_lookdev.gd`
|
||||
- **Matched center baseline:** `Jajce Center 02` preserves a fixed before/after
|
||||
overview plus a close grass-readability capture under `docs/baselines/`
|
||||
- **Runtime baseline:** `Simulation Garden 01` is captured as paired debug and
|
||||
cinematic `main.tscn` images plus an empty/restocked pantry close pair under
|
||||
`docs/baselines/` with `tools/capture_simulation_garden.gd`
|
||||
@@ -336,13 +339,16 @@ distance with resource `safety_risk`, `comfort_distance`, and
|
||||
|
||||
- a centered 512 m Terrain3D landscape split across four regions;
|
||||
- deterministic shaped terrain data and six game-owned surface layers;
|
||||
- fortress, houses, mill, bridge, shader river/waterfall, mist, and smoke;
|
||||
- a larger summit citadel, eight cascading houses, mill, full-span bridge,
|
||||
rock-framed waterfall, mist, smoke, and a Terrain3D-sampled river ribbon;
|
||||
- authored dirt path strips that make the current village task loop readable;
|
||||
- compact silhouette props for the ridge landmark, guard, study, and rest
|
||||
sites, plus a physical pantry whose empty/low/stocked arrangement follows
|
||||
authoritative food storage;
|
||||
- warm sky, fog, shadows, coherent wind-reactive foliage, and breeze-aligned
|
||||
chimney smoke;
|
||||
- short patchy camera-local grass that receives at most eight real player/NPC
|
||||
transforms, authored conifer clusters, and oversized ambient butterflies;
|
||||
- one sparse terrain-aware calligraphic gust field with two or three soft
|
||||
tapered strokes per burst;
|
||||
- eighteen ResourceNodes preserving stable food/wood discovery IDs;
|
||||
@@ -418,6 +424,8 @@ views, modifiers, and priorities.
|
||||
Focused `RefCounted` collaborators keep rule ownership visible:
|
||||
|
||||
- action systems own selection, execution progress, and target resolution;
|
||||
- `SimulationPopulationView` owns the disposable all/living/starving stable-ID
|
||||
query indexes rebuilt at deterministic tick boundaries;
|
||||
- `VillageEconomy` owns storage/inventory transactions and synchronized
|
||||
village resource views;
|
||||
- `SimulationEventLog` owns deterministic event history and queries;
|
||||
@@ -545,6 +553,7 @@ NpcVisual navigates through the active world
|
||||
│ ├── events/ Ordered event history and queries
|
||||
│ ├── knowledge/ Per-NPC facts, provenance, transfer, and retention
|
||||
│ ├── opportunities/ Knowledge-gated unresolved-condition projection
|
||||
│ ├── population/ Transient per-tick NPC query indexes
|
||||
│ ├── persistence/ Validated local save-slot storage
|
||||
│ ├── relationships/ Directed social consequences and queries
|
||||
│ └── state/ Versioned simulation-state records
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Jajce Center 02
|
||||
|
||||
Captured: 2026-07-16
|
||||
|
||||
## Files
|
||||
|
||||
- [before overview](jajce_center_02_before.png)
|
||||
- [after overview](jajce_center_02_after.png)
|
||||
- [close gameplay grass check](jajce_grass_gameplay_02.png)
|
||||
- `tools/capture_jajce_lookdev.gd`
|
||||
|
||||
The two overview images use the same 1280×720 camera, fixed daylight value,
|
||||
warmup, and Metal renderer. The gameplay image is a separate readability check,
|
||||
not part of the matched comparison.
|
||||
|
||||
## Reference reading
|
||||
|
||||
The reduction uses Jajce's hierarchy rather than attempting a literal city
|
||||
reconstruction:
|
||||
|
||||
- the [UNESCO tentative-list description](https://whc.unesco.org/en/tentativelists/2098/)
|
||||
describes the citadel at the summit of a rocky pyramid, ramparts descending
|
||||
its slopes, and residential roofs forming an amphitheater beneath it;
|
||||
- the [official Jajce fortress overview](https://www.agencija-jajce.ba/en/the-fortress/)
|
||||
supports treating the fortress as the primary long-distance landmark;
|
||||
- official Studio Ghibli still collections for
|
||||
[My Neighbor Totoro](https://www.ghibli.jp/works/totoro/) and
|
||||
[Princess Mononoke](https://www.ghibli.jp/works/mononoke/) were used only as
|
||||
broad references for layered green masses, clear silhouettes, restrained
|
||||
natural motion, and small warm human details inside large landscapes;
|
||||
- [Tiny Glade](https://store.steampowered.com/app/2198150/Tiny_Glade/) supports
|
||||
the low-detail diorama reading of whimsical castles, cottages, ruins, and
|
||||
incidental life;
|
||||
- the [Terrain3D instancer guidance](https://terrain3d.readthedocs.io/en/latest/docs/instancer.html)
|
||||
informed the split between camera-local particle grass and intentional tree
|
||||
placement.
|
||||
|
||||
No building, frame, or asset is copied from those references. The project keeps
|
||||
its own compact low-poly language and real simulation constraints.
|
||||
|
||||
## Implemented reduction
|
||||
|
||||
- The ridge block becomes a larger warm-stone citadel with a rocky plinth,
|
||||
ramparts, descending walls, towers, gate, crenellations, banner, and narrow
|
||||
window slits.
|
||||
- Eight timber-accented plaster houses now cascade below the citadel instead of
|
||||
leaving three isolated boxes in an empty field.
|
||||
- The waterfall is wider, softly animated, misted, and framed by irregular
|
||||
mossy rock columns. Its plunge pool feeds a turquoise ribbon that samples the
|
||||
live Terrain3D height field instead of disappearing beneath it.
|
||||
- The bridge gains a full river span, stone abutments, center pier, rails, and
|
||||
posts. House windows and chimney smoke add small warm points.
|
||||
- Existing rounded deciduous trees are richer and wind-reactive; authored
|
||||
conifer clusters strengthen the citadel, falls, and valley edges.
|
||||
- Three intentionally oversized butterflies add quiet ambient life.
|
||||
- A bounded camera-local Terrain3D grass field uses roughly 10,400 particles.
|
||||
Its project-owned process material keeps blades short and patchy, while a
|
||||
shader receives at most eight real player/NPC transforms and bends grass
|
||||
away from their feet.
|
||||
- Haze and saturation are reduced so green terrain, warm roofs, stone, and
|
||||
turquoise water separate more clearly.
|
||||
|
||||
## Authority boundary
|
||||
|
||||
Grass, butterflies, smoke, water motion, and foliage sway are transient
|
||||
presentation. They do not enter saves or checksums. Player and `NpcVisual`
|
||||
nodes merely expose real transforms to the grass shader.
|
||||
|
||||
Finite food and wood still come only from stable-ID `ResourceNode` anchors
|
||||
bound to simulation-owned records. The conifers and grass are decorative; the
|
||||
next resource-discovery phase can pair selected Terrain3D-authored visuals with
|
||||
intentional resource anchors without turning every blade or background tree
|
||||
into authoritative state. Livestock is deliberately deferred until one animal
|
||||
has a real identity/state/interaction contract; ambient butterflies do not
|
||||
pretend to be simulated food or work.
|
||||
|
||||
## Remaining visual work
|
||||
|
||||
This is a stronger prototype composition, not final environment art. The next
|
||||
art pass should sculpt a true river channel and waterfall shelf into Terrain3D,
|
||||
replace procedural fortress/house geometry gradually, add bank stones and
|
||||
flower/fern clusters through bounded instancing, and validate a representative
|
||||
frame-time budget. The matched comparison should remain the checkpoint before
|
||||
those asset-heavy changes.
|
||||
|
||||
## Capture command
|
||||
|
||||
```bash
|
||||
HOME="$PWD/logs/quality/godot_profile" \
|
||||
APPDATA="$PWD/logs/quality/godot_profile" \
|
||||
LOCALAPPDATA="$PWD/logs/quality/godot_profile" \
|
||||
/Applications/Godot.app/Contents/MacOS/Godot \
|
||||
--path "$PWD" --script res://tools/capture_jajce_lookdev.gd -- \
|
||||
--output=res://docs/baselines/jajce_center_02_after.png
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 729 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dcg13u5wqr23m"
|
||||
path="res://.godot/imported/jajce_center_02_after.png-be0a331c22c145d626fa09f682c7e379.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://docs/baselines/jajce_center_02_after.png"
|
||||
dest_files=["res://.godot/imported/jajce_center_02_after.png-be0a331c22c145d626fa09f682c7e379.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 704 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dipu1epmps356"
|
||||
path="res://.godot/imported/jajce_center_02_before.png-a6dad6237a960278853b124b5c1180c2.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://docs/baselines/jajce_center_02_before.png"
|
||||
dest_files=["res://.godot/imported/jajce_center_02_before.png-a6dad6237a960278853b124b5c1180c2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 553 KiB |
@@ -0,0 +1,40 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://du1awnpktb7q0"
|
||||
path="res://.godot/imported/jajce_grass_gameplay_02.png-4b072b6e4f1c146d62492370903db793.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://docs/baselines/jajce_grass_gameplay_02.png"
|
||||
dest_files=["res://.godot/imported/jajce_grass_gameplay_02.png-4b072b6e4f1c146d62492370903db793.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
@@ -21,3 +21,6 @@ Reviewed captures:
|
||||
- [Simulation scaling baseline 01](SIMULATION_SCALING_BASELINE_01.md) records
|
||||
the first full-fidelity population/history measurements and the bounded next
|
||||
optimization.
|
||||
- [Simulation scaling baseline 02](SIMULATION_SCALING_BASELINE_02.md) records
|
||||
checksum-identical results after the shared per-tick population view: the
|
||||
600-NPC case is 23.3% faster, while small fixtures expose its fixed cost.
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Simulation scaling baseline 02
|
||||
|
||||
This is the reviewed comparison after introducing one reusable per-tick
|
||||
`SimulationPopulationView`. The view indexes all, living, and starving NPCs by
|
||||
stable ID, is refreshed as each NPC advances, and is shared by relationship and
|
||||
action-selection queries. It is transient derived state and is not serialized.
|
||||
|
||||
The machine-readable samples are in
|
||||
[`simulation_scaling_baseline_02.json`](simulation_scaling_baseline_02.json).
|
||||
The workload, seed, host, exclusions, warmup, and three-sample median contract
|
||||
match [baseline 01](SIMULATION_SCALING_BASELINE_01.md).
|
||||
|
||||
## Results
|
||||
|
||||
| Case | NPCs | Seeded history | Median us/tick | Ticks/s | Simulated realtime | Arrival share | End JSON |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| Population 6 | 6 | 0 | 63.85 | 15,662.93 | 18,795.5x | 4.5% | 0.06 MiB |
|
||||
| Population 60 | 60 | 0 | 662.62 | 1,509.17 | 1,811.0x | 5.7% | 0.56 MiB |
|
||||
| Population 600 | 600 | 0 | 11,654.10 | 85.81 | 103.0x | 18.0% | 5.67 MiB |
|
||||
| History 600 | 60 | 600 | 722.60 | 1,383.90 | 1,660.7x | 5.2% | 0.71 MiB |
|
||||
| History 6,000 | 60 | 6,000 | 1,430.65 | 698.98 | 838.8x | 2.6% | 2.04 MiB |
|
||||
|
||||
All samples within each case were deterministic. More importantly, every final
|
||||
checksum exactly matches the corresponding baseline-01 checksum, so the
|
||||
optimization preserves selection, interleaved NPC updates, event growth, and
|
||||
serialized continuation state.
|
||||
|
||||
## Comparison
|
||||
|
||||
| Case | Baseline 01 us/tick | Baseline 02 us/tick | Change |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Population 6 | 55.74 | 63.85 | 14.6% slower |
|
||||
| Population 60 | 621.87 | 662.62 | 6.6% slower |
|
||||
| Population 600 | 15,187.74 | 11,654.10 | **23.3% faster** |
|
||||
| History 600 | 677.12 | 722.60 | 6.7% slower |
|
||||
| History 6,000 | 1,421.19 | 1,430.65 | 0.7% slower |
|
||||
|
||||
The shared dictionaries add a small fixed per-tick cost, visible in tiny
|
||||
fixtures and normal local timing noise. At the measured population knee they
|
||||
remove repeated all-NPC lookup construction and improve the 600-NPC case from
|
||||
65.84 to 85.81 ticks per second. The local 50-ticks-per-second reference target
|
||||
remains comfortably met without introducing simulation LOD.
|
||||
|
||||
## Decision and next measurement
|
||||
|
||||
Keep the population view. It has a real relationship/action consumer, improves
|
||||
the measured pressure point, and preserves exact state. Do not add batching or
|
||||
active/abstract NPC modes merely to improve this data-only number.
|
||||
|
||||
The next bounded scale slice should support the world vision directly: a
|
||||
loaded-resource spatial query owned by `ActiveWorldAdapter`. It should let
|
||||
villagers discover finite, stable-ID `ResourceNode` anchors placed much farther
|
||||
apart across Terrain3D while preserving the current score, reachability,
|
||||
reservation, and save/rebind contracts. A benchmark should compare 18, 180,
|
||||
and 1,800 loaded candidates before choosing a grid, tree, or other index.
|
||||
|
||||
Terrain3D-instanced visual trees and grass are not automatically authoritative
|
||||
resources. Intentional harvest anchors must continue to bind simulation-owned
|
||||
`ResourceStateRecord` data by stable ID; large decorative populations can stay
|
||||
presentation-only until a gameplay rule needs them.
|
||||
|
||||
## Capture command
|
||||
|
||||
```bash
|
||||
/Applications/Godot.app/Contents/MacOS/Godot \
|
||||
--headless --path "$PWD" \
|
||||
--script res://tools/benchmark_simulation_scaling.gd -- \
|
||||
--host-label=Apple_M1_Max_64_GB \
|
||||
--output=res://docs/benchmarks/simulation_scaling_baseline_02.json
|
||||
```
|
||||
@@ -0,0 +1,308 @@
|
||||
{
|
||||
"benchmark_seed": 8088,
|
||||
"captured_utc": "2026-07-16T21:38:42Z",
|
||||
"cases": [
|
||||
{
|
||||
"arrival_share_percent": 4.47176756206437,
|
||||
"arrival_usec_median": 571,
|
||||
"arrival_usec_samples": [
|
||||
563,
|
||||
571,
|
||||
579
|
||||
],
|
||||
"arrivals_processed": 209,
|
||||
"case_id": "population_006",
|
||||
"elapsed_usec_max": 12771,
|
||||
"elapsed_usec_median": 12769,
|
||||
"elapsed_usec_min": 12507,
|
||||
"elapsed_usec_samples": [
|
||||
12507,
|
||||
12769,
|
||||
12771
|
||||
],
|
||||
"end_event_count": 221,
|
||||
"end_known_reference_count": 0,
|
||||
"end_state_bytes": 58576,
|
||||
"end_tick": 210,
|
||||
"events_recorded": 209,
|
||||
"final_checksum": "100dd5dc1e0c22ea23f35dd876d6f56fd5c33c58b1586b6fa9aec47ee1536511",
|
||||
"history_seed_events": 0,
|
||||
"measured_ticks": 200,
|
||||
"npc_updates": 1200,
|
||||
"population": 6,
|
||||
"realtime_factor_median": 18795.5204009711,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"seed": 8088,
|
||||
"setup_usec_median": 627,
|
||||
"setup_usec_samples": [
|
||||
546,
|
||||
627,
|
||||
6728
|
||||
],
|
||||
"simulation_usec_median": 12173,
|
||||
"simulation_usec_samples": [
|
||||
11916,
|
||||
12173,
|
||||
12186
|
||||
],
|
||||
"start_event_count": 12,
|
||||
"start_known_reference_count": 0,
|
||||
"start_state_bytes": 7632,
|
||||
"start_tick": 10,
|
||||
"state_growth_bytes": 50944,
|
||||
"tick_interval": 1.2,
|
||||
"ticks_per_second_median": 15662.9336674759,
|
||||
"usec_per_tick_median": 63.845,
|
||||
"warmup_arrivals": 12,
|
||||
"warmup_ticks": 10,
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
},
|
||||
{
|
||||
"arrival_share_percent": 5.66845000490481,
|
||||
"arrival_usec_median": 7512,
|
||||
"arrival_usec_samples": [
|
||||
7458,
|
||||
7512,
|
||||
7571
|
||||
],
|
||||
"arrivals_processed": 2090,
|
||||
"case_id": "population_060",
|
||||
"elapsed_usec_max": 133541,
|
||||
"elapsed_usec_median": 132523,
|
||||
"elapsed_usec_min": 130182,
|
||||
"elapsed_usec_samples": [
|
||||
130182,
|
||||
132523,
|
||||
133541
|
||||
],
|
||||
"end_event_count": 2210,
|
||||
"end_known_reference_count": 0,
|
||||
"end_state_bytes": 587324,
|
||||
"end_tick": 210,
|
||||
"events_recorded": 2090,
|
||||
"final_checksum": "58c7f96fc5df7f5399fd82d1a9c212b3c53067e807059e88ffa484ba0eb41e16",
|
||||
"history_seed_events": 0,
|
||||
"measured_ticks": 200,
|
||||
"npc_updates": 12000,
|
||||
"population": 60,
|
||||
"realtime_factor_median": 1811.00639134339,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"seed": 8088,
|
||||
"setup_usec_median": 3788,
|
||||
"setup_usec_samples": [
|
||||
3481,
|
||||
3788,
|
||||
3917
|
||||
],
|
||||
"simulation_usec_median": 125039,
|
||||
"simulation_usec_samples": [
|
||||
122647,
|
||||
125039,
|
||||
125926
|
||||
],
|
||||
"start_event_count": 120,
|
||||
"start_known_reference_count": 0,
|
||||
"start_state_bytes": 71278,
|
||||
"start_tick": 10,
|
||||
"state_growth_bytes": 516046,
|
||||
"tick_interval": 1.2,
|
||||
"ticks_per_second_median": 1509.17199278616,
|
||||
"usec_per_tick_median": 662.615,
|
||||
"warmup_arrivals": 120,
|
||||
"warmup_ticks": 10,
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
},
|
||||
{
|
||||
"arrival_share_percent": 17.9571567087978,
|
||||
"arrival_usec_median": 418549,
|
||||
"arrival_usec_samples": [
|
||||
417929,
|
||||
418549,
|
||||
425849
|
||||
],
|
||||
"arrivals_processed": 20950,
|
||||
"case_id": "population_600",
|
||||
"elapsed_usec_max": 2346707,
|
||||
"elapsed_usec_median": 2330820,
|
||||
"elapsed_usec_min": 2326981,
|
||||
"elapsed_usec_samples": [
|
||||
2326981,
|
||||
2330820,
|
||||
2346707
|
||||
],
|
||||
"end_event_count": 22150,
|
||||
"end_known_reference_count": 0,
|
||||
"end_state_bytes": 5948447,
|
||||
"end_tick": 210,
|
||||
"events_recorded": 20950,
|
||||
"final_checksum": "fa4efac63fde63f92eb28141bebd1922f665e85ffe7b7ac2b9702dae6e4c24b7",
|
||||
"history_seed_events": 0,
|
||||
"measured_ticks": 200,
|
||||
"npc_updates": 120000,
|
||||
"population": 600,
|
||||
"realtime_factor_median": 102.968054161196,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"seed": 8088,
|
||||
"setup_usec_median": 34966,
|
||||
"setup_usec_samples": [
|
||||
34505,
|
||||
34966,
|
||||
34980
|
||||
],
|
||||
"simulation_usec_median": 1912210,
|
||||
"simulation_usec_samples": [
|
||||
1908995,
|
||||
1912210,
|
||||
1920790
|
||||
],
|
||||
"start_event_count": 1200,
|
||||
"start_known_reference_count": 0,
|
||||
"start_state_bytes": 713361,
|
||||
"start_tick": 10,
|
||||
"state_growth_bytes": 5235086,
|
||||
"tick_interval": 1.2,
|
||||
"ticks_per_second_median": 85.8067118009971,
|
||||
"usec_per_tick_median": 11654.1,
|
||||
"warmup_arrivals": 1200,
|
||||
"warmup_ticks": 10,
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
},
|
||||
{
|
||||
"arrival_share_percent": 5.24844484116275,
|
||||
"arrival_usec_median": 7585,
|
||||
"arrival_usec_samples": [
|
||||
7540,
|
||||
7585,
|
||||
7604
|
||||
],
|
||||
"arrivals_processed": 2090,
|
||||
"case_id": "history_000600",
|
||||
"elapsed_usec_max": 145539,
|
||||
"elapsed_usec_median": 144519,
|
||||
"elapsed_usec_min": 144335,
|
||||
"elapsed_usec_samples": [
|
||||
144335,
|
||||
144519,
|
||||
145539
|
||||
],
|
||||
"end_event_count": 2810,
|
||||
"end_known_reference_count": 0,
|
||||
"end_state_bytes": 743004,
|
||||
"end_tick": 810,
|
||||
"events_recorded": 2090,
|
||||
"final_checksum": "7ae86044f7683359d6bfbc8753e2656f3af90f23d040039154c6869dca48cf65",
|
||||
"history_seed_events": 600,
|
||||
"measured_ticks": 200,
|
||||
"npc_updates": 12000,
|
||||
"population": 60,
|
||||
"realtime_factor_median": 1660.68129450107,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"seed": 8088,
|
||||
"setup_usec_median": 22723,
|
||||
"setup_usec_samples": [
|
||||
21887,
|
||||
22723,
|
||||
23413
|
||||
],
|
||||
"simulation_usec_median": 136897,
|
||||
"simulation_usec_samples": [
|
||||
136707,
|
||||
136897,
|
||||
137966
|
||||
],
|
||||
"start_event_count": 720,
|
||||
"start_known_reference_count": 180,
|
||||
"start_state_bytes": 253098,
|
||||
"start_tick": 610,
|
||||
"state_growth_bytes": 489906,
|
||||
"tick_interval": 1.2,
|
||||
"ticks_per_second_median": 1383.90107875089,
|
||||
"usec_per_tick_median": 722.595,
|
||||
"warmup_arrivals": 120,
|
||||
"warmup_ticks": 10,
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
},
|
||||
{
|
||||
"arrival_share_percent": 2.64320413797924,
|
||||
"arrival_usec_median": 7563,
|
||||
"arrival_usec_samples": [
|
||||
7552,
|
||||
7563,
|
||||
7628
|
||||
],
|
||||
"arrivals_processed": 2090,
|
||||
"case_id": "history_006000",
|
||||
"elapsed_usec_max": 291609,
|
||||
"elapsed_usec_median": 286130,
|
||||
"elapsed_usec_min": 285483,
|
||||
"elapsed_usec_samples": [
|
||||
285483,
|
||||
286130,
|
||||
291609
|
||||
],
|
||||
"end_event_count": 8210,
|
||||
"end_known_reference_count": 0,
|
||||
"end_state_bytes": 2141615,
|
||||
"end_tick": 6210,
|
||||
"events_recorded": 2090,
|
||||
"final_checksum": "b4df2bac21ed5ce6dcd92736e210db5f3114d959dcb15ae7796493362911b6d7",
|
||||
"history_seed_events": 6000,
|
||||
"measured_ticks": 200,
|
||||
"npc_updates": 12000,
|
||||
"population": 60,
|
||||
"realtime_factor_median": 838.779575717331,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"seed": 8088,
|
||||
"setup_usec_median": 153306,
|
||||
"setup_usec_samples": [
|
||||
153290,
|
||||
153306,
|
||||
154064
|
||||
],
|
||||
"simulation_usec_median": 278467,
|
||||
"simulation_usec_samples": [
|
||||
277892,
|
||||
278467,
|
||||
284022
|
||||
],
|
||||
"start_event_count": 6120,
|
||||
"start_known_reference_count": 180,
|
||||
"start_state_bytes": 1649700,
|
||||
"start_tick": 6010,
|
||||
"state_growth_bytes": 491915,
|
||||
"tick_interval": 1.2,
|
||||
"ticks_per_second_median": 698.982979764443,
|
||||
"usec_per_tick_median": 1430.65,
|
||||
"warmup_arrivals": 120,
|
||||
"warmup_ticks": 10,
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
}
|
||||
],
|
||||
"engine_version": "4.7-stable (official)",
|
||||
"exclusions": [
|
||||
"manager_and_fixture_setup",
|
||||
"state_serialization",
|
||||
"world_scene",
|
||||
"rendering",
|
||||
"navigation",
|
||||
"npc_visual"
|
||||
],
|
||||
"host_label": "Apple_M1_Max_64_GB",
|
||||
"measured_ticks": 200,
|
||||
"platform": "macOS",
|
||||
"processor_count": 10,
|
||||
"sample_count": 3,
|
||||
"schema_version": 1,
|
||||
"timed_phases": [
|
||||
"simulation_tick",
|
||||
"headless_arrival_completion"
|
||||
],
|
||||
"warmup_ticks": 10,
|
||||
"workload": "All NPCs receive full per-tick needs/task updates and ordinary action decisions; travel resolves through the deterministic immediate-arrival headless convention.",
|
||||
"workload_id": "full_fidelity_headless_arrival"
|
||||
}
|
||||
Reference in New Issue
Block a user