wip(standalone): GPU step 9 -- remote band closed by CPU fallback; CPU still wins whole view

Re-verified f2de8fe (hashes, walk 5,642/5,642, far topology 0 diffs vs CPU and
WARP, GPU survival). The remote LOD3/4 band was a direct-GPU coverage loss
(overlapping pixels identical, surface pieces missing, e.g. (6,2,0,lod3));
LOD3/4 now mesh on CPU, root cause in the GPU route still unidentified.
A minimal first-submit repro (pools, first-touch dispatch, device flags) did
not reproduce the 2.4 s stall. Whole view: CPU 1.438 s vs GPU 4.704 s median.
Crossing probe overflowed GPU output slots: no valid crossing latency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-07 04:36:34 +02:00
1 parent f2de8fe9c6
commit b0da995978
21 files changed
+698 -4

No files matched your search

+77 -4
View File
@@ -1323,6 +1323,7 @@ namespace
ID3D11Buffer* VertexBuffer=nullptr;
ID3D11Buffer* IndexBuffer=nullptr;
UINT IndexCount=0;
UINT DrawOnlyIndexStart=0,DrawOnlyIndexCount=0;
uint64 VertexCount=0;
};
struct FCrossingWork
@@ -1534,6 +1535,7 @@ namespace
bool bAutoCaptureWritten=false;
bool bFullDetailReported=false;
bool bInitialClipmapReported=false;
bool bWholeViewPresented=false;
bool bProfileAttribution=false;
bool bProfileDetailBand=false;
bool bCompareEvaluators=false;
@@ -1567,6 +1569,8 @@ namespace
int32 CaptureOnlyLod=-1;
bool bCaptureOnlyTile=false;
int32 CaptureOnlyTileX=0,CaptureOnlyTileY=0,CaptureOnlyTileZ=0,CaptureOnlyTileLod=0;
bool bDrawOnlyTile=false;
int32 DrawOnlyTileX=0,DrawOnlyTileY=0,DrawOnlyTileZ=0,DrawOnlyTileLod=0;
int32 CaptureOutputSlot=-1;
int32 Width=1280,Height=800;
float Yaw=0.0f,Pitch=0.0f,MoveSpeed=8.0f;
@@ -1683,6 +1687,12 @@ namespace
V3 Forward() const
{return Normalize({std::sin(Yaw)*std::cos(Pitch),std::cos(Yaw)*std::cos(Pitch),std::sin(Pitch)});}
bool IsDrawOnlyTile(const FVoxelTileKey& Key) const
{
return !bDrawOnlyTile||(Key.Level==DrawOnlyTileLod&&Key.Coord.X==DrawOnlyTileX
&&Key.Coord.Y==DrawOnlyTileY&&Key.Coord.Z==DrawOnlyTileZ);
}
FIntVector CameraChunk() const
{return StandaloneWorldMesher::CameraChunk(FVector(Position.X*4.0,Position.Y*4.0,Position.Z*4.0));}
@@ -2104,7 +2114,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-ColdProcessStart).count());
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
BuildTargetChunk=StandaloneWorldMesher::CameraChunk(CameraVoxels);
bFullDetailReported=false;bInitialClipmapReported=false;bAutoCaptureWritten=false;
bFullDetailReported=false;bInitialClipmapReported=false;bWholeViewPresented=false;bAutoCaptureWritten=false;
if(bProfileAttribution)
{
VoxelDensityProfile::SetMode(VoxelDensityProfile::EMode::Attribution,128);
@@ -2174,6 +2184,20 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
DesiredTileSet.clear();DesiredTileSet.reserve(DesiredTiles.size()*2+1);
for(const FVoxelTileKey& Key:DesiredTiles)DesiredTileSet.insert(Key);
if(bDrawOnlyTile)
{
const FVoxelTileKey DrawKey(FIntVector(DrawOnlyTileX,DrawOnlyTileY,DrawOnlyTileZ),
DrawOnlyTileLod);
if(!DesiredTileSet.contains(DrawKey))
{
Error="the requested draw-only tile is not in the full desired clipmap";
std::fprintf(stderr,"World load failed: %s\n",Error.c_str());
PostMessageA(Window,WM_CLOSE,0,0);
return;
}
std::printf("[DrawOnlyTile] key=(%d,%d,%d,lod%d) desired_targets=%zu filter=draw_only\n",
DrawOnlyTileX,DrawOnlyTileY,DrawOnlyTileZ,DrawOnlyTileLod,DesiredTiles.size());
}
for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0;
for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level];
const int32 StreamWorkers=bShapePipelineActive?(WorkerCount>=8?2:1):WorkerCount;
@@ -2434,10 +2458,21 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::vector<FVoxelTileKey> CpuTiles;CpuTiles.reserve(Keys.size());
std::vector<FVoxelTileKey> Eligible;Eligible.reserve(Keys.size());
uint64 SkirtTransitionFallbackTiles=0;
uint64 RemoteLod3CpuFallbackTiles=0,RemoteLod4CpuFallbackTiles=0;
for(const FVoxelTileKey& Key:Keys)
{
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
const bool NearGpuEligible=Key.Level<World->FullResClipLevels;
if(Key.Level==3||Key.Level==4)
{
CpuTiles.push_back(Key);
if(Key.Level==3)++RemoteLod3CpuFallbackTiles;
else ++RemoteLod4CpuFallbackTiles;
if(bDrawOnlyTile&&IsDrawOnlyTile(Key))
std::printf("[DrawOnlyTileRoute] key=(%d,%d,%d,lod%d) route=cpu_remote_band_fallback\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level);
continue;
}
if(NearGpuEligible||ShapeWorld->isFarFieldTileEligible(CoreKey))
{
const uint8 VisibleSkirtFaces=World->bGenerateSkirts
@@ -2464,7 +2499,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
else Eligible.push_back(Key);
}
else CpuTiles.push_back(Key);
if(bDrawOnlyTile&&IsDrawOnlyTile(Key))
std::printf("[DrawOnlyTileRoute] key=(%d,%d,%d,lod%d) route=%s\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
Eligible.empty()||Eligible.back()!=Key?"cpu_fallback":"gpu_eligible");
}
if(RemoteLod3CpuFallbackTiles+RemoteLod4CpuFallbackTiles>0)
std::printf("[GpuRemoteBandFallback] lod3_cpu_tiles=%llu lod4_cpu_tiles=%llu reason=remote_capture_pixel_coverage\n",
static_cast<unsigned long long>(RemoteLod3CpuFallbackTiles),
static_cast<unsigned long long>(RemoteLod4CpuFallbackTiles));
if(Eligible.size()>GpuFarOutputCapacitySlots)
{
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
@@ -3027,9 +3070,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{std::fprintf(stderr,"LOD %d batch exceeded D3D11 limits.\n",Key.Level);return false;}
std::vector<FGpuVertex> Vertices;Vertices.reserve(VertexCount);
std::vector<uint32> Indices;Indices.reserve(IndexCount);
UINT DrawOnlyIndexStart=0,DrawOnlyIndexCount=0;
for(const FVoxelTileKey& Tile:Members)
{
const auto It=ResidentTiles.find(Tile);if(It==ResidentTiles.end())continue;
const size_t TileIndexStart=Indices.size();
const uint32 Base=static_cast<uint32>(Vertices.size());
const FResidentTileMesh& Resident=It->second;
for(const FStandaloneCpuVertex& V:Resident.Vertices)
@@ -3047,6 +3092,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Indices.push_back(Base+Resident.Indices[Start+1]);
Indices.push_back(Base+Resident.Indices[Start+2]);
}
if(bDrawOnlyTile&&IsDrawOnlyTile(Tile))
{
DrawOnlyIndexStart=static_cast<UINT>(TileIndexStart);
DrawOnlyIndexCount=static_cast<UINT>(Indices.size()-TileIndexStart);
}
}
FStreamingGpuBatch NewBatch;
const bool bVertex=MakeBuffer(&NewBatch.VertexBuffer,Vertices.data(),Vertices.size()*sizeof(FGpuVertex),D3D11_BIND_VERTEX_BUFFER);
@@ -3058,6 +3108,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return false;
}
NewBatch.IndexCount=static_cast<UINT>(Indices.size());NewBatch.VertexCount=Vertices.size();
NewBatch.DrawOnlyIndexStart=DrawOnlyIndexStart;
NewBatch.DrawOnlyIndexCount=DrawOnlyIndexCount;
if(Existing!=StreamingBatches.end())
{
TotalVertices-=Existing->second.VertexCount;TotalIndices-=Existing->second.IndexCount;
@@ -3459,9 +3511,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const FStreamingGpuBatch& L=Pair.second;
if(!bShowLevel[Pair.first.Level]
||(CaptureOnlyLod>=0&&Pair.first.Level!=CaptureOnlyLod)
||!L.VertexBuffer||!L.IndexBuffer||!L.IndexCount)continue;
||!L.VertexBuffer||!L.IndexBuffer||!L.IndexCount
||(bDrawOnlyTile&&L.DrawOnlyIndexCount==0))continue;
const UINT Stride=sizeof(FGpuVertex),Offset=0;Context->IASetVertexBuffers(0,1,&L.VertexBuffer,&Stride,&Offset);
Context->IASetIndexBuffer(L.IndexBuffer,DXGI_FORMAT_R32_UINT,0);Context->DrawIndexed(L.IndexCount,0,0);
Context->IASetIndexBuffer(L.IndexBuffer,DXGI_FORMAT_R32_UINT,0);
if(bDrawOnlyTile)
Context->DrawIndexed(L.DrawOnlyIndexCount,L.DrawOnlyIndexStart,0);
else Context->DrawIndexed(L.IndexCount,0,0);
}
// Avoid issuing hundreds of tiny indirect draw calls while the
// initial far batches are still being built. Once the clipmap is
@@ -3474,7 +3530,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
for(auto& Pair:GpuFarTiles)
{
FGpuFarTile& Tile=Pair.second;
if(!bShowLevel[Pair.first.Level]
if(!IsDrawOnlyTile(Pair.first)||!bShowLevel[Pair.first.Level]
||(CaptureOnlyLod>=0&&Pair.first.Level!=CaptureOnlyLod)
||!Tile.VertexBuffer||!Tile.IndexBuffer||!Tile.IndirectArgs)continue;
if(!bInitialClipmapReported&&Pair.first.Level>=World->FullResClipLevels)continue;
@@ -3580,6 +3636,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const auto PresentCallStart=std::chrono::steady_clock::now();
const HRESULT PresentResult=SwapChain->Present(PresentSyncInterval,PresentFlags);
const auto PresentCallEnd=std::chrono::steady_clock::now();
if(bInitialClipmapReported&&!bWholeViewPresented)
{
bWholeViewPresented=true;
std::printf("[WholeViewPresented] at_ms=%.3f present_ms=%.3f sync=%u hr=0x%08lx\n",
std::chrono::duration<double,std::milli>(PresentCallEnd-ColdProcessStart).count(),
std::chrono::duration<double,std::milli>(PresentCallEnd-PresentCallStart).count(),
PresentSyncInterval,static_cast<unsigned long>(PresentResult));
std::fflush(stdout);
}
if(bGpuWarmupHoldReported&&!bGpuWarmupReleaseReported)
{
bGpuWarmupReleaseReported=true;
@@ -5533,6 +5598,14 @@ int main(int Argc,char** Argv)
App.bCaptureOnlyTile=true;
else {std::fprintf(stderr,"Invalid --capture-only-tile value: %s\n",Values.c_str());return 2;}
}
else if(Arg.starts_with("--draw-only-tile="))
{
const std::string Values(Arg.substr(std::string_view("--draw-only-tile=").size()));
if(std::sscanf(Values.c_str(),"%d,%d,%d,%d",&App.DrawOnlyTileX,
&App.DrawOnlyTileY,&App.DrawOnlyTileZ,&App.DrawOnlyTileLod)==4)
App.bDrawOnlyTile=true;
else {std::fprintf(stderr,"Invalid --draw-only-tile value: %s\n",Values.c_str());return 2;}
}
else if(Arg.starts_with("--capture-output-slot="))
App.CaptureOutputSlot=std::max(-1,std::atoi(
std::string(Arg.substr(std::string_view("--capture-output-slot=").size())).c_str()));