feat(streaming): horizon distance with real tiles only -- sheets off by default
The sheet system models terrain as one ground and one cap height per column, which cannot express this world's overhangs, floating islands and open sky; three attempts to fix the MC/sheet seam produced holes, then caps over the void, then both. Measured alternative: cover the render distance with real marching-cubes tiles and switch the far-sheet ring off. Benchmarks at RenderDistanceChunks=768, all tiles loaded at steady state: L4 57,473 tiles / 30.1M tris / 837 MiB / 2,514 s worker / 296 s to settle; L5 10,841 / 8.1M / 226 MiB / 375 s / 108 s; L6 3,281 / 2.9M / 82 MiB / 127 s / 77 s. Defaults now MaxClipLevel=5, bFarSheetRing=false. Invariants: strate crossing PASS, gate stress PASS (140.06 m, 0 full stops), determinism PASS with 10,841 tile hashes identical vs -voxel.CrtFma3=0. Peak memory ~4.2 GiB in all three. Owner action required: his DA_Settings still serializes MaxClipLevel=3 (asset edits are out of scope for Codex), so his world needs that value raised to 5 to use the new far field. At twice the distance the estimate is ~39.5k tiles / 29-30M tris / ~0.8 GiB / ~1,500 worker-seconds, which would want L6 rather than L5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
ea3f0d9a52
commit
370c39e473
7 files changed
+475
-32
No files matched your search
@@ -98,7 +98,9 @@ namespace
|
||||
int32 AllAir = 0;
|
||||
int32 AllSolid = 0;
|
||||
int32 Mixed = 0;
|
||||
int64 Vertices = 0;
|
||||
int64 Triangles = 0;
|
||||
uint64 GeometryBytes = 0;
|
||||
|
||||
for (const FTileSample& Sample : Samples)
|
||||
{
|
||||
@@ -108,7 +110,9 @@ namespace
|
||||
if (Sample.Verdict == 0) ++Mixed;
|
||||
else if (Sample.Verdict == 1) ++AllSolid;
|
||||
else if (Sample.Verdict == 2) ++AllAir;
|
||||
Vertices += Sample.Vertices;
|
||||
Triangles += Sample.Triangles;
|
||||
GeometryBytes += Sample.GeometryBytes;
|
||||
Request.Add(Sample.RequestToApplySeconds);
|
||||
Queue.Add(Sample.QueueWaitSeconds);
|
||||
Generation.Add(Sample.GenerationSeconds);
|
||||
@@ -118,14 +122,16 @@ namespace
|
||||
}
|
||||
|
||||
return FString::Printf(
|
||||
TEXT("{\"count\":%d,\"empty\":%d,\"all_air\":%d,\"all_solid\":%d,\"mixed\":%d,\"triangles\":%lld,"
|
||||
TEXT("{\"count\":%d,\"empty\":%d,\"all_air\":%d,\"all_solid\":%d,\"mixed\":%d,\"vertices\":%lld,\"triangles\":%lld,\"geometry_bytes\":%llu,"
|
||||
"\"request_apply_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s},"
|
||||
"\"queue_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s},"
|
||||
"\"generation_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s},"
|
||||
"\"classify_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s},"
|
||||
"\"mesh_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s},"
|
||||
"\"apply_s\":{\"p50\":%s,\"p95\":%s,\"max\":%s}}"),
|
||||
Count, Empty, AllAir, AllSolid, Mixed, static_cast<long long>(Triangles),
|
||||
Count, Empty, AllAir, AllSolid, Mixed,
|
||||
static_cast<long long>(Vertices), static_cast<long long>(Triangles),
|
||||
static_cast<unsigned long long>(GeometryBytes),
|
||||
*Number(Percentile(Request, 0.50)), *Number(Percentile(Request, 0.95)), *Number(Maximum(Request)),
|
||||
*Number(Percentile(Queue, 0.50)), *Number(Percentile(Queue, 0.95)), *Number(Maximum(Queue)),
|
||||
*Number(Percentile(Generation, 0.50)), *Number(Percentile(Generation, 0.95)), *Number(Maximum(Generation)),
|
||||
@@ -260,12 +266,13 @@ void RecordTile(const FTileSample& Sample)
|
||||
GState->Samples.Add(Sample);
|
||||
++GState->TileRecordsWritten;
|
||||
WriteEventLocked(*GState, TEXT("tile"), FString::Printf(
|
||||
TEXT("\"tile\":[%d,%d,%d],\"level\":%d,\"verdict\":%d,\"proof\":%d,\"empty\":%d,\"triangles\":%d,"
|
||||
TEXT("\"tile\":[%d,%d,%d],\"level\":%d,\"verdict\":%d,\"proof\":%d,\"empty\":%d,\"vertices\":%d,\"triangles\":%d,\"geometry_bytes\":%llu,"
|
||||
"\"request_to_apply_s\":%s,\"queue_wait_s\":%s,\"worker_queue_s\":%s,\"result_queue_s\":%s,"
|
||||
"\"generation_s\":%s,\"classify_s\":%s,\"mesh_s\":%s,\"stream_s\":%s,\"apply_s\":%s"),
|
||||
Sample.TileX, Sample.TileY, Sample.TileZ,
|
||||
Sample.Level, Sample.Verdict, Sample.bSealedSolidProof ? 1 : 0,
|
||||
Sample.bEmpty ? 1 : 0, Sample.Triangles,
|
||||
Sample.bEmpty ? 1 : 0, Sample.Vertices, Sample.Triangles,
|
||||
static_cast<unsigned long long>(Sample.GeometryBytes),
|
||||
*Number(Sample.RequestToApplySeconds), *Number(Sample.QueueWaitSeconds),
|
||||
*Number(Sample.WorkerQueueSeconds), *Number(Sample.ResultQueueSeconds),
|
||||
*Number(Sample.GenerationSeconds), *Number(Sample.ClassifySeconds),
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "Kismet/KismetMaterialLibrary.h"
|
||||
#include "Engine/VolumeTexture.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Slate/SceneViewport.h"
|
||||
#include "Camera/CameraActor.h"
|
||||
#include "VoxelAtmosphereManager.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
@@ -535,6 +537,7 @@ void AVoxelWorld::RegenerateAllChunks()
|
||||
for (auto& Pair : TileComponents) { if (Pair.Value) ReleaseTileComponent(Pair.Value); }
|
||||
TileComponents.Empty();
|
||||
LoadedTiles.Empty();
|
||||
LoadedTileGeometry.Reset();
|
||||
CollisionReadyTiles.Empty();
|
||||
CollisionNotRequiredTiles.Empty();
|
||||
CollisionSolidTiles.Empty();
|
||||
@@ -1420,6 +1423,7 @@ void AVoxelWorld::BeginPlay()
|
||||
AppliedTileCount = 0;
|
||||
AppliedVisibleTileCount = 0;
|
||||
AppliedTriangleCount = 0;
|
||||
LoadedTileGeometry.Reset();
|
||||
for (int32 LOD = 0; LOD < TrackedClassifierLODCount; ++LOD)
|
||||
{
|
||||
AppliedVisibleTileCountByLevel[LOD] = 0;
|
||||
@@ -1536,18 +1540,43 @@ void AVoxelWorld::BeginPlay()
|
||||
}
|
||||
|
||||
int32 TestRenderDistanceChunks = 0;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("voxel.TestRenderDistanceChunks="),
|
||||
const bool bTestRenderDistance =
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestRenderDistanceChunks="),
|
||||
TestRenderDistanceChunks)
|
||||
&& TestRenderDistanceChunks > 0)
|
||||
&& TestRenderDistanceChunks > 0;
|
||||
int32 TestMaxClipLevel = -1;
|
||||
const bool bTestMaxClipLevel =
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestMaxClipLevel="), TestMaxClipLevel);
|
||||
int32 TestFarSheetRing = -1;
|
||||
const bool bTestFarSheetRing =
|
||||
FParse::Value(FCommandLine::Get(), TEXT("voxel.TestFarSheetRing="), TestFarSheetRing);
|
||||
if (bTestRenderDistance || bTestMaxClipLevel || bTestFarSheetRing)
|
||||
{
|
||||
if (UVoxelSettings* TestSettings = DuplicateObject<UVoxelSettings>(Settings, this))
|
||||
{
|
||||
const int32 AuthoredRenderDistance = Settings->RenderDistanceChunks;
|
||||
TestSettings->RenderDistanceChunks = TestRenderDistanceChunks;
|
||||
const int32 AuthoredMaxClipLevel = Settings->MaxClipLevel;
|
||||
const bool bAuthoredFarSheetRing = Settings->bFarSheetRing;
|
||||
if (bTestRenderDistance)
|
||||
{
|
||||
TestSettings->RenderDistanceChunks = TestRenderDistanceChunks;
|
||||
}
|
||||
if (bTestMaxClipLevel)
|
||||
{
|
||||
TestSettings->MaxClipLevel = FMath::Clamp(TestMaxClipLevel, 0, 8);
|
||||
}
|
||||
if (bTestFarSheetRing)
|
||||
{
|
||||
TestSettings->bFarSheetRing = TestFarSheetRing != 0;
|
||||
}
|
||||
Settings = TestSettings;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] transient_render_distance_chunks=%d authored=%d"),
|
||||
TestRenderDistanceChunks, AuthoredRenderDistance);
|
||||
TEXT("[VoxelForgeHorizonMeasure] transient_settings=1 "
|
||||
"render_distance_chunks=%d authored=%d max_clip_level=%d authored=%d "
|
||||
"far_sheet_ring=%d authored=%d"),
|
||||
Settings->RenderDistanceChunks, AuthoredRenderDistance,
|
||||
Settings->MaxClipLevel, AuthoredMaxClipLevel,
|
||||
Settings->bFarSheetRing ? 1 : 0, bAuthoredFarSheetRing ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1801,6 +1830,7 @@ void AVoxelWorld::BeginPlay()
|
||||
void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
{
|
||||
bHeadlessStreamingTestMovement = false;
|
||||
bHeadlessStreamingTestExitOnSteady = false;
|
||||
bHeadlessStreamingTestExitRequested = false;
|
||||
bStartupTraceThroughCrossing = false;
|
||||
bHeadlessStreamingTestCenterOverride = false;
|
||||
@@ -1917,9 +1947,14 @@ void AVoxelWorld::ConfigureHeadlessStreamingTest()
|
||||
float Speed = 800.0f;
|
||||
float StartSeconds = 20.0f;
|
||||
float ExitSeconds = 0.0f;
|
||||
int32 ExitOnSteadyValue = 0;
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestMoveSpeedCmPerSecond="), Speed);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestMoveStartSeconds="), StartSeconds);
|
||||
FParse::Value(CommandLine, TEXT("voxel.TestExitSeconds="), ExitSeconds);
|
||||
if (FParse::Value(CommandLine, TEXT("voxel.TestExitOnSteady="), ExitOnSteadyValue))
|
||||
{
|
||||
bHeadlessStreamingTestExitOnSteady = ExitOnSteadyValue != 0;
|
||||
}
|
||||
|
||||
int32 CrossingValue = 0;
|
||||
bHeadlessStrateCrossingTest =
|
||||
@@ -2181,6 +2216,9 @@ void AVoxelWorld::WriteTileHashDump()
|
||||
Writer->WriteValue(TEXT("lod_octave_drop"), Settings->LODOctaveDrop);
|
||||
Writer->WriteValue(TEXT("full_res_clip_levels"), Settings->FullResClipLevels);
|
||||
Writer->WriteValue(TEXT("coarse_tile_cells"), Settings->CoarseTileCells);
|
||||
Writer->WriteValue(TEXT("max_clip_level"), Settings->MaxClipLevel);
|
||||
Writer->WriteValue(TEXT("render_distance_chunks"), Settings->RenderDistanceChunks);
|
||||
Writer->WriteValue(TEXT("far_sheet_ring"), Settings->bFarSheetRing);
|
||||
Writer->WriteValue(TEXT("strate_content_cut_min_level"),
|
||||
Settings->GetEffectiveStrateContentCutMinLevel());
|
||||
Writer->WriteObjectEnd();
|
||||
@@ -3592,6 +3630,10 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
TMap<int32, int32> SteadyLevelCounts;
|
||||
TMap<int32, FIntVector> SteadyLevelMins;
|
||||
TMap<int32, FIntVector> SteadyLevelMaxs;
|
||||
TMap<int32, int32> LoadedLevelCounts;
|
||||
TMap<int32, uint64> LoadedLevelVertices;
|
||||
TMap<int32, uint64> LoadedLevelTriangles;
|
||||
TMap<int32, uint64> LoadedLevelGeometryBytes;
|
||||
for (const FVoxelTileKey& Key : DesiredSorted)
|
||||
{
|
||||
++SteadyLevelCounts.FindOrAdd(Key.Level);
|
||||
@@ -3604,6 +3646,16 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
Max.Y = FMath::Max(Max.Y, Key.Coord.Y);
|
||||
Max.Z = FMath::Max(Max.Z, Key.Coord.Z);
|
||||
}
|
||||
for (const FVoxelTileKey& Key : LoadedTiles)
|
||||
{
|
||||
++LoadedLevelCounts.FindOrAdd(Key.Level);
|
||||
if (const FLoadedTileGeometryStats* Geometry = LoadedTileGeometry.Find(Key))
|
||||
{
|
||||
LoadedLevelVertices.FindOrAdd(Key.Level) += Geometry->Vertices;
|
||||
LoadedLevelTriangles.FindOrAdd(Key.Level) += Geometry->Triangles;
|
||||
LoadedLevelGeometryBytes.FindOrAdd(Key.Level) += Geometry->GeometryBytes;
|
||||
}
|
||||
}
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("steady_state"), FString::Printf(
|
||||
TEXT("\"desired_tiles\":%d,\"loaded_tiles\":%d,\"collision_ready_tiles\":%d,"
|
||||
"\"collision_not_required_tiles\":%d,\"pending_collision_cooks\":%d,"
|
||||
@@ -3635,8 +3687,22 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
*FString::SanitizeFloat(TileExtentMetres),
|
||||
*FString::SanitizeFloat(SpanX), *FString::SanitizeFloat(SpanY), *FString::SanitizeFloat(SpanZ),
|
||||
Settings && Level > VF_MaxMarchingCubesLevel(Settings) ? 1 : 0));
|
||||
VoxelForgeStartupTrace::RecordEvent(TEXT("steady_loaded_level"), FString::Printf(
|
||||
TEXT("\"level\":%d,\"tiles\":%d,\"vertices\":%llu,\"triangles\":%llu,\"geometry_bytes\":%llu"),
|
||||
Level, LoadedLevelCounts.FindRef(Level),
|
||||
static_cast<unsigned long long>(LoadedLevelVertices.FindRef(Level)),
|
||||
static_cast<unsigned long long>(LoadedLevelTriangles.FindRef(Level)),
|
||||
static_cast<unsigned long long>(LoadedLevelGeometryBytes.FindRef(Level))));
|
||||
}
|
||||
const bool bExitOnSteady = bHeadlessStreamingTestExitOnSteady;
|
||||
VoxelForgeStartupTrace::Finish(TEXT("desired_set_satisfied_and_queue_drained"));
|
||||
if (bExitOnSteady && !bHeadlessStreamingTestExitRequested)
|
||||
{
|
||||
bHeadlessStreamingTestExitRequested = true;
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeStreamingTest] steady_exit_requested=1"));
|
||||
FPlatformMisc::RequestExit(false, TEXT("VoxelForge steady streaming measurement complete"));
|
||||
}
|
||||
}
|
||||
|
||||
// Complete the one-shot diagnostic request only after the player's level-0 centre tile is
|
||||
@@ -3745,12 +3811,17 @@ void AVoxelWorld::Tick(float DeltaTime)
|
||||
>= static_cast<double>(TestCeilingCaptureDelaySeconds))
|
||||
{
|
||||
bTestCeilingCaptureRequested = true;
|
||||
const bool bExecSucceeded = GEngine
|
||||
&& GEngine->Exec(World, TEXT("HighResShot 1"));
|
||||
// UEngine::Exec is not routed through the game viewport in the command-line game
|
||||
// target, so HighResShot returns false there even with a live render viewport. Call
|
||||
// the viewport API directly; it schedules the same one-frame high-res capture.
|
||||
const bool bCaptureAccepted = GEngine
|
||||
&& GEngine->GameViewport
|
||||
&& GEngine->GameViewport->GetGameViewport()
|
||||
&& GEngine->GameViewport->GetGameViewport()->TakeHighResScreenShot();
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeTestCeiling] capture_requested=%d elapsed_s=%.3f "
|
||||
"visible_tiles=%llu triangles=%llu"),
|
||||
bExecSucceeded ? 1 : 0,
|
||||
bCaptureAccepted ? 1 : 0,
|
||||
FPlatformTime::Seconds() - TestCeilingViewBeginSeconds,
|
||||
static_cast<unsigned long long>(AppliedVisibleTileCount),
|
||||
static_cast<unsigned long long>(AppliedTriangleCount));
|
||||
@@ -4680,6 +4751,7 @@ void AVoxelWorld::HandleTileCollisionCookComplete(
|
||||
// A failed/ignored current cook is not a loaded guarantee. Leaving the tile unloaded makes
|
||||
// the normal submit loop retry it while the pawn remains gated.
|
||||
LoadedTiles.Remove(Tile);
|
||||
LoadedTileGeometry.Remove(Tile);
|
||||
bAllChunksLoaded = false;
|
||||
}
|
||||
|
||||
@@ -4927,7 +4999,9 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
|
||||
Sample.Verdict = Result.ClassifyVerdict;
|
||||
Sample.bSealedSolidProof = Result.bSealedSolidProof;
|
||||
Sample.bEmpty = bResultEmpty;
|
||||
Sample.Vertices = Result.NumVertices;
|
||||
Sample.Triangles = Result.NumTriangles;
|
||||
Sample.GeometryBytes = Result.GeometryBytes;
|
||||
Sample.RequestToApplySeconds = RequestToApply;
|
||||
Sample.WorkerQueueSeconds = WorkerQueueSeconds;
|
||||
Sample.ResultQueueSeconds = ResultQueueSeconds;
|
||||
@@ -4992,6 +5066,7 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
|
||||
FPlatformTime::Cycles64() - EmptyReleaseStartCycles));
|
||||
}
|
||||
LoadedTiles.Add(Result.Tile);
|
||||
LoadedTileGeometry.Remove(Result.Tile);
|
||||
LogTileApplied();
|
||||
if (Result.Tile.Level == 0 && Result.bRecordStreamingLatency)
|
||||
{
|
||||
@@ -5014,11 +5089,16 @@ bool AVoxelWorld::ApplyTileResult(FChunkResult& Result)
|
||||
CollisionSolidTiles.Remove(Result.Tile);
|
||||
PendingCollisionCooks.Remove(Result.Tile);
|
||||
LoadedTiles.Remove(Result.Tile);
|
||||
LoadedTileGeometry.Remove(Result.Tile);
|
||||
bAllChunksLoaded = false;
|
||||
LogTileApplied();
|
||||
return false;
|
||||
}
|
||||
LoadedTiles.Add(Result.Tile);
|
||||
LoadedTileGeometry.Add(Result.Tile, FLoadedTileGeometryStats{
|
||||
static_cast<uint64>(FMath::Max(0, Result.NumVertices)),
|
||||
static_cast<uint64>(FMath::Max(0, Result.NumTriangles)),
|
||||
Result.GeometryBytes});
|
||||
LogTileApplied();
|
||||
return true;
|
||||
}
|
||||
@@ -6503,6 +6583,12 @@ void AVoxelWorld::GenerateTileResult(const FVoxelTileKey& Tile, const FIntVector
|
||||
VoxelDensityProfile::FScopedTimer RuntimeStreamTimer(
|
||||
VoxelDensityProfile::EBucket::RuntimeStreamBuilding);
|
||||
BuildTileStreamSet(*Result.Streams, MeshData);
|
||||
Result.NumVertices = MeshData.Vertices.Num();
|
||||
Result.GeometryBytes = 0;
|
||||
Result.Streams->ForEach([&](const RealtimeMesh::FRealtimeMeshStream& Stream)
|
||||
{
|
||||
Result.GeometryBytes += static_cast<uint64>(Stream.GetAllocatedSize());
|
||||
});
|
||||
StreamSeconds = bMeasureTile ? FPlatformTime::Seconds() - StreamStartSeconds : 0.0;
|
||||
if (ShouldAbortWork(ObsoleteFlag))
|
||||
{
|
||||
@@ -6651,6 +6737,7 @@ void AVoxelWorld::UnloadTile(const FVoxelTileKey& Tile)
|
||||
TileComponents.Remove(Tile);
|
||||
}
|
||||
LoadedTiles.Remove(Tile);
|
||||
LoadedTileGeometry.Remove(Tile);
|
||||
TransitionHold.Remove(Tile); // couvre aussi le settled cull (qui ne tient pas la hold à jour)
|
||||
}
|
||||
|
||||
|
||||
@@ -148,8 +148,9 @@ public:
|
||||
|
||||
// Coarsest LOD level. 0 = full-res only (no clipmap). Each level up doubles tile size &
|
||||
// reach at ~constant cost — raise this to see much farther for cheap. (Level L step = 2^L.)
|
||||
// Ship default: level 5, the measured horizon-distance balance at 768 chunks.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0", ClampMax = "8"))
|
||||
int32 MaxClipLevel = 4;
|
||||
int32 MaxClipLevel = 5;
|
||||
|
||||
// How many near levels mesh at FULL resolution (CHUNK_SIZE cells). Levels at or above this
|
||||
// use the cheaper CoarseTileCells → MUCH faster gen for the far field (it's far, so the
|
||||
@@ -171,9 +172,9 @@ public:
|
||||
// cell must fit inside a strate band — level ≥7 blanks via the too-coarse skip) and buy the
|
||||
// remaining horizon here. Cost: the extra ring is all same-level tiles — tile/draw/gen count
|
||||
// grows with (distance / 2^MaxClipLevel)², so each MaxClipLevel step down quadruples the ring.
|
||||
// 0 = off (natural reach, byte-identical streaming).
|
||||
// Ship default: 768 chunks (about 6.1 km at 8 m/chunk). 0 = off (natural reach).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap", meta = (ClampMin = "0"))
|
||||
int32 RenderDistanceChunks = 0;
|
||||
int32 RenderDistanceChunks = 768;
|
||||
|
||||
// F18 — the render-distance ring streams per-surface SHEETS instead of MC tiles: in an open
|
||||
// strate the far field is exactly two heightfields (TerrainZ + sky-cap CeilSurf, both already
|
||||
@@ -182,10 +183,11 @@ public:
|
||||
// sheet spans 2^FarSheetSpanLevels MC-tile footprints per axis). Non-open strates produce
|
||||
// empty sheets (their far ring was enclosed rock anyway). Carved features (passages, chasms,
|
||||
// spine) don't show at sheet distance. Needs the strate band armed (StrateContentCutMinLevel
|
||||
// active); in the inter-strate gap the sheet ring blanks until you land. Off = the ring stays
|
||||
// MC tiles at level MaxClipLevel (pre-F18 behaviour). Only matters when RenderDistanceChunks > 0.
|
||||
// active); in the inter-strate gap the sheet ring blanks until you land. The ship default is OFF:
|
||||
// the ring stays real MC tiles at level MaxClipLevel (pre-F18 behaviour). Only matters when
|
||||
// RenderDistanceChunks > 0.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel|Clipmap")
|
||||
bool bFarSheetRing = true;
|
||||
bool bFarSheetRing = false;
|
||||
|
||||
// Sheet tile size = MaxClipLevel + this many levels (2 → one sheet covers 4×4 MC-tile
|
||||
// footprints → ~16× fewer far components). Sampling density stays that of the MaxClipLevel
|
||||
|
||||
@@ -16,7 +16,9 @@ namespace VoxelForgeStartupTrace
|
||||
int32 Verdict = -1; // Mixed=0, AllSolid=1, AllAir=2, -1=not classified (sheet/early path)
|
||||
bool bSealedSolidProof = false;
|
||||
bool bEmpty = true;
|
||||
int32 Vertices = 0;
|
||||
int32 Triangles = 0;
|
||||
uint64 GeometryBytes = 0;
|
||||
// This is worker-result -> game-thread mesh submission. Collision readiness is a later
|
||||
// per-tile RMC completion event recorded separately as collision_ready.
|
||||
double RequestToApplySeconds = 0.0;
|
||||
|
||||
@@ -109,6 +109,8 @@ struct FChunkResult
|
||||
uint64 GenerationEndCycles = 0;
|
||||
uint64 ApplyStartCycles = 0;
|
||||
int32 ClassifyVerdict = -1; double ClassifySeconds = 0.0; double MeshSeconds = 0.0; double StreamSeconds = 0.0; int32 NumTriangles = 0; int32 NumCeilingTriangles = 0;
|
||||
int32 NumVertices = 0;
|
||||
uint64 GeometryBytes = 0;
|
||||
bool bSealedSolidProof = false; // true when the cheap gap/seal proof skipped meshing
|
||||
bool bOutOfLayoutAirProof = false; // true when the exact constant-air guard skipped meshing
|
||||
// Clean, low-overhead work counters. These are carried with the result so the worker can
|
||||
@@ -154,6 +156,14 @@ struct FVoxelTileHashDumpRecord
|
||||
FString GeometryHash;
|
||||
};
|
||||
|
||||
/** Resident CPU geometry payload associated with one currently loaded tile. */
|
||||
struct FLoadedTileGeometryStats
|
||||
{
|
||||
uint64 Vertices = 0;
|
||||
uint64 Triangles = 0;
|
||||
uint64 GeometryBytes = 0;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class VOXELFORGE_API AVoxelWorld : public AActor
|
||||
{
|
||||
@@ -985,6 +995,7 @@ public:
|
||||
uint64 AppliedVisibleTileCountByLevel[TrackedClassifierLODCount]{};
|
||||
uint64 AppliedTriangleCountByLevel[TrackedClassifierLODCount]{};
|
||||
uint64 AppliedBandTileCountByLevel[TrackedClassifierLODCount]{};
|
||||
TMap<FVoxelTileKey, FLoadedTileGeometryStats> LoadedTileGeometry;
|
||||
|
||||
struct FStreamingLatencySample
|
||||
{
|
||||
@@ -998,6 +1009,7 @@ public:
|
||||
// Headless validation hook. These are intentionally non-UPROPERTY state and are enabled only
|
||||
// by explicit command-line switches; they do not alter ordinary player movement or streaming.
|
||||
bool bHeadlessStreamingTestMovement = false;
|
||||
bool bHeadlessStreamingTestExitOnSteady = false;
|
||||
bool bHeadlessStreamingTestExitRequested = false;
|
||||
bool bStartupTraceThroughCrossing = false;
|
||||
bool bHeadlessStreamingTestCenterOverride = false;
|
||||
|
||||
Reference in new issue
Block a user