diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep14Captures.py b/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep14Captures.py new file mode 100644 index 0000000..8101ae3 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/CompareStep14Captures.py @@ -0,0 +1,59 @@ +from pathlib import Path + +import numpy as np +from PIL import Image + + +ROOT = Path(__file__).resolve().parent +VIEWS = ( + "chamber", + "tunnel", + "passage", + "far", + "passage_floor_closeup", + "tunnel_bend", + "remote_yaw0", + "remote_yaw90", + "remote_yaw180", + "remote_yaw270", +) + +rows = [] +for view in VIEWS: + cpu_path = ROOT / f"step14_cpu_{view}.png" + gpu_path = ROOT / f"step14_gpu_{view}.png" + cpu = np.asarray(Image.open(cpu_path).convert("RGB"), dtype=np.int16) + gpu = np.asarray(Image.open(gpu_path).convert("RGB"), dtype=np.int16) + if cpu.shape != gpu.shape: + raise RuntimeError(f"dimension mismatch for {view}: {cpu.shape} vs {gpu.shape}") + diff = np.abs(cpu - gpu) + pixel_max = diff.max(axis=2) + rows.append( + ( + view, + f"{cpu.shape[1]}x{cpu.shape[0]}", + float(diff.mean()), + float(np.sqrt(np.square(diff, dtype=np.float64).mean())), + float(np.count_nonzero(pixel_max) * 100.0 / pixel_max.size), + float(np.count_nonzero(pixel_max > 32) * 100.0 / pixel_max.size), + int(pixel_max.max()), + ) + ) + +lines = [ + "# Step 14 full-residency capture comparison", + "", + "All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. The files were compared numerically as RGB arrays and were not opened in an image viewer.", + "", + "| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel |", + "|---|---:|---:|---:|---:|---:|---:|", +] +for row in rows: + view, size, mae, rmse, changed, over32, maximum = row + lines.append( + f"| {view} | {size} | {mae:.4f} | {rmse:.4f} | {changed:.4f}% | {over32:.4f}% | {maximum} |" + ) + +report = ROOT / "step14_capture_comparison.md" +report.write_text("\n".join(lines) + "\n", encoding="utf-8") +print(report.read_text(encoding="utf-8"), end="") diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h index 9210b8d..3b52a79 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h @@ -26,7 +26,7 @@ namespace VoxelForgeGpuFarViewer const std::filesystem::path& shaderPath,std::string& error); bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, const std::filesystem::path& shaderPath,bool dispatchShaders, - const std::string& entryFilter,std::string& error); + const std::string& entryFilter,bool fenceEachKernel,std::string& error); bool CompleteFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, const std::filesystem::path& shaderPath,std::string& error); bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context, diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp index 6d0eb53..3bcb3b3 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp @@ -628,22 +628,27 @@ namespace std::chrono::duration(std::chrono::steady_clock::now()-PipelineStart).count()); } - void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter) + void QueueTinyShaderWarmup(bool dispatchShaders,const std::string& entryFilter, + bool fenceEachKernel) { const auto WarmupStart=Clock::now(); + const auto ResourceCreateStart=Clock::now(); static constexpr std::array entries={ "EvaluateFarFieldCS","EvaluateNearFieldCS","ClassifyCS","ClassifyFarCS","ScanGroupsCS","EmitCS", "ApplyNearFieldDetailCS", "EmitNearCS","BuildFarConnectorRingsCS","EmitFarConnectorPanelsCS", "ClassifyFarSkirtsCS","ScanFarSkirtsCS","EmitFarSkirtsCS","NoiseProbeCS"}; - if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),entryFilter)==entries.end()) + const bool skipEntryFilter=entryFilter.starts_with("skip:"); + const std::string_view filteredEntry=skipEntryFilter + ?std::string_view(entryFilter).substr(5):std::string_view(entryFilter); + if(!entryFilter.empty()&&std::find(entries.begin(),entries.end(),filteredEntry)==entries.end()) throw std::runtime_error("unknown shader warmup entry: "+entryFilter); constexpr std::uint32_t sampleCount=27,cellCount=1,groupCount=1; TileConstants tile{}; tile.cells=1;tile.sampleDim=3;tile.cellCount=cellCount;tile.groupCount=groupCount; - tile.stepVoxels=entryFilter=="EmitNearCS"?1:2; - tile.nearFieldMode=(entryFilter.empty()||entryFilter=="EvaluateNearFieldCS" - ||entryFilter=="ApplyNearFieldDetailCS"||entryFilter=="EmitNearCS")?1u:0u; + tile.stepVoxels=filteredEntry=="EmitNearCS"?1:2; + tile.nearFieldMode=(entryFilter.empty()||filteredEntry=="EvaluateNearFieldCS" + ||filteredEntry=="ApplyNearFieldDetailCS"||filteredEntry=="EmitNearCS")?1u:0u; tile.voxelSizeCm=25.0f;tile.outputVertexCapacity=3; FarFieldConstants field{}; field.strataLo=-100.0f;field.strataHi=100.0f;field.strataBlend=12.0f; @@ -763,6 +768,12 @@ namespace auto noiseInputSrv=Srv(noiseInputs.Get(),noiseInput.size()); auto noiseOutputUav=Uav(noiseOutputs.Get(),noiseInput.size()); + const double resourceCreateMs=std::chrono::duration( + Clock::now()-ResourceCreateStart).count(); + if(fenceEachKernel) + std::printf("[GpuShaderWarmupProfile] stage=warmup_resource_create duration_ms=%.3f\n", + resourceCreateMs); + if(!dispatchShaders) { std::printf("[GpuShaderWarmup] resources_only=true tiny_samples=%u tiny_cells=%u dispatches=0\n", @@ -780,121 +791,174 @@ namespace context->CSSetShaderResources(0,srvCount,nullSrvs); context->CSSetUnorderedAccessViews(0,8,nullUavs,nullptr); }; - auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry) + auto Dispatch=[&](ID3D11ComputeShader* shader,const char* entry, + Clock::time_point stateStart) { - if(!entryFilter.empty()&&entryFilter!=entry)return; - if(entryFilter.empty()&&(std::string_view(entry)=="ClassifyCS" + if(!entryFilter.empty()&&!skipEntryFilter&&filteredEntry!=entry)return; + if(skipEntryFilter&&filteredEntry==entry)return; + if((entryFilter.empty()||skipEntryFilter)&&(std::string_view(entry)=="ClassifyCS" ||std::string_view(entry)=="EmitNearCS"||std::string_view(entry)=="NoiseProbeCS"))return; const auto start=Clock::now(); context->CSSetShader(shader,nullptr,0);context->Dispatch(1,1,1); ++dispatchCount; - std::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry, - std::chrono::duration(Clock::now()-start).count()); + const double stateMs=std::chrono::duration(start-stateStart).count(); + const double dispatchMs=std::chrono::duration(Clock::now()-start).count(); + if(fenceEachKernel) + { + D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; + ComPtr eventQuery; + const auto queryStart=Clock::now(); + Check(device->CreateQuery(&eventDesc,&eventQuery), + (std::string("CreateQuery(shader warmup ")+entry+")").c_str()); + const double queryCreateMs=std::chrono::duration( + Clock::now()-queryStart).count(); + context->End(eventQuery.Get()); + const auto flushStart=Clock::now(); + context->Flush(); + const double flushMs=std::chrono::duration( + Clock::now()-flushStart).count(); + const auto fenceStart=Clock::now(); + for(;;) + { + const HRESULT hr=immediateContext->GetData(eventQuery.Get(),nullptr,0, + D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_OK)break; + if(FAILED(hr))throw std::runtime_error( + std::string("GetData(shader warmup ")+entry+") failed"); + if(std::chrono::duration(Clock::now()-fenceStart).count()>10.0) + throw std::runtime_error(std::string("shader warmup fence timed out: ")+entry); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + const double fenceWaitMs=std::chrono::duration( + Clock::now()-fenceStart).count(); + std::printf("[GpuShaderWarmupProfile] entry=%s state_bind_ms=%.3f first_dispatch_ms=%.3f query_create_ms=%.3f flush_ms=%.3f fence_wait_ms=%.3f fence=complete\n", + entry,stateMs,dispatchMs,queryCreateMs,flushMs,fenceWaitMs); + } + else + std::printf("[GpuShaderWarmup] entry=%s dispatch_call_ms=%.3f groups=1\n",entry,dispatchMs); }; ID3D11ShaderResourceView* evalSrvs[21]{}; evalSrvs[10]=shapeSrv.Get();evalSrvs[11]=binSrv.Get();evalSrvs[12]=shapeIdSrv.Get(); evalSrvs[13]=protectedAirSrv.Get();evalSrvs[14]=protectedSolidSrv.Get(); evalSrvs[16]=detailSrv.Get();evalSrvs[17]=proxySrv.Get(); + auto stateStart=Clock::now(); context->CSSetShaderResources(0,20,evalSrvs); ID3D11UnorderedAccessView* evalUavs[8]{}; evalUavs[1]=nearDetailBaseUav.Get(); evalUavs[5]=nearNormalUav.Get();evalUavs[6]=evalCounterUav.Get();evalUavs[7]=densityUav.Get(); context->CSSetUnorderedAccessViews(0,8,evalUavs,nullptr); - Dispatch(fieldEval.Get(),"EvaluateFarFieldCS");ClearBindings(); - Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS");ClearBindings(); + Dispatch(fieldEval.Get(),"EvaluateFarFieldCS",stateStart);ClearBindings(); + stateStart=Clock::now(); + Dispatch(fieldEvalNear.Get(),"EvaluateNearFieldCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* nearDetailSrvs[21]{}; nearDetailSrvs[13]=protectedAirSrv.Get();nearDetailSrvs[14]=protectedSolidSrv.Get(); nearDetailSrvs[16]=detailSrv.Get();nearDetailSrvs[17]=proxySrv.Get(); nearDetailSrvs[20]=nearDetailBaseSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,21,nearDetailSrvs); ID3D11UnorderedAccessView* nearDetailUavs[8]{}; nearDetailUavs[5]=nearNormalUav.Get();nearDetailUavs[7]=densityUav.Get(); context->CSSetUnorderedAccessViews(0,8,nearDetailUavs,nullptr); - Dispatch(fieldEvalNearDetail.Get(),"ApplyNearFieldDetailCS");ClearBindings(); + Dispatch(fieldEvalNearDetail.Get(),"ApplyNearFieldDetailCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* classifySrvs[16]{}; classifySrvs[0]=signSrv.Get();classifySrvs[1]=triangleSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,16,classifySrvs); ID3D11UnorderedAccessView* classifyUavs[8]{}; classifyUavs[0]=caseUav.Get();classifyUavs[1]=localOffsetUav.Get();classifyUavs[2]=groupCountUav.Get(); context->CSSetUnorderedAccessViews(0,8,classifyUavs,nullptr); - Dispatch(classify.Get(),"ClassifyCS");ClearBindings(16); + Dispatch(classify.Get(),"ClassifyCS",stateStart);ClearBindings(16); ID3D11ShaderResourceView* farClassifySrvs[16]{}; farClassifySrvs[1]=triangleSrv.Get();farClassifySrvs[15]=densitySrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,16,farClassifySrvs); context->CSSetUnorderedAccessViews(0,8,classifyUavs,nullptr); - Dispatch(fieldClassify.Get(),"ClassifyFarCS");ClearBindings(16); + Dispatch(fieldClassify.Get(),"ClassifyFarCS",stateStart);ClearBindings(16); ID3D11ShaderResourceView* scanSrvs[9]{};scanSrvs[8]=groupCountSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,9,scanSrvs); ID3D11UnorderedAccessView* scanUavs[8]{};scanUavs[3]=groupOffsetUav.Get();scanUavs[4]=argsUav.Get(); context->CSSetUnorderedAccessViews(0,8,scanUavs,nullptr); - Dispatch(scan.Get(),"ScanGroupsCS");ClearBindings(9); + Dispatch(scan.Get(),"ScanGroupsCS",stateStart);ClearBindings(9); ID3D11ShaderResourceView* emitSrvs[20]{}; emitSrvs[1]=triangleSrv.Get();emitSrvs[2]=densitySrv.Get();emitSrvs[3]=caseSrv.Get(); emitSrvs[4]=localOffsetSrv.Get();emitSrvs[5]=groupOffsetSrv.Get(); emitSrvs[6]=midpointIdSrv.Get();emitSrvs[7]=midpointRecordSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,20,emitSrvs); ID3D11UnorderedAccessView* emitUavs[8]{};emitUavs[5]=vertexUav.Get();emitUavs[6]=indexUav.Get(); context->CSSetUnorderedAccessViews(0,8,emitUavs,nullptr); - Dispatch(emit.Get(),"EmitCS");ClearBindings(); - Dispatch(emitNear.Get(),"EmitNearCS");ClearBindings(); + Dispatch(emit.Get(),"EmitCS",stateStart);ClearBindings(); + stateStart=Clock::now(); + Dispatch(emitNear.Get(),"EmitNearCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* ringSrvs[5]={connectorPanelSrv.Get(),nullptr,shapeSrv.Get(),binSrv.Get(),shapeIdSrv.Get()}; + stateStart=Clock::now(); context->CSSetShaderResources(8,5,ringSrvs); ID3D11UnorderedAccessView* ringUavs[8]{};ringUavs[4]=argsUav.Get();ringUavs[7]=connectorRingUav.Get(); context->CSSetUnorderedAccessViews(0,8,ringUavs,nullptr); - Dispatch(connectorRings.Get(),"BuildFarConnectorRingsCS");ClearBindings(); + Dispatch(connectorRings.Get(),"BuildFarConnectorRingsCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* panelSrvs[2]={connectorPanelSrv.Get(),connectorRingSrv.Get()}; + stateStart=Clock::now(); context->CSSetShaderResources(8,2,panelSrvs); ID3D11UnorderedAccessView* panelUavs[8]{};panelUavs[5]=panelVertexUav.Get();panelUavs[6]=panelIndexUav.Get(); context->CSSetUnorderedAccessViews(0,8,panelUavs,nullptr); - Dispatch(connectorPanels.Get(),"EmitFarConnectorPanelsCS");ClearBindings(); + Dispatch(connectorPanels.Get(),"EmitFarConnectorPanelsCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* skirtSrvs[22]{}; skirtSrvs[10]=shapeSrv.Get();skirtSrvs[11]=binSrv.Get();skirtSrvs[12]=shapeIdSrv.Get(); skirtSrvs[13]=protectedAirSrv.Get();skirtSrvs[14]=protectedSolidSrv.Get();skirtSrvs[19]=vertexSrv.Get(); skirtSrvs[21]=skirtProbeSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,22,skirtSrvs); ID3D11UnorderedAccessView* classifySkirtUavs[8]{}; classifySkirtUavs[0]=skirtScratchUav.Get();classifySkirtUavs[2]=skirtProbeUav.Get(); classifySkirtUavs[4]=argsUav.Get(); context->CSSetUnorderedAccessViews(0,8,classifySkirtUavs,nullptr); - Dispatch(classifyFarSkirts.Get(),"ClassifyFarSkirtsCS");ClearBindings(); + Dispatch(classifyFarSkirts.Get(),"ClassifyFarSkirtsCS",stateStart);ClearBindings(); ID3D11UnorderedAccessView* scanSkirtUavs[8]{}; + stateStart=Clock::now(); scanSkirtUavs[0]=skirtScratchUav.Get();scanSkirtUavs[4]=argsUav.Get(); context->CSSetUnorderedAccessViews(0,8,scanSkirtUavs,nullptr); - Dispatch(scanFarSkirts.Get(),"ScanFarSkirtsCS");ClearBindings(); + Dispatch(scanFarSkirts.Get(),"ScanFarSkirtsCS",stateStart);ClearBindings(); + stateStart=Clock::now(); context->CSSetShaderResources(0,22,skirtSrvs); ID3D11UnorderedAccessView* emitSkirtUavs[8]{}; emitSkirtUavs[0]=skirtScratchUav.Get();emitSkirtUavs[4]=argsUav.Get(); emitSkirtUavs[5]=panelVertexUav.Get();emitSkirtUavs[6]=panelIndexUav.Get(); context->CSSetUnorderedAccessViews(0,8,emitSkirtUavs,nullptr); - Dispatch(emitFarSkirts.Get(),"EmitFarSkirtsCS");ClearBindings(); + Dispatch(emitFarSkirts.Get(),"EmitFarSkirtsCS",stateStart);ClearBindings(); ID3D11ShaderResourceView* noiseSrvs[10]{};noiseSrvs[9]=noiseInputSrv.Get(); + stateStart=Clock::now(); context->CSSetShaderResources(0,10,noiseSrvs); ID3D11UnorderedAccessView* noiseUavs[8]{};noiseUavs[6]=noiseOutputUav.Get(); context->CSSetUnorderedAccessViews(0,8,noiseUavs,nullptr); - Dispatch(noiseProbe.Get(),"NoiseProbeCS");ClearBindings(10); + Dispatch(noiseProbe.Get(),"NoiseProbeCS",stateStart);ClearBindings(10); ID3D11Buffer* nullConstants[3]{};context->CSSetConstantBuffers(0,3,nullConstants); context->CSSetShader(nullptr,nullptr,0); - D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; - Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)"); - context->End(shaderWarmupCompletion.Get());context->Flush(); + if(!fenceEachKernel) + { + D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; + Check(device->CreateQuery(&eventDesc,&shaderWarmupCompletion),"CreateQuery(shader warmup completion)"); + context->End(shaderWarmupCompletion.Get());context->Flush(); + } if(dispatchShaders&&dispatchCount==0) throw std::runtime_error("shader warmup selected no dispatches"); - std::printf("[GpuShaderWarmup] dispatched=%u selected=%s tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=pending\n", + std::printf("[GpuShaderWarmup] dispatched=%u selected=%s tiny_samples=%u tiny_cells=%u queue_ms=%.3f completion=%s\n", dispatchCount,entryFilter.empty()?"production":entryFilter.c_str(),sampleCount,cellCount, - std::chrono::duration(Clock::now()-WarmupStart).count()); + std::chrono::duration(Clock::now()-WarmupStart).count(), + fenceEachKernel?"per_kernel_fenced":"pending"); } bool CompleteTinyShaderWarmup() @@ -3219,11 +3283,12 @@ namespace VoxelForgeGpuFarViewer bool QueueFarShaderWarmup(ID3D11Device* device,ID3D11DeviceContext* context, const std::filesystem::path& shaderPath,bool dispatchShaders, - const std::string& entryFilter,std::string& error) + const std::string& entryFilter,bool fenceEachKernel,std::string& error) { try { - GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup(dispatchShaders,entryFilter); + GetFarViewerRunner(device,context,shaderPath).QueueTinyShaderWarmup( + dispatchShaders,entryFilter,fenceEachKernel); return true; } catch(const std::exception& e){error=e.what();return false;} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CaptureMatrix.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CaptureMatrix.ps1 new file mode 100644 index 0000000..2a66bf5 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CaptureMatrix.ps1 @@ -0,0 +1,80 @@ +[CmdletBinding()] +param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe') + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} +$Views=@( + @{Name='chamber';Camera='-11.75,-123.25,-19.85,0,0'}, + @{Name='tunnel';Camera='-11.75,-123.25,-19.85,90,0'}, + @{Name='passage';Camera='-39.19,6.27,-28.51,63,0'}, + @{Name='far';Camera='-11.61244,-118.2212,-19.8452,0,0'}, + @{Name='passage_floor_closeup';Camera='-39.19,6.27,-25.51,63,-25'}, + @{Name='tunnel_bend';Camera='-198.97,-99.57,-45.58,33.59,23.17'}, + @{Name='remote_yaw0';Camera='500,0,0,0,0'}, + @{Name='remote_yaw90';Camera='500,0,0,90,0'}, + @{Name='remote_yaw180';Camera='500,0,0,180,0'}, + @{Name='remote_yaw270';Camera='500,0,0,270,0'} +) +$EightGB=8L*1024L*1024L*1024L +foreach($View in $Views) +{ + foreach($Mode in @('cpu','gpu')) + { + $Stem="step14_${Mode}_$($View.Name)" + $CaptureName="$Stem.png" + $Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--capture-after-clipmap', + "--camera=$($View.Camera)","--$Mode-far", + "--capture=Research/OwnerShapeWorld/gpu_field_port/$CaptureName") + $StdoutPath=Join-Path $PSScriptRoot ($Stem+'.stdout.log') + $StderrPath=Join-Path $PSScriptRoot ($Stem+'.stderr.log') + $CapturePath=Join-Path $PSScriptRoot $CaptureName + Write-Output "STEP14_CAPTURE mode=$Mode view=$($View.Name) affinity=0xFF00 command=`"$ExecutablePath`" $($Arguments -join ' ')" + for($Attempt=0;$Attempt-lt2;$Attempt++) + { + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($PeakPrivate-ge$EightGB) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 100 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode + if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single permitted retry.'} + if($OverMemoryLimit){throw "Capture viewer stopped at the 8 GiB private-memory ceiling: $Stem"} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + if(-not$LogText.Contains('[InitialClipmapReady]')-or-not(Test-Path -LiteralPath $CapturePath -PathType Leaf)) + {throw "Full-residency capture missing: $Stem; exit=$ChildExitCode"} + $Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)') + $CombinedGB=$PeakPrivate/1GB + if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB} + if($CombinedGB-ge8.0){throw "Capture exceeded conservative private + DXGI-local 8 GiB ceiling: $Stem ($CombinedGB GiB)"} + $Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s') + Write-Output "STEP14_CAPTURE_DONE mode=$Mode view=$($View.Name) ready_s=$($Ready.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) path=$CapturePath exit=$ChildExitCode" + if($ChildExitCode-ne0){throw "Capture viewer returned $ChildExitCode`: $Stem"} + break + } + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CpuReference.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CpuReference.ps1 new file mode 100644 index 0000000..64fef33 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14CpuReference.ps1 @@ -0,0 +1,63 @@ +[CmdletBinding()] +param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe') + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} +$Reference=$null +foreach($Workers in @(1,8)) +{ + $Name="step14_cpu_reference_w$Workers" + $StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log') + $StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log') + $Arguments=@('--stream-benchmark','--shape-world','--force-generate','--stream-hash', + '--clip-radius=3',"--workers=$Workers") + Write-Output "STEP14_CPU_REFERENCE workers=$Workers affinity=0xFF00 priority=BelowNormal command=`"$ExecutablePath`" $($Arguments -join ' ')" + for($Attempt=0;$Attempt-lt2;$Attempt++) + { + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($Child.PrivateMemorySize64-ge(8L*1GB)) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 100 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode + if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'} + if($OverMemoryLimit){throw "CPU reference stopped at the 8 GiB private-memory ceiling: W$Workers"} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + $Stream=[regex]::Match($LogText,'(?m)^\[StreamFullClipmap\].*generated=2226/2226.*density_hash=(0x[0-9a-fA-F]+) mesh_hash=(0x[0-9a-fA-F]+) payload_hash=(0x[0-9a-fA-F]+) topology_hash=(0x[0-9a-fA-F]+)') + $Walkability=[regex]::Match($LogText,'(?m)^\[Walkability\] PASS 5642/5642') + if(-not$Stream.Success-or-not$Walkability.Success) + {throw "CPU reference failed full-generation/hash/walkability checks for W$Workers; exit=$ChildExitCode"} + $Hashes=@($Stream.Groups[1].Value.ToLowerInvariant(),$Stream.Groups[2].Value.ToLowerInvariant(), + $Stream.Groups[3].Value.ToLowerInvariant(),$Stream.Groups[4].Value.ToLowerInvariant()) + if($null-eq$Reference){$Reference=$Hashes} + elseif(($Reference-join'|')-ne($Hashes-join'|')){throw 'CPU hashes differ between W1 and W8.'} + Write-Output "STEP14_CPU_REFERENCE_RESULT workers=$Workers hashes=$($Hashes -join ',') walkability=5642/5642 peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) exit=$ChildExitCode" + if($ChildExitCode-ne0){throw "CPU reference returned $ChildExitCode`: W$Workers"} + break + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14FinalInterleaved.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14FinalInterleaved.ps1 new file mode 100644 index 0000000..d4ba776 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14FinalInterleaved.ps1 @@ -0,0 +1,77 @@ +[CmdletBinding()] +param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe') + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} + +$Runs=@( + @{Mode='cpu';Name='step14_race_cpu_A'}, + @{Mode='gpu';Name='step14_race_gpu_A'}, + @{Mode='gpu';Name='step14_race_gpu_B'}, + @{Mode='cpu';Name='step14_race_cpu_B'} +) +$EightGB=8L*1024L*1024L*1024L +foreach($Run in $Runs) +{ + $Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap',"--$($Run.Mode)-far") + if($Run.Mode-eq'gpu'){$Arguments+='--gpu-far-no-present-hold'} + $StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log') + $StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log') + Write-Output "STEP14_RACE_RUN mode=$($Run.Mode) name=$($Run.Name) priority=BelowNormal affinity=0xFF00 workers=8 command=`"$ExecutablePath`" $($Arguments -join ' ')" + for($Attempt=0;$Attempt-lt2;$Attempt++) + { + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $HostRunClock=[Diagnostics.Stopwatch]::StartNew() + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($Child.PrivateMemorySize64-ge$EightGB) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 50 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop();$HostRunMs=$HostRunClock.Elapsed.TotalMilliseconds + $ViewerExitCode=[int]$Child.ExitCode + if($ViewerExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ViewerExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'} + if($OverMemoryLimit){throw "Viewer stopped at the 8 GiB private-memory ceiling: $($Run.Name)"} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + if(-not$LogText.Contains('[InitialClipmapReady]')) + {throw "Full clipmap was not ready in $($Run.Name); exit=$ViewerExitCode"} + $Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s') + $Presented=[regex]::Match($LogText,'(?m)^\[WholeViewPresented\] at_ms=([0-9.]+) present_ms=([0-9.]+).*hr=0x([0-9A-Fa-f]{8})') + if(-not$Presented.Success){throw "Whole-view present marker missing in $($Run.Name)"} + $Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+)') + $Wait=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] completion_wait_ms=([0-9.]+)') + $WarmupMs=if($Warmup.Success){[double]$Warmup.Groups[1].Value+$(if($Wait.Success){[double]$Wait.Groups[1].Value}else{0.0})}else{0.0} + $Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)') + $CombinedGB=$PeakPrivate/1GB + if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB} + if($CombinedGB-ge8.0){throw "Viewer exceeded conservative private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"} + Write-Output "STEP14_RACE_RESULT mode=$($Run.Mode) name=$($Run.Name) warmup_ms=$([math]::Round($WarmupMs,3)) ready_ms=$([math]::Round([double]$Ready.Groups[1].Value*1000,3)) viewer_whole_view_present_return_ms=$($Presented.Groups[1].Value) present_ms=$($Presented.Groups[2].Value) present_hr=0x$($Presented.Groups[3].Value.ToUpperInvariant()) host_process_exit_ms=$([math]::Round($HostRunMs,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode" + $Controls=[regex]::Match($LogText,'(?m)^\[ProcessControls\].*') + if($Controls.Success){Write-Output $Controls.Value} + if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"} + break + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14GpuSurvival.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14GpuSurvival.ps1 new file mode 100644 index 0000000..6140ba7 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14GpuSurvival.ps1 @@ -0,0 +1,59 @@ +[CmdletBinding()] +param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe') + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} +$Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far-audit','--gpu-far-prewarm') +$Name='step14_gpu_survival' +$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log') +$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log') +$EightGB=8L*1024L*1024L*1024L +Write-Output "STEP14_SURVIVAL_RUN command=`"$ExecutablePath`" $($Arguments -join ' ')" +for($Attempt=0;$Attempt-lt2;$Attempt++) +{ + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($Child.PrivateMemorySize64-ge$EightGB) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 100 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$ChildExitCode=[int]$Child.ExitCode + if($ChildExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ChildExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'} + if($OverMemoryLimit){throw 'GPU survival audit stopped at the private-memory ceiling.'} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + if(-not$LogText.Contains('[InitialClipmapReady]')){throw "GPU survival audit did not reach full residency; exit=$ChildExitCode"} + $Audit=[regex]::Match($LogText,'(?m)^\[GpuFarSurvivalAudit\].*status=(PASS|FAIL)') + $Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)') + if(-not$Audit.Success){throw 'GPU survival audit result marker missing.'} + $DxgiMB=if($Memory.Success){[double]$Memory.Groups[1].Value}else{0.0} + $CombinedGB=($PeakPrivate+$DxgiMB*1MB)/1GB + if($CombinedGB-ge8.0){throw "GPU survival exceeded conservative private + DXGI-local 8 GiB ceiling: $CombinedGB GiB"} + Write-Output "STEP14_SURVIVAL_RESULT audit=$($Audit.Groups[1].Value) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) dxgi_local_peak_mb=$DxgiMB conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ChildExitCode stdout=$StdoutPath stderr=$StderrPath" + if($Audit.Groups[1].Value-ne'PASS'){throw 'GPU survival audit failed.'} + if($ChildExitCode-ne0){throw "Viewer returned $ChildExitCode"} + break +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14ShaderFirstUse.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14ShaderFirstUse.ps1 new file mode 100644 index 0000000..0381429 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14ShaderFirstUse.ps1 @@ -0,0 +1,67 @@ +[CmdletBinding()] +param( + [string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe', + [string]$Entry='ApplyNearFieldDetailCS', + [string]$SampleName='near_detail' +) + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} +if($Entry-ne'ApplyNearFieldDetailCS'){throw "Unsupported Step 14 shader entry: $Entry"} +$Name='step14_shader_'+$SampleName +$StdoutPath=Join-Path $PSScriptRoot ($Name+'.stdout.log') +$StderrPath=Join-Path $PSScriptRoot ($Name+'.stderr.log') +$Arguments=@('--workers=8','--gpu-far-warmup-only',"--gpu-far-warmup-entry=$Entry",'--gpu-far-warmup-kernel-fences') +$EightGB=8L*1024L*1024L*1024L +Write-Output "STEP14_SHADER_RUN entry=$Entry sample=$SampleName freshness=fresh_process priority=BelowNormal affinity=0xFF00 command=`"$ExecutablePath`" $($Arguments -join ' ')" +for($Attempt=0;$Attempt-lt2;$Attempt++) +{ + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $HostRunClock=[Diagnostics.Stopwatch]::StartNew() + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($Child.PrivateMemorySize64-ge$EightGB) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 50 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop() + $ViewerExitCode=[int]$Child.ExitCode + if($ViewerExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ViewerExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'} + if($OverMemoryLimit){throw "Shader profile stopped at the 8 GiB private-memory ceiling: $SampleName"} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + $Create=[regex]::Match($LogText,'(?m)^\[GpuPipelineProfile\] stage=shader_object_create entry='+[regex]::Escape($Entry)+' duration_ms=([0-9.]+)') + $Profile=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmupProfile\] entry='+[regex]::Escape($Entry)+' .*') + $Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=1 selected='+[regex]::Escape($Entry)+'.*queue_ms=([0-9.]+)') + $Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)') + if(-not$Create.Success-or-not$Profile.Success-or-not$Warmup.Success) + {throw "Isolated profile markers missing for $Entry; exit=$ViewerExitCode"} + $CombinedGB=$PeakPrivate/1GB + if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB} + if($CombinedGB-ge8.0){throw "Shader profile exceeded private + DXGI-local 8 GiB ceiling: $SampleName ($CombinedGB GiB)"} + Write-Output "STEP14_SHADER_RESULT entry=$Entry sample=$SampleName object_create_ms=$($Create.Groups[1].Value) profile_ms=$($Profile.Groups[0].Value) isolated_queue_ms=$($Warmup.Groups[1].Value) host_process_exit_ms=$([math]::Round($HostRunClock.Elapsed.TotalMilliseconds,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode" + if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode for $SampleName"} + break +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14WarmupProfile.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14WarmupProfile.ps1 new file mode 100644 index 0000000..1c74ba2 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunStep14WarmupProfile.ps1 @@ -0,0 +1,83 @@ +[CmdletBinding()] +param([string]$ExecutableName='VoxelForgeGpuFieldStep14Warmup.exe',[switch]$SkipControlOnly,[string]$Label='profile') + +$ErrorActionPreference='Stop' +$HostProcess=Get-Process -Id $PID +$HostProcess.PriorityClass='BelowNormal' +$HostProcess.ProcessorAffinity=[IntPtr]0xFF00 +$StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename.'} +if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "Viewer not found: $ExecutablePath"} + +$Runs=@( + @{Name="step14_${Label}_workers_active";Idle=$false;WarmupOnly=$false}, + @{Name="step14_${Label}_workers_idle";Idle=$true;WarmupOnly=$false}, + @{Name="step14_${Label}_skip_near_detail";Idle=$true;WarmupOnly=$true} +) +if($SkipControlOnly){$Runs=@($Runs[2])} +$EightGB=8L*1024L*1024L*1024L +foreach($Run in $Runs) +{ + if($Run.WarmupOnly) + {$Arguments=@('--workers=8','--gpu-far-warmup-only','--gpu-far-warmup-kernel-fences', + '--gpu-far-warmup-entry=skip:ApplyNearFieldDetailCS')} + else + { + $Arguments=@('--clip-radius=3','--workers=8','--quit-after-clipmap','--gpu-far', + '--gpu-far-warmup-kernel-fences') + if($Run.Idle){$Arguments+='--gpu-far-warmup-before-workers'} + } + $StdoutPath=Join-Path $PSScriptRoot ($Run.Name+'.stdout.log') + $StderrPath=Join-Path $PSScriptRoot ($Run.Name+'.stderr.log') + Write-Output "STEP14_PROFILE_RUN name=$($Run.Name) cpu_workers_idle=$($Run.Idle.ToString().ToLowerInvariant()) priority=BelowNormal affinity=0xFF00 command=`"$ExecutablePath`" $($Arguments -join ' ')" + for($Attempt=0;$Attempt-lt2;$Attempt++) + { + if($Attempt-gt0) + {Write-Output '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.';Start-Sleep -Seconds 60} + Remove-Item -LiteralPath $StdoutPath,$StderrPath -Force -ErrorAction SilentlyContinue + $HostRunClock=[Diagnostics.Stopwatch]::StartNew() + $Child=Start-Process -FilePath $ExecutablePath -ArgumentList $Arguments -WorkingDirectory $StandaloneRoot ` + -WindowStyle Hidden -PassThru -RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath + $PeakPrivate=0L;$OverMemoryLimit=$false + try + { + while(-not$Child.HasExited) + { + $Child.Refresh();if($Child.HasExited){break} + $Child.PriorityClass='BelowNormal';$Child.ProcessorAffinity=[IntPtr]0xFF00 + $PeakPrivate=[Math]::Max($PeakPrivate,$Child.PrivateMemorySize64) + if($Child.PrivateMemorySize64-ge$EightGB) + {$OverMemoryLimit=$true;$Child.Kill();$Child.WaitForExit();break} + Start-Sleep -Milliseconds 50 + } + } + finally + { + if(-not$Child.HasExited){$Child.Kill();$Child.WaitForExit()} + } + $Child.WaitForExit();$Child.Refresh();$HostRunClock.Stop() + $ViewerExitCode=[int]$Child.ExitCode + if($ViewerExitCode-eq-1073741502-and$Attempt-eq0){continue} + if($ViewerExitCode-eq-1073741502){throw '0xC0000142 repeated after its single retry; stopping.'} + if($OverMemoryLimit){throw "Profile process stopped at the 8 GiB private-memory ceiling: $($Run.Name)"} + $LogText=Get-Content -LiteralPath $StdoutPath -Raw + if(-not$Run.WarmupOnly-and-not$LogText.Contains('[InitialClipmapReady]')) + {throw "Full clipmap did not become ready in $($Run.Name); exit=$ViewerExitCode"} + if(-not$LogText.Contains('[GpuShaderWarmupProfile] stage=warmup_resource_create')) + {throw "Warmup profile markers are missing in $($Run.Name); exit=$ViewerExitCode"} + $Warmup=[regex]::Match($LogText,'(?m)^\[GpuShaderWarmup\] dispatched=[0-9]+ .*queue_ms=([0-9.]+) completion=per_kernel_fenced') + $Ready=[regex]::Match($LogText,'(?m)^\[InitialClipmapReady\] ([0-9.]+) s') + $Memory=[regex]::Match($LogText,'(?m)^\[GpuFarMemory\] dxgi_local_sampled_peak_mb=([0-9.]+)') + if(-not$Warmup.Success-or(-not$Run.WarmupOnly-and-not$Ready.Success)) + {throw "Warmup or ready timing missing in $($Run.Name)"} + $CombinedGB=$PeakPrivate/1GB + if($Memory.Success){$CombinedGB=($PeakPrivate+[double]$Memory.Groups[1].Value*1MB)/1GB} + if($CombinedGB-ge8.0){throw "Warmup profile exceeded private + DXGI-local 8 GiB ceiling: $($Run.Name) ($CombinedGB GiB)"} + $ReadyMs=if($Ready.Success){[math]::Round([double]$Ready.Groups[1].Value*1000,3)}else{'not_applicable'} + Write-Output "STEP14_PROFILE_RESULT name=$($Run.Name) cpu_workers_idle=$($Run.Idle.ToString().ToLowerInvariant()) warmup_ms=$($Warmup.Groups[1].Value) ready_ms=$ReadyMs host_process_exit_ms=$([math]::Round($HostRunClock.Elapsed.TotalMilliseconds,3)) peak_private_gb=$([math]::Round($PeakPrivate/1GB,2)) conservative_private_plus_dxgi_gb=$([math]::Round($CombinedGB,2)) exit=$ViewerExitCode" + if($ViewerExitCode-ne0){throw "Viewer returned $ViewerExitCode`: $($Run.Name)"} + break + } +} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP14_REPORT.md b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP14_REPORT.md new file mode 100644 index 0000000..71f4d12 --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP14_REPORT.md @@ -0,0 +1,80 @@ +# GPU port, step 14: warm-up attribution and near-detail compile cost + +Date: 2026-10-07. All edits and generated artifacts are under `Standalone\`. MSVC builds used `/MP8`; viewer and probe processes ran hidden at BelowNormal priority and affinity `0xFF00`. No `0xC0000142` retry was needed, maximum conservative private plus DXGI-local memory was 5.44 GiB, and no cache or driver settings were changed. No viewer remained open, and no full-size capture was opened in an image viewer. No commit, push, or stash was made. + +## Result + +The production warm-up was not the sum of isolated shader first-use times. In the production queue, `ApplyNearFieldDetailCS` accounts for most of the first-use stall, and its driver submission cost is paid on the immediate-context `Flush`. Creating its shader object, making warm-up resources, binding state, dispatching, and waiting on the final event are all small by comparison. A control that creates every shader object and warm-up resource but skips only the near-detail dispatch cuts the warm-up substantially; the connector-ring dispatch then becomes the next visible first-use stall. + +The near-detail HLSL now shares the anchored/direct owner-roughness loop body and evaluates standard/ridged fractal outputs from the same Perlin samples. The Step 13 `ApplyNearFieldDetailCS` bytecode was 448,588 bytes; the Step 14 bytecode is 120,860 bytes, a 73.1% reduction. Its isolated first-use queue fell from 1,595.548 ms to 709.471 ms, a 55.5% reduction. + +Production warm-up fell from the Step 13 median of 2,041.698 ms to 1,052.313 ms. Whole-view readiness did not improve with it: the Step 14 interleaved race measured 1.352 s CPU versus 3.196 s hybrid. Most shader warm-up overlaps CPU shape compilation; the remaining hybrid floor is the tile stream after warm-up. + +## Warm-up attribution + +The viewer now has an opt-in `--gpu-far-warmup-kernel-fences` profile. It records pipeline shader-object creation, warm-up resource creation, resource/state binding, each first dispatch, event-query creation, `Flush`, and completion fence, in production dispatch order. The normal production queue remains batched and uses one final event. `--gpu-far-warmup-before-workers` runs the same path before CPU generation workers start. + +Baseline Step 13 shader, per-kernel fenced: + +| Stage | CPU workers active | CPU workers idle | +|---|---:|---:| +| Shader pipeline creation, all entries | 52.733 ms | 61.128 ms | +| `ApplyNearFieldDetailCS` shader-object creation | 16.586 ms | 18.803 ms | +| Warm-up resource creation | 2.356 ms | 2.395 ms | +| Near-detail state binding | 0.005 ms | 0.006 ms | +| Near-detail dispatch call | 0.002 ms | 0.003 ms | +| Near-detail `Flush` | **1,980.816 ms** | **1,734.219 ms** | +| Near-detail completion fence | 7.101 ms | 8.804 ms | +| Entire fenced warm-up | 2,139.680 ms | 1,892.968 ms | + +The skip control used the same shader-object creation and resources, then fenced all production entries except `ApplyNearFieldDetailCS`. Its idle-worker queue was 520.764 ms; `BuildFarConnectorRingsCS` took 363.553 ms in `Flush`. With near detail dispatched first, that ring `Flush` was under 0.4 ms in both baseline profiles. This demonstrates why adding isolated cold costs overcounts production: the entries share driver-side first-use work. The near-detail dispatch accounts for the majority of the warm-up, while the connector ring is the secondary cold entry. + +The final Step 14 per-kernel profile measured near-detail `Flush` at 964.597 ms with workers active and 667.969 ms with workers idle. The corresponding complete fenced queues were 1,138.410 ms and 837.211 ms. The final skip-near-detail control was 535.680 ms, with a 382.239 ms connector-ring `Flush`. + +The evidence identifies first-use driver work on the shader submission path: direct dispatch calls remain about 0.002–0.006 ms, object creation is milliseconds, resource creation is about 2 ms, and the long delay lands in `Flush`; the skip control removes that dispatch while leaving object creation intact. The D3D11 API does not expose a driver compiler timer, so the exact internal AMD operation cannot be separated from other driver translation/validation work. The measured behavior is consistent with synchronous first-use shader translation/compilation at `Flush`, serialized by the per-entry control. + +## Near-detail restructuring + +- Kept the generic looped value-noise routine introduced in Step 13. +- Replaced separate anchored (step 8) and direct (step 2) owner-roughness call paths with a `[loop]` over those two sample modes. The direct sample is still skipped when passage influence is at or below 0.40. +- Added `NearOwnerFractalPair`, which computes the standard and ridged accumulators from one Perlin sample per octave. The owner noise type selects the same prior output, including the averaged type. +- `ApplyNearFieldDetailCS` does not call `NearFieldDensityAt` or the root-refinement helper, so root refinement was not part of this kernel and was not removed. + +The isolated shader profile after both changes reported object creation 5.831 ms, state binding 0.001 ms, dispatch 0.003 ms, `Flush` 689.707 ms, event wait 16.272 ms, and queue 709.471 ms. The event wait includes the profiling loop's 1 ms polling interval; it is not a GPU execution-time estimate. + +## Correctness, survival, and captures + +- Whole near audit: **960/960 tiles**, zero hardware topology differences, zero WARP topology differences, zero differences across **229,376 protected cells**, and bit-identical repeated GPU output. +- Whole far audit: **1,114** eligible tiles, 152 near-transition tiles skipped, zero hardware and WARP topology differences, zero differences across **127,401 protected cells** and **353 thin-feature cells**, and bit-identical repeated GPU output. +- CPU reference at 1 and 8 workers: identical density `0xdb2991c73215af4c`, mesh `0x8236c5edd7fc8e4f`, payload `0x4c48fa75a543d58e`, topology `0xbea22e76b34a4dfc`; walkability **5,642/5,642**. +- Full-residency GPU survival audit passed at **5.08 GiB** conservative private plus DXGI-local memory. +- All ten 1280×800 CPU/GPU capture pairs reached full residency. Numeric RGB results exactly match the Step 13 comparison: MAE range **0.0484–1.6092**; the highest combined private plus DXGI-local sample was **5.44 GiB**. See [step14_capture_comparison.md](step14_capture_comparison.md). The PNGs were compared as arrays and not viewed. + +## Interleaved whole-view race + +Order was CPU A, GPU A, GPU B, CPU B. All runs used 8 workers. The GPU warm-up numbers below are the normal batched production path without per-kernel fences. + +| Run | Warm-up | Viewer ready / present return | Host process exit | +|---|---:|---:|---:| +| CPU A | — | 1,353 ms | 1,522.097 ms | +| GPU A | 1,048.305 ms | 3,084 ms | 3,235.229 ms | +| GPU B | 1,056.321 ms | 3,308 ms | 3,466.244 ms | +| CPU B | — | 1,350 ms | 1,471.182 ms | + +Median viewer readiness was **1.352 s CPU vs 3.196 s hybrid**: CPU leads by **1.845 s / 2.36×**. Median host-clock process exit was **1.497 s CPU vs 3.351 s hybrid**: CPU leads by **1.854 s / 2.24×**. Median warm-up was **1.052 s**, about 0.989 s below Step 13. `Present` returned `DXGI_STATUS_OCCLUDED`, so these clocks measure readiness and API return, not physical scanout. + +The warm-up overlaps shape compilation: GPU shape compilation took 835–903 ms while a 1.048–1.056 s warm-up ran. After GPU input export completed at 1.302–1.306 s, whole-view readiness took another 1.778–2.003 s. Those runs queued 1,082 GPU tiles in 35 batches and kept 1,144 CPU fallback tiles; CPU input preparation was 112–119 ms wall time and measured GPU dispatch timestamps summed to 200–216 ms. The current floor is therefore the post-warm-up hybrid tile stream and CPU fallback/submission path, not near-detail compilation alone. The GPU path still loses the whole-view race. + +## Lower-priority coverage case + +The direct-GPU `(6,2,0,lod3)` coverage issue remains open. The viewer's established CPU fallback for remote LOD3/4 remains enabled; this step's whole-far audit skips near transitions and does not claim direct LOD3/4 coverage parity. The prior diagnostic and capture evidence remains in [STEP9_REPORT.md](STEP9_REPORT.md). + +## Reproduction artifacts + +- Warm-up controls: [RunStep14WarmupProfile.ps1](RunStep14WarmupProfile.ps1) and `step14_profile_*` / `step14_final_*` logs. +- Near shader compile and first-use: [CompileStep14Shader.ps1](CompileStep14Shader.ps1), [RunStep14ShaderFirstUse.ps1](RunStep14ShaderFirstUse.ps1), and the `step14_shader_*.stdout.log` files. +- Agreement: `step14_whole_near_agreement.stdout.log`, `step14_whole_far_agreement.stdout.log`. +- CPU reference: [RunStep14CpuReference.ps1](RunStep14CpuReference.ps1) and `step14_cpu_reference_w*.stdout.log`. +- Survival: [RunStep14GpuSurvival.ps1](RunStep14GpuSurvival.ps1) and `step14_gpu_survival.stdout.log`. +- Captures: [RunStep14CaptureMatrix.ps1](RunStep14CaptureMatrix.ps1), [CompareStep14Captures.py](CompareStep14Captures.py), and `step14_capture_comparison.md`. +- Race: [RunStep14FinalInterleaved.ps1](RunStep14FinalInterleaved.ps1) and `step14_race_*.stdout.log`. diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ApplyNearFieldDetailCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ApplyNearFieldDetailCS.cso index 14abb08..f79aa29 100644 Binary files a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ApplyNearFieldDetailCS.cso and b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ApplyNearFieldDetailCS.cso differ diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/step14_capture_comparison.md b/Standalone/Research/OwnerShapeWorld/gpu_field_port/step14_capture_comparison.md new file mode 100644 index 0000000..dce244f --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/step14_capture_comparison.md @@ -0,0 +1,16 @@ +# Step 14 full-residency capture comparison + +All ten CPU/GPU pairs were captured after `[InitialClipmapReady]`. The files were compared numerically as RGB arrays and were not opened in an image viewer. + +| View | Size | RGB MAE | RGB RMSE | Changed pixels | Pixels max channel >32 | Max channel | +|---|---:|---:|---:|---:|---:|---:| +| chamber | 1280x800 | 0.6076 | 1.8655 | 42.6368% | 0.0183% | 124 | +| tunnel | 1280x800 | 0.6383 | 3.3799 | 31.7335% | 0.2772% | 130 | +| passage | 1280x800 | 1.6092 | 3.6878 | 50.4982% | 0.1733% | 52 | +| far | 1280x800 | 0.7909 | 2.1558 | 44.8325% | 0.0198% | 128 | +| passage_floor_closeup | 1280x800 | 0.9314 | 2.4800 | 43.0219% | 0.0500% | 100 | +| tunnel_bend | 1280x800 | 0.8129 | 2.1599 | 40.9629% | 0.0708% | 55 | +| remote_yaw0 | 1280x800 | 0.1994 | 2.6632 | 4.2472% | 0.2415% | 125 | +| remote_yaw90 | 1280x800 | 0.4083 | 4.3552 | 2.2432% | 0.6948% | 94 | +| remote_yaw180 | 1280x800 | 0.0484 | 1.3206 | 2.9787% | 0.0341% | 102 | +| remote_yaw270 | 1280x800 | 0.2355 | 3.1104 | 5.5593% | 0.4098% | 107 | diff --git a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl index 6cd6a3f..1a75887 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl +++ b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl @@ -289,6 +289,80 @@ FarFieldSpatialBin FarFieldFindBin(float3 position) return emptyBin; } +void NearOwnerFractalPair(float x,float y,float z,int octaves, + out float standardValue,out float ridgedValue) +{ + float standardTotal=0.0f,ridgedTotal=0.0f,maxValue=0.0f; + float frequency=1.0f,amplitude=1.0f,weight=1.0f; + [loop] + for(int octave=0;octave0.0f))return 0.0f; + const float effectiveZ=verticalScale>0.0f&&verticalScale!=1.0f?z/verticalScale:z; + const float mainX=x*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,11.3f); + const float mainY=y*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,13.7f); + const float mainZ=effectiveZ*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,17.1f); + float fineX=x*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,19.1f)+2000.0f; + float fineY=y*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,23.7f)+2500.0f; + float fineZ=effectiveZ*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,29.3f)+3000.0f; + float mx=mainX,my=mainY,mz=mainZ; + if(domainWarpStrength>0.0f) + { + const float wf=domainWarpFrequency,ws=domainWarpStrength; + float wx=VF_FieldPerlin3D(x*wf+VF_FieldVoxelSeedOffset(seed,5.2f), + y*wf+VF_FieldVoxelSeedOffset(seed,1.3f),effectiveZ*wf+VF_FieldVoxelSeedOffset(seed,9.7f)); + float wy=VF_FieldPerlin3D(x*wf+100.0f+VF_FieldVoxelSeedOffset(seed,7.7f), + y*wf+200.0f+VF_FieldVoxelSeedOffset(seed,3.1f),effectiveZ*wf+300.0f); + float wz=VF_FieldPerlin3D(x*wf+400.0f,y*wf+500.0f+VF_FieldVoxelSeedOffset(seed,11.9f), + effectiveZ*wf+600.0f+VF_FieldVoxelSeedOffset(seed,13.3f)); + wx*=1.25f*ws;wy*=1.25f*ws;wz*=1.25f*ws; + mx+=wx;my+=wy;mz+=wz; + fineX+=wx;fineY+=wy;fineZ+=wz; + } + float rough=0.0f,fine=0.0f; + if(noiseType==3u) + { + rough=VF_FieldOwnerCellular(mx,my,mz); + fine=VF_FieldOwnerCellular(fineX,fineY,fineZ); + } + else + { + float roughStandard,roughRidged,fineStandard,fineRidged; + NearOwnerFractalPair(mx,my,mz,3,roughStandard,roughRidged); + NearOwnerFractalPair(fineX,fineY,fineZ,2,fineStandard,fineRidged); + if(noiseType==1u) + { rough=roughRidged;fine=fineRidged; } + else if(noiseType==2u) + { + rough=(roughStandard+roughRidged)*0.5f; + fine=(fineStandard+fineRidged)*0.5f; + } + else + { rough=roughStandard;fine=fineStandard; } + } + return (rough+fine*0.4f)*1.25f*surfaceRoughness; +} + float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInfluence, FarFieldDetailLayer layer,uint anchorStep) { @@ -301,17 +375,17 @@ float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInflue float total; if(sampleZ1>sampleZ0) { - const float lower=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ0, + const float lower=NearOwnerRoughnessCore(position.x,position.y,sampleZ0, FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale, layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType); - const float upper=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ1, + const float upper=NearOwnerRoughnessCore(position.x,position.y,sampleZ1, FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale, layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType); const float alpha=(position.z-sampleZ0)/(sampleZ1-sampleZ0); total=lower+(upper-lower)*alpha; } else - total=VF_FieldOwnerRoughnessCore(position.x,position.y,position.z, + total=NearOwnerRoughnessCore(position.x,position.y,position.z, FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale, layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType); if(surfaceSdf<0.0f) @@ -324,9 +398,18 @@ float NearOwnerRoughness(float3 position,float surfaceSdf,float caveInfluence, float passageInfluence,FarFieldDetailLayer layer) { const float passage=saturate(passageInfluence); - const float anchored=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,8); + float anchored=0.0f,direct=0.0f; + [loop] + for(uint sampleIndex=0;sampleIndex<2;++sampleIndex) + { + if(sampleIndex==1&&passage<=0.40f)continue; + const uint anchorStep=sampleIndex==0?8u:2u; + const float value=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence, + layer,anchorStep); + if(sampleIndex==0)anchored=value; + else direct=value; + } if(passage<=0.40f)return anchored; - const float direct=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,2); if(passage>=0.60f)return direct; const float directWeight=VF_FieldSmoothUnit((passage-0.40f)/0.20f); const float anchoredWeight=1.0f-directWeight; diff --git a/Standalone/Source/StandaloneViewer.cpp b/Standalone/Source/StandaloneViewer.cpp index 04f4781..2e91cf6 100644 --- a/Standalone/Source/StandaloneViewer.cpp +++ b/Standalone/Source/StandaloneViewer.cpp @@ -1491,6 +1491,7 @@ namespace bool bGpuFarShaderPrewarm=false,bDisableGpuWarmupPresentHold=false; bool bGpuFarWarmupResourcesOnly=false; bool bGpuFarWarmupNormalPriority=false,bGpuFarWarmupBeforeWorkers=false; + bool bGpuFarWarmupKernelFences=false; bool bGpuFarWarmupOnly=false; std::string GpuWarmupEntryFilter; std::thread GpuShaderWarmupThread; @@ -1897,6 +1898,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; } bGpuShaderWarmupQueueSucceeded=VoxelForgeGpuFarViewer::QueueFarShaderWarmup( Device,Context,GpuFarShaderPath,!bGpuFarWarmupResourcesOnly,GpuWarmupEntryFilter, + bGpuFarWarmupKernelFences, GpuShaderWarmupQueueError); }); std::printf("[GpuShaderWarmup] queue_thread=started overlap=%s\n", @@ -5620,6 +5622,8 @@ int main(int Argc,char** Argv) App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupBeforeWorkers=true, App.bShapeWorld=true,App.bForceGenerate=true,App.bPresentCpuFrameBeforeGpu=true, App.bHoldCpuFrameDuringGpuWarmup=true; + else if(Arg=="--gpu-far-warmup-kernel-fences") + App.bGpuFarWarmupKernelFences=true; else if(Arg=="--gpu-far-warmup-only") App.bGpuFarEnabled=true,App.bGpuFarShaderPrewarm=true,App.bGpuFarWarmupOnly=true, App.bShapeWorld=true,App.bForceGenerate=true; @@ -5891,7 +5895,7 @@ int main(int Argc,char** Argv) const auto WarmupOnlyStart=std::chrono::steady_clock::now(); std::string WarmupError; if(!VoxelForgeGpuFarViewer::QueueFarShaderWarmup(App.Device,App.Context,App.GpuFarShaderPath, - true,App.GpuWarmupEntryFilter,WarmupError)) + true,App.GpuWarmupEntryFilter,App.bGpuFarWarmupKernelFences,WarmupError)) {std::fprintf(stderr,"[GpuShaderWarmupError] queue: %s\n",WarmupError.c_str());return 5;} if(!VoxelForgeGpuFarViewer::CompleteFarShaderWarmup(App.Device,App.Context, App.GpuFarShaderPath,WarmupError))