wip(standalone): GPU step 3 -- GPU decides far rock/air, sparse CPU exceptions, hybrid viewer (H)
1,114 of 1,266 far tiles meshed on the GPU; vs CPU and vs WARP: 0 sign/topology differences, including the protected/thin exception audit; max symmetric vertex distance 0.125 cm; repeat RX runs identical; GPU field+mesh 0.029 ms/tile average. Strata survival 7,200/7,200, but connector survival fails (GPU lacks the connector repair panels). Hybrid cold clipmap 5.74 s vs CPU 2.23 s: dispatch and readback overhead, not compute (~33 ms of GPU work in total). 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
b61ccff549
commit
782f195bdb
15 files changed
+1018
-173
No files matched your search
@@ -501,8 +501,9 @@ public:
|
||||
{return std::abs(A.z-B.z)<0.01f;}),Out.stratumBoundaries.end());
|
||||
}
|
||||
Out.sampleShapeRanges.resize(SampleCount);
|
||||
Out.rockAirSigns.assign((SampleCount+31u)/32u,0u);
|
||||
Out.protectedAir.assign((SampleCount+31u)/32u,0u);
|
||||
Out.protectedSolid.assign((SampleCount+31u)/32u,0u);
|
||||
Out.protectedThin.assign((SampleCount+31u)/32u,0u);
|
||||
|
||||
size_t SampleIndex=0;
|
||||
for(int32 Z=-1;Z<=Cells+1;++Z)
|
||||
@@ -512,13 +513,53 @@ public:
|
||||
const int32 WX=Origin.X+X*Step,WY=Origin.Y+Y*Step,WZ=Origin.Z+Z*Step;
|
||||
if(WX>=Min[0]&&WX<=Max[0]&&WY>=Min[1]&&WY<=Max[1]&&WZ>=Min[2]&&WZ<=Max[2])
|
||||
{Error="tile lattice intersects the near analytic/detail region; this export is far-field only";return false;}
|
||||
const float Density=DensityAtLattice(WX,WY,WZ);
|
||||
if(Density>=0.0f)Out.rockAirSigns[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
if(IsInsideCriticalAir(WX,WY,WZ))
|
||||
{
|
||||
Out.protectedAir[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
++Out.protectedSampleCount;
|
||||
}
|
||||
else if(IsNearCriticalAirBoundary(WX,WY,WZ,4.0))
|
||||
{
|
||||
// Preserve exact signs within roughly one far-field lattice step of
|
||||
// route air. This catches thin walls/floors at the route boundary
|
||||
// without evaluating the ordinary far field on the CPU.
|
||||
const float CriticalDensity=AnalyticDensityAt(static_cast<float>(WX),
|
||||
static_cast<float>(WY),static_cast<float>(WZ));
|
||||
Out.protectedThin[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
if(CriticalDensity>=0.0f)Out.protectedAir[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
else
|
||||
{
|
||||
Out.protectedSolid[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
++Out.protectedSolidSampleCount;
|
||||
}
|
||||
++Out.protectedThinSampleCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bSealSample=false;
|
||||
for(const VoxelForgeCore::FarFieldStratumBoundary& Boundary:Out.stratumBoundaries)
|
||||
{
|
||||
if(std::abs(static_cast<double>(WZ)-Boundary.z)>0.5)continue;
|
||||
// Only the exact lattice samples that lie on a seal height need a
|
||||
// CPU sign override. The surrounding far field remains GPU-decided.
|
||||
const float SealDensity=AnalyticDensityAt(static_cast<float>(WX),
|
||||
static_cast<float>(WY),static_cast<float>(WZ));
|
||||
if(SealDensity>=0.0f)
|
||||
Out.protectedAir[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
else
|
||||
Out.protectedSolid[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
++Out.protectedSealSampleCount;
|
||||
if(SealDensity<0.0f)++Out.protectedSolidSampleCount;
|
||||
bSealSample=true;
|
||||
break;
|
||||
}
|
||||
if(!bSealSample&&IsInsideSolidOverlay(WX,WY,WZ))
|
||||
{
|
||||
Out.protectedSolid[SampleIndex>>5]|=1u<<(SampleIndex&31u);
|
||||
++Out.protectedSolidSampleCount;
|
||||
++Out.protectedSolidOverlaySampleCount;
|
||||
}
|
||||
}
|
||||
const auto Found=SpatialBins.find(PointBin(WX,WY,WZ));
|
||||
if(Found==SpatialBins.end())continue;
|
||||
auto& Ids=PerSample[SampleIndex];
|
||||
@@ -586,6 +627,19 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsFarFieldTileOutsideNearRegion(const FIntVector& Origin,int32 Cells,int32 Step) const
|
||||
{
|
||||
if(!FarBuilt||Cells<=0||Cells>32||Step<=0)return false;
|
||||
const int32 Origins[3]={Origin.X,Origin.Y,Origin.Z};
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
{
|
||||
const double First=std::ceil((static_cast<double>(Min[Axis])-Origins[Axis])/Step);
|
||||
const double Last=std::floor((static_cast<double>(Max[Axis])-Origins[Axis])/Step);
|
||||
if(First>Cells+1||Last< -1)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebugCriticalAirAt(double X,double Y,double Z) const { return IsInsideCriticalAir(X,Y,Z); }
|
||||
bool DebugSolidOverlayAt(double X,double Y,double Z) const { return IsInsideSolidOverlay(X,Y,Z); }
|
||||
|
||||
@@ -3336,6 +3390,27 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsNearCriticalAirBoundary(int32 X,int32 Y,int32 Z,double Margin) const
|
||||
{
|
||||
const auto It=CriticalAirBins.find(PointBin(X,Y,Z));
|
||||
if(It==CriticalAirBins.end())return false;
|
||||
for(uint32 Id:It->second)
|
||||
{
|
||||
const FPrimitive& P=Primitives[Id];if(!P.bCarve||!P.bCritical)continue;
|
||||
if(X<P.Min[0]-Margin||X>P.Max[0]+Margin
|
||||
||Y<P.Min[1]-Margin||Y>P.Max[1]+Margin
|
||||
||Z<P.Min[2]-Margin||Z>P.Max[2]+Margin)continue;
|
||||
float Distance=ShapeDistance(P,X,Y,Z);
|
||||
if(P.bHasFloorClip)
|
||||
{
|
||||
const float FloorDistance=static_cast<float>(FloorAt(P,X,Y)-Z);
|
||||
Distance=SmoothMaxDistance(Distance,FloorDistance,static_cast<float>(P.FloorBlendRadius));
|
||||
}
|
||||
if(std::abs(Distance)<=Margin)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsInsideSolidOverlay(double X,double Y,double Z) const
|
||||
{
|
||||
const auto It=SpatialBins.find(PointBin(X,Y,Z));
|
||||
|
||||
Reference in new issue
Block a user