far shells: 3D cell bins for HasDownwardHit/HasUpwardHit candidates (L104, exact)
Near tiles legitimately need floor/ceiling boundary patches, but every hit query scanned the tile's whole XY column. Direction-specific 3D cell bins conservatively index triangle bounds; the existing predicates still decide every hit. Candidate visits per query -88..-99%; identical query and hit counts. Hit-function cost ~1.7-1.9 -> ~0.35 s per FI/TN run. Near-ready @W12: FI 1.63 -> 1.52, TN 1.51 -> 1.38, VS 1.00 -> 0.92 s. All tile/whole hashes equal L103 (Clang/GCC W1/W8/W12). The 24-28% "idle" is in the serial base-shape (~0.24 s) and far-parent (~0.13 s) phases; the LOD0-1 drain is >= 99.4% busy. Fast tier PASS; Linux UE builds; FI Unreal arrival 1.96-1.98 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
8d62ba57ed
commit
6a80cb0ab3
15 files changed
+2009
-15
No files matched your search
@@ -6,7 +6,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -823,11 +826,18 @@ inline void AppendFarConnectorShells(const FStandaloneShapeWorld& Shape,const FV
|
||||
struct FFarMeshColumnIndex
|
||||
{
|
||||
FFarMeshColumnIndex(const FVoxelMeshData& InMesh,const FIntVector& InOrigin,int32 InExtent,
|
||||
int32 InCells,double InStep,double InVoxelSizeCm)
|
||||
int32 InCells,double InStep,double InVoxelSizeCm,int32 InLevel)
|
||||
:Mesh(InMesh),Origin(InOrigin),Extent(InExtent),Cells(InCells),Step(InStep),
|
||||
VoxelSizeCm(InVoxelSizeCm),
|
||||
ByColumn(static_cast<size_t>(InCells)*InCells)
|
||||
Level(InLevel),ByColumn(static_cast<size_t>(InCells)*InCells),
|
||||
bUseHitIndex(UseHitIndex()),bMeasureHitStats(MeasureHitStats())
|
||||
{
|
||||
if(bUseHitIndex)
|
||||
{
|
||||
const size_t CellCount=static_cast<size_t>(Cells)*Cells*Cells;
|
||||
ByDownwardCell.resize(CellCount);
|
||||
ByUpwardCell.resize(CellCount);
|
||||
}
|
||||
for(int32 Triangle=0;Triangle+2<Mesh.Triangles.Num();Triangle+=3)
|
||||
{
|
||||
const int32 A=Mesh.Triangles[Triangle],B=Mesh.Triangles[Triangle+1],C=Mesh.Triangles[Triangle+2];
|
||||
@@ -836,15 +846,47 @@ struct FFarMeshColumnIndex
|
||||
const double MaxX=std::max({PositionAxis(A,0),PositionAxis(B,0),PositionAxis(C,0)});
|
||||
const double MinY=std::min({PositionAxis(A,1),PositionAxis(B,1),PositionAxis(C,1)});
|
||||
const double MaxY=std::max({PositionAxis(A,1),PositionAxis(B,1),PositionAxis(C,1)});
|
||||
const double MinZ=std::min({PositionAxis(A,2),PositionAxis(B,2),PositionAxis(C,2)});
|
||||
const double MaxZ=std::max({PositionAxis(A,2),PositionAxis(B,2),PositionAxis(C,2)});
|
||||
const int32 X0=std::clamp(static_cast<int32>(std::floor((MinX-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 X1=std::clamp(static_cast<int32>(std::floor((MaxX-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 Y0=std::clamp(static_cast<int32>(std::floor((MinY-Origin.Y)/Step)),0,Cells-1);
|
||||
const int32 Y1=std::clamp(static_cast<int32>(std::floor((MaxY-Origin.Y)/Step)),0,Cells-1);
|
||||
for(int32 Y=Y0;Y<=Y1;++Y)for(int32 X=X0;X<=X1;++X)
|
||||
ByColumn[static_cast<size_t>(Y)*Cells+X].push_back(Triangle/3);
|
||||
if(!bUseHitIndex||A>=Mesh.Normals.Num()||B>=Mesh.Normals.Num()
|
||||
||C>=Mesh.Normals.Num())continue;
|
||||
const double NormalSum=Mesh.Normals[A].Z+Mesh.Normals[B].Z+Mesh.Normals[C].Z;
|
||||
const bool bDown=!(NormalSum>=-1.5),bUp=!(NormalSum<=1.5);
|
||||
if(!bDown&&!bUp)continue;
|
||||
const double ZMargin=(MaxZ-MinZ)*2.0e-7+1.0e-8;
|
||||
const int32 Z0=std::clamp(static_cast<int32>(std::floor((MinZ-ZMargin-Origin.Z)/Step)),0,Cells-1);
|
||||
const int32 Z1=std::clamp(static_cast<int32>(std::floor((MaxZ+ZMargin-Origin.Z)/Step)),0,Cells-1);
|
||||
for(int32 Z=Z0;Z<=Z1;++Z)for(int32 Y=Y0;Y<=Y1;++Y)for(int32 X=X0;X<=X1;++X)
|
||||
{
|
||||
const size_t Cell=(static_cast<size_t>(Z)*Cells+Y)*Cells+X;
|
||||
if(bDown)ByDownwardCell[Cell].push_back(Triangle/3);
|
||||
if(bUp)ByUpwardCell[Cell].push_back(Triangle/3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~FFarMeshColumnIndex()
|
||||
{
|
||||
if(!bMeasureHitStats||DownQueries+UpQueries==0)return;
|
||||
std::fprintf(stderr,
|
||||
"[FarHitQueryStats] lod=%d origin=(%d,%d,%d) "
|
||||
"down_queries=%llu down_candidate_tests=%llu down_hit_queries=%llu "
|
||||
"up_queries=%llu up_candidate_tests=%llu up_hit_queries=%llu\n",
|
||||
Level,Origin.X,Origin.Y,Origin.Z,
|
||||
static_cast<unsigned long long>(DownQueries),
|
||||
static_cast<unsigned long long>(DownCandidateTests),
|
||||
static_cast<unsigned long long>(DownHitQueries),
|
||||
static_cast<unsigned long long>(UpQueries),
|
||||
static_cast<unsigned long long>(UpCandidateTests),
|
||||
static_cast<unsigned long long>(UpHitQueries));
|
||||
}
|
||||
|
||||
std::vector<double> VerticalHits(double X,double Y,double ZMin,double ZMax) const
|
||||
{
|
||||
const int32 CX=std::clamp(static_cast<int32>(std::floor((X-Origin.X)/Step)),0,Cells-1);
|
||||
@@ -873,11 +915,13 @@ struct FFarMeshColumnIndex
|
||||
|
||||
bool HasDownwardHit(double X,double Y,double Z,double Tolerance) const
|
||||
{
|
||||
const int32 CX=std::clamp(static_cast<int32>(std::floor((X-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 CY=std::clamp(static_cast<int32>(std::floor((Y-Origin.Y)/Step)),0,Cells-1);
|
||||
const auto& Candidates=ByColumn[static_cast<size_t>(CY)*Cells+CX];
|
||||
if(bMeasureHitStats)++DownQueries;
|
||||
std::vector<int32> CandidateScratch;
|
||||
const auto& Candidates=FindHitCandidates(X,Y,Z,Tolerance,true,CandidateScratch);
|
||||
bool bHit=false;
|
||||
for(const int32 Triangle:Candidates)
|
||||
{
|
||||
if(bMeasureHitStats)++DownCandidateTests;
|
||||
const int32 IA=Mesh.Triangles[Triangle*3],IB=Mesh.Triangles[Triangle*3+1],
|
||||
IC=Mesh.Triangles[Triangle*3+2];
|
||||
if(IA<0||IB<0||IC<0||IA>=Mesh.Normals.Num()||IB>=Mesh.Normals.Num()
|
||||
@@ -893,9 +937,10 @@ struct FFarMeshColumnIndex
|
||||
const double W=1.0-U-V;
|
||||
if(U<-1.0e-7||V<-1.0e-7||W<-1.0e-7)continue;
|
||||
const double Hit=AZ+U*(BZ-AZ)+V*(CZ-AZ);
|
||||
if(std::abs(Hit-Z)<=Tolerance)return true;
|
||||
if(std::abs(Hit-Z)<=Tolerance){bHit=true;break;}
|
||||
}
|
||||
return false;
|
||||
if(bMeasureHitStats&&bHit)++DownHitQueries;
|
||||
return bHit;
|
||||
}
|
||||
|
||||
bool HasDownwardCellCoverage(double X0,double Y0,double X1,double Y1,
|
||||
@@ -917,11 +962,13 @@ struct FFarMeshColumnIndex
|
||||
|
||||
bool HasUpwardHit(double X,double Y,double Z,double Tolerance) const
|
||||
{
|
||||
const int32 CX=std::clamp(static_cast<int32>(std::floor((X-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 CY=std::clamp(static_cast<int32>(std::floor((Y-Origin.Y)/Step)),0,Cells-1);
|
||||
const auto& Candidates=ByColumn[static_cast<size_t>(CY)*Cells+CX];
|
||||
if(bMeasureHitStats)++UpQueries;
|
||||
std::vector<int32> CandidateScratch;
|
||||
const auto& Candidates=FindHitCandidates(X,Y,Z,Tolerance,false,CandidateScratch);
|
||||
bool bHit=false;
|
||||
for(const int32 Triangle:Candidates)
|
||||
{
|
||||
if(bMeasureHitStats)++UpCandidateTests;
|
||||
const int32 IA=Mesh.Triangles[Triangle*3],IB=Mesh.Triangles[Triangle*3+1],
|
||||
IC=Mesh.Triangles[Triangle*3+2];
|
||||
if(IA<0||IB<0||IC<0||IA>=Mesh.Normals.Num()||IB>=Mesh.Normals.Num()
|
||||
@@ -937,9 +984,10 @@ struct FFarMeshColumnIndex
|
||||
const double W=1.0-U-V;
|
||||
if(U<-1.0e-7||V<-1.0e-7||W<-1.0e-7)continue;
|
||||
const double Hit=AZ+U*(BZ-AZ)+V*(CZ-AZ);
|
||||
if(std::abs(Hit-Z)<=Tolerance)return true;
|
||||
if(std::abs(Hit-Z)<=Tolerance){bHit=true;break;}
|
||||
}
|
||||
return false;
|
||||
if(bMeasureHitStats&&bHit)++UpHitQueries;
|
||||
return bHit;
|
||||
}
|
||||
|
||||
bool HasUpwardCellCoverage(double X0,double Y0,double X1,double Y1,
|
||||
@@ -952,6 +1000,53 @@ struct FFarMeshColumnIndex
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<int32>& FindHitCandidates(double X,double Y,double Z,double Tolerance,
|
||||
bool bDown,std::vector<int32>& Scratch) const
|
||||
{
|
||||
const int32 CX=std::clamp(static_cast<int32>(std::floor((X-Origin.X)/Step)),0,Cells-1);
|
||||
const int32 CY=std::clamp(static_cast<int32>(std::floor((Y-Origin.Y)/Step)),0,Cells-1);
|
||||
const size_t Column=static_cast<size_t>(CY)*Cells+CX;
|
||||
if(!bUseHitIndex||!std::isfinite(Tolerance)||Tolerance<0.0||Step<=0.0)
|
||||
return ByColumn[Column];
|
||||
const int32 Z0=std::clamp(static_cast<int32>(std::floor((Z-Tolerance-Origin.Z)/Step)),
|
||||
0,Cells-1);
|
||||
const int32 Z1=std::clamp(static_cast<int32>(std::floor((Z+Tolerance-Origin.Z)/Step)),
|
||||
0,Cells-1);
|
||||
const auto& Buckets=bDown?ByDownwardCell:ByUpwardCell;
|
||||
const size_t First=(static_cast<size_t>(Z0)*Cells+CY)*Cells+CX;
|
||||
if(Z0==Z1)return Buckets[First];
|
||||
Scratch.clear();
|
||||
for(int32 BucketZ=Z0;BucketZ<=Z1;++BucketZ)
|
||||
{
|
||||
const size_t Cell=(static_cast<size_t>(BucketZ)*Cells+CY)*Cells+CX;
|
||||
const auto& Candidates=Buckets[Cell];
|
||||
Scratch.insert(Scratch.end(),Candidates.begin(),Candidates.end());
|
||||
}
|
||||
std::sort(Scratch.begin(),Scratch.end());
|
||||
Scratch.erase(std::unique(Scratch.begin(),Scratch.end()),Scratch.end());
|
||||
return Scratch;
|
||||
}
|
||||
|
||||
static bool UseHitIndex()
|
||||
{
|
||||
static const bool Enabled=[]()
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L104_FAR_HIT_CACHE");
|
||||
return Value==nullptr||Value[0]!='0'||Value[1]!='\0';
|
||||
}();
|
||||
return Enabled;
|
||||
}
|
||||
|
||||
static bool MeasureHitStats()
|
||||
{
|
||||
static const bool Enabled=[]()
|
||||
{
|
||||
const char* Value=std::getenv("VOXELFORGE_L104_FAR_HIT_STATS");
|
||||
return Value&&Value[0]=='1'&&Value[1]=='\0';
|
||||
}();
|
||||
return Enabled;
|
||||
}
|
||||
|
||||
double PositionAxis(int32 Index,int32 Axis) const
|
||||
{
|
||||
const FVector& P=Mesh.Vertices[Index];
|
||||
@@ -963,7 +1058,12 @@ private:
|
||||
int32 Extent=0,Cells=0;
|
||||
double Step=1.0;
|
||||
double VoxelSizeCm=25.0;
|
||||
int32 Level=0;
|
||||
std::vector<std::vector<int32>> ByColumn;
|
||||
std::vector<std::vector<int32>> ByDownwardCell,ByUpwardCell;
|
||||
bool bUseHitIndex=false,bMeasureHitStats=false;
|
||||
mutable std::uint64_t DownQueries=0,DownCandidateTests=0,DownHitQueries=0;
|
||||
mutable std::uint64_t UpQueries=0,UpCandidateTests=0,UpHitQueries=0;
|
||||
};
|
||||
|
||||
inline std::vector<double> FindFarStratumCrossings(const FStandaloneShapeWorld& Shape,
|
||||
@@ -1092,7 +1192,7 @@ inline void AppendFarStratumShells(const FStandaloneOwnerWorld& World,const FSta
|
||||
const FColor Color=bHasColors&&OldVertexCount>0?Mesh.Colors[0]:FColor(160,160,160,255);
|
||||
const int32 Cells=std::max(1,World.CoarseTileCells);
|
||||
FFarMeshColumnIndex MeshIndex(Mesh,Origin,Extent,Cells,Step,
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0);
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0,Key.Level);
|
||||
std::vector<FVector> NewVertices,NewNormals;
|
||||
std::vector<FVector2f> NewUVs;std::vector<FColor> NewColors;
|
||||
std::vector<int32> NewTriangles;
|
||||
@@ -1374,7 +1474,7 @@ inline void AppendStratumBoundaryCeilings(const FStandaloneOwnerWorld& World,
|
||||
|
||||
const int32 Cells=std::max(1,World.CoarseTileCells);
|
||||
FFarMeshColumnIndex MeshIndex(Mesh,Origin,Extent,Cells,Step,
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0);
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0,Key.Level);
|
||||
const int32 OldVertexCount=Mesh.Vertices.Num();
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,OldVertexCount);
|
||||
const bool bHasUVs=Mesh.UVs.Num()==OldVertexCount;
|
||||
@@ -1550,7 +1650,7 @@ inline void AppendStratumBoundaryFloors(const FStandaloneOwnerWorld& World,
|
||||
|
||||
const int32 Cells=std::max(1,World.CoarseTileCells);
|
||||
FFarMeshColumnIndex MeshIndex(Mesh,Origin,Extent,Cells,Step,
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0);
|
||||
static_cast<double>(World.CoreVoxelSizeMicrons)/10000.0,Key.Level);
|
||||
const int32 OldVertexCount=Mesh.Vertices.Num();
|
||||
const int32 OldSurfaceVertices=std::clamp(Mesh.NumSurfaceVertices,0,OldVertexCount);
|
||||
const bool bHasUVs=Mesh.UVs.Num()==OldVertexCount;
|
||||
|
||||
Reference in new issue
Block a user