wip(standalone): GPU step 19 -- streamed GPU input export; RUNNERS.md
GPU tile inputs are published as workers finish (LOD then camera distance); first
batch at 32 ready, later batches up to 256. Export wall 224 -> ~105 ms. A camera-near
partial shape compile was tried and removed (duplicated work, missing inputs).
My runs outside the sandbox (Step19Stream.exe, marker removed first, interleaved):
CPU 1.380 / 1.332 / 1.302 s
GPU 1.358 (marker miss: 0 GPU tiles) / 1.409 / 1.263 / 1.246 s (marker match,
~1,400 GPU tiles, warm-up queue 5-6 ms)
Cached timeline (run d): shape compile 70-652 ms, idle gap 698-850 ms, export
850-954 ms, GPU tail to 1,246 ms (235 ms GPU timestamps). Run b: one 15 ms tile,
ready 1.409 s. Target <= 1.0 s not met.
Audits: hashes w1/w8, walk 5,642/5,642, near 960 / far 1,114 0 diffs vs CPU and
WARP, thin 353, survival pass, ten captures as step 18, peak 6.17 GB.
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
2fea2710f7
commit
89fd9a0021
8 files changed
+273
-66
No files matched your search
@@ -34,6 +34,7 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
@@ -1448,6 +1449,15 @@ namespace
|
||||
|
||||
struct FApp
|
||||
{
|
||||
struct FPreparedGpuFarInputResult
|
||||
{
|
||||
FVoxelTileKey Key;
|
||||
std::optional<VoxelForgeCore::FarFieldTileInput> Input;
|
||||
std::string Error;
|
||||
std::chrono::steady_clock::time_point FinishedAt{};
|
||||
uint64 FinishedFrame=0;
|
||||
};
|
||||
|
||||
HWND Window=nullptr;
|
||||
ID3D11Device* Device=nullptr;
|
||||
ID3D11DeviceContext* Context=nullptr;
|
||||
@@ -1527,8 +1537,11 @@ namespace
|
||||
std::string GpuWarmupEntryFilter;
|
||||
std::thread GpuShaderWarmupThread;
|
||||
std::thread GpuFarPreparationThread;
|
||||
std::mutex GpuFarPreparedResultsMutex;
|
||||
std::deque<FPreparedGpuFarInputResult> GpuFarPreparedResults;
|
||||
std::atomic<bool> bGpuFarPreparationFinished{false};
|
||||
bool bGpuFarPreparationStarted=false,bGpuFarInputsFinalized=false;
|
||||
bool bGpuFarFirstPreparedInputReported=false;
|
||||
bool bGpuWarmupCompleted=false,bGpuFarTimelineEnabled=false;
|
||||
bool bGpuWarmupRoutePending=false;
|
||||
bool bGpuShaderWarmupQueueSucceeded=false;
|
||||
@@ -1719,6 +1732,7 @@ namespace
|
||||
GpuFarBatches.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0;
|
||||
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;
|
||||
@@ -1726,6 +1740,7 @@ namespace
|
||||
bGpuWarmupRoutePending=false;
|
||||
GpuFarWarmupCpuClaimedTiles=0;GpuFarWarmupGpuTakeoverTiles=0;
|
||||
GpuFarPreparationWallMs=0.0;GpuFarPreparationWorkerMs=0.0;
|
||||
bGpuFarFirstPreparedInputReported=false;
|
||||
GpuFarPumpCalls=0;GpuFarCompletionPolls=0;
|
||||
GpuFarFirstPumpMs=GpuFarLastPumpMs=GpuFarFirstSubmitMs=GpuFarLastSubmitMs=-1.0;
|
||||
GpuFarFirstCompletionPollMs=GpuFarLastCompletionPollMs=-1.0;
|
||||
@@ -2229,8 +2244,6 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("Standalone max clip level override applied after world reload: max_level=%d.\n",
|
||||
MaxClipLevelOverride);
|
||||
}
|
||||
// Compile one world field before streaming. The streamer already prioritizes the
|
||||
// player's neighborhood, so a second near-only field would only duplicate work.
|
||||
const auto ShapeSetupStart=std::chrono::steady_clock::now();
|
||||
if(bShapeWorld&&!BuildShapeWorldForCamera(Error,true,false,true))return false;
|
||||
std::printf("[ConfigureProfile] stage=shape_world_request duration_ms=%.3f\n",
|
||||
@@ -2777,12 +2790,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const auto PrepareStart=std::chrono::steady_clock::now();
|
||||
std::printf("[ColdTimeline] stage=gpu_input_export begin_ms=%.3f tiles=%zu\n",
|
||||
ColdTimelineMilliseconds(PrepareStart),NeedPreparation.size());
|
||||
std::vector<std::optional<VoxelForgeCore::FarFieldTileInput>> Inputs(NeedPreparation.size());
|
||||
std::vector<std::string> Errors(NeedPreparation.size());
|
||||
std::vector<std::chrono::steady_clock::time_point> FinishedAt(NeedPreparation.size());
|
||||
std::vector<uint64> FinishedFrame(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)
|
||||
@@ -2796,33 +2806,39 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
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
|
||||
{
|
||||
Inputs[Index]=Key.Level<World->FullResClipLevels
|
||||
?ShapeWorld->exportGpuFieldTile(CoreKey,Errors[Index])
|
||||
:ShapeWorld->exportFarFieldTile(CoreKey,Errors[Index]);
|
||||
if(Inputs[Index]&&Key.Level>=World->FullResClipLevels)
|
||||
Result.Input=Key.Level<World->FullResClipLevels
|
||||
?ShapeWorld->exportGpuFieldTile(CoreKey,Result.Error)
|
||||
:ShapeWorld->exportFarFieldTile(CoreKey,Result.Error);
|
||||
if(Result.Input&&Key.Level>=World->FullResClipLevels)
|
||||
{
|
||||
Inputs[Index]->visibleSkirtFaceMask=World->bGenerateSkirts
|
||||
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.
|
||||
Inputs[Index]->skirtDepthVoxels=std::max(1.0f,
|
||||
static_cast<float>(World->SkirtCells))*Inputs[Index]->step;
|
||||
Result.Input->skirtDepthVoxels=std::max(1.0f,
|
||||
static_cast<float>(World->SkirtCells))*Result.Input->step;
|
||||
if(!bGpuConnectorRepairEnabled)
|
||||
{
|
||||
Inputs[Index]->connectorPanels.clear();
|
||||
Inputs[Index]->connectorRingCount=0;
|
||||
Inputs[Index]->connectorVertexCount=0;
|
||||
Result.Input->connectorPanels.clear();
|
||||
Result.Input->connectorRingCount=0;
|
||||
Result.Input->connectorVertexCount=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(const std::exception& Error) { Errors[Index]=Error.what(); }
|
||||
FinishedAt[Index]=std::chrono::steady_clock::now();
|
||||
FinishedFrame[Index]=ColdTimelineFrame();
|
||||
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));
|
||||
}
|
||||
}
|
||||
WorkerElapsed[Worker]=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-WorkerStart).count();
|
||||
@@ -2833,29 +2849,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::chrono::steady_clock::now()-PrepareStart).count();
|
||||
double WorkerSumMs=0.0;for(const double Elapsed:WorkerElapsed)WorkerSumMs+=Elapsed;
|
||||
GpuFarPreparationWallMs+=PreparationMs;GpuFarPreparationWorkerMs+=WorkerSumMs;
|
||||
std::size_t Failed=0;
|
||||
for(std::size_t Index=0;Index<NeedPreparation.size();++Index)
|
||||
{
|
||||
if(Inputs[Index])
|
||||
{
|
||||
GpuFarPreparedInputs.emplace(NeedPreparation[Index],std::move(*Inputs[Index]));
|
||||
GpuFarPreparedAt.emplace(NeedPreparation[Index],FinishedAt[Index]);
|
||||
const FVoxelTileKey& Key=NeedPreparation[Index];
|
||||
if(bGpuFarTimelineEnabled)
|
||||
std::printf("[GpuTileTimeline] event=prepared key=(%d,%d,%d,lod%d) at_ms=%.3f frame=%llu\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||
ColdTimelineMilliseconds(FinishedAt[Index]),
|
||||
static_cast<unsigned long long>(FinishedFrame[Index]));
|
||||
}
|
||||
else
|
||||
{
|
||||
++Failed;
|
||||
GpuFarPreparationErrors.emplace(NeedPreparation[Index],Errors[Index].empty()
|
||||
?"far-field input export failed":Errors[Index]);
|
||||
}
|
||||
}
|
||||
std::printf("[GpuFarInputPrepare] tiles=%zu workers=%zu wall_ms=%.3f worker_sum_ms=%.3f failed=%zu\n",
|
||||
NeedPreparation.size(),ThreadCount,PreparationMs,WorkerSumMs,Failed);
|
||||
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);
|
||||
@@ -2866,9 +2862,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
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;
|
||||
bGpuFarFirstPreparedInputReported=false;
|
||||
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
|
||||
bGpuWarmupRoutePending=false;
|
||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return;
|
||||
@@ -3026,40 +3024,58 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
return;
|
||||
}
|
||||
bGpuFarPreparationFinished.store(false,std::memory_order_relaxed);
|
||||
const std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
|
||||
std::vector<FVoxelTileKey> NeedPreparation=GpuFarNeedPreparation;
|
||||
std::stable_sort(NeedPreparation.begin(),NeedPreparation.end(),
|
||||
[&](const FVoxelTileKey& A,const FVoxelTileKey& B){return GpuFarKeyLess(A,B);});
|
||||
GpuFarPreparationThread=std::thread([this,NeedPreparation]()
|
||||
{
|
||||
try { PrepareGpuFarInputs(NeedPreparation); }
|
||||
catch(const std::exception& Error)
|
||||
{
|
||||
std::lock_guard Lock(GpuFarPreparedResultsMutex);
|
||||
for(const FVoxelTileKey& Key:NeedPreparation)
|
||||
GpuFarPreparationErrors.emplace(Key,Error.what());
|
||||
{
|
||||
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)
|
||||
GpuFarPreparationErrors.emplace(Key,"unknown input preparation failure");
|
||||
{
|
||||
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");
|
||||
}
|
||||
bGpuFarPreparationFinished.store(true,std::memory_order_release);
|
||||
});
|
||||
}
|
||||
|
||||
bool GpuFarKeyLess(const FVoxelTileKey& A,const FVoxelTileKey& B)const
|
||||
{
|
||||
if(A.Level!=B.Level)return A.Level<B.Level;
|
||||
const FIntVector AO=A.OriginVoxels(),BO=B.OriginVoxels();
|
||||
const double AC=(AO.X+0.5*A.ExtentVoxels()-Position.X*4.0)*(AO.X+0.5*A.ExtentVoxels()-Position.X*4.0)
|
||||
+(AO.Y+0.5*A.ExtentVoxels()-Position.Y*4.0)*(AO.Y+0.5*A.ExtentVoxels()-Position.Y*4.0)
|
||||
+(AO.Z+0.5*A.ExtentVoxels()-Position.Z*4.0)*(AO.Z+0.5*A.ExtentVoxels()-Position.Z*4.0);
|
||||
const double BC=(BO.X+0.5*B.ExtentVoxels()-Position.X*4.0)*(BO.X+0.5*B.ExtentVoxels()-Position.X*4.0)
|
||||
+(BO.Y+0.5*B.ExtentVoxels()-Position.Y*4.0)*(BO.Y+0.5*B.ExtentVoxels()-Position.Y*4.0)
|
||||
+(BO.Z+0.5*B.ExtentVoxels()-Position.Z*4.0)*(BO.Z+0.5*B.ExtentVoxels()-Position.Z*4.0);
|
||||
return AC<BC;
|
||||
}
|
||||
|
||||
void SortGpuFarQueue()
|
||||
{
|
||||
std::sort(GpuFarQueue.begin(),GpuFarQueue.end(),[&](const FVoxelTileKey& A,const FVoxelTileKey& B)
|
||||
{
|
||||
if(A.Level!=B.Level)return A.Level<B.Level;
|
||||
const FIntVector AO=A.OriginVoxels(),BO=B.OriginVoxels();
|
||||
const double AC=(AO.X+0.5*A.ExtentVoxels()-Position.X*4.0)*(AO.X+0.5*A.ExtentVoxels()-Position.X*4.0)
|
||||
+(AO.Y+0.5*A.ExtentVoxels()-Position.Y*4.0)*(AO.Y+0.5*A.ExtentVoxels()-Position.Y*4.0)
|
||||
+(AO.Z+0.5*A.ExtentVoxels()-Position.Z*4.0)*(AO.Z+0.5*A.ExtentVoxels()-Position.Z*4.0);
|
||||
const double BC=(BO.X+0.5*B.ExtentVoxels()-Position.X*4.0)*(BO.X+0.5*B.ExtentVoxels()-Position.X*4.0)
|
||||
+(BO.Y+0.5*B.ExtentVoxels()-Position.Y*4.0)*(BO.Y+0.5*B.ExtentVoxels()-Position.Y*4.0)
|
||||
+(BO.Z+0.5*B.ExtentVoxels()-Position.Z*4.0)*(BO.Z+0.5*B.ExtentVoxels()-Position.Z*4.0);
|
||||
return AC<BC;
|
||||
});
|
||||
std::sort(GpuFarQueue.begin(),GpuFarQueue.end(),
|
||||
[&](const FVoxelTileKey& A,const FVoxelTileKey& B){return GpuFarKeyLess(A,B);});
|
||||
GpuFarQueued.clear();for(const FVoxelTileKey& Key:GpuFarQueue)GpuFarQueued.insert(Key);
|
||||
}
|
||||
|
||||
@@ -3132,8 +3148,61 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
bool FinalizeGpuFarPreparation()
|
||||
{
|
||||
if(bGpuFarInputsFinalized)return true;
|
||||
if(!bGpuFarPreparationStarted
|
||||
||!bGpuFarPreparationFinished.load(std::memory_order_acquire))return false;
|
||||
if(!bGpuFarPreparationStarted)return false;
|
||||
std::deque<FPreparedGpuFarInputResult> ReadyResults;
|
||||
{
|
||||
std::lock_guard Lock(GpuFarPreparedResultsMutex);
|
||||
ReadyResults.swap(GpuFarPreparedResults);
|
||||
}
|
||||
std::vector<FVoxelTileKey> Failed;
|
||||
bool bQueueChanged=false;
|
||||
for(FPreparedGpuFarInputResult& Result:ReadyResults)
|
||||
{
|
||||
const FVoxelTileKey& Key=Result.Key;
|
||||
if(Result.Input)
|
||||
{
|
||||
GpuFarPreparedInputs.emplace(Key,std::move(*Result.Input));
|
||||
GpuFarPreparedAt.emplace(Key,Result.FinishedAt);
|
||||
GpuFarEligibleTiles.insert(Key);
|
||||
if(!GpuFarTiles.contains(Key)&&GpuFarQueued.insert(Key).second)
|
||||
{
|
||||
GpuFarQueue.push_back(Key);
|
||||
bQueueChanged=true;
|
||||
}
|
||||
if(bGpuFarTimelineEnabled)
|
||||
std::printf("[GpuTileTimeline] event=prepared key=(%d,%d,%d,lod%d) at_ms=%.3f frame=%llu\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||
ColdTimelineMilliseconds(Result.FinishedAt),
|
||||
static_cast<unsigned long long>(Result.FinishedFrame));
|
||||
if(!bGpuFarFirstPreparedInputReported)
|
||||
{
|
||||
bGpuFarFirstPreparedInputReported=true;
|
||||
std::printf("[ColdTimeline] event=gpu_first_input_ready key=(%d,%d,%d,lod%d) at_ms=%.3f\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||
ColdTimelineMilliseconds(Result.FinishedAt));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GpuFarPreparationErrors.emplace(Key,Result.Error.empty()
|
||||
?"far-field input export failed":std::move(Result.Error));
|
||||
Failed.push_back(Key);
|
||||
}
|
||||
}
|
||||
if(bQueueChanged)SortGpuFarQueue();
|
||||
if(!Failed.empty())
|
||||
{
|
||||
for(const FVoxelTileKey& Key:Failed)
|
||||
{
|
||||
const auto Error=GpuFarPreparationErrors.find(Key);
|
||||
std::fprintf(stderr,"[GpuFieldInputFallback] key=(%d,%d,%d,lod%d) reason=%s\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||
Error==GpuFarPreparationErrors.end()?"far-field input export failed":Error->second.c_str());
|
||||
}
|
||||
AddGpuFarCpuFallbackTiles(Failed);
|
||||
}
|
||||
if(!bGpuFarPreparationFinished.load(std::memory_order_acquire))
|
||||
return !GpuFarQueue.empty();
|
||||
if(GpuFarPreparationThread.joinable())GpuFarPreparationThread.join();
|
||||
if(!bGpuFarEnabled)
|
||||
{
|
||||
@@ -3145,11 +3214,11 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
bGpuFarInputsFinalized=true;
|
||||
return false;
|
||||
}
|
||||
std::vector<FVoxelTileKey> Failed;
|
||||
for(const FVoxelTileKey& Key:GpuFarCandidateTiles)
|
||||
{
|
||||
if(GpuFarTiles.contains(Key))
|
||||
{GpuFarEligibleTiles.insert(Key);continue;}
|
||||
if(GpuFarEligibleTiles.contains(Key)||GpuFarPreparationErrors.contains(Key))continue;
|
||||
if(!GpuFarPreparedInputs.contains(Key))
|
||||
{
|
||||
Failed.push_back(Key);
|
||||
@@ -3160,9 +3229,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
continue;
|
||||
}
|
||||
GpuFarEligibleTiles.insert(Key);
|
||||
GpuFarQueue.push_back(Key);
|
||||
if(GpuFarQueued.insert(Key).second)GpuFarQueue.push_back(Key);
|
||||
}
|
||||
SortGpuFarQueue();
|
||||
if(!GpuFarQueue.empty())SortGpuFarQueue();
|
||||
AddGpuFarCpuFallbackTiles(Failed);
|
||||
bGpuFarInputsFinalized=true;
|
||||
const std::size_t NearGpuTiles=static_cast<std::size_t>(std::count_if(
|
||||
@@ -3201,7 +3270,23 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
StartGpuFarPreparation();
|
||||
if(!bGpuFarEnabled||!FinalizeGpuFarPreparation()||GpuFarQueue.empty())return;
|
||||
const bool bColdDrainBeforeWholeView=!bInitialClipmapReported&&!bGpuFarAuditEnabled
|
||||
&&!bDisableGpuColdStartDrain;
|
||||
&&!bDisableGpuColdStartDrain&&bGpuFarWarmupBeforeWorkers;
|
||||
constexpr std::size_t StartupFirstGpuBatchTarget=32;
|
||||
constexpr std::size_t StartupGpuBatchTarget=256;
|
||||
constexpr double StartupGpuPumpBudgetMilliseconds=8.0;
|
||||
const bool bWaitForStreamingGpuBatch=!bColdDrainBeforeWholeView
|
||||
&&!bGpuFarAuditEnabled&&!bInitialClipmapReported;
|
||||
const std::size_t GpuPumpTileLimit=bWaitForStreamingGpuBatch
|
||||
?(GpuFarSubmissionBatches==0?StartupFirstGpuBatchTarget:StartupGpuBatchTarget):32;
|
||||
const double GpuPumpBudgetMilliseconds=bWaitForStreamingGpuBatch
|
||||
?StartupGpuPumpBudgetMilliseconds:4.0;
|
||||
if(bWaitForStreamingGpuBatch
|
||||
&&!bGpuFarPreparationFinished.load(std::memory_order_acquire)
|
||||
&&GpuFarQueue.size()<StartupFirstGpuBatchTarget)return;
|
||||
if(bWaitForStreamingGpuBatch&&GpuFarSubmissionBatches==0)
|
||||
std::printf("[GpuFarStreamingPolicy] first_batch_tiles=%zu start_threshold=%zu later_batch_limit=%zu pump_budget_ms=%.1f order=lod_then_camera_distance\n",
|
||||
GpuFarQueue.size(),StartupFirstGpuBatchTarget,StartupGpuBatchTarget,
|
||||
StartupGpuPumpBudgetMilliseconds);
|
||||
const auto PumpStart=std::chrono::steady_clock::now();
|
||||
const double PumpMs=std::chrono::duration<double,std::milli>(PumpStart-ColdProcessStart).count();
|
||||
if(GpuFarFirstPumpMs<0.0)GpuFarFirstPumpMs=PumpMs;
|
||||
@@ -3225,7 +3310,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
int32 Built=0;
|
||||
std::vector<FVoxelTileKey> BuiltKeys;
|
||||
BuiltKeys.reserve(bColdDrainBeforeWholeView?GpuFarQueue.size():32);
|
||||
BuiltKeys.reserve(bColdDrainBeforeWholeView?GpuFarQueue.size():GpuPumpTileLimit);
|
||||
do
|
||||
{
|
||||
const FVoxelTileKey Key=GpuFarQueue.front();GpuFarQueue.pop_front();GpuFarQueued.erase(Key);
|
||||
@@ -3721,8 +3806,9 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
}
|
||||
while(!GpuFarQueue.empty()&&(bColdDrainBeforeWholeView
|
||||
||(Built<32&&std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-Start).count()<4.0)));
|
||||
||(Built<GpuPumpTileLimit&&std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-Start).count()
|
||||
<GpuPumpBudgetMilliseconds)));
|
||||
if(Built>0)
|
||||
{
|
||||
if(bDeferredBatch)
|
||||
|
||||
Reference in new issue
Block a user