wip(L5, side branch): Linux offscreen captures + sampled far-LOD gate + no-op octave cutoff
Not for assembler/world: no octave qualifies under the cutoff's supremum criterion (world unchanged), and the build is +4.6% slower than L4 with the flag off (reviewer interleaved W12). Keeps the working EGL/GLES capture path and a gate whose metric is vertical height error, which overstates screen error on steep slopes; its "exact far meshes exceed 1 px / 126 px pop" claims are unconfirmed. See Linux5/REPORT.md "Orchestrator review". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ED33VnqepD4YY6gDAAA832
This commit is contained in:
1 parent
7770074503
commit
b21a19d561
33 files changed
+1977
-26
No files matched your search
@@ -0,0 +1,512 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamingWorld.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace StandaloneScreenSpaceGate
|
||||
{
|
||||
inline constexpr double OnePixelRadians=1.57079632679489661923/2560.0;
|
||||
|
||||
struct FErrorRow
|
||||
{
|
||||
int Lod=0;
|
||||
std::uint64_t Samples=0,Missing=0,Floating=0;
|
||||
double P50=0.0,P99=0.0,Maximum=0.0;
|
||||
bool Valid=false;
|
||||
};
|
||||
|
||||
struct FPopRow
|
||||
{
|
||||
int CoarseLod=0;
|
||||
std::uint64_t Samples=0,Cracks=0,Floating=0;
|
||||
double P50=0.0,P99=0.0,Maximum=0.0;
|
||||
bool Valid=false;
|
||||
};
|
||||
|
||||
struct FHeightGrid
|
||||
{
|
||||
int Side=65;
|
||||
double MinimumX=0.0,MaximumX=0.0,MinimumY=0.0,MaximumY=0.0;
|
||||
std::vector<std::vector<double>> Hits;
|
||||
|
||||
FHeightGrid()=default;
|
||||
FHeightGrid(int InSide,double MinX,double MaxX,double MinY,double MaxY)
|
||||
:Side(InSide),MinimumX(MinX),MaximumX(MaxX),MinimumY(MinY),MaximumY(MaxY),
|
||||
Hits(static_cast<std::size_t>(InSide)*InSide)
|
||||
{}
|
||||
|
||||
double X(int Ix) const
|
||||
{return MinimumX+(MaximumX-MinimumX)*Ix/(Side-1);}
|
||||
double Y(int Iy) const
|
||||
{return MinimumY+(MaximumY-MinimumY)*Iy/(Side-1);}
|
||||
|
||||
void Add(int Ix,int Iy,double Z)
|
||||
{
|
||||
auto& Values=Hits[static_cast<std::size_t>(Iy)*Side+Ix];
|
||||
Values.push_back(Z);
|
||||
}
|
||||
|
||||
const std::vector<double>& At(int Ix,int Iy) const
|
||||
{return Hits[static_cast<std::size_t>(Iy)*Side+Ix];}
|
||||
};
|
||||
|
||||
inline double Percentile(std::vector<double>& Values,double P)
|
||||
{
|
||||
if(Values.empty())return 0.0;
|
||||
std::sort(Values.begin(),Values.end());
|
||||
const std::size_t Index=std::min(Values.size()-1,
|
||||
static_cast<std::size_t>(std::ceil(P*Values.size())-1));
|
||||
return Values[Index];
|
||||
}
|
||||
|
||||
inline double TileAabbDistanceMeters(const FVoxelTileKey& Key,
|
||||
const FVector& CameraVoxels,double VoxelMeters)
|
||||
{
|
||||
const double Extent=static_cast<double>(Key.ExtentVoxels());
|
||||
const double Min[3]={static_cast<double>(Key.OriginVoxels().X),
|
||||
static_cast<double>(Key.OriginVoxels().Y),static_cast<double>(Key.OriginVoxels().Z)};
|
||||
const double Cam[3]={CameraVoxels.X,CameraVoxels.Y,CameraVoxels.Z};
|
||||
double Squared=0.0;
|
||||
for(int Axis=0;Axis<3;++Axis)
|
||||
{
|
||||
const double Delta=Cam[Axis]<Min[Axis]?Min[Axis]-Cam[Axis]
|
||||
:Cam[Axis]>Min[Axis]+Extent?Cam[Axis]-(Min[Axis]+Extent):0.0;
|
||||
Squared+=Delta*Delta;
|
||||
}
|
||||
return std::sqrt(Squared)*VoxelMeters;
|
||||
}
|
||||
|
||||
inline bool TileHasNativeSurface(const FVoxelTileKey& Key,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& Assembly,
|
||||
double VoxelMeters)
|
||||
{
|
||||
const double Extent=static_cast<double>(Key.ExtentVoxels());
|
||||
const double X=(static_cast<double>(Key.OriginVoxels().X)+Extent*0.5)*256.0;
|
||||
const double Y=(static_cast<double>(Key.OriginVoxels().Y)+Extent*0.5)*256.0;
|
||||
const double MinZ=static_cast<double>(Key.OriginVoxels().Z);
|
||||
const double MaxZ=MinZ+Extent;
|
||||
for(const auto& Plan:Assembly.plan.strates)
|
||||
{
|
||||
VoxelForgeCore::Heightfield2D Field(Assembly.recipe,Plan);
|
||||
if(!Field.isValid())continue;
|
||||
const auto Column=Field.point(static_cast<std::int64_t>(std::llround(X)),
|
||||
static_cast<std::int64_t>(std::llround(Y)),0);
|
||||
const double Ground=static_cast<double>(Column.groundHeightSubvoxels)/256.0;
|
||||
const double Cap=static_cast<double>(Column.capHeightSubvoxels)/256.0;
|
||||
const double Margin=std::max(2.0,0.25/VoxelMeters);
|
||||
if(Ground<Cap&&((Ground>=MinZ-Margin&&Ground<=MaxZ+Margin)
|
||||
||(Cap>=MinZ-Margin&&Cap<=MaxZ+Margin)))return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline FHeightGrid Rasterize(std::span<const FStandaloneCpuTile* const> Tiles,
|
||||
const FVoxelTileKey& Patch,double VoxelMeters,int Side=65)
|
||||
{
|
||||
const double Extent=static_cast<double>(Patch.ExtentVoxels())*VoxelMeters;
|
||||
const double MinX=static_cast<double>(Patch.OriginVoxels().X)*VoxelMeters;
|
||||
const double MinY=static_cast<double>(Patch.OriginVoxels().Y)*VoxelMeters;
|
||||
FHeightGrid Grid(Side,MinX,MinX+Extent,MinY,MinY+Extent);
|
||||
for(const FStandaloneCpuTile* Tile:Tiles)
|
||||
{
|
||||
if(!Tile||Tile->Indices.size()<3)continue;
|
||||
const std::size_t TriangleCount=Tile->Indices.size()/3;
|
||||
for(std::size_t Triangle=0;Triangle<TriangleCount;++Triangle)
|
||||
{
|
||||
if(Triangle<Tile->TriangleSkirtFaceMasks.size()
|
||||
&&Tile->TriangleSkirtFaceMasks[Triangle]!=0)continue;
|
||||
const std::uint32_t I0=Tile->Indices[Triangle*3];
|
||||
const std::uint32_t I1=Tile->Indices[Triangle*3+1];
|
||||
const std::uint32_t I2=Tile->Indices[Triangle*3+2];
|
||||
if(I0>=Tile->Vertices.size()||I1>=Tile->Vertices.size()||I2>=Tile->Vertices.size())continue;
|
||||
const auto& A=Tile->Vertices[I0].Position;
|
||||
const auto& B=Tile->Vertices[I1].Position;
|
||||
const auto& C=Tile->Vertices[I2].Position;
|
||||
const double Denominator=(B[1]-C[1])*(A[0]-C[0])+(C[0]-B[0])*(A[1]-C[1]);
|
||||
if(std::abs(Denominator)<1e-10)continue;
|
||||
const double MinTriX=std::min({A[0],B[0],C[0]}),MaxTriX=std::max({A[0],B[0],C[0]});
|
||||
const double MinTriY=std::min({A[1],B[1],C[1]}),MaxTriY=std::max({A[1],B[1],C[1]});
|
||||
const double StepX=Extent/(Side-1),StepY=Extent/(Side-1);
|
||||
const int Ix0=std::clamp(static_cast<int>(std::ceil((MinTriX-MinX)/StepX)),0,Side-1);
|
||||
const int Ix1=std::clamp(static_cast<int>(std::floor((MaxTriX-MinX)/StepX)),0,Side-1);
|
||||
const int Iy0=std::clamp(static_cast<int>(std::ceil((MinTriY-MinY)/StepY)),0,Side-1);
|
||||
const int Iy1=std::clamp(static_cast<int>(std::floor((MaxTriY-MinY)/StepY)),0,Side-1);
|
||||
for(int Iy=Iy0;Iy<=Iy1;++Iy)
|
||||
for(int Ix=Ix0;Ix<=Ix1;++Ix)
|
||||
{
|
||||
const double X=Grid.X(Ix),Y=Grid.Y(Iy);
|
||||
const double W0=((B[1]-C[1])*(X-C[0])+(C[0]-B[0])*(Y-C[1]))/Denominator;
|
||||
const double W1=((C[1]-A[1])*(X-C[0])+(A[0]-C[0])*(Y-C[1]))/Denominator;
|
||||
const double W2=1.0-W0-W1;
|
||||
if(W0<-1e-6||W1<-1e-6||W2<-1e-6)continue;
|
||||
Grid.Add(Ix,Iy,W0*A[2]+W1*B[2]+W2*C[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& Values:Grid.Hits)
|
||||
{
|
||||
std::sort(Values.begin(),Values.end());
|
||||
Values.erase(std::unique(Values.begin(),Values.end(),[](double A,double B)
|
||||
{return std::abs(A-B)<0.02;}),Values.end());
|
||||
}
|
||||
return Grid;
|
||||
}
|
||||
|
||||
inline std::vector<double> ExactHeightRoots(const FHeightGrid& Grid,int Ix,int Iy,
|
||||
const FVoxelTileKey& Patch,double VoxelMeters,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& Assembly)
|
||||
{
|
||||
std::vector<double> Roots;
|
||||
const double XSubvoxels=Grid.X(Ix)/VoxelMeters*256.0;
|
||||
const double YSubvoxels=Grid.Y(Iy)/VoxelMeters*256.0;
|
||||
const double MinZ=static_cast<double>(Patch.OriginVoxels().Z);
|
||||
const double MaxZ=MinZ+Patch.ExtentVoxels();
|
||||
for(const auto& Plan:Assembly.plan.strates)
|
||||
{
|
||||
VoxelForgeCore::Heightfield2D Field(Assembly.recipe,Plan);
|
||||
if(!Field.isValid())continue;
|
||||
const auto Column=Field.point(static_cast<std::int64_t>(std::llround(XSubvoxels)),
|
||||
static_cast<std::int64_t>(std::llround(YSubvoxels)),0);
|
||||
if(Column.groundHeightSubvoxels>=Column.capHeightSubvoxels)continue;
|
||||
const double Ground=static_cast<double>(Column.groundHeightSubvoxels)/(256.0)*VoxelMeters;
|
||||
const double Cap=static_cast<double>(Column.capHeightSubvoxels)/(256.0)*VoxelMeters;
|
||||
const double PlanMinZ=static_cast<double>(Plan.bottomChunkZ)*32.0*VoxelMeters;
|
||||
const double PlanMaxZ=static_cast<double>(Plan.topChunkZ+1)*32.0*VoxelMeters;
|
||||
if(Ground>=MinZ*VoxelMeters-0.25*VoxelMeters
|
||||
&&Ground<=MaxZ*VoxelMeters+0.25*VoxelMeters
|
||||
&&Ground>=PlanMinZ-0.25*VoxelMeters&&Ground<=PlanMaxZ+0.25*VoxelMeters)
|
||||
Roots.push_back(Ground);
|
||||
if(Cap>=MinZ*VoxelMeters-0.25*VoxelMeters
|
||||
&&Cap<=MaxZ*VoxelMeters+0.25*VoxelMeters
|
||||
&&Cap>=PlanMinZ-0.25*VoxelMeters&&Cap<=PlanMaxZ+0.25*VoxelMeters)
|
||||
Roots.push_back(Cap);
|
||||
}
|
||||
std::sort(Roots.begin(),Roots.end());
|
||||
Roots.erase(std::unique(Roots.begin(),Roots.end(),[](double A,double B)
|
||||
{return std::abs(A-B)<0.01;}),Roots.end());
|
||||
return Roots;
|
||||
}
|
||||
|
||||
inline bool MatchNearest(const std::vector<double>& Hits,const std::vector<bool>& Used,
|
||||
double Root,std::size_t& OutIndex,double& OutError)
|
||||
{
|
||||
double Best=std::numeric_limits<double>::infinity();std::size_t BestIndex=Hits.size();
|
||||
for(std::size_t I=0;I<Hits.size();++I)
|
||||
if(I>=Used.size()||!Used[I])
|
||||
{
|
||||
const double Error=std::abs(Hits[I]-Root);
|
||||
if(Error<Best){Best=Error;BestIndex=I;}
|
||||
}
|
||||
if(BestIndex==Hits.size())return false;
|
||||
OutIndex=BestIndex;OutError=Best;return true;
|
||||
}
|
||||
|
||||
inline FErrorRow CompareToExact(const FHeightGrid& MeshGrid,const FVoxelTileKey& Patch,
|
||||
double VoxelMeters,double PixelBudgetMeters,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& Assembly)
|
||||
{
|
||||
FErrorRow Row;Row.Lod=Patch.Level;Row.Valid=true;
|
||||
std::vector<double> Errors;
|
||||
for(int Iy=0;Iy<MeshGrid.Side;++Iy)
|
||||
for(int Ix=0;Ix<MeshGrid.Side;++Ix)
|
||||
{
|
||||
const auto Roots=ExactHeightRoots(MeshGrid,Ix,Iy,Patch,VoxelMeters,Assembly);
|
||||
if(Roots.empty()&&MeshGrid.At(Ix,Iy).empty())continue;
|
||||
std::vector<bool> Used(MeshGrid.At(Ix,Iy).size(),false);
|
||||
for(double Root:Roots)
|
||||
{
|
||||
std::size_t HitIndex=0;double ErrorMeters=0.0;
|
||||
if(!MatchNearest(MeshGrid.At(Ix,Iy),Used,Root,HitIndex,ErrorMeters))
|
||||
{
|
||||
++Row.Missing;
|
||||
Errors.push_back(2.0);
|
||||
continue;
|
||||
}
|
||||
Used[HitIndex]=true;
|
||||
Errors.push_back(ErrorMeters/PixelBudgetMeters);
|
||||
++Row.Samples;
|
||||
}
|
||||
// Heightfield2D declares ground and cap roots only. Other mesh intersections may
|
||||
// be valid tunnel geometry; the exact-mesh comparison checks extra pieces.
|
||||
}
|
||||
Row.P50=Percentile(Errors,0.50);Row.P99=Percentile(Errors,0.99);
|
||||
Row.Maximum=Errors.empty()?0.0:*std::max_element(Errors.begin(),Errors.end());
|
||||
Row.Samples=Errors.size();
|
||||
return Row;
|
||||
}
|
||||
|
||||
inline FErrorRow CompareToReference(const FHeightGrid& MeshGrid,
|
||||
const FHeightGrid& ReferenceGrid,int Lod,double PixelBudgetMeters)
|
||||
{
|
||||
FErrorRow Row;Row.Lod=Lod;Row.Valid=true;
|
||||
std::vector<double> Errors;
|
||||
for(int Iy=0;Iy<ReferenceGrid.Side;++Iy)
|
||||
for(int Ix=0;Ix<ReferenceGrid.Side;++Ix)
|
||||
{
|
||||
const auto& Reference=ReferenceGrid.At(Ix,Iy);
|
||||
const auto& Mesh=MeshGrid.At(Ix,Iy);
|
||||
if(Reference.empty()&&Mesh.empty())continue;
|
||||
std::vector<bool> Used(Mesh.size(),false);
|
||||
for(double ReferenceZ:Reference)
|
||||
{
|
||||
std::size_t HitIndex=0;double ErrorMeters=0.0;
|
||||
if(!MatchNearest(Mesh,Used,ReferenceZ,HitIndex,ErrorMeters))
|
||||
{
|
||||
++Row.Missing;Errors.push_back(2.0);continue;
|
||||
}
|
||||
Used[HitIndex]=true;
|
||||
Errors.push_back(ErrorMeters/PixelBudgetMeters);
|
||||
}
|
||||
for(std::size_t I=0;I<Mesh.size();++I)
|
||||
if(!Used[I]){++Row.Floating;Errors.push_back(2.0);}
|
||||
}
|
||||
Row.P50=Percentile(Errors,0.50);Row.P99=Percentile(Errors,0.99);
|
||||
Row.Maximum=Errors.empty()?0.0:*std::max_element(Errors.begin(),Errors.end());
|
||||
Row.Samples=Errors.size();
|
||||
return Row;
|
||||
}
|
||||
|
||||
inline FPopRow ComparePop(const FHeightGrid& Coarse,const FHeightGrid& Fine,
|
||||
const FVoxelTileKey& Patch,double VoxelMeters,double PixelBudgetMeters,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& Assembly)
|
||||
{
|
||||
FPopRow Row;Row.CoarseLod=Patch.Level;Row.Valid=true;
|
||||
std::vector<double> Errors;
|
||||
for(int Iy=0;Iy<Coarse.Side;++Iy)
|
||||
for(int Ix=0;Ix<Coarse.Side;++Ix)
|
||||
{
|
||||
const auto& CoarseHits=Coarse.At(Ix,Iy);
|
||||
const auto& FineHits=Fine.At(Ix,Iy);
|
||||
const auto Roots=ExactHeightRoots(Coarse,Ix,Iy,Patch,VoxelMeters,Assembly);
|
||||
if(Roots.empty()&&CoarseHits.empty()&&FineHits.empty())continue;
|
||||
std::vector<bool> CoarseUsed(CoarseHits.size(),false);
|
||||
std::vector<bool> FineUsed(FineHits.size(),false);
|
||||
for(double Root:Roots)
|
||||
{
|
||||
std::size_t CoarseIndex=0,FineIndex=0;
|
||||
double CoarseError=0.0,FineError=0.0;
|
||||
if(!MatchNearest(CoarseHits,CoarseUsed,Root,CoarseIndex,CoarseError)
|
||||
||!MatchNearest(FineHits,FineUsed,Root,FineIndex,FineError))
|
||||
{
|
||||
++Row.Cracks;Errors.push_back(2.0);continue;
|
||||
}
|
||||
CoarseUsed[CoarseIndex]=true;
|
||||
FineUsed[FineIndex]=true;
|
||||
Errors.push_back(std::abs(CoarseHits[CoarseIndex]-FineHits[FineIndex])
|
||||
/PixelBudgetMeters);
|
||||
++Row.Samples;
|
||||
}
|
||||
}
|
||||
Row.P50=Percentile(Errors,0.50);Row.P99=Percentile(Errors,0.99);
|
||||
Row.Maximum=Errors.empty()?0.0:*std::max_element(Errors.begin(),Errors.end());
|
||||
Row.Samples=Errors.size();
|
||||
return Row;
|
||||
}
|
||||
|
||||
inline std::array<double,9> ClosestDisplayedDistancesMeters(
|
||||
const FStandaloneOwnerWorld& World,const FVector& CameraVoxels,
|
||||
std::uint32_t VoxelSizeMicrons)
|
||||
{
|
||||
const double VoxelMeters=static_cast<double>(VoxelSizeMicrons)/1000000.0;
|
||||
std::array<double,9> Minimums{};
|
||||
Minimums.fill(std::numeric_limits<double>::infinity());
|
||||
for(int Lod=0;Lod<=8;++Lod)
|
||||
{
|
||||
const int PhaseCount=1<<Lod;
|
||||
for(int Phase=0;Phase<PhaseCount;++Phase)
|
||||
{
|
||||
FVector Probe=CameraVoxels;
|
||||
Probe.X=static_cast<float>(Phase*32.0+32.0-1.0/256.0);
|
||||
const FStandaloneDesiredTileSet Desired=StandaloneWorldMesher::BuildDesiredSet(World,Probe);
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
{
|
||||
if(Tile.Level!=Lod)continue;
|
||||
Minimums[Lod]=std::min(Minimums[Lod],
|
||||
TileAabbDistanceMeters(Tile,Probe,VoxelMeters));
|
||||
}
|
||||
}
|
||||
}
|
||||
for(double& Distance:Minimums)
|
||||
if(!std::isfinite(Distance))Distance=0.0;
|
||||
return Minimums;
|
||||
}
|
||||
|
||||
inline std::array<double,9> ClipmapClosestDisplayedDistancesMeters(
|
||||
const FStandaloneOwnerWorld& World,std::uint32_t VoxelSizeMicrons)
|
||||
{
|
||||
const double VoxelMeters=static_cast<double>(VoxelSizeMicrons)/1000000.0;
|
||||
std::array<double,9> Distances{};
|
||||
for(int Lod=1;Lod<=8;++Lod)
|
||||
Distances[Lod]=static_cast<double>(World.ClipRadius)
|
||||
*static_cast<double>(CHUNK_SIZE)*(1ull<<(Lod-1))*VoxelMeters;
|
||||
return Distances;
|
||||
}
|
||||
|
||||
inline bool Run(const std::array<std::optional<FStandaloneCpuTile>,9>& SampleTiles,
|
||||
FStandaloneStreamingGenerator& Streamer,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& Assembly,
|
||||
const std::array<double,9>& Distances,std::uint32_t VoxelSizeMicrons,int MaxLod,
|
||||
const std::array<std::int64_t,9>& PixelBudgetsSubvoxels)
|
||||
{
|
||||
const double VoxelMeters=static_cast<double>(VoxelSizeMicrons)/1000000.0;
|
||||
std::array<double,9> Budgets{};
|
||||
std::array<FErrorRow,9> ErrorRows{};
|
||||
std::array<FPopRow,9> PopRows{};
|
||||
bool GatePass=true;
|
||||
std::printf("[ScreenSpaceGate] angular_pixel=%.9f rad (90deg/2560) lod>=3_limit=1.0px sampling=65x65 skirts=excluded metric=vertical_height_error field_reference=direct-Heightfield2D-roots\n",
|
||||
OnePixelRadians);
|
||||
for(int Lod=0;Lod<=MaxLod&&Lod<9;++Lod)
|
||||
{
|
||||
Budgets[Lod]=Distances[Lod]*OnePixelRadians;
|
||||
std::printf("[ScreenSpaceDistance] lod=%d closest_m=%.3f pixel_budget_m=%.6f pixel_budget_cm=%.3f pixel_budget_subvoxels=%lld rule=%s\n",
|
||||
Lod,Distances[Lod],Budgets[Lod],Budgets[Lod]*100.0,
|
||||
static_cast<long long>(PixelBudgetsSubvoxels[Lod]),Lod<3?"exact-2cm":"1px");
|
||||
}
|
||||
VoxelForgeCore::Heightfield2D::resetThreadColumnCacheStats(false,true,0);
|
||||
for(int Lod=3;Lod<=MaxLod&&Lod<9;++Lod)
|
||||
{
|
||||
if(!SampleTiles[Lod]||SampleTiles[Lod]->Vertices.empty())
|
||||
{
|
||||
std::printf("[ScreenSpaceGateLOD] lod=%d status=NO_SURFACE_TILE\n",Lod);
|
||||
GatePass=false;continue;
|
||||
}
|
||||
const FStandaloneCpuTile& CoarseTile=*SampleTiles[Lod];
|
||||
const FStandaloneCpuTile* CoarsePtr=&CoarseTile;
|
||||
const std::span<const FStandaloneCpuTile* const> CoarseSpan(&CoarsePtr,1);
|
||||
const FHeightGrid CoarseGrid=Rasterize(CoarseSpan,CoarseTile.Key,VoxelMeters);
|
||||
const FStandaloneCpuTile ExactTile=Streamer.GenerateDiagnostic(
|
||||
CoarseTile.Key,0,1,true);
|
||||
const FStandaloneCpuTile* ExactPointer=&ExactTile;
|
||||
const std::span<const FStandaloneCpuTile* const> ExactSpan(&ExactPointer,1);
|
||||
const FHeightGrid ExactGrid=Rasterize(ExactSpan,ExactTile.Key,VoxelMeters);
|
||||
ErrorRows[Lod]=CompareToReference(CoarseGrid,ExactGrid,Lod,Budgets[Lod]);
|
||||
const FErrorRow& Error=ErrorRows[Lod];
|
||||
const bool LodPass=Error.Valid&&Error.Missing==0&&Error.Floating==0
|
||||
&&Error.Samples>=16&&Error.Maximum<=1.0;
|
||||
GatePass=GatePass&&LodPass;
|
||||
std::printf("[ScreenSpaceGateLOD] lod=%d key=(%d,%d,%d) samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n",
|
||||
Lod,CoarseTile.Key.Coord.X,CoarseTile.Key.Coord.Y,CoarseTile.Key.Coord.Z,
|
||||
static_cast<unsigned long long>(Error.Samples),Error.P50,Error.P99,Error.Maximum,
|
||||
static_cast<unsigned long long>(Error.Missing),static_cast<unsigned long long>(Error.Floating),
|
||||
LodPass?"PASS":"REJECT");
|
||||
|
||||
const FErrorRow FieldError=CompareToExact(CoarseGrid,CoarseTile.Key,VoxelMeters,
|
||||
Budgets[Lod],Assembly);
|
||||
const bool FieldPass=FieldError.Valid&&FieldError.Missing==0&&FieldError.Floating==0
|
||||
&&FieldError.Samples>=16&&FieldError.Maximum<=1.0;
|
||||
GatePass=GatePass&&FieldPass;
|
||||
std::printf("[ScreenSpaceGateFieldLOD] lod=%d samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n",
|
||||
Lod,static_cast<unsigned long long>(FieldError.Samples),FieldError.P50,
|
||||
FieldError.P99,FieldError.Maximum,static_cast<unsigned long long>(FieldError.Missing),
|
||||
static_cast<unsigned long long>(FieldError.Floating),FieldPass?"PASS":"REJECT");
|
||||
|
||||
bool FaultControlsPass=true;
|
||||
const FHeightGrid MissingGrid(ExactGrid.Side,ExactGrid.MinimumX,ExactGrid.MaximumX,
|
||||
ExactGrid.MinimumY,ExactGrid.MaximumY);
|
||||
const FErrorRow MissingError=CompareToReference(MissingGrid,ExactGrid,Lod,
|
||||
Budgets[Lod]);
|
||||
const bool MissingRejected=MissingError.Missing>0&&MissingError.Maximum>1.0;
|
||||
FaultControlsPass=FaultControlsPass&&MissingRejected;
|
||||
std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-displayed-surface missing=%llu status=%s\n",
|
||||
Lod,static_cast<unsigned long long>(MissingError.Missing),
|
||||
MissingRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
|
||||
|
||||
FStandaloneCpuTile FloatingTile=CoarseTile;
|
||||
const std::uint32_t FloatingVertexBase=static_cast<std::uint32_t>(FloatingTile.Vertices.size());
|
||||
for(FStandaloneCpuVertex Vertex:CoarseTile.Vertices)
|
||||
{
|
||||
Vertex.Position[2]+=2.0f;
|
||||
FloatingTile.Vertices.push_back(Vertex);
|
||||
}
|
||||
for(const std::uint32_t Index:CoarseTile.Indices)
|
||||
FloatingTile.Indices.push_back(FloatingVertexBase+Index);
|
||||
FloatingTile.TriangleSkirtFaceMasks.insert(FloatingTile.TriangleSkirtFaceMasks.end(),
|
||||
CoarseTile.TriangleSkirtFaceMasks.begin(),CoarseTile.TriangleSkirtFaceMasks.end());
|
||||
const FStandaloneCpuTile* FloatingPointer=&FloatingTile;
|
||||
const std::span<const FStandaloneCpuTile* const> FloatingSpan(&FloatingPointer,1);
|
||||
const FHeightGrid FloatingGrid=Rasterize(FloatingSpan,CoarseTile.Key,VoxelMeters);
|
||||
const FErrorRow FloatingError=CompareToReference(FloatingGrid,ExactGrid,Lod,
|
||||
Budgets[Lod]);
|
||||
const bool FloatingRejected=FloatingError.Floating>0||FloatingError.Maximum>1.0;
|
||||
FaultControlsPass=FaultControlsPass&&FloatingRejected;
|
||||
std::printf("[ScreenSpaceFaultInjection] lod=%d case=2m-floating-surface floating=%llu max_px=%.4f status=%s\n",
|
||||
Lod,static_cast<unsigned long long>(FloatingError.Floating),FloatingError.Maximum,
|
||||
FloatingRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
|
||||
|
||||
std::vector<FStandaloneCpuTile> FineTiles;
|
||||
FineTiles.reserve(8);
|
||||
for(int Dz=0;Dz<=1;++Dz)
|
||||
for(int Dy=0;Dy<=1;++Dy)
|
||||
for(int Dx=0;Dx<=1;++Dx)
|
||||
{
|
||||
const FVoxelTileKey FineKey(FIntVector(2*CoarseTile.Key.Coord.X+Dx,
|
||||
2*CoarseTile.Key.Coord.Y+Dy,2*CoarseTile.Key.Coord.Z+Dz),Lod-1);
|
||||
FStandaloneCpuTile FineTile=Streamer.GenerateDiagnostic(FineKey);
|
||||
if(!FineTile.Vertices.empty())FineTiles.push_back(std::move(FineTile));
|
||||
}
|
||||
std::vector<const FStandaloneCpuTile*> FinePointers;
|
||||
FinePointers.reserve(FineTiles.size());
|
||||
for(const auto& Tile:FineTiles)FinePointers.push_back(&Tile);
|
||||
const auto FineSpan=std::span<const FStandaloneCpuTile* const>(
|
||||
FinePointers.data(),FinePointers.size());
|
||||
const FHeightGrid FineGrid=Rasterize(FineSpan,CoarseTile.Key,VoxelMeters);
|
||||
PopRows[Lod]=ComparePop(CoarseGrid,FineGrid,CoarseTile.Key,VoxelMeters,
|
||||
Budgets[Lod],Assembly);
|
||||
const FPopRow& Pop=PopRows[Lod];
|
||||
const bool PopPass=Pop.Valid&&Pop.Cracks==0&&Pop.Floating==0&&Pop.Maximum<=1.0;
|
||||
GatePass=GatePass&&PopPass;
|
||||
std::printf("[ScreenSpaceSwap] lod=%d_to_%d samples=%llu p50_px=%.4f p99_px=%.4f max_px=%.4f cracks=%llu floating=%llu status=%s\n",
|
||||
Lod,Lod-1,static_cast<unsigned long long>(Pop.Samples),
|
||||
Pop.P50,Pop.P99,Pop.Maximum,static_cast<unsigned long long>(Pop.Cracks),
|
||||
static_cast<unsigned long long>(Pop.Floating),
|
||||
PopPass?"PASS":"REJECT");
|
||||
|
||||
const FHeightGrid MissingFineGrid(CoarseGrid.Side,CoarseGrid.MinimumX,
|
||||
CoarseGrid.MaximumX,CoarseGrid.MinimumY,CoarseGrid.MaximumY);
|
||||
const FPopRow MissingFineError=ComparePop(CoarseGrid,MissingFineGrid,
|
||||
CoarseTile.Key,VoxelMeters,Budgets[Lod],Assembly);
|
||||
const bool CrackRejected=MissingFineError.Cracks>0&&MissingFineError.Maximum>1.0;
|
||||
FaultControlsPass=FaultControlsPass&&CrackRejected;
|
||||
GatePass=GatePass&&FaultControlsPass;
|
||||
std::printf("[ScreenSpaceFaultInjection] lod=%d case=missing-fine-ring-surface cracks=%llu max_px=%.4f status=%s\n",
|
||||
Lod,static_cast<unsigned long long>(MissingFineError.Cracks),
|
||||
MissingFineError.Maximum,CrackRejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
|
||||
|
||||
bool NegativeControlsPass=true;
|
||||
const auto CheckNegative=[&](const char* Name,std::uint32_t Drop,std::uint32_t Stride)
|
||||
{
|
||||
FStandaloneCpuTile Bad=Streamer.GenerateDiagnostic(
|
||||
CoarseTile.Key,Drop,Stride,true);
|
||||
const FStandaloneCpuTile* BadPointer=&Bad;
|
||||
const std::span<const FStandaloneCpuTile* const> BadSpan(&BadPointer,1);
|
||||
const FHeightGrid BadGrid=Rasterize(BadSpan,Bad.Key,VoxelMeters);
|
||||
const FErrorRow BadError=CompareToReference(BadGrid,ExactGrid,Lod,
|
||||
Budgets[Lod]);
|
||||
const bool Rejected=BadError.Missing>0||BadError.Floating>0
|
||||
||BadError.Samples<16||BadError.Maximum>1.0;
|
||||
NegativeControlsPass=NegativeControlsPass&&Rejected;
|
||||
std::printf("[ScreenSpaceNegative] lod=%d case=%s p50_px=%.4f p99_px=%.4f max_px=%.4f missing=%llu floating=%llu status=%s\n",
|
||||
Lod,Name,BadError.P50,BadError.P99,BadError.Maximum,
|
||||
static_cast<unsigned long long>(BadError.Missing),
|
||||
static_cast<unsigned long long>(BadError.Floating),
|
||||
Rejected?"REJECTED_AS_REQUIRED":"GATE_FALSE_NEGATIVE");
|
||||
};
|
||||
CheckNegative("s13-global-octave-drop",1,1);
|
||||
CheckNegative("stride-2-column-interpolation",0,2);
|
||||
GatePass=GatePass&&NegativeControlsPass;
|
||||
}
|
||||
std::printf("[ScreenSpaceGateResult] status=%s\n",GatePass?"PASS":"REJECT");
|
||||
return GatePass;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user