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:
Fr0zkaandClaude Opus 5.5 committed 2026-10-27 17:55:39 +01:00
1 parent 68cba5a51d
commit 5e091d254c
14 files changed
+1205 -8

No files matched your search

+257
View File
@@ -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);
}
+54 -3
View File
@@ -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;
+4
View File
@@ -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;