wip(standalone): GPU step 4 -- batched far generation, connector panels on GPU; far survival passes
Up to 32 tiles per deferred command list, one polled event per batch, no per-tile waits; CPU upload/enqueue 723 -> 46 ms. GPU far meshes pass far survival (7,200 strata boundaries, 10,976 connectors, 0 missing/holes). But hybrid cold 4.36 s vs CPU 2.29 s: whole-tile GPU timestamps sum to 646 ms (0.58 ms/tile; step 3's 0.03 ms covered part of the work only), divergent per-thread shape loops; CPU far-input export 1.6 worker-s. CPU 44/44 hashes unchanged. 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
782f195bdb
commit
247682a8e0
18 files changed
+1519
-191
No files matched your search
@@ -33,6 +33,7 @@
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@@ -1352,10 +1353,24 @@ namespace
|
||||
ID3D11Buffer* VertexBuffer=nullptr;
|
||||
ID3D11Buffer* IndexBuffer=nullptr;
|
||||
ID3D11Buffer* IndirectArgs=nullptr;
|
||||
ID3D11Buffer* ConnectorVertexBuffer=nullptr;
|
||||
ID3D11Buffer* ConnectorIndexBuffer=nullptr;
|
||||
ID3D11Query* CompletionEvent=nullptr;
|
||||
VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{};
|
||||
uint64 VertexCapacityBytes=0;
|
||||
uint32 OutputSlot=0,IndirectArgsByteOffset=0,BatchIndex=UINT32_MAX;
|
||||
double GpuDispatchMs=0.0;
|
||||
bool bGpuComplete=false;
|
||||
bool bTimingResolved=false;
|
||||
};
|
||||
struct FGpuFarBatch
|
||||
{
|
||||
ID3D11Query* CompletionEvent=nullptr;
|
||||
VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{};
|
||||
uint32 TileCount=0;
|
||||
uint64 TimestampFrequency=0;
|
||||
double GpuDispatchMs=0.0;
|
||||
bool bGpuComplete=false,bTimingResolved=false;
|
||||
};
|
||||
|
||||
uint64 HashStandaloneFile(const std::string& Path)
|
||||
@@ -1439,6 +1454,7 @@ namespace
|
||||
std::unordered_map<FStandaloneBatchKey,std::vector<FVoxelTileKey>,FStandaloneBatchKeyHash> BatchMembers;
|
||||
std::unordered_map<FStandaloneBatchKey,FStreamingGpuBatch,FStandaloneBatchKeyHash> StreamingBatches;
|
||||
std::unordered_map<FVoxelTileKey,FGpuFarTile,FStandaloneTileKeyHash> GpuFarTiles;
|
||||
std::vector<FGpuFarBatch> GpuFarBatches;
|
||||
std::deque<FVoxelTileKey> GpuFarQueue;
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> GpuFarQueued;
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> GpuFarEligibleTiles;
|
||||
@@ -1474,6 +1490,16 @@ namespace
|
||||
uint64 GpuFarTilesSubmitted=0;
|
||||
double GpuFarGpuDispatchMs=0.0;
|
||||
double GpuFarGpuDispatchMaxMs=0.0;
|
||||
double GpuFarSubmissionSpanMs=0.0,GpuFarSubmissionSpanMaxMs=0.0;
|
||||
uint64 GpuFarLocalMemoryPeakBytes=0,GpuFarLocalMemoryBudgetBytes=0;
|
||||
double GpuFarPoolReserveMs=0.0;
|
||||
uint64 GpuFarSubmissionBatches=0;
|
||||
uint32 GpuFarMaxTilesPerSubmissionBatch=0;
|
||||
uint32 GpuFarOutputCapacitySlots=0,GpuFarNextOutputSlot=0;
|
||||
std::vector<uint32> GpuFarFreeOutputSlots;
|
||||
std::unordered_map<FVoxelTileKey,VoxelForgeCore::FarFieldTileInput,FStandaloneTileKeyHash> GpuFarPreparedInputs;
|
||||
std::unordered_map<FVoxelTileKey,std::string,FStandaloneTileKeyHash> GpuFarPreparationErrors;
|
||||
double GpuFarPreparationWallMs=0.0,GpuFarPreparationWorkerMs=0.0;
|
||||
VoxelForgeCore::DetailVariant ShapeDetailVariant=VoxelForgeCore::DetailVariant::coherent;
|
||||
bool bWireframe=false;
|
||||
bool bCaptured=false;
|
||||
@@ -1531,6 +1557,41 @@ namespace
|
||||
if(SwapChain)SwapChain->Release();if(Context)Context->Release();if(Device)Device->Release();
|
||||
}
|
||||
|
||||
static void ReleaseGpuFarTile(FGpuFarTile& Tile)
|
||||
{
|
||||
if(Tile.VertexBuffer)Tile.VertexBuffer->Release();
|
||||
if(Tile.IndexBuffer)Tile.IndexBuffer->Release();
|
||||
if(Tile.IndirectArgs)Tile.IndirectArgs->Release();
|
||||
if(Tile.ConnectorVertexBuffer)Tile.ConnectorVertexBuffer->Release();
|
||||
if(Tile.ConnectorIndexBuffer)Tile.ConnectorIndexBuffer->Release();
|
||||
if(Tile.CompletionEvent)Tile.CompletionEvent->Release();
|
||||
if(Tile.TimingQueries.start)Tile.TimingQueries.start->Release();
|
||||
if(Tile.TimingQueries.end)Tile.TimingQueries.end->Release();
|
||||
if(Tile.TimingQueries.disjoint)Tile.TimingQueries.disjoint->Release();
|
||||
Tile={};
|
||||
}
|
||||
|
||||
static void ReleaseGpuFarBatch(FGpuFarBatch& Batch)
|
||||
{
|
||||
if(Batch.CompletionEvent)Batch.CompletionEvent->Release();
|
||||
if(Batch.TimingQueries.start)Batch.TimingQueries.start->Release();
|
||||
if(Batch.TimingQueries.end)Batch.TimingQueries.end->Release();
|
||||
if(Batch.TimingQueries.disjoint)Batch.TimingQueries.disjoint->Release();
|
||||
Batch={};
|
||||
}
|
||||
|
||||
void SampleGpuFarMemory()
|
||||
{
|
||||
if(!bGpuFarEnabled||!Device||!Context)return;
|
||||
uint64 Usage=0,Budget=0;std::string Error;
|
||||
if(VoxelForgeGpuFarViewer::QueryFarGpuMemory(Device,Context,GpuFarShaderPath,
|
||||
Usage,Budget,Error))
|
||||
{
|
||||
GpuFarLocalMemoryPeakBytes=std::max(GpuFarLocalMemoryPeakBytes,Usage);
|
||||
GpuFarLocalMemoryBudgetBytes=Budget;
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseMeshes()
|
||||
{
|
||||
for(auto& Pair:StreamingBatches)
|
||||
@@ -1540,16 +1601,20 @@ namespace
|
||||
}
|
||||
StreamingBatches.clear();BatchMembers.clear();ResidentTiles.clear();
|
||||
for(auto& Pair:GpuFarTiles)
|
||||
{
|
||||
if(Pair.second.VertexBuffer)Pair.second.VertexBuffer->Release();
|
||||
if(Pair.second.IndexBuffer)Pair.second.IndexBuffer->Release();
|
||||
if(Pair.second.IndirectArgs)Pair.second.IndirectArgs->Release();
|
||||
if(Pair.second.CompletionEvent)Pair.second.CompletionEvent->Release();
|
||||
}
|
||||
GpuFarTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
ReleaseGpuFarTile(Pair.second);
|
||||
GpuFarTiles.clear();
|
||||
for(FGpuFarBatch& Batch:GpuFarBatches)ReleaseGpuFarBatch(Batch);
|
||||
GpuFarBatches.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0;
|
||||
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();
|
||||
GpuFarPreparationWallMs=0.0;GpuFarPreparationWorkerMs=0.0;
|
||||
GpuFarAudit.reset();bGpuFarAuditReported=false;
|
||||
GpuFarTilesSubmitted=0;GpuFarEnqueueWallSeconds=0.0;GpuFarGpuDispatchMs=0.0;
|
||||
GpuFarGpuDispatchMaxMs=0.0;bGpuFarFirstTileReported=false;
|
||||
GpuFarGpuDispatchMaxMs=0.0;GpuFarSubmissionSpanMs=0.0;GpuFarSubmissionSpanMaxMs=0.0;
|
||||
GpuFarLocalMemoryPeakBytes=0;GpuFarLocalMemoryBudgetBytes=0;
|
||||
bGpuFarFirstTileReported=false;
|
||||
GpuFarPoolReserveMs=0.0;
|
||||
GpuFarSubmissionBatches=0;GpuFarMaxTilesPerSubmissionBatch=0;
|
||||
DirtyBatchSet.clear();DesiredTiles.clear();DesiredTileSet.clear();CrossingWork.clear();
|
||||
DeferredLeaverTiles.clear();
|
||||
for(int32 I=0;I<=MaxLevels;++I)
|
||||
@@ -2104,13 +2169,59 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
auto It=GpuFarTiles.find(Key);
|
||||
if(It==GpuFarTiles.end())return false;
|
||||
FGpuFarTile& Tile=It->second;
|
||||
if(Tile.bGpuComplete)return true;
|
||||
if(Tile.CompletionEvent&&Context
|
||||
if(Tile.BatchIndex!=UINT32_MAX&&Tile.BatchIndex<GpuFarBatches.size())
|
||||
{
|
||||
FGpuFarBatch& Batch=GpuFarBatches[Tile.BatchIndex];
|
||||
if(!Batch.bGpuComplete&&Batch.CompletionEvent&&Context
|
||||
&&Context->GetData(Batch.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK)
|
||||
{
|
||||
Batch.bGpuComplete=true;
|
||||
Batch.CompletionEvent->Release();Batch.CompletionEvent=nullptr;
|
||||
}
|
||||
if(Batch.bGpuComplete&&!Batch.bTimingResolved)
|
||||
{
|
||||
if(VoxelForgeGpuFarViewer::ResolveGpuTiming(Context,Batch.TimingQueries,
|
||||
Batch.GpuDispatchMs,&Batch.TimestampFrequency))
|
||||
{
|
||||
Batch.bTimingResolved=true;
|
||||
GpuFarSubmissionSpanMs+=Batch.GpuDispatchMs;
|
||||
GpuFarSubmissionSpanMaxMs=std::max(GpuFarSubmissionSpanMaxMs,Batch.GpuDispatchMs);
|
||||
}
|
||||
}
|
||||
if(Batch.bGpuComplete&&Batch.bTimingResolved&&!Tile.bTimingResolved
|
||||
&&Batch.TimestampFrequency)
|
||||
{
|
||||
double TileDispatchMs=0.0;
|
||||
if(VoxelForgeGpuFarViewer::ResolveGpuTimestampPair(Context,Tile.TimingQueries,
|
||||
Batch.TimestampFrequency,TileDispatchMs))
|
||||
{
|
||||
Tile.GpuDispatchMs=TileDispatchMs;Tile.bTimingResolved=true;
|
||||
GpuFarGpuDispatchMs+=TileDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,TileDispatchMs);
|
||||
}
|
||||
}
|
||||
return Batch.bGpuComplete;
|
||||
}
|
||||
if(!Tile.bGpuComplete&&Tile.CompletionEvent&&Context
|
||||
&&Context->GetData(Tile.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK)
|
||||
{
|
||||
Tile.bGpuComplete=true;
|
||||
Tile.CompletionEvent->Release();Tile.CompletionEvent=nullptr;
|
||||
}
|
||||
if(Tile.bGpuComplete&&!Tile.bTimingResolved)
|
||||
{
|
||||
double AsyncDispatchMs=0.0;
|
||||
if(VoxelForgeGpuFarViewer::ResolveGpuTiming(Context,Tile.TimingQueries,AsyncDispatchMs))
|
||||
{
|
||||
if(Tile.GpuDispatchMs==0.0)
|
||||
{
|
||||
Tile.GpuDispatchMs=AsyncDispatchMs;
|
||||
GpuFarGpuDispatchMs+=AsyncDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,AsyncDispatchMs);
|
||||
}
|
||||
Tile.bTimingResolved=true;
|
||||
}
|
||||
}
|
||||
return Tile.bGpuComplete;
|
||||
}
|
||||
return ResidentTiles.contains(Key);
|
||||
@@ -2119,20 +2230,98 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
void ScheduleGpuFarTiles(const std::vector<FVoxelTileKey>& Keys)
|
||||
{
|
||||
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear();
|
||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return;
|
||||
std::vector<FVoxelTileKey> CpuTiles;CpuTiles.reserve(Keys.size());
|
||||
std::vector<FVoxelTileKey> Eligible;Eligible.reserve(Keys.size());
|
||||
for(const FVoxelTileKey& Key:Keys)
|
||||
{
|
||||
if(Key.Level<World->FullResClipLevels){CpuTiles.push_back(Key);continue;}
|
||||
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
|
||||
if(ShapeWorld->isFarFieldTileEligible(CoreKey))
|
||||
{
|
||||
GpuFarEligibleTiles.insert(Key);
|
||||
if(!GpuFarTiles.contains(Key))
|
||||
{GpuFarQueue.push_back(Key);GpuFarQueued.insert(Key);}
|
||||
Eligible.push_back(Key);
|
||||
}
|
||||
else CpuTiles.push_back(Key);
|
||||
}
|
||||
if(Eligible.size()>GpuFarOutputCapacitySlots)
|
||||
{
|
||||
for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second);
|
||||
GpuFarTiles.clear();GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0;
|
||||
GpuFarOutputCapacitySlots=static_cast<uint32>(Eligible.size());
|
||||
}
|
||||
if(!Eligible.empty()&&GpuFarOutputCapacitySlots==0)
|
||||
GpuFarOutputCapacitySlots=static_cast<uint32>(Eligible.size());
|
||||
if(!Eligible.empty())
|
||||
{
|
||||
std::string PoolError;const auto ReserveStart=std::chrono::steady_clock::now();
|
||||
if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath,
|
||||
GpuFarOutputCapacitySlots,static_cast<uint32>(World->CoarseTileCells),PoolError))
|
||||
{
|
||||
std::fprintf(stderr,"[GpuFarPoolError] %s\n",PoolError.c_str());
|
||||
bGpuFarEnabled=false;GpuFarOutputCapacitySlots=0;
|
||||
CpuTiles=Keys;Eligible.clear();GpuFarTiles.clear();
|
||||
}
|
||||
GpuFarPoolReserveMs+=std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-ReserveStart).count();
|
||||
SampleGpuFarMemory();
|
||||
}
|
||||
std::vector<FVoxelTileKey> NeedPreparation;NeedPreparation.reserve(Eligible.size());
|
||||
for(const FVoxelTileKey& Key:Eligible)
|
||||
if(!GpuFarTiles.contains(Key))NeedPreparation.push_back(Key);
|
||||
if(!NeedPreparation.empty())
|
||||
{
|
||||
const auto PrepareStart=std::chrono::steady_clock::now();
|
||||
std::vector<std::optional<VoxelForgeCore::FarFieldTileInput>> Inputs(NeedPreparation.size());
|
||||
std::vector<std::string> Errors(NeedPreparation.size());
|
||||
const std::size_t ThreadCount=std::min<std::size_t>(8,NeedPreparation.size());
|
||||
std::atomic<std::size_t> Next{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)
|
||||
{
|
||||
Workers.emplace_back([&,Worker]
|
||||
{
|
||||
const auto WorkerStart=std::chrono::steady_clock::now();
|
||||
for(;;)
|
||||
{
|
||||
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};
|
||||
try { Inputs[Index]=ShapeWorld->exportFarFieldTile(CoreKey,Errors[Index]); }
|
||||
catch(const std::exception& Error) { Errors[Index]=Error.what(); }
|
||||
}
|
||||
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::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]));
|
||||
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);
|
||||
std::fflush(stdout);
|
||||
}
|
||||
for(const FVoxelTileKey& Key:Eligible)
|
||||
{
|
||||
GpuFarEligibleTiles.insert(Key);
|
||||
if(!GpuFarTiles.contains(Key))
|
||||
{GpuFarQueue.push_back(Key);GpuFarQueued.insert(Key);}
|
||||
}
|
||||
if(Streamer)Streamer->SetDesiredTileFilter(CpuTiles);
|
||||
const std::size_t CpuTransitionFallbacks=static_cast<std::size_t>(std::count_if(
|
||||
Keys.begin(),Keys.end(),[&](const FVoxelTileKey& Key)
|
||||
@@ -2145,27 +2334,67 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context
|
||||
||GpuFarQueue.empty())return;
|
||||
const bool bDeferredBatch=!bGpuFarAuditEnabled;
|
||||
if(bDeferredBatch)
|
||||
{
|
||||
std::string BatchError;
|
||||
if(!VoxelForgeGpuFarViewer::BeginFarTileBatch(Device,Context,GpuFarShaderPath,BatchError))
|
||||
{
|
||||
std::fprintf(stderr,"[GpuFarBatchError] begin: %s\n",BatchError.c_str());
|
||||
bGpuFarEnabled=false;return;
|
||||
}
|
||||
}
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
int32 Built=0;
|
||||
std::vector<FVoxelTileKey> BuiltKeys;BuiltKeys.reserve(32);
|
||||
do
|
||||
{
|
||||
const FVoxelTileKey Key=GpuFarQueue.front();GpuFarQueue.pop_front();GpuFarQueued.erase(Key);
|
||||
if(!DesiredTileSet.contains(Key)||GpuFarTiles.contains(Key))continue;
|
||||
FGpuFarTile Tile;
|
||||
if(!GpuFarFreeOutputSlots.empty())
|
||||
{Tile.OutputSlot=GpuFarFreeOutputSlots.back();GpuFarFreeOutputSlots.pop_back();}
|
||||
else if(GpuFarNextOutputSlot<GpuFarOutputCapacitySlots)
|
||||
Tile.OutputSlot=GpuFarNextOutputSlot++;
|
||||
else
|
||||
{
|
||||
std::fprintf(stderr,"[GpuFarPoolError] no free far-output slot for queued tile\n");
|
||||
bGpuFarEnabled=false;break;
|
||||
}
|
||||
VoxelForgeGpuFarViewer::CpuMesh AuditMesh;
|
||||
std::string Error;double EnqueueMs=0.0,GpuDispatchMs=0.0;
|
||||
const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level};
|
||||
if(!VoxelForgeGpuFarViewer::BuildFarTile(Device,Context,*ShapeWorld,CoreKey,
|
||||
GpuFarShaderPath,&Tile.VertexBuffer,&Tile.IndexBuffer,&Tile.IndirectArgs,
|
||||
&Tile.CompletionEvent,
|
||||
(bGpuFarAuditEnabled&&Key.Level>=6)?&AuditMesh:nullptr,
|
||||
&EnqueueMs,&GpuDispatchMs,Error))
|
||||
const auto PreparedInputIt=GpuFarPreparedInputs.find(Key);
|
||||
const VoxelForgeCore::FarFieldTileInput* PreparedInput=PreparedInputIt==GpuFarPreparedInputs.end()
|
||||
?nullptr:&PreparedInputIt->second;
|
||||
if(!PreparedInput)
|
||||
{
|
||||
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());
|
||||
const auto PrepError=GpuFarPreparationErrors.find(Key);
|
||||
std::fprintf(stderr,"[GpuFarInputError] key=(%d,%d,%d,lod%d): %s\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,
|
||||
PrepError==GpuFarPreparationErrors.end()?"prepared far input is missing":PrepError->second.c_str());
|
||||
GpuFarFreeOutputSlots.push_back(Tile.OutputSlot);
|
||||
bGpuFarEnabled=false;
|
||||
break;
|
||||
}
|
||||
if(!VoxelForgeGpuFarViewer::BuildFarTile(Device,Context,*ShapeWorld,CoreKey,
|
||||
PreparedInput,
|
||||
GpuFarShaderPath,&Tile.VertexBuffer,&Tile.IndexBuffer,&Tile.IndirectArgs,
|
||||
&Tile.ConnectorVertexBuffer,&Tile.ConnectorIndexBuffer,
|
||||
Tile.OutputSlot,GpuFarOutputCapacitySlots,&Tile.IndirectArgsByteOffset,
|
||||
bDeferredBatch?nullptr:&Tile.CompletionEvent,
|
||||
&Tile.TimingQueries,
|
||||
(bGpuFarAuditEnabled&&Key.Level>=6)?&AuditMesh:nullptr,
|
||||
&EnqueueMs,&GpuDispatchMs,Error))
|
||||
{
|
||||
GpuFarFreeOutputSlots.push_back(Tile.OutputSlot);
|
||||
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);
|
||||
bGpuFarEnabled=false;
|
||||
break;
|
||||
}
|
||||
if(PreparedInputIt!=GpuFarPreparedInputs.end())GpuFarPreparedInputs.erase(PreparedInputIt);
|
||||
if(bGpuFarAuditEnabled&&GpuFarAudit&&Key.Level>=6)
|
||||
{
|
||||
FStandaloneCpuTile AuditTile;AuditTile.Key=Key;
|
||||
@@ -2182,10 +2411,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
Tile.VertexCapacityBytes=Cells*Cells*Cells*15u*32u;
|
||||
Tile.GpuDispatchMs=GpuDispatchMs;
|
||||
GpuFarEnqueueWallSeconds+=EnqueueMs/1000.0;
|
||||
GpuFarGpuDispatchMs+=GpuDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs);
|
||||
if(GpuDispatchMs>0.0)
|
||||
{
|
||||
GpuFarGpuDispatchMs+=GpuDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs);
|
||||
Tile.bTimingResolved=true;
|
||||
}
|
||||
++GpuFarTilesSubmitted;++Built;
|
||||
GpuFarTiles.emplace(Key,Tile);
|
||||
BuiltKeys.push_back(Key);
|
||||
if(!bGpuFarFirstTileReported)
|
||||
{
|
||||
bGpuFarFirstTileReported=true;
|
||||
@@ -2195,8 +2429,42 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
while(Built<1&& !GpuFarQueue.empty()
|
||||
&&std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-Start).count()<1.0);
|
||||
while(Built<32&&!GpuFarQueue.empty()
|
||||
&&std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-Start).count()<4.0);
|
||||
if(Built>0)
|
||||
{
|
||||
if(bDeferredBatch)
|
||||
{
|
||||
FGpuFarBatch Batch;Batch.TileCount=static_cast<uint32>(Built);
|
||||
std::string BatchError;
|
||||
if(!VoxelForgeGpuFarViewer::SubmitFarTileBatch(Device,Context,GpuFarShaderPath,
|
||||
&Batch.CompletionEvent,&Batch.TimingQueries,BatchError))
|
||||
{
|
||||
std::fprintf(stderr,"[GpuFarBatchError] submit: %s\n",BatchError.c_str());
|
||||
bGpuFarEnabled=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32 BatchIndex=static_cast<uint32>(GpuFarBatches.size());
|
||||
GpuFarBatches.push_back(Batch);
|
||||
for(const FVoxelTileKey& Key:BuiltKeys)
|
||||
{
|
||||
auto It=GpuFarTiles.find(Key);
|
||||
if(It!=GpuFarTiles.end())It->second.BatchIndex=BatchIndex;
|
||||
}
|
||||
++GpuFarSubmissionBatches;
|
||||
GpuFarMaxTilesPerSubmissionBatch=std::max<uint32>(GpuFarMaxTilesPerSubmissionBatch,
|
||||
static_cast<uint32>(Built));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++GpuFarSubmissionBatches;
|
||||
GpuFarMaxTilesPerSubmissionBatch=std::max<uint32>(GpuFarMaxTilesPerSubmissionBatch,
|
||||
static_cast<uint32>(Built));
|
||||
}
|
||||
}
|
||||
SampleGpuFarMemory();
|
||||
}
|
||||
|
||||
void ToggleFarRenderer()
|
||||
@@ -2423,10 +2691,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
for(auto It=GpuFarTiles.begin();It!=GpuFarTiles.end();)
|
||||
{
|
||||
if(DesiredTileSet.contains(It->first)){++It;continue;}
|
||||
if(It->second.VertexBuffer)It->second.VertexBuffer->Release();
|
||||
if(It->second.IndexBuffer)It->second.IndexBuffer->Release();
|
||||
if(It->second.IndirectArgs)It->second.IndirectArgs->Release();
|
||||
if(It->second.CompletionEvent)It->second.CompletionEvent->Release();
|
||||
GpuFarFreeOutputSlots.push_back(It->second.OutputSlot);
|
||||
ReleaseGpuFarTile(It->second);
|
||||
It=GpuFarTiles.erase(It);
|
||||
}
|
||||
ScheduleGpuFarTiles(DesiredTiles);
|
||||
@@ -2786,11 +3052,19 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
static_cast<unsigned long long>(TotalIndices),StreamingBatches.size());
|
||||
if(bGpuFarEnabled)
|
||||
{
|
||||
SampleGpuFarMemory();
|
||||
const double AverageGpuMs=GpuFarTilesSubmitted
|
||||
?GpuFarGpuDispatchMs/static_cast<double>(GpuFarTilesSubmitted):0.0;
|
||||
std::printf("[GpuFarTiming] submitted_tiles=%llu cpu_export_upload_enqueue_wall_ms=%.3f gpu_timestamp_total_ms=%.3f gpu_timestamp_mean_per_one_tile_frame_ms=%.6f gpu_timestamp_max_tile_ms=%.6f frame_budget_90hz_ms=11.111\n",
|
||||
std::printf("[GpuFarTiming] submitted_tiles=%llu submission_batches=%llu max_tiles_per_batch=%u pooled_output_reserve_wall_ms=%.3f cpu_input_prepare_wall_ms=%.3f cpu_input_worker_sum_ms=%.3f cpu_upload_enqueue_sum_ms=%.3f gpu_dispatch_timestamp_total_ms=%.3f gpu_dispatch_mean_per_tile_ms=%.6f gpu_dispatch_max_tile_ms=%.6f gpu_submission_span_total_ms=%.3f gpu_submission_span_max_batch_ms=%.3f frame_budget_90hz_ms=11.111\n",
|
||||
static_cast<unsigned long long>(GpuFarTilesSubmitted),
|
||||
GpuFarEnqueueWallSeconds*1000.0,GpuFarGpuDispatchMs,AverageGpuMs,GpuFarGpuDispatchMaxMs);
|
||||
static_cast<unsigned long long>(GpuFarSubmissionBatches),GpuFarMaxTilesPerSubmissionBatch,
|
||||
GpuFarPoolReserveMs,GpuFarPreparationWallMs,GpuFarPreparationWorkerMs,
|
||||
GpuFarEnqueueWallSeconds*1000.0,
|
||||
GpuFarGpuDispatchMs,AverageGpuMs,GpuFarGpuDispatchMaxMs,
|
||||
GpuFarSubmissionSpanMs,GpuFarSubmissionSpanMaxMs);
|
||||
std::printf("[GpuFarMemory] dxgi_local_sampled_peak_mb=%.1f dxgi_local_budget_mb=%.1f\n",
|
||||
GpuFarLocalMemoryPeakBytes/(1024.0*1024.0),
|
||||
GpuFarLocalMemoryBudgetBytes/(1024.0*1024.0));
|
||||
}
|
||||
ReportTileTimings();std::fflush(stdout);
|
||||
if(bQuitAfterClipmap&&CapturePath.empty())PostMessageA(Window,WM_CLOSE,0,0);
|
||||
@@ -2908,7 +3182,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const UINT Stride=32,Offset=0;
|
||||
Context->IASetVertexBuffers(0,1,&Tile.VertexBuffer,&Stride,&Offset);
|
||||
Context->IASetIndexBuffer(Tile.IndexBuffer,DXGI_FORMAT_R32_UINT,0);
|
||||
Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs,0);
|
||||
Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs,Tile.IndirectArgsByteOffset);
|
||||
if(Tile.ConnectorVertexBuffer&&Tile.ConnectorIndexBuffer)
|
||||
{
|
||||
Context->IASetVertexBuffers(0,1,&Tile.ConnectorVertexBuffer,&Stride,&Offset);
|
||||
Context->IASetIndexBuffer(Tile.ConnectorIndexBuffer,DXGI_FORMAT_R32_UINT,0);
|
||||
Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs,
|
||||
Tile.IndirectArgsByteOffset+5u*sizeof(uint32));
|
||||
}
|
||||
}
|
||||
}
|
||||
CaptureRenderedFrame();
|
||||
|
||||
Reference in new issue
Block a user