wip(standalone): GPU step 21 -- GPU input export shares the 8 workers (3:1 priority); no wall gain

Export tasks interleave with CPU tile jobs on the existing pool. Slow-dispatch keys
are logged (>5 ms, with -Timeline).
My runs outside the sandbox (Step21.exe, marker removed first, interleaved, GPU with
-Timeline):
  CPU 1.356 / 1.321 / 1.277 s
  GPU 1.453 (marker miss, 0 GPU tiles) / 1.249 / 1.216 / 1.217 s (match)
Export now starts at 686 ms in run c (was ~850), but the whole view is unchanged
(1.216 s): from first input (721 ms) to ready (1,216 ms) the GPU path spends ~500 ms
wall for ~255 ms of GPU timestamps. In runs b/d the output pool itself began late
(~860-875 ms). Slow tiles: (0,-6,-5,lod1) 28.5 ms, (-4,-15,-3,lod0) 11.3 ms,
(-3,-13,-2,lod0) 8.2 ms, all ordinary near-detail tiles.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival 6.13 GiB, ten captures as step 18.

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 23:20:42 +02:00
1 parent 2cb3344e1b
commit 38f6009200
5 files changed
+308 -119

No files matched your search

+165 -114
View File
@@ -1371,7 +1371,11 @@ namespace
VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{};
uint64 VertexCapacityBytes=0;
uint32 OutputSlot=0,IndirectArgsByteOffset=0,BatchIndex=UINT32_MAX;
int32 InputCells=0,InputStep=0;
uint64 ProtectedSamples=0,ProtectedThinSamples=0,ProtectedSolidSamples=0;
uint32 NearProxySamples=0,ConnectorRings=0,ConnectorVertices=0;
uint8 VisibleSkirtFaceMask=0;
bool bNearDetailInput=false;
bool bSkirtDrawArgsAudited=false;
double GpuDispatchMs=0.0;
std::chrono::steady_clock::time_point PreparedAt{};
@@ -1457,6 +1461,19 @@ namespace
std::chrono::steady_clock::time_point FinishedAt{};
uint64 FinishedFrame=0;
};
struct FGpuFarPreparationBatch
{
std::once_flag StartOnce;
std::chrono::steady_clock::time_point DispatchAt{};
std::chrono::steady_clock::time_point PrepareStart{};
std::atomic<std::size_t> Remaining{0};
std::atomic<std::size_t> Failed{0};
std::atomic<uint64> WorkerNanoseconds{0};
std::atomic<bool> bCancelled{false};
uint64 Generation=0;
std::size_t TileCount=0;
std::size_t SharedWorkerCount=0;
};
HWND Window=nullptr;
ID3D11Device* Device=nullptr;
@@ -1536,9 +1553,10 @@ namespace
bool bGpuFarWarmupOnly=false;
std::string GpuWarmupEntryFilter;
std::thread GpuShaderWarmupThread;
std::thread GpuFarPreparationThread;
std::shared_ptr<FGpuFarPreparationBatch> GpuFarPreparationState;
std::mutex GpuFarPreparedResultsMutex;
std::deque<FPreparedGpuFarInputResult> GpuFarPreparedResults;
std::atomic<uint64> GpuFarPreparationGeneration{0};
std::atomic<bool> bGpuFarPreparationFinished{false};
bool bGpuFarPreparationStarted=false,bGpuFarInputsFinalized=false;
bool bGpuFarFirstPreparedInputReported=false;
@@ -1661,7 +1679,6 @@ namespace
double BatchUploadBudgetMilliseconds=2.0;
~FApp(){
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
if(GpuShaderWarmupThread.joinable())GpuShaderWarmupThread.join();
StopGeneration(true);ReleaseMeshes();
if(SolidRasterizer)SolidRasterizer->Release();if(WireRasterizer)WireRasterizer->Release();
@@ -1734,6 +1751,7 @@ namespace
GpuFarBatches.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0;
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();GpuFarPreparedAt.clear();
GpuFarPreparationState.reset();
{std::lock_guard Lock(GpuFarPreparedResultsMutex);GpuFarPreparedResults.clear();}
GpuFarCandidateTiles.clear();GpuFarBaseCpuFallbackTiles.clear();
GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
@@ -1774,7 +1792,7 @@ namespace
(void)bCancel;
if(Streamer)Streamer->Stop();
Streamer.reset();
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
GpuFarPreparationState.reset();
bGpuFarPreparationStarted=false;
if(FullShapeBuildThread.joinable())FullShapeBuildThread.join();
PendingFullShapeWorld.reset();
@@ -2598,7 +2616,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
void StartFarShapeGeneration()
{
if(!bFarShapeStagePending||!ShapeWorld||ShapeWorld->hasFarWorld()||!Streamer)return;
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
ShapeWorld->buildFarWorld();
bFarShapeStagePending=false;
Streamer->Stop();
@@ -2745,6 +2762,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
Batch.TimestampFrequency,TileDispatchMs))
{
Tile.GpuDispatchMs=TileDispatchMs;Tile.bTimingResolved=true;
ReportGpuFarSlowDispatchTile(Key,Tile,TileDispatchMs);
GpuFarGpuDispatchMs+=TileDispatchMs;
if(TileDispatchMs>GpuFarGpuDispatchMaxMs)
{
@@ -2775,6 +2793,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
if(Tile.GpuDispatchMs==0.0)
{
Tile.GpuDispatchMs=AsyncDispatchMs;
ReportGpuFarSlowDispatchTile(Key,Tile,AsyncDispatchMs);
GpuFarGpuDispatchMs+=AsyncDispatchMs;
if(AsyncDispatchMs>GpuFarGpuDispatchMaxMs)
{
@@ -2795,91 +2814,122 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
return ResidentTiles.contains(Key);
}
void PrepareGpuFarInputs(std::vector<FVoxelTileKey> NeedPreparation,
std::chrono::steady_clock::time_point DispatchAt)
void PrepareGpuFarInputTile(const FVoxelTileKey& Key,uint8 VisibleSkirtFaceMask,
const std::shared_ptr<FGpuFarPreparationBatch>& Batch)
{
if(NeedPreparation.empty())return;
const auto PrepareStart=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_thread_started at_ms=%.3f dispatch_to_start_ms=%.3f frame=%llu\n",
ColdTimelineMilliseconds(PrepareStart),
std::chrono::duration<double,std::milli>(PrepareStart-DispatchAt).count(),
static_cast<unsigned long long>(ColdTimelineFrame()));
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu\n",
ColdTimelineMilliseconds(PrepareStart),NeedPreparation.size());
const std::size_t ThreadCount=std::min<std::size_t>(8,NeedPreparation.size());
std::atomic<std::size_t> Next{0};
std::atomic<std::size_t> Failed{0};
std::vector<double> WorkerElapsed(ThreadCount,0.0);
std::vector<std::thread> Workers;Workers.reserve(ThreadCount);
for(std::size_t Worker=0;Worker<ThreadCount;++Worker)
if(Batch->bCancelled.load(std::memory_order_acquire))return;
std::call_once(Batch->StartOnce,[&]()
{
Workers.emplace_back([&,Worker]
Batch->PrepareStart=std::chrono::steady_clock::now();
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_workers_started at_ms=%.3f dispatch_to_start_ms=%.3f frame=%llu shared_workers=%zu\n",
ColdTimelineMilliseconds(Batch->PrepareStart),
std::chrono::duration<double,std::milli>(
Batch->PrepareStart-Batch->DispatchAt).count(),
static_cast<unsigned long long>(ColdTimelineFrame()),
Batch->SharedWorkerCount);
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu shared_workers=%zu\n",
ColdTimelineMilliseconds(Batch->PrepareStart),Batch->TileCount,
Batch->SharedWorkerCount);
});
const auto WorkerStart=std::chrono::steady_clock::now();
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
FPreparedGpuFarInputResult Result;Result.Key=Key;
try
{
Result.Input=Key.Level<World->FullResClipLevels
?ShapeWorld->exportGpuFieldTile(CoreKey,Result.Error)
:ShapeWorld->exportFarFieldTile(CoreKey,Result.Error);
if(Result.Input&&Key.Level>=World->FullResClipLevels)
{
const auto WorkerStart=std::chrono::steady_clock::now();
for(;;)
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(!bGpuConnectorRepairEnabled)
{
const std::size_t Index=Next.fetch_add(1,std::memory_order_relaxed);
if(Index>=NeedPreparation.size())break;
const FVoxelTileKey& Key=NeedPreparation[Index];
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
FPreparedGpuFarInputResult Result;Result.Key=Key;
try
{
Result.Input=Key.Level<World->FullResClipLevels
?ShapeWorld->exportGpuFieldTile(CoreKey,Result.Error)
:ShapeWorld->exportFarFieldTile(CoreKey,Result.Error);
if(Result.Input&&Key.Level>=World->FullResClipLevels)
{
Result.Input->visibleSkirtFaceMask=World->bGenerateSkirts
&&bGpuSkirtEmitEnabled
?ComputeVisibleSkirtFaceMask(Key,DesiredTileSet):0;
// 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(!bGpuConnectorRepairEnabled)
{
Result.Input->connectorPanels.clear();
Result.Input->connectorRingCount=0;
Result.Input->connectorVertexCount=0;
}
}
}
catch(const std::exception& Error) { Result.Error=Error.what(); }
Result.FinishedAt=std::chrono::steady_clock::now();
Result.FinishedFrame=ColdTimelineFrame();
if(!Result.Input)Failed.fetch_add(1,std::memory_order_relaxed);
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
GpuFarPreparedResults.push_back(std::move(Result));
}
Result.Input->connectorPanels.clear();
Result.Input->connectorRingCount=0;
Result.Input->connectorVertexCount=0;
}
WorkerElapsed[Worker]=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-WorkerStart).count();
});
}
}
for(std::thread& Worker:Workers)Worker.join();
const double PreparationMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-PrepareStart).count();
double WorkerSumMs=0.0;for(const double Elapsed:WorkerElapsed)WorkerSumMs+=Elapsed;
GpuFarPreparationWallMs+=PreparationMs;GpuFarPreparationWorkerMs+=WorkerSumMs;
std::printf("[GpuFarInputPrepare] tiles=%zu workers=%zu wall_ms=%.3f worker_sum_ms=%.3f failed=%zu\n",
NeedPreparation.size(),ThreadCount,PreparationMs,WorkerSumMs,
Failed.load(std::memory_order_relaxed));
std::printf("[ColdTimeline] stage=gpu_input_export end_ms=%.3f duration_ms=%.3f worker_sum_ms=%.3f\n",
ColdTimelineMilliseconds(std::chrono::steady_clock::now()),
PreparationMs,WorkerSumMs);
std::fflush(stdout);
catch(const std::exception& Error) { Result.Error=Error.what(); }
catch(...) { Result.Error="unknown input preparation failure"; }
if(Batch->bCancelled.load(std::memory_order_acquire))
{
Batch->Remaining.fetch_sub(1,std::memory_order_relaxed);
return;
}
Result.FinishedAt=std::chrono::steady_clock::now();
Result.FinishedFrame=ColdTimelineFrame();
if(!Result.Input)Batch->Failed.fetch_add(1,std::memory_order_relaxed);
const uint64 WorkerNanoseconds=static_cast<uint64>(std::chrono::duration_cast<
std::chrono::nanoseconds>(Result.FinishedAt-WorkerStart).count());
Batch->WorkerNanoseconds.fetch_add(WorkerNanoseconds,std::memory_order_relaxed);
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
if(Batch->bCancelled.load(std::memory_order_acquire)
||Batch->Generation!=GpuFarPreparationGeneration.load(std::memory_order_acquire))
{
Batch->Remaining.fetch_sub(1,std::memory_order_relaxed);
return;
}
GpuFarPreparedResults.push_back(std::move(Result));
}
if(Batch->Remaining.fetch_sub(1,std::memory_order_acq_rel)==1)
{
const auto PrepareEnd=std::chrono::steady_clock::now();
const double PreparationMs=std::chrono::duration<double,std::milli>(
PrepareEnd-Batch->PrepareStart).count();
const double WorkerSumMs=static_cast<double>(
Batch->WorkerNanoseconds.load(std::memory_order_relaxed))/1.0e6;
GpuFarPreparationWallMs+=PreparationMs;
GpuFarPreparationWorkerMs+=WorkerSumMs;
std::printf("[GpuFarInputPrepare] tiles=%zu workers=%zu mode=shared_priority_queue wall_ms=%.3f worker_sum_ms=%.3f failed=%zu\n",
Batch->TileCount,Batch->SharedWorkerCount,PreparationMs,WorkerSumMs,
Batch->Failed.load(std::memory_order_relaxed));
std::printf("[ColdTimeline] stage=gpu_input_export end_ms=%.3f duration_ms=%.3f worker_sum_ms=%.3f\n",
ColdTimelineMilliseconds(PrepareEnd),PreparationMs,WorkerSumMs);
std::fflush(stdout);
std::lock_guard Lock(GpuFarPreparedResultsMutex);
if(!Batch->bCancelled.load(std::memory_order_acquire)
&&Batch->Generation==GpuFarPreparationGeneration.load(std::memory_order_acquire))
bGpuFarPreparationFinished.store(true,std::memory_order_release);
}
}
void ReportGpuFarSlowDispatchTile(const FVoxelTileKey& Key,const FGpuFarTile& Tile,
double DispatchMs)const
{
if(!bGpuFarTimelineEnabled||DispatchMs<5.0)return;
std::printf("[GpuFarSlowDispatchTile] key=(%d,%d,%d,lod%d) dispatch_ms=%.6f cells=%d step=%d near_detail=%d protected=%llu thin=%llu solid=%llu near_proxy_samples=%u skirt_faces=0x%02x connector_rings=%u connector_vertices=%u\n",
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,DispatchMs,Tile.InputCells,
Tile.InputStep,Tile.bNearDetailInput?1:0,
static_cast<unsigned long long>(Tile.ProtectedSamples),
static_cast<unsigned long long>(Tile.ProtectedThinSamples),
static_cast<unsigned long long>(Tile.ProtectedSolidSamples),Tile.NearProxySamples,
static_cast<unsigned>(Tile.VisibleSkirtFaceMask),Tile.ConnectorRings,
Tile.ConnectorVertices);
}
void ScheduleGpuFarTiles(const std::vector<FVoxelTileKey>& Keys)
{
if(GpuFarPreparationState)
GpuFarPreparationState->bCancelled.store(true,std::memory_order_release);
if(Streamer)Streamer->ClearPriorityTasks();
GpuFarPreparationState.reset();
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
GpuFarPreparationGeneration.fetch_add(1,std::memory_order_acq_rel);
GpuFarPreparedResults.clear();
}
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();GpuFarPreparedAt.clear();
{std::lock_guard Lock(GpuFarPreparedResultsMutex);GpuFarPreparedResults.clear();}
GpuFarCandidateTiles.clear();GpuFarBaseCpuFallbackTiles.clear();
GpuFarCpuFallbackTiles.clear();GpuFarNeedPreparation.clear();
bGpuFarPreparationStarted=false;bGpuFarInputsFinalized=false;
@@ -3044,7 +3094,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
bGpuFarPreparationFinished.store(true,std::memory_order_relaxed);
return;
}
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
const auto PreparationListStart=std::chrono::steady_clock::now();
std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
std::stable_sort(NeedPreparation.begin(),NeedPreparation.end(),
@@ -3052,45 +3101,38 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
const double PreparationListMs=std::chrono::duration<double,std::milli>(
std::chrono::steady_clock::now()-PreparationListStart).count();
const auto DispatchAt=std::chrono::steady_clock::now();
auto Batch=std::make_shared<FGpuFarPreparationBatch>();
Batch->DispatchAt=DispatchAt;Batch->TileCount=NeedPreparation.size();
Batch->SharedWorkerCount=Streamer?static_cast<std::size_t>(Streamer->GetWorkerCount()):0;
Batch->Generation=GpuFarPreparationGeneration.load(std::memory_order_acquire);
Batch->Remaining.store(NeedPreparation.size(),std::memory_order_relaxed);
GpuFarPreparationState=Batch;
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
if(bGpuFarTimelineEnabled)
std::printf("[ColdTimeline] event=gpu_input_export_thread_dispatch at_ms=%.3f after_pool_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu\n",
std::printf("[ColdTimeline] event=gpu_input_export_priority_queue at_ms=%.3f after_pool_ms=%.3f memory_sample_ms=%.3f list_copy_sort_ms=%.3f tiles=%zu shared_workers=%zu\n",
ColdTimelineMilliseconds(DispatchAt),
std::chrono::duration<double,std::milli>(DispatchAt-PoolEnd).count(),
MemorySampleMs,PreparationListMs,NeedPreparation.size());
GpuFarPreparationThread=std::thread([this,NeedPreparation,DispatchAt]()
MemorySampleMs,PreparationListMs,NeedPreparation.size(),Batch->SharedWorkerCount);
constexpr std::size_t GpuInputPriorityBurst=3;
std::printf("[GpuFarSchedulingPolicy] workers=%zu gpu_input_priority_burst=%zu cpu_mesh_jobs_per_burst=1 mode=shared_worker_pool\n",
Batch->SharedWorkerCount,GpuInputPriorityBurst);
std::vector<std::function<void()>> Tasks;Tasks.reserve(NeedPreparation.size());
for(const FVoxelTileKey& Key:NeedPreparation)
{
const bool bPrioritySet=SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_NORMAL)!=0;
if(bGpuFarTimelineEnabled)
std::printf("[GpuFarPreparationThread] priority=%s process=BelowNormal thread_relative=Normal\n",
bPrioritySet?"Normal":"set_failed");
try { PrepareGpuFarInputs(NeedPreparation,DispatchAt); }
catch(const std::exception& Error)
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
for(const FVoxelTileKey& Key:NeedPreparation)
{
FPreparedGpuFarInputResult Result;Result.Key=Key;Result.Error=Error.what();
Result.FinishedAt=std::chrono::steady_clock::now();
Result.FinishedFrame=ColdTimelineFrame();
GpuFarPreparedResults.push_back(std::move(Result));
}
std::fprintf(stderr,"[GpuFarInputPrepareError] %s\n",Error.what());
}
catch(...)
{
std::lock_guard Lock(GpuFarPreparedResultsMutex);
for(const FVoxelTileKey& Key:NeedPreparation)
{
FPreparedGpuFarInputResult Result;Result.Key=Key;
Result.Error="unknown input preparation failure";
Result.FinishedAt=std::chrono::steady_clock::now();
Result.FinishedFrame=ColdTimelineFrame();
GpuFarPreparedResults.push_back(std::move(Result));
}
std::fprintf(stderr,"[GpuFarInputPrepareError] unknown failure\n");
}
const uint8 VisibleSkirtFaceMask=Key.Level>=World->FullResClipLevels
&&World->bGenerateSkirts&&bGpuSkirtEmitEnabled
?ComputeVisibleSkirtFaceMask(Key,DesiredTileSet):0;
Tasks.emplace_back([this,Key,VisibleSkirtFaceMask,Batch]()
{PrepareGpuFarInputTile(Key,VisibleSkirtFaceMask,Batch);});
}
if(!Streamer||!Streamer->QueuePriorityTasks(std::move(Tasks)))
{
std::fprintf(stderr,"[GpuFarInputPrepareError] shared worker queue is unavailable\n");
GpuFarPreparationState.reset();bGpuFarEnabled=false;
bGpuFarInputsFinalized=true;
bGpuFarPreparationFinished.store(true,std::memory_order_release);
});
AddGpuFarCpuFallbackTiles(DesiredTiles);
}
}
bool GpuFarKeyLess(const FVoxelTileKey& A,const FVoxelTileKey& B)const
@@ -3237,7 +3279,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
if(!bGpuFarPreparationFinished.load(std::memory_order_acquire))
return !GpuFarQueue.empty();
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
if(!bGpuFarEnabled)
{
GpuFarCandidateTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();
@@ -3378,6 +3419,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
break;
}
const uint8 PreparedSkirtFaceMask=PreparedInput->visibleSkirtFaceMask;
Tile.InputCells=PreparedInput->cells;Tile.InputStep=PreparedInput->step;
Tile.bNearDetailInput=PreparedInput->nearDetailEnabled;
Tile.ProtectedSamples=PreparedInput->protectedSampleCount;
Tile.ProtectedThinSamples=PreparedInput->protectedThinSampleCount;
Tile.ProtectedSolidSamples=PreparedInput->protectedSolidSampleCount;
Tile.NearProxySamples=static_cast<uint32>(std::min<std::size_t>(
PreparedInput->nearPackedProxy.size(),std::numeric_limits<uint32>::max()));
Tile.ConnectorRings=PreparedInput->connectorRingCount;
Tile.ConnectorVertices=PreparedInput->connectorVertexCount;
Tile.VisibleSkirtFaceMask=PreparedSkirtFaceMask;
if(!VoxelForgeGpuFarViewer::BuildFarTile(Device,Context,*ShapeWorld,CoreKey,
PreparedInput,
GpuFarShaderPath,&Tile.VertexBuffer,&Tile.IndexBuffer,&Tile.IndirectArgs,
@@ -3809,7 +3860,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
}
Tile.VertexCapacityBytes=static_cast<uint64>(
VoxelForgeGpuFarViewer::PooledOutputVertexCapacityPerTile)*32u;
Tile.VisibleSkirtFaceMask=PreparedSkirtFaceMask;
const auto PreparedAtIt=GpuFarPreparedAt.find(Key);
if(PreparedAtIt!=GpuFarPreparedAt.end())Tile.PreparedAt=PreparedAtIt->second;
Tile.SubmittedAt=std::chrono::steady_clock::now();
@@ -3817,6 +3867,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
GpuFarEnqueueWallSeconds+=EnqueueMs/1000.0;
if(GpuDispatchMs>0.0)
{
ReportGpuFarSlowDispatchTile(Key,Tile,GpuDispatchMs);
GpuFarGpuDispatchMs+=GpuDispatchMs;
if(GpuDispatchMs>GpuFarGpuDispatchMaxMs)
{