wip(standalone): GPU step 15 -- cold-start drain: hybrid whole view 3.2 -> 1.25-1.68 s
Timeline showed the post-export window was frame-paced: the 4-batch / 2 ms per-frame upload budget held CPU-fallback tiles up to 755 ms after they finished, and 35 GPU batches drained one per frame. Before the first whole view, GPU work now drains as one batch and CPU fallback starts with GPU input preparation (--gpu-far-no-cold-drain = paced control). Luna's race: CPU 1.393 s vs GPU 1.677 s. My interleaved runs (Step15Timeline, RunGpuFarViewer): GPU 2.902 (first launch) / 1.252 / 1.209 s; CPU 2.196 / 2.250 / 2.061 s. The CPU viewer still uses the paced upload budget at cold start (stream 674 -> 2250 ms in my runs vs 741 -> 1422 in Luna's), so the race is not yet fair. Audits: hashes, walk 5,642/5,642, 0 diffs vs CPU/WARP. 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
f322b1c089
commit
c152708a67
16 files changed
+3378
-86
No files matched your search
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Expand Step 15 timeline logs into per-frame, per-batch, and per-CPU-tile CSVs."""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def write_csv(path: Path, columns: list[str], rows: list[dict[str, object]]) -> None:
|
||||
with path.open("w", newline="", encoding="utf-8") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=columns)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
|
||||
|
||||
def optional_float(match: re.Match[str] | None, group: str = 1) -> float | None:
|
||||
return float(match.group(group)) if match else None
|
||||
|
||||
|
||||
def optional_int(match: re.Match[str] | None, group: str = 1) -> int | None:
|
||||
return int(match.group(group)) if match else None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("log", type=Path)
|
||||
parser.add_argument("--prefix", type=Path)
|
||||
args = parser.parse_args()
|
||||
log_path = args.log.resolve()
|
||||
prefix = args.prefix or log_path.with_suffix("")
|
||||
if not prefix.is_absolute():
|
||||
prefix = Path.cwd() / prefix
|
||||
|
||||
cpu: dict[str, dict[str, object]] = {}
|
||||
gpu_batches: dict[int, dict[str, object]] = {}
|
||||
frames: dict[int, dict[str, object]] = {}
|
||||
cpu_event_re = re.compile(
|
||||
r"^\[CpuTileTimeline\] event=(queued|started|finished|uploaded|resident) "
|
||||
r"key=\(([^)]*)\) at_ms=([0-9.]+) frame=([0-9]+)"
|
||||
)
|
||||
gpu_batch_re = re.compile(
|
||||
r"^\[GpuBatchTimeline\] event=(prepared|submitted|completed|uploaded|resident) "
|
||||
r"batch=([0-9]+) at_ms=([0-9.]+) frame=([0-9]+)"
|
||||
)
|
||||
frame_re = re.compile(r"^\[ColdFrame\] stage=(begin|end) frame=([0-9]+) at_ms=([0-9.]+)")
|
||||
|
||||
for line in log_path.read_text(encoding="utf-8", errors="replace").splitlines():
|
||||
match = cpu_event_re.search(line)
|
||||
if match:
|
||||
event, key, at_ms, frame = match.groups()
|
||||
row = cpu.setdefault(key, {"key": key})
|
||||
row[f"{event}_ms"] = float(at_ms)
|
||||
row[f"{event}_frame"] = int(frame)
|
||||
for field in ("queued", "started", "finished", "worker"):
|
||||
field_match = re.search(rf"\b{field}_ms=([0-9.]+)", line)
|
||||
if field_match:
|
||||
row[f"{field}_ms"] = float(field_match.group(1))
|
||||
batch_match = re.search(r"\bbatch=(\(lod[^)]*\))", line)
|
||||
if batch_match:
|
||||
row["cpu_batch"] = batch_match.group(1)
|
||||
continue
|
||||
|
||||
match = gpu_batch_re.search(line)
|
||||
if match:
|
||||
event, batch_id, at_ms, frame = match.groups()
|
||||
row = gpu_batches.setdefault(int(batch_id), {"batch": int(batch_id)})
|
||||
row[f"{event}_ms"] = float(at_ms)
|
||||
row[f"{event}_frame"] = int(frame)
|
||||
tile_count = re.search(r"\btile_count=([0-9]+)", line)
|
||||
if tile_count:
|
||||
row["tile_count"] = int(tile_count.group(1))
|
||||
continue
|
||||
|
||||
match = frame_re.search(line)
|
||||
if match:
|
||||
stage, frame, at_ms = match.groups()
|
||||
row = frames.setdefault(int(frame), {"frame": int(frame)})
|
||||
row[f"{stage}_ms"] = float(at_ms)
|
||||
for field in ("inter_draw_gap_ms", "cpu_pump_ms", "gpu_pump_ms", "pre_present_ms", "present_ms"):
|
||||
field_match = re.search(rf"\b{field}=([0-9.]+)", line)
|
||||
if field_match:
|
||||
row[field] = float(field_match.group(1))
|
||||
present_sync = re.search(r"\bpresent_sync=([0-9]+)", line)
|
||||
if present_sync:
|
||||
row["present_sync"] = int(present_sync.group(1))
|
||||
held = re.search(r"\bheld=([01])", line)
|
||||
if held:
|
||||
row["held"] = int(held.group(1))
|
||||
|
||||
cpu_rows = sorted(cpu.values(), key=lambda row: str(row["key"]))
|
||||
for row in cpu_rows:
|
||||
queued = row.get("queued_ms")
|
||||
started = row.get("started_ms")
|
||||
finished = row.get("finished_ms")
|
||||
uploaded = row.get("uploaded_ms")
|
||||
row["queue_wait_ms"] = started - queued if queued is not None and started is not None else None
|
||||
row["finish_to_upload_ms"] = uploaded - finished if finished is not None and uploaded is not None else None
|
||||
|
||||
gpu_rows = sorted(gpu_batches.values(), key=lambda row: int(row["batch"]))
|
||||
frame_rows = sorted(frames.values(), key=lambda row: int(row["frame"]))
|
||||
cpu_columns = [
|
||||
"key", "cpu_batch", "queued_ms", "queued_frame", "started_ms", "started_frame",
|
||||
"finished_ms", "finished_frame", "worker_ms", "uploaded_ms", "uploaded_frame",
|
||||
"resident_ms", "resident_frame", "queue_wait_ms", "finish_to_upload_ms",
|
||||
]
|
||||
gpu_columns = [
|
||||
"batch", "tile_count", "prepared_ms", "prepared_frame", "submitted_ms", "submitted_frame",
|
||||
"completed_ms", "completed_frame", "uploaded_ms", "uploaded_frame", "resident_ms", "resident_frame",
|
||||
]
|
||||
frame_columns = [
|
||||
"frame", "begin_ms", "end_ms", "inter_draw_gap_ms", "cpu_pump_ms", "gpu_pump_ms",
|
||||
"pre_present_ms", "present_ms", "present_sync", "held",
|
||||
]
|
||||
cpu_path = prefix.with_name(prefix.name + "_cpu_tiles.csv")
|
||||
gpu_path = prefix.with_name(prefix.name + "_gpu_batches.csv")
|
||||
frame_path = prefix.with_name(prefix.name + "_frames.csv")
|
||||
write_csv(cpu_path, cpu_columns, cpu_rows)
|
||||
write_csv(gpu_path, gpu_columns, gpu_rows)
|
||||
write_csv(frame_path, frame_columns, frame_rows)
|
||||
|
||||
for event in ("queued", "started", "finished", "uploaded", "resident"):
|
||||
count = sum(f"{event}_ms" in row for row in cpu_rows)
|
||||
print(f"cpu_{event}_tiles={count}")
|
||||
print(f"gpu_batches={len(gpu_rows)} gpu_batch_tiles={sum(int(row.get('tile_count', 0)) for row in gpu_rows)} frames={len(frame_rows)}")
|
||||
if cpu_rows:
|
||||
finished_rows = [row for row in cpu_rows if "finished_ms" in row]
|
||||
uploaded_rows = [row for row in cpu_rows if "uploaded_ms" in row]
|
||||
if finished_rows:
|
||||
last_finished = max(finished_rows, key=lambda row: float(row["finished_ms"]))
|
||||
print(f"last_cpu_finished_ms={last_finished['finished_ms']:.3f} key={last_finished['key']}")
|
||||
if uploaded_rows:
|
||||
last_uploaded = max(uploaded_rows, key=lambda row: float(row["uploaded_ms"]))
|
||||
print(f"last_cpu_uploaded_ms={last_uploaded['uploaded_ms']:.3f} key={last_uploaded['key']}")
|
||||
delayed = [row for row in uploaded_rows if row.get("finish_to_upload_ms") is not None]
|
||||
if delayed:
|
||||
max_delay = max(delayed, key=lambda row: float(row["finish_to_upload_ms"]))
|
||||
print(f"max_cpu_finish_to_upload_ms={max_delay['finish_to_upload_ms']:.3f} key={max_delay['key']}")
|
||||
if gpu_rows:
|
||||
for stage in ("prepared", "submitted", "completed", "resident"):
|
||||
present = [row for row in gpu_rows if f"{stage}_ms" in row]
|
||||
if present:
|
||||
last = max(present, key=lambda row: float(row[f"{stage}_ms"]))
|
||||
print(f"last_gpu_batch_{stage}_ms={last[f'{stage}_ms']:.3f} batch={last['batch']} frame={last.get(f'{stage}_frame')}")
|
||||
print(f"cpu_tiles_csv={cpu_path}")
|
||||
print(f"gpu_batches_csv={gpu_path}")
|
||||
print(f"frames_csv={frame_path}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,59 @@
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
VIEWS = (
|
||||
"chamber",
|
||||
"tunnel",
|
||||
"passage",
|
||||
"far",
|
||||
"passage_floor_closeup",
|
||||
"tunnel_bend",
|
||||
"remote_yaw0",
|
||||
"remote_yaw90",
|
||||
"remote_yaw180",
|
||||
"remote_yaw270",
|
||||
)
|
||||
|
||||
rows = []
|
||||
for view in VIEWS:
|
||||
cpu_path = ROOT / f"step15_cpu_{view}.png"
|
||||
gpu_path = ROOT / f"step15_gpu_{view}.png"
|
||||
cpu = np.asarray(Image.open(cpu_path).convert("RGB"), dtype=np.int16)
|
||||
gpu = np.asarray(Image.open(gpu_path).convert("RGB"), dtype=np.int16)
|
||||
if cpu.shape != gpu.shape:
|
||||
raise RuntimeError(f"dimension mismatch for {view}: {cpu.shape} vs {gpu.shape}")
|
||||
diff = np.abs(cpu - gpu)
|
||||
pixel_max = diff.max(axis=2)
|
||||
rows.append(
|
||||
(
|
||||
view,
|
||||
f"{cpu.shape[1]}x{cpu.shape[0]}",
|
||||
float(diff.mean()),
|
||||
float(np.sqrt(np.square(diff, dtype=np.float64).mean())),
|
||||
float(np.count_nonzero(pixel_max) * 100.0 / pixel_max.size),
|
||||
float(np.count_nonzero(pixel_max > 32) * 100.0 / pixel_max.size),
|
||||
int(pixel_max.max()),
|
||||
)
|
||||
)
|
||||
|
||||
lines = [
|
||||
"# Step 15 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.",
|
||||
"",
|
||||
"| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel |",
|
||||
"|---|---:|---:|---:|---:|---:|---:|",
|
||||
]
|
||||
for row in rows:
|
||||
view, size, mae, rmse, changed, over32, maximum = row
|
||||
lines.append(
|
||||
f"| {view} | {size} | {mae:.4f} | {rmse:.4f} | {changed:.4f}% | {over32:.4f}% | {maximum} |"
|
||||
)
|
||||
|
||||
report = ROOT / "step15_capture_comparison.md"
|
||||
report.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print(report.read_text(encoding="utf-8"), end="")
|
||||
@@ -0,0 +1,80 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe')
|
||||
|
||||
$ErrorActionPreference='Stop'
|
||||
$HostProcess=Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass='BelowNormal'
|
||||
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
|
||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||
{throw 'ExecutableName must be an executable filename.'}
|
||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
||||
$Views=@(
|
||||
@{Name='chamber';Camera='-11.75,-123.25,-19.85,0,0'},
|
||||
@{Name='tunnel';Camera='-11.75,-123.25,-19.85,90,0'},
|
||||
@{Name='passage';Camera='-39.19,6.27,-28.51,63,0'},
|
||||
@{Name='far';Camera='-11.61244,-118.2212,-19.8452,0,0'},
|
||||
@{Name='passage_floor_closeup';Camera='-39.19,6.27,-25.51,63,-25'},
|
||||
@{Name='tunnel_bend';Camera='-198.97,-99.57,-45.58,33.59,23.17'},
|
||||
@{Name='remote_yaw0';Camera='500,0,0,0,0'},
|
||||
@{Name='remote_yaw90';Camera='500,0,0,90,0'},
|
||||
@{Name='remote_yaw180';Camera='500,0,0,180,0'},
|
||||
@{Name='remote_yaw270';Camera='500,0,0,270,0'}
|
||||
)
|
||||
$EightGB=8L*1024L*1024L*1024L
|
||||
foreach($View in $Views)
|
||||
{
|
||||
foreach($Mode in @('cpu','gpu'))
|
||||
{
|
||||
$Stem="step15_${Mode}_$($View.Name)"
|
||||
$CaptureName="$Stem.png"
|
||||
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--capture-after-clipmap',
|
||||
"--camera=$($View.Camera)","--$Mode-far",
|
||||
"--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
||||
$StdoutPath=Join-Path $PSScriptRoot ($Stem+'.stdout.log')
|
||||
$StderrPath=Join-Path $PSScriptRoot ($Stem+'.stderr.log')
|
||||
$CapturePath=Join-Path $PSScriptRoot $CaptureName
|
||||
Write-Output "STEP15_CAPTURE mode=$Mode view=$($View.Name) affinity=0xFF00 command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||
{
|
||||
if($Attempt-gt0)
|
||||
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
|
||||
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
|
||||
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
|
||||
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
|
||||
$PeakPrivate=0L;$OverMemoryLimit=$false
|
||||
try
|
||||
{
|
||||
while(-not$Child.HasExited)
|
||||
{
|
||||
$Child.Refresh();if($Child.HasExited){break}
|
||||
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
|
||||
if($PeakPrivate-ge$EightGB)
|
||||
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
|
||||
}
|
||||
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
|
||||
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
|
||||
if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single permitted retry.'}
|
||||
if($OverMemoryLimit){throw "Capture viewer stopped at the 8 GiB private-memory ceiling: $Stem"}
|
||||
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||
if(-not$LogText.Contains('[InitialClipmapReady]')-or-not(Test-Path -LiteralPath $CapturePath -PathType Leaf))
|
||||
{throw "Full-residency capture missing: $Stem; exit=$ChildExitCode"}
|
||||
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
|
||||
$CombinedGB=$PeakPrivate/1GB
|
||||
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
|
||||
if($CombinedGB-ge8.0){throw "Capture exceeded conservative private + DXGI-local 8 GiB ceiling: $Stem ($CombinedGB GiB)"}
|
||||
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
|
||||
Write-Output "STEP15_CAPTURE_DONE mode=$Mode view=$($View.Name) ready_s=$($Ready.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) path=$CapturePath exit=$ChildExitCode"
|
||||
if($ChildExitCode-ne0){throw "Capture viewer returned $ChildExitCode`: $Stem"}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe')
|
||||
|
||||
$ErrorActionPreference='Stop'
|
||||
$HostProcess=Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass='BelowNormal'
|
||||
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
|
||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||
{throw 'ExecutableName must be an executable filename.'}
|
||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
||||
$Reference=$null
|
||||
foreach($Workers in @(1,8))
|
||||
{
|
||||
$Name="step15_cpu_reference_w$Workers"
|
||||
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
||||
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
||||
$Arguments=@('--stream-benchmark','--shape-world','--force-generate','--stream-hash',
|
||||
'--clip-radius=3',"--workers=$Workers")
|
||||
Write-Output "STEP15_CPU_REFERENCE workers=$Workers affinity=0xFF00 priority=BelowNormal command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||
{
|
||||
if($Attempt-gt0)
|
||||
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
|
||||
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
|
||||
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
|
||||
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
|
||||
$PeakPrivate=0L;$OverMemoryLimit=$false
|
||||
try
|
||||
{
|
||||
while(-not$Child.HasExited)
|
||||
{
|
||||
$Child.Refresh();if($Child.HasExited){break}
|
||||
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
|
||||
if($Child.PrivateMemorySize64-ge(8L*1GB))
|
||||
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
|
||||
}
|
||||
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
|
||||
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
|
||||
if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
|
||||
if($OverMemoryLimit){throw "CPU reference stopped at the 8 GiB private-memory ceiling: W$Workers"}
|
||||
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||
$Stream=[regex]::Match($LogText,'(?m)^\[StreamFullClipmap\].*generated=2226/2226.*density_hash=(0x[0-9a-fA-F]+) mesh_hash=(0x[0-9a-fA-F]+) payload_hash=(0x[0-9a-fA-F]+) topology_hash=(0x[0-9a-fA-F]+)')
|
||||
$Walkability=[regex]::Match($LogText,'(?m)^\[Walkability\] PASS 5642/5642')
|
||||
if(-not$Stream.Success-or-not$Walkability.Success)
|
||||
{throw "CPU reference failed full-generation/hash/walkability checks for W$Workers; exit=$ChildExitCode"}
|
||||
$Hashes=@($Stream.Groups[1].Value.ToLowerInvariant(),$Stream.Groups[2].Value.ToLowerInvariant(),
|
||||
$Stream.Groups[3].Value.ToLowerInvariant(),$Stream.Groups[4].Value.ToLowerInvariant())
|
||||
if($null-eq$Reference){$Reference=$Hashes}
|
||||
elseif(($Reference-join'|')-ne($Hashes-join'|')){throw 'CPU hashes differ between W1 and W8.'}
|
||||
Write-Output "STEP15_CPU_REFERENCE_RESULT workers=$Workers hashes=$($Hashes -join ',') walkability=5642/5642 peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) exit=$ChildExitCode"
|
||||
if($ChildExitCode-ne0){throw "CPU reference returned $ChildExitCode`: W$Workers"}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
[CmdletBinding()]
|
||||
param([string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe')
|
||||
|
||||
$ErrorActionPreference='Stop'
|
||||
$HostProcess=Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass='BelowNormal'
|
||||
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
|
||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||
{throw 'ExecutableName must be an executable filename.'}
|
||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
||||
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far-audit','--gpu-far-prewarm')
|
||||
$Name='step15_gpu_survival'
|
||||
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
||||
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
||||
$EightGB=8L*1024L*1024L*1024L
|
||||
Write-Output "STEP15_SURVIVAL_RUN command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||
{
|
||||
if($Attempt-gt0)
|
||||
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
|
||||
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
|
||||
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
|
||||
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
|
||||
$PeakPrivate=0L;$OverMemoryLimit=$false
|
||||
try
|
||||
{
|
||||
while(-not$Child.HasExited)
|
||||
{
|
||||
$Child.Refresh();if($Child.HasExited){break}
|
||||
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
|
||||
if($Child.PrivateMemorySize64-ge$EightGB)
|
||||
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
|
||||
}
|
||||
$Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode
|
||||
if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue}
|
||||
if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
|
||||
if($OverMemoryLimit){throw 'GPU survival audit stopped at the private-memory ceiling.'}
|
||||
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||
if(-not$LogText.Contains('[InitialClipmapReady]')){throw "GPU survival audit did not reach full residency; exit=$ChildExitCode"}
|
||||
$Audit=[regex]::Match($LogText,'(?m)^\[GpuFarSurvivalAudit\].*status=(PASS|FAIL)')
|
||||
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
|
||||
if(-not$Audit.Success){throw 'GPU survival audit result marker missing.'}
|
||||
$DxgiMB=if($Memory.Success){[double]$Memory.Groups[1].Value}else{0.0}
|
||||
$CombinedGB=($PeakPrivate+$DxgiMB*1MB)/1GB
|
||||
if($CombinedGB-ge8.0){throw "GPU survival exceeded conservative private + DXGI-local 8 GiB ceiling: $CombinedGB GiB"}
|
||||
Write-Output "STEP15_SURVIVAL_RESULT audit=$($Audit.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) dxgi_local_peak_mb=$DxgiMB conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ChildExitCode stdout=$StdoutPath stderr=$StderrPath"
|
||||
if($Audit.Groups[1].Value-ne'PASS'){throw 'GPU survival audit failed.'}
|
||||
if($ChildExitCode-ne0){throw "Viewer returned $ChildExitCode"}
|
||||
break
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet('Paced','NoVsync','Fast','Race')][string]$Mode='Paced',
|
||||
[string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe'
|
||||
)
|
||||
|
||||
$ErrorActionPreference='Stop'
|
||||
$HostProcess=Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass='BelowNormal'
|
||||
$HostProcess.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||
{throw 'ExecutableName must be an executable filename.'}
|
||||
$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
|
||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
||||
|
||||
$Runs=switch($Mode)
|
||||
{
|
||||
'Paced' {@(@{Mode='gpu';Name='step15_timeline_paced';Extra=@('--gpu-far-no-cold-drain','--gpu-far-timeline')})}
|
||||
'NoVsync' {@(@{Mode='gpu';Name='step15_timeline_no_vsync';Extra=@('--gpu-far-no-cold-drain','--no-vsync','--gpu-far-timeline')})}
|
||||
'Fast' {@(@{Mode='gpu';Name='step15_timeline_fast';Extra=@('--gpu-far-timeline')})}
|
||||
'Race' {@(
|
||||
@{Mode='cpu';Name='step15_race_cpu_A';Extra=@()},
|
||||
@{Mode='gpu';Name='step15_race_gpu_A';Extra=@()},
|
||||
@{Mode='gpu';Name='step15_race_gpu_B';Extra=@()},
|
||||
@{Mode='cpu';Name='step15_race_cpu_B';Extra=@()}
|
||||
)}
|
||||
}
|
||||
$EightGB=8L*1024L*1024L*1024L
|
||||
foreach($Run in $Runs)
|
||||
{
|
||||
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap',"--$($Run.Mode)-far")+$Run.Extra
|
||||
$StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log')
|
||||
$StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log')
|
||||
Write-Output "STEP15_RUN mode=$($Run.Mode) name=$($Run.Name) priority=BelowNormal affinity=0xFF00 workers=8 command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||
{
|
||||
if($Attempt-gt0)
|
||||
{Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60}
|
||||
Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue
|
||||
$HostRunClock=[Diagnostics.Stopwatch]::StartNew()
|
||||
$Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot `
|
||||
-WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath
|
||||
$PeakPrivate=0L;$OverMemoryLimit=$false
|
||||
try
|
||||
{
|
||||
while(-not$Child.HasExited)
|
||||
{
|
||||
$Child.Refresh();if($Child.HasExited){break}
|
||||
$Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00
|
||||
$PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
|
||||
if($Child.PrivateMemorySize64-ge$EightGB)
|
||||
{$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break}
|
||||
Start-Sleep -Milliseconds 50
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()}
|
||||
}
|
||||
$Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds
|
||||
$ViewerExitCode=[int]$Child.ExitCode
|
||||
if($ViewerExitCode-eq-1073741502-and$Attempt-eq0){continue}
|
||||
if($ViewerExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'}
|
||||
if($OverMemoryLimit){throw "Viewer stopped at the 8 GiB private-memory ceiling: $($Run.Name)"}
|
||||
$LogText=Get-Content -LiteralPath $StdoutPath -Raw
|
||||
if(-not$LogText.Contains('[InitialClipmapReady]'))
|
||||
{throw "Full clipmap was not ready in $($Run.Name); exit=$ViewerExitCode"}
|
||||
$Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s')
|
||||
$Presented=[regex]::Match($LogText,'(?m)^\[WholeViewPresented\] at_ms=([0-9.]+) present_ms=([0-9.]+).*hr=0x([0-9A-Fa-f]{8})')
|
||||
if(-not$Presented.Success){throw "Whole-view present marker missing in $($Run.Name)"}
|
||||
$Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)')
|
||||
$CombinedGB=$PeakPrivate/1GB
|
||||
if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB}
|
||||
if($CombinedGB-ge8.0){throw "Viewer exceeded conservative private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"}
|
||||
Write-Output "STEP15_RESULT mode=$($Run.Mode) name=$($Run.Name) ready_ms=$([math]::Round([double]$Ready.Groups[1].Value*1000,3)) viewer_whole_view_present_return_ms=$($Presented.Groups[1].Value) present_ms=$($Presented.Groups[2].Value) present_hr=0x$($Presented.Groups[3].Value.ToUpperInvariant()) host_process_exit_ms=$([math]::Round($HostRunMs,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode"
|
||||
if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# GPU field port — Step 15 report
|
||||
|
||||
## Result
|
||||
|
||||
Cold-start draining removes most of the post-export delay, and CPU fallback generation now starts alongside GPU input preparation. The interleaved race still shows the hybrid behind the CPU viewer: median first whole view is **1.677 s GPU vs 1.393 s CPU**. The hybrid does not beat the roughly 1.35 s CPU result yet.
|
||||
|
||||
## Timeline and diagnosis
|
||||
|
||||
The timeline logger is opt-in (`--gpu-far-timeline`). It writes per-frame CSVs, one row per GPU batch, and one row per CPU fallback tile. Times are milliseconds from cold process start; each event also records its frame ordinal. GPU `uploaded` means the compute output became available in its preallocated GPU output buffer; this path has no CPU readback or copy-upload stage.
|
||||
|
||||
| Run | First whole view | CPU fallback | GPU batches | Last GPU batch resident |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Paced control (`--gpu-far-no-cold-drain`) | 2.141 s | 1,144 queued, started, finished, uploaded and resident | 35 / 1,082 tiles | 2.141 s |
|
||||
| Paced control + `--no-vsync` | 2.132 s | same workload | 35 / 1,082 tiles | 2.131 s |
|
||||
| Cold drain, instrumented | 1.681 s | all 1,144 resident by 1.386 s | 1 / 1,082 tiles | 1.681 s |
|
||||
|
||||
In the paced trace, CPU streaming starts at 0.995 s. All 1,144 fallback jobs have finished by 1.205 s, but the last CPU upload/residency event is at 1.949 s. The worst finish-to-upload delay is 755 ms. The upload budget of four batches or 2 ms per frame is the CPU fallback tail. The GPU pump starts around 1.378 s and submits its 35th batch at 2.077 s; that batch completes at 2.140 s and is resident at 2.141 s. The paced schedule spreads its work over 40 frame boundaries.
|
||||
|
||||
Disabling vsync only improves the paced control by 9 ms. Startup presentation already uses nonblocking `sync=0`; the cold upload and submission budgets still pace the work. Thus vsync is not the cause of the roughly 0.8 s CPU upload tail.
|
||||
|
||||
With cold draining enabled, the instrumented trace has the last CPU fallback tile finished at 1.198 s and resident at 1.386 s. The single GPU batch is prepared at 1.440 s (frame 2), submitted at 1.519 s (frame 2), complete/output-available at 1.670 s (frame 12), and resident at 1.681 s (frame 12). The reported GPU dispatch timestamp total is 197 ms for 1,082 tiles. The residual floor after the input export is therefore the GPU path: command preparation/submission followed by about 200 ms of GPU work and completion/residency polling. In the non-instrumented race, export ends at about 1.33 s and the first whole view arrives 0.32–0.38 s later.
|
||||
|
||||
Per-event evidence is in [paced GPU batches](step15_timeline_paced_gpu_batches.csv), [paced CPU tiles](step15_timeline_paced_cpu_tiles.csv), [paced frames](step15_timeline_paced_frames.csv), and the corresponding [cold-drain GPU batches](step15_timeline_fast_gpu_batches.csv), [CPU tiles](step15_timeline_fast_cpu_tiles.csv), and [frames](step15_timeline_fast_frames.csv). The full run logs are [paced](step15_timeline_paced.stdout.log), [no-vsync](step15_timeline_no_vsync.stdout.log), and [cold drain](step15_timeline_fast.stdout.log). Re-run the event summary with `py -3 AnalyzeStep15Timeline.py <log> --prefix <output-prefix>`.
|
||||
|
||||
## Changes
|
||||
|
||||
- Start the CPU streamer before launching asynchronous GPU input preparation. The CPU fallback jobs can run while GPU inputs are exported and prepared.
|
||||
- Before the first complete view only, drain GPU submissions and CPU dirty uploads without the normal frame-sliced caps. Keep the existing four-batch / 2 ms upload budget and GPU submission pacing after the first view is ready. The GPU survival audit retains its bounded submission mode.
|
||||
- While waiting for the initial view in cold-drain mode, use a 1 ms wait interval instead of 11 ms.
|
||||
- Add `--gpu-far-no-cold-drain` as the paced control and `--gpu-far-timeline` for event/frame telemetry.
|
||||
|
||||
The viewer and streamer changes are in `Source/StandaloneViewer.cpp` and `Source/StreamingWorld.h`. The runner and analysis scripts are `RunStep15Timeline.ps1` and `AnalyzeStep15Timeline.py`.
|
||||
|
||||
## Interleaved race and remaining floor
|
||||
|
||||
The two CPU runs reached the first whole view in 1.422 s and 1.363 s (median **1.393 s**). The GPU runs took 1.647 s and 1.707 s (median **1.677 s**), a **285 ms median gap**. Process-exit medians show the same ordering: 1.628 s CPU vs 1.833 s GPU. The GPU runs ended input export at 1.326–1.331 s, then needed another 0.32–0.38 s to make the full view ready.
|
||||
|
||||
GPU shape compilation took 888–897 ms in the race, compared with 585–586 ms in the CPU runs. The logs show asynchronous GPU shader warm-up overlapping shape compilation. This is a likely source of the roughly 300 ms difference, although this race alone does not prove the cause. Moving CPU fallback earlier removed its upload tail as the final gate; it did not make the GPU path faster than the CPU reference. The best measured GPU whole-view time is 1.647 s in the race (1.682 s with detailed event logging).
|
||||
|
||||
## Correctness and resource checks
|
||||
|
||||
- CPU reference hashes match at one and eight workers: density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, topology `0xbea22e76b34a4dfc`. Walkability passed **5,642/5,642**. See [one worker](step15_cpu_reference_w1.stdout.log) and [eight workers](step15_cpu_reference_w8.stdout.log).
|
||||
- Whole-near and whole-far agreement passed. Both GPU and WARP had zero topology differences and zero protected-exception or thin-feature topology differences in the audited cells. Near: 960 tested tiles, 229,376 protected-exception cells. Far: 1,114 tested tiles, 152 transition tiles skipped, 127,401 protected-exception cells and 353 thin-feature cells. See [near agreement](step15_whole_near_agreement.stdout.log) and [far agreement](step15_whole_far_agreement.stdout.log). This is a sign/topology result, not a claim of bitwise vertex-position parity at every LOD.
|
||||
- The GPU survival audit passed: 2.9 GB peak private memory and 2,043 MB peak DXGI local memory (about 4.9 GB combined). Ten full-residency CPU/GPU capture pairs completed; the largest monitored private-plus-DXGI total was 5.06 GB. Captures were compared as numeric RGB arrays and never opened at full size. Per-view values are in [capture comparison](step15_capture_comparison.md).
|
||||
- Viewer runs used BelowNormal priority and affinity `0xFF00`; the viewer build used `/MP8`. Runners monitored memory, allowed one retry on `0xC0000142`, and closed the viewers they launched. The step 15 viewer binary is `../../../bin/VoxelForgeGpuFieldStep15Timeline.exe`.
|
||||
|
||||
## Follow-up
|
||||
|
||||
Direct-GPU LOD3/4 coverage was not changed. The `(6,2,0,lod3)` coverage loss and the resulting 1,144 CPU fallback tiles remain open for a later step. No commit, push, or stash was made.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Step 15 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.
|
||||
|
||||
| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| chamber | 1280x800 | 0.6076 | 1.8655 | 42.6368% | 0.0183% | 124 |
|
||||
| tunnel | 1280x800 | 0.6383 | 3.3799 | 31.7335% | 0.2772% | 130 |
|
||||
| passage | 1280x800 | 1.6092 | 3.6878 | 50.4982% | 0.1733% | 52 |
|
||||
| far | 1280x800 | 0.7909 | 2.1558 | 44.8325% | 0.0198% | 128 |
|
||||
| passage_floor_closeup | 1280x800 | 0.9314 | 2.4800 | 43.0219% | 0.0500% | 100 |
|
||||
| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 |
|
||||
| remote_yaw0 | 1280x800 | 0.1994 | 2.6632 | 4.2472% | 0.2415% | 125 |
|
||||
| remote_yaw90 | 1280x800 | 0.4083 | 4.3552 | 2.2432% | 0.6948% | 94 |
|
||||
| remote_yaw180 | 1280x800 | 0.0484 | 1.3206 | 2.9787% | 0.0341% | 102 |
|
||||
| remote_yaw270 | 1280x800 | 0.2355 | 3.1104 | 5.5592% | 0.4098% | 107 |
|
||||
+1145
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,13 @@
|
||||
frame,begin_ms,end_ms,inter_draw_gap_ms,cpu_pump_ms,gpu_pump_ms,pre_present_ms,present_ms,present_sync,held
|
||||
1,1100.864,1364.271,0.0,51.311,0.0,0.061,211.608,0,0
|
||||
2,1364.285,1528.009,0.014,21.686,136.361,,0.0,,1
|
||||
3,1528.013,1543.83,0.004,0.537,0.0,,0.0,,1
|
||||
4,1543.835,1559.293,0.005,0.545,0.0,,0.0,,1
|
||||
5,1559.297,1575.126,0.005,0.537,0.0,,0.0,,1
|
||||
6,1575.129,1590.14,0.003,0.507,0.0,,0.0,,1
|
||||
7,1590.146,1606.153,0.006,0.521,0.0,,0.0,,1
|
||||
8,1606.158,1621.673,0.005,0.552,0.0,,0.0,,1
|
||||
9,1621.678,1637.688,0.005,0.529,0.0,,0.0,,1
|
||||
10,1637.69,1653.713,0.002,0.523,0.0,,0.0,,1
|
||||
11,1653.718,1669.716,0.005,0.536,0.0,,0.0,,1
|
||||
12,1669.721,1682.071,0.005,11.635,0.0,0.167,0.343,1,0
|
||||
|
+2
@@ -0,0 +1,2 @@
|
||||
batch,tile_count,prepared_ms,prepared_frame,submitted_ms,submitted_frame,completed_ms,completed_frame,uploaded_ms,uploaded_frame,resident_ms,resident_frame
|
||||
0,1082,1440.022,2,1519.109,2,1669.724,12,1669.724,12,1681.21,12
|
||||
|
+1145
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,41 @@
|
||||
frame,begin_ms,end_ms,inter_draw_gap_ms,cpu_pump_ms,gpu_pump_ms,pre_present_ms,present_ms,present_sync,held
|
||||
1,1069.083,1376.481,0.0,2.081,0.0,0.047,305.114,0,0
|
||||
2,1376.488,1409.017,0.007,1.364,19.316,,0.0,,1
|
||||
3,1409.023,1440.548,0.006,1.169,18.734,,0.0,,1
|
||||
4,1440.553,1472.572,0.005,3.803,8.211,,0.0,,1
|
||||
5,1472.575,1504.617,0.003,2.169,3.09,,0.0,,1
|
||||
6,1504.623,1535.631,0.006,0.915,4.034,,0.0,,1
|
||||
7,1535.637,1567.173,0.006,1.241,3.938,,0.0,,1
|
||||
8,1567.179,1583.19,0.006,1.088,3.118,,0.0,,1
|
||||
9,1583.194,1615.254,0.004,1.139,4.018,,0.0,,1
|
||||
10,1615.26,1631.271,0.005,0.72,3.571,,0.0,,1
|
||||
11,1631.275,1662.823,0.005,2.21,3.91,,0.0,,1
|
||||
12,1662.828,1694.852,0.005,1.474,5.186,,0.0,,1
|
||||
13,1694.861,1710.866,0.009,1.292,0.769,,0.0,,1
|
||||
14,1710.87,1742.893,0.004,4.652,0.745,,0.0,,1
|
||||
15,1742.898,1758.922,0.005,1.641,0.612,,0.0,,1
|
||||
16,1758.927,1774.093,0.005,0.722,0.586,,0.0,,1
|
||||
17,1774.098,1789.745,0.005,1.673,0.607,,0.0,,1
|
||||
18,1789.749,1805.812,0.004,1.276,0.595,,0.0,,1
|
||||
19,1805.815,1821.773,0.003,1.832,0.557,,0.0,,1
|
||||
20,1821.778,1837.331,0.006,1.339,0.578,,0.0,,1
|
||||
21,1837.333,1853.345,0.003,0.894,0.665,,0.0,,1
|
||||
22,1853.351,1869.359,0.005,1.692,0.594,,0.0,,1
|
||||
23,1869.362,1885.372,0.004,1.783,0.522,,0.0,,1
|
||||
24,1885.46,1901.387,0.088,1.441,0.619,,0.0,,1
|
||||
25,1901.389,1916.91,0.003,0.697,0.638,,0.0,,1
|
||||
26,1916.916,1932.443,0.006,2.677,0.607,,0.0,,1
|
||||
27,1932.448,1948.454,0.005,1.129,0.654,,0.0,,1
|
||||
28,1948.456,1963.974,0.003,0.85,0.665,,0.0,,1
|
||||
29,1963.979,1979.984,0.006,0.597,0.782,,0.0,,1
|
||||
30,1979.987,1996.0,0.003,0.511,0.774,,0.0,,1
|
||||
31,1996.003,2012.013,0.004,0.555,0.751,,0.0,,1
|
||||
32,2012.015,2028.032,0.002,0.211,0.809,,0.0,,1
|
||||
33,2028.035,2043.55,0.002,0.589,0.698,,0.0,,1
|
||||
34,2043.552,2059.84,0.002,0.714,0.636,,0.0,,1
|
||||
35,2059.846,2075.579,0.006,1.253,0.504,,0.0,,1
|
||||
36,2075.67,2091.594,0.09,0.486,0.532,,0.0,,1
|
||||
37,2091.601,2107.883,0.007,0.13,0.0,,0.0,,1
|
||||
38,2107.89,2123.633,0.006,0.19,0.0,,0.0,,1
|
||||
39,2123.64,2139.648,0.006,0.593,0.0,,0.0,,1
|
||||
40,2139.653,2141.213,0.005,0.967,0.0,0.164,0.214,1,0
|
||||
|
+36
@@ -0,0 +1,36 @@
|
||||
batch,tile_count,prepared_ms,prepared_frame,submitted_ms,submitted_frame,completed_ms,completed_frame,uploaded_ms,uploaded_frame,resident_ms,resident_frame
|
||||
0,1,1388.247,2,1397.152,2,1428.933,3,1428.933,3,1428.944,3
|
||||
1,32,1413.788,3,1428.779,3,1452.575,4,1452.575,4,1452.962,4
|
||||
2,32,1446.663,4,1452.387,4,1477.841,5,1477.841,5,1478.167,5
|
||||
3,32,1476.904,5,1477.716,5,1509.58,6,1509.58,6,1509.904,6
|
||||
4,32,1508.634,6,1509.455,6,1540.823,7,1540.823,7,1541.164,7
|
||||
5,32,1539.89,7,1540.698,7,1571.391,8,1571.391,8,1571.745,8
|
||||
6,32,1570.524,8,1571.348,8,1588.361,9,1588.361,9,1588.831,9
|
||||
7,32,1587.36,9,1588.213,9,1619.568,10,1619.568,10,1619.906,10
|
||||
8,32,1618.58,10,1619.437,10,1637.409,11,1637.409,11,1637.768,11
|
||||
9,32,1636.46,11,1637.279,11,1669.497,12,1669.497,12,1669.875,12
|
||||
10,32,1668.525,12,1669.335,12,1712.223,14,1712.223,14,1715.47,14
|
||||
11,32,1696.536,13,1696.773,13,1790.566,18,1790.566,18,1790.965,18
|
||||
12,32,1716.002,14,1716.131,14,1806.677,19,1806.677,19,1807.244,19
|
||||
13,32,1744.93,15,1745.036,15,1806.908,19,1806.908,19,1807.551,19
|
||||
14,32,1760.01,16,1760.119,16,1807.254,19,1807.254,19,1807.607,19
|
||||
15,32,1776.158,17,1776.262,17,1822.736,20,1822.736,20,1823.076,20
|
||||
16,32,1791.398,18,1791.506,18,1870.466,23,1870.466,23,1870.791,23
|
||||
17,32,1807.989,19,1808.168,19,1870.794,23,1870.794,23,1871.105,23
|
||||
18,32,1823.473,20,1823.582,20,1886.083,24,1886.083,24,1886.458,24
|
||||
19,32,1838.669,21,1838.779,21,1886.461,24,1886.461,24,1886.87,24
|
||||
20,32,1855.403,22,1855.522,22,1918.875,26,1918.875,26,1919.237,26
|
||||
21,32,1871.524,23,1871.632,23,1919.24,26,1919.24,26,1919.564,26
|
||||
22,32,1887.293,24,1887.406,24,1933.218,27,1933.218,27,1933.548,27
|
||||
23,32,1902.493,25,1902.611,25,1964.111,29,1964.111,29,1964.532,29
|
||||
24,32,1919.974,26,1920.09,26,1980.078,30,1980.078,30,1980.454,30
|
||||
25,32,1933.992,27,1934.117,27,1996.101,31,1996.101,31,1996.514,31
|
||||
26,32,1949.667,28,1949.805,28,2028.126,33,2028.126,33,2028.542,33
|
||||
27,32,1965.075,29,1965.229,29,2043.643,34,2043.643,34,2044.176,34
|
||||
28,32,1980.984,30,1981.137,30,2059.958,35,2059.958,35,2060.398,35
|
||||
29,32,1996.98,31,1997.17,31,2060.401,35,2060.401,35,2060.745,35
|
||||
30,32,2012.661,32,2012.887,32,2060.748,35,2060.748,35,2061.077,35
|
||||
31,32,2029.032,33,2029.187,33,2075.774,36,2075.774,36,2076.128,36
|
||||
32,32,2044.651,34,2044.782,34,2123.753,39,2123.753,39,2124.177,39
|
||||
33,32,2061.439,35,2061.568,35,2139.765,40,2139.765,40,2140.204,40
|
||||
34,25,2076.466,36,2076.657,36,2140.207,40,2140.207,40,2140.534,40
|
||||
|
Reference in new issue
Block a user