perf(core): FI LOD0 attribution, exact Z-inner traversal; fix a mesh gap at the passage shaft foot (L57); R26
Sampled cycle attribution of FI LOD0 field work: grid fill 83%, boundary 13%, skirts 4%; island roots, columns and helix dominate, the column cache path is real but secondary (no thrashing). FI-only Z-inner traversal at LOD0 (same lattice samples, exact) cuts FI whole view 5.35 -> 5.03 s and moving p50 by 12%; global Z-inner and <=4-island culling were measured and rejected. The meshing band now includes the planned passage support column's conservative bounds, closing a real gap where the arrival shaft meets the floor (WorldAssemblyEvaluatorVersion 208 -> 209). fast/medium/full PASS, Clang and GCC. Research R26 (self-checked): native lattice (maze/shaft) generator design. 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
4e64b75044
commit
1e3e04bbac
14 files changed
+777
-48
No files matched your search
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
|
||||
#include <intrin.h>
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace VoxelForgeCoreShared
|
||||
{
|
||||
inline std::uint64_t ReadCpuCycles() noexcept
|
||||
{
|
||||
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
|
||||
_mm_lfence();
|
||||
const std::uint64_t value=__rdtsc();
|
||||
_mm_lfence();
|
||||
return value;
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
_mm_lfence();
|
||||
const std::uint64_t value=__rdtsc();
|
||||
_mm_lfence();
|
||||
return value;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline std::uint64_t CpuCycleReadPairOverhead() noexcept
|
||||
{
|
||||
static const std::uint64_t overhead=[]()
|
||||
{
|
||||
std::uint64_t minimum=std::numeric_limits<std::uint64_t>::max();
|
||||
for(unsigned i=0;i<64;++i)
|
||||
{
|
||||
const std::uint64_t start=ReadCpuCycles();
|
||||
const std::uint64_t end=ReadCpuCycles();
|
||||
minimum=std::min(minimum,end-start);
|
||||
}
|
||||
return minimum==std::numeric_limits<std::uint64_t>::max()?0:minimum;
|
||||
}();
|
||||
return overhead;
|
||||
}
|
||||
|
||||
inline std::uint64_t AdjustedCpuCycleDelta(std::uint64_t start,
|
||||
std::uint64_t end) noexcept
|
||||
{
|
||||
const std::uint64_t delta=end-start;
|
||||
const std::uint64_t overhead=CpuCycleReadPairOverhead();
|
||||
return delta>overhead?delta-overhead:0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
|
||||
#include "VoxelForgeCore/CpuCycleCounter.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace VoxelForgeCore::floating_island_detail
|
||||
{
|
||||
enum class EIslandFieldCaller : std::uint8_t
|
||||
{
|
||||
GridFill=0,Skirt=1,Boundary=2,PassageFilter=3,Other=4,Count=5
|
||||
};
|
||||
|
||||
enum class EIslandAttributionBucket : std::uint8_t
|
||||
{
|
||||
Field=0,Island=1,Root=2,Connector=3,Helix=4,Column=5,
|
||||
LocalProbe=6,SharedCache=7,MouthFilter=8,Boundary=9,CallerGrid=10,
|
||||
CallerSkirt=11,CallerBoundary=12,CallerPassageFilter=13,CallerOther=14,
|
||||
Count=15
|
||||
};
|
||||
|
||||
struct FIslandFieldAttributionInstrumentation
|
||||
{
|
||||
static constexpr std::uint64_t SampleMask=255;
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandFieldCaller::Count)> sampledFieldCalls{};
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandFieldCaller::Count)> fieldCycles{};
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandFieldCaller::Count)> sampledNativeCalls{};
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandFieldCaller::Count)> nativeCycles{};
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandAttributionBucket::Count)> exclusiveCycles{};
|
||||
std::array<std::uint64_t,static_cast<std::size_t>(EIslandAttributionBucket::Count)> scopeSamples{};
|
||||
std::uint64_t traceSampledMisses=0,traceSeenBefore=0,traceEvictions=0,
|
||||
traceReusedEvictions=0;
|
||||
std::array<std::uint64_t,8> traceSet{};
|
||||
std::array<std::int64_t,8> traceX{},traceY{},traceEvictedX{},traceEvictedY{};
|
||||
std::array<std::uint64_t,8> traceIsland{},traceEvictedIsland{};
|
||||
std::array<std::uint8_t,8> traceFlags{};
|
||||
std::size_t traceCount=0;
|
||||
};
|
||||
|
||||
inline FIslandFieldAttributionInstrumentation*& ActiveIslandFieldAttribution() noexcept
|
||||
{
|
||||
static thread_local FIslandFieldAttributionInstrumentation* active=nullptr;
|
||||
return active;
|
||||
}
|
||||
|
||||
inline FIslandFieldAttributionInstrumentation* SetActiveIslandFieldAttribution(
|
||||
FIslandFieldAttributionInstrumentation* value) noexcept
|
||||
{
|
||||
auto* previous=ActiveIslandFieldAttribution();
|
||||
ActiveIslandFieldAttribution()=value;
|
||||
return previous;
|
||||
}
|
||||
|
||||
inline EIslandFieldCaller& ActiveIslandFieldCaller() noexcept
|
||||
{
|
||||
static thread_local EIslandFieldCaller caller=EIslandFieldCaller::Other;
|
||||
return caller;
|
||||
}
|
||||
|
||||
struct FScopedIslandFieldCaller
|
||||
{
|
||||
explicit FScopedIslandFieldCaller(EIslandFieldCaller value) noexcept
|
||||
:previous(ActiveIslandFieldCaller()),enabled(ActiveIslandFieldAttribution()!=nullptr)
|
||||
{if(enabled)ActiveIslandFieldCaller()=value;}
|
||||
~FScopedIslandFieldCaller(){if(enabled)ActiveIslandFieldCaller()=previous;}
|
||||
EIslandFieldCaller previous;
|
||||
bool enabled=false;
|
||||
};
|
||||
|
||||
inline std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandAttributionBucket::Count)>&
|
||||
IslandComponentSampleSequence() noexcept
|
||||
{
|
||||
static thread_local std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandAttributionBucket::Count)> sequence{};
|
||||
return sequence;
|
||||
}
|
||||
|
||||
inline std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandFieldCaller::Count)>&
|
||||
IslandFieldSampleSequence() noexcept
|
||||
{
|
||||
static thread_local std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandFieldCaller::Count)> sequence{};
|
||||
return sequence;
|
||||
}
|
||||
|
||||
inline std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandFieldCaller::Count)>&
|
||||
IslandNativeSampleSequence() noexcept
|
||||
{
|
||||
static thread_local std::array<std::uint64_t,
|
||||
static_cast<std::size_t>(EIslandFieldCaller::Count)> sequence{};
|
||||
return sequence;
|
||||
}
|
||||
|
||||
struct FScopedIslandAttributionTimer
|
||||
{
|
||||
explicit FScopedIslandAttributionTimer(EIslandAttributionBucket bucket) noexcept
|
||||
:metrics(ActiveIslandFieldAttribution()),bucketIndex(static_cast<std::size_t>(bucket))
|
||||
{
|
||||
if(!metrics)return;
|
||||
if(bucketIndex==static_cast<std::size_t>(EIslandAttributionBucket::Field))
|
||||
{
|
||||
const auto callerIndex=static_cast<std::size_t>(ActiveIslandFieldCaller());
|
||||
const auto calls=++IslandFieldSampleSequence()[callerIndex];
|
||||
const std::uint64_t phase=(callerIndex*43u+17u)
|
||||
&FIslandFieldAttributionInstrumentation::SampleMask;
|
||||
sampled=(((calls+phase)&FIslandFieldAttributionInstrumentation::SampleMask)==0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& calls=IslandComponentSampleSequence()[bucketIndex];
|
||||
++calls;
|
||||
const std::uint64_t phase=(static_cast<std::uint64_t>(bucketIndex)*29u+17u)
|
||||
&FIslandFieldAttributionInstrumentation::SampleMask;
|
||||
sampled=(((calls+phase)&FIslandFieldAttributionInstrumentation::SampleMask)==0);
|
||||
}
|
||||
if(sampled)start=VoxelForgeCoreShared::ReadCpuCycles();
|
||||
}
|
||||
~FScopedIslandAttributionTimer(){Stop();}
|
||||
std::uint64_t Stop() noexcept
|
||||
{
|
||||
if(!sampled)return 0;
|
||||
sampled=false;
|
||||
const std::uint64_t raw=VoxelForgeCoreShared::ReadCpuCycles()-start;
|
||||
const std::uint64_t overhead=VoxelForgeCoreShared::CpuCycleReadPairOverhead();
|
||||
const std::uint64_t cycles=raw>overhead?raw-overhead:0;
|
||||
metrics->exclusiveCycles[bucketIndex]+=cycles;
|
||||
++metrics->scopeSamples[bucketIndex];
|
||||
if(bucketIndex==static_cast<std::size_t>(EIslandAttributionBucket::Field))
|
||||
{
|
||||
const std::size_t callerIndex=static_cast<std::size_t>(ActiveIslandFieldCaller());
|
||||
metrics->fieldCycles[callerIndex]+=cycles;
|
||||
++metrics->sampledFieldCalls[callerIndex];
|
||||
}
|
||||
return cycles;
|
||||
}
|
||||
FIslandFieldAttributionInstrumentation* metrics=nullptr;
|
||||
std::size_t bucketIndex=0;
|
||||
std::uint64_t start=0;
|
||||
bool sampled=false;
|
||||
};
|
||||
|
||||
struct FScopedIslandAttributionSample
|
||||
{
|
||||
explicit FScopedIslandAttributionSample() noexcept
|
||||
{
|
||||
auto* attribution=ActiveIslandFieldAttribution();
|
||||
if(!attribution)return;
|
||||
const auto caller=ActiveIslandFieldCaller();
|
||||
const std::size_t callerIndex=static_cast<std::size_t>(caller);
|
||||
const std::uint64_t sequence=++IslandNativeSampleSequence()[callerIndex];
|
||||
const std::uint64_t phase=(callerIndex*43u+93u)
|
||||
&FIslandFieldAttributionInstrumentation::SampleMask;
|
||||
if(((sequence+phase)&FIslandFieldAttributionInstrumentation::SampleMask)!=0)return;
|
||||
sampledAttribution=attribution;
|
||||
sampledCaller=caller;
|
||||
sampled=true;
|
||||
start=VoxelForgeCoreShared::ReadCpuCycles();
|
||||
++attribution->sampledNativeCalls[callerIndex];
|
||||
}
|
||||
~FScopedIslandAttributionSample()
|
||||
{
|
||||
if(!sampled)return;
|
||||
const std::uint64_t raw=VoxelForgeCoreShared::ReadCpuCycles()-start;
|
||||
const std::uint64_t overhead=VoxelForgeCoreShared::CpuCycleReadPairOverhead();
|
||||
const std::uint64_t duration=raw>overhead?raw-overhead:0;
|
||||
const std::size_t callerIndex=static_cast<std::size_t>(sampledCaller);
|
||||
sampledAttribution->nativeCycles[callerIndex]+=duration;
|
||||
}
|
||||
FIslandFieldAttributionInstrumentation* sampledAttribution=nullptr;
|
||||
EIslandFieldCaller sampledCaller=EIslandFieldCaller::Other;
|
||||
std::uint64_t start=0;
|
||||
bool sampled=false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "VoxelForgeCore/CpuCycleCounter.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
@@ -20,6 +22,14 @@ namespace VoxelForgeCoreShared
|
||||
std::uint64_t computes = 0;
|
||||
std::uint64_t conflictReplacements = 0;
|
||||
std::uint64_t lockWaitNanoseconds = 0;
|
||||
std::uint64_t sampledInitialReadCycles = 0;
|
||||
std::uint64_t sampledInitialReads = 0;
|
||||
std::uint64_t sampledLockedReadCycles = 0;
|
||||
std::uint64_t sampledLockedReads = 0;
|
||||
std::uint64_t sampledLockCycles = 0;
|
||||
std::uint64_t sampledLocks = 0;
|
||||
std::uint64_t sampledBuildCycles = 0;
|
||||
std::uint64_t sampledBuilds = 0;
|
||||
};
|
||||
|
||||
// Exact input identity for a pure XY column value. LOD is deliberately not part of this key:
|
||||
@@ -79,17 +89,24 @@ namespace VoxelForgeCoreShared
|
||||
|
||||
struct FSpinGuard
|
||||
{
|
||||
explicit FSpinGuard(std::atomic_flag& value, FColumnCacheMetrics* metrics)
|
||||
explicit FSpinGuard(std::atomic_flag& value, FColumnCacheMetrics* metrics,
|
||||
bool sampleCycles)
|
||||
: Flag(value), Metrics(metrics)
|
||||
{
|
||||
const auto started = Metrics ? std::chrono::steady_clock::now()
|
||||
: std::chrono::steady_clock::time_point{};
|
||||
const std::uint64_t cycleStart=sampleCycles?ReadCpuCycles():0;
|
||||
while (Flag.test_and_set(std::memory_order_acquire))
|
||||
std::this_thread::yield();
|
||||
if (Metrics)
|
||||
Metrics->lockWaitNanoseconds += static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now() - started).count());
|
||||
if(sampleCycles)
|
||||
{
|
||||
Metrics->sampledLockCycles+=AdjustedCpuCycleDelta(cycleStart,ReadCpuCycles());
|
||||
++Metrics->sampledLocks;
|
||||
}
|
||||
}
|
||||
~FSpinGuard() { Flag.clear(std::memory_order_release); }
|
||||
std::atomic_flag& Flag;
|
||||
@@ -120,21 +137,45 @@ namespace VoxelForgeCoreShared
|
||||
const std::size_t setIndex = static_cast<std::size_t>(tag) & (SetCount - 1);
|
||||
FSet& set = Sets[setIndex];
|
||||
TValue value{};
|
||||
if (TryReadSet(set, keyWords, tag, value))
|
||||
static thread_local std::uint64_t timingSequence=0;
|
||||
const bool sampleCycles=metrics&&((++timingSequence&127u)==0);
|
||||
const std::uint64_t initialReadStart=sampleCycles?ReadCpuCycles():0;
|
||||
const bool initialHit=TryReadSet(set, keyWords, tag, value);
|
||||
if(sampleCycles)
|
||||
{
|
||||
metrics->sampledInitialReadCycles+=AdjustedCpuCycleDelta(
|
||||
initialReadStart,ReadCpuCycles());
|
||||
++metrics->sampledInitialReads;
|
||||
}
|
||||
if (initialHit)
|
||||
{
|
||||
if (metrics) ++metrics->hits;
|
||||
return value;
|
||||
}
|
||||
if (metrics) ++metrics->misses;
|
||||
|
||||
FSpinGuard lock(set.Writer, metrics);
|
||||
if (TryReadSet(set, keyWords, tag, value))
|
||||
FSpinGuard lock(set.Writer, metrics,sampleCycles);
|
||||
const std::uint64_t lockedReadStart=sampleCycles?ReadCpuCycles():0;
|
||||
const bool lockedHit=TryReadSet(set, keyWords, tag, value);
|
||||
if(sampleCycles)
|
||||
{
|
||||
metrics->sampledLockedReadCycles+=AdjustedCpuCycleDelta(
|
||||
lockedReadStart,ReadCpuCycles());
|
||||
++metrics->sampledLockedReads;
|
||||
}
|
||||
if (lockedHit)
|
||||
{
|
||||
if (metrics) ++metrics->hits;
|
||||
return value;
|
||||
}
|
||||
|
||||
const std::uint64_t buildStart=sampleCycles?ReadCpuCycles():0;
|
||||
value = std::forward<TFactory>(factory)();
|
||||
if(sampleCycles)
|
||||
{
|
||||
metrics->sampledBuildCycles+=AdjustedCpuCycleDelta(buildStart,ReadCpuCycles());
|
||||
++metrics->sampledBuilds;
|
||||
}
|
||||
computed = true;
|
||||
if (metrics) ++metrics->computes;
|
||||
FValueWords valueWords{};
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace VoxelForgeCore
|
||||
// version 169 sank every connector 2 m; it left four FI route support probes low.
|
||||
// version 170 sinks only connectors touching the anchored root by 1 m.
|
||||
// Version 208 keeps the crater tread scoped to style-zero FI mouths.
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 208;
|
||||
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 209;
|
||||
|
||||
enum class FieldChannel : std::uint32_t
|
||||
{
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "VoxelForgeCore/VoxelForgeCore.h"
|
||||
#include "VoxelForgeCore/ThreadLocalStorage.h"
|
||||
#include "VoxelForgeCore/SharedColumnCache.h"
|
||||
#include "VoxelForgeCore/FloatingIslandInstrumentation.h"
|
||||
|
||||
// Keep Core's expression-level FMA behavior fixed regardless of the toolchain setting.
|
||||
// Clang's "fast" mode ignores the pragma, so Core's runtime self-test rejects that build.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "VoxelForgeCore/ThreadLocalStorage.h"
|
||||
#include "VoxelForgeCore/CpuCycleCounter.h"
|
||||
#include "VoxelForgeCore/FloatingIslandInstrumentation.h"
|
||||
#include "VoxelForgeCore/SharedColumnCache.h"
|
||||
#include "Json.h"
|
||||
#include "VoxelForgeCore/VoxelForgeCore.h"
|
||||
|
||||
@@ -474,7 +477,10 @@ namespace floating_island_detail
|
||||
struct FIslandColumnCacheInstrumentation
|
||||
{
|
||||
std::uint64_t localHits=0,localMisses=0,localConflictReplacements=0;
|
||||
std::uint64_t localProbeCycles=0,localProbeSamples=0;
|
||||
std::uint64_t tileStartCycles=0,tileCycles=0;
|
||||
VoxelForgeCoreShared::FColumnCacheMetrics shared;
|
||||
FIslandFieldAttributionInstrumentation attribution;
|
||||
};
|
||||
|
||||
inline FIslandColumnCacheInstrumentation*& ActiveIslandColumnCacheInstrumentation() noexcept
|
||||
@@ -488,16 +494,49 @@ namespace floating_island_detail
|
||||
{
|
||||
FIslandColumnCacheInstrumentation* previous=ActiveIslandColumnCacheInstrumentation();
|
||||
ActiveIslandColumnCacheInstrumentation()=value;
|
||||
SetActiveIslandFieldAttribution(value?&value->attribution:nullptr);
|
||||
return previous;
|
||||
}
|
||||
|
||||
using FSharedIslandColumnCache =
|
||||
VoxelForgeCoreShared::TSharedColumnValueCache<FIslandColumnCacheEntry,16384,4>;
|
||||
|
||||
struct FIslandColumnTraceKey
|
||||
{
|
||||
bool valid=false;
|
||||
std::uint64_t recipeHash=0,planHash=0,island=0;
|
||||
std::int64_t x=0,y=0;
|
||||
};
|
||||
|
||||
struct FIslandColumnReuseTrace
|
||||
{
|
||||
std::array<FIslandColumnTraceKey,128> recentMisses{};
|
||||
std::array<FIslandColumnTraceKey,128> recentEvictions{};
|
||||
std::uint64_t missSequence=0,printedEvents=0;
|
||||
};
|
||||
|
||||
inline bool SameTraceKey(const FIslandColumnTraceKey& left,
|
||||
const FIslandColumnTraceKey& right) noexcept
|
||||
{
|
||||
return left.valid&&right.valid&&left.recipeHash==right.recipeHash
|
||||
&&left.planHash==right.planHash&&left.island==right.island
|
||||
&&left.x==right.x&&left.y==right.y;
|
||||
}
|
||||
|
||||
inline std::size_t TraceKeyIndex(const FIslandColumnTraceKey& key) noexcept
|
||||
{
|
||||
std::uint64_t hash=key.island^static_cast<std::uint64_t>(key.x)*0x9e3779b97f4a7c15ull
|
||||
^static_cast<std::uint64_t>(key.y)*0xc2b2ae3d27d4eb4full
|
||||
^key.recipeHash^std::rotl(key.planHash,17);
|
||||
hash^=hash>>33;hash*=0xff51afd7ed558ccdull;hash^=hash>>33;
|
||||
return static_cast<std::size_t>(hash&127u);
|
||||
}
|
||||
|
||||
inline const FIslandColumnCacheEntry& IslandColumnCache(
|
||||
const FloatingIslandRecipeV1& recipe,const FloatingIslandPlanV1& plan,
|
||||
const FloatingIslandAnchorV1& island,double x,double y)
|
||||
{
|
||||
FScopedIslandAttributionTimer columnTimer(EIslandAttributionBucket::Column);
|
||||
// Density samples arrive on integer Core lattice columns; reuse the exact column metrics
|
||||
// across Z samples without changing the field value or touching shared world state.
|
||||
// Les echantillons suivent les colonnes Core entieres; reutiliser les memes metriques XY.
|
||||
@@ -507,6 +546,7 @@ namespace floating_island_detail
|
||||
static thread_local VoxelForgeCoreThreadLocal::FLazy<
|
||||
std::array<FIslandColumnCacheEntry,SetCount*Ways>> Cache;
|
||||
auto& cache=Cache.Get();
|
||||
FScopedIslandAttributionTimer localProbeTimer(EIslandAttributionBucket::LocalProbe);
|
||||
const std::int64_t SampleX=static_cast<std::int64_t>(x);
|
||||
const std::int64_t SampleY=static_cast<std::int64_t>(y);
|
||||
std::uint64_t IndexKey=island.stableId
|
||||
@@ -545,10 +585,56 @@ namespace floating_island_detail
|
||||
if(!Entry.bValid&&!bFoundInvalid)
|
||||
{Replacement=&Entry;bFoundInvalid=true;}
|
||||
}
|
||||
localProbeTimer.Stop();
|
||||
if(auto* stats=ActiveIslandColumnCacheInstrumentation())
|
||||
{
|
||||
++stats->localMisses;
|
||||
if(!bFoundInvalid)++stats->localConflictReplacements;
|
||||
static thread_local FIslandColumnReuseTrace trace;
|
||||
if((++trace.missSequence&1023u)==0)
|
||||
{
|
||||
auto& a=stats->attribution;
|
||||
++a.traceSampledMisses;
|
||||
const FIslandColumnTraceKey requested{true,recipe.contentHash,
|
||||
plan.contentHash,island.stableId,SampleX,SampleY};
|
||||
const std::size_t requestSlot=TraceKeyIndex(requested);
|
||||
const bool seenBefore=SameTraceKey(trace.recentMisses[requestSlot],requested);
|
||||
const bool reusedEviction=SameTraceKey(
|
||||
trace.recentEvictions[requestSlot],requested);
|
||||
if(seenBefore)++a.traceSeenBefore;
|
||||
if(reusedEviction)
|
||||
{
|
||||
++a.traceReusedEvictions;
|
||||
trace.recentEvictions[requestSlot].valid=false;
|
||||
}
|
||||
FIslandColumnTraceKey evicted{};
|
||||
std::size_t replacementWay=0;
|
||||
if(!bFoundInvalid&&Replacement)
|
||||
{
|
||||
evicted={Replacement->bValid,Replacement->RecipeContentHash,
|
||||
Replacement->PlanContentHash,Replacement->StableId,
|
||||
Replacement->SampleX,Replacement->SampleY};
|
||||
replacementWay=static_cast<std::size_t>(Replacement-&cache[Base]);
|
||||
++a.traceEvictions;
|
||||
if(evicted.valid)
|
||||
trace.recentEvictions[TraceKeyIndex(evicted)]=evicted;
|
||||
}
|
||||
trace.recentMisses[requestSlot]=requested;
|
||||
if(trace.printedEvents<16&&a.traceCount<a.traceSet.size())
|
||||
{
|
||||
const std::size_t event=a.traceCount++;
|
||||
++trace.printedEvents;
|
||||
a.traceSet[event]=Base/Ways;
|
||||
a.traceX[event]=SampleX;a.traceY[event]=SampleY;
|
||||
a.traceIsland[event]=island.stableId;
|
||||
a.traceEvictedX[event]=evicted.valid?evicted.x:0;
|
||||
a.traceEvictedY[event]=evicted.valid?evicted.y:0;
|
||||
a.traceEvictedIsland[event]=evicted.valid?evicted.island:0;
|
||||
a.traceFlags[event]=static_cast<std::uint8_t>(
|
||||
(!bFoundInvalid?1u:0u)|(seenBefore?2u:0u)|(reusedEviction?4u:0u)
|
||||
|(replacementWay<<3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto build=[&]()
|
||||
@@ -575,9 +661,14 @@ namespace floating_island_detail
|
||||
Entry.Radial=std::hypot(Entry.DeltaX,Entry.DeltaY);
|
||||
Entry.Angle=std::atan2(Entry.DeltaY,Entry.DeltaX);
|
||||
Entry.LobePhase=LobePhase(recipe,island);
|
||||
double contourOffset=0.0;
|
||||
{
|
||||
FScopedIslandAttributionTimer helixTimer(EIslandAttributionBucket::Helix);
|
||||
contourOffset=LobeOffsetWithPhase(recipe,Entry.Angle,
|
||||
static_cast<double>(island.topZSubvoxels),Entry.LobePhase);
|
||||
}
|
||||
const double EffectiveRadius=std::max(1.0,static_cast<double>(island.radiusSubvoxels)
|
||||
+LobeOffsetWithPhase(recipe,Entry.Angle,static_cast<double>(island.topZSubvoxels),
|
||||
Entry.LobePhase));
|
||||
+contourOffset);
|
||||
const double Edge=std::clamp(Entry.Radial/EffectiveRadius,0.0,1.0);
|
||||
const double Drop=(1.0-Q16ToDouble(recipe.topFlattenQ16))
|
||||
*static_cast<double>(island.topHalfSubvoxels)*2.0*Edge*Edge;
|
||||
@@ -594,8 +685,11 @@ namespace floating_island_detail
|
||||
plan.contentHash,SampleX,SampleY,island.stableId};
|
||||
bool computed=false;
|
||||
auto* stats=ActiveIslandColumnCacheInstrumentation();
|
||||
Entry=shared->getOrCompute(key,build,computed,
|
||||
stats?&stats->shared:nullptr);
|
||||
{
|
||||
FScopedIslandAttributionTimer sharedCacheTimer(EIslandAttributionBucket::SharedCache);
|
||||
Entry=shared->getOrCompute(key,build,computed,
|
||||
stats?&stats->shared:nullptr);
|
||||
}
|
||||
}
|
||||
else Entry=build();
|
||||
*Replacement=Entry;
|
||||
@@ -658,6 +752,7 @@ namespace floating_island_detail
|
||||
const FloatingIslandAnchorV1& island,
|
||||
std::int32_t rootIndex, double x, double y, double z)
|
||||
{
|
||||
FScopedIslandAttributionTimer attributionTimer(EIslandAttributionBucket::Root);
|
||||
// Cache derived root constants per worker. The complete immutable input key keeps this
|
||||
// value-only cache safe across worlds, epochs and worker schedules.
|
||||
// Mettre en cache les constantes par worker; la cle complete evite tout partage d'etat.
|
||||
@@ -778,6 +873,7 @@ namespace floating_island_detail
|
||||
const FloatingIslandAnchorV1& island,
|
||||
double x, double y, double z)
|
||||
{
|
||||
FScopedIslandAttributionTimer islandTimer(EIslandAttributionBucket::Island);
|
||||
if (recipe.geometryVersion < 2)
|
||||
{
|
||||
const double dx = x - static_cast<double>(island.centerSubvoxels[0]);
|
||||
@@ -805,8 +901,13 @@ namespace floating_island_detail
|
||||
const double radial=Column.Radial;
|
||||
const double topZ = static_cast<double>(island.topZSubvoxels);
|
||||
const double radius = static_cast<double>(island.radiusSubvoxels);
|
||||
double contourOffset=0.0;
|
||||
{
|
||||
FScopedIslandAttributionTimer helixTimer(EIslandAttributionBucket::Helix);
|
||||
contourOffset=LobeOffsetWithPhase(recipe,Column.Angle,z,Column.LobePhase);
|
||||
}
|
||||
const double effectiveRadius = std::max(0.0,
|
||||
radius + LobeOffsetWithPhase(recipe,Column.Angle,z,Column.LobePhase));
|
||||
radius + contourOffset);
|
||||
const double edge = std::clamp(radial / std::max(effectiveRadius, 1.0), 0.0, 1.0);
|
||||
const double lowerSurface = static_cast<double>(IslandCrustBottom(recipe, island))
|
||||
+ radius * 0.16 * edge * edge;
|
||||
@@ -853,6 +954,7 @@ namespace floating_island_detail
|
||||
bool* topSurface = nullptr,
|
||||
double* alongFraction = nullptr)
|
||||
{
|
||||
FScopedIslandAttributionTimer connectorTimer(EIslandAttributionBucket::Connector);
|
||||
const double ax = static_cast<double>(edge.startSubvoxels[0]);
|
||||
const double ay = static_cast<double>(edge.startSubvoxels[1]);
|
||||
const double az = static_cast<double>(edge.startSubvoxels[2]);
|
||||
@@ -2184,14 +2286,19 @@ bool FloatingIslandFieldV1::isValid() const noexcept
|
||||
std::int64_t FloatingIslandFieldV1::densitySubvoxels(std::int64_t x,
|
||||
std::int64_t y, std::int64_t z) const noexcept
|
||||
{
|
||||
floating_island_detail::FScopedIslandAttributionTimer fieldTimer(
|
||||
floating_island_detail::EIslandAttributionBucket::Field);
|
||||
// The selected evaluator validates once; do not scan the plan twice per scoped sample.
|
||||
// L'evaluateur choisi valide une seule fois; ne pas reparcourir le plan a chaque echantillon.
|
||||
std::int64_t density=floating_island_detail::MaxDensity;
|
||||
if (const auto* list = ScopedWorldAssemblyFeatureQueriesV1::find(recipe_, plan_, x, y, z))
|
||||
return densitySubvoxelsForTile(x, y, z, *list);
|
||||
if (!isValid() || !floating_island_detail::ValidScalar(x)
|
||||
density=densitySubvoxelsForTile(x, y, z, *list);
|
||||
else if (!isValid() || !floating_island_detail::ValidScalar(x)
|
||||
|| !floating_island_detail::ValidScalar(y) || !floating_island_detail::ValidScalar(z))
|
||||
return floating_island_detail::MaxDensity;
|
||||
return floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
|
||||
density=floating_island_detail::MaxDensity;
|
||||
else density=floating_island_detail::FieldDensity(*recipe_, *plan_, x, y, z);
|
||||
fieldTimer.Stop();
|
||||
return density;
|
||||
}
|
||||
|
||||
std::vector<WorldAssemblyFeatureBoundsV1> FloatingIslandFieldV1::solidSupportBounds() const
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "VoxelDensityPrimitives.h"
|
||||
#include "VoxelForgeCore/VoxelForgeCore.h"
|
||||
#include "VoxelForgeFieldShared.h"
|
||||
#include "VoxelForgeCore/FloatingIslandInstrumentation.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -414,6 +415,12 @@ public:
|
||||
return FindBoundStratumForChunk(FMath::FloorToInt(Z/LayoutWidth));
|
||||
}
|
||||
|
||||
bool IsFloatingIslandFieldAtZ(float Z) const
|
||||
{
|
||||
const FBoundStratumField* Bound=FindBoundStratumForZ(Z);
|
||||
return Bound&&Bound->Kind==FBoundStratumField::EKind::FloatingIslands;
|
||||
}
|
||||
|
||||
const FBoundStratumField* FindBoundStratumForManagerZ(float Z) const
|
||||
{
|
||||
if(!std::isfinite(Z)||!Owner||!(BoundManagerLayoutChunkWidthVoxels>0.0f))
|
||||
@@ -6139,6 +6146,7 @@ private:
|
||||
const auto* FieldAdapter=std::get_if<FFloatingIslandFieldAdapter>(
|
||||
&Bound->Evaluator);
|
||||
if(!FieldAdapter||!FieldAdapter->Evaluator.isValid())return false;
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandAttributionSample attributionSample;
|
||||
constexpr double Scale=static_cast<double>(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
constexpr double Maximum=1000000000000.0/Scale;
|
||||
if(std::abs(static_cast<double>(X))>Maximum||std::abs(static_cast<double>(Y))>Maximum
|
||||
@@ -6266,8 +6274,12 @@ private:
|
||||
}
|
||||
if(mouthFilterWeight>0.0)
|
||||
{
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandAttributionTimer mouthFilterTimer(
|
||||
VoxelForgeCore::floating_island_detail::EIslandAttributionBucket::MouthFilter);
|
||||
constexpr std::int64_t FilterStep=8*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
std::int64_t weightedSum=0;
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller passageFilterCaller(
|
||||
VoxelForgeCore::floating_island_detail::EIslandFieldCaller::PassageFilter);
|
||||
for(int dz=-1;dz<=1;++dz)
|
||||
for(int dy=-1;dy<=1;++dy)
|
||||
for(int dx=-1;dx<=1;++dx)
|
||||
@@ -6306,6 +6318,8 @@ private:
|
||||
{
|
||||
bool surroundedByAir=true;
|
||||
std::int64_t minimumAirDensity=std::numeric_limits<std::int64_t>::max();
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandFieldCaller passageFilterCaller(
|
||||
VoxelForgeCore::floating_island_detail::EIslandFieldCaller::PassageFilter);
|
||||
for(std::size_t axis=0;axis<3&&surroundedByAir;++axis)
|
||||
for(const int sign:{-1,1})
|
||||
{
|
||||
@@ -6323,6 +6337,8 @@ private:
|
||||
if(surroundedByAir)Density=std::max(Density,minimumAirDensity);
|
||||
}
|
||||
}
|
||||
VoxelForgeCore::floating_island_detail::FScopedIslandAttributionTimer boundaryTimer(
|
||||
VoxelForgeCore::floating_island_detail::EIslandAttributionBucket::Boundary);
|
||||
const std::int64_t Bottom=Plan->manifest.recipe.layoutMinimumZSubvoxels;
|
||||
const std::int64_t Top=Plan->manifest.recipe.layoutMaximumZSubvoxels;
|
||||
const std::int64_t Seal=Plan->manifest.recipe.boundarySealSubvoxels;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <thread>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
namespace vfcore::detail
|
||||
{
|
||||
@@ -2013,6 +2014,7 @@ namespace VoxelForgeCore
|
||||
?impl_->sharedHeightfieldColumns.get():nullptr,
|
||||
options.useSharedColumnCache?impl_->sharedIslandColumns.get():nullptr);
|
||||
floating_island_detail::FIslandColumnCacheInstrumentation islandCacheInstrumentation;
|
||||
islandCacheInstrumentation.tileStartCycles=VoxelForgeCoreShared::ReadCpuCycles();
|
||||
FScopedIslandColumnCacheInstrumentation islandCacheScope(
|
||||
&islandCacheInstrumentation,options.instrumentTileDrain);
|
||||
const auto& world=*impl_->owner;
|
||||
@@ -2161,6 +2163,8 @@ namespace VoxelForgeCore
|
||||
const auto CopyIslandCacheMetrics=[&]()
|
||||
{
|
||||
if(!options.instrumentTileDrain)return;
|
||||
islandCacheInstrumentation.tileCycles=VoxelForgeCoreShared::ReadCpuCycles()
|
||||
-islandCacheInstrumentation.tileStartCycles;
|
||||
out.diagnostics.islandLocalCacheHits=islandCacheInstrumentation.localHits;
|
||||
out.diagnostics.islandLocalCacheMisses=islandCacheInstrumentation.localMisses;
|
||||
out.diagnostics.islandLocalCacheConflictReplacements=
|
||||
@@ -2172,6 +2176,73 @@ namespace VoxelForgeCore
|
||||
islandCacheInstrumentation.shared.conflictReplacements;
|
||||
out.diagnostics.islandSharedCacheLockWaitNanoseconds=
|
||||
islandCacheInstrumentation.shared.lockWaitNanoseconds;
|
||||
const auto& a=islandCacheInstrumentation.attribution;
|
||||
const auto Estimate=[](std::uint64_t cycles,std::uint64_t calls,
|
||||
std::uint64_t samples)->std::uint64_t
|
||||
{
|
||||
return samples?static_cast<std::uint64_t>(
|
||||
static_cast<long double>(cycles)*calls/samples):0;
|
||||
};
|
||||
constexpr std::array<const char*,5> CallerNames={
|
||||
"grid","skirt","boundary","filter","other"};
|
||||
constexpr std::array<const char*,10> BucketNames={
|
||||
"field","island","roots","connectors","helix","column",
|
||||
"local_probe","shared_cache","mouth_filter","boundary"};
|
||||
std::uint64_t sampledNativeCalls=0;
|
||||
for(const auto samples:a.sampledNativeCalls)sampledNativeCalls+=samples;
|
||||
std::ostringstream line;
|
||||
line<<"[FiFieldAttribution] key=("<<key.coordinate.x<<","<<key.coordinate.y<<","<<key.coordinate.z
|
||||
<<",lod"<<key.lod<<") tile_cycles="<<islandCacheInstrumentation.tileCycles
|
||||
<<" sample_period=256 native_calls_est="<<sampledNativeCalls*256
|
||||
<<" native_samples="<<sampledNativeCalls;
|
||||
for(std::size_t i=0;i<a.sampledNativeCalls.size();++i)
|
||||
{
|
||||
line<<" native_"<<CallerNames[i]<<"="<<a.sampledNativeCalls[i]*256<<"/"
|
||||
<<a.sampledNativeCalls[i]<<"/"
|
||||
<<Estimate(a.nativeCycles[i],a.sampledNativeCalls[i]*256,
|
||||
a.sampledNativeCalls[i]);
|
||||
line<<" field_"<<CallerNames[i]<<"="<<a.sampledFieldCalls[i]*256<<"/"
|
||||
<<a.sampledFieldCalls[i]<<"/"
|
||||
<<Estimate(a.fieldCycles[i],a.sampledFieldCalls[i]*256,
|
||||
a.sampledFieldCalls[i]);
|
||||
}
|
||||
for(std::size_t i=0;i<BucketNames.size();++i)
|
||||
line<<" comp_"<<BucketNames[i]<<"="
|
||||
<<Estimate(a.exclusiveCycles[i],a.scopeSamples[i]*256,
|
||||
a.scopeSamples[i])
|
||||
<<"/"<<a.scopeSamples[i]<<"/"<<a.scopeSamples[i]*256;
|
||||
const auto& shared=islandCacheInstrumentation.shared;
|
||||
line<<" local_probe_calls="<<(islandCacheInstrumentation.localHits
|
||||
+islandCacheInstrumentation.localMisses)
|
||||
<<" shared_calls_est="<<shared.sampledInitialReads*128
|
||||
<<" shared_initial_read="<<shared.sampledInitialReads<<"/"
|
||||
<<Estimate(shared.sampledInitialReadCycles,shared.sampledInitialReads*128,
|
||||
shared.sampledInitialReads)
|
||||
<<" shared_locked_read="<<shared.sampledLockedReads<<"/"
|
||||
<<Estimate(shared.sampledLockedReadCycles,shared.sampledLockedReads*128,
|
||||
shared.sampledLockedReads)
|
||||
<<" shared_lock="<<shared.sampledLocks<<"/"
|
||||
<<Estimate(shared.sampledLockCycles,shared.sampledLocks*128,
|
||||
shared.sampledLocks)
|
||||
<<" shared_build="<<shared.sampledBuilds<<"/"
|
||||
<<Estimate(shared.sampledBuildCycles,shared.sampledBuilds*128,
|
||||
shared.sampledBuilds)
|
||||
<<" trace_sampled="<<a.traceSampledMisses
|
||||
<<" trace_seen_before="<<a.traceSeenBefore
|
||||
<<" trace_evictions="<<a.traceEvictions
|
||||
<<" trace_reused_evictions="<<a.traceReusedEvictions
|
||||
<<" trace_events="<<a.traceCount;
|
||||
std::printf("%s\n",line.str().c_str());
|
||||
for(std::size_t i=0;i<a.traceCount;++i)
|
||||
std::printf("[FiReuseTrace] tile=(%d,%d,%d,lod%d) set=%llu island=%llu xy=(%lld,%lld) evicted_island=%llu evicted_xy=(%lld,%lld) flags=%u\n",
|
||||
key.coordinate.x,key.coordinate.y,key.coordinate.z,key.lod,
|
||||
static_cast<unsigned long long>(a.traceSet[i]),
|
||||
static_cast<unsigned long long>(a.traceIsland[i]),
|
||||
static_cast<long long>(a.traceX[i]),static_cast<long long>(a.traceY[i]),
|
||||
static_cast<unsigned long long>(a.traceEvictedIsland[i]),
|
||||
static_cast<long long>(a.traceEvictedX[i]),
|
||||
static_cast<long long>(a.traceEvictedY[i]),
|
||||
static_cast<unsigned>(a.traceFlags[i]));
|
||||
};
|
||||
out.diagnostics.featureRefinedResolution=bFeatureRefinedResolution;
|
||||
if(options.profileMesherStages)
|
||||
@@ -2419,6 +2490,30 @@ namespace VoxelForgeCore
|
||||
RelevantMinimum=std::min(RelevantMinimum,FeatureMinimum);
|
||||
RelevantMaximum=std::max(RelevantMaximum,FeatureMaximum);
|
||||
}
|
||||
// A resolved passage void column can add solid support above or
|
||||
// below the native heightfield's local Z range. Its finite radial
|
||||
// and vertical bounds must participate before the mesh band is clipped.
|
||||
for(const auto& Crossing:tileShape.GetPassageVoidCrossings())
|
||||
{
|
||||
if(!Crossing.needsColumn||Crossing.maximumRadiusSubvoxels<=0
|
||||
||Crossing.topZSubvoxels<=Crossing.bottomZSubvoxels)continue;
|
||||
const double Radius=static_cast<double>(Crossing.maximumRadiusSubvoxels)
|
||||
/static_cast<double>(SubvoxelScale);
|
||||
const double CenterX=static_cast<double>(Crossing.centerXSubvoxels)
|
||||
/static_cast<double>(SubvoxelScale);
|
||||
const double CenterY=static_cast<double>(Crossing.centerYSubvoxels)
|
||||
/static_cast<double>(SubvoxelScale);
|
||||
if(CenterX+Radius<TileMinimum[0]/static_cast<double>(SubvoxelScale)
|
||||
||CenterX-Radius>TileMaximum[0]/static_cast<double>(SubvoxelScale)
|
||||
||CenterY+Radius<TileMinimum[1]/static_cast<double>(SubvoxelScale)
|
||||
||CenterY-Radius>TileMaximum[1]/static_cast<double>(SubvoxelScale))continue;
|
||||
RelevantMinimum=std::min(RelevantMinimum,
|
||||
static_cast<double>(Crossing.bottomZSubvoxels)
|
||||
/static_cast<double>(SubvoxelScale));
|
||||
RelevantMaximum=std::max(RelevantMaximum,
|
||||
static_cast<double>(Crossing.topZSubvoxels)
|
||||
/static_cast<double>(SubvoxelScale));
|
||||
}
|
||||
// The native heightfield band only covers the owner terrain and its
|
||||
// manifest features. Resolved SDF stamps can carve below that band or
|
||||
// add a floor there, so include every overlapping stamp's bounded Z
|
||||
|
||||
Reference in new issue
Block a user