acdream/docs/research/2026-07-25-slice-h-b-light-top-k-report.md
Erik a2a1e5916d perf(lighting): bound global light selection
Replace over-cap full sorting with a retained exact top-k heap while preserving the accepted tie-order fallback. Differential tests lock randomized and Town Network-scale output, and the measured 463-light path cuts selector CPU by 29 percent without warmed allocations.
2026-07-25 05:40:32 +02:00

2.3 KiB

Slice H-b — exact bounded point-light selection

Result

LightManager.BuildPointLightSnapshot no longer sorts every qualifying light when more than 128 lights are eligible. It keeps the best 128 in a retained worst-first heap, sorts only those 128 for submission, and performs no allocation after warm-up.

The optimization preserves the accepted pre-slice output exactly:

  • dynamic lights still precede static lights;
  • each pool is still ordered by squared player distance;
  • the visible-cell, lit-state, and directional-light filters are unchanged;
  • in-budget snapshots remain in registration order;
  • equal-rank overflow frames which could expose .NET's unstable sort retain the previous complete-sort path and exact object-reference order.

The retail oracle and readable pseudocode are in 2026-07-25-slice-h-b-light-top-k-pseudocode.md. Retail's own selector is a bounded ordered insertion into separate static and dynamic pools. This slice ports the bounded-work principle without claiming to close AP-85's larger pool/cap/lifecycle divergence.

Evidence

Focused conformance covers:

  • exact randomized differential comparison against the previous full-sort oracle;
  • equal-distance overflow and cap-boundary behavior;
  • lit, directional, dynamic, static, and visible-cell filtering;
  • a deterministic 463-light Town Network-scale candidate set;
  • zero warmed allocations on both bounded and equal-rank fallback routes.

A Release diagnostic microbenchmark ran 100,000 snapshot builds with 463 eligible uniquely ranked lights:

Route Elapsed Managed allocation
previous complete sort 1,377.578 ms 0 bytes
retained bounded selector 976.993 ms 0 bytes

That sample is a 1.410x throughput improvement, or about 29.1% less CPU time inside selection. It is a narrow microbenchmark rather than a whole-frame FPS claim; its purpose is to prove that the replacement removes work rather than merely moving it.

Safety boundary

No shader, light parameters, candidate membership, draw order, or production scene ownership changed. AP-85 remains open because retail's exact 7-dynamic/40-static pools and DBObj-resident cell lifecycle are outside this performance-only slice.

G4's independent visual rollback remains:

git revert ef1d263337997bb030eadb7b8e71d73dc659907a