feat(simulation): T-1152 server half — WindowGranularity enum, derive_orbital_at_metres, Region rung on the same carrier
R1 measured first: a capped Region tile through the real production path (build_district_window_layer + T-1151 par_iter) at the 64x64 wire cap costs 0.40-0.48ms — faster than the shipped district n=64 window, so Jeroen's progressive capped-density tiling ruling is comfortably interactive on-demand. Raw orbital derive ~0.9µs/cell (~2.3x faster than full derive; region_baseline dominates, not invent_primitives). R5 redesign: WindowGranularity enum (Quarter/District/Region), serde named-variant per the RoadNodeKind precedent, spacing from D-243 scale:: constants — the single source of truth. Additive serde-default window_granularity_v2 request field (None = legacy u32 path; v2 wins when Some); DistrictWindowLayer.granularity_v2 always echoed. Legacy u32 echo for Region uses reserved WINDOW_GRANULARITY_REGION_KEY = u32::MAX (never a legal input) so the old slot cannot lie about aliasing. Cache and coalescing keys carry the enum itself (Ord by declaration order, D-010). n stays district-extent at every rung; Region's cell grid is a DIVISION (round(n/100), min 1) with its own per-axis ceiling DISTRICT_WINDOW_MAX_N_REGION=6400 and a bounded halving-loop clamp (no closed form under the rounding division — the client mirror must replicate the loop). derive_orbital_at_metres: bilinear envelope reads + region_baseline temperature, NO invent_primitives (proven by test — slope_q pinned 0), routed through the shared build_district_profile classification tail so the existing colorizer family renders orbital cells unchanged. R2 stepped-categorical behavior documented at the function, not implied. Region aliasing + clamp/echo tests mirror the T-1150 discipline. 1813 lib tests green; clippy clean; fixture regenerated (254->278 bytes, new echoed field).
This commit is contained in:
Binary file not shown.
@@ -1569,6 +1569,128 @@ pub fn derive_at_metres(
|
||||
)
|
||||
}
|
||||
|
||||
/// The orbital-rung derivation (T-1152, zoom ladder design doc §2/§4): the
|
||||
/// coarse-granularity twin of [`derive_at_metres`] that skips [`invent_primitives`]
|
||||
/// entirely — **no coastline warp, no detail-scatter octave sum, no classification
|
||||
/// noise call of any kind**. Per the design doc's orbital row: "`region_baseline_at_district`
|
||||
/// only — bilinear blend of 4 region baselines, no `invent_primitives`, no
|
||||
/// classification [driver]." Orbital sample spacing (≥205 km, D-243's region rung
|
||||
/// and coarser) sits below `detail_scatter`'s own octave floor
|
||||
/// (`OCTAVE_WAVELENGTHS_M`'s coarsest entry is 32,768 m ≈ 32.8 km — an order of
|
||||
/// magnitude finer than a region), so the invented terrain has nothing left to
|
||||
/// contribute at this spacing; calling it would burn cycles synthesizing detail
|
||||
/// no orbital pixel can resolve. What DOES vary at orbital spacing is the
|
||||
/// **envelope** the heightmap itself carries (the `TerrainAnalysis` continental
|
||||
/// shape) and the **region climate baseline** — this function samples exactly
|
||||
/// those two, nothing else.
|
||||
///
|
||||
/// **Cost model (design doc R1 — measure first):** one `bilinear` (elevation),
|
||||
/// one `bilinear_bool` (ocean mask), one `region_baseline_at_district` call (its
|
||||
/// own cost is 4×`derive_region_baseline_c` on a cache miss, O(1) on a cache hit)
|
||||
/// — no octave sum, no coast-warp trig, no character/envelope computation. See
|
||||
/// `server/tests/zoom_ladder_bench.rs`'s `bench_derive_orbital_at_metres` for the
|
||||
/// measured per-cell figure this claim rests on.
|
||||
///
|
||||
/// Produces the SAME six-field tail every other rung produces (`morphology_zone`,
|
||||
/// `elev_q`, `temperature_c`, `moisture_q`, `vegetation_class`, `glaciation_grade`)
|
||||
/// by routing the bilinear-only primitives through the same
|
||||
/// [`build_district_profile`] classification tail every other rung uses — one
|
||||
/// classification pipeline, never a second orbital-only decision tree (D-227:
|
||||
/// classification thresholds don't get a coarse-rung variant any more than the
|
||||
/// quarter rung got its own "quarter mode" thresholds, design doc §6).
|
||||
///
|
||||
/// **R2 (stepped fields):** `moisture_q`/`temperature_c`/`morphology_zone`/etc.
|
||||
/// are exactly as stepped here as at every other rung — `region_baseline_at_district`
|
||||
/// floor-divides to the containing `DistrictPos` regardless of caller spacing (see
|
||||
/// [`derive_at_metres`]'s own doc on this), so this function does not make
|
||||
/// temperature MORE continuous at orbital scale; it inherits the same
|
||||
/// district-tier step the design doc documents as permanent, by construction.
|
||||
///
|
||||
/// **`slope_q` is fixed at 0`** — the bilinear-only envelope carries no
|
||||
/// per-cell slope signal at orbital spacing (`ta.slope_deg` is a district-scale
|
||||
/// proxy; sampling it here would imply a precision the coarse envelope doesn't
|
||||
/// have). `slope_q` only affects morphology gates 3–6 (FjordWall/CliffCoast/
|
||||
/// BraidedDelta/DuneStrand) and the invented-primitives `carve` term this
|
||||
/// function never runs — passing 0 means those gates fall through to their
|
||||
/// low-slope alternatives, which is the correct behavior for a coastline sampled
|
||||
/// at coarser-than-detail-scatter resolution (no invented ruggedness to report).
|
||||
pub fn derive_orbital_at_metres(
|
||||
seed: SeedChain,
|
||||
body_id: &str,
|
||||
body_params: &BodyParams,
|
||||
ta: &TerrainAnalysis,
|
||||
wx: f64,
|
||||
wy: f64,
|
||||
climate: &ClimateConstants,
|
||||
) -> DistrictProfile {
|
||||
// Same world-metres -> fractional heightmap pixel + latitude mapping
|
||||
// derive_at_metres uses — the envelope is the SAME TerrainAnalysis grid at
|
||||
// every rung, only the sampling density differs.
|
||||
let (px, py, _world_x_m, _world_y_m, lat_deg) = match body_params.body_radius_km {
|
||||
Some(r_km) if r_km > 0.0 => {
|
||||
let circumference_m = std::f64::consts::TAU * r_km * 1000.0;
|
||||
let meridian_m = std::f64::consts::PI * r_km * 1000.0;
|
||||
let px = (wx / circumference_m).rem_euclid(1.0) * ta.w as f64;
|
||||
let lat_frac = (wy / meridian_m).clamp(-0.5, 0.5);
|
||||
let py = (0.5 + lat_frac) * ta.h.saturating_sub(1) as f64;
|
||||
(px, py, wx, wy, -lat_frac * 180.0)
|
||||
}
|
||||
_ => {
|
||||
let dm = scale::DISTRICT_M as f64;
|
||||
let px = (wx / dm).clamp(0.0, ta.w.saturating_sub(1) as f64);
|
||||
let py = (wy / dm).clamp(0.0, ta.h.saturating_sub(1) as f64);
|
||||
let lat_deg = if ta.h > 1 {
|
||||
90.0 - (py / (ta.h - 1) as f64) * 180.0
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
(px, py, px * dm, py * dm, lat_deg)
|
||||
}
|
||||
};
|
||||
|
||||
let params = BodyParams {
|
||||
latitude_deg: lat_deg,
|
||||
..body_params.clone()
|
||||
};
|
||||
|
||||
// The envelope only — no coast-warp, no detail-scatter. This is exactly
|
||||
// `invent_primitives`' step-1 "driver tier" raw bilinear reads, promoted to
|
||||
// be the FINAL primitives instead of a one-step-stale input to invention.
|
||||
let elev_q =
|
||||
((bilinear(&ta.elev_pct, ta.w, ta.h, px, py) as f64 * 100.0).round() as i32).clamp(0, 100);
|
||||
let ocean_fraction_q = ((bilinear_bool(&ta.ocean_mask, ta.w, ta.h, px, py) as f64 * 100.0)
|
||||
.round() as i32)
|
||||
.clamp(0, 100);
|
||||
// No invented ruggedness at orbital spacing (see the function doc's note
|
||||
// on slope_q) — the envelope carries no per-cell slope signal this coarse.
|
||||
let slope_q = 0;
|
||||
|
||||
let district_pos: DistrictPos = (
|
||||
(wx / scale::DISTRICT_M as f64).floor() as i32,
|
||||
(wy / scale::DISTRICT_M as f64).floor() as i32,
|
||||
);
|
||||
let region_baseline_c = region_profile::region_baseline_at_district(
|
||||
seed.seed(),
|
||||
body_id,
|
||||
district_pos,
|
||||
¶ms,
|
||||
climate,
|
||||
seed,
|
||||
None, // no pre-built cache; derive on-the-fly, same posture as derive_at_metres
|
||||
);
|
||||
|
||||
build_district_profile(
|
||||
seed,
|
||||
¶ms,
|
||||
climate,
|
||||
slope_q,
|
||||
elev_q,
|
||||
ocean_fraction_q,
|
||||
region_baseline_c,
|
||||
BasinDirection::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Bilinear interpolation of a row-major `f32` field at fractional `(px, py)`.
|
||||
/// Columns wrap (equirectangular); rows clamp at the poles.
|
||||
fn bilinear(field: &[f32], w: usize, h: usize, px: f64, py: f64) -> f32 {
|
||||
@@ -2066,6 +2188,153 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// derive_orbital_at_metres (T-1152, design doc §2/§4 orbital row)
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
/// Determinism (D-010/D-227): two independent orbital derives at the same
|
||||
/// position produce a bit-identical `DistrictProfile`, mirroring
|
||||
/// `derive_district_is_deterministic`'s pattern for the finer rungs.
|
||||
#[test]
|
||||
fn derive_orbital_at_metres_is_deterministic() {
|
||||
let hm = test_hm();
|
||||
let ta = test_ta(&hm);
|
||||
let climate = ClimateConstants::default();
|
||||
let p = earth_params();
|
||||
let dm = scale::REGION_M as f64;
|
||||
|
||||
let a = derive_orbital_at_metres(
|
||||
test_seed(),
|
||||
"test_body",
|
||||
&p,
|
||||
&ta,
|
||||
3.0 * dm,
|
||||
2.0 * dm,
|
||||
&climate,
|
||||
);
|
||||
let b = derive_orbital_at_metres(
|
||||
test_seed(),
|
||||
"test_body",
|
||||
&p,
|
||||
&ta,
|
||||
3.0 * dm,
|
||||
2.0 * dm,
|
||||
&climate,
|
||||
);
|
||||
assert_district_profiles_eq(&a, &b);
|
||||
}
|
||||
|
||||
/// The orbital path must NOT run `invent_primitives` — the design doc's
|
||||
/// central constraint (§2: "no invent_primitives at orbital wavelengths").
|
||||
/// Direct proof: `slope_q` is always exactly 0 (invention is the only
|
||||
/// source of nonzero slope_q at this call depth — see
|
||||
/// `derive_orbital_at_metres`'s doc on why slope_q is fixed), sampled
|
||||
/// across enough distinct positions that a nonzero value appearing even
|
||||
/// once would falsify the claim.
|
||||
#[test]
|
||||
fn derive_orbital_at_metres_never_invents_slope() {
|
||||
let hm = test_hm();
|
||||
let ta = test_ta(&hm);
|
||||
let climate = ClimateConstants::default();
|
||||
let p = earth_params();
|
||||
let dm = scale::REGION_M as f64;
|
||||
|
||||
for i in 0..25 {
|
||||
let wx = (i * 7) as f64 * dm * 0.37;
|
||||
let wy = (i * 11) as f64 * dm * 0.29;
|
||||
let prof =
|
||||
derive_orbital_at_metres(test_seed(), "test_body", &p, &ta, wx, wy, &climate);
|
||||
assert_eq!(
|
||||
prof.slope_q, 0,
|
||||
"orbital derive must never report invented slope (position {i})"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The orbital derive's `elev_q`/`temperature_c` must come from the SAME
|
||||
/// envelope + region-baseline sources `derive_at_metres` reads — not an
|
||||
/// independent/divergent computation. At a position where the invented
|
||||
/// scatter happens to contribute exactly zero (impossible to guarantee by
|
||||
/// construction, so this test instead checks the WEAKER, always-true
|
||||
/// property: both paths' `elev_q` derive from the same underlying
|
||||
/// bilinear envelope, so they must be close — within the invented
|
||||
/// scatter's own bounded contribution range, not arbitrarily different).
|
||||
/// This guards against the orbital path silently reading a different
|
||||
/// terrain field entirely (a copy-paste bug this refactor is exactly the
|
||||
/// kind of change that could introduce).
|
||||
#[test]
|
||||
fn derive_orbital_at_metres_elevation_tracks_the_same_envelope() {
|
||||
let hm = test_hm();
|
||||
let ta = test_ta(&hm);
|
||||
let climate = ClimateConstants::default();
|
||||
let p = earth_params();
|
||||
let dm = scale::DISTRICT_M as f64;
|
||||
|
||||
// Sample at a DISTRICT-aligned position (within the orbital function's
|
||||
// legal domain — it accepts any world position, this just makes the
|
||||
// district-mode comparison call meaningful) so both paths read the
|
||||
// exact same fractional heightmap pixel.
|
||||
let wx = 40.0 * dm;
|
||||
let wy = 20.0 * dm;
|
||||
let orbital = derive_orbital_at_metres(test_seed(), "test_body", &p, &ta, wx, wy, &climate);
|
||||
let full = derive_at_metres(test_seed(), "test_body", &p, &ta, wx, wy, &climate, 0.0);
|
||||
|
||||
// The invented scatter is a bounded perturbation on top of the raw
|
||||
// envelope (detail_scatter's amplitude is capped well under 100 elev_q
|
||||
// points) — the two must be in the same ballpark, not exactly equal
|
||||
// (that would defeat the point of invention existing at all at the
|
||||
// finer rung) and not wildly different (that would mean the orbital
|
||||
// path is reading a different field).
|
||||
let elev_diff = (orbital.elev_q - full.elev_q).abs();
|
||||
assert!(
|
||||
elev_diff <= 50,
|
||||
"orbital elev_q ({}) and full-derive elev_q ({}) must come from the \
|
||||
same envelope, not diverge arbitrarily",
|
||||
orbital.elev_q,
|
||||
full.elev_q
|
||||
);
|
||||
}
|
||||
|
||||
/// Orbital-scale windows must still fill all six dense wire arrays the
|
||||
/// client's colorizer family reads (T-1152: "the orbital cells must fill
|
||||
/// the same six dense arrays the DistrictWindowLayer carries") — this is
|
||||
/// checked at the `DistrictProfile` level (the pre-packing source of
|
||||
/// those six fields): every field the packer reads
|
||||
/// (`morphology_zone`/`elev_q`/`temperature_c`/`moisture_q`/
|
||||
/// `vegetation_class`/`glaciation_grade`) must be populated the same way
|
||||
/// regardless of rung — this test asserts the orbital output is a
|
||||
/// legitimate `DistrictProfile`, not a partially-filled stand-in.
|
||||
#[test]
|
||||
fn derive_orbital_at_metres_populates_all_six_wire_fields() {
|
||||
let hm = test_hm();
|
||||
let ta = test_ta(&hm);
|
||||
let climate = ClimateConstants::default();
|
||||
let p = earth_params();
|
||||
let dm = scale::REGION_M as f64;
|
||||
|
||||
let prof = derive_orbital_at_metres(
|
||||
test_seed(),
|
||||
"test_body",
|
||||
&p,
|
||||
&ta,
|
||||
5.0 * dm,
|
||||
3.0 * dm,
|
||||
&climate,
|
||||
);
|
||||
assert!((0..=100).contains(&prof.elev_q));
|
||||
assert!((0..=100).contains(&prof.moisture_q));
|
||||
// temperature_c is Some for a breathable-atmosphere body (earth_params).
|
||||
assert!(prof.temperature_c.is_some());
|
||||
// morphology_zone/vegetation_class/glaciation_grade are enums with no
|
||||
// "unset" state — successfully constructing the DistrictProfile at
|
||||
// all (no panic) is the actual assertion; the field reads below just
|
||||
// confirm they're reachable typed values, matching the discipline
|
||||
// `derive_district_is_deterministic` and neighbours already use.
|
||||
let _ = prof.morphology_zone;
|
||||
let _ = prof.vegetation_class;
|
||||
let _ = prof.glaciation_grade;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_district_profile_is_deterministic() {
|
||||
let hm = test_hm();
|
||||
|
||||
@@ -34,7 +34,9 @@ use crate::atlas::cascade::{run_cascade_from_heightmap, CascadeLayer};
|
||||
use crate::atlas::district_profile::{BodyParams, ClimateConstants, DistrictPos};
|
||||
use crate::atlas::features::TerrainAnalysis;
|
||||
use crate::atlas::heightmap::{load_heightmap_png, GRID_H, GRID_W};
|
||||
use crate::atlas::layer_proxy::{build_district_window_layer, DistrictWindowLayer};
|
||||
use crate::atlas::layer_proxy::{
|
||||
build_district_window_layer, DistrictWindowLayer, WindowGranularity,
|
||||
};
|
||||
use crate::atlas::shell::{fill_chunk, FilledChunk};
|
||||
use crate::atlas::skeleton_gen::{assign_all_block_tags, generate_quarter_skeleton};
|
||||
use crate::atlas::trait_catalog_reader::ExteriorCatalog;
|
||||
@@ -208,11 +210,18 @@ pub enum GenWorkItem {
|
||||
/// before this item is built — never trusted from the wire again here.
|
||||
center: DistrictPos,
|
||||
n: u32,
|
||||
/// Derivation granularity (T-1150) — `WINDOW_GRANULARITY_DISTRICT` (1)
|
||||
/// or `WINDOW_GRANULARITY_QUARTER` (4). Already resolved via
|
||||
/// `resolve_window_granularity` by the caller.
|
||||
granularity: u32,
|
||||
/// Derivation granularity (T-1150, widened T-1152 to the full
|
||||
/// [`WindowGranularity`] vocabulary — `District`/`Quarter` (finer)
|
||||
/// plus `Region` (coarser, T-1152)). Already resolved via
|
||||
/// `resolve_window_granularity_v2` by the caller — this is a
|
||||
/// concrete rung, never a raw wire value.
|
||||
granularity: WindowGranularity,
|
||||
/// Octave cutoff in whole metres (T-1149/T-1150), `0` = no cutoff.
|
||||
/// Meaningless for `granularity: Region` (`derive_orbital_at_metres`
|
||||
/// never calls the octave-scatter path this cuts off) but still
|
||||
/// carried and echoed uniformly — see `derive_orbital_at_metres`'s
|
||||
/// doc for why the field is harmless-but-unused there, not
|
||||
/// special-cased away.
|
||||
min_wl_m: u32,
|
||||
},
|
||||
}
|
||||
@@ -227,13 +236,17 @@ impl GenWorkItem {
|
||||
|
||||
/// Coalescing key for `DeriveWindow` items only — `(connection, body,
|
||||
/// granularity)` (T-1150, design doc §3 [SOFT] recommendation, extending
|
||||
/// T-1137's `(connection, body)`). `granularity` is part of the key so an
|
||||
/// in-flight district-spacing (granularity 1) pan-burst is never
|
||||
/// superseded by an unrelated quarter-spacing (granularity 4) request for
|
||||
/// the same connection+body, and vice versa — the two rungs are separate
|
||||
/// in-flight derives, not competing updates to the same one.
|
||||
/// T-1137's `(connection, body)`; widened T-1152 to carry the full
|
||||
/// [`WindowGranularity`] enum rather than the legacy `u32`, so `Region`
|
||||
/// occupies its own coalescing slot exactly like `District`/`Quarter` do
|
||||
/// — this is one of the five T-1150 touch points the R5 redesign must
|
||||
/// carry the new representation through). `granularity` is part of the
|
||||
/// key so an in-flight district-spacing pan-burst is never superseded by
|
||||
/// an unrelated quarter- or region-spacing request for the same
|
||||
/// connection+body, and vice versa — every rung is a separate in-flight
|
||||
/// derive, not a competing update to the same one.
|
||||
/// `None` for every other variant (they don't coalesce this way).
|
||||
pub fn window_supersede_key(&self) -> Option<(ConnectionId, &str, u32)> {
|
||||
pub fn window_supersede_key(&self) -> Option<(ConnectionId, &str, WindowGranularity)> {
|
||||
if let GenWorkItem::DeriveWindow {
|
||||
body_id,
|
||||
conn_id,
|
||||
@@ -1150,19 +1163,14 @@ mod tests {
|
||||
/// coalescing tests can exercise the granularity axis of
|
||||
/// `window_supersede_key()` without a second near-duplicate helper.
|
||||
fn derive_window(body_id: &str, conn_id: ConnectionId, center: DistrictPos) -> GenWorkItem {
|
||||
derive_window_at(
|
||||
body_id,
|
||||
conn_id,
|
||||
center,
|
||||
crate::atlas::layer_proxy::WINDOW_GRANULARITY_DISTRICT,
|
||||
)
|
||||
derive_window_at(body_id, conn_id, center, WindowGranularity::District)
|
||||
}
|
||||
|
||||
fn derive_window_at(
|
||||
body_id: &str,
|
||||
conn_id: ConnectionId,
|
||||
center: DistrictPos,
|
||||
granularity: u32,
|
||||
granularity: WindowGranularity,
|
||||
) -> GenWorkItem {
|
||||
GenWorkItem::DeriveWindow {
|
||||
body_id: body_id.to_string(),
|
||||
@@ -1316,21 +1324,11 @@ mod tests {
|
||||
|
||||
let conn = ConnectionId(9);
|
||||
q.submit_window(
|
||||
derive_window_at(
|
||||
"GranBody",
|
||||
conn,
|
||||
(0, 0),
|
||||
crate::atlas::layer_proxy::WINDOW_GRANULARITY_DISTRICT,
|
||||
),
|
||||
derive_window_at("GranBody", conn, (0, 0), WindowGranularity::District),
|
||||
GenPriority::Immediate,
|
||||
);
|
||||
q.submit_window(
|
||||
derive_window_at(
|
||||
"GranBody",
|
||||
conn,
|
||||
(0, 0),
|
||||
crate::atlas::layer_proxy::WINDOW_GRANULARITY_QUARTER,
|
||||
),
|
||||
derive_window_at("GranBody", conn, (0, 0), WindowGranularity::Quarter),
|
||||
GenPriority::Immediate,
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -1352,21 +1350,11 @@ mod tests {
|
||||
|
||||
let conn = ConnectionId(11);
|
||||
q.submit_window(
|
||||
derive_window_at(
|
||||
"SameGranBody",
|
||||
conn,
|
||||
(0, 0),
|
||||
crate::atlas::layer_proxy::WINDOW_GRANULARITY_QUARTER,
|
||||
),
|
||||
derive_window_at("SameGranBody", conn, (0, 0), WindowGranularity::Quarter),
|
||||
GenPriority::Immediate,
|
||||
);
|
||||
q.submit_window(
|
||||
derive_window_at(
|
||||
"SameGranBody",
|
||||
conn,
|
||||
(5, 5),
|
||||
crate::atlas::layer_proxy::WINDOW_GRANULARITY_QUARTER,
|
||||
),
|
||||
derive_window_at("SameGranBody", conn, (5, 5), WindowGranularity::Quarter),
|
||||
GenPriority::Immediate,
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
+661
-88
File diff suppressed because it is too large
Load Diff
@@ -418,7 +418,7 @@ fn drain_generation_completions(
|
||||
body_id,
|
||||
layer.center,
|
||||
layer.n,
|
||||
layer.granularity,
|
||||
layer.granularity_v2,
|
||||
layer.min_wl_m,
|
||||
),
|
||||
*layer,
|
||||
@@ -914,6 +914,7 @@ mod tests {
|
||||
window_center: None,
|
||||
window_n: 0,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: None,
|
||||
window_min_wl_m: 0,
|
||||
},
|
||||
)]));
|
||||
|
||||
@@ -1202,6 +1202,7 @@ mod inbound_tests {
|
||||
window_center: None,
|
||||
window_n: 0,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: None,
|
||||
window_min_wl_m: 0,
|
||||
};
|
||||
let frame = rmp_serde::to_vec_named(&req).unwrap();
|
||||
@@ -1249,6 +1250,7 @@ mod inbound_tests {
|
||||
window_center: None,
|
||||
window_n: 0,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: None,
|
||||
window_min_wl_m: 0,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
@@ -372,6 +372,7 @@ fn single_tick_drains_all_ready_inbound_frames() {
|
||||
window_center: None,
|
||||
window_n: 0,
|
||||
window_granularity: 0,
|
||||
window_granularity_v2: None,
|
||||
window_min_wl_m: 0,
|
||||
};
|
||||
let payload = rmp_serde::to_vec_named(&req).expect("failed to serialize");
|
||||
|
||||
@@ -6,7 +6,7 @@ use settled_reach_server::atlas::layer1::Layer1Output;
|
||||
use settled_reach_server::atlas::layer_proxy::{
|
||||
AtlasLayerResponse, AtlasLayerStatus, DistrictWindowLayer, QuarterFootprintEntry,
|
||||
QuarterFootprintLayer, RegionGridLayer, RoadGraphEdge, RoadGraphLayer, RoadGraphNode,
|
||||
SettlementEntry, SettlementLayer, SettlementSizeClass, REGION_TEMP_NONE_DC,
|
||||
SettlementEntry, SettlementLayer, SettlementSizeClass, WindowGranularity, REGION_TEMP_NONE_DC,
|
||||
WINDOW_GRANULARITY_DISTRICT,
|
||||
};
|
||||
use settled_reach_server::atlas::region_profile::{SeasonPhase, WeatherState};
|
||||
@@ -727,6 +727,7 @@ fn generate_atlas_layer_response_fixtures() {
|
||||
center: (10, -5),
|
||||
n: 2,
|
||||
granularity: WINDOW_GRANULARITY_DISTRICT,
|
||||
granularity_v2: WindowGranularity::District,
|
||||
min_wl_m: 0,
|
||||
morphology: vec![0, 8, 14, 16], // OpenOcean, AlluvialPlain, Alpine, Wetland
|
||||
elev_q: vec![0, 45, 98, 60],
|
||||
|
||||
@@ -17,11 +17,14 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use settled_reach_server::atlas::district_profile::{
|
||||
derive_at_metres, BodyParams, ClimateConstants,
|
||||
derive_at_metres, derive_orbital_at_metres, BodyParams, ClimateConstants,
|
||||
};
|
||||
use settled_reach_server::atlas::drainage;
|
||||
use settled_reach_server::atlas::features::TerrainAnalysis;
|
||||
use settled_reach_server::atlas::heightmap::BodyHeightmap;
|
||||
use settled_reach_server::atlas::layer_proxy::{
|
||||
build_district_window_layer, WindowGranularity, DISTRICT_WINDOW_MAX_N_REGION, WIRE_CAP_CELLS,
|
||||
};
|
||||
use settled_reach_server::atlas::scale;
|
||||
use settled_reach_server::seed::{SeedChain, SeedDomain};
|
||||
|
||||
@@ -151,3 +154,187 @@ fn bench_derive_at_metres_district_and_quarter_spacing() {
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Time `n_cells` sequential `derive_orbital_at_metres` calls — the
|
||||
/// region-baseline-blend-only path (T-1152, design doc §2/§4/§9 R1), no
|
||||
/// `invent_primitives` call at any point. Mirrors `time_derive_sweep`'s shape
|
||||
/// exactly so the two numbers are directly comparable.
|
||||
fn time_orbital_sweep(
|
||||
seed: SeedChain,
|
||||
body_id: &str,
|
||||
params: &BodyParams,
|
||||
ta: &TerrainAnalysis,
|
||||
climate: &ClimateConstants,
|
||||
grid_side: u32,
|
||||
step_m: f64,
|
||||
) -> (std::time::Duration, f64) {
|
||||
let n_cells = (grid_side * grid_side) as u64;
|
||||
let t0 = Instant::now();
|
||||
for row in 0..grid_side {
|
||||
for col in 0..grid_side {
|
||||
let wx = col as f64 * step_m;
|
||||
let wy = row as f64 * step_m;
|
||||
let prof = derive_orbital_at_metres(seed, body_id, params, ta, wx, wy, climate);
|
||||
std::hint::black_box(prof.elev_q);
|
||||
}
|
||||
}
|
||||
let elapsed = t0.elapsed();
|
||||
let per_cell_ns = elapsed.as_secs_f64() * 1e9 / n_cells as f64;
|
||||
(elapsed, per_cell_ns)
|
||||
}
|
||||
|
||||
/// T-1152 / design doc §9 R1: "MEASURE FIRST" — per-cell cost of the
|
||||
/// orbital-mode region-baseline-blend-only path (no `invent_primitives`) at
|
||||
/// coarse (region-scale, ≥205 km) spacings, plus a realistic full-orbital-frame
|
||||
/// extrapolation (1600×900 canvas). This is the number the design doc's §4/§7
|
||||
/// planetary-rung cost story rested on as an UNMEASURED extrapolation —
|
||||
/// this test replaces "extrapolated from the uncut per-cell rate" with an
|
||||
/// actually-measured orbital-path rate.
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn bench_derive_orbital_at_metres_region_spacing() {
|
||||
let hm = bench_hm();
|
||||
let ta = bench_ta(&hm);
|
||||
let params = bench_params();
|
||||
let climate = ClimateConstants::default();
|
||||
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
|
||||
let grid_side = 64u32; // 4,096 cells/sweep, same shape as the district/quarter sweeps above
|
||||
|
||||
println!("\n=== T-1152 orbital-rung derive_orbital_at_metres benchmark ===");
|
||||
println!(
|
||||
"grid: {grid_side}x{grid_side} = {} cells/sweep\n",
|
||||
grid_side * grid_side
|
||||
);
|
||||
|
||||
let region_m = scale::REGION_M as f64;
|
||||
|
||||
// Region spacing (204,800 m) — the coarsest named rung short of the
|
||||
// planet-wide elastic seam (D-243).
|
||||
let (elapsed, per_cell_ns) =
|
||||
time_orbital_sweep(seed, "bench", ¶ms, &ta, &climate, grid_side, region_m);
|
||||
println!(
|
||||
"orbital, region spacing (204.8km): {:>8.2} ms total, {:>7.1} ns/cell ({:.3} µs/cell)",
|
||||
elapsed.as_secs_f64() * 1000.0,
|
||||
per_cell_ns,
|
||||
per_cell_ns / 1000.0
|
||||
);
|
||||
|
||||
// Same spacing, for direct comparison: the FULL derive_at_metres path
|
||||
// (invent_primitives included) at the SAME region spacing — quantifies
|
||||
// exactly what skipping invention buys, at the spacing where it matters.
|
||||
let (elapsed_full, per_cell_ns_full) = time_derive_sweep(
|
||||
seed, "bench", ¶ms, &ta, &climate, grid_side, region_m, 0.0,
|
||||
);
|
||||
println!(
|
||||
"district-mode (full derive_at_metres) at region spacing: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} µs/cell)",
|
||||
elapsed_full.as_secs_f64() * 1000.0,
|
||||
per_cell_ns_full,
|
||||
per_cell_ns_full / 1000.0
|
||||
);
|
||||
println!(
|
||||
"orbital speedup vs. full derive at the same spacing: {:.2}x\n",
|
||||
per_cell_ns_full / per_cell_ns
|
||||
);
|
||||
|
||||
// Realistic full-orbital-frame estimate: a 1600x900 canvas at
|
||||
// ~1-2 px/cell equivalents (design doc §4's worked example resolution
|
||||
// class). Single-thread extrapolation from the MEASURED per-cell rate —
|
||||
// labelled as an extrapolation, not claimed as independently measured at
|
||||
// full canvas size (the parallel/chunked throughput is a SEPARATE
|
||||
// measurement, T-1151's row-chunked par_iter, already landed and reused
|
||||
// unchanged by the orbital rung's serving path — see the ticket report).
|
||||
for (label, px_per_cell) in [("1 px/cell", 1u32), ("2 px/cell", 2u32)] {
|
||||
let cols = 1600 / px_per_cell;
|
||||
let rows = 900 / px_per_cell;
|
||||
let cells = (cols as u64) * (rows as u64);
|
||||
let est_ms = cells as f64 * per_cell_ns / 1e6;
|
||||
println!(
|
||||
"full-canvas 1600x900 @ {label} ({cols}x{rows} = {cells} cells): \
|
||||
{est_ms:.1} ms single-thread (EXTRAPOLATED from the measured per-cell rate above)"
|
||||
);
|
||||
}
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
/// **T-1152 R1 — the number that actually governs interactive latency**, as
|
||||
/// opposed to the full-canvas single-shot extrapolation above (which the
|
||||
/// design doc's own carrier ruling makes moot — Jeroen's ruling is
|
||||
/// progressive capped-density TILING, never a whole-canvas one-shot derive).
|
||||
/// This measures a single served Region-granularity window tile through the
|
||||
/// REAL production path (`build_district_window_layer`, including its
|
||||
/// row-chunked `par_iter`, T-1151) at the wire-size cap — the same function
|
||||
/// `serve_district_window`/`run_work_item`'s `DeriveWindow` arm calls, not a
|
||||
/// hand-rolled sweep. This is the measured (not extrapolated) parallel
|
||||
/// number the design doc's §7 flagged as missing ("no chunked-par_iter
|
||||
/// benchmark has been run").
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn bench_served_region_window_tile_at_wire_cap() {
|
||||
let hm = bench_hm();
|
||||
let ta = bench_ta(&hm);
|
||||
let params = bench_params();
|
||||
let climate = ClimateConstants::default();
|
||||
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
|
||||
|
||||
println!("\n=== T-1152 served Region-window-tile benchmark (real production path) ===");
|
||||
|
||||
// The largest n the server will ever actually derive at Region
|
||||
// granularity is DISTRICT_WINDOW_MAX_N_REGION, clamped further by
|
||||
// clamp_window_n_v2 to the WIRE_CAP_CELLS ceiling — use the SAME
|
||||
// capped n a real client's oversized request would resolve to.
|
||||
let n = DISTRICT_WINDOW_MAX_N_REGION;
|
||||
|
||||
// Warm-up call (first call on a body pays no extra cost here since ta is
|
||||
// already built — this just avoids counting one-time allocator warm-up
|
||||
// noise in the timed sample).
|
||||
let _ = build_district_window_layer(
|
||||
seed,
|
||||
"bench",
|
||||
¶ms,
|
||||
&ta,
|
||||
(0, 0),
|
||||
n,
|
||||
&climate,
|
||||
WindowGranularity::Region,
|
||||
0,
|
||||
);
|
||||
|
||||
let iterations = 20;
|
||||
let t0 = Instant::now();
|
||||
let mut last_side = 0usize;
|
||||
for _ in 0..iterations {
|
||||
let layer = build_district_window_layer(
|
||||
seed,
|
||||
"bench",
|
||||
¶ms,
|
||||
&ta,
|
||||
(0, 0),
|
||||
n,
|
||||
&climate,
|
||||
WindowGranularity::Region,
|
||||
0,
|
||||
);
|
||||
last_side = (layer.morphology.len() as f64).sqrt().round() as usize;
|
||||
std::hint::black_box(layer.elev_q.len());
|
||||
}
|
||||
let elapsed = t0.elapsed();
|
||||
let per_call_ms = elapsed.as_secs_f64() * 1000.0 / iterations as f64;
|
||||
|
||||
println!(
|
||||
"n={n} (DISTRICT_WINDOW_MAX_N_REGION), derived {last_side}x{last_side} region cells \
|
||||
({} cells, WIRE_CAP_CELLS={WIRE_CAP_CELLS}):",
|
||||
last_side * last_side
|
||||
);
|
||||
println!(
|
||||
" {iterations} calls, {:.2} ms total, {per_call_ms:.3} ms/call \
|
||||
(row-chunked par_iter, {} Rayon threads available)",
|
||||
elapsed.as_secs_f64() * 1000.0,
|
||||
std::thread::available_parallelism()
|
||||
.map(|n| n.get())
|
||||
.unwrap_or(0)
|
||||
);
|
||||
println!(
|
||||
" compare: shipped district n=64 cap measures ~5 ms/call (design doc §7, MEASURED)\n"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user