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:
1 parent
7d1808fdf2
commit
5aa285c3ef
26 files changed
+1536
-400
No files matched your search
@@ -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.
|
||||
|
||||
Reference in new issue
Block a user