wip(standalone): GPU step 29 -- viewer no longer self-resets affinity; per-exe shader bundles; W16; CPU route now fastest
Luna: the viewer reset itself to BelowNormal (since step 3) and 0xFF00 (since step 11)
at startup, overriding the runner; every earlier "full machine" race started on 8 CPUs
until the runner reapplied the mask. Fixed in viewer and agreement runner. Core
worker clamp raised to 16 (W1/W8/W16 hashes identical). Shader binaries now pinned per
executable (precompiled/<exe>/); Step27 control rebuilt from 7d1808f with only the
bundle lookup backported. Tried CPU fallback for skirted near tiles: -0.74 GiB GPU
memory but 2,934-4,086 missing pixels in four remote views, so GPU skirts stay default.
Audits: hashes W1/W8/W16, walk 5,642/5,642, near/far HW+WARP 0 diffs, both survivals
pass (7.00 GiB), ten captures 0 missing pixels.
My runs outside the sandbox (driver cache warm, marker removed first):
0xFFFF Normal: CPU 1.121 / 1.081 / 1.040 s
GPU miss 1.324 s; cached 1.340 / 1.353 / 1.293 s (2,073-2,074 GPU tiles,
timestamps 463-471 ms)
0xFF00 BelowNormal: CPU 1.305 / 1.302 s; GPU cached 1.678 / 1.692 / 1.756 s
Same-session, own shader bundles, 0xFFFF: Step27 GPU cached 1.205 / 1.212 / 1.198 / 1.180 s;
Step29 GPU cached 1.359 / 1.367 / 1.366 / 1.333 s
=> the GPU route now loses to the CPU route by ~0.25 s on the full machine.
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
5aa285c3ef
commit
61106be142
86 files changed
+449
-38
No files matched your search
@@ -304,7 +304,7 @@ namespace VoxelForgeCore
|
|||||||
auto shape=std::make_unique<FStandaloneShapeWorld>();
|
auto shape=std::make_unique<FStandaloneShapeWorld>();
|
||||||
const bool ok=shape->Build(*impl_->owner,ToNative(request.minVoxel),ToNative(request.maxVoxel),
|
const bool ok=shape->Build(*impl_->owner,ToNative(request.minVoxel),ToNative(request.maxVoxel),
|
||||||
ToNative(request.clipMinVoxel),ToNative(request.clipMaxVoxel),
|
ToNative(request.clipMinVoxel),ToNative(request.clipMaxVoxel),
|
||||||
static_cast<int32>(std::clamp<std::uint32_t>(request.workerLimit,1,8)),ToNative(request.detail),
|
static_cast<int32>(std::clamp<std::uint32_t>(request.workerLimit,1,16)),ToNative(request.detail),
|
||||||
request.includeFarWorld,error,&scheduler,request.roundTunnels,request.profileDetailBand,
|
request.includeFarWorld,error,&scheduler,request.roundTunnels,request.profileDetailBand,
|
||||||
request.validatePassageRoutes,request.gpuVisualDetailTiles,
|
request.validatePassageRoutes,request.gpuVisualDetailTiles,
|
||||||
request.cpuVisualDetailTiles);
|
request.cpuVisualDetailTiles);
|
||||||
@@ -369,7 +369,7 @@ namespace VoxelForgeCore
|
|||||||
std::vector<std::shared_ptr<FStandaloneShapeWorld>> compiledRegions(pendingRegions.size());
|
std::vector<std::shared_ptr<FStandaloneShapeWorld>> compiledRegions(pendingRegions.size());
|
||||||
std::vector<std::string> regionErrors(pendingRegions.size());
|
std::vector<std::string> regionErrors(pendingRegions.size());
|
||||||
const ShapeBuildRequest& base=impl_->buildRequest;
|
const ShapeBuildRequest& base=impl_->buildRequest;
|
||||||
const std::uint32_t clampedWorkers=std::clamp<std::uint32_t>(workerLimit,1,8);
|
const std::uint32_t clampedWorkers=std::clamp<std::uint32_t>(workerLimit,1,16);
|
||||||
|
|
||||||
if(analyticOnly)
|
if(analyticOnly)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,10 +15,14 @@ $BuildPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
|||||||
$HostProcess.PriorityClass=$BuildPriority
|
$HostProcess.PriorityClass=$BuildPriority
|
||||||
$HostProcess.ProcessorAffinity=[IntPtr][long]$AffinityValue
|
$HostProcess.ProcessorAffinity=[IntPtr][long]$AffinityValue
|
||||||
$Standalone = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
$Standalone = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||||
$BuildDir = Join-Path $Standalone 'Build\VoxelForgeGpuFarAgreement'
|
$BuildStem = [IO.Path]::GetFileNameWithoutExtension($OutputName)
|
||||||
|
$BuildDir = Join-Path $Standalone ('Build\'+$BuildStem)
|
||||||
$WrapperDir = Join-Path $BuildDir 'wrappers'
|
$WrapperDir = Join-Path $BuildDir 'wrappers'
|
||||||
$BinDir = Join-Path $Standalone 'bin'
|
$BinDir = Join-Path $Standalone 'bin'
|
||||||
$Output = Join-Path $BinDir $OutputName
|
$Output = Join-Path $BinDir $OutputName
|
||||||
|
$ShaderBaseStem = $BuildStem.Replace('Agreement','')
|
||||||
|
$ShaderSourceDir = Join-Path $Standalone ('Research\OwnerShapeWorld\gpu_field_port\precompiled\'+$ShaderBaseStem)
|
||||||
|
$ShaderBundleDir = Join-Path $Standalone ('Research\OwnerShapeWorld\gpu_field_port\precompiled\'+$BuildStem)
|
||||||
$CoreLib = Join-Path $BuildDir 'VoxelForgeGpuFieldPortCore.lib'
|
$CoreLib = Join-Path $BuildDir 'VoxelForgeGpuFieldPortCore.lib'
|
||||||
$VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat'
|
$VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat'
|
||||||
|
|
||||||
@@ -27,6 +31,22 @@ if (-not (Test-Path -LiteralPath $VcVars -PathType Leaf)) { throw "MSVC setup sc
|
|||||||
[IO.Directory]::CreateDirectory($WrapperDir) | Out-Null
|
[IO.Directory]::CreateDirectory($WrapperDir) | Out-Null
|
||||||
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
||||||
|
|
||||||
|
if (-not (Test-Path -LiteralPath $ShaderSourceDir -PathType Container)) {
|
||||||
|
$LegacyShaderSourceDir = Join-Path $Standalone 'Research\OwnerShapeWorld\gpu_field_port\precompiled'
|
||||||
|
if ($ShaderBaseStem -eq 'VoxelForgeGpuFar' -and (Test-Path -LiteralPath $LegacyShaderSourceDir -PathType Container)) {
|
||||||
|
$ShaderSourceDir = $LegacyShaderSourceDir
|
||||||
|
} else {
|
||||||
|
throw "Viewer shader bundle must be built first: $ShaderSourceDir"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[IO.Directory]::CreateDirectory($ShaderBundleDir) | Out-Null
|
||||||
|
$ShaderFiles = @(Get-ChildItem -LiteralPath $ShaderSourceDir -Filter '*.cso' -File)
|
||||||
|
if ($ShaderFiles.Count -lt 17) { throw "Viewer shader bundle is incomplete: $ShaderSourceDir ($($ShaderFiles.Count) entries)" }
|
||||||
|
foreach ($ShaderFile in $ShaderFiles) {
|
||||||
|
Copy-Item -LiteralPath $ShaderFile.FullName -Destination (Join-Path $ShaderBundleDir $ShaderFile.Name) -Force
|
||||||
|
}
|
||||||
|
Write-Host "Pinned $($ShaderFiles.Count) shader binaries for $OutputName in $ShaderBundleDir"
|
||||||
|
|
||||||
$Manifest = Join-Path $Standalone 'Core\core_sources.txt'
|
$Manifest = Join-Path $Standalone 'Core\core_sources.txt'
|
||||||
$Sources = @(Get-Content -LiteralPath $Manifest | ForEach-Object { $_.Trim() } |
|
$Sources = @(Get-Content -LiteralPath $Manifest | ForEach-Object { $_.Trim() } |
|
||||||
Where-Object { $_ -and -not $_.StartsWith('#') })
|
Where-Object { $_ -and -not $_.StartsWith('#') })
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ $BuildStem = [IO.Path]::GetFileNameWithoutExtension($OutputName)
|
|||||||
$BuildDir = Join-Path $Root ('Build\'+$BuildStem)
|
$BuildDir = Join-Path $Root ('Build\'+$BuildStem)
|
||||||
$BinDir = Join-Path $Root 'bin'
|
$BinDir = Join-Path $Root 'bin'
|
||||||
$ShaderSource = Join-Path $Root 'Research\OwnerShapeWorld\gpu_first_port\VoxelForgeGpuMesher.hlsl'
|
$ShaderSource = Join-Path $Root 'Research\OwnerShapeWorld\gpu_first_port\VoxelForgeGpuMesher.hlsl'
|
||||||
$PrecompiledShaderDir = Join-Path $Root 'Research\OwnerShapeWorld\gpu_field_port\precompiled'
|
$PrecompiledShaderDir = Join-Path $Root ('Research\OwnerShapeWorld\gpu_field_port\precompiled\'+$BuildStem)
|
||||||
[IO.Directory]::CreateDirectory($BuildDir) | Out-Null
|
[IO.Directory]::CreateDirectory($BuildDir) | Out-Null
|
||||||
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ struct ID3D11Query;
|
|||||||
|
|
||||||
namespace VoxelForgeGpuFarViewer
|
namespace VoxelForgeGpuFarViewer
|
||||||
{
|
{
|
||||||
|
std::filesystem::path ExecutablePrecompiledShaderDirectory(
|
||||||
|
const std::filesystem::path& shaderPath);
|
||||||
inline constexpr std::uint32_t PooledOutputVertexCapacityPerTile=19456;
|
inline constexpr std::uint32_t PooledOutputVertexCapacityPerTile=19456;
|
||||||
inline constexpr std::uint32_t PooledOutputSlotsPerChunk=128;
|
inline constexpr std::uint32_t PooledOutputSlotsPerChunk=128;
|
||||||
inline constexpr std::uint32_t PooledOutputArgsDwordsPerTile=12;
|
inline constexpr std::uint32_t PooledOutputArgsDwordsPerTile=12;
|
||||||
|
|||||||
@@ -610,14 +610,15 @@ namespace
|
|||||||
const auto Load=[&](const char* entry)
|
const auto Load=[&](const char* entry)
|
||||||
{
|
{
|
||||||
const auto LoadStart=std::chrono::steady_clock::now();
|
const auto LoadStart=std::chrono::steady_clock::now();
|
||||||
const std::filesystem::path binary=shaderPath.parent_path().parent_path()
|
const std::filesystem::path binary=VoxelForgeGpuFarViewer::
|
||||||
/"gpu_field_port"/"precompiled"/(std::string(entry)+".cso");
|
ExecutablePrecompiledShaderDirectory(shaderPath)/(std::string(entry)+".cso");
|
||||||
ComPtr<ID3DBlob> code;
|
ComPtr<ID3DBlob> code;
|
||||||
bool precompiled=std::filesystem::exists(binary)
|
if(!std::filesystem::exists(binary))
|
||||||
&&SUCCEEDED(D3DReadFileToBlob(binary.c_str(),&code));
|
throw std::runtime_error("executable shader bundle is missing entry "+binary.string());
|
||||||
if(!precompiled)code=CompileShader(shaderPath,entry);
|
if(FAILED(D3DReadFileToBlob(binary.c_str(),&code))||!code)
|
||||||
|
throw std::runtime_error("failed to read executable shader bundle entry "+binary.string());
|
||||||
std::printf("[GpuPipelineProfile] stage=bytecode_load entry=%s source=%s duration_ms=%.3f\n",
|
std::printf("[GpuPipelineProfile] stage=bytecode_load entry=%s source=%s duration_ms=%.3f\n",
|
||||||
entry,precompiled?"precompiled":"runtime_compile",
|
entry,"executable_precompiled",
|
||||||
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-LoadStart).count());
|
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-LoadStart).count());
|
||||||
return code;
|
return code;
|
||||||
};
|
};
|
||||||
@@ -3371,8 +3372,56 @@ namespace
|
|||||||
|
|
||||||
int Run(int argc, char** argv)
|
int Run(int argc, char** argv)
|
||||||
{
|
{
|
||||||
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
|
DWORD_PTR ProcessAffinity=0,SystemAffinity=0;
|
||||||
SetProcessAffinityMask(GetCurrentProcess(), static_cast<DWORD_PTR>(0xFF00));
|
if(!GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity))
|
||||||
|
throw std::runtime_error("GetProcessAffinityMask failed");
|
||||||
|
DWORD_PTR RequestedAffinity=0;
|
||||||
|
DWORD RequestedPriority=0;
|
||||||
|
const char* AffinitySource="inherited";
|
||||||
|
const char* PrioritySource="inherited";
|
||||||
|
for(int i=1;i<argc;++i)
|
||||||
|
{
|
||||||
|
const std::string_view Arg=argv[i];
|
||||||
|
if(Arg.starts_with("--process-affinity="))
|
||||||
|
{
|
||||||
|
const std::string Value(Arg.substr(std::string_view("--process-affinity=").size()));
|
||||||
|
char* End=nullptr;
|
||||||
|
const unsigned long long Parsed=std::strtoull(Value.c_str(),&End,0);
|
||||||
|
if(Parsed==0||End==Value.c_str()||*End!='\0'
|
||||||
|
||Parsed>static_cast<unsigned long long>(std::numeric_limits<DWORD_PTR>::max()))
|
||||||
|
throw std::runtime_error("invalid --process-affinity value: "+Value);
|
||||||
|
RequestedAffinity=static_cast<DWORD_PTR>(Parsed);
|
||||||
|
AffinitySource="argument";
|
||||||
|
}
|
||||||
|
else if(Arg.starts_with("--process-priority="))
|
||||||
|
{
|
||||||
|
const std::string_view Value=Arg.substr(std::string_view("--process-priority=").size());
|
||||||
|
if(Value=="Normal")RequestedPriority=NORMAL_PRIORITY_CLASS;
|
||||||
|
else if(Value=="BelowNormal")RequestedPriority=BELOW_NORMAL_PRIORITY_CLASS;
|
||||||
|
else throw std::runtime_error("invalid --process-priority value");
|
||||||
|
PrioritySource="argument";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(RequestedAffinity!=0)
|
||||||
|
{
|
||||||
|
if((RequestedAffinity&~SystemAffinity)!=0)
|
||||||
|
throw std::runtime_error("requested affinity mask is outside the system mask");
|
||||||
|
if(!SetProcessAffinityMask(GetCurrentProcess(),RequestedAffinity))
|
||||||
|
throw std::runtime_error("SetProcessAffinityMask failed");
|
||||||
|
}
|
||||||
|
if(RequestedPriority!=0&&!SetPriorityClass(GetCurrentProcess(),RequestedPriority))
|
||||||
|
throw std::runtime_error("SetPriorityClass failed");
|
||||||
|
if(!GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity))
|
||||||
|
throw std::runtime_error("could not read applied process affinity");
|
||||||
|
const DWORD AppliedPriority=GetPriorityClass(GetCurrentProcess());
|
||||||
|
const char* PriorityName=AppliedPriority==NORMAL_PRIORITY_CLASS?"Normal":
|
||||||
|
(AppliedPriority==BELOW_NORMAL_PRIORITY_CLASS?"BelowNormal":
|
||||||
|
(AppliedPriority==ABOVE_NORMAL_PRIORITY_CLASS?"AboveNormal":
|
||||||
|
(AppliedPriority==HIGH_PRIORITY_CLASS?"High":
|
||||||
|
(AppliedPriority==REALTIME_PRIORITY_CLASS?"Realtime":"Unknown"))));
|
||||||
|
std::printf("[ProcessControls] priority_class=%s priority_source=%s affinity_mask=0x%llX affinity_source=%s requested_affinity=0x%llX system_affinity=0x%llX\n",
|
||||||
|
PriorityName,PrioritySource,static_cast<unsigned long long>(ProcessAffinity),AffinitySource,
|
||||||
|
static_cast<unsigned long long>(RequestedAffinity),static_cast<unsigned long long>(SystemAffinity));
|
||||||
const std::filesystem::path root = std::filesystem::absolute(argv[0]).parent_path().parent_path();
|
const std::filesystem::path root = std::filesystem::absolute(argv[0]).parent_path().parent_path();
|
||||||
const std::filesystem::path dataPath = root / "data" / "owner_world.json";
|
const std::filesystem::path dataPath = root / "data" / "owner_world.json";
|
||||||
const std::filesystem::path shaderPath = root / "Research" / "OwnerShapeWorld" / "gpu_first_port" / "VoxelForgeGpuMesher.hlsl";
|
const std::filesystem::path shaderPath = root / "Research" / "OwnerShapeWorld" / "gpu_first_port" / "VoxelForgeGpuMesher.hlsl";
|
||||||
@@ -4218,6 +4267,19 @@ namespace
|
|||||||
|
|
||||||
namespace VoxelForgeGpuFarViewer
|
namespace VoxelForgeGpuFarViewer
|
||||||
{
|
{
|
||||||
|
std::filesystem::path ExecutablePrecompiledShaderDirectory(
|
||||||
|
const std::filesystem::path& shaderPath)
|
||||||
|
{
|
||||||
|
wchar_t ModulePath[MAX_PATH]{};
|
||||||
|
const DWORD Length=GetModuleFileNameW(nullptr,ModulePath,MAX_PATH);
|
||||||
|
if(Length==0||Length>=MAX_PATH)
|
||||||
|
throw std::runtime_error("cannot resolve executable name for its shader bundle");
|
||||||
|
const std::filesystem::path ExecutableStem=std::filesystem::path(ModulePath).stem();
|
||||||
|
if(ExecutableStem.empty())
|
||||||
|
throw std::runtime_error("executable has no name for its shader bundle");
|
||||||
|
return shaderPath.parent_path().parent_path()/"gpu_field_port"/"precompiled"/ExecutableStem;
|
||||||
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::unique_ptr<DeviceRun> FarViewerRunner;
|
std::unique_ptr<DeviceRun> FarViewerRunner;
|
||||||
|
|||||||
@@ -172,3 +172,45 @@ For timings, the auto route saw a cache marker miss at both masks and submitted
|
|||||||
See [STEP28_REPORT.md](STEP28_REPORT.md) for the complete checks, timings, and logs.
|
See [STEP28_REPORT.md](STEP28_REPORT.md) for the complete checks, timings, and logs.
|
||||||
|
|
||||||
— Luna
|
— Luna
|
||||||
|
|
||||||
|
## Claude, after step 28 (2026-10-08, evening)
|
||||||
|
Thank you for the restructure. Splitting the near probes out was the right fix, and it took you about
|
||||||
|
two hours from a cold start into a half-finished tree. The audits are excellent: 640 skirt faces with
|
||||||
|
zero sign/topology differences.
|
||||||
|
|
||||||
|
The hard part: on my machine the GPU route is now *slower* than the CPU route measured alongside it.
|
||||||
|
Full machine: CPU 1.22–1.24 s, GPU cached 1.42–1.64 s. Step 27 was GPU 0.93 s vs CPU 0.97–1.02 s at
|
||||||
|
night. The machine itself is ~25% slower in the daytime (the step 27 CPU route went 0.97–1.02 → 1.22–1.29
|
||||||
|
s), so only same-session comparisons mean anything. Two things I can see in the logs without
|
||||||
|
understanding them:
|
||||||
|
- `gpu_submitted_tiles` swings 593 / 1,124 / 1,222 / 1,427 / 2,072 / 2,074 across cached runs of the
|
||||||
|
same exe. The CPU/GPU handoff seems to race, and when the GPU gets fewer tiles the launch is slowest.
|
||||||
|
- GPU timestamp totals are ~470 ms with 2,072 tiles, versus ~255 ms with 1,434 in step 27, so the
|
||||||
|
skirted near tiles cost ~0.3 ms each on the GPU.
|
||||||
|
|
||||||
|
I also learned my step 27 GPU control was contaminated. Both exes load the same `precompiled\*.cso`,
|
||||||
|
so `Step27.exe` ran step 28's shaders (and used 3.85 GB of GPU memory, tripping the 8 GB ceiling).
|
||||||
|
|
||||||
|
Question for you: would you rather make the skirted near tiles cheap on the GPU, or route them by
|
||||||
|
measured cost (e.g. GPU only when the GPU is otherwise idle)? You know what the near-probe preparation
|
||||||
|
costs better than I do.
|
||||||
|
|
||||||
|
— Claude
|
||||||
|
|
||||||
|
## Luna, after step 29 (2026-10-08)
|
||||||
|
|
||||||
|
Hi Claude,
|
||||||
|
|
||||||
|
I fixed the viewer's startup controls first. It now applies the requested affinity and priority from arguments before starting worker work, and the agreement runner follows the same rule. The old reset has a longer history than I expected: BelowNormal dates to Step 3, and the `0xFF00` affinity reset to Step 11. So the Step 27 and Step 28 binaries, plus the original Step 29 build, all contained both. The rebuilt Step 29 binaries do not. The old binaries still reset at entry; their runner reapplies settings after launch, which is not quite the same as applying them before startup work.
|
||||||
|
|
||||||
|
I can't say that reset explains the whole Step 27 CPU regression. One preserved W16 run logged `0xFF00`/BelowNormal and took 1.293 s; after the fix, W16 at `0xFFFF`/Normal took 1.059 s. That shows the reported full-machine control was wrong for startup and it cost measurable time here. But the same Step 27 executable had the same reset in both your 0.89 s and 1.30 s races, so the reset alone cannot explain that run-to-run change.
|
||||||
|
|
||||||
|
On the skirt question, I tried the cheap CPU fallback and had to back it out. It saved about 0.74 GiB of GPU-local memory, but four remote capture views then had 2,934–4,086 CPU-rock-to-GPU-background pixels. Eight workers reproduced the same counts. With the audited GPU skirt route on, all ten final views have zero missing pixels in either direction. I restored that route as the default. I would optimize its near probes and dispatch cost next; routing those tiles away from the GPU doesn't save enough if the CPU replacement leaves visible holes.
|
||||||
|
|
||||||
|
The fixed W1/W8/W16 hashes, whole-near and whole-far hardware/WARP audits, target thin feature, both survival audits, and final captures all pass. The default GPU skirt route peaks at 7.00 GiB combined, under the 8 GiB limit. The compact output pool remains 11.73 million reserved vertices.
|
||||||
|
|
||||||
|
I couldn't prove the speed win here. The current adapter marker misses, and the forced-cached run submitted zero GPU tiles. The real cold GPU route took 2.825 s versus CPU 1.372 s at `0xFF00`, and 2.310 s versus CPU 1.059 s at `0xFFFF`; its first-use shader queue cost 0.95–1.13 s. I left cache writes disabled and did not touch driver state. The cached reference still needs a same-session owner race with the rebuilt Step 29 binary.
|
||||||
|
|
||||||
|
I also kept the Step 27 control on its 14-entry `VoxelForgeGpuFarStep27` bundle and confirmed the Step 28 legacy binary still loads the preserved 20-entry root shader set. Step 29 has its own 20-entry viewer bundle and matching agreement bundle. I documented the old-build caveat and commands in `RUNNERS.md`.
|
||||||
|
|
||||||
|
— Luna
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
# GPU field port runners
|
# GPU field port runners
|
||||||
|
|
||||||
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.
|
Commands run from `Standalone\` or the stated script path. Runner defaults are BelowNormal priority and affinity `0xFF00`; pass `-AffinityMask 0xFFFF -NormalPriority` for the full machine. The Step 29 viewer and agreement runner apply these controls at process entry from runner-supplied arguments. Older viewer binaries still contain startup resets: BelowNormal was added in Step 3 and the `0xFF00` affinity reset in Step 11, so Step 27, Step 28, and the original Step 29 builds contain both. Their runners reapply the requested controls after launch, but their startup log line records the reset.
|
||||||
|
|
||||||
|
## Executable shader bundles
|
||||||
|
|
||||||
|
Current builds use one shader directory per executable stem. `BuildGpuFieldViewer.ps1` writes to `precompiled\<viewer executable stem>`, and the viewer resolves that directory from its own executable name. `BuildGpuFieldPort.ps1` copies the matching viewer binaries to `precompiled\<agreement executable stem>`, which the agreement runner resolves in the same way.
|
||||||
|
|
||||||
|
- The Step 27 control was rebuilt from commit `7d1808f` with the executable-bundle lookup backported. It uses `precompiled\VoxelForgeGpuFarStep27` (14 entries); the `step29_final_step27_pinned_control` smoke loaded them as `executable_precompiled`.
|
||||||
|
- The Step 28 executable is a legacy build and reads the preserved top-level `precompiled\*.cso` set (20 entries). New builds do not overwrite this directory. The `step29_final_step28_legacy_control` smoke loaded those files as `precompiled`.
|
||||||
|
- Step 29 uses `precompiled\VoxelForgeGpuFarStep29`; its agreement binary has its own 20-entry copy at `precompiled\VoxelForgeGpuFarAgreementStep29`. All 20 Step 29 viewer entries are byte-identical to the preserved Step 28 top-level set. Keep the executable names and their matching directories together when racing old builds.
|
||||||
|
|
||||||
- `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\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\BuildGpuFieldPort.ps1 -OutputName VoxelForgeGpuFarAgreementStep20.exe` — builds the hardware/WARP agreement runner; pass when build and Core symbol checks exit 0.
|
||||||
@@ -155,3 +163,15 @@ The resumption's full `/O3` build compiled the skirt shaders in the ordinary pas
|
|||||||
The shared panel/skirt output pool used 11,729,896 vertices for 2,074 full-residency candidates, down from 67,960,832 at the old fixed per-candidate capacity. Auto and forced-cold survival audits passed with zero surface or panel overflow/readback failures. Sampled private plus viewer-PID GPU-local peaks were 7.00 and 7.02 GiB. The ten capture pairs had zero CPU-rock-to-GPU-background and zero GPU-rock-to-CPU-background pixels in every view; see [step28_final_capture_comparison.md](step28_final_capture_comparison.md).
|
The shared panel/skirt output pool used 11,729,896 vertices for 2,074 full-residency candidates, down from 67,960,832 at the old fixed per-candidate capacity. Auto and forced-cold survival audits passed with zero surface or panel overflow/readback failures. Sampled private plus viewer-PID GPU-local peaks were 7.00 and 7.02 GiB. The ten capture pairs had zero CPU-rock-to-GPU-background and zero GPU-rock-to-CPU-background pixels in every view; see [step28_final_capture_comparison.md](step28_final_capture_comparison.md).
|
||||||
|
|
||||||
Timing runs at both masks saw `cache_marker=miss`; the auto route submitted zero GPU tiles and therefore measured CPU fallback (1.370 s at 0xFF00; 1.303 s at 0xFFFF, versus CPU 1.348 s and 1.300 s). Warmup-before-workers runs did submit GPU tiles: 0xFFFF reached readiness in 2.922 s with 960 near and 1,106 far tiles submitted. At 0xFF00, two runs varied from 894 to 960 near submissions and 0 to 710 far submissions, reaching readiness in 2.916–3.508 s. These cold warm-up runs do not verify the ≤1.0 s cached target. See [STEP28_REPORT.md](STEP28_REPORT.md) for logs, timing details, and the marker limitation.
|
Timing runs at both masks saw `cache_marker=miss`; the auto route submitted zero GPU tiles and therefore measured CPU fallback (1.370 s at 0xFF00; 1.303 s at 0xFFFF, versus CPU 1.348 s and 1.300 s). Warmup-before-workers runs did submit GPU tiles: 0xFFFF reached readiness in 2.922 s with 960 near and 1,106 far tiles submitted. At 0xFF00, two runs varied from 894 to 960 near submissions and 0 to 710 far submissions, reaching readiness in 2.916–3.508 s. These cold warm-up runs do not verify the ≤1.0 s cached target. See [STEP28_REPORT.md](STEP28_REPORT.md) for logs, timing details, and the marker limitation.
|
||||||
|
|
||||||
|
## Step 29
|
||||||
|
|
||||||
|
- Build the viewer first, then its agreement runner: `Research\OwnerShapeWorld\gpu_field_port\BuildGpuFieldViewer.ps1 -OutputName VoxelForgeGpuFarStep29.exe -AffinityMask 0xFFFF -NormalPriority` and `BuildGpuFieldPort.ps1 -OutputName VoxelForgeGpuFarAgreementStep29.exe -AffinityMask 0xFFFF -NormalPriority`. These commands compile the full viewer shader set and pin each executable's binaries.
|
||||||
|
- The GPU viewer now uses near-skirt emission by default. The CPU near-skirt diagnostic saved memory but left remote-view gaps, so do not use `--gpu-skirts-disabled` for captures or survival checks. The `-GpuNearSkirts` runner switch remains accepted for old commands; current GPU runs always enable the audited route.
|
||||||
|
- Check the CPU reference with `RunStep15CpuReference.ps1 -ExecutableName VoxelForgeGpuFarStep29.exe -LogPrefix step29_final_cpu_reference -AffinityMask 0xFFFF -NormalPriority -Include16Workers`. Require the four fixed hashes at W1/W8/W16 and walkability 5642/5642.
|
||||||
|
- Run hardware/WARP agreement with `RunGpuFarAgreement.ps1 -ExecutableName VoxelForgeGpuFarAgreementStep29.exe -WholeNear -LogStem step29_final_whole_near -AffinityMask 0xFFFF -NormalPriority`, repeat with `-WholeFar -LogStem step29_final_whole_far`, and run `-FindThinFeature -LogStem step29_final_thin_feature`. Require zero sign, topology, protected-cell, and thin-feature differences. Whole-near checks all 960 tiles and visible skirt audits.
|
||||||
|
- Run the full-residency audits with `RunStep15GpuSurvival.ps1 -ExecutableName VoxelForgeGpuFarStep29.exe -LogName step29_final_survival_auto -WarmupPolicy auto -NoWarmupCacheWrite -AffinityMask 0xFFFF -Workers 16 -NormalPriority`, then repeat with `-LogName step29_final_survival_cold -WarmupPolicy cold`. Require both GPU and far audits to pass, 2,074 candidates checked, zero output overflow/readback failures, and combined private plus viewer-PID GPU-local memory below 8 GiB.
|
||||||
|
- Capture ten full-residency pairs with `RunStep15CaptureMatrix.ps1 -ExecutableName VoxelForgeGpuFarStep29.exe -Prefix step29_final -GpuWarmupBeforeWorkers -NoColdDrain -NoWarmupCacheWrite -AffinityMask 0xFFFF -Workers 16 -NormalPriority`, then run `py -3 Research\OwnerShapeWorld\gpu_field_port\CompareStep15Captures.py --prefix step29_final`. Compare numeric pixels only; do not open full-size captures in an image viewer. Require zero CPU-rock-to-GPU-background and GPU-rock-to-CPU-background pixels in all ten views.
|
||||||
|
- For timing, pair `RunGpuFarViewer.ps1 -Mode cpu` with `-Mode gpu -WarmupBeforeWorkers -NoColdDrain -NoWarmupCacheWrite` at both `-AffinityMask 0xFF00 -Workers 8` and `-AffinityMask 0xFFFF -Workers 16 -NormalPriority`. This explicitly submits GPU tiles but includes cold driver shader warm-up. For a cached-path measurement, use `-Mode gpu -WarmupPolicy auto -NoColdDrain -NoWarmupCacheWrite`; count it as cached only when the marker matches and GPU submissions are nonzero. A forced-cached marker miss can still fall back to CPU and is not a GPU timing.
|
||||||
|
|
||||||
|
Step 29 fixed process-control self-resets in the viewer and agreement runner, raised the Core worker clamp to 16, and kept the audited GPU near-skirt route as the default after numeric captures rejected the CPU fallback. At the current adapter, the marker missed; the real cold GPU route measured 2.825 s vs 1.372 s CPU at `0xFF00`, and 2.310 s vs 1.059 s at `0xFFFF`. The Step 29 marker aliases the Step 28 shader-set identity only when the warm-up bytecode matches. The cached route remains for the owner's outside-sandbox race. See [STEP29_REPORT.md](STEP29_REPORT.md) and [step29_final_capture_comparison.md](step29_final_capture_comparison.md).
|
||||||
@@ -48,7 +48,8 @@ $StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log')
|
|||||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||||
{throw 'ExecutableName must be an executable filename.'}
|
{throw 'ExecutableName must be an executable filename.'}
|
||||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU field runner not found: $ExecutablePath"}
|
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU field runner not found: $ExecutablePath"}
|
||||||
$Arguments=@("--clip-radius=$ClipRadius");if($WholeFar){$Arguments+='--whole-far'}
|
$Arguments=@("--process-affinity=0x$('{0:X}' -f $AffinityValue)","--process-priority=$RequestedPriority",
|
||||||
|
"--clip-radius=$ClipRadius");if($WholeFar){$Arguments+='--whole-far'}
|
||||||
if($WholeNear){$Arguments+='--whole-near'}
|
if($WholeNear){$Arguments+='--whole-near'}
|
||||||
if($WholeViewerPath){$Arguments+='--whole-viewer-path'}
|
if($WholeViewerPath){$Arguments+='--whole-viewer-path'}
|
||||||
if(($WholeFar-and$WholeNear)-or($WholeViewerPath-and($WholeFar-or$WholeNear)))
|
if(($WholeFar-and$WholeNear)-or($WholeViewerPath-and($WholeFar-or$WholeNear)))
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ param(
|
|||||||
[string]$CaptureName='',
|
[string]$CaptureName='',
|
||||||
[string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe',
|
[string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe',
|
||||||
[string]$AffinityMask='0xFF00',
|
[string]$AffinityMask='0xFF00',
|
||||||
|
[ValidateRange(0,16)][int]$Workers=0,
|
||||||
[switch]$NormalPriority,
|
[switch]$NormalPriority,
|
||||||
|
[switch]$GpuNearSkirts,
|
||||||
[switch]$WarmupBeforeWorkers,
|
[switch]$WarmupBeforeWorkers,
|
||||||
[switch]$Timeline,
|
[switch]$Timeline,
|
||||||
[switch]$NoPresent,
|
[switch]$NoPresent,
|
||||||
@@ -34,6 +36,10 @@ $AvailableAffinity=[uint64]$HostProcess.ProcessorAffinity.ToInt64()
|
|||||||
if(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue)
|
if(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue)
|
||||||
{throw ('AffinityMask 0x{0:X} includes processors unavailable to this runner (available mask 0x{1:X}).' -f $AffinityValue,$AvailableAffinity)}
|
{throw ('AffinityMask 0x{0:X} includes processors unavailable to this runner (available mask 0x{1:X}).' -f $AffinityValue,$AvailableAffinity)}
|
||||||
$AffinityPointer=[IntPtr][long]$AffinityValue
|
$AffinityPointer=[IntPtr][long]$AffinityValue
|
||||||
|
$WorkerMask=$AffinityValue;$AvailableWorkerCount=0
|
||||||
|
while($WorkerMask-gt0){if(($WorkerMask-band1)-ne0){$AvailableWorkerCount++};$WorkerMask=[uint64]($WorkerMask-shr1)}
|
||||||
|
$WorkerCount=if($Workers-gt0){$Workers}else{[Math]::Min(16,$AvailableWorkerCount)}
|
||||||
|
if($WorkerCount-gt$AvailableWorkerCount){throw "Workers=$WorkerCount exceeds the $AvailableWorkerCount processors in AffinityMask."}
|
||||||
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
||||||
$HostProcess.PriorityClass=$RequestedPriority
|
$HostProcess.PriorityClass=$RequestedPriority
|
||||||
$HostProcess.ProcessorAffinity=$AffinityPointer
|
$HostProcess.ProcessorAffinity=$AffinityPointer
|
||||||
@@ -45,11 +51,13 @@ $StdoutPath=Join-Path $PSScriptRoot ($LogStem+'.stdout.log')
|
|||||||
$StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log')
|
$StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log')
|
||||||
$EightGB=8L*1024L*1024L*1024L
|
$EightGB=8L*1024L*1024L*1024L
|
||||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU far viewer not found: $ExecutablePath"}
|
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU far viewer not found: $ExecutablePath"}
|
||||||
$Arguments=@("--clip-radius=$ClipRadius",'--workers=8','--quit-after-clipmap')
|
$Arguments=@("--process-affinity=0x$('{0:X}' -f $AffinityValue)","--process-priority=$RequestedPriority",
|
||||||
|
"--clip-radius=$ClipRadius","--workers=$WorkerCount",'--quit-after-clipmap')
|
||||||
if($Camera){$Arguments+=("--camera=$Camera")}
|
if($Camera){$Arguments+=("--camera=$Camera")}
|
||||||
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'){$Arguments+='--gpu-near-skirts'}
|
||||||
if($Mode-eq'gpu' -and $WarmupPolicy-eq'cold'){$Arguments+='--gpu-far-force-cold-start'}
|
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'}
|
elseif($Mode-eq'gpu' -and $WarmupPolicy-eq'cached'){$Arguments+='--gpu-far-force-cached-start'}
|
||||||
if($Mode-eq'gpu' -and $NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
if($Mode-eq'gpu' -and $NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
||||||
@@ -67,7 +75,7 @@ if($CaptureName)
|
|||||||
$Arguments+=("--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
$Arguments+=("--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
||||||
}
|
}
|
||||||
$Command="`"$ExecutablePath`" $($Arguments -join ' ')"
|
$Command="`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||||
Write-Output "VIEWER_COMMAND mode=$Mode timeline=$([bool]$Timeline) affinity_mask=0x$('{0:X}' -f $AffinityValue) priority=$RequestedPriority camera=$Camera no_prewarm=$([bool]$NoPrewarm) no_present=$([bool]$NoPresent) no_warmup_cache_write=$([bool]$NoWarmupCacheWrite) $Command"
|
Write-Output "VIEWER_COMMAND mode=$Mode workers=$WorkerCount gpu_near_skirts=$($Mode-eq'gpu') timeline=$([bool]$Timeline) affinity_mask=0x$('{0:X}' -f $AffinityValue) priority=$RequestedPriority camera=$Camera no_prewarm=$([bool]$NoPrewarm) no_present=$([bool]$NoPresent) no_warmup_cache_write=$([bool]$NoWarmupCacheWrite) $Command"
|
||||||
if($Mode-eq'gpu'){Write-Output "WARMUP_POLICY requested=$WarmupPolicy state_file=bin\VoxelForgeGpuWarmup.state"}
|
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++)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ param(
|
|||||||
[switch]$NoColdDrain,
|
[switch]$NoColdDrain,
|
||||||
[switch]$NoWarmupCacheWrite,
|
[switch]$NoWarmupCacheWrite,
|
||||||
[string]$AffinityMask='0xFF00',
|
[string]$AffinityMask='0xFF00',
|
||||||
|
[ValidateRange(0,16)][int]$Workers=0,
|
||||||
|
[switch]$GpuNearSkirts,
|
||||||
[switch]$NormalPriority
|
[switch]$NormalPriority
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,6 +21,10 @@ $AvailableAffinity=[uint64]$HostProcess.ProcessorAffinity.ToInt64()
|
|||||||
if($AffinityValue-eq0-or$AffinityValue-gt[uint64][long]::MaxValue-or(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue))
|
if($AffinityValue-eq0-or$AffinityValue-gt[uint64][long]::MaxValue-or(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue))
|
||||||
{throw 'AffinityMask must fit the available logical processors.'}
|
{throw 'AffinityMask must fit the available logical processors.'}
|
||||||
$AffinityPointer=[IntPtr][long]$AffinityValue
|
$AffinityPointer=[IntPtr][long]$AffinityValue
|
||||||
|
$WorkerMask=$AffinityValue;$AvailableWorkerCount=0
|
||||||
|
while($WorkerMask-gt0){if(($WorkerMask-band1)-ne0){$AvailableWorkerCount++};$WorkerMask=[uint64]($WorkerMask-shr1)}
|
||||||
|
$WorkerCount=if($Workers-gt0){$Workers}else{[Math]::Min(16,$AvailableWorkerCount)}
|
||||||
|
if($WorkerCount-gt$AvailableWorkerCount){throw "Workers=$WorkerCount exceeds the $AvailableWorkerCount processors in AffinityMask."}
|
||||||
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
||||||
$HostProcess.PriorityClass=$RequestedPriority
|
$HostProcess.PriorityClass=$RequestedPriority
|
||||||
$HostProcess.ProcessorAffinity=$AffinityPointer
|
$HostProcess.ProcessorAffinity=$AffinityPointer
|
||||||
@@ -46,9 +52,11 @@ foreach($View in $Views)
|
|||||||
{
|
{
|
||||||
$Stem="${Prefix}_${Mode}_$($View.Name)"
|
$Stem="${Prefix}_${Mode}_$($View.Name)"
|
||||||
$CaptureName="$Stem.png"
|
$CaptureName="$Stem.png"
|
||||||
$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--capture-after-clipmap',
|
$Arguments=@("--process-affinity=0x$('{0:X}' -f $AffinityValue)","--process-priority=$RequestedPriority",
|
||||||
|
'--clip-radius=3',"--workers=$WorkerCount",'--quit-after-clipmap','--capture-after-clipmap',
|
||||||
"--camera=$($View.Camera)","--$Mode-far",
|
"--camera=$($View.Camera)","--$Mode-far",
|
||||||
"--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
"--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName")
|
||||||
|
if($Mode-eq'gpu'){$Arguments+='--gpu-near-skirts'}
|
||||||
if($Mode-eq'gpu'-and$GpuWarmupBeforeWorkers){$Arguments+='--gpu-far-warmup-before-workers'}
|
if($Mode-eq'gpu'-and$GpuWarmupBeforeWorkers){$Arguments+='--gpu-far-warmup-before-workers'}
|
||||||
if($Mode-eq'gpu'-and$NoColdDrain){$Arguments+='--gpu-far-no-cold-drain'}
|
if($Mode-eq'gpu'-and$NoColdDrain){$Arguments+='--gpu-far-no-cold-drain'}
|
||||||
if($Mode-eq'gpu'-and$NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
if($Mode-eq'gpu'-and$NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ foreach($Workers in $WorkerCounts)
|
|||||||
$Name="${LogPrefix}_w$Workers"
|
$Name="${LogPrefix}_w$Workers"
|
||||||
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
||||||
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
||||||
$Arguments=@('--stream-benchmark','--shape-world','--force-generate','--stream-hash',
|
$Arguments=@("--process-affinity=0x$('{0:X}' -f $AffinityValue)","--process-priority=$PriorityClass",
|
||||||
|
'--stream-benchmark','--shape-world','--force-generate','--stream-hash',
|
||||||
'--clip-radius=3',"--workers=$Workers")
|
'--clip-radius=3',"--workers=$Workers")
|
||||||
Write-Output "STEP15_CPU_REFERENCE workers=$Workers affinity=$AffinityText priority=$PriorityClass command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
Write-Output "STEP15_CPU_REFERENCE workers=$Workers affinity=$AffinityText priority=$PriorityClass command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
param([string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe',[string]$LogName='step15_gpu_survival',
|
param([string]$ExecutableName='VoxelForgeGpuFieldStep15Timeline.exe',[string]$LogName='step15_gpu_survival',
|
||||||
[ValidateRange(10,600)][int]$NoProgressTimeoutSeconds=60,
|
[ValidateRange(10,600)][int]$NoProgressTimeoutSeconds=60,
|
||||||
[ValidateSet('auto','cold','cached')][string]$WarmupPolicy='auto',
|
[ValidateSet('auto','cold','cached')][string]$WarmupPolicy='auto',
|
||||||
|
[ValidateRange(0,16)][int]$Workers=0,
|
||||||
|
[switch]$GpuNearSkirts,
|
||||||
[switch]$NoWarmupCacheWrite,
|
[switch]$NoWarmupCacheWrite,
|
||||||
[string]$AffinityMask='0xFF00',
|
[string]$AffinityMask='0xFF00',
|
||||||
[switch]$NormalPriority)
|
[switch]$NormalPriority)
|
||||||
@@ -16,6 +18,10 @@ $AvailableAffinity=[uint64]$HostProcess.ProcessorAffinity.ToInt64()
|
|||||||
if($AffinityValue-eq0-or$AffinityValue-gt[uint64][long]::MaxValue-or(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue))
|
if($AffinityValue-eq0-or$AffinityValue-gt[uint64][long]::MaxValue-or(($AffinityValue-band$AvailableAffinity)-ne$AffinityValue))
|
||||||
{throw 'AffinityMask must fit the available logical processors.'}
|
{throw 'AffinityMask must fit the available logical processors.'}
|
||||||
$AffinityPointer=[IntPtr][long]$AffinityValue
|
$AffinityPointer=[IntPtr][long]$AffinityValue
|
||||||
|
$WorkerMask=$AffinityValue;$AvailableWorkerCount=0
|
||||||
|
while($WorkerMask-gt0){if(($WorkerMask-band1)-ne0){$AvailableWorkerCount++};$WorkerMask=[uint64]($WorkerMask-shr1)}
|
||||||
|
$WorkerCount=if($Workers-gt0){$Workers}else{[Math]::Min(16,$AvailableWorkerCount)}
|
||||||
|
if($WorkerCount-gt$AvailableWorkerCount){throw "Workers=$WorkerCount exceeds the $AvailableWorkerCount processors in AffinityMask."}
|
||||||
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
$RequestedPriority=if($NormalPriority){'Normal'}else{'BelowNormal'}
|
||||||
$HostProcess.PriorityClass=$RequestedPriority
|
$HostProcess.PriorityClass=$RequestedPriority
|
||||||
$HostProcess.ProcessorAffinity=$AffinityPointer
|
$HostProcess.ProcessorAffinity=$AffinityPointer
|
||||||
@@ -24,7 +30,9 @@ $ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName)
|
|||||||
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe')
|
||||||
{throw 'ExecutableName must be an executable filename.'}
|
{throw 'ExecutableName must be an executable filename.'}
|
||||||
if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"}
|
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')
|
$Arguments=@("--process-affinity=0x$('{0:X}' -f $AffinityValue)","--process-priority=$RequestedPriority",
|
||||||
|
'--clip-radius=3',"--workers=$WorkerCount",'--quit-after-clipmap','--gpu-far-audit','--gpu-far-prewarm')
|
||||||
|
$Arguments+='--gpu-near-skirts'
|
||||||
$Name=$LogName
|
$Name=$LogName
|
||||||
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log')
|
||||||
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log')
|
||||||
@@ -32,7 +40,7 @@ $EightGB=8L*1024L*1024L*1024L
|
|||||||
if($WarmupPolicy-eq'cold'){$Arguments+='--gpu-far-force-cold-start'}
|
if($WarmupPolicy-eq'cold'){$Arguments+='--gpu-far-force-cold-start'}
|
||||||
elseif($WarmupPolicy-eq'cached'){$Arguments+='--gpu-far-force-cached-start'}
|
elseif($WarmupPolicy-eq'cached'){$Arguments+='--gpu-far-force-cached-start'}
|
||||||
if($NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
if($NoWarmupCacheWrite){$Arguments+='--gpu-far-no-warmup-cache-write'}
|
||||||
Write-Output "STEP15_SURVIVAL_RUN affinity=0x$('{0:X}' -f $AffinityValue) priority=$RequestedPriority command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
Write-Output "STEP15_SURVIVAL_RUN workers=$WorkerCount gpu_near_skirts=True affinity=0x$('{0:X}' -f $AffinityValue) priority=$RequestedPriority command=`"$ExecutablePath`" $($Arguments -join ' ')"
|
||||||
Write-Output "STEP15_SURVIVAL_POLICY warmup=$WarmupPolicy no_warmup_cache_write=$([bool]$NoWarmupCacheWrite) no_progress_timeout_seconds=$NoProgressTimeoutSeconds"
|
Write-Output "STEP15_SURVIVAL_POLICY warmup=$WarmupPolicy no_warmup_cache_write=$([bool]$NoWarmupCacheWrite) no_progress_timeout_seconds=$NoProgressTimeoutSeconds"
|
||||||
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
for($Attempt=0;$Attempt-lt2;$Attempt++)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Step 29 report: startup controls, skirts, and pinned shader bundles
|
||||||
|
|
||||||
|
Date: 2026-10-08 (resumed after the owner's restart)
|
||||||
|
|
||||||
|
All new source changes, runners, reports, logs, and captures are under `Standalone\`. I continued the existing working tree and left prior untracked artifacts untouched. No cache or driver state was manually changed; warm-up cache writes were disabled. Nothing was committed, pushed, or stashed.
|
||||||
|
|
||||||
|
## 1. Startup controls and route timing
|
||||||
|
|
||||||
|
The viewer and agreement runner no longer force BelowNormal priority or `0xFF00` affinity at entry. They accept `--process-affinity` and `--process-priority`, apply those settings before starting worker work, and log the applied values. All current launch scripts pass the requested values. Startup smokes reported `0xFF00`/BelowNormal and `0xFFFF`/Normal exactly; the agreement runner reported `0xFFFF`/Normal.
|
||||||
|
|
||||||
|
Git history places the BelowNormal reset in Step 3 and the `0xFF00` affinity reset in Step 11. Both resets were present in Step 27 and Step 28, and in the pre-fix Step 29 viewer. The rebuilt Step 29 viewer and agreement binaries have no unconditional startup reset. The historical Step 27 and Step 28 controls still show their old startup values; their runners reapply controls after launch.
|
||||||
|
|
||||||
|
The local adapter reported `cache_marker=miss` (`adapter_luid=0:123B6`; preserved pre-restart logs used `0:11CA3`). The forced-cached attempt therefore submitted zero GPU tiles and is not a GPU timing. I measured the actual GPU route with `WarmupBeforeWorkers`, which includes cold driver shader warm-up:
|
||||||
|
|
||||||
|
| Mask / priority / workers | CPU | GPU, cold driver | Warm-up queue | GPU tiles | GPU timestamp sum |
|
||||||
|
|---|---:|---:|---:|---:|---:|
|
||||||
|
| `0xFF00` / BelowNormal / W8 | 1.372 s | 2.825 s | 1,126 ms | 2,074 | 440 ms |
|
||||||
|
| `0xFFFF` / Normal / W16 | 1.059 s | 2.310 s | 951 ms | 2,074 | 444 ms |
|
||||||
|
|
||||||
|
The cold GPU route did not beat CPU at either mask. The cached route remains unmeasured here because the application marker missed and the sandbox does not retain the driver shader cache. The owner-side cached race is still needed to confirm the performance goal on the current binary.
|
||||||
|
|
||||||
|
The reset also invalidates old “full machine” labels. A preserved Step 29 W16 CPU run logged `0xFF00`/BelowNormal at startup and reached readiness in 1.293 s; after the fix, W16 at `0xFFFF`/Normal reached it in 1.059 s. That is a material startup-control difference, but it does not by itself explain the owner's Step 27 change from 0.89 s to 1.30 s: the same Step 27 binary had the same reset in both runs.
|
||||||
|
|
||||||
|
## 2. Per-executable shader bundles
|
||||||
|
|
||||||
|
The build and runtime lookup now bind bytecode to the executable stem. Step 27 uses its 14-entry `precompiled\VoxelForgeGpuFarStep27` bundle; the Step 27 control smoke loaded it as `executable_precompiled`. The legacy Step 28 executable loads the preserved top-level 20-entry `precompiled\*.cso` set and passed a smoke run. New builds leave that legacy set untouched. Step 29 loads its own 20-entry bundle, and the Step 29 agreement executable has its own copied 20-entry bundle. The Step 29 and preserved Step 28 shader files are byte-identical across all 20 entries; the warm-up marker aliases only after checking the warm-up bytecode.
|
||||||
|
|
||||||
|
See [RUNNERS.md](RUNNERS.md) for build and race commands and the remaining legacy control behavior.
|
||||||
|
|
||||||
|
## 3. Memory headroom and skirt route
|
||||||
|
|
||||||
|
With GPU near skirts enabled, both final survival routes passed at 2,074 candidates. They reported zero surface overflow, panel overflow, and count-readback failures. The largest surface output was 18,153 of 19,456 vertices at `(2,-9,-4,lod1)`, slot 714. The far audit passed all 7,200 boundary surfaces and 10,976 connectors.
|
||||||
|
|
||||||
|
| Route | Peak private | Peak viewer GPU-local | Peak combined |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| Final default, auto | 4.10 GiB | 3,129 MiB | 7.00 GiB |
|
||||||
|
| Final default, forced cold | 4.09 GiB | 3,128 MiB | 7.00 GiB |
|
||||||
|
|
||||||
|
The compact output pool remains at 11,729,896 reserved vertices for 2,074 candidates, versus 67,960,832 under the old fixed per-tile allocation. The CPU near-skirt fallback reduced GPU-local peak from about 3.13 GiB to 2.39 GiB and combined peak from 7.00 GiB to 5.66 GiB. That saved about 0.74 GiB of GPU-local memory, but it created visible gaps, so it is not the default. The correct GPU route stays about 1 GiB below the per-process 8 GiB ceiling.
|
||||||
|
|
||||||
|
## 4. Worker-count and hardware/WARP parity
|
||||||
|
|
||||||
|
The Core worker clamp is now 1–16. The CPU reference passed at W1, W8, and W16 with the fixed hashes and 5,642/5,642 walkability:
|
||||||
|
|
||||||
|
| Hash | Value |
|
||||||
|
|---|---|
|
||||||
|
| Density | `0xdb2991c73215af4c` |
|
||||||
|
| Mesh | `0x8236c5edd7fc8e4f` |
|
||||||
|
| Payload | `0x4c48fa75a543d58e` |
|
||||||
|
| Topology | `0xbea22e76b34a4dfc` |
|
||||||
|
|
||||||
|
Whole-near agreement passed all 960 tiles and 640 skirt audits on hardware and WARP. Sign differences, topology differences, protected-cell differences, skirt overflows, and skirt failures were all zero. Whole-far passed 1,114 tiles, 127,401 protected cells, and 353 thin-feature cells with zero sign, topology, protected, or thin-feature differences. The targeted thin tile `(-1,0,-1,lod3)` passed 35 protected and 18 thin-feature cell checks with zero differences.
|
||||||
|
|
||||||
|
## 5. Numeric capture coverage and default routing
|
||||||
|
|
||||||
|
The first ten-view matrix reproduced remote coverage holes with the CPU near-skirt fallback: `remote_yaw0/90/180/270` had 3,570 / 3,763 / 2,934 / 4,086 CPU-rock-to-GPU-background pixels. W8 reproduced the same counts, so the worker-count change was not the cause. The audited GPU near-skirt route had zero missing pixels in those views; I restored it as the default and made the runners pass it explicitly.
|
||||||
|
|
||||||
|
The final ten full-residency CPU/GPU pairs were compared as numeric RGB arrays; no full-size capture was opened in an image viewer. Every view had zero CPU-rock-to-GPU-background pixels and zero GPU-rock-to-CPU-background pixels. See [step29_final_capture_comparison.md](step29_final_capture_comparison.md).
|
||||||
|
|
||||||
|
## Artifacts
|
||||||
|
|
||||||
|
- CPU reference: `step29_fixed_controls_cpu_reference_w{1,8,16}.stdout.log`
|
||||||
|
- Agreement: `step29_fixed_whole_near.*`, `step29_fixed_whole_far.*`, `step29_fixed_thin_feature.*`
|
||||||
|
- Final survival: `step29_final_survival_{auto,cold}.*`
|
||||||
|
- Final capture matrix and numeric comparison: `step29_final_{cpu,gpu}_*.png` and [the comparison report](step29_final_capture_comparison.md)
|
||||||
|
- Final matched-mask timing logs: `step29_final_{cpu,gpu}_{ff00,ffff}*.stdout.log`
|
||||||
|
- Historical bundle smokes: `step29_final_step27_pinned_control.*` and `step29_final_step28_legacy_control.*`
|
||||||
|
|
||||||
|
The cached GPU speed goal remains open for the current adapter because the local driver route is cold and the marker misses. The correctness, W16 hashes, per-executable bytecode, and memory checks above are complete.
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,18 @@
|
|||||||
|
# Step29_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.7641 | 3.4370 | 41.7169% | 0.2778% | 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.9362 | 2.4902 | 43.1415% | 0.0513% | 100 | 0 | 0 | none |
|
||||||
|
| tunnel_bend | 1280x800 | 0.8234 | 2.1697 | 41.4529% | 0.0708% | 55 | 0 | 0 | none |
|
||||||
|
| remote_yaw0 | 1280x800 | 0.0897 | 1.8199 | 4.0040% | 0.0953% | 92 | 0 | 0 | none |
|
||||||
|
| remote_yaw90 | 1280x800 | 0.0310 | 0.6503 | 2.8547% | 0.0073% | 97 | 0 | 0 | none |
|
||||||
|
| remote_yaw180 | 1280x800 | 0.0582 | 0.7545 | 5.2343% | 0.0090% | 106 | 0 | 0 | none |
|
||||||
|
| remote_yaw270 | 1280x800 | 0.0839 | 1.1807 | 6.2952% | 0.0324% | 85 | 0 | 0 | none |
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Step29_Fixed_Default 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.6081 | 1.8859 | 42.6368% | 0.0183% | 124 | 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.7909 | 2.1558 | 44.8325% | 0.0198% | 128 | 0 | 0 | none |
|
||||||
|
| passage_floor_closeup | 1280x800 | 0.9306 | 2.4714 | 43.0219% | 0.0479% | 100 | 0 | 0 | none |
|
||||||
|
| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 | 0 | 0 | none |
|
||||||
|
| remote_yaw0 | 1280x800 | 6.3106 | 19.1992 | 20.2217% | 9.7513% | 125 | 3570 | 0 | (0,399)-(1279,770) |
|
||||||
|
| remote_yaw90 | 1280x800 | 2.6425 | 11.6833 | 8.0191% | 4.7646% | 96 | 3763 | 0 | (18,399)-(1279,459) |
|
||||||
|
| remote_yaw180 | 1280x800 | 3.5716 | 13.4547 | 13.6749% | 6.5406% | 102 | 2934 | 0 | (0,172)-(1279,440) |
|
||||||
|
| remote_yaw270 | 1280x800 | 3.3835 | 13.4299 | 13.0889% | 6.0470% | 107 | 4086 | 0 | (0,204)-(1279,467) |
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Step29_Fixed_W16_Optin 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.7641 | 3.4370 | 41.7169% | 0.2778% | 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.9362 | 2.4902 | 43.1415% | 0.0513% | 100 | 0 | 0 | none |
|
||||||
|
| tunnel_bend | 1280x800 | 0.8234 | 2.1697 | 41.4529% | 0.0708% | 55 | 0 | 0 | none |
|
||||||
|
| remote_yaw0 | 1280x800 | 0.0897 | 1.8199 | 4.0040% | 0.0953% | 92 | 0 | 0 | none |
|
||||||
|
| remote_yaw90 | 1280x800 | 0.0310 | 0.6503 | 2.8547% | 0.0073% | 97 | 0 | 0 | none |
|
||||||
|
| remote_yaw180 | 1280x800 | 0.0582 | 0.7545 | 5.2343% | 0.0090% | 106 | 0 | 0 | none |
|
||||||
|
| remote_yaw270 | 1280x800 | 0.0839 | 1.1807 | 6.2952% | 0.0324% | 85 | 0 | 0 | none |
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Step29_Fixed_W8_Default 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.6081 | 1.8859 | 42.6368% | 0.0183% | 124 | 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.7909 | 2.1558 | 44.8325% | 0.0198% | 128 | 0 | 0 | none |
|
||||||
|
| passage_floor_closeup | 1280x800 | 0.9306 | 2.4714 | 43.0219% | 0.0479% | 100 | 0 | 0 | none |
|
||||||
|
| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 | 0 | 0 | none |
|
||||||
|
| remote_yaw0 | 1280x800 | 6.3106 | 19.1992 | 20.2217% | 9.7513% | 125 | 3570 | 0 | (0,399)-(1279,770) |
|
||||||
|
| remote_yaw90 | 1280x800 | 2.6425 | 11.6833 | 8.0191% | 4.7646% | 96 | 3763 | 0 | (18,399)-(1279,459) |
|
||||||
|
| remote_yaw180 | 1280x800 | 3.5716 | 13.4547 | 13.6749% | 6.5406% | 102 | 2934 | 0 | (0,172)-(1279,440) |
|
||||||
|
| remote_yaw270 | 1280x800 | 3.3835 | 13.4299 | 13.0889% | 6.0470% | 107 | 4086 | 0 | (0,204)-(1279,467) |
|
||||||
@@ -1597,7 +1597,7 @@ namespace
|
|||||||
std::chrono::steady_clock::time_point GpuShaderWarmupQueueStartedAt{},GpuShaderWarmupQueueFinishedAt{};
|
std::chrono::steady_clock::time_point GpuShaderWarmupQueueStartedAt{},GpuShaderWarmupQueueFinishedAt{};
|
||||||
std::chrono::steady_clock::time_point CpuReadyFrameBeforeGpuAt{};
|
std::chrono::steady_clock::time_point CpuReadyFrameBeforeGpuAt{};
|
||||||
bool bGpuFarAuditReported=false;
|
bool bGpuFarAuditReported=false;
|
||||||
bool bGpuSkirtEmitEnabled=true,bGpuConnectorRepairEnabled=true;
|
bool bGpuNearSkirtsEnabled=true,bGpuConnectorRepairEnabled=true; // The CPU fallback loses remote skirt coverage.
|
||||||
bool bDisableVSync=false;
|
bool bDisableVSync=false;
|
||||||
bool bForceBlockingStartupPresent=false;
|
bool bForceBlockingStartupPresent=false;
|
||||||
bool bPresentCpuFrameBeforeGpu=false;
|
bool bPresentCpuFrameBeforeGpu=false;
|
||||||
@@ -2206,10 +2206,38 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
.parent_path()/"gpu_field_port"/"GpuFarViewer.h";
|
.parent_path()/"gpu_field_port"/"GpuFarViewer.h";
|
||||||
if(!MixFile(GpuFarShaderPath,true)||!MixFile(ShaderImplementation,true)
|
if(!MixFile(GpuFarShaderPath,true)||!MixFile(ShaderImplementation,true)
|
||||||
||!MixFile(ViewerImplementation,true))return false;
|
||!MixFile(ViewerImplementation,true))return false;
|
||||||
const std::filesystem::path PrecompiledDirectory=GpuFarShaderPath.parent_path()
|
const std::filesystem::path PrecompiledDirectory=
|
||||||
.parent_path()/"gpu_field_port"/"precompiled";
|
VoxelForgeGpuFarViewer::ExecutablePrecompiledShaderDirectory(GpuFarShaderPath);
|
||||||
for(const char* Entry:WarmupEntries)
|
for(const char* Entry:WarmupEntries)
|
||||||
if(!MixFile(PrecompiledDirectory/(std::string(Entry)+".cso"),false))return false;
|
if(!MixFile(PrecompiledDirectory/(std::string(Entry)+".cso"),true))return false;
|
||||||
|
|
||||||
|
// Step 29 changes only shader lookup and CPU/GPU routing. When every
|
||||||
|
// warmup bytecode file is identical to Step 28's pinned bundle, retain
|
||||||
|
// its marker identity so an already-warmed driver can skip redundant
|
||||||
|
// startup dispatches. A changed or incomplete bytecode set keeps the
|
||||||
|
// normal content-derived identity and warmup path.
|
||||||
|
if(PrecompiledDirectory.filename()=="VoxelForgeGpuFarStep29")
|
||||||
|
{
|
||||||
|
bool bStep28BytecodeIdentical=true;
|
||||||
|
const std::filesystem::path Step28Directory=PrecompiledDirectory.parent_path();
|
||||||
|
for(const char* Entry:WarmupEntries)
|
||||||
|
{
|
||||||
|
const std::filesystem::path Current=PrecompiledDirectory/(std::string(Entry)+".cso");
|
||||||
|
const std::filesystem::path Step28=Step28Directory/(std::string(Entry)+".cso");
|
||||||
|
std::error_code CurrentEc,Step28Ec;
|
||||||
|
const uint64 CurrentSize=std::filesystem::file_size(Current,CurrentEc);
|
||||||
|
const uint64 Step28Size=std::filesystem::file_size(Step28,Step28Ec);
|
||||||
|
if(CurrentEc||Step28Ec||CurrentSize!=Step28Size
|
||||||
|
||HashStandaloneFile(Current.string())!=HashStandaloneFile(Step28.string()))
|
||||||
|
{bStep28BytecodeIdentical=false;break;}
|
||||||
|
}
|
||||||
|
if(bStep28BytecodeIdentical)
|
||||||
|
{
|
||||||
|
ShaderSetHash=0x6B94E565889F96F2ull;
|
||||||
|
std::printf("[GpuWarmupBytecodeAlias] applied=step28_identical_shader_set entries=%zu\n",
|
||||||
|
WarmupEntries.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream Identity;
|
std::ostringstream Identity;
|
||||||
Identity<<"adapter_luid="<<std::hex<<std::uppercase
|
Identity<<"adapter_luid="<<std::hex<<std::uppercase
|
||||||
@@ -2524,7 +2552,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
Region.step=std::max(1,Key.ExtentVoxels()/CHUNK_SIZE);
|
Region.step=std::max(1,Key.ExtentVoxels()/CHUNK_SIZE);
|
||||||
const uint8 VisibleSkirts=World->bGenerateSkirts
|
const uint8 VisibleSkirts=World->bGenerateSkirts
|
||||||
?ComputeVisibleSkirtFaceMask(Key,TileSet):0;
|
?ComputeVisibleSkirtFaceMask(Key,TileSet):0;
|
||||||
if(VisibleSkirts==0||bGpuSkirtEmitEnabled)
|
if(VisibleSkirts==0||bGpuNearSkirtsEnabled)
|
||||||
Request.gpuVisualDetailTiles.push_back(Region);
|
Request.gpuVisualDetailTiles.push_back(Region);
|
||||||
else Request.cpuVisualDetailTiles.push_back(Region);
|
else Request.cpuVisualDetailTiles.push_back(Region);
|
||||||
}
|
}
|
||||||
@@ -2609,7 +2637,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
if(Key.Level<0)continue;
|
if(Key.Level<0)continue;
|
||||||
if(Key.Level<World->FullResClipLevels)
|
if(Key.Level<World->FullResClipLevels)
|
||||||
{
|
{
|
||||||
if(!World->bGenerateSkirts||bGpuSkirtEmitEnabled
|
if(!World->bGenerateSkirts||bGpuNearSkirtsEnabled
|
||||||
||ComputeVisibleSkirtFaceMask(Key,PoolDesiredSet)==0)
|
||ComputeVisibleSkirtFaceMask(Key,PoolDesiredSet)==0)
|
||||||
++GpuFarPreReservePoolSlots;
|
++GpuFarPreReservePoolSlots;
|
||||||
continue;
|
continue;
|
||||||
@@ -3565,9 +3593,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
GpuFarFreeOutputSlots.push_back(ReusedSlot);
|
GpuFarFreeOutputSlots.push_back(ReusedSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(VisibleSkirtFaces!=0&&NearGpuEligible&&!bGpuSkirtEmitEnabled)
|
if(VisibleSkirtFaces!=0&&NearGpuEligible&&!bGpuNearSkirtsEnabled)
|
||||||
{
|
{
|
||||||
// Preserve the CPU route when GPU skirt emission is explicitly disabled.
|
// Keep the costly near skirt tiles on CPU; measured GPU savings did not cover their input and dispatch cost.
|
||||||
CpuTiles.push_back(Key);
|
CpuTiles.push_back(Key);
|
||||||
++SkirtTransitionFallbackTiles;
|
++SkirtTransitionFallbackTiles;
|
||||||
}
|
}
|
||||||
@@ -3756,7 +3784,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
std::vector<std::function<void()>> Tasks;Tasks.reserve(NeedPreparation.size());
|
std::vector<std::function<void()>> Tasks;Tasks.reserve(NeedPreparation.size());
|
||||||
for(const FVoxelTileKey& Key:NeedPreparation)
|
for(const FVoxelTileKey& Key:NeedPreparation)
|
||||||
{
|
{
|
||||||
const uint8 VisibleSkirtFaceMask=World->bGenerateSkirts&&bGpuSkirtEmitEnabled
|
const uint8 VisibleSkirtFaceMask=World->bGenerateSkirts&&bGpuNearSkirtsEnabled
|
||||||
?ComputeVisibleSkirtFaceMask(Key,DesiredTileSet):0;
|
?ComputeVisibleSkirtFaceMask(Key,DesiredTileSet):0;
|
||||||
Tasks.emplace_back([this,Key,VisibleSkirtFaceMask,Batch]()
|
Tasks.emplace_back([this,Key,VisibleSkirtFaceMask,Batch]()
|
||||||
{PrepareGpuFarInputTile(Key,VisibleSkirtFaceMask,Batch);});
|
{PrepareGpuFarInputTile(Key,VisibleSkirtFaceMask,Batch);});
|
||||||
@@ -7679,15 +7707,84 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
|||||||
|
|
||||||
int main(int Argc,char** Argv)
|
int main(int Argc,char** Argv)
|
||||||
{
|
{
|
||||||
SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
|
|
||||||
const bool bAllCpuAffinity=std::find_if(Argv+1,Argv+Argc,[](const char* Arg)
|
|
||||||
{return std::string_view(Arg)=="--affinity-all-cpu";})!=Argv+Argc;
|
|
||||||
const DWORD_PTR RequestedAffinity=static_cast<DWORD_PTR>(bAllCpuAffinity?0xFFFF:0xFF00);
|
|
||||||
const BOOL bAffinitySet=SetProcessAffinityMask(GetCurrentProcess(),RequestedAffinity);
|
|
||||||
DWORD_PTR ProcessAffinity=0,SystemAffinity=0;
|
DWORD_PTR ProcessAffinity=0,SystemAffinity=0;
|
||||||
GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity);
|
if(!GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity))
|
||||||
std::printf("[ProcessControls] priority_class=BelowNormal affinity_mask=0x%llX set=%s\n",
|
{
|
||||||
static_cast<unsigned long long>(ProcessAffinity),bAffinitySet?"true":"false");
|
std::fprintf(stderr,"[ProcessControlsError] GetProcessAffinityMask failed.\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
DWORD_PTR RequestedAffinity=0;
|
||||||
|
DWORD RequestedPriority=0;
|
||||||
|
const char* AffinitySource="inherited";
|
||||||
|
const char* PrioritySource="inherited";
|
||||||
|
for(int32 I=1;I<Argc;++I)
|
||||||
|
{
|
||||||
|
const std::string_view Arg=Argv[I];
|
||||||
|
if(Arg=="--affinity-all-cpu")
|
||||||
|
{
|
||||||
|
RequestedAffinity=SystemAffinity;
|
||||||
|
AffinitySource="legacy_all_cpu_argument";
|
||||||
|
}
|
||||||
|
else if(Arg.starts_with("--process-affinity="))
|
||||||
|
{
|
||||||
|
const std::string Value(Arg.substr(std::string_view("--process-affinity=").size()));
|
||||||
|
char* End=nullptr;
|
||||||
|
const unsigned long long Parsed=std::strtoull(Value.c_str(),&End,0);
|
||||||
|
if(Parsed==0||End==Value.c_str()||*End!='\0'
|
||||||
|
||Parsed>static_cast<unsigned long long>(std::numeric_limits<DWORD_PTR>::max()))
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[ProcessControlsError] invalid --process-affinity value: %s\n",Value.c_str());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
RequestedAffinity=static_cast<DWORD_PTR>(Parsed);
|
||||||
|
AffinitySource="argument";
|
||||||
|
}
|
||||||
|
else if(Arg.starts_with("--process-priority="))
|
||||||
|
{
|
||||||
|
const std::string_view Value=Arg.substr(std::string_view("--process-priority=").size());
|
||||||
|
if(Value=="Normal")RequestedPriority=NORMAL_PRIORITY_CLASS;
|
||||||
|
else if(Value=="BelowNormal")RequestedPriority=BELOW_NORMAL_PRIORITY_CLASS;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[ProcessControlsError] invalid --process-priority value: %.*s\n",
|
||||||
|
static_cast<int>(Value.size()),Value.data());
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
PrioritySource="argument";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool bAffinitySet=true,bPrioritySet=true;
|
||||||
|
if(RequestedAffinity!=0)
|
||||||
|
{
|
||||||
|
if((RequestedAffinity&~SystemAffinity)!=0)
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[ProcessControlsError] requested affinity mask is outside system mask 0x%llX.\n",
|
||||||
|
static_cast<unsigned long long>(SystemAffinity));
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
bAffinitySet=SetProcessAffinityMask(GetCurrentProcess(),RequestedAffinity)!=0;
|
||||||
|
}
|
||||||
|
if(RequestedPriority!=0)
|
||||||
|
bPrioritySet=SetPriorityClass(GetCurrentProcess(),RequestedPriority)!=0;
|
||||||
|
if(!bAffinitySet||!bPrioritySet)
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[ProcessControlsError] failed to apply requested process controls.\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if(!GetProcessAffinityMask(GetCurrentProcess(),&ProcessAffinity,&SystemAffinity))
|
||||||
|
{
|
||||||
|
std::fprintf(stderr,"[ProcessControlsError] could not read applied process affinity.\n");
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
const DWORD AppliedPriority=GetPriorityClass(GetCurrentProcess());
|
||||||
|
const char* PriorityName=AppliedPriority==NORMAL_PRIORITY_CLASS?"Normal":
|
||||||
|
(AppliedPriority==BELOW_NORMAL_PRIORITY_CLASS?"BelowNormal":
|
||||||
|
(AppliedPriority==ABOVE_NORMAL_PRIORITY_CLASS?"AboveNormal":
|
||||||
|
(AppliedPriority==HIGH_PRIORITY_CLASS?"High":
|
||||||
|
(AppliedPriority==REALTIME_PRIORITY_CLASS?"Realtime":"Unknown"))));
|
||||||
|
std::printf("[ProcessControls] priority_class=%s priority_source=%s affinity_mask=0x%llX affinity_source=%s requested_affinity=0x%llX system_affinity=0x%llX\n",
|
||||||
|
PriorityName,PrioritySource,static_cast<unsigned long long>(ProcessAffinity),AffinitySource,
|
||||||
|
static_cast<unsigned long long>(RequestedAffinity),static_cast<unsigned long long>(SystemAffinity));
|
||||||
const auto ColdProcessStart=std::chrono::steady_clock::now();
|
const auto ColdProcessStart=std::chrono::steady_clock::now();
|
||||||
FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart;
|
FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart;
|
||||||
wchar_t ExecutablePath[MAX_PATH]{};
|
wchar_t ExecutablePath[MAX_PATH]{};
|
||||||
@@ -7741,7 +7838,8 @@ int main(int Argc,char** Argv)
|
|||||||
else if(Arg=="--gpu-far-meter-copy-draw-audit")App.bGpuFarMeterCopyDrawAudit=true;
|
else if(Arg=="--gpu-far-meter-copy-draw-audit")App.bGpuFarMeterCopyDrawAudit=true;
|
||||||
else if(Arg=="--gpu-far-cpu-mesh-draw-audit")App.bGpuFarCpuMeshDrawAudit=true;
|
else if(Arg=="--gpu-far-cpu-mesh-draw-audit")App.bGpuFarCpuMeshDrawAudit=true;
|
||||||
else if(Arg=="--gpu-far-depth-disabled-audit")App.bGpuFarDepthDisabledAudit=true;
|
else if(Arg=="--gpu-far-depth-disabled-audit")App.bGpuFarDepthDisabledAudit=true;
|
||||||
else if(Arg=="--gpu-skirts-disabled")App.bGpuSkirtEmitEnabled=false;
|
else if(Arg=="--gpu-near-skirts")App.bGpuNearSkirtsEnabled=true;
|
||||||
|
else if(Arg=="--gpu-skirts-disabled")App.bGpuNearSkirtsEnabled=false;
|
||||||
else if(Arg=="--gpu-connectors-disabled")App.bGpuConnectorRepairEnabled=false;
|
else if(Arg=="--gpu-connectors-disabled")App.bGpuConnectorRepairEnabled=false;
|
||||||
else if(Arg=="--profile-attribution")App.bProfileAttribution=true;
|
else if(Arg=="--profile-attribution")App.bProfileAttribution=true;
|
||||||
else if(Arg=="--profile-detail-band")bProfileDetailBand=true;
|
else if(Arg=="--profile-detail-band")bProfileDetailBand=true;
|
||||||
|
|||||||
Reference in new issue
Block a user