wip(assembler): round S13 -- tile cost counted per LOD (diagnostics only, no output change)
Exclusive per-stage tile timers and probes. Findings: LOD0 is MC-exclusive (10.4 of 14 ms p95, 243 ns/sample); LOD4's 3x cost is column-cache misses (8,224 vs ~1,600-1,900 at LOD3/5), cap refine secondary; per-tile feature lists cost ~0.001 ms; cross-worker column repeats common (277k at LOD0). Global octave drop and stride-2 interpolation probes fail the 2 cm gate. Worker cap 1 kept (cap 0 worse 40 m/s tick tail). Report: Standalone/Research/WorldAssemblerM3/Round17. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
212814a75c
commit
87b0e5d8cd
9 files changed
+763
-76
No files matched your search
@@ -126,6 +126,10 @@ namespace VoxelForgeCore
|
||||
double classificationMilliseconds = 0.0;
|
||||
double columnComputeMilliseconds = 0.0;
|
||||
double columnEvaluationExclusiveMilliseconds = 0.0;
|
||||
double marchingCubesColumnComputeMilliseconds = 0.0;
|
||||
double groundRefinementColumnComputeMilliseconds = 0.0;
|
||||
double capRefinementColumnComputeMilliseconds = 0.0;
|
||||
double classificationExclusiveMilliseconds = 0.0;
|
||||
double marchingCubesExclusiveMilliseconds = 0.0;
|
||||
double groundRefinementExclusiveMilliseconds = 0.0;
|
||||
double capRefinementExclusiveMilliseconds = 0.0;
|
||||
@@ -687,7 +691,8 @@ namespace VoxelForgeCore
|
||||
std::int64_t minimumYSubvoxels, std::int64_t minimumZSubvoxels,
|
||||
std::int64_t maximumXSubvoxels, std::int64_t maximumYSubvoxels,
|
||||
std::int64_t maximumZSubvoxels, std::int32_t stepVoxels) const noexcept;
|
||||
static void resetThreadColumnCacheStats(bool captureCoordinates = false) noexcept;
|
||||
static void resetThreadColumnCacheStats(bool captureCoordinates = false,
|
||||
bool clearCache = false, std::uint32_t octaveDrop = 0) noexcept;
|
||||
static const HeightfieldColumnCacheStats& threadColumnCacheStats() noexcept;
|
||||
FieldInterval interval(std::int64_t minimumXSubvoxels,
|
||||
std::int64_t minimumYSubvoxels,
|
||||
|
||||
@@ -1479,12 +1479,18 @@ namespace VoxelForgeCore
|
||||
const bool bSkipFarShellRepair=key.lod>=7&&bUseNativeSurfaceHorizon;
|
||||
FStandaloneShapeWorld::ResetThreadClassificationStats();
|
||||
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(
|
||||
options.captureColumnCoordinates&&key.lod==8);
|
||||
options.captureColumnCoordinates);
|
||||
bool bNativeSurfaceTile=false;
|
||||
bool bNativeSurfaceBandSafe=false;
|
||||
double classificationColumnComputeMilliseconds=0.0;
|
||||
if(bHasWorldAssembly)
|
||||
{
|
||||
const double columnMillisecondsBeforeClassification=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto Classification=tileShape.ClassifyBoxDetailed(origin,step,cells);
|
||||
classificationColumnComputeMilliseconds=std::max(0.0,
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds
|
||||
-columnMillisecondsBeforeClassification);
|
||||
bNativeSurfaceTile=Classification.bNativeSurface;
|
||||
// NativeSurfaceDensityAt consumes only the bounded manifest heightfield and its
|
||||
// bounded type-2/type-4 features. Unknown tile occupancy does not make that Z support
|
||||
@@ -1518,7 +1524,16 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.columnCacheHits=Columns.columnHits;
|
||||
out.diagnostics.columnCacheMisses=Columns.columnMisses;
|
||||
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
|
||||
out.diagnostics.classificationExclusiveMilliseconds=std::max(0.0,
|
||||
Counts.Milliseconds-classificationColumnComputeMilliseconds);
|
||||
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
|
||||
if(options.captureColumnCoordinates)
|
||||
{
|
||||
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
|
||||
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
|
||||
out.diagnostics.columnCoordinateCaptureComplete=Columns.coordinateCaptureComplete;
|
||||
out.diagnostics.evaluatedColumnCoordinates=Columns.evaluatedCoordinates;
|
||||
}
|
||||
out.densitySampleCount=0;
|
||||
return out;
|
||||
}
|
||||
@@ -1662,6 +1677,8 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.marchingCubesExclusiveMilliseconds=std::max(0.0,
|
||||
MarchingCubesWallMilliseconds-(ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing));
|
||||
out.diagnostics.marchingCubesColumnComputeMilliseconds=std::max(0.0,
|
||||
ColumnMillisecondsAfterMeshing-ColumnMillisecondsBeforeMeshing);
|
||||
const double MeshingMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-MeshingStart).count();
|
||||
out.diagnostics.farHorizonVersion=key.lod>=4?options.farHorizonVersion:0;
|
||||
@@ -1712,6 +1729,8 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.groundRefinementExclusiveMilliseconds=std::max(0.0,
|
||||
GroundWallMilliseconds-(ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround));
|
||||
out.diagnostics.groundRefinementColumnComputeMilliseconds=std::max(0.0,
|
||||
ColumnMillisecondsAfterGround-ColumnMillisecondsBeforeGround);
|
||||
const double ColumnMillisecondsBeforeCap=
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto CapStart=std::chrono::steady_clock::now();
|
||||
@@ -1723,6 +1742,8 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
out.diagnostics.capRefinementExclusiveMilliseconds=std::max(0.0,
|
||||
CapWallMilliseconds-(ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap));
|
||||
out.diagnostics.capRefinementColumnComputeMilliseconds=std::max(0.0,
|
||||
ColumnMillisecondsAfterCap-ColumnMillisecondsBeforeCap);
|
||||
out.diagnostics.farHorizonMilliseconds=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-HorizonStart).count();
|
||||
out.diagnostics.capRefined=CapStats.Refined;
|
||||
@@ -1747,7 +1768,9 @@ namespace VoxelForgeCore
|
||||
out.diagnostics.columnComputeMilliseconds=Columns.columnComputeMilliseconds;
|
||||
out.diagnostics.columnEvaluationExclusiveMilliseconds=Columns.columnComputeMilliseconds;
|
||||
out.diagnostics.classificationMilliseconds=Counts.Milliseconds;
|
||||
if(options.captureColumnCoordinates&&key.lod==8)
|
||||
out.diagnostics.classificationExclusiveMilliseconds=std::max(0.0,
|
||||
Counts.Milliseconds-classificationColumnComputeMilliseconds);
|
||||
if(options.captureColumnCoordinates)
|
||||
{
|
||||
out.diagnostics.columnEvaluationWorkerHash=static_cast<std::uint64_t>(
|
||||
std::hash<std::thread::id>{}(std::this_thread::get_id()))+1u;
|
||||
|
||||
@@ -1027,11 +1027,18 @@ namespace world_assembly_detail
|
||||
std::int64_t wavelengthSubvoxels)
|
||||
{ return FloorDiv(positionSubvoxels * Q16, wavelengthSubvoxels); }
|
||||
|
||||
// Audit-only override; normal tile generation resets this to zero before evaluating a recipe.
|
||||
// Surcharge reservee aux mesures; la generation remet zero avant d'evaluer une recette.
|
||||
inline thread_local std::uint32_t HeightfieldColumnOctaveDrop = 0;
|
||||
|
||||
inline std::int32_t FractalNoiseQ16(std::int64_t xQ16, std::int64_t yQ16,
|
||||
std::int64_t zQ16, std::uint32_t octaves, bool ridged = false)
|
||||
{
|
||||
std::int64_t total = 0, weightSum = 0, weight = Q16, ridgeWeight = Q16;
|
||||
for (std::uint32_t octave = 0; octave < octaves; ++octave)
|
||||
std::uint32_t effectiveOctaves=octaves;
|
||||
if(HeightfieldColumnOctaveDrop!=0&&octaves!=0)
|
||||
effectiveOctaves=std::max(1u,octaves-std::min(octaves-1,HeightfieldColumnOctaveDrop));
|
||||
for (std::uint32_t octave = 0; octave < effectiveOctaves; ++octave)
|
||||
{
|
||||
const std::int64_t scale = 1ll << octave;
|
||||
const std::int64_t noise = Perlin3DQ16(xQ16 * scale,yQ16 * scale,zQ16 * scale);
|
||||
@@ -1965,9 +1972,15 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
|
||||
return out;
|
||||
}
|
||||
|
||||
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates) noexcept
|
||||
void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool clearCache,
|
||||
std::uint32_t octaveDrop) noexcept
|
||||
{
|
||||
// Diagnostic probes may clear old tile entries before comparing isolated sample grids.
|
||||
// Les sondes de mesure peuvent vider les entrees d'une tuile avant de comparer deux grilles.
|
||||
if(clearCache)world_assembly_detail::HeightfieldColumnCache.fill(
|
||||
world_assembly_detail::HeightfieldColumnCacheEntry{});
|
||||
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
|
||||
world_assembly_detail::HeightfieldColumnOctaveDrop=octaveDrop;
|
||||
Stats.pointCalls=0;
|
||||
Stats.columnHits=0;
|
||||
Stats.columnMisses=0;
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
# World Assembler S13 — Round 17
|
||||
|
||||
**2026-10-19 · What does a tile actually cost?**
|
||||
|
||||
## Result
|
||||
|
||||
No generation optimization shipped. The measurements show that owner-plan tile time is mostly
|
||||
column evaluation plus MC, that LOD4 is dominated by column-cache misses, and that cross-worker XY
|
||||
reuse is common. The interpolation and global octave-drop probes fail the 2 cm quality bar. A shared
|
||||
column page still needs a measured candidate with bounded memory, synchronization, and lifecycle
|
||||
proof, so I did not add one. Density, mesh, collision, and recipe output are unchanged; no recipe
|
||||
version bump is needed.
|
||||
|
||||
The current BuildHost 40 m/s trace passed the support gate **75/75**. It reported a 4.825 s
|
||||
initial-set ready time, 28.760 worker-seconds, and a 0.510 s stationary ring. The last initial LOD0
|
||||
tile applied at 1.146 s. At 40 m/s, `UWorld::Tick` was 3.133/5.426 ms p95/max at spawn and
|
||||
30.314/43.142 ms while walking. The worker-second and full-set targets passed in this trace; the
|
||||
LOD0 and strict S12 tick-tail targets did not. Startup-clock measurements and worker-task intervals
|
||||
use different origins from the S12 actor report, so I do not treat 4.825 s as an apples-to-apples
|
||||
replacement for S12's 4.911 s.
|
||||
|
||||
## 1. Exclusive tile cost on the owner plan
|
||||
|
||||
The BuildHost audit measures eight cold, no-hash tiles per requested LOD, with a separate hashed
|
||||
full-feature-list versus per-tile-list parity check (`hashes_identical=1`). The table gives p95
|
||||
stage time and the p95 of each tile's `stage time / density samples`, in `ms / ns per sample`.
|
||||
Percentiles are computed independently, so stage p95 values do not add to the full-tile p95. The
|
||||
sample denominator includes the normal halo: LOD0–1 are 32 MC cells / 35³ = 42,875 samples;
|
||||
LOD3–8 are 16 coarse cells / 19³ = 6,859 samples. The owner configuration is not a 32³ lattice at
|
||||
every LOD.
|
||||
|
||||
| LOD | Full scan → per-tile list p95 (ms) | Classify excl. | MC column eval. | MC excl. | Ground column eval. | Ground excl. | Cap column eval. | Cap excl. | Mesh assembly |
|
||||
|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 0 | 13.806 → 14.160 | 0.0064 / 0.15 | 3.708 / 86.48 | 10.436 / 243.40 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0251 / 0.59 |
|
||||
| 1 | 9.616 → 9.584 | 0.0821 / 0.02 | 4.096 / 95.52 | 7.191 / 167.72 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0314 / 0.73 |
|
||||
| 3 | 6.952 → 6.878 | 0.0286 / 9.90 | 3.989 / 1,090.54 | 2.879 / 419.80 | 0 / 0 | 0 / 0 | 0 / 0 | 0 / 0 | 0.0075 / 2.97 |
|
||||
| 4 | 36.445 → 35.790 | 0.0015 / 0.22 | 29.361 / 4,280.71 | 2.948 / 429.84 | 0 / 0 | 0.0001 / 0.01 | 4.865 / 709.35 | 1.259 / 183.61 | 0.0231 / 3.37 |
|
||||
| 5 | 10.586 → 10.420 | 0.0010 / 0.15 | 2.958 / 431.27 | 1.195 / 174.25 | 4.894 / 713.57 | 2.234 / 325.63 | 0.405 / 59.06 | 0.241 / 35.08 | 0.0054 / 0.79 |
|
||||
| 8 | 14.778 → 14.507 | 0.0046 / 0.67 | 5.671 / 826.84 | 2.366 / 345.01 | 1.775 / 258.74 | 1.652 / 240.87 | 1.823 / 265.78 | 1.348 / 196.50 | 0.0252 / 3.67 |
|
||||
|
||||
The paired full/candidate timings are the feature-query negative control: the full plan scans feature
|
||||
bounds; the candidate builds the per-tile feature lists used by Core. All tested hashes matched.
|
||||
Their timing differences are small and mixed in sign. Feature-list p95 itself was only 0.0009–
|
||||
0.0015 ms on this owner sample.
|
||||
|
||||
There is no separate volumetric 3D density-noise stage in native SurfaceWorld. Its surface recipe
|
||||
does call `FractalNoiseQ16`/Perlin while evaluating 2D XY columns, so that work is included in
|
||||
column evaluation. Material/color assignment is not a separate timer; it is included in meshing and
|
||||
the final mesh/payload path. The current route has no tunnel, room, shaft, or route-density stage.
|
||||
Classification-exclusive time subtracts column work done while classifying; MC/ground/cap column
|
||||
time is reported beside the corresponding non-column exclusive time. The `horizon_p95_ms` counter
|
||||
wraps ground and cap refinement and is inclusive, so it is not another exclusive stage. Far-shell
|
||||
repair was negligible in this sample (at most 0.0003 ms p95).
|
||||
|
||||
### Why LOD4 is expensive
|
||||
|
||||
LOD4 full tile p95 is 35.790 ms, versus 6.878 ms at LOD3 and 10.420 ms at LOD5. LOD4 is the first
|
||||
cap-refined level: it samples cap at 16 voxels (4 m); LOD3 has no cap refinement, and LOD5 cap uses
|
||||
the 128-voxel (32 m) boundary grid. Ground refinement starts at LOD5. LOD4's cap work costs
|
||||
4.865 ms of column evaluation and 1.259 ms exclusive at p95.
|
||||
|
||||
That cap difference does not explain most of the spike. LOD4 MC performs 29.361 ms p95 of column
|
||||
evaluation, and its sampled tile has 8,224 column misses versus 1,634 at LOD3 and 1,916 at LOD5.
|
||||
Thus the measured anomaly is primarily poor column reuse at LOD4, with cap refinement adding a
|
||||
smaller separate cost. The data does not isolate why the thread-local direct-mapped cache misses
|
||||
rise at LOD4; tile extent, halo/normal probes, and XY spread all differ with LOD.
|
||||
|
||||
## 2. Do far tiles evaluate detail they cannot show?
|
||||
|
||||
The LOD8 tile p95 spends 9.203 ms in column evaluation out of 14.507 ms. There is no volumetric
|
||||
cave-noise loop in this path, but the 2D heightfield recipe still evaluates its full configured
|
||||
noise octaves at every requested XY column. A measurement-only probe dropped the highest one or two
|
||||
octaves globally on two LOD8 tiles. It reported 26.50% / 40.31% less column time, but ground-height
|
||||
p95 errors of 7,545 / 7,581 cm and cap-height errors of 2,427 / 7,859 cm; all 578 cap samples were
|
||||
over 2 cm. Near LOD0–1 the same probe was slower than baseline, so its timing is not a reliable
|
||||
frequency cost estimate. It also renormalizes the remaining octaves and does not implement a
|
||||
per-recipe wavelength cutoff at half the MC cell size.
|
||||
|
||||
This rejects global octave removal. A frequency-aware cutoff could change appearance while keeping
|
||||
determinism, but this probe does not justify a cutoff or quantify its screen-space result. It was
|
||||
measurement-only and did not affect tile hashes.
|
||||
|
||||
## 3. Per-tile feature lists and 2D cache reuse
|
||||
|
||||
Core's `World::generateTile` builds bounded per-tile query lists for overlapping
|
||||
`Heightfield2D` strata and `FloatingIslandFieldV1` plans, including the normal halo. Those two
|
||||
WorldAssembler families share the scoped feature-list route. The legacy Unreal `UVoxelGenerator`
|
||||
room/tunnel/shaft families use their own room, tunnel, and generator caches; Round 1's Core tile list
|
||||
is not one shared list for all legacy generator families. On the owner SurfaceWorld tiles, list-build
|
||||
time is under 0.002 ms p95.
|
||||
|
||||
`Heightfield2D::point(x,y,z)` may be called at many Z/normal samples, but the thread-local XY cache
|
||||
normally evaluates a column once per worker-cache miss. The cold BuildHost sample shows:
|
||||
|
||||
| LOD | Field-point calls p95 | Column misses p95 | Approx. hit rate |
|
||||
|---:|---:|---:|---:|
|
||||
| 0 | 44,100 | 2,244 | 94.9% |
|
||||
| 1 | 44,100 | 2,289 | 94.8% |
|
||||
| 3 | 4,191 | 1,634 | 61.0% |
|
||||
| 4 | 9,405 | 8,224 | 12.6% |
|
||||
| 5 | 8,197 | 1,916 | 76.6% |
|
||||
| 8 | 4,441 | 2,708 | 39.0% |
|
||||
|
||||
The Standalone W8 capture recorded all tiles and all LODs. `repeated` means an XY coordinate was
|
||||
re-evaluated by a different same-LOD tile; the neighbor count includes any prior adjacent tile,
|
||||
and the cross-worker column counts a neighbor repeat observed on a different worker.
|
||||
|
||||
| LOD | Tiles | Unique XY | Repeated tile evaluations | Neighbor repeats | Cross-worker neighbor repeats |
|
||||
|---:|---:|---:|---:|---:|---:|
|
||||
| 0 | 512 | 128,240 | 347,280 | 280,233 | 276,658 |
|
||||
| 1 | 448 | 103,302 | 300,242 | 249,825 | 244,941 |
|
||||
| 2 | 448 | 32,274 | 102,713 | 84,318 | 79,402 |
|
||||
| 3 | 384 | 33,279 | 87,158 | 81,680 | 78,272 |
|
||||
| 4 | 320 | 91,325 | 230,492 | 229,811 | 202,684 |
|
||||
| 5 | 144 | 71,950 | 118,630 | 118,580 | 112,191 |
|
||||
| 6 | 96 | 280,313 | 63,523 | 63,523 | 63,435 |
|
||||
| 7 | 48 | 71,529 | 7,780 | 7,780 | 7,138 |
|
||||
| 8 | 153 | 106,219 | 32,879 | 32,879 | 30,956 |
|
||||
|
||||
This makes immutable shared column pages worth a candidate A/B, especially for LOD0–5. The counts
|
||||
do not measure synchronization, page compilation, eviction, memory, or resulting wall-time savings.
|
||||
No page cache was added on counts alone. The column capture is opt-in, worker-local, and read back
|
||||
after generation.
|
||||
|
||||
## 4. Coarse-lattice interpolation probe
|
||||
|
||||
The measurement-only probe evaluated exact ground/cap columns at every MC XY sample, then sampled
|
||||
every second MC sample and bilinearly interpolated the rest. No near-zero or thin-feature fallback
|
||||
was included, so this is a lower-cost bound and a failure screen, not a production candidate. The
|
||||
table is two tiles per LOD. Errors are p95 over sampled columns; counts over 2 cm are explicit.
|
||||
|
||||
| LOD | Measured column-compute time saved at stride 2 | Ground p95 / >2 cm | Cap p95 / >2 cm |
|
||||
|---:|---:|---:|---:|
|
||||
| 0 | 73.48% | 0.78 cm / 0 of 2,178 | 1.32 cm / 25 of 2,178 |
|
||||
| 1 | 73.96% | 2.54 cm / 236 of 2,178 | 3.00 cm / 375 of 2,178 |
|
||||
| 3 | 71.99% | 388.62 cm / 405 of 578 | 37.70 cm / 397 of 578 |
|
||||
| 4 | 71.39% | 702.29 cm / 403 of 578 | 68.92 cm / 401 of 578 |
|
||||
| 5 | 71.27% | 1,248.85 cm / 411 of 578 | 87.74 cm / 395 of 578 |
|
||||
| 8 | 72.81% | 4,369.95 cm / 414 of 578 | 2,201.17 cm / 415 of 578 |
|
||||
|
||||
At stride two, exact column evaluations fell from 2,178 to 578 at LOD0–1 and from 578 to 162 at
|
||||
LOD3–8. The measured column-compute time reduction is shown above; it was 71–74%. Stride four
|
||||
reduced exact evaluations from 2,178 to 162 at LOD0–1 and from 578 to 50 at LOD3–8, roughly
|
||||
90–92%. It already gives LOD0 ground/cap p95 errors of 2.72/2.09 cm (293/2,178 and 123/2,178
|
||||
samples over 2 cm). At LOD1, stride two fails the
|
||||
2 cm threshold in both fields; coarse LOD errors are much larger. A near-zero exact fallback could
|
||||
recover surface crossings, but it was not simulated here and could erase much of the nominal saving.
|
||||
This probe does not exercise volumetric tunnel/wall thickness or the arrival-to-departure route
|
||||
law. No interpolation was enabled; exact sampled outputs, walkability, and route behavior are
|
||||
unchanged.
|
||||
|
||||
## 5. S12 worker-cap follow-up and current trace
|
||||
|
||||
The matched cap comparison kept `voxel.StreamingReadyQueueDepth=8` and changed only
|
||||
`voxel.StreamingBackgroundWorkerCap`. Both controls were recorded in the JSONL trace, as were the
|
||||
effective slots and the eight actual background worker count.
|
||||
|
||||
| Speed | Background cap | Effective slots / actual tile workers | Initial set ready | Spawn tick p95/max | Walk tick p95/max | Ring |
|
||||
|---:|---:|---:|---:|---:|---:|---:|
|
||||
| 6 m/s | 0 | 10 / 8 | 4.669 s | 3.483 / 5.184 ms | 2.708 / 28.581 ms | — |
|
||||
| 6 m/s | 1 | 8 / 8 | 4.745 s | 3.283 / 14.945 ms | 2.696 / 28.535 ms | — |
|
||||
| 40 m/s | 0 | 10 / 8 | 4.633 s | 3.455 / 5.468 ms | 32.345 / 55.115 ms | 75/75 |
|
||||
| 40 m/s | 1, matched | 8 / 8 | 4.712 s | 3.031 / 5.364 ms | 30.541 / 41.598 ms | 75/75 |
|
||||
| 40 m/s | 1, final build | 8 / 8 | 4.825 s | 3.133 / 5.426 ms | 30.314 / 43.142 ms | 75/75 |
|
||||
|
||||
Cap 0 gained only 76 ms at 6 m/s and about 79 ms in the matched 40 m/s pair, while its 40 m/s
|
||||
walk tick tail was substantially worse. The final cap-1 run confirms the ring at 75/75 gates. Keep
|
||||
cap 1 as the default; the latest tick p95/max is still 0.752/3.915 ms above Round 16's 29.562 /
|
||||
39.227 ms 40 m/s result, so the strict tick-tail target is not claimed.
|
||||
|
||||
On the final cap-1 40 m/s trace, the initial set contains 2,578 tiles and totals 28.760 worker-
|
||||
seconds. The worker-seconds by LOD were: LOD0 3.81, LOD1 3.94, LOD2 0.81, LOD3 2.36, LOD4 7.73,
|
||||
LOD5 1.68, LOD6 2.17, LOD7 1.97, LOD8 4.31. The ring was ready at 0.510 s; full initial set
|
||||
ready at 4.825 s. LOD0's last worker ended at 1.131 s and its last apply was at 1.146 s.
|
||||
|
||||
## Builds, quality, determinism, and files
|
||||
|
||||
Build commands:
|
||||
|
||||
```powershell
|
||||
.\Tools\BuildVoxelForgeCoreHost.ps1 -MaxParallelActions 12 -TimeoutSeconds 900
|
||||
.\Standalone\Build.ps1 -Target All -CompileWorkers 12 -BuildPriority Normal
|
||||
```
|
||||
|
||||
Measurement commands (each was a fresh process; BuildHost runs were serial, Normal priority, and
|
||||
limited to the 12-CPU affinity):
|
||||
|
||||
```powershell
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -SharedFeatureAuditOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 0 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 600 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 1 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 0 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -StreamingTraceOnly -StreamingOrder Legacy -NearReserveMode Dynamic -CoreVoxelSizeCm 25 -StreamingCollisionLookahead 1 -TestMoveDirection '0,1,0' -CoreRegionConcurrentAdmission 1 -StreamingResultBacklogCap 256 -TestMoveSpeedCmPerSecond 4000 -StreamingReadyQueueDepth 8 -StreamingBackgroundWorkerCap 1 -TimeoutSeconds 900
|
||||
```
|
||||
|
||||
The final Standalone commands used the same options for each worker count; W8 also enabled the
|
||||
all-LOD overlap capture:
|
||||
|
||||
```powershell
|
||||
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=1 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=8 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --profile-column-overlap --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||
.\Standalone\bin\VoxelForgeCoreStandalone.exe --workers=12 --world-assembler-prototype --stream-benchmark --stream-pipeline --stream-hash --force-generate --clip-radius=3 --max-clip-level=8 --camera=0,-35,-487.94,0,-22 --far-horizon-lod8-step=0 --process-priority=Normal --process-affinity=4095 --frame-limit=60
|
||||
```
|
||||
|
||||
Current-build compatibility gates:
|
||||
|
||||
```powershell
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -CoreOffTest -CoreVoxelSizeCm 12.5 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV1 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -HybridFixturesOnly -CoreMesherRecipe WorldAssemblerHybridV2 -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||
.\Tools\TestVoxelForgeCoreWorldHost.ps1 -FloatingIslandsOnly -CoreVoxelSizeCm 25 -TimeoutSeconds 900
|
||||
```
|
||||
|
||||
The current BuildHost SurfaceWorld actor run passed and reported stable W1/W8/W16 hashes:
|
||||
density/mesh/payload/topology `0cd2a1a4152ee4aa / a06243aee16b65d5 / c700162935c262d7 /
|
||||
0af901aa42b452bc`. The current BuildHost cap audit passed S4 agreement (128 points, max 0.0504 cm
|
||||
against 2 cm) and 1,584/1,584 cap rays (100%). The BuildHost actor run also passed S3 far-horizon
|
||||
agreement at max 0.0427 cm against 2 cm. Current Standalone W1/W8/W12 each matched full-view hashes:
|
||||
density/mesh/payload/topology `c10697a8f48380c7 / e75c9119a1498cd5 / 49a9a8d1835a6c8c /
|
||||
edcb23288b5e8724`. W8's final full-view pipeline was 3.518 s for 2,553/2,553 tiles.
|
||||
|
||||
The current-build Core-off gate passed `legacy_unchanged=PASS` at 12.5 cm. M2 Hybrid V1 and V2
|
||||
each passed all five analytic fixtures and the border suite at 25 cm. FloatingIslands passed at
|
||||
25 cm with stable W1/W8/W16 hashes `4dcfb37585d7d986 / f14f76fbf065c6ed / 53035c45fd1a082e /
|
||||
23550100654a4ec3`; its island and span capsule contacts, content anchors, and carve/fill checks
|
||||
passed. These runs used the current BuildHost binaries. No density output changed, so paired
|
||||
near/far captures and a recipe bump were not required. No runtime CVar or behavior change was
|
||||
introduced; the cap-0 negative controls above were trace-read back.
|
||||
|
||||
Full BuildHost logs are under
|
||||
`Saved/BuildHost/HostProject/Plugins/VoxelForge/Saved/CoreWorldValidation/Logs/` with stems
|
||||
`CoreWorldValidation-core_off_voxel_12.5_applyboost_default_legacy_order_nearreserve_auto`,
|
||||
`CoreWorldValidation-owner_surface_applyboost_default_legacy_order_nearreserve_auto_worldassemblerhybridv1`,
|
||||
`CoreWorldValidation-owner_surface_applyboost_default_legacy_order_nearreserve_auto_worldassemblerhybridv2`,
|
||||
and `CoreWorldValidation-floating_islands_applyboost_default_legacy_order_nearreserve_auto`.
|
||||
|
||||
Current binary SHA-256:
|
||||
|
||||
| Binary | SHA-256 |
|
||||
|---|---|
|
||||
| BuildHost `UnrealEditor-VoxelForge.dll` | `811F83D43EDE47EE870DD608F25C9F902422E1B264901917726CDBECE0EC0A37` |
|
||||
| BuildHost `UnrealEditor-VoxelForgeCore.dll` | `AA74A8BC8E1C9C59FB7EA94D5A6BA71341EF8208EDE2458A7B5FAC0045FF2520` |
|
||||
| BuildHost `UnrealEditor-VoxelForgeEditor.dll` | `A851836ED435E8795AF860EBFB49AD84B603FF0D94600637D9B8CE52E0710EE3` |
|
||||
| Standalone `VoxelForgeCoreStandalone.exe` | `704F433791AF0D483D082CCBB2043DB4194323F40A986DA72F338FC424BF82F3` |
|
||||
|
||||
Round 17 changed these existing source files; no new source file was added:
|
||||
|
||||
| File | Likely compile spot |
|
||||
|---|---|
|
||||
| `Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h`, `Standalone/Core/src/VoxelForgeCore.cpp`, `Standalone/Core/src/WorldAssemblerInternal.h` | BuildHost VoxelForgeCore and Standalone Core |
|
||||
| `Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp` | BuildHost VoxelForgeEditor commandlet |
|
||||
| `Standalone/Source/StreamingWorld.h`, `Standalone/Source/StandaloneViewer.cpp` | Standalone viewer executable |
|
||||
|
||||
Async-safety review: no actor queues or worker mutation code changed. `Epoch`, MPSC queue modes,
|
||||
`EndPlay` draining, worker `bShuttingDown` checks, `ModsLock`, and shared region-page lifetime are
|
||||
unchanged. Coordinate capture is opt-in thread-local diagnostic data copied into each tile result;
|
||||
UObject and RMC mutation remains on the game thread. RealtimeMeshComponent was read-only. No
|
||||
`.uproject`, owner editor, `.uasset`, generated header, `Binaries/`, or `Intermediate/` source was
|
||||
edited. Files over 300 KB are under `Saved/BuildHost/`. No `0xC0000142` occurred and no new
|
||||
`UECC-*` crash folder appeared.
|
||||
|
||||
The first BuildHost compile failed because two probe lambdas omitted the configured tile-size
|
||||
capture; that was fixed and the next build passed. The Standalone build passed with the existing
|
||||
`vswhere.exe` lookup warning and `VoxelStrateManager.cpp` C4838 warnings. A final source check and
|
||||
both current targets passed. No commit, push, or stash.
|
||||
|
||||
### Dialogue
|
||||
|
||||
**User:** Shouldn't a tile be way cheaper? Measure the stages and explain LOD4 before choosing a fix.
|
||||
|
||||
**Codex:** The largest measured cost is XY column evaluation, and LOD4 has a sharp spike in cache
|
||||
misses plus its first fine cap pass. Two-step interpolation reduced measured column-compute time by
|
||||
roughly 72%, but moved far ground by tens of meters; global octave dropping also fails badly.
|
||||
Cross-worker column
|
||||
reuse is a strong candidate for a future measured page-cache experiment, so Round 17 keeps output
|
||||
unchanged and records the floor instead of shipping an unproven approximation.
|
||||
@@ -6976,7 +6976,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
|
||||
VoxelForgeCore::DetailVariant DetailVariant,const VoxelForgeCore::JobScheduler& Scheduler,
|
||||
bool bHashDensity,bool bSkipUvAndColors,bool bSkipMaterialGroups,bool bSkipUV,bool bSkipColors,
|
||||
std::chrono::steady_clock::time_point ColdStart,bool bProfileFarColumnOverlap,
|
||||
std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap,
|
||||
int32 FarHorizonLOD8InteriorStepVoxels)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
@@ -7045,7 +7045,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const auto ShapeReady=Clock::now();
|
||||
const double ShapeSeconds=std::chrono::duration<double>(ShapeReady-ShapeBuildStart).count();
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileFarColumnOverlap,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
|
||||
FarHorizonLOD8InteriorStepVoxels);
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired);
|
||||
@@ -7078,19 +7078,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
FVoxelTileKey Tile;
|
||||
std::uint64_t WorkerHash=0;
|
||||
};
|
||||
std::unordered_map<FColumnXY,FFirstColumnUse,FColumnXYHash> LOD8ColumnFirstUse;
|
||||
std::uint64_t LOD8ColumnDuplicateEvents=0,LOD8NeighbourDuplicateEvents=0;
|
||||
std::uint64_t LOD8CrossWorkerNeighbourDuplicateEvents=0;
|
||||
std::size_t LOD8ColumnCaptureTiles=0;
|
||||
bool bLOD8ColumnCaptureComplete=true;
|
||||
std::array<std::unordered_map<FColumnXY,std::vector<FFirstColumnUse>,FColumnXYHash>,9> ColumnUsesByLOD;
|
||||
std::array<std::uint64_t,9> ColumnDuplicateEventsByLOD{},NeighbourDuplicateEventsByLOD{};
|
||||
std::array<std::uint64_t,9> CrossWorkerNeighbourDuplicateEventsByLOD{};
|
||||
std::array<std::uint64_t,9> ColumnCaptureTilesByLOD{},ExpectedTilesByLOD{};
|
||||
std::array<bool,9> ColumnCaptureCompleteByLOD{};
|
||||
ColumnCaptureCompleteByLOD.fill(true);
|
||||
std::array<double,9> WorkerSecondsByLOD{};
|
||||
std::array<double,9> ColumnEvalMsByLOD{},MarchingExclusiveMsByLOD{};
|
||||
std::array<double,9> GroundRefineExclusiveMsByLOD{},CapRefineExclusiveMsByLOD{};
|
||||
std::array<double,9> MeshAssemblyMsByLOD{};
|
||||
std::array<std::uint64_t,9> TileCountByLOD{};
|
||||
const auto AreLOD8Neighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B)
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
if(Tile.Level>=0&&Tile.Level<static_cast<int32>(ExpectedTilesByLOD.size()))
|
||||
++ExpectedTilesByLOD[static_cast<std::size_t>(Tile.Level)];
|
||||
const auto AreSameLODNeighbours=[](const FVoxelTileKey& A,const FVoxelTileKey& B)
|
||||
{
|
||||
if(A.Level!=8||B.Level!=8)return false;
|
||||
if(A.Level!=B.Level)return false;
|
||||
const std::int64_t DX=std::abs(static_cast<std::int64_t>(A.Coord.X)-B.Coord.X);
|
||||
const std::int64_t DY=std::abs(static_cast<std::int64_t>(A.Coord.Y)-B.Coord.Y);
|
||||
const std::int64_t DZ=std::abs(static_cast<std::int64_t>(A.Coord.Z)-B.Coord.Z);
|
||||
@@ -7122,10 +7126,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
CapRefineExclusiveMsByLOD[LOD]+=Tile.CoreDiagnostics.capRefinementExclusiveMilliseconds;
|
||||
MeshAssemblyMsByLOD[LOD]+=Tile.CoreDiagnostics.meshAssemblyMilliseconds;
|
||||
}
|
||||
if(bProfileFarColumnOverlap&&Tile.Key.Level==8)
|
||||
if(bProfileColumnOverlap&&Tile.Key.Level>=0
|
||||
&&Tile.Key.Level<static_cast<int32>(ColumnCaptureTilesByLOD.size()))
|
||||
{
|
||||
++LOD8ColumnCaptureTiles;
|
||||
bLOD8ColumnCaptureComplete=bLOD8ColumnCaptureComplete
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
++ColumnCaptureTilesByLOD[LOD];
|
||||
ColumnCaptureCompleteByLOD[LOD]=ColumnCaptureCompleteByLOD[LOD]
|
||||
&&Tile.CoreDiagnostics.columnCoordinateCaptureComplete;
|
||||
std::vector<std::array<std::int64_t,2>> Coordinates=
|
||||
Tile.CoreDiagnostics.evaluatedColumnCoordinates;
|
||||
@@ -7134,16 +7140,26 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
for(const auto& Coordinate:Coordinates)
|
||||
{
|
||||
const FColumnXY XY{Coordinate[0],Coordinate[1]};
|
||||
const auto [First,Inserted]=LOD8ColumnFirstUse.try_emplace(XY,
|
||||
FFirstColumnUse{Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
|
||||
if(Inserted)continue;
|
||||
++LOD8ColumnDuplicateEvents;
|
||||
if(AreLOD8Neighbours(First->second.Tile,Tile.Key))
|
||||
auto [Uses,Inserted]=ColumnUsesByLOD[LOD].try_emplace(XY);
|
||||
std::vector<FFirstColumnUse>& PriorUses=Uses->second;
|
||||
if(Inserted)
|
||||
{
|
||||
++LOD8NeighbourDuplicateEvents;
|
||||
if(First->second.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash)
|
||||
++LOD8CrossWorkerNeighbourDuplicateEvents;
|
||||
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
|
||||
continue;
|
||||
}
|
||||
++ColumnDuplicateEventsByLOD[LOD];
|
||||
bool bHasNeighbourDuplicate=false,bHasCrossWorkerNeighbourDuplicate=false;
|
||||
for(const FFirstColumnUse& Prior:PriorUses)
|
||||
{
|
||||
if(!AreSameLODNeighbours(Prior.Tile,Tile.Key))continue;
|
||||
bHasNeighbourDuplicate=true;
|
||||
bHasCrossWorkerNeighbourDuplicate=bHasCrossWorkerNeighbourDuplicate
|
||||
||Prior.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash;
|
||||
}
|
||||
if(bHasNeighbourDuplicate)++NeighbourDuplicateEventsByLOD[LOD];
|
||||
if(bHasCrossWorkerNeighbourDuplicate)
|
||||
++CrossWorkerNeighbourDuplicateEventsByLOD[LOD];
|
||||
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
|
||||
}
|
||||
}
|
||||
WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
|
||||
@@ -7191,14 +7207,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
}
|
||||
Streamer.Stop();
|
||||
if(bProfileFarColumnOverlap)
|
||||
bool bColumnOverlapCapturePassing=true;
|
||||
if(bProfileColumnOverlap)
|
||||
{
|
||||
std::printf("[WorldAssemblerLOD8ColumnOverlap] tiles=%zu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
|
||||
LOD8ColumnCaptureTiles,LOD8ColumnFirstUse.size(),
|
||||
static_cast<unsigned long long>(LOD8ColumnDuplicateEvents),
|
||||
static_cast<unsigned long long>(LOD8NeighbourDuplicateEvents),
|
||||
static_cast<unsigned long long>(LOD8CrossWorkerNeighbourDuplicateEvents),
|
||||
bLOD8ColumnCaptureComplete?1:0);
|
||||
for(std::size_t LOD=0;LOD<ColumnCaptureTilesByLOD.size();++LOD)
|
||||
{
|
||||
const bool bLODComplete=ColumnCaptureCompleteByLOD[LOD]
|
||||
&&ColumnCaptureTilesByLOD[LOD]==ExpectedTilesByLOD[LOD];
|
||||
bColumnOverlapCapturePassing=bColumnOverlapCapturePassing&&bLODComplete;
|
||||
std::printf("[WorldAssemblerLODColumnOverlap] lod=%zu tiles=%llu expected_tiles=%llu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
|
||||
LOD,static_cast<unsigned long long>(ColumnCaptureTilesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(ExpectedTilesByLOD[LOD]),
|
||||
ColumnUsesByLOD[LOD].size(),
|
||||
static_cast<unsigned long long>(ColumnDuplicateEventsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(NeighbourDuplicateEventsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(CrossWorkerNeighbourDuplicateEventsByLOD[LOD]),
|
||||
bLODComplete?1:0);
|
||||
}
|
||||
}
|
||||
for(std::size_t LOD=0;LOD<TileCountByLOD.size();++LOD)
|
||||
{
|
||||
@@ -7253,7 +7278,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
return NeighborhoodFinished==static_cast<int32>(Neighborhood.size())
|
||||
&&FullDetailFinished==FullDetailExpected&&Finished==Update.DesiredTiles&&bHashesPassing
|
||||
&&(!bProfileFarColumnOverlap||(bLOD8ColumnCaptureComplete&&LOD8ColumnCaptureTiles>0))?0:6;
|
||||
&&(!bProfileColumnOverlap||bColumnOverlapCapturePassing)?0:6;
|
||||
}
|
||||
int RunStreamingBenchmark(FStandaloneOwnerWorld& World,VoxelForgeCore::World* CoreWorld,
|
||||
const FVector& CameraPositionVoxels,
|
||||
@@ -9601,7 +9626,7 @@ int main(int Argc,char** Argv)
|
||||
std::string WorldAssemblyManifestOutputPath;
|
||||
bool bStreamBenchmark=false;
|
||||
bool bStreamPipeline=false;
|
||||
bool bProfileFarColumnOverlap=false;
|
||||
bool bProfileColumnOverlap=false;
|
||||
bool bCoarseFirstStreamOrder=false;
|
||||
bool bProfileDetailBand=false;
|
||||
bool bPassageDetailAuditOnly=false;
|
||||
@@ -9739,8 +9764,8 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg=="--coarse-first-stream-order")bCoarseFirstStreamOrder=true;
|
||||
else if(Arg=="--stream-pipeline")
|
||||
{bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
|
||||
else if(Arg=="--profile-far-column-overlap")
|
||||
{bProfileFarColumnOverlap=true;bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
|
||||
else if(Arg=="--profile-column-overlap"||Arg=="--profile-far-column-overlap")
|
||||
{bProfileColumnOverlap=true;bStreamPipeline=true;bStreamBenchmark=true;App.bShapeWorld=true;App.bForceGenerate=true;}
|
||||
else if(Arg=="--edit-benchmark")
|
||||
{bEditBenchmark=true;bStreamBenchmark=true;App.bShapeWorld=true;}
|
||||
else if(Arg=="--stream-hash")bStreamHash=true;
|
||||
@@ -9957,7 +9982,7 @@ int main(int Argc,char** Argv)
|
||||
FVector(App.Position.X*4.0,App.Position.Y*4.0,App.Position.Z*4.0),
|
||||
App.WorkerCount,StreamBenchmarkMaxLevel,App.ShapeDetailVariant,App.CoreScheduler,
|
||||
bStreamHash||!BakeMeshCachePath.empty(),bSkipUvAndColors,bSkipMaterialGroups,
|
||||
bSkipUV,bSkipColors,ColdProcessStart,bProfileFarColumnOverlap,
|
||||
bSkipUV,bSkipColors,ColdProcessStart,bProfileColumnOverlap,
|
||||
App.FarHorizonLOD8InteriorStepVoxels);
|
||||
if(bEditBenchmark)
|
||||
return RunEditTransactionBenchmark(*App.World,*App.ShapeWorld,
|
||||
|
||||
@@ -424,13 +424,13 @@ public:
|
||||
bool bInSkipUV=false,
|
||||
bool bInSkipColors=false,
|
||||
VoxelForgeCore::World* InCoreWorld=nullptr,
|
||||
bool bInCaptureFarColumnCoordinates=false,
|
||||
bool bInCaptureColumnCoordinates=false,
|
||||
int32 InLod8HorizonInteriorStepVoxels=0)
|
||||
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
|
||||
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
|
||||
CoreWorld(InCoreWorld),bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
|
||||
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors),
|
||||
bCaptureFarColumnCoordinates(bInCaptureFarColumnCoordinates),
|
||||
bCaptureColumnCoordinates(bInCaptureColumnCoordinates),
|
||||
Lod8HorizonInteriorStepVoxels(std::max(0,InLod8HorizonInteriorStepVoxels))
|
||||
{
|
||||
VoxelGenLOD::InitializeConsoleSwitches();
|
||||
@@ -799,7 +799,7 @@ private:
|
||||
Options.bandMinZVoxel=Job.BandZMin;Options.bandMaxZVoxel=Job.BandZMax;
|
||||
Options.hashDensity=bHashDensity;Options.skipUvAndColors=bSkipUvAndColors;
|
||||
Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors;
|
||||
Options.captureColumnCoordinates=bCaptureFarColumnCoordinates&&Level==8;
|
||||
Options.captureColumnCoordinates=bCaptureColumnCoordinates;
|
||||
Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels;
|
||||
std::string Error;
|
||||
auto Result=CoreWorld->generateTile(
|
||||
@@ -971,7 +971,7 @@ private:
|
||||
bool bSkipMaterialGroups=false;
|
||||
bool bSkipUV=false;
|
||||
bool bSkipColors=false;
|
||||
bool bCaptureFarColumnCoordinates=false;
|
||||
bool bCaptureColumnCoordinates=false;
|
||||
int32 Lod8HorizonInteriorStepVoxels=0;
|
||||
std::atomic<uint64> CacheHitCount{0};
|
||||
mutable std::mutex Mutex;
|
||||
|
||||
Reference in new issue
Block a user