wip(standalone): GPU step 20 -- detail band batched by 8 columns; gap instrumented
ApplyDetailBand claims eight adjacent columns per batch, keeping each column's scalar order: shape stage ~586 -> ~566 ms in Luna's profile. Hashes w1/w8 identical. My runs outside the sandbox (Step20.exe, marker removed first, interleaved): CPU 1.319 / 1.287 / 1.288 s GPU 1.320 (marker miss, 0 GPU tiles) / 1.244 / 1.215 / 1.219 s (match, ~1,400 GPU) Shape compile 549-570 ms in both modes. The 669-821 ms gap in cached runs is still there: export starts only after [ChunkCrossingWork] reports the ~840 CPU tiles (LOD0-2) finished, so GPU export waits behind CPU meshing on the shared workers. Slowest GPU tile 1.7 / 18.4 / 27.3 ms in runs b/c/d. Audits: hashes, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and WARP, thin 353, survival 6.14 GB, ten captures as step 18. Far vertex-distance diagnostic: 247 bound failures (max 64.56 cm), unchanged since step 16. 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
89fd9a0021
commit
2cb3344e1b
6 files changed
+146
-18
No files matched your search
@@ -3390,7 +3390,9 @@ private:
|
||||
CandidateCount.fetch_add(LocalCandidates,std::memory_order_relaxed);
|
||||
ProxyCount.fetch_add(LocalProxies,std::memory_order_relaxed);
|
||||
FlipCount.fetch_add(LocalFlips,std::memory_order_relaxed);
|
||||
},&Timing,&WorkerActiveSeconds);
|
||||
// Each X/Y column is independent. Claim eight adjacent columns at a time to
|
||||
// reduce scheduler atomics while preserving per-column scalar evaluation order.
|
||||
},&Timing,&WorkerActiveSeconds,8);
|
||||
Stats.SurfaceCandidates=CandidateCount.load(std::memory_order_relaxed);
|
||||
Stats.SurfaceProxyNodes=ProxyCount.load(std::memory_order_relaxed);
|
||||
Stats.DetailFlips=FlipCount.load(std::memory_order_relaxed);
|
||||
|
||||
@@ -2,14 +2,25 @@
|
||||
|
||||
All commands run from `Standalone\` or the stated script path; runners set BelowNormal priority, affinity `0xFF00`, `/MP8` builds, an 8 GB process ceiling, and one retry for `0xC0000142` where applicable.
|
||||
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\BuildGpuFieldViewer.ps1 -OutputName VoxelForgeGpuFarStep19Stream.exe` — builds the standalone viewer; pass when the build exits 0 and prints `CORE_LINK_SAFETY_PASS`.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\BuildGpuFieldPort.ps1 -OutputName VoxelForgeGpuFarAgreementStep19.exe` — builds the hardware/WARP agreement runner; pass when build and Core symbol checks exit 0.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -WarmupPolicy auto -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep19Stream.exe` — measures the cached whole-view GPU path and export overlap; pass when `[InitialClipmapReady]` appears, GPU submissions are nonzero with zero input failures, exit is 0, and private plus DXGI-local memory stays below 8 GB. A matching app marker alone does not establish a cached driver queue.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarAgreement.ps1 -ExecutableName VoxelForgeGpuFarAgreementStep19.exe -WholeNear` (repeat with `-WholeFar`) — compares CPU, hardware GPU, and WARP; pass when `overall_port` and `topology_protected_status` are `PASS`, whole-far audits nonzero thin-feature cells, and protected/thin topology difference counts are zero. `-FindThinFeature` is a targeted sample selector and may select a featureless tile.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15CpuReference.ps1 -ExecutableName VoxelForgeGpuFarStep19Stream.exe` — checks CPU W1/W8 determinism; pass when all four hashes match, 2,226/2,226 tiles generate, and walkability is 5,642/5,642.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15GpuSurvival.ps1 -ExecutableName VoxelForgeGpuFarStep19Stream.exe` — checks GPU mesh survival and required field readback at full residency; pass when `[GpuFarSurvivalAudit]` is `PASS`, `[InitialClipmapReady]` appears, exit is 0, and private plus DXGI-local memory stays below 8 GB.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15CaptureMatrix.ps1 -ExecutableName VoxelForgeGpuFarStep19Stream.exe -Prefix <run> -GpuWarmupBeforeWorkers` then `python Research\OwnerShapeWorld\gpu_field_port\CompareStep15Captures.py --prefix <run>` — captures ten full-residency CPU/GPU view pairs and compares RGB arrays numerically; pass when all captures exist, each GPU log reports GPU submissions, memory stays below 8 GB, and CPU-rock-to-GPU-background counts match the Step 18 baseline (0 in eight views, 2 in each of `remote_yaw180` and `remote_yaw270`).
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15Timeline.ps1 -Mode Race -ExecutableName VoxelForgeGpuFarStep19Stream.exe` — runs interleaved CPU/GPU launch timing; pass when each run reports whole-view ready and present markers with exit 0 and memory below 8 GB.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep17TileControls.ps1 -ExecutableName VoxelForgeGpuFarStep19Stream.exe -Prefix <run>` — audits pooled LOD3 output, indirect draw arguments, and CPU/GPU triangles; pass when output indices and draw args are valid, surface/skirt triangles match, and memory stays below 8 GB.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\BuildGpuFieldViewer.ps1 -OutputName VoxelForgeGpuFarStep20.exe` — builds the standalone viewer; pass when the build exits 0 and prints `CORE_LINK_SAFETY_PASS`.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\BuildGpuFieldPort.ps1 -OutputName VoxelForgeGpuFarAgreementStep20.exe` — builds the hardware/WARP agreement runner; pass when build and Core symbol checks exit 0.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -WarmupPolicy cold -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep20.exe -LogStem step20_forced_cold` — forces warm-up after the CPU view; pass when `[GpuWarmupLaunchDecision]` reports `forced_cold_start`, the CPU whole view is ready and presented, and the deferred warm-up handoff reports `PASS`. With `--quit-after-clipmap`, GPU takeover may be zero because all candidates were already generated on CPU.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -WarmupPolicy cached -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep20.exe -LogStem step20_forced_cached` — forces warm-up to overlap startup; pass when `[GpuWarmupLaunchDecision]` reports `forced_cached_start` and the whole view becomes ready. GPU submissions can be zero if the driver warm-up queue is still cold; this forces the application route and does not prove the driver queue is cached.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -WarmupBeforeWorkers -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep20.exe -LogStem step20_gpu_takeover` — drains the shader warm-up queue before generation workers; pass when warm-up reports completion before workers start, GPU submissions are nonzero, there are zero input failures, and private plus DXGI-local memory stays below 8 GB. Its wall time includes cold shader warm-up and is a route/parity control, not cached-launch timing.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -NoPrewarm -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep20.exe -LogStem step20_direct_gpu` — starts GPU input export without the app warm-up queue to expose dispatch scheduling and per-tile timing; pass when the whole view is ready, submissions are nonzero with zero input failures, and private plus DXGI-local memory stays below 8 GB. Treat its timing as a cold-dispatch diagnostic.
|
||||
- The timeline reports export dispatch and worker start separately. `[GpuFarMaxDispatchTile]` reports the tile key and timestamp for the slowest GPU tile in that run.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarViewer.ps1 -Mode gpu -WarmupPolicy auto -NoColdDrain -Timeline -ExecutableName VoxelForgeGpuFarStep20.exe -LogStem step20_auto` — measures the app-marker route and export overlap; use it for cached-launch timing only when the warm-up marker matches and the driver's cached state is independently known.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunGpuFarAgreement.ps1 -ExecutableName VoxelForgeGpuFarAgreementStep20.exe -WholeNear` (repeat with `-WholeFar`) — compares CPU, hardware GPU, and WARP; pass when `overall_port` and `topology_protected_status` are `PASS`, whole-far audits nonzero thin-feature cells, and protected/thin topology difference counts are zero. `-FindThinFeature` is a targeted sample selector and may select a featureless tile.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15CpuReference.ps1 -ExecutableName VoxelForgeGpuFarStep20.exe -LogPrefix step20_cpu_reference` — checks CPU W1/W8 determinism; pass when all four hashes match, 2,226/2,226 tiles generate, and walkability is 5,642/5,642.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15GpuSurvival.ps1 -ExecutableName VoxelForgeGpuFarStep20.exe` — checks GPU mesh survival and required field readback at full residency; pass when `[GpuFarSurvivalAudit]` is `PASS`, `[InitialClipmapReady]` appears, exit is 0, and private plus DXGI-local memory stays below 8 GB.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15CaptureMatrix.ps1 -ExecutableName VoxelForgeGpuFarStep20.exe -Prefix <run> -GpuWarmupBeforeWorkers` then `python Research\OwnerShapeWorld\gpu_field_port\CompareStep15Captures.py --prefix <run>` — captures ten full-residency CPU/GPU view pairs and compares RGB arrays numerically; pass when all captures exist, each GPU log reports GPU submissions, memory stays below 8 GB, and CPU-rock-to-GPU-background counts match the Step 18 baseline (0 in eight views, 2 in each of `remote_yaw180` and `remote_yaw270`).
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep15Timeline.ps1 -Mode Race -ExecutableName VoxelForgeGpuFarStep20.exe` — runs interleaved CPU/GPU launch timing; pass when each run reports whole-view ready and present markers with exit 0 and memory below 8 GB.
|
||||
- `Research\OwnerShapeWorld\gpu_field_port\RunStep17TileControls.ps1 -ExecutableName VoxelForgeGpuFarStep20.exe -Prefix <run>` — audits pooled LOD3 output, indirect draw arguments, and CPU/GPU triangles; pass when output indices and draw args are valid, surface/skirt triangles match, and memory stays below 8 GB.
|
||||
|
||||
The numeric capture comparison report for this step is [step19_final_capture_comparison.md](step19_final_capture_comparison.md). The cached-launch timing in this sandbox is not representative while the GPU warm-up queue remains cold; see [STEP19_REPORT.md](STEP19_REPORT.md).
|
||||
Step 20's measured shape profile found `ApplyDetailBand` was the largest phase. Its line scheduler now claims eight adjacent X/Y columns per batch; the per-column evaluation order is unchanged. One W8 profile run measured detail-band wall time 365.241 -> 337.967 ms and whole shape stage 585.702 -> 565.952 ms. Recheck timings across runs because the final W8 CPU reference measured 592.622 ms.
|
||||
|
||||
Step 19's apparent 698-850 ms idle gap was an instrumentation gap. Its `ChunkCrossingWork` report was emitted at the end of CPU stream work, with 830 CPU tiles generated and 1,396 queued jobs canceled for GPU ownership; it reports 0.780 s wall and 1.379 s worker time from the earlier generation start. Step 20 logs pool completion, export dispatch, and worker start independently. In the no-prewarm diagnostic, pool-end-to-dispatch was 0.117 ms and dispatch-to-thread-start was 28.395 ms.
|
||||
|
||||
The no-prewarm diagnostic took 2.058 s to whole view and started GPU submission at 1.047 s. Its slowest tile was `(-3,-14,-5,lod0)` at 25.901 ms. Step 19 run b's 15.254 ms tile cannot be identified from its old log because it did not retain the tile key. Step 20 records that key for future runs.
|
||||
|
||||
The numeric capture comparison reports are [Step 19](step19_final_capture_comparison.md) and [Step 20](step20final_capture_comparison.md). The sandbox forced routes exercise both startup paths but do not establish that the external driver's queue is cached; the forced-cached control still had a cold driver queue and zero GPU submissions. Use the auto command for the owner's cached-path verification. See [STEP20_REPORT.md](STEP20_REPORT.md) for the full findings and audit results.
|
||||
@@ -8,6 +8,7 @@ param(
|
||||
[switch]$WarmupBeforeWorkers,
|
||||
[switch]$Timeline,
|
||||
[switch]$NoColdDrain,
|
||||
[switch]$NoPrewarm,
|
||||
[ValidateSet('auto','cold','cached')][string]$WarmupPolicy='auto'
|
||||
)
|
||||
|
||||
@@ -31,6 +32,7 @@ else{$Arguments+='--gpu-far-audit'}
|
||||
if($Mode-eq'gpu' -and $WarmupPolicy-eq'cold'){$Arguments+='--gpu-far-force-cold-start'}
|
||||
elseif($Mode-eq'gpu' -and $WarmupPolicy-eq'cached'){$Arguments+='--gpu-far-force-cached-start'}
|
||||
if($Mode-eq'gpu'-and$WarmupBeforeWorkers){$Arguments+='--gpu-far-warmup-before-workers'}
|
||||
if($Mode-eq'gpu'-and$NoPrewarm){$Arguments+='--gpu-far-no-prewarm'}
|
||||
if($Timeline){$Arguments+='--gpu-far-timeline'}
|
||||
if($Mode-eq'gpu'-and$NoColdDrain){$Arguments+='--gpu-far-no-cold-drain'}
|
||||
if($CaptureName)
|
||||
@@ -41,7 +43,7 @@ if($CaptureName)
|
||||
$Arguments+=("--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
||||
}
|
||||
$Command="`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||
Write-Output "VIEWER_COMMAND mode=$Mode timeline=$([bool]$Timeline) $Command"
|
||||
Write-Output "VIEWER_COMMAND mode=$Mode timeline=$([bool]$Timeline) no_prewarm=$([bool]$NoPrewarm) $Command"
|
||||
if($Mode-eq'gpu'){Write-Output "WARMUP_POLICY requested=$WarmupPolicy state_file=bin\VoxelForgeGpuWarmup.state"}
|
||||
|
||||
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# GPU field port — Step 20 report
|
||||
|
||||
Step 20 instruments the post-pool export handoff and slowest GPU tile, and batches detail-band work across adjacent columns. Builds, CPU reference checks, WholeNear/WholeFar agreement, survival audit, and the ten-view capture matrix completed.
|
||||
|
||||
## Idle gap and input export
|
||||
|
||||
Step 19 run d has `gpu_output_pool` ending at 698.010 ms and `gpu_input_export` beginning at 849.617 ms. The `ChunkCrossingWork` record appears immediately before export and reports 830 CPU tiles generated, 1,396 queued CPU jobs canceled for GPU ownership, 0.780 s wall time, and 1.379 s summed worker time. The gap was uninstrumented CPU stream/ownership work, not an intentional sleep.
|
||||
|
||||
Step 20 reports the output-pool end, export-thread dispatch, and worker start separately. In the final no-prewarm run: pool ended at 754.531 ms, dispatch followed at 754.652 ms (0.117 ms later), and the worker started at 783.047 ms (28.395 ms after dispatch). It prepared 1,434 inputs in 248.520 ms; the first input was ready at 931.548 ms. So there is little pool/dispatch overhead. CPU input preparation and cold GPU pumping dominate the later start.
|
||||
|
||||
The preparation coordinator now sets its relative thread priority to Normal while the process remains BelowNormal. The measured 28.395 ms dispatch-to-start interval shows that this did not remove the startup delay.
|
||||
|
||||
## Shape compile
|
||||
|
||||
Profiling put `surface-band` at roughly 353–381 ms of the W8 shape stage, with about 9.39 million candidates and 2.76 million detail-noise evaluations. `ApplyDetailBand` now claims eight adjacent X/Y columns per scheduler batch. Each column retains its original scalar evaluation order.
|
||||
|
||||
| W8 profile | Detail-band wall | Surface-band stage | Whole shape stage |
|
||||
|---|---:|---:|---:|
|
||||
| Step 20 baseline build | 365.241 ms | 380.776 ms | 585.702 ms |
|
||||
| Eight-column batch candidate | 337.967 ms | 353.716 ms | 565.952 ms |
|
||||
|
||||
The candidate profile saved 27.274 ms in the detail-band wall and 19.750 ms in total shape time. A later W8 reference run measured 592.622 ms, so the end-to-end gain is modest and timing still varies across runs. W1 measured 2,021.160 ms.
|
||||
|
||||
W1 and W8 produced identical hashes: density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, topology `0xbea22e76b34a4dfc`. Both generated 2,226/2,226 tiles and passed walkability 5,642/5,642.
|
||||
|
||||
## GPU start and tile timing
|
||||
|
||||
The no-prewarm diagnostic reached the whole view in 2.058 s, submitted 1,434 GPU tiles, and accumulated 271.503 ms of GPU timestamps. First submission was at 1,047.480 ms. The slowest tile was `(-3,-14,-5,lod0)` at 25.901 ms. The first cold pump used 581.382 ms of CPU time. Private memory was 3.60 GiB and DXGI-local memory peaked at 2,667.4 MB (about 6.21 GiB combined). This cold route is not a cached-launch measurement.
|
||||
|
||||
On the warmup-before-workers route, the marker matched, first submission was at 1,568.152 ms, and whole-view readiness was 1.927 s. Its slowest tile was `(1,-6,-5,lod1)` at 1.763 ms; maximum pump CPU time was 36.791 ms. The warmed route had no comparable 15 ms tile.
|
||||
|
||||
The sandbox's forced-cached route still had a cold driver queue (1,285 ms queue time), submitted zero GPU tiles, and reached the view in 2.216 s. The ≤1.0 s cached target is therefore not verified here. The forced-cold CPU view completed in 1.325 s, consistent with the owner's 1.30–1.38 s CPU runs. Use the auto route in `RUNNERS.md` to verify on the externally warmed driver.
|
||||
|
||||
Step 19 run b's 15.254 ms dispatch cannot be traced to a tile because that log did not record the key. Step 20 now records the slowest tile key on each measured run. The cold/warm contrast points to transient startup or dispatch cost as a possible cause, but does not identify run b's tile.
|
||||
|
||||
## Correctness and survival
|
||||
|
||||
- WholeNear: 960 tiles; 229,376 protected-exception cells; zero hardware/WARP sign or topology differences.
|
||||
- WholeFar: 1,114 tested tiles (152 near-transition tiles skipped); 127,401 protected-exception cells and 353 thin-feature cells; zero hardware/WARP sign or topology differences.
|
||||
- The separate WholeFar vertex-distance diagnostic reports 247 bound failures across LOD6/7/8 (46/48/153; maximum 64.560 cm). Step 16, 18, and 19 logs report the same counts and distribution, so this is unchanged by Step 20. The sign and topology audits pass.
|
||||
- GPU survival audit: PASS at full residency; peak private memory 3.54 GB plus 2,670.8 MB DXGI-local, 6.14 GB conservatively combined.
|
||||
- All ten CPU/GPU captures reached full residency and submitted GPU tiles. Peak combined memory was 6.17 GB. Numeric comparison matches the Step 18 missing-coverage signature: zero CPU-rock-to-GPU-background pixels in eight views and two each in `remote_yaw180` and `remote_yaw270`. Images were compared as RGB arrays only.
|
||||
|
||||
## Artifacts
|
||||
|
||||
- [RUNNERS.md](RUNNERS.md) — Step 20 commands and route notes.
|
||||
- [step20final_capture_comparison.md](step20final_capture_comparison.md) — numeric ten-view comparison.
|
||||
- [CPU W1 reference log](step20_priority_cpu_reference_w1.stdout.log) and [CPU W8 reference log](step20_priority_cpu_reference_w8.stdout.log).
|
||||
- [WholeNear agreement log](step20_whole_near.stdout.log), [WholeFar agreement log](step20_whole_far.stdout.log), [survival log](step20_gpu_survival.stdout.log), and [no-prewarm timing log](step20_final_direct_gpu.stdout.log).
|
||||
- Builds: `VoxelForgeGpuFarStep20.exe` and `VoxelForgeGpuFarAgreementStep20.exe`.
|
||||
|
||||
No commit, push, or stash was made.
|
||||
@@ -0,0 +1,18 @@
|
||||
# Step20Final full-residency capture comparison
|
||||
|
||||
All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. The files were compared numerically as RGB arrays and were not opened in an image viewer.
|
||||
|
||||
`CPU rock to GPU background` isolates the prior missing-coverage signature. Captures were loaded as RGB arrays only; no full-size image was opened in a viewer.
|
||||
|
||||
| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel | CPU rock to GPU background | GPU rock to CPU background | Missing-pixel bounds |
|
||||
|---|---:|---:|---:|---:|---:|---:|---:|---:|---|
|
||||
| chamber | 1280x800 | 0.6075 | 1.8638 | 42.6368% | 0.0183% | 123 | 0 | 0 | none |
|
||||
| tunnel | 1280x800 | 0.6383 | 3.3799 | 31.7335% | 0.2772% | 130 | 0 | 0 | none |
|
||||
| passage | 1280x800 | 1.6092 | 3.6878 | 50.4982% | 0.1733% | 52 | 0 | 0 | none |
|
||||
| far | 1280x800 | 0.7910 | 2.1578 | 44.8325% | 0.0198% | 128 | 0 | 0 | none |
|
||||
| passage_floor_closeup | 1280x800 | 0.9314 | 2.4800 | 43.0219% | 0.0500% | 100 | 0 | 0 | none |
|
||||
| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 | 0 | 0 | none |
|
||||
| remote_yaw0 | 1280x800 | 0.0416 | 1.0679 | 2.7991% | 0.0305% | 90 | 0 | 0 | none |
|
||||
| remote_yaw90 | 1280x800 | 0.0100 | 0.4323 | 0.9746% | 0.0035% | 94 | 0 | 0 | none |
|
||||
| remote_yaw180 | 1280x800 | 0.0470 | 1.3091 | 2.9463% | 0.0311% | 102 | 2 | 0 | (415,172)-(834,173) |
|
||||
| remote_yaw270 | 1280x800 | 0.0826 | 1.6874 | 5.1076% | 0.0580% | 107 | 2 | 0 | (835,204)-(886,205) |
|
||||
@@ -1579,6 +1579,8 @@ namespace
|
||||
uint64 StartupNonBlockingPresentCalls=0,StartupNonBlockingPresentSkips=0;
|
||||
double GpuFarGpuDispatchMs=0.0;
|
||||
double GpuFarGpuDispatchMaxMs=0.0;
|
||||
FVoxelTileKey GpuFarGpuDispatchMaxKey{};
|
||||
bool bGpuFarGpuDispatchMaxKeyValid=false;
|
||||
double GpuFarSubmissionSpanMs=0.0,GpuFarSubmissionSpanMaxMs=0.0;
|
||||
uint64 GpuFarLocalMemoryPeakBytes=0,GpuFarLocalMemoryBudgetBytes=0;
|
||||
double GpuFarPoolReserveMs=0.0;
|
||||
@@ -1750,6 +1752,7 @@ namespace
|
||||
GpuFarAudit.reset();bGpuFarAuditReported=false;
|
||||
GpuFarTilesSubmitted=0;GpuFarEnqueueWallSeconds=0.0;GpuFarGpuDispatchMs=0.0;
|
||||
GpuFarGpuDispatchMaxMs=0.0;GpuFarSubmissionSpanMs=0.0;GpuFarSubmissionSpanMaxMs=0.0;
|
||||
GpuFarGpuDispatchMaxKey={};bGpuFarGpuDispatchMaxKeyValid=false;
|
||||
GpuFarLocalMemoryPeakBytes=0;GpuFarLocalMemoryBudgetBytes=0;
|
||||
bGpuFarFirstTileReported=false;
|
||||
GpuSkirtDrawAuditTiles=GpuSkirtDrawAuditZeroCounts=GpuSkirtDrawAuditOverflow=0;
|
||||
@@ -2743,7 +2746,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
Tile.GpuDispatchMs=TileDispatchMs;Tile.bTimingResolved=true;
|
||||
GpuFarGpuDispatchMs+=TileDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,TileDispatchMs);
|
||||
if(TileDispatchMs>GpuFarGpuDispatchMaxMs)
|
||||
{
|
||||
GpuFarGpuDispatchMaxMs=TileDispatchMs;
|
||||
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Batch.bGpuComplete)
|
||||
@@ -2769,7 +2776,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
Tile.GpuDispatchMs=AsyncDispatchMs;
|
||||
GpuFarGpuDispatchMs+=AsyncDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,AsyncDispatchMs);
|
||||
if(AsyncDispatchMs>GpuFarGpuDispatchMaxMs)
|
||||
{
|
||||
GpuFarGpuDispatchMaxMs=AsyncDispatchMs;
|
||||
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
|
||||
}
|
||||
}
|
||||
Tile.bTimingResolved=true;
|
||||
}
|
||||
@@ -2784,10 +2795,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
return ResidentTiles.contains(Key);
|
||||
}
|
||||
|
||||
void PrepareGpuFarInputs(std::vector<FVoxelTileKey> NeedPreparation)
|
||||
void PrepareGpuFarInputs(std::vector<FVoxelTileKey> NeedPreparation,
|
||||
std::chrono::steady_clock::time_point DispatchAt)
|
||||
{
|
||||
if(NeedPreparation.empty())return;
|
||||
const auto PrepareStart=std::chrono::steady_clock::now();
|
||||
if(bGpuFarTimelineEnabled)
|
||||
std::printf("[ColdTimeline] event=gpu_input_export_thread_started at_ms=%.3f dispatch_to_start_ms=%.3f frame=%llu\n",
|
||||
ColdTimelineMilliseconds(PrepareStart),
|
||||
std::chrono::duration<double,std::milli>(PrepareStart-DispatchAt).count(),
|
||||
static_cast<unsigned long long>(ColdTimelineFrame()));
|
||||
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu\n",
|
||||
ColdTimelineMilliseconds(PrepareStart),NeedPreparation.size());
|
||||
const std::size_t ThreadCount=std::min<std::size_t>(8,NeedPreparation.size());
|
||||
@@ -3017,19 +3034,36 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::chrono::steady_clock::now()-ReserveStart).count();
|
||||
std::printf("[ColdTimeline] stage=gpu_output_pool end_ms=%.3f duration_ms=%.3f\n",
|
||||
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),GpuFarPoolReserveMs);
|
||||
const auto PoolEnd=std::chrono::steady_clock::now();
|
||||
const auto MemorySampleStart=std::chrono::steady_clock::now();
|
||||
SampleGpuFarMemory();
|
||||
const double MemorySampleMs=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-MemorySampleStart).count();
|
||||
if(GpuFarNeedPreparation.empty())
|
||||
{
|
||||
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
|
||||
return;
|
||||
}
|
||||
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
|
||||
const auto PreparationListStart=std::chrono::steady_clock::now();
|
||||
std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
|
||||
std::stable_sort(NeedPreparation.begin(),NeedPreparation.end(),
|
||||
[&](const FVoxelTileKey& A,const FVoxelTileKey& B){return GpuFarKeyLess(A,B);});
|
||||
GpuFarPreparationThread=std::thread([this,NeedPreparation]()
|
||||
const double PreparationListMs=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-PreparationListStart).count();
|
||||
const auto DispatchAt=std::chrono::steady_clock::now();
|
||||
if(bGpuFarTimelineEnabled)
|
||||
std::printf("[ColdTimeline] event=gpu_input_export_thread_dispatch at_ms=%.3f after_pool_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu\n",
|
||||
ColdTimelineMilliseconds(DispatchAt),
|
||||
std::chrono::duration<double,std::milli>(DispatchAt-PoolEnd).count(),
|
||||
MemorySampleMs,PreparationListMs,NeedPreparation.size());
|
||||
GpuFarPreparationThread=std::thread([this,NeedPreparation,DispatchAt]()
|
||||
{
|
||||
try { PrepareGpuFarInputs(NeedPreparation); }
|
||||
const bool bPrioritySet=SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL)!=0;
|
||||
if(bGpuFarTimelineEnabled)
|
||||
std::printf("[GpuFarPreparationThread] priority=%s process=BelowNormal thread_relative=Normal\n",
|
||||
bPrioritySet?"Normal":"set_failed");
|
||||
try { PrepareGpuFarInputs(NeedPreparation,DispatchAt); }
|
||||
catch(const std::exception& Error)
|
||||
{
|
||||
std::lock_guard Lock(GpuFarPreparedResultsMutex);
|
||||
@@ -3784,7 +3818,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(GpuDispatchMs>0.0)
|
||||
{
|
||||
GpuFarGpuDispatchMs+=GpuDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs);
|
||||
if(GpuDispatchMs>GpuFarGpuDispatchMaxMs)
|
||||
{
|
||||
GpuFarGpuDispatchMaxMs=GpuDispatchMs;
|
||||
GpuFarGpuDispatchMaxKey=Key;bGpuFarGpuDispatchMaxKeyValid=true;
|
||||
}
|
||||
Tile.bTimingResolved=true;
|
||||
}
|
||||
++GpuFarTilesSubmitted;++Built;
|
||||
@@ -4577,6 +4615,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
const double AverageGpuMs=GpuFarTilesSubmitted
|
||||
?GpuFarGpuDispatchMs/static_cast<double>(GpuFarTilesSubmitted):0.0;
|
||||
if(bGpuFarGpuDispatchMaxKeyValid)
|
||||
std::printf("[GpuFarMaxDispatchTile] key=(%d,%d,%d,lod%d) dispatch_ms=%.6f\n",
|
||||
GpuFarGpuDispatchMaxKey.Coord.X,GpuFarGpuDispatchMaxKey.Coord.Y,
|
||||
GpuFarGpuDispatchMaxKey.Coord.Z,GpuFarGpuDispatchMaxKey.Level,
|
||||
GpuFarGpuDispatchMaxMs);
|
||||
std::printf("[GpuFarModeTiming] mode=%s cpu_mesh_tiles=%llu cpu_mesh_worker_sum_s=%.6f cpu_input_export_worker_sum_s=%.6f gpu_submitted_tiles=%llu gpu_busy_timestamp_sum_ms=%.3f whole_view_wall_s=%.6f warmup_cpu_claimed=%llu warmup_gpu_takeover=%llu\n",
|
||||
bGpuFarAuditEnabled?"gpu_audit":"gpu_hybrid",
|
||||
static_cast<unsigned long long>(CpuMeshTiles),CpuMeshWorkerSeconds,
|
||||
|
||||
Reference in new issue
Block a user