wip(standalone): GPU step 28 -- skirted near tiles on GPU (correct, but SLOWER); 16 shape workers

Luna: near skirts were CPU-only by implementation gap, not correctness. Ported with
near-resolution refinement and overflow fallback; near-probe preparation split out so
the skirt kernels no longer inline the near field (first attempt: two fxc /O3 compiles
ran 10.5 h and were stopped). Shape compile proven identical at W1/W8/W16.
-SkipCpuDetailBand retired (154/640 skirt audit failures, +174 ms planning).
Audits: hashes W1/W8/W16, walk 5,642/5,642, whole-near 960 tiles + 640 skirt faces
0 sign/topology diffs (one 0.001 cm vertex-key delta), far/thin 0 diffs, both
survivals pass with 2,074 candidates at 7.00 / 7.02 GiB (was ~6.2), ten captures pass.

My runs outside the sandbox (Step28.exe). NOTE the machine was ~25% slower than last
night for every build (step 27 CPU full machine 0.97-1.02 s at night, 1.22-1.29 s now):
  0xFF00: CPU 1.337 / 1.291 / 1.295 s; GPU miss 1.337 s;
          GPU cached 1.663 / 1.424 / 1.617 s (GPU tiles 1,427 / 2,072 / 1,222)
  0xFFFF: CPU 1.221 / 1.231 / 1.236 s (step 27 same session 1.285 / 1.222 s)
          GPU cached 1.582 / 1.550 / 1.416, 1.635 / 1.441 / 1.470 / 1.445 s
          (GPU tiles vary 593-2,074; timestamps up to ~470 ms)
=> GPU route now slower than the CPU route measured alongside it. Step 27 GPU control
   was not clean (both exes load the same precompiled .cso files).

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-08 17:07:40 +02:00
1 parent 7d1808fdf2
commit 5aa285c3ef
26 files changed
+1536 -400

No files matched your search

+214 -40
View File
@@ -1403,6 +1403,16 @@ namespace
std::chrono::steady_clock::time_point ConsumedAt{};
bool bGpuComplete=false,bTimingResolved=false;
};
struct FGpuFarPanelOutputRange
{
uint32 BaseVertex=0;
uint32 CapacityVertices=0;
};
struct FGpuFarPanelFreeRange
{
uint32 BaseVertex=0;
uint32 CapacityVertices=0;
};
enum class EGpuWarmupLaunchPolicy
{
@@ -1632,7 +1642,9 @@ namespace
uint32 GpuFarMaxTilesPerSubmissionBatch=0;
uint32 GpuFarOutputCapacitySlots=0,GpuFarNextOutputSlot=0;
uint32 GpuFarOutputPoolReservedSlots=0;
uint64 GpuFarOutputCountChecks=0,GpuFarOutputOverflowCount=0;
uint32 GpuFarPanelOutputHighWater=0,GpuFarPanelOutputReservedCapacityVertices=0;
uint64 GpuFarOutputCountChecks=0,GpuFarOutputOverflowCount=0,
GpuFarConnectorOverflowCount=0;
uint64 GpuFarOutputCountReadbackFailures=0;
uint32 GpuFarMaxRequiredVertices=0,GpuFarMaxRequiredOutputSlot=UINT32_MAX;
FVoxelTileKey GpuFarMaxRequiredVertexKey{};
@@ -1644,6 +1656,8 @@ namespace
std::unordered_map<FVoxelTileKey,std::chrono::steady_clock::time_point,FStandaloneTileKeyHash> GpuFarPreparedAt;
std::vector<FVoxelTileKey> GpuFarCandidateTiles,GpuFarBaseCpuFallbackTiles;
std::vector<FVoxelTileKey> GpuFarCpuFallbackTiles,GpuFarNeedPreparation;
std::unordered_map<FVoxelTileKey,FGpuFarPanelOutputRange,FStandaloneTileKeyHash> GpuFarPanelOutputRanges;
std::vector<FGpuFarPanelFreeRange> GpuFarPanelOutputFreeRanges;
uint64 GpuFarWarmupCpuClaimedTiles=0,GpuFarWarmupGpuTakeoverTiles=0;
double GpuFarPreparationWallMs=0.0,GpuFarPreparationWorkerMs=0.0;
uint64 GpuFarPumpCalls=0,GpuFarCompletionPolls=0;
@@ -1664,7 +1678,6 @@ namespace
bool bGpuFirstBackedPresentReported=false;
bool bProfileAttribution=false;
bool bProfileDetailBand=false;
bool bGpuFarSkipCpuDetailBand=false;
bool bCompareEvaluators=false;
bool bParityOnly=false;
bool bDensityHash=false;
@@ -1790,6 +1803,8 @@ namespace
{std::lock_guard Lock(GpuFarPreparedResultsMutex);GpuFarPreparedResults.clear();}
GpuFarCandidateTiles.clear();GpuFarBaseCpuFallbackTiles.clear();
GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
GpuFarPanelOutputRanges.clear();GpuFarPanelOutputFreeRanges.clear();
GpuFarPanelOutputHighWater=GpuFarPanelOutputReservedCapacityVertices=0;
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
bGpuFarPreparationDeferredReported=false;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
@@ -1811,6 +1826,9 @@ namespace
GpuFarNearPackedSkippedSamples=GpuFarProtectedRouteSamples=0;
GpuFarProtectedThinSamples=GpuFarProtectedSolidSamples=0;
GpuFarProtectedSealSamples=GpuFarProtectedOverlaySamples=0;
GpuFarOutputCountChecks=GpuFarOutputOverflowCount=GpuFarConnectorOverflowCount=0;
GpuFarOutputCountReadbackFailures=0;GpuFarMaxRequiredVertices=0;
GpuFarMaxRequiredOutputSlot=UINT32_MAX;GpuFarMaxRequiredVertexKey={};
GpuFarGpuDispatchMaxMs=0.0;GpuFarSubmissionSpanMs=0.0;GpuFarSubmissionSpanMaxMs=0.0;
GpuFarGpuDispatchMaxKey={};bGpuFarGpuDispatchMaxKeyValid=false;
GpuFarLocalMemoryPeakBytes=0;GpuFarLocalMemoryBudgetBytes=0;
@@ -1821,6 +1839,7 @@ namespace
GpuFarPoolReserveMs=0.0;
GpuFarSubmissionBatches=0;GpuFarMaxTilesPerSubmissionBatch=0;
GpuFarOutputPoolReservedSlots=0;
GpuFarPanelOutputReservedCapacityVertices=0;
DirtyBatchSet.clear();DesiredTiles.clear();DesiredTileSet.clear();CrossingWork.clear();
DeferredLeaverTiles.clear();
for(int32 I=0;I<=MaxLevels;++I)
@@ -2103,10 +2122,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ColdTimelineMilliseconds(PoolStart),PreReservePoolSlots);
std::string PoolError;
if(VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
static_cast<uint32>(PreReservePoolSlots),static_cast<uint32>(CoarseTileCells),PoolError))
static_cast<uint32>(PreReservePoolSlots),static_cast<uint32>(CoarseTileCells),PoolError,0))
{
GpuFarOutputCapacitySlots=static_cast<uint32>(PreReservePoolSlots);
GpuFarOutputPoolReservedSlots=GpuFarOutputCapacitySlots;
GpuFarPanelOutputReservedCapacityVertices=0;
const double PoolMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-PoolStart).count();
GpuFarPoolReserveMs+=PoolMs;
@@ -2490,7 +2510,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void PopulateGpuVisualDetailBandPlan(VoxelForgeCore::ShapeBuildRequest& Request,
const std::vector<FVoxelTileKey>& TileKeys) const
{
if(!bGpuFarEnabled||(!bProfileDetailBand&&!bGpuFarSkipCpuDetailBand))return;
if(!bGpuFarEnabled||!bProfileDetailBand)return;
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> TileSet;
TileSet.reserve(TileKeys.size()*2+1);
for(const FVoxelTileKey& Key:TileKeys)TileSet.insert(Key);
@@ -2504,11 +2524,10 @@ 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);
const uint8 VisibleSkirts=World->bGenerateSkirts
?ComputeVisibleSkirtFaceMask(Key,TileSet):0;
if(VisibleSkirts==0)Request.gpuVisualDetailTiles.push_back(Region);
if(VisibleSkirts==0||bGpuSkirtEmitEnabled)
Request.gpuVisualDetailTiles.push_back(Region);
else Request.cpuVisualDetailTiles.push_back(Region);
}
Request.skipCpuDetailBandForGpuVisuals=bGpuFarSkipCpuDetailBand
&&!Request.gpuVisualDetailTiles.empty();
}
bool BuildShapeWorldForCamera(std::string& Error,bool bIncludeFarWorld=false,
@@ -2590,7 +2609,8 @@ 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<World->FullResClipLevels)
{
if(!World->bGenerateSkirts||ComputeVisibleSkirtFaceMask(Key,PoolDesiredSet)==0)
if(!World->bGenerateSkirts||bGpuSkirtEmitEnabled
||ComputeVisibleSkirtFaceMask(Key,PoolDesiredSet)==0)
++GpuFarPreReservePoolSlots;
continue;
}
@@ -3074,10 +3094,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
BatchOutputKeys.push_back(BatchKey);
}
std::vector<uint32> RequiredVertexCounts;
std::vector<uint32> ConnectorOverflowFlags;
std::string CountReadbackError;
const bool bOutputCountsRead=
VoxelForgeGpuFarViewer::ReadFarTileRequiredVertexCounts(Device,Context,
GpuFarShaderPath,OutputSlots,RequiredVertexCounts,CountReadbackError);
VoxelForgeGpuFarViewer::ReadFarTileOutputStatuses(Device,Context,
GpuFarShaderPath,OutputSlots,RequiredVertexCounts,
ConnectorOverflowFlags,CountReadbackError);
std::vector<FVoxelTileKey> OverflowFallbackKeys;
if(!bOutputCountsRead)
{
@@ -3112,6 +3134,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
CheckedTile.OutputSlot,Required,
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile,CurrentBatchIndex);
}
if(OutputIndex<ConnectorOverflowFlags.size()
&&ConnectorOverflowFlags[OutputIndex]!=0)
{
++GpuFarConnectorOverflowCount;
if(std::find(OverflowFallbackKeys.begin(),OverflowFallbackKeys.end(),BatchKey)
==OverflowFallbackKeys.end())OverflowFallbackKeys.push_back(BatchKey);
std::fprintf(stderr,"[GpuFarPanelOverflow] key=(%d,%d,%d,lod%d) output_slot=%u batch=%u action=cpu_fallback\n",
BatchKey.Coord.X,BatchKey.Coord.Y,BatchKey.Coord.Z,BatchKey.Level,
CheckedTile.OutputSlot,CurrentBatchIndex);
}
}
}
for(const FVoxelTileKey& FallbackKey:OverflowFallbackKeys)
@@ -3120,6 +3152,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarPreparedInputs.erase(FallbackKey);
GpuFarPreparationErrors.erase(FallbackKey);
GpuFarPreparedAt.erase(FallbackKey);
ReleaseGpuFarPanelOutputRange(FallbackKey);
auto FallbackTileIt=GpuFarTiles.find(FallbackKey);
if(FallbackTileIt==GpuFarTiles.end())continue;
const uint32 ReusableSlot=FallbackTileIt->second.OutputSlot;
@@ -3202,12 +3235,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
&&Context->GetData(Tile.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK)
{
std::vector<uint32> RequiredVertexCounts;
std::vector<uint32> ConnectorOverflowFlags;
std::string CountReadbackError;
const bool bOutputCountRead=VoxelForgeGpuFarViewer::ReadFarTileRequiredVertexCounts(
const bool bOutputCountRead=VoxelForgeGpuFarViewer::ReadFarTileOutputStatuses(
Device,Context,GpuFarShaderPath,{Tile.OutputSlot},RequiredVertexCounts,
CountReadbackError);
ConnectorOverflowFlags,CountReadbackError);
const bool bOverflow=!bOutputCountRead||RequiredVertexCounts.empty()
||RequiredVertexCounts[0]>VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile;
||RequiredVertexCounts[0]>VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile
||ConnectorOverflowFlags.empty()||ConnectorOverflowFlags[0]!=0;
if(!bOutputCountRead)
{
++GpuFarOutputCountReadbackFailures;
@@ -3231,12 +3266,19 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Tile.OutputSlot,Required,
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile);
}
if(!ConnectorOverflowFlags.empty()&&ConnectorOverflowFlags[0]!=0)
{
++GpuFarConnectorOverflowCount;
std::fprintf(stderr,"[GpuFarPanelOverflow] key=(%d,%d,%d,lod%d) output_slot=%u action=cpu_fallback\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Tile.OutputSlot);
}
}
if(bOverflow)
{
const uint32 ReusableSlot=Tile.OutputSlot;
GpuFarEligibleTiles.erase(Key);GpuFarPreparedInputs.erase(Key);
GpuFarPreparationErrors.erase(Key);GpuFarPreparedAt.erase(Key);
ReleaseGpuFarPanelOutputRange(Key);
ReleaseGpuFarTile(Tile);GpuFarTiles.erase(Key);
GpuFarFreeOutputSlots.push_back(ReusableSlot);
AddGpuFarCpuFallbackTiles({Key});
@@ -3305,15 +3347,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Result.Input=Key.Level<World->FullResClipLevels
?ShapeWorld->exportGpuFieldTile(CoreKey,Result.Error)
:ShapeWorld->exportFarFieldTile(CoreKey,Result.Error);
if(Result.Input&&Key.Level>=World->FullResClipLevels)
if(Result.Input&&VisibleSkirtFaceMask!=0)
{
Result.Input->visibleSkirtFaceMask=VisibleSkirtFaceMask;
// The CPU far mesher uses extent / CoarseTileCells, which can
// differ from Key.StepVoxels() when coarse tiles use a reduced
// cell count. Reuse the exported mesher step so skirt depth
// stays identical to the CPU reference.
Result.Input->skirtDepthVoxels=std::max(1.0f,
static_cast<float>(World->SkirtCells))*Result.Input->step;
}
if(Result.Input&&Key.Level>=World->FullResClipLevels)
{
if(!bGpuConnectorRepairEnabled)
{
Result.Input->connectorPanels.clear();
@@ -3382,6 +3423,104 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Tile.ConnectorVertices);
}
void CoalesceGpuFarPanelOutputFreeRanges()
{
std::sort(GpuFarPanelOutputFreeRanges.begin(),GpuFarPanelOutputFreeRanges.end(),
[](const FGpuFarPanelFreeRange& A,const FGpuFarPanelFreeRange& B)
{return A.BaseVertex<B.BaseVertex;});
std::vector<FGpuFarPanelFreeRange> Merged;
Merged.reserve(GpuFarPanelOutputFreeRanges.size());
for(const FGpuFarPanelFreeRange& Range:GpuFarPanelOutputFreeRanges)
{
if(Range.CapacityVertices==0)continue;
if(!Merged.empty()&&static_cast<uint64>(Merged.back().BaseVertex)
+Merged.back().CapacityVertices==Range.BaseVertex)
Merged.back().CapacityVertices+=Range.CapacityVertices;
else Merged.push_back(Range);
}
GpuFarPanelOutputFreeRanges=std::move(Merged);
while(!GpuFarPanelOutputFreeRanges.empty())
{
const FGpuFarPanelFreeRange& Last=GpuFarPanelOutputFreeRanges.back();
if(static_cast<uint64>(Last.BaseVertex)+Last.CapacityVertices
!=GpuFarPanelOutputHighWater)break;
GpuFarPanelOutputHighWater=Last.BaseVertex;
GpuFarPanelOutputFreeRanges.pop_back();
}
}
void ReleaseGpuFarPanelOutputRange(const FVoxelTileKey& Key)
{
const auto It=GpuFarPanelOutputRanges.find(Key);
if(It==GpuFarPanelOutputRanges.end())return;
if(It->second.CapacityVertices!=0)
GpuFarPanelOutputFreeRanges.push_back({It->second.BaseVertex,It->second.CapacityVertices});
GpuFarPanelOutputRanges.erase(It);
CoalesceGpuFarPanelOutputFreeRanges();
}
bool AllocateGpuFarPanelOutputRange(std::uint32_t CapacityVertices,
FGpuFarPanelOutputRange& Out)
{
if(CapacityVertices==0){Out={};return true;}
for(auto It=GpuFarPanelOutputFreeRanges.begin();It!=GpuFarPanelOutputFreeRanges.end();++It)
{
if(It->CapacityVertices<CapacityVertices)continue;
Out={It->BaseVertex,CapacityVertices};
It->BaseVertex+=CapacityVertices;
It->CapacityVertices-=CapacityVertices;
if(It->CapacityVertices==0)GpuFarPanelOutputFreeRanges.erase(It);
return true;
}
if(static_cast<uint64>(GpuFarPanelOutputHighWater)+CapacityVertices
>std::numeric_limits<uint32>::max())return false;
Out={GpuFarPanelOutputHighWater,CapacityVertices};
GpuFarPanelOutputHighWater+=CapacityVertices;
return true;
}
bool ReserveGpuFarPanelOutputPool(std::string& Error)
{
for(auto It=GpuFarPanelOutputRanges.begin();It!=GpuFarPanelOutputRanges.end();)
{
if(GpuFarEligibleTiles.contains(It->first)){++It;continue;}
const FVoxelTileKey Key=It->first;++It;
ReleaseGpuFarPanelOutputRange(Key);
}
for(const FVoxelTileKey& Key:GpuFarQueue)
{
const auto InputIt=GpuFarPreparedInputs.find(Key);
if(InputIt==GpuFarPreparedInputs.end())
{Error="prepared input is missing while reserving its panel output";return false;}
const VoxelForgeCore::FarFieldTileInput& Input=InputIt->second;
const uint64 RequiredCapacity64=static_cast<uint64>(Input.connectorVertexCount)
+(Input.visibleSkirtFaceMask!=0
?VoxelForgeGpuFarViewer::NearSkirtOutputVertexCapacityPerTile:0u);
if(RequiredCapacity64>std::numeric_limits<uint32>::max())
{Error="per-tile panel output capacity exceeds the 32-bit vertex limit";return false;}
const uint32 RequiredCapacity=static_cast<uint32>(RequiredCapacity64);
auto RangeIt=GpuFarPanelOutputRanges.find(Key);
if(RangeIt!=GpuFarPanelOutputRanges.end()
&&RangeIt->second.CapacityVertices>=RequiredCapacity)continue;
if(RangeIt!=GpuFarPanelOutputRanges.end())ReleaseGpuFarPanelOutputRange(Key);
FGpuFarPanelOutputRange Range{};
if(!AllocateGpuFarPanelOutputRange(RequiredCapacity,Range))
{Error="panel output pool exceeds the 32-bit vertex limit";return false;}
GpuFarPanelOutputRanges.emplace(Key,Range);
}
if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),Error,
GpuFarPanelOutputHighWater))return false;
GpuFarOutputPoolReservedSlots=std::max(GpuFarOutputPoolReservedSlots,
GpuFarOutputCapacitySlots);
GpuFarPanelOutputReservedCapacityVertices=std::max(
GpuFarPanelOutputReservedCapacityVertices,GpuFarPanelOutputHighWater);
std::printf("[GpuPanelOutputPool] queued_tiles=%zu live_ranges=%zu capacity_vertices=%u high_water=%u status=reserved\n",
GpuFarQueue.size(),GpuFarPanelOutputRanges.size(),
GpuFarPanelOutputReservedCapacityVertices,GpuFarPanelOutputHighWater);
return true;
}
void ScheduleGpuFarTiles(const std::vector<FVoxelTileKey>& Keys)
{
if(GpuFarPreparationState)
@@ -3405,7 +3544,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return;
std::vector<FVoxelTileKey> CpuTiles;CpuTiles.reserve(Keys.size());
std::vector<FVoxelTileKey> Eligible;Eligible.reserve(Keys.size());
uint64 SkirtTransitionFallbackTiles=0;
uint64 NearGpuSkirtTiles=0,SkirtTransitionFallbackTiles=0;
for(const FVoxelTileKey& Key:Keys)
{
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
@@ -3426,14 +3565,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarFreeOutputSlots.push_back(ReusedSlot);
}
}
if(VisibleSkirtFaces!=0&&NearGpuEligible)
if(VisibleSkirtFaces!=0&&NearGpuEligible&&!bGpuSkirtEmitEnabled)
{
// Keep fine tiles on the CPU reference path. Far tiles carry the
// CPU-decided visible face mask to the deterministic GPU skirt pass.
// Preserve the CPU route when GPU skirt emission is explicitly disabled.
CpuTiles.push_back(Key);
++SkirtTransitionFallbackTiles;
}
else Eligible.push_back(Key);
else
{
if(VisibleSkirtFaces!=0&&NearGpuEligible)++NearGpuSkirtTiles;
Eligible.push_back(Key);
}
}
else CpuTiles.push_back(Key);
if(bDrawOnlyTile&&IsDrawOnlyTile(Key))
@@ -3443,6 +3585,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
const std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> CurrentEligible(
Eligible.begin(),Eligible.end());
for(auto It=GpuFarPanelOutputRanges.begin();It!=GpuFarPanelOutputRanges.end();)
{
if(CurrentEligible.contains(It->first)){++It;continue;}
const FVoxelTileKey Key=It->first;++It;
ReleaseGpuFarPanelOutputRange(Key);
}
for(auto It=GpuFarTiles.begin();It!=GpuFarTiles.end();)
{
if(CurrentEligible.contains(It->first)){++It;continue;}
@@ -3493,7 +3641,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::printf("[GpuFieldEligibility] candidate_gpu_tiles=%zu near_gpu_tiles=%zu cpu_fallback_tiles=%zu warmup_cpu_fallback=%s\n",
GpuFarCandidateTiles.size(),NearGpuTiles,GpuFarCpuFallbackTiles.size(),
bGpuWarmupRoutePending?"yes":"no");
std::printf("[GpuSkirtSeamFallback] near_tiles=%llu reason=fine_visible_lod_skirts gpu_far_skirts=active\n",
std::printf("[GpuSkirtSeamRouting] near_skirt_gpu_tiles=%llu near_cpu_fallback_tiles=%llu\n",
static_cast<unsigned long long>(NearGpuSkirtTiles),
static_cast<unsigned long long>(SkirtTransitionFallbackTiles));
}
@@ -3536,11 +3685,12 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ColdTimelineMilliseconds(ReserveStart),GpuFarOutputCapacitySlots);
std::string PoolError;
if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),PoolError))
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),PoolError,0))
{
std::fprintf(stderr,"[GpuFarPoolError] %s\n",PoolError.c_str());
bGpuFarEnabled=false;bGpuFarPreparationStarted=true;
GpuFarOutputCapacitySlots=0;GpuFarOutputPoolReservedSlots=0;
GpuFarPanelOutputReservedCapacityVertices=0;
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
GpuFarTiles.clear();GpuFarCandidateTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();
GpuFarNeedPreparation.clear();GpuFarEligibleTiles.clear();
@@ -3606,8 +3756,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());
for(const FVoxelTileKey& Key:NeedPreparation)
{
const uint8 VisibleSkirtFaceMask=Key.Level>=World->FullResClipLevels
&&World->bGenerateSkirts&&bGpuSkirtEmitEnabled
const uint8 VisibleSkirtFaceMask=World->bGenerateSkirts&&bGpuSkirtEmitEnabled
?ComputeVisibleSkirtFaceMask(Key,DesiredTileSet):0;
Tasks.emplace_back([this,Key,VisibleSkirtFaceMask,Batch]()
{PrepareGpuFarInputTile(Key,VisibleSkirtFaceMask,Batch);});
@@ -3819,6 +3968,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
if(!GpuFarQueue.empty())SortGpuFarQueue();
AddGpuFarCpuFallbackTiles(Failed);
std::string PanelPoolError;
if(!ReserveGpuFarPanelOutputPool(PanelPoolError))
{
std::fprintf(stderr,"[GpuFarPoolError] panel-output reservation failed: %s\n",
PanelPoolError.c_str());
bGpuFarEnabled=false;GpuFarQueue.clear();GpuFarQueued.clear();
GpuFarEligibleTiles.clear();
AddGpuFarCpuFallbackTiles(DesiredTiles);
bGpuFarInputsFinalized=true;
return false;
}
bGpuFarInputsFinalized=true;
const std::size_t NearGpuTiles=static_cast<std::size_t>(std::count_if(
GpuFarEligibleTiles.begin(),GpuFarEligibleTiles.end(),[&](const FVoxelTileKey& Key)
@@ -3868,7 +4028,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
StartGpuFarPreparation();
if(!CompleteGpuShaderWarmupAndRoute())return;
StartGpuFarPreparation();
if(!bGpuFarEnabled||!FinalizeGpuFarPreparation()||GpuFarQueue.empty())return;
if(!bGpuFarEnabled||!FinalizeGpuFarPreparation()||!bGpuFarInputsFinalized
||GpuFarQueue.empty())return;
const bool bColdDrainBeforeWholeView=!bInitialClipmapReported&&!bGpuFarAuditEnabled
&&!bDisableGpuColdStartDrain&&bGpuFarWarmupBeforeWorkers;
constexpr std::size_t StartupFirstGpuBatchTarget=32;
@@ -3954,6 +4115,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Tile.ConnectorRings=PreparedInput->connectorRingCount;
Tile.ConnectorVertices=PreparedInput->connectorVertexCount;
Tile.VisibleSkirtFaceMask=PreparedSkirtFaceMask;
const auto PanelRangeIt=GpuFarPanelOutputRanges.find(Key);
if(PanelRangeIt==GpuFarPanelOutputRanges.end())
{
GpuFarFreeOutputSlots.push_back(Tile.OutputSlot);
std::fprintf(stderr,"[GpuFarPoolError] panel-output range is missing for key=(%d,%d,%d,lod%d)\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level);
GpuFarPanelOutputRanges.erase(Key);
bGpuFarEnabled=false;
break;
}
const FGpuFarPanelOutputRange PanelRange=PanelRangeIt->second;
if(!VoxelForgeGpuFarViewer::BuildFarTile(Device,Context,*ShapeWorld,CoreKey,
PreparedInput,
GpuFarShaderPath,&Tile.VertexBuffer,&Tile.IndexBuffer,&Tile.IndirectArgs,
@@ -3962,9 +4134,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bDeferredBatch?nullptr:&Tile.CompletionEvent,
&Tile.TimingQueries,
((bGpuFarAuditEnabled&&Key.Level>=6)||bAuditDrawOnlyGpuTile)?&AuditMesh:nullptr,
&EnqueueMs,&GpuDispatchMs,Error))
&EnqueueMs,&GpuDispatchMs,Error,true,
PanelRange.BaseVertex,PanelRange.CapacityVertices))
{
GpuFarFreeOutputSlots.push_back(Tile.OutputSlot);
ReleaseGpuFarPanelOutputRange(Key);
std::fprintf(stderr,"[GpuFarTileError] key=(%d,%d,%d,lod%d): %s\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Error.c_str());
if(PreparedInputIt!=GpuFarPreparedInputs.end())GpuFarPreparedInputs.erase(PreparedInputIt);
@@ -4707,7 +4881,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::string PageError;
size_t CompiledPageCount=0;
if(!ShapeWorld->compileTileRegion(NewCoreTiles,
static_cast<uint32>(std::clamp(WorkerCount,1,8)),CoreScheduler,PageError,
static_cast<uint32>(std::clamp(WorkerCount,1,16)),CoreScheduler,PageError,
&CompiledPageCount,&PageStats,true))
{
std::fprintf(stderr,"[ShapeRegionPage] compile failed at center=(%d,%d,%d): %s\n",
@@ -4729,7 +4903,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
NewDesired.CenterChunk.X,NewDesired.CenterChunk.Y,NewDesired.CenterChunk.Z,
NewCoreTiles.size(),static_cast<unsigned long long>(PageCoveredTiles),
PageStats.regionsBuilt,static_cast<unsigned long long>(PageStats.latticeNodes),
std::clamp(WorkerCount,1,8),PageStats.shapeSeconds,PageStats.intervalSeconds,
std::clamp(WorkerCount,1,16),PageStats.shapeSeconds,PageStats.intervalSeconds,
PageStats.surfaceSeconds,PageStats.workerSeconds,PageSeconds,
ResidentTiles.size()-std::min(ResidentTiles.size(),Leavers.size()),Leavers.size());
}
@@ -5268,9 +5442,10 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
ReportTileTimings();
if(GpuFarOutputCountChecks>0)
std::printf("[GpuFarSlotCapacitySummary] checked_tiles=%llu overflow_tiles=%llu count_readback_failures=%llu capacity_vertices=%u max_required_vertices=%u max_key=(%d,%d,%d,lod%d) max_slot=%u check_path=normal_after_completion_event\n",
std::printf("[GpuFarSlotCapacitySummary] checked_tiles=%llu surface_overflow_tiles=%llu panel_overflow_tiles=%llu count_readback_failures=%llu capacity_vertices=%u max_required_vertices=%u max_key=(%d,%d,%d,lod%d) max_slot=%u check_path=normal_after_completion_event\n",
static_cast<unsigned long long>(GpuFarOutputCountChecks),
static_cast<unsigned long long>(GpuFarOutputOverflowCount),
static_cast<unsigned long long>(GpuFarConnectorOverflowCount),
static_cast<unsigned long long>(GpuFarOutputCountReadbackFailures),
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile,
GpuFarMaxRequiredVertices,GpuFarMaxRequiredVertexKey.Coord.X,
@@ -6292,7 +6467,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::atomic<size_t> Next{0};
std::vector<std::thread> Threads;
const int32 ThreadCount=std::clamp(Workers,1,8);
const int32 ThreadCount=std::clamp(Workers,1,16);
Threads.reserve(ThreadCount);
for(int32 Worker=0;Worker<ThreadCount;++Worker)
{
@@ -6691,7 +6866,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
ShapeRequest.maxVoxel={FineMax.X,FineMax.Y,FineMax.Z};
ShapeRequest.clipMinVoxel={ClipMin.X,ClipMin.Y,ClipMin.Z};
ShapeRequest.clipMaxVoxel={ClipMax.X,ClipMax.Y,ClipMax.Z};
ShapeRequest.workerLimit=static_cast<uint32>(std::clamp(WorkerCount,1,8));
ShapeRequest.workerLimit=static_cast<uint32>(std::clamp(WorkerCount,1,16));
ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld;
ShapeRequest.roundTunnels=bRoundTunnels;
ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls;
@@ -7377,7 +7552,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
request.maxVoxel={fineMax.X,fineMax.Y,fineMax.Z};
request.clipMinVoxel={clipMin.X,clipMin.Y,clipMin.Z};
request.clipMaxVoxel={clipMax.X,clipMax.Y,clipMax.Z};
request.workerLimit=static_cast<uint32>(std::clamp(workers,1,8));
request.workerLimit=static_cast<uint32>(std::clamp(workers,1,16));
request.detail=detail;request.roundTunnels=roundTunnels;
request.enhancedTunnelWalls=enhancedWalls;request.includeFarWorld=false;
return true;
@@ -7409,7 +7584,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
std::string error;
const std::array<VoxelForgeCore::TileKey,1> pageTiles={target};
std::size_t pageTilesA=0;
if(!base.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,8)),
if(!base.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,16)),
scheduler,error,&pageTilesA,nullptr,false))
{std::fprintf(stderr,"[TilePathHashAudit] path-A page compile failed: %s\n",error.c_str());return 11;}
auto pathA=base.generateTile(target,options,error);
@@ -7459,9 +7634,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
{std::fprintf(stderr,"[TilePathHashDirections] setup failed: %s\n",error.c_str());return 11;}
std::size_t analyticForwardCovered=0,analyticReverseCovered=0;
VoxelForgeCore::TileRegionCompileStats forwardStats,reverseStats;
if(!analyticForward.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,8)),
if(!analyticForward.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,16)),
scheduler,error,&analyticForwardCovered,&forwardStats,true)
||!analyticReverse.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,8)),
||!analyticReverse.compileTileRegion(pageTiles,static_cast<uint32>(std::clamp(workers,1,16)),
scheduler,error,&analyticReverseCovered,&reverseStats,true))
{std::fprintf(stderr,"[TilePathHashDirections] region compile failed: %s\n",error.c_str());return 11;}
auto analyticPathForward=analyticForward.generateTile(target,options,error);
@@ -7570,7 +7745,6 @@ int main(int Argc,char** Argv)
else if(Arg=="--gpu-connectors-disabled")App.bGpuConnectorRepairEnabled=false;
else if(Arg=="--profile-attribution")App.bProfileAttribution=true;
else if(Arg=="--profile-detail-band")bProfileDetailBand=true;
else if(Arg=="--gpu-far-skip-cpu-detail-band")App.bGpuFarSkipCpuDetailBand=true;
else if(Arg=="--compare-evaluators")App.bCompareEvaluators=true;
else if(Arg=="--parity-only")App.bParityOnly=true;
else if(Arg=="--density-hash")App.bDensityHash=true;
@@ -7685,7 +7859,7 @@ int main(int Argc,char** Argv)
else if(Arg.starts_with("--workers="))
{
const int32 Parsed=std::atoi(std::string(Arg.substr(10)).c_str());
App.WorkerCount=std::clamp(Parsed,1,8);
App.WorkerCount=std::clamp(Parsed,1,16);
}
else if(Arg.starts_with("--clip-radius="))
App.ClipRadius=std::clamp(std::atoi(std::string(Arg.substr(14)).c_str()),1,64);
@@ -1783,7 +1783,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
}
if(MaximumProbeDensity>0.25f)
{
if(RefinementDepth>=1)continue;
if(RefinementDepth>=1)
{
continue;
}
const auto MakeMidpoint=[&](const FSkirtVertex& X,
const FSkirtVertex& Y)
{
@@ -1842,9 +1845,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
}
MeshData.NumSkirtTriangles += (Tris.Num() - BaseTriNum) / 3;
};
EmitSkirts(GroundTris,GroundSkirtFaceMasks);
EmitSkirts(CapTris,CapSkirtFaceMasks);
}
EmitSkirts(GroundTris,GroundSkirtFaceMasks);
EmitSkirts(CapTris,CapSkirtFaceMasks);
}
// F17 — concatène sol PUIS sky-cap : un run d'indices contigu par polygroup (voir
// FVoxelMeshData::NumCeilingTriangles). Mêmes triangles qu'avant, seul l'ordre change.