wip(standalone): GPU step 3 -- GPU decides far rock/air, sparse CPU exceptions, hybrid viewer (H)
1,114 of 1,266 far tiles meshed on the GPU; vs CPU and vs WARP: 0 sign/topology differences, including the protected/thin exception audit; max symmetric vertex distance 0.125 cm; repeat RX runs identical; GPU field+mesh 0.029 ms/tile average. Strata survival 7,200/7,200, but connector survival fails (GPU lacks the connector repair panels). Hybrid cold clipmap 5.74 s vs CPU 2.23 s: dispatch and readback overhead, not compute (~33 ms of GPU work in total). 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
b61ccff549
commit
782f195bdb
15 files changed
+1018
-173
No files matched your search
@@ -2,6 +2,7 @@
|
||||
#include "StreamingWorld.h"
|
||||
#include "StandaloneCoreScheduler.h"
|
||||
#include "../Core/src/StandaloneSnapshotAdapter.h"
|
||||
#include "../Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h"
|
||||
#include "VoxelDensityProfile.h"
|
||||
|
||||
#ifdef TEXT
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <complex>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
@@ -1345,6 +1347,16 @@ namespace
|
||||
std::chrono::steady_clock::time_point StartTime{};
|
||||
bool bReported=false;
|
||||
};
|
||||
struct FGpuFarTile
|
||||
{
|
||||
ID3D11Buffer* VertexBuffer=nullptr;
|
||||
ID3D11Buffer* IndexBuffer=nullptr;
|
||||
ID3D11Buffer* IndirectArgs=nullptr;
|
||||
ID3D11Query* CompletionEvent=nullptr;
|
||||
uint64 VertexCapacityBytes=0;
|
||||
double GpuDispatchMs=0.0;
|
||||
bool bGpuComplete=false;
|
||||
};
|
||||
|
||||
uint64 HashStandaloneFile(const std::string& Path)
|
||||
{
|
||||
@@ -1426,6 +1438,10 @@ namespace
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> DesiredTileSet;
|
||||
std::unordered_map<FStandaloneBatchKey,std::vector<FVoxelTileKey>,FStandaloneBatchKeyHash> BatchMembers;
|
||||
std::unordered_map<FStandaloneBatchKey,FStreamingGpuBatch,FStandaloneBatchKeyHash> StreamingBatches;
|
||||
std::unordered_map<FVoxelTileKey,FGpuFarTile,FStandaloneTileKeyHash> GpuFarTiles;
|
||||
std::deque<FVoxelTileKey> GpuFarQueue;
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> GpuFarQueued;
|
||||
std::unordered_set<FVoxelTileKey,FStandaloneTileKeyHash> GpuFarEligibleTiles;
|
||||
std::unordered_set<FStandaloneBatchKey,FStandaloneBatchKeyHash> DirtyBatchSet;
|
||||
std::unordered_map<uint64,FCrossingWork> CrossingWork;
|
||||
std::vector<FVoxelTileKey> DesiredTiles;
|
||||
@@ -1448,6 +1464,16 @@ namespace
|
||||
bool bShapeAuditOnly=false;
|
||||
bool bShapeCompare=false;
|
||||
bool bFarShapeStagePending=false;
|
||||
bool bGpuFarEnabled=false;
|
||||
bool bGpuFarAuditEnabled=false;
|
||||
bool bGpuFarAuditReported=false;
|
||||
std::unique_ptr<FFarSurvivalAudit> GpuFarAudit;
|
||||
bool bGpuFarFirstTileReported=false;
|
||||
std::filesystem::path GpuFarShaderPath;
|
||||
double GpuFarEnqueueWallSeconds=0.0;
|
||||
uint64 GpuFarTilesSubmitted=0;
|
||||
double GpuFarGpuDispatchMs=0.0;
|
||||
double GpuFarGpuDispatchMaxMs=0.0;
|
||||
VoxelForgeCore::DetailVariant ShapeDetailVariant=VoxelForgeCore::DetailVariant::coherent;
|
||||
bool bWireframe=false;
|
||||
bool bCaptured=false;
|
||||
@@ -1513,6 +1539,17 @@ namespace
|
||||
if(Pair.second.IndexBuffer)Pair.second.IndexBuffer->Release();
|
||||
}
|
||||
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();
|
||||
GpuFarAudit.reset();bGpuFarAuditReported=false;
|
||||
GpuFarTilesSubmitted=0;GpuFarEnqueueWallSeconds=0.0;GpuFarGpuDispatchMs=0.0;
|
||||
GpuFarGpuDispatchMaxMs=0.0;bGpuFarFirstTileReported=false;
|
||||
DirtyBatchSet.clear();DesiredTiles.clear();DesiredTileSet.clear();CrossingWork.clear();
|
||||
DeferredLeaverTiles.clear();
|
||||
for(int32 I=0;I<=MaxLevels;++I)
|
||||
@@ -1668,7 +1705,7 @@ namespace
|
||||
cbuffer C:register(b0){float4x4 VP;float3 Camera;float FogStart;float3 Forward;float FogEnd;float3 LightDir;float Ambient;float4 Pad;}
|
||||
struct I{float3 p:POSITION;float3 n:NORMAL;};
|
||||
struct O{float4 p:SV_POSITION;float3 world:TEXCOORD0;float3 n:NORMAL;};
|
||||
O VS(I i){O o;o.p=mul(float4(i.p,1),VP);o.world=i.p;o.n=normalize(i.n);return o;}
|
||||
O VS(I i){O o;float3 p=i.p*Pad.x;o.p=mul(float4(p,1),VP);o.world=p;o.n=normalize(i.n);return o;}
|
||||
float4 PS(O i):SV_TARGET{
|
||||
float3 N=normalize(i.n);float3 V=normalize(Camera-i.world);float3 L=normalize(-LightDir);
|
||||
float Diffuse=saturate(dot(N,L));float Head=saturate(dot(N,V));
|
||||
@@ -1974,6 +2011,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
(!bShapeWorld&&MeshCache.IsLoaded())?&MeshCache:nullptr,
|
||||
bFarShapeStagePending?World->FullResClipLevels-1:-1,
|
||||
false,false,false,false,ShapeWorld.get());
|
||||
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld())
|
||||
{
|
||||
ScheduleGpuFarTiles(DesiredTiles);
|
||||
if(bGpuFarAuditEnabled)
|
||||
{
|
||||
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarEligibleTiles.size());
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(GpuFarEligibleTiles.contains(Key))AuditKeys.push_back(Key);
|
||||
GpuFarAudit=std::make_unique<FFarSurvivalAudit>(*World,*ShapeWorld,AuditKeys,false);
|
||||
}
|
||||
}
|
||||
if(bShapePipelineActive)Streamer->SetDesiredTileFilter(InitialNeighborhoodTiles);
|
||||
else if(CaptureTileLimit>0&&CapturePath.size())
|
||||
Streamer->SetDesiredTileFilter(DesiredTiles);
|
||||
@@ -2025,6 +2073,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
Streamer.reset();
|
||||
Streamer=std::make_unique<FStandaloneStreamingGenerator>(*World,WorkerCount,false,nullptr,-1,
|
||||
false,false,false,false,ShapeWorld.get());
|
||||
if(bGpuFarEnabled)
|
||||
{
|
||||
ScheduleGpuFarTiles(DesiredTiles);
|
||||
if(bGpuFarAuditEnabled&&World)
|
||||
{
|
||||
std::vector<FVoxelTileKey> AuditKeys;AuditKeys.reserve(GpuFarEligibleTiles.size());
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(GpuFarEligibleTiles.contains(Key))AuditKeys.push_back(Key);
|
||||
GpuFarAudit=std::make_unique<FFarSurvivalAudit>(*World,*ShapeWorld,AuditKeys,false);
|
||||
}
|
||||
}
|
||||
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
|
||||
++CrossingIndex;
|
||||
const FStandaloneStreamingUpdate Update=Streamer->Start(CameraVoxels,ResidentKeys,CrossingIndex);
|
||||
@@ -2037,6 +2096,145 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
bool IsRenderedTileReady(const FVoxelTileKey& Key)
|
||||
{
|
||||
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld()
|
||||
&&GpuFarEligibleTiles.contains(Key))
|
||||
{
|
||||
auto It=GpuFarTiles.find(Key);
|
||||
if(It==GpuFarTiles.end())return false;
|
||||
FGpuFarTile& Tile=It->second;
|
||||
if(Tile.bGpuComplete)return true;
|
||||
if(Tile.CompletionEvent&&Context
|
||||
&&Context->GetData(Tile.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK)
|
||||
{
|
||||
Tile.bGpuComplete=true;
|
||||
Tile.CompletionEvent->Release();Tile.CompletionEvent=nullptr;
|
||||
}
|
||||
return Tile.bGpuComplete;
|
||||
}
|
||||
return ResidentTiles.contains(Key);
|
||||
}
|
||||
|
||||
void ScheduleGpuFarTiles(const std::vector<FVoxelTileKey>& Keys)
|
||||
{
|
||||
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return;
|
||||
std::vector<FVoxelTileKey> CpuTiles;CpuTiles.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);}
|
||||
}
|
||||
else CpuTiles.push_back(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)
|
||||
{return Key.Level>=World->FullResClipLevels&&!GpuFarEligibleTiles.contains(Key);}));
|
||||
std::printf("[GpuFarEligibility] eligible_gpu_tiles=%zu cpu_transition_fallback_tiles=%zu\n",
|
||||
GpuFarEligibleTiles.size(),CpuTransitionFallbacks);
|
||||
}
|
||||
|
||||
void PumpGpuFarGeneration()
|
||||
{
|
||||
if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context
|
||||
||GpuFarQueue.empty())return;
|
||||
const auto Start=std::chrono::steady_clock::now();
|
||||
int32 Built=0;
|
||||
do
|
||||
{
|
||||
const FVoxelTileKey Key=GpuFarQueue.front();GpuFarQueue.pop_front();GpuFarQueued.erase(Key);
|
||||
if(!DesiredTileSet.contains(Key)||GpuFarTiles.contains(Key))continue;
|
||||
FGpuFarTile Tile;
|
||||
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))
|
||||
{
|
||||
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());
|
||||
bGpuFarEnabled=false;
|
||||
break;
|
||||
}
|
||||
if(bGpuFarAuditEnabled&&GpuFarAudit&&Key.Level>=6)
|
||||
{
|
||||
FStandaloneCpuTile AuditTile;AuditTile.Key=Key;
|
||||
AuditTile.SurfaceVertexCount=static_cast<uint32>(AuditMesh.vertices.size());
|
||||
AuditTile.Vertices.reserve(AuditMesh.vertices.size());
|
||||
for(const auto& Vertex:AuditMesh.vertices)
|
||||
AuditTile.Vertices.push_back({
|
||||
{Vertex.position[0]*0.01f,Vertex.position[1]*0.01f,Vertex.position[2]*0.01f},
|
||||
{Vertex.normal[0],Vertex.normal[1],Vertex.normal[2]}});
|
||||
AuditTile.Indices=std::move(AuditMesh.indices);
|
||||
GpuFarAudit->AuditTile(AuditTile);
|
||||
}
|
||||
const uint64 Cells=static_cast<uint64>(World->CoarseTileCells);
|
||||
Tile.VertexCapacityBytes=Cells*Cells*Cells*15u*32u;
|
||||
Tile.GpuDispatchMs=GpuDispatchMs;
|
||||
GpuFarEnqueueWallSeconds+=EnqueueMs/1000.0;
|
||||
GpuFarGpuDispatchMs+=GpuDispatchMs;
|
||||
GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs);
|
||||
++GpuFarTilesSubmitted;++Built;
|
||||
GpuFarTiles.emplace(Key,Tile);
|
||||
if(!bGpuFarFirstTileReported)
|
||||
{
|
||||
bGpuFarFirstTileReported=true;
|
||||
std::printf("[GpuFarFirstTile] key=(%d,%d,%d,lod%d) cpu_export_upload_enqueue_ms=%.3f vertex_capacity_mb=%.3f mode=direct-indirect-draw\n",
|
||||
Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,EnqueueMs,
|
||||
Tile.VertexCapacityBytes/(1024.0*1024.0));
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
while(Built<1&& !GpuFarQueue.empty()
|
||||
&&std::chrono::duration<double,std::milli>(std::chrono::steady_clock::now()-Start).count()<1.0);
|
||||
}
|
||||
|
||||
void ToggleFarRenderer()
|
||||
{
|
||||
if(!bShapeWorld||!ShapeWorld||!ShapeWorld->hasFarWorld())
|
||||
{
|
||||
bGpuFarEnabled=!bGpuFarEnabled;
|
||||
std::printf("[FarRenderer] GPU far %s; it will take effect when far shapes are ready.\n",
|
||||
bGpuFarEnabled?"enabled":"disabled");
|
||||
return;
|
||||
}
|
||||
bGpuFarEnabled=!bGpuFarEnabled;
|
||||
if(bGpuFarEnabled)
|
||||
{
|
||||
ScheduleGpuFarTiles(DesiredTiles);
|
||||
std::vector<FVoxelTileKey> CpuFar;
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(GpuFarEligibleTiles.contains(Key)&&ResidentTiles.contains(Key))CpuFar.push_back(Key);
|
||||
for(const FVoxelTileKey& Key:CpuFar)RemoveResidentTile(Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear();
|
||||
Streamer->SetDesiredTileFilter(DesiredTiles);
|
||||
}
|
||||
std::vector<FVoxelTileKey> ResidentKeys;ResidentKeys.reserve(ResidentTiles.size());
|
||||
for(const auto& Pair:ResidentTiles)ResidentKeys.push_back(Pair.first);
|
||||
const FVector CameraVoxels(Position.X*4.0,Position.Y*4.0,Position.Z*4.0);
|
||||
++CrossingIndex;
|
||||
const FStandaloneStreamingUpdate Update=Streamer->UpdateDesired(
|
||||
CameraVoxels,ResidentKeys,CrossingIndex);
|
||||
FCrossingWork& Work=CrossingWork[CrossingIndex];
|
||||
Work.SubmittedJobs=Update.NewJobs;Work.PendingJobs=Update.NewJobs;
|
||||
Work.StartTime=std::chrono::steady_clock::now();
|
||||
std::printf("[FarRenderer] mode=%s cpu_far_jobs=%d gpu_far_ready=%zu gpu_far_queued=%zu (H toggles)\n",
|
||||
bGpuFarEnabled?"GPU":"CPU",Update.NewJobs,GpuFarTiles.size(),GpuFarQueue.size());
|
||||
}
|
||||
|
||||
void TryCompleteShapePipeline()
|
||||
{
|
||||
if(!bShapePipelineActive||InitialNeighborhoodTiles.empty())return;
|
||||
@@ -2222,6 +2420,16 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
DesiredTiles=NewDesired.Tiles;
|
||||
DesiredTileSet=std::move(NewSet);
|
||||
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();
|
||||
It=GpuFarTiles.erase(It);
|
||||
}
|
||||
ScheduleGpuFarTiles(DesiredTiles);
|
||||
RefreshResidentSkirtMasks();
|
||||
for(int32 I=0;I<=MaxLevels;++I)DesiredByLevel[I]=0;
|
||||
for(const FVoxelTileKey& Tile:DesiredTiles)++DesiredByLevel[Tile.Level];
|
||||
@@ -2489,7 +2697,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
{
|
||||
bool bDesiredResident=true;
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(!ResidentTiles.contains(Key)){bDesiredResident=false;break;}
|
||||
if(!IsRenderedTileReady(Key)){bDesiredResident=false;break;}
|
||||
if(bDesiredResident)
|
||||
{
|
||||
std::vector<FVoxelTileKey> ReadyToDrop;
|
||||
@@ -2531,7 +2739,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const uint64 CacheHits=Streamer?Streamer->GetCacheHits():0;
|
||||
std::size_t DesiredResidentCount=0;
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(ResidentTiles.contains(Key))++DesiredResidentCount;
|
||||
if(IsRenderedTileReady(Key))++DesiredResidentCount;
|
||||
std::sort(NearestReadyMilliseconds.begin(),NearestReadyMilliseconds.end());
|
||||
const auto Percentile=[&](std::size_t Percent)
|
||||
{
|
||||
@@ -2568,7 +2776,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
}
|
||||
size_t DesiredResidentCount=0;
|
||||
for(const FVoxelTileKey& Key:DesiredTiles)
|
||||
if(ResidentTiles.contains(Key))++DesiredResidentCount;
|
||||
if(IsRenderedTileReady(Key))++DesiredResidentCount;
|
||||
if(!bInitialClipmapReported&&DesiredResidentCount==DesiredTiles.size()&&DirtyBatchSet.empty())
|
||||
{
|
||||
bInitialClipmapReported=true;
|
||||
@@ -2576,10 +2784,28 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("[InitialClipmapReady] %.3f s after cold process start; %zu tiles, %llu vertices, %llu indices, %zu GPU batches.\n",
|
||||
Elapsed,DesiredResidentCount,static_cast<unsigned long long>(TotalVertices),
|
||||
static_cast<unsigned long long>(TotalIndices),StreamingBatches.size());
|
||||
if(bGpuFarEnabled)
|
||||
{
|
||||
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",
|
||||
static_cast<unsigned long long>(GpuFarTilesSubmitted),
|
||||
GpuFarEnqueueWallSeconds*1000.0,GpuFarGpuDispatchMs,AverageGpuMs,GpuFarGpuDispatchMaxMs);
|
||||
}
|
||||
ReportTileTimings();std::fflush(stdout);
|
||||
if(bQuitAfterClipmap&&CapturePath.empty())PostMessageA(Window,WM_CLOSE,0,0);
|
||||
}
|
||||
bGenerationRunning=DesiredResidentCount!=DesiredTiles.size()||!DirtyBatchSet.empty();
|
||||
if(bInitialClipmapReported&&bGpuFarAuditEnabled&&GpuFarAudit&&!bGpuFarAuditReported)
|
||||
{
|
||||
GpuFarAudit->Finalize();GpuFarAudit->Print();
|
||||
bGpuFarAuditReported=true;
|
||||
std::printf("[GpuFarSurvivalAudit] mesh_source=GPU field_readback=required status=%s\n",
|
||||
GpuFarAudit->Passing()?"PASS":"FAIL");
|
||||
std::fflush(stdout);
|
||||
if(bQuitAfterClipmap&&CapturePath.empty())PostMessageA(Window,WM_CLOSE,0,0);
|
||||
}
|
||||
bGenerationRunning=DesiredResidentCount!=DesiredTiles.size()||!DirtyBatchSet.empty()
|
||||
||(bGpuFarEnabled&&!GpuFarQueue.empty());
|
||||
}
|
||||
|
||||
void CaptureRenderedFrame()
|
||||
@@ -2608,7 +2834,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
uint64 VisibleIndices=0;for(const auto& Pair:StreamingBatches)if(bShowLevel[Pair.first.Level])VisibleIndices+=Pair.second.IndexCount;
|
||||
std::ostringstream S;S<<"VoxelForge standalone | seed "<<Seed<<" | "<<(bShapeWorld?"shape-interval owner world":(bRoundTunnels?"round/winding research tunnels":"base world"))
|
||||
<<(bRoundTunnels?(bRoundTunnelEnhanced?" (detailed walls)":" (legacy walls)"):"")
|
||||
<<" | "<<VisibleIndices/3<<" tris | "<<FrameMs<<" ms | "<<(bGenerationRunning?"generating":"ready")
|
||||
<<" | "<<VisibleIndices/3<<" tris | "<<FrameMs<<" ms | far "<<(bGpuFarEnabled?"GPU":"CPU")
|
||||
<<" | "<<(bGenerationRunning?"generating":"ready")
|
||||
<<" | pos "<<std::fixed<<std::setprecision(1)<<Position.X<<", "<<Position.Y<<", "<<Position.Z<<" m";
|
||||
SetWindowTextA(Window,S.str().c_str());
|
||||
}
|
||||
@@ -2616,6 +2843,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
void Draw()
|
||||
{
|
||||
PumpGeneration();
|
||||
PumpGpuFarGeneration();
|
||||
const auto Now=std::chrono::steady_clock::now();
|
||||
if(FlightTargetCrossings>0&&bFullDetailReported&&!bFlightStarted)
|
||||
{
|
||||
@@ -2658,6 +2886,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
C.ViewProjection=Transpose(Combined);C.Camera[0]=Position.X;C.Camera[1]=Position.Y;C.Camera[2]=Position.Z;
|
||||
C.Forward[0]=F.X;C.Forward[1]=F.Y;C.Forward[2]=F.Z;C.LightDirection[0]=-0.5f;C.LightDirection[1]=-0.72f;C.LightDirection[2]=-0.48f;
|
||||
C.FogStart=260.0f;C.FogEnd=2600.0f;C.Ambient=0.36f;
|
||||
C.Padding[0]=1.0f;
|
||||
Context->UpdateSubresource(Constants,0,nullptr,&C,0,0);Context->VSSetConstantBuffers(0,1,&Constants);Context->PSSetConstantBuffers(0,1,&Constants);
|
||||
Context->VSSetShader(VertexShader,nullptr,0);Context->PSSetShader(PixelShader,nullptr,0);
|
||||
for(const auto& Pair:StreamingBatches)
|
||||
@@ -2667,6 +2896,21 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const UINT Stride=sizeof(FGpuVertex),Offset=0;Context->IASetVertexBuffers(0,1,&L.VertexBuffer,&Stride,&Offset);
|
||||
Context->IASetIndexBuffer(L.IndexBuffer,DXGI_FORMAT_R32_UINT,0);Context->DrawIndexed(L.IndexCount,0,0);
|
||||
}
|
||||
if(bGpuFarEnabled&&ShapeWorld&&ShapeWorld->hasFarWorld())
|
||||
{
|
||||
C.Padding[0]=0.01f;
|
||||
Context->UpdateSubresource(Constants,0,nullptr,&C,0,0);
|
||||
Context->VSSetConstantBuffers(0,1,&Constants);Context->PSSetConstantBuffers(0,1,&Constants);
|
||||
for(const auto& Pair:GpuFarTiles)
|
||||
{
|
||||
const FGpuFarTile& Tile=Pair.second;
|
||||
if(!bShowLevel[Pair.first.Level]||!Tile.VertexBuffer||!Tile.IndexBuffer||!Tile.IndirectArgs)continue;
|
||||
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);
|
||||
}
|
||||
}
|
||||
CaptureRenderedFrame();
|
||||
SwapChain->Present(1,0);UpdateTitle();
|
||||
}
|
||||
@@ -2693,6 +2937,7 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("Round-tunnel comparison: %s wall field.\n",
|
||||
GApp->bRoundTunnelEnhanced?"detailed/smoothed":"legacy smooth tube");
|
||||
}
|
||||
if(!bWasDown&&W=='H')GApp->ToggleFarRenderer();
|
||||
if(!bWasDown&&(W=='N'||W=='P'))GApp->RequestSeed(W=='N'?1:-1);
|
||||
if(!bWasDown&&W>='1'&&W<='8'){const int32 I=static_cast<int32>(W-'1');GApp->bShowLevel[I]=!GApp->bShowLevel[I];}
|
||||
if(!bWasDown&&W=='9')GApp->bShowLevel[8]=!GApp->bShowLevel[8];
|
||||
@@ -4425,8 +4670,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
|
||||
int main(int Argc,char** Argv)
|
||||
{
|
||||
SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS);
|
||||
SetProcessAffinityMask(GetCurrentProcess(),static_cast<DWORD_PTR>(0xFF00));
|
||||
const auto ColdProcessStart=std::chrono::steady_clock::now();
|
||||
FApp App;App.DataPath=DefaultDataPath().string();App.ColdProcessStart=ColdProcessStart;
|
||||
wchar_t ExecutablePath[MAX_PATH]{};
|
||||
const DWORD ExecutablePathLength=GetModuleFileNameW(nullptr,ExecutablePath,MAX_PATH);
|
||||
if(ExecutablePathLength>0&&ExecutablePathLength<MAX_PATH)
|
||||
App.GpuFarShaderPath=std::filesystem::path(ExecutablePath).parent_path().parent_path()
|
||||
/"Research"/"OwnerShapeWorld"/"gpu_first_port"/"VoxelForgeGpuMesher.hlsl";
|
||||
std::string JoinedCommandLine;
|
||||
for(int32 I=1;I<Argc;++I)
|
||||
{if(!JoinedCommandLine.empty())JoinedCommandLine.push_back(' ');JoinedCommandLine+=Argv[I];}
|
||||
@@ -4464,6 +4716,10 @@ int main(int Argc,char** Argv)
|
||||
else if(Arg=="--parity-only")App.bParityOnly=true;
|
||||
else if(Arg=="--density-hash")App.bDensityHash=true;
|
||||
else if(Arg=="--shape-world")App.bShapeWorld=true;
|
||||
else if(Arg=="--gpu-far")App.bGpuFarEnabled=true,App.bShapeWorld=true,App.bForceGenerate=true;
|
||||
else if(Arg=="--cpu-far")App.bGpuFarEnabled=false,App.bShapeWorld=true,App.bForceGenerate=true;
|
||||
else if(Arg=="--gpu-far-audit")
|
||||
{App.bGpuFarEnabled=true;App.bGpuFarAuditEnabled=true;App.bShapeWorld=true;App.bForceGenerate=true;App.bQuitAfterClipmap=true;}
|
||||
else if(Arg=="--shape-audit")App.bShapeWorld=true,App.bShapeAuditOnly=true;
|
||||
else if(Arg=="--grid-stair-audit")bGridStairAudit=true,App.bShapeWorld=true;
|
||||
else if(Arg=="--tile-path-hash-audit")bTilePathHashAudit=true,App.bShapeWorld=true;
|
||||
@@ -4669,7 +4925,7 @@ int main(int Argc,char** Argv)
|
||||
GApp=&App;
|
||||
if(!App.Window||!App.InitializeDevice()){MessageBoxA(nullptr,"Direct3D 11 initialization failed.","VoxelForge standalone",MB_ICONERROR);return 3;}
|
||||
ShowWindow(App.Window,SW_SHOW);UpdateWindow(App.Window);
|
||||
std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n");
|
||||
std::printf("Controls: hold right mouse to look; WASD move; Space/Ctrl (E/Q) up/down; Shift faster; wheel changes speed; F wireframe; G toggles legacy/detailed round walls; H toggles CPU/GPU far tiles; 1-9 toggle LOD; 0 toggle all levels; N/P change seed; T swaps base/round tunnels; V swaps owner density/shape intervals; Esc quit.\n");
|
||||
std::printf("Owner data: %s; starting at first-strate open-space sample (%.2f, %.2f, %.2f) m.\n",
|
||||
App.DataPath.c_str(),App.Position.X,App.Position.Y,App.Position.Z);
|
||||
std::fflush(stdout);
|
||||
|
||||
Reference in new issue
Block a user