profile: in-process SIGPROF sampler + exclusive route/phase census (L77); closes to 0.1%
Opt-in (L77 build + --linux77-profile; compiled out by default, 0.02% handler cost). FI/TN cost account closes against direct grid-fill timing within 0.15% (LOD0-2 bins within 1.4%). Passage carving = 66% of LOD0-2 CPU (52-54% whole view), passage support 10-14%, base packed + analytic field ~3%. Hotspots: value noise 17%, lower_bound in VisitNearestPassageSpatialNode 14%, ApplyResolvedStratePassageCarves 6.5%. LOD0/1 complete LAST (4.1/4.6 s) while LOD2 is ready at 2.2 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
68cba5a51d
commit
5e091d254c
14 files changed
+1205
-8
No files matched your search
@@ -0,0 +1,257 @@
|
||||
#define _GNU_SOURCE 1
|
||||
#include "VoxelForgeCore/Linux77Profiler.h"
|
||||
|
||||
#if defined(VF_L77_PROFILE)
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cinttypes>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <time.h>
|
||||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
namespace VoxelForgeCore::Linux77Profiler
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr std::uint32_t Capacity=1u<<20;
|
||||
constexpr std::size_t MaxRegisteredThreads=32;
|
||||
struct FSample
|
||||
{
|
||||
std::uintptr_t pc=0;
|
||||
std::uint8_t lod=0xff,route=0,stage=0,phase=0,halo=0,weight=1;
|
||||
};
|
||||
static_assert(std::atomic<std::uint32_t>::is_always_lock_free);
|
||||
static_assert(std::atomic<std::uint64_t>::is_always_lock_free);
|
||||
std::array<FSample,Capacity> Samples{};
|
||||
std::atomic<std::uint32_t> NextSample{0};
|
||||
std::atomic<std::uint64_t> LostSamples{0},HandlerSamples{0},HandlerNanoseconds{0};
|
||||
std::atomic<std::uint64_t> ExpandedTicks{0};
|
||||
std::atomic<bool> Enabled{false};
|
||||
volatile sig_atomic_t TimerActive=0;
|
||||
std::atomic<std::uint32_t> ActiveHandlers{0};
|
||||
std::uint32_t PeriodMicroseconds=1000;
|
||||
std::mutex TimerRegistryMutex;
|
||||
std::array<timer_t,MaxRegisteredThreads> ThreadTimers{};
|
||||
std::array<pid_t,MaxRegisteredThreads> ThreadIds{};
|
||||
std::array<clockid_t,MaxRegisteredThreads> ThreadCpuClockIds{};
|
||||
std::array<std::uint64_t,MaxRegisteredThreads> ThreadCpuStartNanoseconds{};
|
||||
std::array<std::uint64_t,MaxRegisteredThreads> ThreadProfileCpuNanoseconds{};
|
||||
std::array<std::atomic<std::uint64_t>,MaxRegisteredThreads> ThreadSampleCounts{};
|
||||
std::size_t ThreadTimerCount=0;
|
||||
inline thread_local bool ThreadTimerRegistered=false;
|
||||
std::atomic<std::uint64_t> TimerOverruns{0};
|
||||
|
||||
std::uint64_t ThreadCpuNanoseconds() noexcept
|
||||
{
|
||||
timespec time{};
|
||||
if(clock_gettime(CLOCK_THREAD_CPUTIME_ID,&time)!=0)return 0;
|
||||
return static_cast<std::uint64_t>(time.tv_sec)*1000000000ull
|
||||
+static_cast<std::uint64_t>(time.tv_nsec);
|
||||
}
|
||||
std::uint64_t UserCpuNanoseconds(const timeval& value) noexcept
|
||||
{
|
||||
return static_cast<std::uint64_t>(value.tv_sec)*1000000000ull
|
||||
+static_cast<std::uint64_t>(value.tv_usec)*1000ull;
|
||||
}
|
||||
void SigprofHandler(int, siginfo_t* information, void* context) noexcept
|
||||
{
|
||||
if(!TimerActive)return;
|
||||
ActiveHandlers.fetch_add(1,std::memory_order_relaxed);
|
||||
const std::uint64_t start=ThreadCpuNanoseconds();
|
||||
HandlerSamples.fetch_add(1,std::memory_order_relaxed);
|
||||
const int timerIndex=information?information->si_value.sival_int:-1;
|
||||
if(timerIndex>=0&&static_cast<std::size_t>(timerIndex)<MaxRegisteredThreads)
|
||||
{
|
||||
ThreadSampleCounts[static_cast<std::size_t>(timerIndex)].fetch_add(
|
||||
1,std::memory_order_relaxed);
|
||||
if(information&&information->si_overrun>0)
|
||||
TimerOverruns.fetch_add(static_cast<std::uint64_t>(information->si_overrun),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
const std::uint32_t index=NextSample.fetch_add(1,std::memory_order_relaxed);
|
||||
const std::uint64_t overrun=information&&information->si_overrun>0
|
||||
?static_cast<std::uint64_t>(information->si_overrun):0;
|
||||
const std::uint8_t weight=static_cast<std::uint8_t>(
|
||||
std::min<std::uint64_t>(255,overrun+1));
|
||||
ExpandedTicks.fetch_add(weight,std::memory_order_relaxed);
|
||||
if(index<Capacity)
|
||||
{
|
||||
std::uintptr_t pc=0;
|
||||
#if defined(__x86_64__) && defined(REG_RIP)
|
||||
if(context)
|
||||
pc=static_cast<std::uintptr_t>(
|
||||
static_cast<ucontext_t*>(context)->uc_mcontext.gregs[REG_RIP]);
|
||||
#endif
|
||||
const FThreadContext& tag=CurrentContext;
|
||||
Samples[index]={pc,tag.lod,static_cast<std::uint8_t>(tag.route),
|
||||
static_cast<std::uint8_t>(tag.stage),
|
||||
static_cast<std::uint8_t>(tag.phase),static_cast<std::uint8_t>(tag.halo),weight};
|
||||
}
|
||||
else LostSamples.fetch_add(1,std::memory_order_relaxed);
|
||||
const std::uint64_t end=ThreadCpuNanoseconds();
|
||||
if(end>=start)HandlerNanoseconds.fetch_add(end-start,std::memory_order_relaxed);
|
||||
ActiveHandlers.fetch_sub(1,std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEnabled() noexcept{return Enabled.load(std::memory_order_relaxed);}
|
||||
void SetEnabled(bool enabled) noexcept{Enabled.store(enabled,std::memory_order_relaxed);}
|
||||
|
||||
void RegisterThread()
|
||||
{
|
||||
if(ThreadTimerRegistered||!IsEnabled())return;
|
||||
std::lock_guard<std::mutex> lock(TimerRegistryMutex);
|
||||
if(ThreadTimerRegistered||!IsEnabled()||ThreadTimerCount>=MaxRegisteredThreads)return;
|
||||
struct sigevent event{};
|
||||
event.sigev_notify=SIGEV_THREAD_ID;
|
||||
event.sigev_signo=SIGPROF;
|
||||
event.sigev_value.sival_int=static_cast<int>(ThreadTimerCount);
|
||||
event._sigev_un._tid=static_cast<int>(syscall(SYS_gettid));
|
||||
timer_t timer{};
|
||||
if(timer_create(CLOCK_THREAD_CPUTIME_ID,&event,&timer)!=0)
|
||||
{
|
||||
std::fprintf(stderr,"[L77SamplerError] action=thread_timer_create errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
itimerspec interval{};
|
||||
interval.it_value.tv_nsec=static_cast<long>(PeriodMicroseconds%1000000u)*1000l;
|
||||
interval.it_value.tv_sec=static_cast<time_t>(PeriodMicroseconds/1000000u);
|
||||
interval.it_interval=interval.it_value;
|
||||
const std::size_t timerIndex=ThreadTimerCount;
|
||||
clockid_t threadCpuClock{};
|
||||
if(pthread_getcpuclockid(pthread_self(),&threadCpuClock)!=0)
|
||||
{
|
||||
timer_delete(timer);
|
||||
std::fprintf(stderr,"[L77SamplerError] action=thread_clock errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
timespec threadCpuStart{};
|
||||
if(clock_gettime(threadCpuClock,&threadCpuStart)!=0)
|
||||
{
|
||||
timer_delete(timer);
|
||||
std::fprintf(stderr,"[L77SamplerError] action=thread_clock_read errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
ThreadCpuClockIds[timerIndex]=threadCpuClock;
|
||||
ThreadCpuStartNanoseconds[timerIndex]=
|
||||
static_cast<std::uint64_t>(threadCpuStart.tv_sec)*1000000000ull
|
||||
+static_cast<std::uint64_t>(threadCpuStart.tv_nsec);
|
||||
ThreadIds[timerIndex]=static_cast<pid_t>(event._sigev_un._tid);
|
||||
if(timer_settime(timer,0,&interval,nullptr)!=0)
|
||||
{
|
||||
timer_delete(timer);
|
||||
std::fprintf(stderr,"[L77SamplerError] action=thread_timer_start errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
ThreadTimers[ThreadTimerCount++]=timer;
|
||||
ThreadTimerRegistered=true;
|
||||
}
|
||||
|
||||
void Start(std::uint32_t periodMicroseconds)
|
||||
{
|
||||
if(TimerActive)return;
|
||||
PeriodMicroseconds=periodMicroseconds?periodMicroseconds:1000;
|
||||
NextSample.store(0,std::memory_order_relaxed);
|
||||
LostSamples.store(0,std::memory_order_relaxed);
|
||||
HandlerSamples.store(0,std::memory_order_relaxed);
|
||||
HandlerNanoseconds.store(0,std::memory_order_relaxed);
|
||||
ExpandedTicks.store(0,std::memory_order_relaxed);
|
||||
TimerOverruns.store(0,std::memory_order_relaxed);
|
||||
struct sigaction action{};
|
||||
action.sa_sigaction=&SigprofHandler;
|
||||
action.sa_flags=SA_SIGINFO|SA_RESTART;
|
||||
sigemptyset(&action.sa_mask);
|
||||
if(sigaction(SIGPROF,&action,nullptr)!=0)
|
||||
{
|
||||
std::fprintf(stderr,"[L77SamplerError] action=install errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
SetEnabled(true);
|
||||
TimerActive=1;
|
||||
RegisterThread();
|
||||
std::printf("[L77Sampler] action=start method=thread_cpu_timer period_us=%u capacity=%u\n",
|
||||
PeriodMicroseconds,Capacity);
|
||||
}
|
||||
|
||||
void StopAndPrint()
|
||||
{
|
||||
if(!TimerActive)return;
|
||||
SetEnabled(false);
|
||||
std::size_t registeredThreadCount=0;
|
||||
std::uint64_t profiledThreadCpuNanoseconds=0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(TimerRegistryMutex);
|
||||
registeredThreadCount=ThreadTimerCount;
|
||||
for(std::size_t index=0;index<ThreadTimerCount;++index)
|
||||
{
|
||||
itimerspec stopped{};
|
||||
timer_settime(ThreadTimers[index],0,&stopped,nullptr);
|
||||
timespec now{};
|
||||
if(clock_gettime(ThreadCpuClockIds[index],&now)==0)
|
||||
{
|
||||
const std::uint64_t elapsed=static_cast<std::uint64_t>(now.tv_sec)
|
||||
*1000000000ull+static_cast<std::uint64_t>(now.tv_nsec)
|
||||
-ThreadCpuStartNanoseconds[index];
|
||||
ThreadProfileCpuNanoseconds[index]=elapsed;
|
||||
profiledThreadCpuNanoseconds+=elapsed;
|
||||
}
|
||||
timer_delete(ThreadTimers[index]);
|
||||
}
|
||||
ThreadTimerCount=0;
|
||||
}
|
||||
TimerActive=0;
|
||||
while(ActiveHandlers.load(std::memory_order_relaxed)!=0){}
|
||||
rusage usage{};
|
||||
getrusage(RUSAGE_SELF,&usage);
|
||||
const std::uint64_t userNs=UserCpuNanoseconds(usage.ru_utime);
|
||||
const std::uint64_t systemNs=UserCpuNanoseconds(usage.ru_stime);
|
||||
const std::uint32_t stored=std::min(NextSample.load(std::memory_order_relaxed),Capacity);
|
||||
const std::uint64_t handlerCount=HandlerSamples.load(std::memory_order_relaxed);
|
||||
const std::uint64_t handlerNs=HandlerNanoseconds.load(std::memory_order_relaxed);
|
||||
const std::uint64_t expandedTicks=ExpandedTicks.load(std::memory_order_relaxed);
|
||||
std::printf("[L77Sampler] action=stop samples=%u handlers=%llu expanded_ticks=%llu lost=%llu timer_overruns=%llu timer_threads=%zu requested_period_us=%u profiled_thread_cpu_s=%.6f whole_process_cpu_s=%.6f handler_cpu_ms=%.3f handler_share_pct=%.3f observed_us_per_tick=%.3f status=%s\n",
|
||||
stored,static_cast<unsigned long long>(handlerCount),
|
||||
static_cast<unsigned long long>(expandedTicks),
|
||||
static_cast<unsigned long long>(LostSamples.load(std::memory_order_relaxed)),
|
||||
static_cast<unsigned long long>(TimerOverruns.load(std::memory_order_relaxed)),
|
||||
registeredThreadCount,PeriodMicroseconds,
|
||||
static_cast<double>(profiledThreadCpuNanoseconds)/1.0e9,
|
||||
static_cast<double>(userNs+systemNs)/1.0e9,
|
||||
static_cast<double>(handlerNs)/1.0e6,
|
||||
profiledThreadCpuNanoseconds?100.0*static_cast<double>(handlerNs)
|
||||
/static_cast<double>(profiledThreadCpuNanoseconds):0.0,
|
||||
expandedTicks?static_cast<double>(profiledThreadCpuNanoseconds)
|
||||
/static_cast<double>(expandedTicks)/1000.0:0.0,
|
||||
stored>0&&handlerCount>0?"PASS":"NO_SAMPLES");
|
||||
for(std::size_t index=0;index<registeredThreadCount;++index)
|
||||
std::printf("[L77ThreadTimer] index=%zu tid=%d cpu_ms=%.3f samples=%llu\n",index,
|
||||
static_cast<int>(ThreadIds[index]),
|
||||
static_cast<double>(ThreadProfileCpuNanoseconds[index])/1.0e6,
|
||||
static_cast<unsigned long long>(ThreadSampleCounts[index].load(
|
||||
std::memory_order_relaxed)));
|
||||
for(std::uint32_t index=0;index<stored;++index)
|
||||
{
|
||||
const FSample& sample=Samples[index];
|
||||
const auto route=static_cast<ERoute>(sample.route);
|
||||
const auto stage=static_cast<EStage>(sample.stage);
|
||||
const auto phase=static_cast<EPhase>(sample.phase);
|
||||
std::printf("[L77Sample] pc=0x%" PRIxPTR " lod=%u stage=%s route=%s phase=%s halo=%u weight=%u\n",
|
||||
sample.pc,static_cast<unsigned>(sample.lod),StageName(stage),
|
||||
RouteName(route),PhaseName(phase),static_cast<unsigned>(sample.halo),
|
||||
static_cast<unsigned>(sample.weight));
|
||||
}
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -49,6 +49,7 @@ namespace
|
||||
bool ProfileLodWork=false;
|
||||
bool ProfilePassageComposer=false;
|
||||
bool InstrumentTileDrain=false;
|
||||
bool Linux77Profile=false;
|
||||
bool ExactMidpointDensityMemo=false;
|
||||
bool ActiveLaneBatching=false;
|
||||
bool ParallelSignEdgeRepair=false;
|
||||
@@ -1442,6 +1443,7 @@ namespace
|
||||
" --audit-uniform-block-proofs (fill and check every interval-certified block)\n"
|
||||
" --profile-lod-work (report per-LOD density sample counts)\n"
|
||||
" --instrument-tile-drain (opt-in CpuTileTimeline and per-tile stages/counters)\n"
|
||||
" --linux77-profile (Linux-only SIGPROF stack and route census)\n"
|
||||
" --profile-passage-composer (emit per-tile passage work counters)\n"
|
||||
" --exact-midpoint-density-memo (exact-input midpoint query memo A/B)\n"
|
||||
" --active-lane-batching --proof-block-cells=N (proof-masked density fill A/B)\n"
|
||||
@@ -1500,6 +1502,8 @@ namespace
|
||||
else if(arg=="--audit-uniform-block-proofs")options.AuditUniformBlockProofs=true;
|
||||
else if(arg=="--profile-lod-work")options.ProfileLodWork=true;
|
||||
else if(arg=="--instrument-tile-drain")options.InstrumentTileDrain=true;
|
||||
else if(arg=="--linux77-profile")
|
||||
{options.Linux77Profile=true;options.ProfileLodWork=true;}
|
||||
else if(arg=="--profile-passage-composer")
|
||||
{options.ProfilePassageComposer=true;options.InstrumentTileDrain=true;}
|
||||
else if(arg=="--exact-midpoint-density-memo")options.ExactMidpointDensityMemo=true;
|
||||
@@ -2224,5 +2228,5 @@ int main(int argc,char** argv)
|
||||
?options.ParallelSignEdgeRepair:options.FloatingIslandsRecipe,
|
||||
options.AuditNativeLattice,options.LatticeOrphanControl,
|
||||
options.CaptureSkipWalkability,options.ProfilePassageComposer,
|
||||
options.StreamHashAllowUniformSkips);
|
||||
options.StreamHashAllowUniformSkips,options.Linux77Profile);
|
||||
}
|
||||
@@ -1873,7 +1873,8 @@ namespace StandaloneStreamBenchmark
|
||||
bool bActiveLaneBatching=false,int32 ProofBlockCells=8,
|
||||
bool bParallelSignEdgeRepair=false,bool bAuditNativeLattice=false,
|
||||
bool bLatticeOrphanControl=false,bool bCaptureSkipWalkability=false,
|
||||
bool bProfilePassageComposer=false,bool bAllowUniformSkipsWhenHashing=false)
|
||||
bool bProfilePassageComposer=false,bool bAllowUniformSkipsWhenHashing=false,
|
||||
bool bLinux77Profile=false)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -2039,11 +2040,12 @@ namespace StandaloneStreamBenchmark
|
||||
bAllowUniformSkipsWhenHashing);
|
||||
Streamer.SetUniformBlockProofAuditEnabled(bAuditUniformBlockProofs);
|
||||
Streamer.SetTileDrainInstrumentationEnabled(bInstrumentTileDrain);
|
||||
Streamer.SetLinux77ProfileEnabled(bLinux77Profile);
|
||||
Streamer.SetPassageComposerProfilingEnabled(bProfilePassageComposer);
|
||||
Streamer.SetExactMidpointDensityMemoEnabled(bExactMidpointDensityMemo);
|
||||
Streamer.SetActiveLaneBatchingEnabled(bActiveLaneBatching);
|
||||
Streamer.SetProofBlockCells(ProofBlockCells);
|
||||
if(bScreenSpaceGate||bReportLodWorkProfile||bInstrumentTileDrain)
|
||||
if(bScreenSpaceGate||bReportLodWorkProfile||bInstrumentTileDrain||bLinux77Profile)
|
||||
Streamer.SetMesherStageProfilingEnabled(true);
|
||||
if(bInstrumentTileDrain)
|
||||
{
|
||||
@@ -2053,6 +2055,7 @@ namespace StandaloneStreamBenchmark
|
||||
bExactMidpointDensityMemo?"on":"off",
|
||||
bParallelSignEdgeRepair?"on":"off");
|
||||
}
|
||||
if(bLinux77Profile)VoxelForgeCore::Linux77Profiler::Start(1000);
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate EarlyUpdate=Streamer.StartWithDesired(ShapeFreeDesired);
|
||||
if(EarlyUpdate.DesiredTiles!=static_cast<int32>(ShapeFreeDesired.Tiles.size()))
|
||||
@@ -5051,6 +5054,13 @@ namespace StandaloneStreamBenchmark
|
||||
std::unique_ptr<FFarLODProfile> FarLODProfile;
|
||||
if(bReportLodWorkProfile)FarLODProfile=std::make_unique<FFarLODProfile>();
|
||||
std::array<std::uint64_t,9> DensitySamplesByLOD{};
|
||||
using FL77Route=VoxelForgeCore::Linux77Profiler::ERoute;
|
||||
using FL77Stage=VoxelForgeCore::Linux77Profiler::EStage;
|
||||
std::array<std::array<std::uint64_t,
|
||||
VoxelForgeCore::Linux77Profiler::StageCount
|
||||
*VoxelForgeCore::Linux77Profiler::RouteCount*2>,9> L77DispatchByLOD{};
|
||||
std::array<std::uint64_t,9> L77GridInteriorByLOD{},L77GridHaloByLOD{};
|
||||
std::array<double,9> L77LODReadySeconds{};
|
||||
std::array<std::uint64_t,9> SkirtTrianglesByLOD{};
|
||||
std::array<std::uint64_t,9> TileCountByLOD{};
|
||||
std::array<std::uint64_t,9> ClassifiedAirTilesByLOD{},ClassifiedSolidTilesByLOD{};
|
||||
@@ -5117,8 +5127,19 @@ namespace StandaloneStreamBenchmark
|
||||
if(Tile.Key.Level>=0&&Tile.Key.Level<static_cast<int32>(TileCountByLOD.size()))
|
||||
{
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
++TileCountByLOD[LOD];
|
||||
const auto& TileDiagnostics=Tile.CoreDiagnostics;
|
||||
++TileCountByLOD[LOD];
|
||||
if(bLinux77Profile)
|
||||
{
|
||||
const auto& D=TileDiagnostics;
|
||||
for(std::size_t Counter=0;Counter<D.linux77DispatchCounters.size();++Counter)
|
||||
L77DispatchByLOD[LOD][Counter]+=D.linux77DispatchCounters[Counter];
|
||||
L77GridInteriorByLOD[LOD]+=D.linux77GridInteriorSamples;
|
||||
L77GridHaloByLOD[LOD]+=D.linux77GridHaloSamples;
|
||||
if(TileCountByLOD[LOD]==ExpectedTilesByLOD[LOD])
|
||||
L77LODReadySeconds[LOD]=std::chrono::duration<double>(
|
||||
Clock::now()-ColdStart).count();
|
||||
}
|
||||
if(TileDiagnostics.tileWasClassified)
|
||||
{
|
||||
switch(TileDiagnostics.tileClass)
|
||||
@@ -5500,6 +5521,36 @@ namespace StandaloneStreamBenchmark
|
||||
MovingMcOtherMsByLOD[LOD]);
|
||||
}
|
||||
}
|
||||
if(bLinux77Profile)
|
||||
{
|
||||
VoxelForgeCore::Linux77Profiler::StopAndPrint();
|
||||
for(std::size_t LOD=0;LOD<TileCountByLOD.size();++LOD)
|
||||
{
|
||||
if(ExpectedTilesByLOD[LOD]==0)continue;
|
||||
std::printf("[WorldAssemblerL77LODReady] lod=%zu tiles=%llu cold_ready_s=%.6f\n",
|
||||
LOD,static_cast<unsigned long long>(ExpectedTilesByLOD[LOD]),
|
||||
L77LODReadySeconds[LOD]);
|
||||
std::printf("[WorldAssemblerL77GridSamples] lod=%zu interior=%llu halo=%llu\n",
|
||||
LOD,static_cast<unsigned long long>(L77GridInteriorByLOD[LOD]),
|
||||
static_cast<unsigned long long>(L77GridHaloByLOD[LOD]));
|
||||
for(std::size_t Stage=0;Stage<VoxelForgeCore::Linux77Profiler::StageCount;++Stage)
|
||||
for(std::size_t Route=0;Route<VoxelForgeCore::Linux77Profiler::RouteCount;++Route)
|
||||
{
|
||||
const std::size_t Index=(Stage*VoxelForgeCore::Linux77Profiler::RouteCount
|
||||
+Route)*2;
|
||||
const std::uint64_t Calls=L77DispatchByLOD[LOD][Index];
|
||||
const std::uint64_t Points=L77DispatchByLOD[LOD][Index+1];
|
||||
if(Calls==0&&Points==0)continue;
|
||||
std::printf("[WorldAssemblerL77Dispatch] lod=%zu stage=%s route=%s calls=%llu points=%llu\n",
|
||||
LOD,VoxelForgeCore::Linux77Profiler::StageName(
|
||||
static_cast<FL77Stage>(Stage)),
|
||||
VoxelForgeCore::Linux77Profiler::RouteName(
|
||||
static_cast<FL77Route>(Route)),
|
||||
static_cast<unsigned long long>(Calls),
|
||||
static_cast<unsigned long long>(Points));
|
||||
}
|
||||
}
|
||||
}
|
||||
Streamer.Stop();
|
||||
Streamer.DumpColdTimelineTrace();
|
||||
bool bScreenSpaceGatePassing=true;
|
||||
|
||||
@@ -714,6 +714,8 @@ public:
|
||||
{bProfileMesherStages=Enabled;}
|
||||
void SetTileDrainInstrumentationEnabled(bool Enabled)
|
||||
{bInstrumentTileDrain=Enabled;}
|
||||
void SetLinux77ProfileEnabled(bool Enabled)
|
||||
{bLinux77Profile=Enabled;}
|
||||
void SetPassageComposerProfilingEnabled(bool Enabled)
|
||||
{bProfilePassageComposer=Enabled;}
|
||||
void SetExactMidpointDensityMemoEnabled(bool Enabled)
|
||||
@@ -902,6 +904,7 @@ private:
|
||||
Options.profileMesherStages=(bProfileMesherStages||bInstrumentTileDrain)&&!ForceGenerate;
|
||||
Options.profilePassageComposer=bProfilePassageComposer;
|
||||
Options.instrumentTileDrain=bInstrumentTileDrain&&!ForceGenerate;
|
||||
Options.linux77Profile=bLinux77Profile;
|
||||
Options.exactMidpointDensityMemo=bExactMidpointDensityMemo&&!ForceGenerate;
|
||||
Options.activeLaneBatching=bActiveLaneBatching&&!ForceGenerate;
|
||||
Options.proofBlockCells=static_cast<std::uint32_t>(ProofBlockCells);
|
||||
@@ -1195,6 +1198,7 @@ private:
|
||||
int32 Lod8HorizonInteriorStepVoxels=0;
|
||||
bool bProfileMesherStages=false;
|
||||
bool bInstrumentTileDrain=false;
|
||||
bool bLinux77Profile=false;
|
||||
bool bProfilePassageComposer=false;
|
||||
bool bExactMidpointDensityMemo=false;
|
||||
bool bActiveLaneBatching=false;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "VoxelHybridSharpMesher.h"
|
||||
#include "MarchingCubesTables.h"
|
||||
#include "StandaloneShapeWorld.h"
|
||||
#include "VoxelForgeCore/Linux77Profiler.h"
|
||||
#include "VoxelDensityProfile.h"
|
||||
#if WITH_EDITOR
|
||||
#include "VoxelRedefinedDensityField.h"
|
||||
@@ -1047,6 +1048,8 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
// Bande de strate : seules les rangées Z réellement lues (cellules CzLo..CzHi + marges de
|
||||
// gradient) sont échantillonnées — le reste du tampon reste non initialisé et non lu.
|
||||
const uint64 StageDensityGridStartCycles=OutStageTimings?FPlatformTime::Cycles64():0;
|
||||
VoxelForgeCore::Linux77Profiler::FScopedStage l77GridFillStage(
|
||||
VoxelForgeCore::Linux77Profiler::EStage::GridFill);
|
||||
VoxelDensityProfile::FScopedTimer MesherDensityGridTimer(
|
||||
VoxelDensityProfile::EBucket::MesherDensityGrid);
|
||||
#if WITH_EDITOR
|
||||
@@ -1264,6 +1267,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const int32 SampleZ=OriginVoxels.Z+gz*Step;
|
||||
const bool bBoundarySample=gx<0||gx>GridDim||gy<0||gy>GridDim
|
||||
||gz<0||gz>GridDim;
|
||||
VoxelForgeCore::Linux77Profiler::FScopedHalo l77Halo(bBoundarySample);
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller islandCaller(
|
||||
bBoundarySample
|
||||
?VoxelForgeCore::floating_island_detail::EIslandFieldCaller::Boundary
|
||||
@@ -1272,6 +1276,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
?ShapeWorldContext->DensityAtLattice(SampleX,SampleY,SampleZ)
|
||||
:Generator->GetDensityAtLattice(SampleX,SampleY,SampleZ,
|
||||
bCapturePreEditGrid ? &PreEditDensity : nullptr);
|
||||
if(OutStageTimings)
|
||||
{
|
||||
if(bBoundarySample)++OutStageTimings->DensityGridHaloSamples;
|
||||
else ++OutStageTimings->DensityGridInteriorSamples;
|
||||
}
|
||||
if (bCapturePreEditGrid)
|
||||
{
|
||||
OutPreEditDensityGrid->Samples[LocalIndex] = PreEditDensity;
|
||||
@@ -1304,6 +1313,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
float Densities[8]{};
|
||||
const bool bBoundarySample=GX0<0||GX0+BatchCount-1>GridDim
|
||||
||gy<0||gy>GridDim||gz<0||gz>GridDim;
|
||||
VoxelForgeCore::Linux77Profiler::FScopedHalo l77Halo(bBoundarySample);
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller islandCaller(
|
||||
bBoundarySample
|
||||
?VoxelForgeCore::floating_island_detail::EIslandFieldCaller::Boundary
|
||||
@@ -1314,6 +1324,14 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
++OutStageTimings->X8DensityCalls;
|
||||
OutStageTimings->X8ActiveLanes+=static_cast<uint64>(BatchCount);
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 SampleGX=GX0+Lane;
|
||||
if(SampleGX<0||SampleGX>GridDim||gy<0||gy>GridDim
|
||||
||gz<0||gz>GridDim)
|
||||
++OutStageTimings->DensityGridHaloSamples;
|
||||
else ++OutStageTimings->DensityGridInteriorSamples;
|
||||
}
|
||||
}
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
@@ -1350,12 +1368,24 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
float Densities[CHUNK_SIZE+3]{};
|
||||
const bool bBoundarySample=gx<0||gx>GridDim||gy<0||gy>GridDim
|
||||
||GzLo<0||GzHi>GridDim;
|
||||
VoxelForgeCore::Linux77Profiler::FScopedHalo l77Halo(bBoundarySample);
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller islandCaller(
|
||||
bBoundarySample
|
||||
?VoxelForgeCore::floating_island_detail::EIslandFieldCaller::Boundary
|
||||
:VoxelForgeCore::floating_island_detail::EIslandFieldCaller::GridFill);
|
||||
ActiveShapeWorld->DensityAtLatticeZRun(SampleX,SampleY,
|
||||
OriginVoxels.Z+GzLo*Step,Step,BatchCount,Densities);
|
||||
if(OutStageTimings)
|
||||
{
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 LocalGz=GzLo+Lane;
|
||||
if(gx<0||gx>GridDim||gy<0||gy>GridDim
|
||||
||LocalGz<0||LocalGz>GridDim)
|
||||
++OutStageTimings->DensityGridHaloSamples;
|
||||
else ++OutStageTimings->DensityGridInteriorSamples;
|
||||
}
|
||||
}
|
||||
for(int32 Lane=0;Lane<BatchCount;++Lane)
|
||||
{
|
||||
const int32 LocalGz=GzLo+Lane;
|
||||
@@ -1505,6 +1535,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const int32 SampleY=OriginVoxels.Y+gy*Step;
|
||||
const int32 SampleZ=OriginVoxels.Z+gz*Step;
|
||||
const bool bBoundarySample=gx<0||gx>GridDim||gy<0||gy>GridDim;
|
||||
VoxelForgeCore::Linux77Profiler::FScopedHalo l77Halo(bBoundarySample);
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller islandCaller(
|
||||
bBoundarySample
|
||||
?VoxelForgeCore::floating_island_detail::EIslandFieldCaller::Boundary
|
||||
@@ -1527,6 +1558,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
}
|
||||
MesherDensityGridTimer.End();
|
||||
l77GridFillStage.Restore();
|
||||
VoxelForgeCore::Linux77Profiler::FScopedStage l77MarchingCubesStage(
|
||||
VoxelForgeCore::Linux77Profiler::EStage::MarchingCubes);
|
||||
if(OutStageTimings)
|
||||
OutStageTimings->DensityGridMilliseconds=static_cast<double>(
|
||||
FPlatformTime::Cycles64()-StageDensityGridStartCycles)
|
||||
@@ -2119,6 +2153,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
const int32 CapSurfaceTriangleCount=CapTris.Num()/3;
|
||||
TArray<uint8> GroundSkirtFaceMasks,CapSkirtFaceMasks;
|
||||
const uint64 SkirtStageStartCycles=OutStageTimings?FPlatformTime::Cycles64():0;
|
||||
l77MarchingCubesStage.Restore();
|
||||
VoxelForgeCore::Linux77Profiler::FScopedStage l77SkirtStage(
|
||||
VoxelForgeCore::Linux77Profiler::EStage::Skirt);
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller skirtFieldCaller(
|
||||
VoxelForgeCore::floating_island_detail::EIslandFieldCaller::Skirt);
|
||||
if (bGenerateSkirts && (GroundTris.Num() + CapTris.Num()) > 0)
|
||||
@@ -2382,6 +2419,9 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
OutStageTimings->SkirtMilliseconds=static_cast<double>(
|
||||
FPlatformTime::Cycles64()-SkirtStageStartCycles)
|
||||
*FPlatformTime::GetSecondsPerCycle64()*1000.0;
|
||||
l77SkirtStage.Restore();
|
||||
VoxelForgeCore::Linux77Profiler::FScopedStage l77MesherFinalStage(
|
||||
VoxelForgeCore::Linux77Profiler::EStage::MarchingCubes);
|
||||
|
||||
// F17 — concatène sol PUIS sky-cap : un run d'indices contigu par polygroup (voir
|
||||
// FVoxelMeshData::NumCeilingTriangles). Mêmes triangles qu'avant, seul l'ordre change.
|
||||
|
||||
@@ -59,6 +59,8 @@ struct FVoxelMarchingCubesStageTimings
|
||||
uint64 GradientMemoHits = 0;
|
||||
uint64 X8DensityCalls = 0;
|
||||
uint64 X8ActiveLanes = 0;
|
||||
uint64 DensityGridInteriorSamples = 0;
|
||||
uint64 DensityGridHaloSamples = 0;
|
||||
uint64 CandidateCells = 0;
|
||||
uint64 ZBandRejectedCells = 0;
|
||||
uint64 CellClassificationCalls = 0;
|
||||
|
||||
Reference in new issue
Block a user