wip(assembler): round L7 -- shared column cache across workers: whole view 16-19% faster, world unchanged
SharedColumnCache.h: a world-owned, bounded 4-way second-tier cache of exact column values (heightfield and floating islands), keyed by recipe/plan identity plus content hash, XY and island id. Lock-free seqlock reads; set-local spin lock on misses. The FLazy thread-local tier stays first. 83.6% of repeated column work was cross-worker; formula evaluations -60.9%. W12 L1 view: Luna 2.165 -> 1.754 s; reviewer interleaved 2.059 -> 1.722 s. 8 m crossing new tiles 82 -> 52 ms. +66 MiB peak RSS. All hashes (whole view, per-LOD, exact queries, islands seed 0) identical at W1/W8/W12, Clang off and GCC default. MSVC/UBT not compiled here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
1 parent
9898e057fd
commit
4b1108bf86
12 files changed
+751
-92
No files matched your search
@@ -35,6 +35,7 @@ namespace
|
||||
bool StreamPipeline=false;
|
||||
bool StreamHash=false;
|
||||
bool ProfileColumnOverlap=false;
|
||||
bool UseSharedColumnCache=true;
|
||||
bool ForceGenerate=false;
|
||||
bool MeshCoverage=false;
|
||||
bool ScreenSpaceGate=false;
|
||||
@@ -47,8 +48,10 @@ namespace
|
||||
int TlsProbeWorkers=0;
|
||||
std::string TlsProbeTouch="none";
|
||||
bool HasCamera=false;
|
||||
bool HasCameraStep=false;
|
||||
bool AutoSurfaceCamera=false;
|
||||
FVector CameraMeters=FVector::ZeroVector;
|
||||
FVector CameraStepMeters=FVector::ZeroVector;
|
||||
float CameraYawDegrees=0.0f;
|
||||
float CameraPitchDegrees=0.0f;
|
||||
std::filesystem::path MeshExportPath;
|
||||
@@ -77,6 +80,15 @@ namespace
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseCameraStep(std::string_view text,FVector& meters)
|
||||
{
|
||||
const std::string values(text);
|
||||
float x=0.0f,y=0.0f,z=0.0f;
|
||||
if(std::sscanf(values.c_str(),"%f,%f,%f",&x,&y,&z)!=3)return false;
|
||||
meters={x,y,z};
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseOptions(int argc,char** argv,FOptions& options)
|
||||
{
|
||||
const std::filesystem::path executable=std::filesystem::absolute(argv[0]);
|
||||
@@ -91,6 +103,8 @@ namespace
|
||||
std::printf("Usage: %s [owner_world.json] --world-assembler-prototype --stream-benchmark --stream-pipeline [options]\n"
|
||||
" --workers=N --clip-radius=N --max-clip-level=N --max-level=N\n"
|
||||
" --camera=X,Y,Z,YAW,PITCH (position in metres) --stream-hash\n"
|
||||
" --camera-step=X,Y,Z (optional second view step in metres)\n"
|
||||
" --column-cache=shared|thread-local (default: shared)\n"
|
||||
" --mesh-export=FILE (write the exact generated tile meshes for offline rendering)\n"
|
||||
" --profile-column-overlap --far-horizon-lod8-step=N --force-generate\n"
|
||||
" --seed=N --recipe=surface-world|floating-islands\n"
|
||||
@@ -149,6 +163,20 @@ namespace
|
||||
{std::fprintf(stderr,"Invalid --camera value: %.*s\n",static_cast<int>(arg.size()),arg.data());return false;}
|
||||
options.HasCamera=true;
|
||||
}
|
||||
else if(arg.starts_with("--camera-step="))
|
||||
{
|
||||
if(!ParseCameraStep(arg.substr(std::string_view("--camera-step=").size()),
|
||||
options.CameraStepMeters))
|
||||
{std::fprintf(stderr,"Invalid --camera-step value: %.*s\n",static_cast<int>(arg.size()),arg.data());return false;}
|
||||
options.HasCameraStep=true;
|
||||
}
|
||||
else if(arg.starts_with("--column-cache="))
|
||||
{
|
||||
const std::string_view mode=arg.substr(std::string_view("--column-cache=").size());
|
||||
if(mode=="shared")options.UseSharedColumnCache=true;
|
||||
else if(mode=="thread-local")options.UseSharedColumnCache=false;
|
||||
else {std::fprintf(stderr,"Invalid --column-cache value: %.*s\n",static_cast<int>(arg.size()),arg.data());return false;}
|
||||
}
|
||||
else if(arg.starts_with("--mesh-export="))
|
||||
{
|
||||
options.MeshExportPath=std::filesystem::path(arg.substr(14));
|
||||
@@ -199,10 +227,12 @@ int main(int argc,char** argv)
|
||||
if(!ParseOptions(argc,argv,options))return 2;
|
||||
if(options.TlsProbeRequested)
|
||||
return StandaloneTlsProbe::Run(options.TlsProbeWorkers,options.TlsProbeTouch);
|
||||
std::printf("[HeadlessConfig] data=%s workers=%d clip_radius=%d max_clip_level=%d max_level=%d hash=%s column_overlap=%s force_generate=%s recipe=%s seed_override=%s render_distance_override=%s\n",
|
||||
std::printf("[HeadlessConfig] data=%s workers=%d clip_radius=%d max_clip_level=%d max_level=%d hash=%s column_overlap=%s force_generate=%s recipe=%s shared_column_cache=%s camera_step_m=(%.3f,%.3f,%.3f) seed_override=%s render_distance_override=%s\n",
|
||||
options.DataPath.string().c_str(),options.WorkerCount,options.ClipRadius,options.MaxClipLevel,
|
||||
options.MaxLevel,options.StreamHash?"on":"off",options.ProfileColumnOverlap?"on":"off",
|
||||
options.ForceGenerate?"on":"off",options.FloatingIslandsRecipe?"floating-islands":"surface-world",
|
||||
options.UseSharedColumnCache?"on":"off",options.CameraStepMeters.X,options.CameraStepMeters.Y,
|
||||
options.CameraStepMeters.Z,
|
||||
options.HasSeedOverride?"yes":"no",options.HasRenderDistanceOverride?"yes":"no");
|
||||
std::printf("[ColdTimeline] stage=owner_data_load begin_ms=%.3f\n",
|
||||
std::chrono::duration<double,std::milli>(Clock::now()-coldStart).count());
|
||||
@@ -309,10 +339,14 @@ int main(int argc,char** argv)
|
||||
{std::fprintf(stderr,"Shape interval build failed: %s\n",error.c_str());return 2;}
|
||||
|
||||
FStandaloneCoreScheduler scheduler;
|
||||
const FVector cameraStepVoxels=options.HasCameraStep
|
||||
?FVector(options.CameraStepMeters.X*4.0f,options.CameraStepMeters.Y*4.0f,
|
||||
options.CameraStepMeters.Z*4.0f):FVector::ZeroVector;
|
||||
return StandaloneStreamBenchmark::RunPipelinedStreamingBenchmark(ownerWorld,snapshot,
|
||||
cameraVoxels,options.WorkerCount,options.MaxLevel,VoxelForgeCore::DetailVariant::coherent,
|
||||
scheduler,options.StreamHash,false,false,false,false,coldStart,
|
||||
options.ProfileColumnOverlap,options.FarHorizonLOD8InteriorStepVoxels,
|
||||
options.MeshCoverage,options.MeshExportPath,options.CameraYawDegrees,
|
||||
options.CameraPitchDegrees,options.ScreenSpaceGate);
|
||||
options.CameraPitchDegrees,options.ScreenSpaceGate,options.UseSharedColumnCache,
|
||||
cameraStepVoxels);
|
||||
}
|
||||
@@ -137,7 +137,8 @@ namespace StandaloneStreamBenchmark
|
||||
std::chrono::steady_clock::time_point ColdStart,bool bProfileColumnOverlap,
|
||||
int32 FarHorizonLOD8InteriorStepVoxels,bool bReportMeshCoverage=false,
|
||||
const std::filesystem::path& MeshExportPath={},float CameraYawDegrees=0.0f,
|
||||
float CameraPitchDegrees=0.0f,bool bScreenSpaceGate=false)
|
||||
float CameraPitchDegrees=0.0f,bool bScreenSpaceGate=false,
|
||||
bool bUseSharedColumnCache=true,FVector CameraStepVoxels=FVector::ZeroVector)
|
||||
{
|
||||
using Clock=std::chrono::steady_clock;
|
||||
FStandaloneOwnerWorld& World=*OwnerWorld;
|
||||
@@ -167,6 +168,21 @@ namespace StandaloneStreamBenchmark
|
||||
if(MaxLevel>=0)
|
||||
Desired.Tiles.erase(std::remove_if(Desired.Tiles.begin(),Desired.Tiles.end(),
|
||||
[&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),Desired.Tiles.end());
|
||||
const bool bHasCameraStep=std::abs(CameraStepVoxels.X)+std::abs(CameraStepVoxels.Y)
|
||||
+std::abs(CameraStepVoxels.Z)>1.0e-6f;
|
||||
FStandaloneDesiredTileSet MovedDesired;
|
||||
if(bHasCameraStep)
|
||||
{
|
||||
const FVector MovedCamera=CameraPositionVoxels+CameraStepVoxels;
|
||||
MovedDesired=StandaloneWorldMesher::BuildDesiredSet(World,MovedCamera);
|
||||
const auto MovedCoreTiles=Shape.desiredTiles({MovedCamera.X,MovedCamera.Y,MovedCamera.Z});
|
||||
MovedDesired.Tiles.clear();MovedDesired.Tiles.reserve(MovedCoreTiles.size());
|
||||
for(const auto& Key:MovedCoreTiles)
|
||||
MovedDesired.Tiles.emplace_back(FIntVector(Key.coordinate.x,Key.coordinate.y,Key.coordinate.z),Key.lod);
|
||||
if(MaxLevel>=0)
|
||||
MovedDesired.Tiles.erase(std::remove_if(MovedDesired.Tiles.begin(),MovedDesired.Tiles.end(),
|
||||
[&](const FVoxelTileKey& Tile){return Tile.Level>MaxLevel;}),MovedDesired.Tiles.end());
|
||||
}
|
||||
if(!Desired.Tiles.empty())
|
||||
{
|
||||
FIntVector volumeMin(INT32_MAX,INT32_MAX,INT32_MAX);
|
||||
@@ -191,17 +207,22 @@ namespace StandaloneStreamBenchmark
|
||||
}
|
||||
FIntVector FineMin(INT32_MAX,INT32_MAX,INT32_MAX),FineMax(INT32_MIN,INT32_MIN,INT32_MIN);
|
||||
FIntVector ClipMin(INT32_MAX,INT32_MAX,INT32_MAX),ClipMax(INT32_MIN,INT32_MIN,INT32_MIN);
|
||||
for(const FVoxelTileKey& Tile:Desired.Tiles)
|
||||
const auto AccumulateBounds=[&](const std::vector<FVoxelTileKey>& Tiles)
|
||||
{
|
||||
const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels();
|
||||
ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z);
|
||||
ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent);
|
||||
if(Tile.Level<World.FullResClipLevels)
|
||||
for(const FVoxelTileKey& Tile:Tiles)
|
||||
{
|
||||
FineMin.X=std::min(FineMin.X,Origin.X);FineMin.Y=std::min(FineMin.Y,Origin.Y);FineMin.Z=std::min(FineMin.Z,Origin.Z);
|
||||
FineMax.X=std::max(FineMax.X,Origin.X+Extent);FineMax.Y=std::max(FineMax.Y,Origin.Y+Extent);FineMax.Z=std::max(FineMax.Z,Origin.Z+Extent);
|
||||
const FIntVector Origin=Tile.OriginVoxels();const int32 Extent=Tile.ExtentVoxels();
|
||||
ClipMin.X=std::min(ClipMin.X,Origin.X);ClipMin.Y=std::min(ClipMin.Y,Origin.Y);ClipMin.Z=std::min(ClipMin.Z,Origin.Z);
|
||||
ClipMax.X=std::max(ClipMax.X,Origin.X+Extent);ClipMax.Y=std::max(ClipMax.Y,Origin.Y+Extent);ClipMax.Z=std::max(ClipMax.Z,Origin.Z+Extent);
|
||||
if(Tile.Level<World.FullResClipLevels)
|
||||
{
|
||||
FineMin.X=std::min(FineMin.X,Origin.X);FineMin.Y=std::min(FineMin.Y,Origin.Y);FineMin.Z=std::min(FineMin.Z,Origin.Z);
|
||||
FineMax.X=std::max(FineMax.X,Origin.X+Extent);FineMax.Y=std::max(FineMax.Y,Origin.Y+Extent);FineMax.Z=std::max(FineMax.Z,Origin.Z+Extent);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
AccumulateBounds(Desired.Tiles);
|
||||
if(bHasCameraStep)AccumulateBounds(MovedDesired.Tiles);
|
||||
if(FineMin.X==INT32_MAX||ClipMin.X==INT32_MAX)
|
||||
{std::fprintf(stderr,"[StreamPipeline] no desired detail or clipmap tiles.\n");return 7;}
|
||||
|
||||
@@ -242,7 +263,7 @@ namespace StandaloneStreamBenchmark
|
||||
const double ShapeSeconds=std::chrono::duration<double>(ShapeReady-ShapeBuildStart).count();
|
||||
FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel,
|
||||
bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap,
|
||||
FarHorizonLOD8InteriorStepVoxels);
|
||||
FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache);
|
||||
if(bScreenSpaceGate)Streamer.SetMesherStageProfilingEnabled(true);
|
||||
const auto StreamStart=Clock::now();
|
||||
const FStandaloneStreamingUpdate Update=Streamer.StartWithDesired(Desired);
|
||||
@@ -287,13 +308,19 @@ namespace StandaloneStreamBenchmark
|
||||
return X^(Y+static_cast<std::size_t>(0x9E3779B9u)+(X<<6)+(X>>2));
|
||||
}
|
||||
};
|
||||
struct FFirstColumnUse
|
||||
struct FColumnEvaluationEvent
|
||||
{
|
||||
FColumnXY XY;
|
||||
FVoxelTileKey Tile;
|
||||
std::uint64_t WorkerHash=0;
|
||||
std::uint64_t Sequence=0;
|
||||
bool bOutsideTileFootprint=false;
|
||||
};
|
||||
std::array<std::unordered_map<FColumnXY,std::vector<FFirstColumnUse>,FColumnXYHash>,9> ColumnUsesByLOD;
|
||||
std::array<std::uint64_t,9> ColumnDuplicateEventsByLOD{},NeighbourDuplicateEventsByLOD{};
|
||||
std::array<std::vector<FColumnEvaluationEvent>,9> ColumnEventsByLOD;
|
||||
std::array<std::uint64_t,9> ColumnEvaluationEventsByLOD{},UniqueColumnCoordinatesByLOD{};
|
||||
std::array<std::uint64_t,9> ColumnDuplicateEventsByLOD{},SameWorkerEvictedRepeatsByLOD{};
|
||||
std::array<std::uint64_t,9> DifferentWorkerRepeatsByLOD{},HaloNormalRepeatsByLOD{};
|
||||
std::array<std::uint64_t,9> HaloNormalEvaluationsByLOD{},NeighbourDuplicateEventsByLOD{};
|
||||
std::array<std::uint64_t,9> CrossWorkerNeighbourDuplicateEventsByLOD{};
|
||||
std::array<std::uint64_t,9> ColumnCaptureTilesByLOD{},ExpectedTilesByLOD{};
|
||||
std::array<bool,9> ColumnCaptureCompleteByLOD{};
|
||||
@@ -376,33 +403,12 @@ namespace StandaloneStreamBenchmark
|
||||
++ColumnCaptureTilesByLOD[LOD];
|
||||
ColumnCaptureCompleteByLOD[LOD]=ColumnCaptureCompleteByLOD[LOD]
|
||||
&&Tile.CoreDiagnostics.columnCoordinateCaptureComplete;
|
||||
std::vector<std::array<std::int64_t,2>> Coordinates=
|
||||
Tile.CoreDiagnostics.evaluatedColumnCoordinates;
|
||||
std::sort(Coordinates.begin(),Coordinates.end());
|
||||
Coordinates.erase(std::unique(Coordinates.begin(),Coordinates.end()),Coordinates.end());
|
||||
for(const auto& Coordinate:Coordinates)
|
||||
auto& Events=ColumnEventsByLOD[LOD];
|
||||
for(const auto& Coordinate:Tile.CoreDiagnostics.evaluatedColumnCoordinates)
|
||||
{
|
||||
const FColumnXY XY{Coordinate[0],Coordinate[1]};
|
||||
auto [Uses,Inserted]=ColumnUsesByLOD[LOD].try_emplace(XY);
|
||||
std::vector<FFirstColumnUse>& PriorUses=Uses->second;
|
||||
if(Inserted)
|
||||
{
|
||||
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
|
||||
continue;
|
||||
}
|
||||
++ColumnDuplicateEventsByLOD[LOD];
|
||||
bool bHasNeighbourDuplicate=false,bHasCrossWorkerNeighbourDuplicate=false;
|
||||
for(const FFirstColumnUse& Prior:PriorUses)
|
||||
{
|
||||
if(!AreSameLODNeighbours(Prior.Tile,Tile.Key))continue;
|
||||
bHasNeighbourDuplicate=true;
|
||||
bHasCrossWorkerNeighbourDuplicate=bHasCrossWorkerNeighbourDuplicate
|
||||
||Prior.WorkerHash!=Tile.CoreDiagnostics.columnEvaluationWorkerHash;
|
||||
}
|
||||
if(bHasNeighbourDuplicate)++NeighbourDuplicateEventsByLOD[LOD];
|
||||
if(bHasCrossWorkerNeighbourDuplicate)
|
||||
++CrossWorkerNeighbourDuplicateEventsByLOD[LOD];
|
||||
PriorUses.push_back({Tile.Key,Tile.CoreDiagnostics.columnEvaluationWorkerHash});
|
||||
Events.push_back({{Coordinate.x,Coordinate.y},Tile.Key,
|
||||
Tile.CoreDiagnostics.columnEvaluationWorkerHash,Coordinate.sequence,
|
||||
Coordinate.outsideTileFootprint});
|
||||
}
|
||||
}
|
||||
WholeViewReady=std::max(WholeViewReady,Tile.CompletedAt);
|
||||
@@ -449,6 +455,45 @@ namespace StandaloneStreamBenchmark
|
||||
bFullDetailReported=true;
|
||||
}
|
||||
}
|
||||
if(bHasCameraStep)
|
||||
{
|
||||
const auto MovingStart=Clock::now();
|
||||
const auto MovingUpdate=Streamer.UpdateDesiredWithSet(MovedDesired,Desired.Tiles,1);
|
||||
std::array<std::uint64_t,9> MovingTilesByLOD{};
|
||||
std::array<double,9> MovingWorkerSecondsByLOD{};
|
||||
int32 MovingFinished=0;
|
||||
while(MovingFinished<MovingUpdate.NewJobs)
|
||||
{
|
||||
std::vector<FStandaloneCpuTile> Ready=Streamer.TakeReady();
|
||||
if(Ready.empty())
|
||||
{std::this_thread::sleep_for(std::chrono::milliseconds(1));continue;}
|
||||
for(const FStandaloneCpuTile& Tile:Ready)
|
||||
{
|
||||
++MovingFinished;
|
||||
if(Tile.Key.Level>=0&&Tile.Key.Level<static_cast<int32>(MovingTilesByLOD.size()))
|
||||
{
|
||||
const std::size_t LOD=static_cast<std::size_t>(Tile.Key.Level);
|
||||
++MovingTilesByLOD[LOD];
|
||||
MovingWorkerSecondsByLOD[LOD]+=Tile.WorkerSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
const double MovingWallSeconds=std::chrono::duration<double>(Clock::now()-MovingStart).count();
|
||||
const double StepDistanceMeters=std::sqrt(
|
||||
static_cast<double>(CameraStepVoxels.X)*CameraStepVoxels.X
|
||||
+static_cast<double>(CameraStepVoxels.Y)*CameraStepVoxels.Y
|
||||
+static_cast<double>(CameraStepVoxels.Z)*CameraStepVoxels.Z)
|
||||
*static_cast<double>(World.CoreVoxelSizeMicrons)/1000000.0;
|
||||
std::printf("[MovingCameraStep] step_voxels=(%.3f,%.3f,%.3f) distance_m=%.3f desired_tiles=%zu new_tiles=%d completed=%d wall_s=%.6f shared_column_cache=%s\n",
|
||||
CameraStepVoxels.X,CameraStepVoxels.Y,CameraStepVoxels.Z,StepDistanceMeters,
|
||||
MovedDesired.Tiles.size(),MovingUpdate.NewJobs,MovingFinished,MovingWallSeconds,
|
||||
bUseSharedColumnCache?"on":"off");
|
||||
for(std::size_t LOD=0;LOD<MovingTilesByLOD.size();++LOD)
|
||||
if(MovingTilesByLOD[LOD]!=0)
|
||||
std::printf("[MovingCameraStepLOD] lod=%zu tiles=%llu worker_sum_s=%.6f\n",
|
||||
LOD,static_cast<unsigned long long>(MovingTilesByLOD[LOD]),
|
||||
MovingWorkerSecondsByLOD[LOD]);
|
||||
}
|
||||
Streamer.Stop();
|
||||
bool bScreenSpaceGatePassing=true;
|
||||
if(bScreenSpaceGate)
|
||||
@@ -472,6 +517,58 @@ namespace StandaloneStreamBenchmark
|
||||
bSceneExportPassing?"PASS":"FAIL",MeshExportPath.string().c_str());
|
||||
bSceneExportPassing=bSceneExportPassing&&!FileSizeError;
|
||||
}
|
||||
if(bProfileColumnOverlap)
|
||||
{
|
||||
struct FColumnPriorUse
|
||||
{
|
||||
FVoxelTileKey Tile;
|
||||
std::uint64_t WorkerHash=0;
|
||||
};
|
||||
for(std::size_t LOD=0;LOD<ColumnEventsByLOD.size();++LOD)
|
||||
{
|
||||
auto& Events=ColumnEventsByLOD[LOD];
|
||||
std::sort(Events.begin(),Events.end(),[](const FColumnEvaluationEvent& A,
|
||||
const FColumnEvaluationEvent& B){return A.Sequence<B.Sequence;});
|
||||
std::unordered_map<FColumnXY,std::vector<FColumnPriorUse>,FColumnXYHash> UsesByXY;
|
||||
UsesByXY.reserve(Events.size());
|
||||
for(const FColumnEvaluationEvent& Event:Events)
|
||||
{
|
||||
++ColumnEvaluationEventsByLOD[LOD];
|
||||
if(Event.bOutsideTileFootprint)++HaloNormalEvaluationsByLOD[LOD];
|
||||
auto [Found,Inserted]=UsesByXY.try_emplace(Event.XY);
|
||||
auto& PriorUses=Found->second;
|
||||
if(Inserted)
|
||||
{
|
||||
++UniqueColumnCoordinatesByLOD[LOD];
|
||||
PriorUses.push_back({Event.Tile,Event.WorkerHash});
|
||||
continue;
|
||||
}
|
||||
++ColumnDuplicateEventsByLOD[LOD];
|
||||
if(Event.bOutsideTileFootprint)
|
||||
++HaloNormalRepeatsByLOD[LOD];
|
||||
else
|
||||
{
|
||||
const bool bSameWorkerSeen=std::any_of(PriorUses.begin(),PriorUses.end(),
|
||||
[&](const FColumnPriorUse& Prior)
|
||||
{return Prior.WorkerHash==Event.WorkerHash;});
|
||||
if(bSameWorkerSeen)++SameWorkerEvictedRepeatsByLOD[LOD];
|
||||
else ++DifferentWorkerRepeatsByLOD[LOD];
|
||||
}
|
||||
bool bHasNeighbourDuplicate=false,bHasCrossWorkerNeighbourDuplicate=false;
|
||||
for(const FColumnPriorUse& Prior:PriorUses)
|
||||
{
|
||||
if(!AreSameLODNeighbours(Prior.Tile,Event.Tile))continue;
|
||||
bHasNeighbourDuplicate=true;
|
||||
bHasCrossWorkerNeighbourDuplicate=bHasCrossWorkerNeighbourDuplicate
|
||||
||Prior.WorkerHash!=Event.WorkerHash;
|
||||
}
|
||||
if(bHasNeighbourDuplicate)++NeighbourDuplicateEventsByLOD[LOD];
|
||||
if(bHasCrossWorkerNeighbourDuplicate)
|
||||
++CrossWorkerNeighbourDuplicateEventsByLOD[LOD];
|
||||
PriorUses.push_back({Event.Tile,Event.WorkerHash});
|
||||
}
|
||||
}
|
||||
}
|
||||
bool bColumnOverlapCapturePassing=true;
|
||||
if(bProfileColumnOverlap)
|
||||
{
|
||||
@@ -480,11 +577,16 @@ namespace StandaloneStreamBenchmark
|
||||
const bool bLODComplete=ColumnCaptureCompleteByLOD[LOD]
|
||||
&&ColumnCaptureTilesByLOD[LOD]==ExpectedTilesByLOD[LOD];
|
||||
bColumnOverlapCapturePassing=bColumnOverlapCapturePassing&&bLODComplete;
|
||||
std::printf("[WorldAssemblerLODColumnOverlap] lod=%zu tiles=%llu expected_tiles=%llu unique_xy_columns=%zu repeated_tile_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
|
||||
std::printf("[WorldAssemblerLODColumnOverlap] lod=%zu tiles=%llu expected_tiles=%llu total_column_evaluations=%llu unique_xy_columns=%llu repeated_column_evaluations=%llu same_worker_evicted_repeats=%llu different_worker_repeats=%llu halo_normal_repeats=%llu halo_normal_evaluations=%llu neighbouring_duplicate_evaluations=%llu cross_worker_neighbouring_duplicates=%llu capture_complete=%d\n",
|
||||
LOD,static_cast<unsigned long long>(ColumnCaptureTilesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(ExpectedTilesByLOD[LOD]),
|
||||
ColumnUsesByLOD[LOD].size(),
|
||||
static_cast<unsigned long long>(ColumnEvaluationEventsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(UniqueColumnCoordinatesByLOD[LOD]),
|
||||
static_cast<unsigned long long>(ColumnDuplicateEventsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(SameWorkerEvictedRepeatsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(DifferentWorkerRepeatsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(HaloNormalRepeatsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(HaloNormalEvaluationsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(NeighbourDuplicateEventsByLOD[LOD]),
|
||||
static_cast<unsigned long long>(CrossWorkerNeighbourDuplicateEventsByLOD[LOD]),
|
||||
bLODComplete?1:0);
|
||||
|
||||
@@ -428,12 +428,14 @@ public:
|
||||
bool bInSkipColors=false,
|
||||
VoxelForgeCore::World* InCoreWorld=nullptr,
|
||||
bool bInCaptureColumnCoordinates=false,
|
||||
int32 InLod8HorizonInteriorStepVoxels=0)
|
||||
int32 InLod8HorizonInteriorStepVoxels=0,
|
||||
bool bInSharedColumnCache=true)
|
||||
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
|
||||
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
|
||||
CoreWorld(InCoreWorld),bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups),
|
||||
bSkipUV(bInSkipUV),bSkipColors(bInSkipColors),
|
||||
bCaptureColumnCoordinates(bInCaptureColumnCoordinates),
|
||||
bSharedColumnCache(bInSharedColumnCache),
|
||||
Lod8HorizonInteriorStepVoxels(std::max(0,InLod8HorizonInteriorStepVoxels))
|
||||
{
|
||||
VoxelGenLOD::InitializeConsoleSwitches();
|
||||
@@ -816,6 +818,7 @@ private:
|
||||
Options.skipUvAndColors=bSkipUvAndColors;
|
||||
Options.skipMaterialGroups=bSkipMaterialGroups;Options.skipUv=bSkipUV;Options.skipColors=bSkipColors;
|
||||
Options.captureColumnCoordinates=bCaptureColumnCoordinates;
|
||||
Options.useSharedColumnCache=bSharedColumnCache;
|
||||
Options.lod8HorizonInteriorStepVoxels=Lod8HorizonInteriorStepVoxels;
|
||||
Options.profileMesherStages=bProfileMesherStages&&!ForceGenerate;
|
||||
Options.diagnosticGlobalOctaveDrop=DiagnosticGlobalOctaveDrop;
|
||||
@@ -993,6 +996,7 @@ private:
|
||||
bool bSkipUV=false;
|
||||
bool bSkipColors=false;
|
||||
bool bCaptureColumnCoordinates=false;
|
||||
bool bSharedColumnCache=true;
|
||||
int32 Lod8HorizonInteriorStepVoxels=0;
|
||||
bool bProfileMesherStages=false;
|
||||
std::atomic<uint64> CacheHitCount{0};
|
||||
|
||||
Reference in new issue
Block a user