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:
1 parent
2cb3344e1b
commit
38f6009200
5 files changed
+308
-119
No files matched your search
@@ -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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user