wip(assembler): round S2 (Sol) -- shared per-tile feature queries for every Core family; island placement 30x faster
Island-only worker context replaced by a shared worker-local candidate domain (WorldAssemblyFeatureTileListV1 / ScopedWorldAssemblyFeatureQueriesV1) serving SurfaceWorld, FloatingIslands and far-shell queries; a per-domain cost hint keeps the full scan on small plans. Content placement prepares the same queries and skips proven air: owner island cell p95 7.07 -> 0.24 ms; populated fixture 572 exact placements, 3.27 s -> 29 ms. Full/candidate hashes equal with dropped-feature controls on both families; S1 default W1/W8/W16 hashes reproduced; Core-off and M2 fixtures pass. Default contract for future families in ARCHITECTURE 8.23. Run on gpt-6.1-sol medium. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
d6e69420e8
commit
6ddb834b1c
19 files changed
+1849
-132
No files matched your search
@@ -15,6 +15,45 @@
|
||||
|
||||
namespace VoxelForgeCore
|
||||
{
|
||||
namespace
|
||||
{
|
||||
thread_local const ScopedWorldAssemblyFeatureQueriesV1* ActiveAssemblyQueries = nullptr;
|
||||
}
|
||||
|
||||
ScopedWorldAssemblyFeatureQueriesV1::ScopedWorldAssemblyFeatureQueriesV1(
|
||||
std::span<const WorldAssemblyFeatureQueryV1> queries, bool forceCandidatesForValidation) noexcept
|
||||
: queries_(queries), forceCandidates_(forceCandidatesForValidation), previous_(ActiveAssemblyQueries)
|
||||
{
|
||||
// Resolve cost hints once per domain, never rediscover them in the sample loop.
|
||||
// Resoudre les indications de cout une fois par domaine, hors de la boucle d'echantillons.
|
||||
ActiveAssemblyQueries = nullptr;
|
||||
for (const auto& query : queries_)
|
||||
if (forceCandidates_ || query.features.candidateEvaluationUseful)
|
||||
{ ActiveAssemblyQueries = this; break; }
|
||||
}
|
||||
|
||||
ScopedWorldAssemblyFeatureQueriesV1::~ScopedWorldAssemblyFeatureQueriesV1()
|
||||
{ ActiveAssemblyQueries = previous_; }
|
||||
|
||||
const WorldAssemblyFeatureTileListV1* ScopedWorldAssemblyFeatureQueriesV1::find(
|
||||
const void* recipe, const void* plan, std::int64_t x, std::int64_t y,
|
||||
std::int64_t z, bool columnOnly) noexcept
|
||||
{
|
||||
const std::array<std::int64_t, 3> point{x,y,z};
|
||||
if (ActiveAssemblyQueries)
|
||||
for (const auto& query : ActiveAssemblyQueries->queries_)
|
||||
{
|
||||
if (query.recipe != recipe || query.plan != plan
|
||||
|| (!ActiveAssemblyQueries->forceCandidates_ && !query.features.candidateEvaluationUseful)) continue;
|
||||
bool inside = true;
|
||||
for (std::size_t axis=0; axis<(columnOnly ? 2u : 3u); ++axis)
|
||||
inside = inside && point[axis]>=query.features.minimumSubvoxels[axis]
|
||||
&& point[axis]<=query.features.maximumSubvoxels[axis];
|
||||
if (inside) return &query.features;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
|
||||
std::span<const WorldAssemblyFeatureBoundsV1> features,
|
||||
const std::array<std::int64_t, 3>& tileMinimumSubvoxels,
|
||||
@@ -52,8 +91,54 @@ std::vector<std::uint32_t> BuildWorldAssemblyFeatureTileListV1(
|
||||
return result;
|
||||
}
|
||||
|
||||
WorldAssemblyFeatureColumnRangeV1 BuildWorldAssemblyFeatureColumnRangeV1(
|
||||
std::span<const WorldAssemblyFeatureBoundsV1> bounds, std::int64_t x, std::int64_t y)
|
||||
{
|
||||
WorldAssemblyFeatureColumnRangeV1 result;
|
||||
result.known=true;
|
||||
result.empty=true;
|
||||
for(const auto& bound:bounds)
|
||||
{
|
||||
for(std::size_t axis=0;axis<3;++axis)
|
||||
if(bound.minimumSubvoxels[axis]>bound.maximumSubvoxels[axis]) return {};
|
||||
if(x<bound.minimumSubvoxels[0]||x>bound.maximumSubvoxels[0]
|
||||
||y<bound.minimumSubvoxels[1]||y>bound.maximumSubvoxels[1])continue;
|
||||
if(result.empty)
|
||||
{
|
||||
result.minimumZSubvoxels=bound.minimumSubvoxels[2];
|
||||
result.maximumZSubvoxels=bound.maximumSubvoxels[2];
|
||||
result.empty=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.minimumZSubvoxels=std::min(result.minimumZSubvoxels,bound.minimumSubvoxels[2]);
|
||||
result.maximumZSubvoxels=std::max(result.maximumZSubvoxels,bound.maximumSubvoxels[2]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace world_assembly_detail
|
||||
{
|
||||
// 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.
|
||||
struct FeatureRange
|
||||
{
|
||||
const std::vector<WorldAssemblyFeatureV1>& features;
|
||||
const WorldAssemblyFeatureTileListV1* list;
|
||||
struct Iterator
|
||||
{
|
||||
const FeatureRange* range;
|
||||
std::size_t index;
|
||||
const WorldAssemblyFeatureV1& operator*() const
|
||||
{ return range->features[range->list ? range->list->featureIndices[index] : index]; }
|
||||
Iterator& operator++() { ++index; return *this; }
|
||||
bool operator!=(const Iterator& other) const { return index!=other.index; }
|
||||
};
|
||||
Iterator begin() const { return {this,0}; }
|
||||
Iterator end() const { return {this,list ? list->featureIndices.size() : features.size()}; }
|
||||
};
|
||||
|
||||
constexpr std::int64_t Q16 = 1ll << 16;
|
||||
constexpr std::int64_t MinimumCliffShelfThicknessSubvoxels = 2 * WorldAssemblySubvoxelsPerVoxel;
|
||||
constexpr std::int64_t MaxSerializedMagnitude = 1000000000000ll;
|
||||
@@ -1294,7 +1379,8 @@ namespace world_assembly_detail
|
||||
:CapHeightV2(recipe,plan,neighbor,x,y);
|
||||
std::int64_t cap=primaryCap+((neighborCap-primaryCap)
|
||||
*(static_cast<std::int64_t>(Q16)-selection.primaryWeightQ16))/Q16;
|
||||
for(const auto& feature:plan.features)
|
||||
const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(&recipe,&plan,x,y,0,true);
|
||||
for(const auto& feature:FeatureRange{plan.features,list})
|
||||
{
|
||||
if(feature.type==1)
|
||||
{
|
||||
@@ -1730,6 +1816,42 @@ bool Heightfield2D::isValid() const noexcept
|
||||
});
|
||||
}
|
||||
|
||||
WorldAssemblyFeatureTileListV1 Heightfield2D::buildFeatureTileList(
|
||||
const std::array<std::int64_t,3>& minimum,
|
||||
const std::array<std::int64_t,3>& maximum) const
|
||||
{
|
||||
WorldAssemblyFeatureTileListV1 result;
|
||||
// One shelf in the staged owner plan has no useful scan to shorten. Keep its full evaluator.
|
||||
// Une seule corniche dans le plan auteur ne justifie pas la selection par echantillon.
|
||||
result.candidateEvaluationUseful=plan_->features.size()>1;
|
||||
result.minimumSubvoxels=minimum;
|
||||
result.maximumSubvoxels=maximum;
|
||||
std::vector<WorldAssemblyFeatureBoundsV1> bounds;
|
||||
bounds.reserve(plan_->features.size());
|
||||
for(const auto& feature:plan_->features)
|
||||
{
|
||||
WorldAssemblyFeatureBoundsV1 bound{feature.stableId,
|
||||
feature.minimumSubvoxels,feature.maximumSubvoxels};
|
||||
// Column writers affect every Z; finite volumes have an exact no-op outside their box.
|
||||
// Les modificateurs de colonne agissent sur tout Z; les volumes sont neutres hors boite.
|
||||
if(feature.type==1||feature.type==3)
|
||||
{
|
||||
bound.minimumSubvoxels[2]=std::numeric_limits<std::int64_t>::min();
|
||||
bound.maximumSubvoxels[2]=std::numeric_limits<std::int64_t>::max();
|
||||
}
|
||||
else if(feature.type!=2&&feature.type!=4)
|
||||
{
|
||||
// Unknown future operations must opt into a proof before they can be culled.
|
||||
// Une operation inconnue reste active tant que sa preuve manque.
|
||||
bound.minimumSubvoxels.fill(std::numeric_limits<std::int64_t>::min());
|
||||
bound.maximumSubvoxels.fill(std::numeric_limits<std::int64_t>::max());
|
||||
}
|
||||
bounds.push_back(bound);
|
||||
}
|
||||
result.featureIndices=BuildWorldAssemblyFeatureTileListV1(bounds,minimum,maximum);
|
||||
return result;
|
||||
}
|
||||
|
||||
HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
|
||||
std::int64_t z) const noexcept
|
||||
{
|
||||
@@ -1744,7 +1866,8 @@ HeightfieldPointSample Heightfield2D::point(std::int64_t x, std::int64_t y,
|
||||
out.capHeightSubvoxels-z);
|
||||
if(recipe_->evaluatorVersion>=2)
|
||||
{
|
||||
for(const auto& feature:plan_->features)
|
||||
const auto* list=ScopedWorldAssemblyFeatureQueriesV1::find(recipe_,plan_,x,y,z);
|
||||
for(const auto& feature:FeatureRange{plan_->features,list})
|
||||
{
|
||||
if(feature.type==2)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user