wip(l62): shared boundary-sample cache + hidden skirt skip -- PARKED: SW +8.5%, moving p50 +119..265%, full tier incomplete
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
135957831b
commit
6ae5c4460d
12 files changed
+705
-111
No files matched your search
@@ -54,12 +54,36 @@ namespace VoxelForgeCoreShared
|
||||
}
|
||||
};
|
||||
|
||||
// Exact identity for one world-lattice density sample. Unlike a column key this includes
|
||||
// Z, sampling settings, and the edit generation; all coordinates remain explicit words.
|
||||
struct FDensitySampleCacheKey
|
||||
{
|
||||
std::uint64_t fieldIdentity = 0;
|
||||
std::uint64_t fieldGeneration = 0;
|
||||
std::uint64_t editSequence = 0;
|
||||
std::uint32_t lod = 0;
|
||||
std::uint32_t step = 1;
|
||||
std::uint32_t octaveDrop = 0;
|
||||
std::int64_t interpolationStride = 0;
|
||||
std::int64_t x = 0, y = 0, z = 0;
|
||||
std::uint64_t queryContextLow = 0, queryContextHigh = 0;
|
||||
|
||||
std::array<std::uint64_t, 12> words() const noexcept
|
||||
{
|
||||
return {fieldIdentity, fieldGeneration, editSequence, lod, step, octaveDrop,
|
||||
static_cast<std::uint64_t>(interpolationStride), static_cast<std::uint64_t>(x),
|
||||
static_cast<std::uint64_t>(y), static_cast<std::uint64_t>(z),
|
||||
queryContextLow, queryContextHigh};
|
||||
}
|
||||
};
|
||||
|
||||
// A bounded, world-owned second-level cache. Existing thread-local caches stay first, so
|
||||
// their ordinary hit path remains private and lock-free. Shared hits read atomic words; only
|
||||
// a shared miss takes the set spin lock. Entries are four-way set associative, evict on
|
||||
// collision, and carry pointer identities plus content hashes to reject stale reuse.
|
||||
// Les caches TLS restent le premier niveau; seul un defaut partage prend le verrou du groupe.
|
||||
template <typename TValue, std::size_t SetCount, std::size_t Ways = 4>
|
||||
template <typename TValue, std::size_t SetCount, std::size_t Ways = 4,
|
||||
typename TKey = FColumnCacheKey>
|
||||
class TSharedColumnValueCache
|
||||
{
|
||||
static_assert(SetCount > 0 && (SetCount & (SetCount - 1)) == 0,
|
||||
@@ -69,14 +93,14 @@ namespace VoxelForgeCoreShared
|
||||
"shared column values must be trivially copyable");
|
||||
|
||||
static constexpr std::size_t ValueWordCount = (sizeof(TValue) + 7) / 8;
|
||||
using FKeyWords = std::array<std::uint64_t, 7>;
|
||||
using FKeyWords = decltype(std::declval<TKey>().words());
|
||||
using FValueWords = std::array<std::uint64_t, ValueWordCount>;
|
||||
|
||||
struct FEntry
|
||||
{
|
||||
std::atomic<std::uint64_t> Version{0};
|
||||
std::atomic<std::uint64_t> Tag{0};
|
||||
std::array<std::atomic<std::uint64_t>, 7> Key{};
|
||||
std::array<std::atomic<std::uint64_t>, FKeyWords{}.size()> Key{};
|
||||
std::array<std::atomic<std::uint64_t>, ValueWordCount> Value{};
|
||||
};
|
||||
|
||||
@@ -128,7 +152,7 @@ namespace VoxelForgeCoreShared
|
||||
static TSharedColumnValueCache* threadActive() noexcept { return Active; }
|
||||
|
||||
template <typename TFactory>
|
||||
TValue getOrCompute(const FColumnCacheKey& key, TFactory&& factory,
|
||||
TValue getOrCompute(const TKey& key, TFactory&& factory,
|
||||
bool& computed, FColumnCacheMetrics* metrics = nullptr)
|
||||
{
|
||||
computed = false;
|
||||
|
||||
@@ -139,6 +139,10 @@ namespace VoxelForgeCore
|
||||
std::uint64_t columnCacheConflictMisses = 0;
|
||||
std::uint64_t sharedColumnCacheHits = 0;
|
||||
std::uint64_t columnEvaluations = 0;
|
||||
std::uint64_t densitySampleCacheHits = 0;
|
||||
std::uint64_t densitySampleCacheComputes = 0;
|
||||
std::uint64_t marchingCubesSkirtTrianglesEmitted = 0;
|
||||
std::uint64_t marchingCubesSkirtTriangleSeedsSkipped = 0;
|
||||
std::uint64_t islandLocalCacheHits = 0;
|
||||
std::uint64_t islandLocalCacheMisses = 0;
|
||||
std::uint64_t islandLocalCacheConflictReplacements = 0;
|
||||
@@ -719,8 +723,42 @@ namespace VoxelForgeCore
|
||||
// Hors du domaine prepare, revenir au parcours complet canonique.
|
||||
std::array<std::int64_t, 3> minimumSubvoxels{};
|
||||
std::array<std::int64_t, 3> maximumSubvoxels{};
|
||||
// Stable semantic identity used by the exact shared density-sample cache.
|
||||
std::uint64_t densityCacheContextLow = 0;
|
||||
std::uint64_t densityCacheContextHigh = 0;
|
||||
};
|
||||
|
||||
inline void FinalizeWorldAssemblyFeatureTileListDensityContextV1(
|
||||
WorldAssemblyFeatureTileListV1& list) noexcept
|
||||
{
|
||||
std::uint64_t low = 0x243f6a8885a308d3ull;
|
||||
std::uint64_t high = 0x13198a2e03707344ull;
|
||||
const auto add = [&](std::uint64_t value)
|
||||
{
|
||||
low ^= value + 0x9e3779b97f4a7c15ull + (low << 6) + (low >> 2);
|
||||
low *= 0xbf58476d1ce4e5b9ull;
|
||||
high ^= value + 0x94d049bb133111ebull + (high << 7) + (high >> 3);
|
||||
high *= 0x9e3779b185ebca87ull;
|
||||
};
|
||||
add(list.featureIndices.size());
|
||||
for (const auto value : list.featureIndices) add(value);
|
||||
add(list.groupBounds.size());
|
||||
for (const auto& group : list.groupBounds)
|
||||
{
|
||||
for (auto value : group.minimumSubvoxels) add(static_cast<std::uint64_t>(value));
|
||||
for (auto value : group.maximumSubvoxels) add(static_cast<std::uint64_t>(value));
|
||||
}
|
||||
add(list.groupSearchOrder.size());
|
||||
for (const auto value : list.groupSearchOrder) add(value);
|
||||
add(list.groupDistanceLowerBoundsSubvoxels.size());
|
||||
for (const auto value : list.groupDistanceLowerBoundsSubvoxels)
|
||||
add(static_cast<std::uint64_t>(value));
|
||||
add(list.groupSearchComplete ? 1u : 0u);
|
||||
add(list.candidateEvaluationUseful ? 1u : 0u);
|
||||
list.densityCacheContextLow = low;
|
||||
list.densityCacheContextHigh = high;
|
||||
}
|
||||
|
||||
using FloatingIslandTileFeatureListV1 = WorldAssemblyFeatureTileListV1;
|
||||
using FloatingIslandFeatureGroupBoundsV1 = WorldAssemblyFeatureGroupBoundsV1;
|
||||
|
||||
@@ -743,10 +781,13 @@ namespace VoxelForgeCore
|
||||
ScopedWorldAssemblyFeatureQueriesV1& operator=(const ScopedWorldAssemblyFeatureQueriesV1&) = delete;
|
||||
static const WorldAssemblyFeatureTileListV1* find(const void* recipe, const void* plan,
|
||||
std::int64_t x, std::int64_t y, std::int64_t z, bool columnOnly = false) noexcept;
|
||||
static std::array<std::uint64_t, 2> densityCacheContext(
|
||||
std::int64_t x, std::int64_t y, std::int64_t z) noexcept;
|
||||
private:
|
||||
std::span<const WorldAssemblyFeatureQueryV1> queries_;
|
||||
bool forceCandidates_ = false;
|
||||
const ScopedWorldAssemblyFeatureQueriesV1* previous_ = nullptr;
|
||||
std::uint64_t densityCacheContextLow_ = 0, densityCacheContextHigh_ = 0;
|
||||
};
|
||||
|
||||
struct VFCORE_API FloatingIslandManifestV1
|
||||
@@ -1343,6 +1384,8 @@ namespace VoxelForgeCore
|
||||
std::uint64_t columnConflictMisses = 0;
|
||||
std::uint64_t sharedColumnHits = 0;
|
||||
std::uint64_t columnEvaluations = 0;
|
||||
std::uint64_t densitySampleCacheHits = 0;
|
||||
std::uint64_t densitySampleCacheComputes = 0;
|
||||
double columnComputeMilliseconds = 0.0;
|
||||
bool captureCoordinates = false;
|
||||
bool coordinateCaptureComplete = true;
|
||||
@@ -1741,6 +1784,9 @@ namespace VoxelForgeCore
|
||||
// Diagnostic seulement; enregistre les colonnes XY evaluees par cette tuile LOD8.
|
||||
bool captureColumnCoordinates = false;
|
||||
bool useSharedColumnCache = true;
|
||||
// Fixed visibility mask for the four horizontal skirts and the two vertical cap skirts.
|
||||
// The mask is part of the standalone mesh-cache identity and defaults to all faces.
|
||||
std::uint8_t visibleSkirtFaceMask = 0x3f;
|
||||
// Separate cold pass only; samples the MC inner stages without changing output.
|
||||
// Passe froide separee seulement; mesure les etapes internes MC sans changer la sortie.
|
||||
bool profileMesherStages = false;
|
||||
|
||||
@@ -2325,7 +2325,8 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
|
||||
result.candidateEvaluationUseful=plan_->islands.size()>4;
|
||||
result.minimumSubvoxels = tileMinimumSubvoxels;
|
||||
result.maximumSubvoxels = tileMaximumSubvoxels;
|
||||
if (!isValid()) return result;
|
||||
if (!isValid())
|
||||
{ FinalizeWorldAssemblyFeatureTileListDensityContextV1(result); return result; }
|
||||
const auto features = solidSupportBounds();
|
||||
|
||||
result.featureIndices = BuildWorldAssemblyFeatureTileListV1(features,
|
||||
@@ -2347,7 +2348,8 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
|
||||
{
|
||||
const auto source = findSourceIsland(edge);
|
||||
if (source == plan_->islands.end())
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear();
|
||||
FinalizeWorldAssemblyFeatureTileListDensityContextV1(result); return result; }
|
||||
auto& group = result.groupBounds[static_cast<std::size_t>(source - plan_->islands.begin())];
|
||||
for (std::size_t axis = 0; axis < 3; ++axis)
|
||||
{
|
||||
@@ -2438,10 +2440,12 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
|
||||
{
|
||||
const std::size_t edgeIndex = static_cast<std::size_t>(featureIndex) - islandCount;
|
||||
if (edgeIndex >= plan_->clusterEdges.size())
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear();
|
||||
FinalizeWorldAssemblyFeatureTileListDensityContextV1(result); return result; }
|
||||
const auto source = findSourceIsland(plan_->clusterEdges[edgeIndex]);
|
||||
if (source == plan_->islands.end())
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear(); return result; }
|
||||
{ result.groupBounds.clear(); result.featureIndices.clear();
|
||||
FinalizeWorldAssemblyFeatureTileListDensityContextV1(result); return result; }
|
||||
selectedGroups[static_cast<std::size_t>(source - plan_->islands.begin())] = 1;
|
||||
}
|
||||
}
|
||||
@@ -2464,6 +2468,7 @@ FloatingIslandTileFeatureListV1 FloatingIslandFieldV1::buildFeatureTileList(
|
||||
if (plan_->clusterEdges[edgeIndex].fromIslandId == plan_->islands[islandIndex].stableId)
|
||||
insertSorted(static_cast<std::uint32_t>(islandCount + edgeIndex));
|
||||
}
|
||||
FinalizeWorldAssemblyFeatureTileListDensityContextV1(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ public:
|
||||
routeFeatureOverlap_=true;
|
||||
}
|
||||
}
|
||||
buildDensityCacheContext();
|
||||
ActivePassageTileQueries=this;
|
||||
}
|
||||
~FScopedPassageTileQueries(){ActivePassageTileQueries=previous_;}
|
||||
@@ -240,8 +241,46 @@ public:
|
||||
bool hasCavernCandidates() const{return !cavernFeatureIndices_.empty();}
|
||||
const std::vector<std::uint32_t>& cavernFeatureCandidates() const
|
||||
{return cavernFeatureIndices_;}
|
||||
static std::array<std::uint64_t,2> densityCacheContext(
|
||||
const FStandaloneShapeWorld& shape) noexcept
|
||||
{
|
||||
const auto* queries=ActivePassageTileQueries;
|
||||
if(!queries||!queries->matches(shape))return {};
|
||||
return {queries->densityCacheContextLow_,queries->densityCacheContextHigh_};
|
||||
}
|
||||
|
||||
private:
|
||||
void buildDensityCacheContext() noexcept
|
||||
{
|
||||
std::uint64_t low=0x3c6ef372fe94f82bull;
|
||||
std::uint64_t high=0xa54ff53a5f1d36f1ull;
|
||||
const auto add=[&](std::uint64_t value)
|
||||
{
|
||||
low^=value+0x9e3779b97f4a7c15ull+(low<<6)+(low>>2);
|
||||
low*=0xbf58476d1ce4e5b9ull;
|
||||
high^=value+0x94d049bb133111ebull+(high<<7)+(high>>3);
|
||||
high*=0x9e3779b185ebca87ull;
|
||||
};
|
||||
add(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(shape_)));
|
||||
const auto addIndices=[&](const std::vector<std::uint32_t>& values)
|
||||
{
|
||||
add(values.size());
|
||||
for(const auto value:values)add(value);
|
||||
};
|
||||
addIndices(featureIndices_);
|
||||
addIndices(stampIndices_);
|
||||
addIndices(cavernFeatureIndices_);
|
||||
for(const auto value:passageRoutes_)add(value);
|
||||
for(const auto value:passageColumns_)add(value);
|
||||
for(const auto value:passageMouthSupports_)add(value);
|
||||
add(routeFeatureOverlap_?1u:0u);
|
||||
add(columnZOverlap_?1u:0u);
|
||||
add(mayContainResolvedBoundaryBand_?1u:0u);
|
||||
add(mayContainTransitionPassageFade_?1u:0u);
|
||||
densityCacheContextLow_=low;
|
||||
densityCacheContextHigh_=high;
|
||||
}
|
||||
|
||||
const FStandaloneShapeWorld* shape_;
|
||||
std::array<std::int64_t,3> minimumSubvoxels_,maximumSubvoxels_;
|
||||
std::vector<std::uint32_t> featureIndices_;
|
||||
@@ -251,6 +290,7 @@ public:
|
||||
bool routeFeatureOverlap_=false,columnZOverlap_=false;
|
||||
bool mayContainResolvedBoundaryBand_=true;
|
||||
bool mayContainTransitionPassageFade_=true;
|
||||
std::uint64_t densityCacheContextLow_=0,densityCacheContextHigh_=0;
|
||||
const FScopedPassageTileQueries* previous_;
|
||||
};
|
||||
|
||||
|
||||
@@ -1041,6 +1041,8 @@ namespace VoxelForgeCore
|
||||
std::atomic<bool> shapeReady{false};
|
||||
std::unique_ptr<world_assembly_detail::FSharedHeightfieldColumnCache>
|
||||
sharedHeightfieldColumns;
|
||||
std::unique_ptr<world_assembly_detail::FSharedDensitySampleCache>
|
||||
sharedDensitySamples;
|
||||
std::unique_ptr<floating_island_detail::FSharedIslandColumnCache>
|
||||
sharedIslandColumns;
|
||||
ShapeBuildRequest buildRequest;
|
||||
@@ -1090,6 +1092,8 @@ namespace VoxelForgeCore
|
||||
impl_->worldAssembly=snapshot.data_->worldAssembly;
|
||||
impl_->resolvedLayout=snapshot.data_->resolvedLayout;
|
||||
impl_->configuration=snapshot.data_->configuration;
|
||||
impl_->sharedDensitySamples=
|
||||
std::make_unique<world_assembly_detail::FSharedDensitySampleCache>();
|
||||
if(impl_->worldAssembly)
|
||||
{
|
||||
if(!impl_->worldAssembly->plan.strates.empty())
|
||||
@@ -2051,6 +2055,7 @@ namespace VoxelForgeCore
|
||||
mesher.bStandaloneProofBlockCells=static_cast<int32>(std::clamp<std::uint32_t>(
|
||||
options.proofBlockCells,1u,static_cast<std::uint32_t>(std::max(1,CHUNK_SIZE))));
|
||||
mesher.bGenerateSkirts=world.bGenerateSkirts;
|
||||
mesher.SetVisibleSkirtFaceMask(options.visibleSkirtFaceMask);
|
||||
mesher.SkirtCells=static_cast<float>(world.SkirtCells)
|
||||
*static_cast<float>(world.OwnerVoxelSizeMicrons)
|
||||
/static_cast<float>(world.CoreVoxelSizeMicrons);
|
||||
@@ -2157,6 +2162,42 @@ namespace VoxelForgeCore
|
||||
MeshCells=FMath::Max(1,cells/Stride);
|
||||
MeshStep=FMath::Max(1,extent/MeshCells);
|
||||
}
|
||||
if(!bNativeOnly&&options.useSharedColumnCache&&impl_->sharedDensitySamples)
|
||||
{
|
||||
mesher.SetSharedDensitySampleCache(impl_->sharedDensitySamples.get(),
|
||||
[](void* Cache,const FStandaloneShapeWorld* ShapeWorld,uint64 FieldGeneration,
|
||||
uint64 EditSequence,int32 Lod,int32 Step,int32 X,int32 Y,int32 Z,
|
||||
bool& OutComputed)->float
|
||||
{
|
||||
VoxelForgeCoreShared::FDensitySampleCacheKey Key;
|
||||
Key.fieldIdentity=static_cast<uint64>(reinterpret_cast<std::uintptr_t>(ShapeWorld));
|
||||
Key.fieldGeneration=FieldGeneration;Key.editSequence=EditSequence;
|
||||
Key.lod=static_cast<uint32>(Lod);Key.step=static_cast<uint32>(Step);
|
||||
Key.octaveDrop=Heightfield2D::threadColumnOctaveDrop();
|
||||
Key.interpolationStride=Heightfield2D::threadColumnInterpolationStrideSubvoxels();
|
||||
Key.x=X;Key.y=Y;Key.z=Z;
|
||||
const auto queryContext = ScopedWorldAssemblyFeatureQueriesV1::densityCacheContext(X,Y,Z);
|
||||
const auto passageContext = FStandaloneShapeWorld::FScopedPassageTileQueries
|
||||
::densityCacheContext(*ShapeWorld);
|
||||
const auto mixContext=[](uint64 Value)
|
||||
{
|
||||
Value^=Value>>30;Value*=0xbf58476d1ce4e5b9ull;
|
||||
Value^=Value>>27;Value*=0x94d049bb133111ebull;
|
||||
return Value^(Value>>31);
|
||||
};
|
||||
Key.queryContextLow=mixContext(queryContext[0]^passageContext[0]
|
||||
^0x9e3779b97f4a7c15ull);
|
||||
Key.queryContextHigh=mixContext(queryContext[1]^passageContext[1]
|
||||
^0xd1b54a32d192ed03ull);
|
||||
const float Value=static_cast<world_assembly_detail::FSharedDensitySampleCache*>(Cache)
|
||||
->getOrCompute(Key,[&](){return ShapeWorld->DensityAtLattice(X,Y,Z);},OutComputed);
|
||||
auto& Stats=world_assembly_detail::HeightfieldColumnStats;
|
||||
if(OutComputed)++Stats.densitySampleCacheComputes;
|
||||
else ++Stats.densitySampleCacheHits;
|
||||
return Value;
|
||||
},static_cast<uint64>(reinterpret_cast<std::uintptr_t>(impl_->worldAssembly.get())),
|
||||
impl_->lastSequence,key.lod,MeshStep);
|
||||
}
|
||||
int64 sampleCount=0;std::vector<float> densityValues;
|
||||
FVoxelMarchingCubesStageTimings McStageTimings;
|
||||
TileMesh out;out.key=key;
|
||||
@@ -2639,12 +2680,19 @@ namespace VoxelForgeCore
|
||||
VoxelForgeCore::Heightfield2D::threadColumnCacheStats().columnComputeMilliseconds;
|
||||
const auto MarchingCubesStart=std::chrono::steady_clock::now();
|
||||
FVoxelUniformBlockProofAuditStats BlockProofAudit;
|
||||
uint64 SkippedSkirtTriangleSeeds=0;
|
||||
FVoxelMeshData mesh=mesher.GenerateMesh(origin,MeshStep,MeshCells,nullptr,
|
||||
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
|
||||
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr,
|
||||
options.profileMesherStages?&McStageTimings:nullptr,
|
||||
options.auditUniformBlockProofs?&BlockProofAudit:nullptr,
|
||||
options.forceFullDensityCapture);
|
||||
options.forceFullDensityCapture,&SkippedSkirtTriangleSeeds);
|
||||
const auto& SampleCacheStats=Heightfield2D::threadColumnCacheStats();
|
||||
out.diagnostics.densitySampleCacheHits=SampleCacheStats.densitySampleCacheHits;
|
||||
out.diagnostics.densitySampleCacheComputes=SampleCacheStats.densitySampleCacheComputes;
|
||||
out.diagnostics.marchingCubesSkirtTrianglesEmitted=
|
||||
static_cast<uint64>(std::max(0,mesh.NumSkirtTriangles));
|
||||
out.diagnostics.marchingCubesSkirtTriangleSeedsSkipped=SkippedSkirtTriangleSeeds;
|
||||
if(options.auditUniformBlockProofs)
|
||||
{
|
||||
out.diagnostics.uniformBlockProofsChecked=BlockProofAudit.CheckedBlocks;
|
||||
|
||||
@@ -32,6 +32,34 @@ ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1(
|
||||
for (const auto& query : queries_)
|
||||
if (forceCandidates_ || query.features.candidateEvaluationUseful)
|
||||
{ ActiveAssemblyQueries = this; break; }
|
||||
if (ActiveAssemblyQueries == this)
|
||||
{
|
||||
std::uint64_t low = 0x6a09e667f3bcc909ull;
|
||||
std::uint64_t high = 0xbb67ae8584caa73bull;
|
||||
const auto add = [&](std::uint64_t value)
|
||||
{
|
||||
low ^= value + 0x9e3779b97f4a7c15ull + (low << 6) + (low >> 2);
|
||||
low *= 0xbf58476d1ce4e5b9ull;
|
||||
high ^= value + 0x94d049bb133111ebull + (high << 7) + (high >> 3);
|
||||
high *= 0x9e3779b185ebca87ull;
|
||||
};
|
||||
add(forceCandidates_ ? 1u : 0u);
|
||||
std::uint64_t activeCount = 0;
|
||||
for (const auto& query : queries_)
|
||||
if (forceCandidates_ || query.features.candidateEvaluationUseful) ++activeCount;
|
||||
add(activeCount);
|
||||
for (const auto& query : queries_)
|
||||
{
|
||||
if (!forceCandidates_ && !query.features.candidateEvaluationUseful) continue;
|
||||
add(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(query.recipe)));
|
||||
add(static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(query.plan)));
|
||||
add(query.features.densityCacheContextLow);
|
||||
add(query.features.densityCacheContextHigh);
|
||||
}
|
||||
if (low == 0 && high == 0) high = 1;
|
||||
densityCacheContextLow_ = low;
|
||||
densityCacheContextHigh_ = high;
|
||||
}
|
||||
}
|
||||
|
||||
ScopedWorldAssemblyFeatureQueriesV1::~ScopedWorldAssemblyFeatureQueriesV1()
|
||||
@@ -56,6 +84,15 @@ const WorldAssemblyFeatureTileListV1* ScopedWorldAssemblyFeatureQueriesV1::find(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<std::uint64_t, 2> ScopedWorldAssemblyFeatureQueriesV1::densityCacheContext(
|
||||
std::int64_t x, std::int64_t y, std::int64_t z) noexcept
|
||||
{
|
||||
(void)x; (void)y; (void)z;
|
||||
if (!ActiveAssemblyQueries) return {};
|
||||
return {ActiveAssemblyQueries->densityCacheContextLow_,
|
||||
ActiveAssemblyQueries->densityCacheContextHigh_};
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
|
||||
std::span<const WorldAssemblyFeatureBoundsV1> features,
|
||||
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
|
||||
@@ -124,6 +161,8 @@ namespace world_assembly_detail
|
||||
{
|
||||
using FSharedHeightfieldColumnCache =
|
||||
VoxelForgeCoreShared::TSharedColumnValueCache<HeightfieldPointSample,131072,4>;
|
||||
using FSharedDensitySampleCache = VoxelForgeCoreShared::TSharedColumnValueCache<
|
||||
float,32768,4,VoxelForgeCoreShared::FDensitySampleCacheKey>;
|
||||
|
||||
// Preserve recipe order without allocating or looking up metadata in the point loop.
|
||||
// Garder l'ordre de recette sans allocation ni recherche de metadonnees par point.
|
||||
@@ -2570,6 +2609,7 @@ WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList(
|
||||
bounds.push_back(bound);
|
||||
}
|
||||
result.featureIndices=BuildWorldAssemblyFeatureTileListV1(bounds,minimum,maximum);
|
||||
FinalizeWorldAssemblyFeatureTileListDensityContextV1(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2634,6 +2674,8 @@ void Heightfield2D::resetThreadColumnCacheStats(bool captureCoordinates, bool cl
|
||||
Stats.columnConflictMisses=0;
|
||||
Stats.sharedColumnHits=0;
|
||||
Stats.columnEvaluations=0;
|
||||
Stats.densitySampleCacheHits=0;
|
||||
Stats.densitySampleCacheComputes=0;
|
||||
Stats.columnComputeMilliseconds=0.0;
|
||||
Stats.captureCoordinates=captureCoordinates;
|
||||
Stats.coordinateCaptureComplete=true;
|
||||
|
||||
Reference in new issue
Block a user