wip(standalone): GPU step 18 -- warm-up marker: hybrid now wins cached and ties first launch
App-owned marker (bin/VoxelForgeGpuWarmup.state: adapter, driver, shader sources and
binaries). Miss -> CPU builds the first view, shader warm-up runs after it. Match ->
warm-up overlaps startup, at Normal thread priority (was Idle).
My runs outside the sandbox (Step18.exe, marker removed first, interleaved):
CPU 1.395 / 1.363 / 1.329 s
GPU 1.409 (marker miss, cold driver: 0 GPU tiles, warm-up 683 ms after present)
/ 1.290 / 1.278 / 1.289 s (marker match: warm-up queue 4.6 / 3.6 / 191 ms,
GPU took 1,394 / 1,410 / 1,115 tiles, CPU mesh sum 5.1 -> 1.3-2.0 s,
GPU busy 206-227 ms)
Cached target was <= 1.26 s: not met. With the GPU taking almost every tile, the
wall barely moves, so the rest of the 1.28 s is outside meshing. Known risk: if the
marker matches but the driver cache was evicted, the launch pays ~2.2 s (Luna's
sandbox runs).
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 tiles 0 diffs vs CPU
and WARP, thin 353 cells, survival 6.14 GB, ten captures (2+2 edge pixels as step 17).
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
6f2d33e0a9
commit
2fea2710f7
6 files changed
+292
-15
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param([string]$ExecutableName='VoxelForgeGpuFarAgreement.exe',[string]$LogStem='whole_far_rx7900_warp',[switch]$WholeFar,[switch]$WholeNear,[int]$ClipRadius=3)
|
param([string]$ExecutableName='VoxelForgeGpuFarAgreement.exe',[string]$LogStem='whole_far_rx7900_warp',[switch]$WholeFar,[switch]$WholeNear,[switch]$FindThinFeature,[int]$ClipRadius=3)
|
||||||
|
|
||||||
$ErrorActionPreference='Stop'
|
$ErrorActionPreference='Stop'
|
||||||
if([IO.Path]::GetFileName($LogStem)-ne$LogStem){throw 'LogStem must be a filename stem.'}
|
if([IO.Path]::GetFileName($LogStem)-ne$LogStem){throw 'LogStem must be a filename stem.'}
|
||||||
@@ -16,6 +16,8 @@ if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU field
|
|||||||
$Arguments=@("--clip-radius=$ClipRadius");if($WholeFar){$Arguments+='--whole-far'}
|
$Arguments=@("--clip-radius=$ClipRadius");if($WholeFar){$Arguments+='--whole-far'}
|
||||||
if($WholeNear){$Arguments+='--whole-near'}
|
if($WholeNear){$Arguments+='--whole-near'}
|
||||||
if($WholeFar-and$WholeNear){throw 'Choose either WholeFar or WholeNear, not both.'}
|
if($WholeFar-and$WholeNear){throw 'Choose either WholeFar or WholeNear, not both.'}
|
||||||
|
if($FindThinFeature-and($WholeFar-or$WholeNear)){throw 'FindThinFeature selects one targeted tile; do not combine it with WholeFar or WholeNear.'}
|
||||||
|
if($FindThinFeature){$Arguments+='--find-thin-feature'}
|
||||||
|
|
||||||
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ param(
|
|||||||
[int]$ClipRadius=3,
|
[int]$ClipRadius=3,
|
||||||
[string]$CaptureName='',
|
[string]$CaptureName='',
|
||||||
[string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe',
|
[string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe',
|
||||||
[switch]$WarmupBeforeWorkers
|
[switch]$WarmupBeforeWorkers,
|
||||||
|
[ValidateSet('auto','cold','cached')][string]$WarmupPolicy='auto'
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference='Stop'
|
$ErrorActionPreference='Stop'
|
||||||
@@ -24,6 +25,8 @@ $Arguments=@("--clip-radius=$ClipRadius",'--workers=8','--quit-after-clipmap')
|
|||||||
if($Mode-eq'gpu'){$Arguments+='--gpu-far'}
|
if($Mode-eq'gpu'){$Arguments+='--gpu-far'}
|
||||||
elseif($Mode-eq'cpu'){$Arguments+='--cpu-far'}
|
elseif($Mode-eq'cpu'){$Arguments+='--cpu-far'}
|
||||||
else{$Arguments+='--gpu-far-audit'}
|
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$WarmupBeforeWorkers){$Arguments+='--gpu-far-warmup-before-workers'}
|
||||||
if($CaptureName)
|
if($CaptureName)
|
||||||
{
|
{
|
||||||
@@ -34,6 +37,7 @@ if($CaptureName)
|
|||||||
}
|
}
|
||||||
$Command="`"$ExecutablePath`" $($Arguments -join ' ')"
|
$Command="`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||||
Write-Output "VIEWER_COMMAND mode=$Mode $Command"
|
Write-Output "VIEWER_COMMAND mode=$Mode $Command"
|
||||||
|
if($Mode-eq'gpu'){Write-Output "WARMUP_POLICY requested=$WarmupPolicy state_file=bin\VoxelForgeGpuWarmup.state"}
|
||||||
|
|
||||||
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
for($Attempt=0;$Attempt -lt 2;$Attempt++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# GPU field port — Step 18 report
|
||||||
|
|
||||||
|
Date: 2026-10-07. The viewer and agreement runner were built from `Standalone\` with `/MP8`. Viewer and audit processes ran BelowNormal with affinity `0xFF00`; memory stayed below 8 GB private plus GPU-local memory. No `0xC0000142` retry was needed. All viewers exited. No driver or cache was manually inspected, cleared, or edited; shader warm-up ran through the application as usual. No full-size image was opened in an image viewer.
|
||||||
|
|
||||||
|
## Startup policy
|
||||||
|
|
||||||
|
The viewer now keeps an app-owned warm-up record at `Standalone\bin\VoxelForgeGpuWarmup.state`. Its key covers the adapter LUID and IDs, driver version, the HLSL source, the GPU runner source, and the production shader binaries. It is a history hint; it does not inspect or prove the contents of the driver cache.
|
||||||
|
|
||||||
|
With plain `--gpu-far`, a missing or mismatched record defers GPU pipeline creation and warm-up until the initial CPU view has been presented. A completed full shader warm-up records the key. A matching record selects the early warm-up route. The regular asynchronous warm-up thread now runs at Normal relative priority within the BelowNormal process; audit and before-worker modes retain their previous priority policy. The step 17 exported-mesher skirt-depth fix is unchanged.
|
||||||
|
|
||||||
|
`RunGpuFarViewer.ps1` accepts `-WarmupPolicy auto|cold|cached`:
|
||||||
|
|
||||||
|
- `auto` follows the app record.
|
||||||
|
- `cold` forces the CPU-first deferred route.
|
||||||
|
- `cached` forces the early route, whether or not the app record matches.
|
||||||
|
|
||||||
|
## Launch measurements
|
||||||
|
|
||||||
|
The interleaved CPU controls were 1.357 s and 1.355 s to `[InitialClipmapReady]` (median 1.356 s). A forced-cold launch reached readiness in 1.427 s and presented the CPU view at 1.477 s; its 726 ms warm-up started after presentation. The initial view had zero GPU submissions, and the handoff audit accounted for all 1,434 candidates as CPU-owned.
|
||||||
|
|
||||||
|
The sandbox did not retain a fast driver warm-up across processes. Forced-eager runs queued warm-up in 1,101 ms and 1,023 ms and reached readiness in 2.188 s and 2.212 s. A plain `auto` launch saw the app marker match, selected the early route, then queued in 1,049 ms and reached readiness in 2.143 s, again with zero GPU submissions. Those runs are cold-driver evidence, not a measurement of the owner's real cached path. The outside-sandbox `-WarmupPolicy cached` run is still needed to confirm the ≤1.26 s target.
|
||||||
|
|
||||||
|
## Correctness and residency audits
|
||||||
|
|
||||||
|
- CPU W1 and W8 hashes matched: `0xdb2991c73215af4c`, `0x8236c5edd7fc8e4f`, `0x4c48fa75a543d58e`, `0xbea22e76b34a4dfc`. Walkability passed **5,642/5,642**.
|
||||||
|
- Whole-near agreement passed on **960 tiles**, with zero topology differences on hardware and WARP and zero differences across **229,376** protected-exception cells.
|
||||||
|
- Whole-far agreement passed on **1,114 tiles** (152 transition tiles skipped), with zero topology differences on hardware and WARP, zero differences across **127,401** protected-exception cells, and zero differences across **353** thin-feature cells.
|
||||||
|
- The selected thin-feature tile `(-1,0,-1,lod3)` audited **18** thin-feature cells and **35** protected-exception cells, with zero topology differences on hardware and WARP. The one-to-one LOD3 tile control `(6,2,0,lod3)` matched **680/680** surface triangles and **64/64** visible skirt triangles, with zero unmatched triangles and 0.001892 cm maximum surface vertex error. The regular GPU, direct-draw, readback-copy, meter-copy, and CPU-mesh draw controls had pixel-identical captures to CPU.
|
||||||
|
- GPU survival passed with `mesh_source=GPU` and required field readback. Peak private memory was **3.53 GB** and DXGI-local memory was **2,674.8 MB**, totaling **6.14 GB** conservatively.
|
||||||
|
- All ten full-residency CPU/GPU view pairs completed. Numeric RGB comparison found zero CPU-rock-to-GPU-background pixels in eight views, two in `remote_yaw180`, and two in `remote_yaw270`, matching the step 17 capture result. Peak combined memory was **6.17 GB**. The comparison is [step18_final_capture_comparison.md](step18_final_capture_comparison.md).
|
||||||
|
- The tile comparison is [step18_tilecontrol_comparison.md](step18_tilecontrol_comparison.md); whole agreement logs are `step18_whole_near_agreement.stdout.log`, `step18_whole_far_agreement.stdout.log`, and `step18_thin_feature_audit.stdout.log`. CPU reference and survival logs use the `step18_` prefix.
|
||||||
|
|
||||||
|
## Diagnostic note
|
||||||
|
|
||||||
|
The optional `gpu_skirts_off` and `gpu_skirts_connectors_off` controls make the pooled readback checker report `indirect_args_valid=0`; their CPU/GPU triangle audit and skirt draw-argument summary still pass with skirts intentionally absent. Their captures show the expected coverage loss from disabling skirts. The normal GPU path, direct draw, and step 17 skirt-depth coverage control pass.
|
||||||
|
|
||||||
|
No commit, push, or stash was made.
|
||||||
|
|
||||||
|
## Note back
|
||||||
|
|
||||||
|
The ordered goals, measured-versus-hypothesis labels, launch table, and explicit operating constraints are the most useful parts of the brief. The report and recent history prevented rediscovering why the skirt fix was needed. A compact current-runner command list with pass criteria is the main thing I still had to reconstruct from older scripts. The repeated operating rules are useful here because they apply to every audit run.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Step18_Final 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) |
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Step 17 LOD3 tile control comparison
|
||||||
|
|
||||||
|
The `(6,2,0,lod3)` target was the only drawn tile, with the full desired clipmap retained. Captures were loaded as RGB arrays and compared numerically; full-size images were not opened in a viewer.
|
||||||
|
|
||||||
|
`CPU rock to GPU background` counts pixels where the CPU target is rock `(96,86,68)` and the control is background `(14,19,27)`, the prior missing-coverage signature.
|
||||||
|
|
||||||
|
| Control | Size | RGB MAE | RGB RMSE | Changed pixels | CPU rock to GPU background | Max channel |
|
||||||
|
|---|---:|---:|---:|---:|---:|---:|
|
||||||
|
| GPU indirect | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| GPU direct | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| GPU readback copy | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| GPU meter copy | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| CPU mesh GPU path | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| Depth disabled | 1280x800 | 0.0097 | 0.4705 | 0.1337% | 0 | 65 |
|
||||||
|
| Skirts disabled | 1280x800 | 1.6339 | 10.5157 | 2.5859% | 26055 | 82 |
|
||||||
|
| Connectors disabled | 1280x800 | 0.0000 | 0.0000 | 0.0000% | 0 | 0 |
|
||||||
|
| Skirts and connectors disabled | 1280x800 | 1.6339 | 10.5157 | 2.5859% | 26055 | 82 |
|
||||||
@@ -1395,6 +1395,13 @@ namespace
|
|||||||
bool bGpuComplete=false,bTimingResolved=false;
|
bool bGpuComplete=false,bTimingResolved=false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class EGpuWarmupLaunchPolicy
|
||||||
|
{
|
||||||
|
Auto,
|
||||||
|
ForceDeferred,
|
||||||
|
ForceEager
|
||||||
|
};
|
||||||
|
|
||||||
uint64 HashStandaloneFile(const std::string& Path)
|
uint64 HashStandaloneFile(const std::string& Path)
|
||||||
{
|
{
|
||||||
std::ifstream File(Path,std::ios::binary);
|
std::ifstream File(Path,std::ios::binary);
|
||||||
@@ -1510,6 +1517,11 @@ namespace
|
|||||||
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
|
bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false;
|
||||||
bool bGpuFarWarmupResourcesOnly=false;
|
bool bGpuFarWarmupResourcesOnly=false;
|
||||||
bool bGpuFarWarmupNormalPriority=false,bGpuFarWarmupBeforeWorkers=false;
|
bool bGpuFarWarmupNormalPriority=false,bGpuFarWarmupBeforeWorkers=false;
|
||||||
|
EGpuWarmupLaunchPolicy GpuWarmupLaunchPolicy=EGpuWarmupLaunchPolicy::Auto;
|
||||||
|
bool bGpuWarmupDeferredUntilFirstView=false,bGpuWarmupDeferredStarted=false;
|
||||||
|
bool bGpuWarmupCacheKeyAvailable=false;
|
||||||
|
std::filesystem::path GpuWarmupStatePath;
|
||||||
|
std::string GpuWarmupCacheKey,GpuWarmupCacheIdentity;
|
||||||
bool bGpuFarWarmupKernelFences=false;
|
bool bGpuFarWarmupKernelFences=false;
|
||||||
bool bGpuFarWarmupOnly=false;
|
bool bGpuFarWarmupOnly=false;
|
||||||
std::string GpuWarmupEntryFilter;
|
std::string GpuWarmupEntryFilter;
|
||||||
@@ -1952,6 +1964,156 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BuildGpuWarmupCacheKey()
|
||||||
|
{
|
||||||
|
GpuWarmupCacheKey.clear();
|
||||||
|
GpuWarmupCacheIdentity.clear();
|
||||||
|
if(!Device||GpuFarShaderPath.empty())return false;
|
||||||
|
|
||||||
|
IDXGIDevice* DxgiDevice=nullptr;
|
||||||
|
if(FAILED(Device->QueryInterface(__uuidof(IDXGIDevice),
|
||||||
|
reinterpret_cast<void**>(&DxgiDevice)))||!DxgiDevice)return false;
|
||||||
|
IDXGIAdapter* Adapter=nullptr;
|
||||||
|
const HRESULT AdapterResult=DxgiDevice->GetAdapter(&Adapter);
|
||||||
|
DxgiDevice->Release();
|
||||||
|
if(FAILED(AdapterResult)||!Adapter)return false;
|
||||||
|
|
||||||
|
DXGI_ADAPTER_DESC AdapterDesc{};
|
||||||
|
const HRESULT DescResult=Adapter->GetDesc(&AdapterDesc);
|
||||||
|
LARGE_INTEGER DriverVersion{};
|
||||||
|
const HRESULT DriverResult=Adapter->CheckInterfaceSupport(
|
||||||
|
__uuidof(IDXGIDevice),&DriverVersion);
|
||||||
|
Adapter->Release();
|
||||||
|
if(FAILED(DescResult)||FAILED(DriverResult))return false;
|
||||||
|
|
||||||
|
static constexpr std::array<const char*,14> WarmupEntries={
|
||||||
|
"EvaluateFarFieldCS","EvaluateNearFieldCS","ClassifyCS","ClassifyFarCS",
|
||||||
|
"ScanGroupsCS","EmitCS","ApplyNearFieldDetailCS","EmitNearCS",
|
||||||
|
"BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS","ClassifyFarSkirtsCS",
|
||||||
|
"ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"};
|
||||||
|
uint64 ShaderSetHash=14695981039346656037ull;
|
||||||
|
const auto MixText=[&](std::string_view Text)
|
||||||
|
{
|
||||||
|
for(const unsigned char Byte:Text)
|
||||||
|
{ShaderSetHash^=Byte;ShaderSetHash*=1099511628211ull;}
|
||||||
|
ShaderSetHash^=0xffu;ShaderSetHash*=1099511628211ull;
|
||||||
|
};
|
||||||
|
const auto MixFile=[&](const std::filesystem::path& Path,bool bRequired)
|
||||||
|
{
|
||||||
|
std::error_code Ec;
|
||||||
|
const bool bExists=std::filesystem::is_regular_file(Path,Ec)&&!Ec;
|
||||||
|
MixText(Path.filename().string());
|
||||||
|
if(!bExists)
|
||||||
|
{
|
||||||
|
MixText("missing");
|
||||||
|
return !bRequired;
|
||||||
|
}
|
||||||
|
const uint64 FileHash=HashStandaloneFile(Path.string());
|
||||||
|
if(FileHash==0)return false;
|
||||||
|
const uint64 FileSize=std::filesystem::file_size(Path,Ec);
|
||||||
|
if(Ec)return false;
|
||||||
|
MixText(std::to_string(FileSize));
|
||||||
|
MixText(std::to_string(FileHash));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
MixText("gpu-far-warmup-signature-v1");
|
||||||
|
const std::filesystem::path ShaderImplementation=GpuFarShaderPath.parent_path()
|
||||||
|
.parent_path()/"gpu_field_port"/"GpuFieldPort.cpp";
|
||||||
|
if(!MixFile(GpuFarShaderPath,true)||!MixFile(ShaderImplementation,true))return false;
|
||||||
|
const std::filesystem::path PrecompiledDirectory=GpuFarShaderPath.parent_path()
|
||||||
|
.parent_path()/"gpu_field_port"/"precompiled";
|
||||||
|
for(const char* Entry:WarmupEntries)
|
||||||
|
if(!MixFile(PrecompiledDirectory/(std::string(Entry)+".cso"),false))return false;
|
||||||
|
|
||||||
|
std::ostringstream Identity;
|
||||||
|
Identity<<"adapter_luid="<<std::hex<<std::uppercase
|
||||||
|
<<static_cast<uint32>(AdapterDesc.AdapterLuid.HighPart)<<":"<<AdapterDesc.AdapterLuid.LowPart
|
||||||
|
<<" vendor="<<AdapterDesc.VendorId<<" device="<<AdapterDesc.DeviceId
|
||||||
|
<<" subsystem="<<AdapterDesc.SubSysId<<" revision="<<AdapterDesc.Revision
|
||||||
|
<<" driver_version=0x"<<static_cast<uint64>(DriverVersion.QuadPart)
|
||||||
|
<<" shader_set=0x"<<ShaderSetHash;
|
||||||
|
GpuWarmupCacheIdentity=Identity.str();
|
||||||
|
GpuWarmupCacheKey="VF_GPU_WARMUP_V1 "+GpuWarmupCacheIdentity;
|
||||||
|
bGpuWarmupCacheKeyAvailable=true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResolveGpuWarmupLaunchPolicy()
|
||||||
|
{
|
||||||
|
if(!bGpuFarEnabled||!bGpuFarShaderPrewarm)return;
|
||||||
|
const bool bKeyAvailable=BuildGpuWarmupCacheKey();
|
||||||
|
bool bCacheMarkerMatches=false;
|
||||||
|
if(bKeyAvailable&&!GpuWarmupStatePath.empty())
|
||||||
|
{
|
||||||
|
std::ifstream Marker(GpuWarmupStatePath,std::ios::binary);
|
||||||
|
std::string SavedKey;
|
||||||
|
if(Marker&&std::getline(Marker,SavedKey))
|
||||||
|
bCacheMarkerMatches=SavedKey==GpuWarmupCacheKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bGpuFarWarmupBeforeWorkers)
|
||||||
|
{
|
||||||
|
bGpuWarmupDeferredUntilFirstView=false;
|
||||||
|
std::printf("[GpuWarmupLaunchDecision] mode=before_workers cache_marker=%s key_available=%s identity=%s\n",
|
||||||
|
bCacheMarkerMatches?"match":"miss",bKeyAvailable?"yes":"no",
|
||||||
|
bKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceDeferred)
|
||||||
|
bGpuWarmupDeferredUntilFirstView=true;
|
||||||
|
else if(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceEager)
|
||||||
|
bGpuWarmupDeferredUntilFirstView=false;
|
||||||
|
else
|
||||||
|
bGpuWarmupDeferredUntilFirstView=!bCacheMarkerMatches;
|
||||||
|
|
||||||
|
const char* Mode=GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceDeferred
|
||||||
|
?"forced_cold_start":(GpuWarmupLaunchPolicy==EGpuWarmupLaunchPolicy::ForceEager
|
||||||
|
?"forced_cached_start":"auto");
|
||||||
|
std::printf("[GpuWarmupLaunchDecision] mode=%s cache_marker=%s route=%s key_available=%s state_path=%s identity=%s\n",
|
||||||
|
Mode,bCacheMarkerMatches?"match":"miss",
|
||||||
|
bGpuWarmupDeferredUntilFirstView?"after_first_CPU_view":"startup_overlap",
|
||||||
|
bKeyAvailable?"yes":"no",GpuWarmupStatePath.string().c_str(),
|
||||||
|
bKeyAvailable?GpuWarmupCacheIdentity.c_str():"unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkGpuWarmupCacheReady()
|
||||||
|
{
|
||||||
|
if(!bGpuFarShaderPrewarm||bGpuFarWarmupResourcesOnly
|
||||||
|
||!GpuWarmupEntryFilter.empty()||!bGpuWarmupCacheKeyAvailable
|
||||||
|
||GpuWarmupStatePath.empty())return;
|
||||||
|
const std::filesystem::path TemporaryPath=GpuWarmupStatePath.wstring()
|
||||||
|
+L"."+std::to_wstring(GetCurrentProcessId())+L".tmp";
|
||||||
|
{
|
||||||
|
std::ofstream Marker(TemporaryPath,std::ios::binary|std::ios::trunc);
|
||||||
|
if(!Marker)
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[GpuWarmupCacheState] write_failed path=%s reason=open_failed\n",
|
||||||
|
TemporaryPath.string().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Marker<<GpuWarmupCacheKey;
|
||||||
|
Marker.flush();
|
||||||
|
if(!Marker.good())
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[GpuWarmupCacheState] write_failed path=%s reason=flush_failed\n",
|
||||||
|
TemporaryPath.string().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!MoveFileExW(TemporaryPath.c_str(),GpuWarmupStatePath.c_str(),
|
||||||
|
MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH))
|
||||||
|
{
|
||||||
|
const DWORD Error=GetLastError();
|
||||||
|
DeleteFileW(TemporaryPath.c_str());
|
||||||
|
std::fprintf(stderr,"[GpuWarmupCacheState] write_failed path=%s win32_error=%lu\n",
|
||||||
|
GpuWarmupStatePath.string().c_str(),static_cast<unsigned long>(Error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::printf("[GpuWarmupCacheState] result=recorded path=%s identity=%s\n",
|
||||||
|
GpuWarmupStatePath.string().c_str(),GpuWarmupCacheIdentity.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
bool SetGpuWarmupContextProtection(bool bProtected)
|
bool SetGpuWarmupContextProtection(bool bProtected)
|
||||||
{
|
{
|
||||||
if(!Context)return false;
|
if(!Context)return false;
|
||||||
@@ -1975,18 +2137,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
std::fprintf(stderr,"[GpuWarmupContextProtection] enable_failed; async immediate-context protection is unavailable\n");
|
std::fprintf(stderr,"[GpuWarmupContextProtection] enable_failed; async immediate-context protection is unavailable\n");
|
||||||
GpuShaderWarmupThread=std::thread([this]()
|
GpuShaderWarmupThread=std::thread([this]()
|
||||||
{
|
{
|
||||||
// Keep cold driver compilation from stealing CPU time from the CPU fallback
|
|
||||||
// workers. Cached shader warm-up is only a few milliseconds, so this does not
|
|
||||||
// delay the normal GPU route when the driver cache is warm.
|
|
||||||
const bool bPreserveAuditPriority=bGpuFarWarmupBeforeWorkers||bGpuFarAuditEnabled;
|
const bool bPreserveAuditPriority=bGpuFarWarmupBeforeWorkers||bGpuFarAuditEnabled;
|
||||||
const int WarmupPriority=bGpuFarWarmupNormalPriority?THREAD_PRIORITY_HIGHEST:
|
const int WarmupPriority=bGpuFarWarmupNormalPriority?THREAD_PRIORITY_HIGHEST:
|
||||||
(bPreserveAuditPriority?THREAD_PRIORITY_BELOW_NORMAL:THREAD_PRIORITY_IDLE);
|
(bPreserveAuditPriority?THREAD_PRIORITY_BELOW_NORMAL:THREAD_PRIORITY_NORMAL);
|
||||||
const bool bPrioritySet=SetThreadPriority(GetCurrentThread(),WarmupPriority)!=0;
|
const bool bPrioritySet=SetThreadPriority(GetCurrentThread(),WarmupPriority)!=0;
|
||||||
std::printf("[GpuShaderWarmupThread] priority=%s process=BelowNormal thread_relative=%s\n",
|
const char* RelativePriority=bGpuFarWarmupNormalPriority?"Highest":
|
||||||
bPrioritySet?(bGpuFarWarmupNormalPriority?"Normal":
|
(bPreserveAuditPriority?"BelowNormal":"Normal");
|
||||||
(bPreserveAuditPriority?"BelowNormal":"Idle")):"set_failed",
|
std::printf("[GpuShaderWarmupThread] priority=%s process=BelowNormal thread_relative=%s purpose=%s\n",
|
||||||
bGpuFarWarmupNormalPriority?"Highest":
|
bPrioritySet?RelativePriority:"set_failed",RelativePriority,
|
||||||
(bPreserveAuditPriority?"BelowNormal":"Idle"));
|
bGpuWarmupDeferredUntilFirstView?"post_first_CPU_view":
|
||||||
|
(bPreserveAuditPriority?"audit_or_before_workers":"cache_candidate_startup"));
|
||||||
std::fflush(stdout);
|
std::fflush(stdout);
|
||||||
bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup(
|
bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup(
|
||||||
Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,GpuWarmupEntryFilter,
|
Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,GpuWarmupEntryFilter,
|
||||||
@@ -1995,7 +2155,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
bGpuShaderWarmupQueueFinished.store(true,std::memory_order_release);
|
bGpuShaderWarmupQueueFinished.store(true,std::memory_order_release);
|
||||||
});
|
});
|
||||||
std::printf("[GpuShaderWarmup] queue_thread=started overlap=%s\n",
|
std::printf("[GpuShaderWarmup] queue_thread=started overlap=%s\n",
|
||||||
bGpuFarWarmupBeforeWorkers?"none_before_generation_workers":"CPU_world_configuration");
|
bGpuWarmupDeferredUntilFirstView?"after_first_CPU_view":
|
||||||
|
(bGpuFarWarmupBeforeWorkers?"none_before_generation_workers":"CPU_world_configuration"));
|
||||||
std::fflush(stdout);
|
std::fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2260,9 +2421,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
{std::fprintf(stderr,"World load failed: %s\n",Error.c_str());MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;}
|
{std::fprintf(stderr,"World load failed: %s\n",Error.c_str());MessageBoxA(Window,Error.c_str(),"VoxelForge viewer",MB_ICONERROR);return;}
|
||||||
std::printf("[ColdTimeline] stage=generation_setup configured_ms=%.3f\n",
|
std::printf("[ColdTimeline] stage=generation_setup configured_ms=%.3f\n",
|
||||||
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count());
|
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count());
|
||||||
if(bGpuFarEnabled&&!EnsureGpuFarPipeline(Error))
|
const bool bPipelineCanStartNow=!bGpuWarmupDeferredUntilFirstView
|
||||||
|
||bGpuWarmupDeferredStarted;
|
||||||
|
if(bGpuFarEnabled&&bPipelineCanStartNow&&!EnsureGpuFarPipeline(Error))
|
||||||
{MessageBoxA(Window,Error.c_str(),"VoxelForge GPU pipeline",MB_ICONERROR);return;}
|
{MessageBoxA(Window,Error.c_str(),"VoxelForge GPU pipeline",MB_ICONERROR);return;}
|
||||||
if(bGpuFarShaderPrewarm&&!bGpuFarWarmupBeforeWorkers)
|
if(bGpuFarShaderPrewarm&&!bGpuFarWarmupBeforeWorkers
|
||||||
|
&&!bGpuWarmupDeferredUntilFirstView)
|
||||||
StartGpuShaderWarmupAsync();
|
StartGpuShaderWarmupAsync();
|
||||||
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
|
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
|
||||||
BuildTargetChunk=StandaloneWorldMesher::CameraChunk(CameraVoxels);
|
BuildTargetChunk=StandaloneWorldMesher::CameraChunk(CameraVoxels);
|
||||||
@@ -2925,6 +3089,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bGpuWarmupCompleted=true;
|
bGpuWarmupCompleted=true;
|
||||||
|
MarkGpuWarmupCacheReady();
|
||||||
SetGpuWarmupContextProtection(false);
|
SetGpuWarmupContextProtection(false);
|
||||||
std::printf("[GpuShaderWarmupComplete] completion_wall_ms=%.3f\n",
|
std::printf("[GpuShaderWarmupComplete] completion_wall_ms=%.3f\n",
|
||||||
std::chrono::duration<double,std::milli>(
|
std::chrono::duration<double,std::milli>(
|
||||||
@@ -3011,6 +3176,24 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
void PumpGpuFarGeneration()
|
void PumpGpuFarGeneration()
|
||||||
{
|
{
|
||||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context)return;
|
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context)return;
|
||||||
|
if(bGpuWarmupDeferredUntilFirstView&&!bGpuWarmupDeferredStarted)
|
||||||
|
{
|
||||||
|
if(!bWholeViewPresented)return;
|
||||||
|
std::string DeferredPipelineError;
|
||||||
|
if(!EnsureGpuFarPipeline(DeferredPipelineError))
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[GpuPipelineInitError] deferred_after_first_CPU_view: %s\n",
|
||||||
|
DeferredPipelineError.c_str());
|
||||||
|
bGpuFarEnabled=false;bGpuWarmupDeferredStarted=true;
|
||||||
|
AddGpuFarCpuFallbackTiles(DesiredTiles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bGpuWarmupDeferredStarted=true;
|
||||||
|
std::printf("[GpuWarmupDeferredStart] at_ms=%.3f first_CPU_view_presented=true\n",
|
||||||
|
std::chrono::duration<double,std::milli>(
|
||||||
|
std::chrono::steady_clock::now()-ColdProcessStart).count());
|
||||||
|
StartGpuShaderWarmupAsync();
|
||||||
|
}
|
||||||
std::string PipelineError;
|
std::string PipelineError;
|
||||||
if(!EnsureGpuFarPipeline(PipelineError))
|
if(!EnsureGpuFarPipeline(PipelineError))
|
||||||
{bGpuFarEnabled=false;AddGpuFarCpuFallbackTiles(DesiredTiles);return;}
|
{bGpuFarEnabled=false;AddGpuFarCpuFallbackTiles(DesiredTiles);return;}
|
||||||
@@ -4663,7 +4846,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
std::chrono::duration<double,std::milli>(GpuPumpEnd-CpuPumpEnd).count());
|
std::chrono::duration<double,std::milli>(GpuPumpEnd-CpuPumpEnd).count());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(bGpuFarEnabled&&bGpuFarShaderPrewarm&&!bGpuWarmupCompleted
|
if(bGpuFarEnabled&&bGpuFarShaderPrewarm&&!bGpuWarmupDeferredUntilFirstView
|
||||||
|
&&!bGpuWarmupCompleted
|
||||||
&&!bGpuShaderWarmupQueueFinished.load(std::memory_order_acquire))
|
&&!bGpuShaderWarmupQueueFinished.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
if(!bGpuWarmupPresentSkipReported)
|
if(!bGpuWarmupPresentSkipReported)
|
||||||
@@ -6544,8 +6728,12 @@ int main(int Argc,char** Argv)
|
|||||||
wchar_t ExecutablePath[MAX_PATH]{};
|
wchar_t ExecutablePath[MAX_PATH]{};
|
||||||
const DWORD ExecutablePathLength=GetModuleFileNameW(nullptr,ExecutablePath,MAX_PATH);
|
const DWORD ExecutablePathLength=GetModuleFileNameW(nullptr,ExecutablePath,MAX_PATH);
|
||||||
if(ExecutablePathLength>0&&ExecutablePathLength<MAX_PATH)
|
if(ExecutablePathLength>0&&ExecutablePathLength<MAX_PATH)
|
||||||
|
{
|
||||||
App.GpuFarShaderPath=std::filesystem::path(ExecutablePath).parent_path().parent_path()
|
App.GpuFarShaderPath=std::filesystem::path(ExecutablePath).parent_path().parent_path()
|
||||||
/"Research"/"OwnerShapeWorld"/"gpu_first_port"/"VoxelForgeGpuMesher.hlsl";
|
/"Research"/"OwnerShapeWorld"/"gpu_first_port"/"VoxelForgeGpuMesher.hlsl";
|
||||||
|
App.GpuWarmupStatePath=std::filesystem::path(ExecutablePath).parent_path()
|
||||||
|
/"VoxelForgeGpuWarmup.state";
|
||||||
|
}
|
||||||
std::string JoinedCommandLine;
|
std::string JoinedCommandLine;
|
||||||
for(int32 I=1;I<Argc;++I)
|
for(int32 I=1;I<Argc;++I)
|
||||||
{if(!JoinedCommandLine.empty())JoinedCommandLine.push_back(' ');JoinedCommandLine+=Argv[I];}
|
{if(!JoinedCommandLine.empty())JoinedCommandLine.push_back(' ');JoinedCommandLine+=Argv[I];}
|
||||||
@@ -6609,6 +6797,10 @@ int main(int Argc,char** Argv)
|
|||||||
App.bHoldCpuFrameDuringGpuWarmup=true;
|
App.bHoldCpuFrameDuringGpuWarmup=true;
|
||||||
else if(Arg=="--gpu-far-warmup-normal-priority")
|
else if(Arg=="--gpu-far-warmup-normal-priority")
|
||||||
App.bGpuFarWarmupNormalPriority=true;
|
App.bGpuFarWarmupNormalPriority=true;
|
||||||
|
else if(Arg=="--gpu-far-force-cold-start")
|
||||||
|
App.GpuWarmupLaunchPolicy=EGpuWarmupLaunchPolicy::ForceDeferred;
|
||||||
|
else if(Arg=="--gpu-far-force-cached-start")
|
||||||
|
App.GpuWarmupLaunchPolicy=EGpuWarmupLaunchPolicy::ForceEager;
|
||||||
else if(Arg=="--gpu-far-warmup-before-workers")
|
else if(Arg=="--gpu-far-warmup-before-workers")
|
||||||
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupBeforeWorkers=true,
|
App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupBeforeWorkers=true,
|
||||||
App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
|
App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true,
|
||||||
@@ -6875,6 +7067,7 @@ int main(int Argc,char** Argv)
|
|||||||
std::printf("[ColdTimeline] stage=viewer_device_init end_ms=%.3f duration_ms=%.3f\n",
|
std::printf("[ColdTimeline] stage=viewer_device_init end_ms=%.3f duration_ms=%.3f\n",
|
||||||
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
|
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count(),
|
||||||
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-DeviceInitStart).count());
|
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-DeviceInitStart).count());
|
||||||
|
App.ResolveGpuWarmupLaunchPolicy();
|
||||||
if(!App.bGpuFarWarmupOnly)
|
if(!App.bGpuFarWarmupOnly)
|
||||||
{
|
{
|
||||||
ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window);
|
ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window);
|
||||||
@@ -6898,6 +7091,7 @@ int main(int Argc,char** Argv)
|
|||||||
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
|
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
|
||||||
App.GpuFarShaderPath,WarmupError))
|
App.GpuFarShaderPath,WarmupError))
|
||||||
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
|
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
|
||||||
|
App.MarkGpuWarmupCacheReady();
|
||||||
uint64 WarmupDxgiUsage=0,WarmupDxgiBudget=0;
|
uint64 WarmupDxgiUsage=0,WarmupDxgiBudget=0;
|
||||||
if(VoxelForgeGpuFarViewer::QueryFarGpuMemory(App.Device,App.Context,App.GpuFarShaderPath,
|
if(VoxelForgeGpuFarViewer::QueryFarGpuMemory(App.Device,App.Context,App.GpuFarShaderPath,
|
||||||
WarmupDxgiUsage,WarmupDxgiBudget,WarmupError))
|
WarmupDxgiUsage,WarmupDxgiBudget,WarmupError))
|
||||||
@@ -6922,6 +7116,7 @@ int main(int Argc,char** Argv)
|
|||||||
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
|
if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context,
|
||||||
App.GpuFarShaderPath,WarmupError))
|
App.GpuFarShaderPath,WarmupError))
|
||||||
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
|
{std::fprintf(stderr,"[GpuShaderWarmupError] completion: %s\n",WarmupError.c_str());return 5;}
|
||||||
|
App.MarkGpuWarmupCacheReady();
|
||||||
App.SetGpuWarmupContextProtection(false);
|
App.SetGpuWarmupContextProtection(false);
|
||||||
App.bGpuWarmupCompleted=true;
|
App.bGpuWarmupCompleted=true;
|
||||||
std::printf("[GpuShaderWarmupBeforeWorkers] completed_before_generation_workers=true\n");
|
std::printf("[GpuShaderWarmupBeforeWorkers] completed_before_generation_workers=true\n");
|
||||||
|
|||||||
Reference in new issue
Block a user