wip(assembler): round S14 -- 4-way column cache + memoized MC gradients (output unchanged)
Worker-local XY column cache uses deterministic 4-way sets at LOD3-8: LOD4 misses 8,224 -> 1,437, conflicts 7,407 -> 0 (direct-map control: 6.62 s / 43.5 worker-s vs 4.24 s / 21.7). Exact LOD0 lattice gradients memoized per tile: MC-exclusive 242 -> 201 ns/sample, two-pass loop unchanged. Hashes identical, no recipe bump. Primary cold trace 4.24 s full set, ring 0.45 s, 75/75 at 40 m/s; a repeat ran uniformly ~40% slower on every LOD (machine contention suspected); tick p95 to re-check on an idle machine. CVars voxel.CoreColumnCacheDirectMapped / voxel.CoreMCGradientMemoization. Report: Standalone/Research/WorldAssemblerM3/Round18. 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
bd5e38594f
commit
ec2afe9c93
11 files changed
+769
-50
No files matched your search
@@ -120,13 +120,16 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
int64* OutDensitySampleCount,
|
||||
FVoxelSharedDensityGrid* OutPreEditDensityGrid,
|
||||
const FVoxelSharedDensityGrid* InSharedDensityGrid,
|
||||
std::vector<float>* OutDebugDensitySamples)
|
||||
std::vector<float>* OutDebugDensitySamples,
|
||||
FVoxelMarchingCubesStageTimings* OutStageTimings)
|
||||
{
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_MesherGenerateMesh);
|
||||
const uint64 StageMesherStartCycles=OutStageTimings?FPlatformTime::Cycles64():0;
|
||||
FVoxelMeshData MeshData;
|
||||
if (OutCaptureGrid) { OutCaptureGrid->Reset(); }
|
||||
if (OutPreEditDensityGrid) { OutPreEditDensityGrid->Reset(); }
|
||||
if (OutDensitySampleCount) { *OutDensitySampleCount = 0; }
|
||||
if (OutStageTimings) { *OutStageTimings = FVoxelMarchingCubesStageTimings{}; }
|
||||
if (OutDebugDensitySamples) { OutDebugDensitySamples->clear(); }
|
||||
if (!Generator) return MeshData;
|
||||
const bool bEmitUV = !bStandaloneSkipUvAndColors && !bStandaloneSkipUV;
|
||||
@@ -851,6 +854,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
// Bande de strate : seules les rangées Z réellement lues (cellules CzLo..CzHi + marges de
|
||||
// gradient) sont échantillonnées — le reste du tampon reste non initialisé et non lu.
|
||||
const uint64 StageDensityGridStartCycles=OutStageTimings?FPlatformTime::Cycles64():0;
|
||||
VoxelDensityProfile::FScopedTimer MesherDensityGridTimer(
|
||||
VoxelDensityProfile::EBucket::MesherDensityGrid);
|
||||
#if WITH_EDITOR
|
||||
@@ -1250,6 +1254,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
}
|
||||
MesherDensityGridTimer.End();
|
||||
if(OutStageTimings)
|
||||
OutStageTimings->DensityGridMilliseconds=static_cast<double>(
|
||||
FPlatformTime::Cycles64()-StageDensityGridStartCycles)
|
||||
*FPlatformTime::GetSecondsPerCycle64()*1000.0;
|
||||
|
||||
if (bUseProofBlocks)
|
||||
{
|
||||
@@ -1345,6 +1353,22 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
return DensityGrid[((gz + 1) * MDim + (gy + 1)) * MDim + (gx + 1)];
|
||||
};
|
||||
// Reuse exact grid-corner gradients between incident cells within this tile.
|
||||
// Reutiliser les gradients exacts des coins entre cellules voisines de cette tuile.
|
||||
static thread_local TArray<FVector> GradientMemo;
|
||||
static thread_local TArray<uint8> GradientMemoValid;
|
||||
const bool bUseGradientMemo=bStandaloneEnableGradientMemoization
|
||||
&&ShapeWorldContext!=nullptr&&Step==1;
|
||||
if(bUseGradientMemo)
|
||||
{
|
||||
GradientMemo.SetNumUninitialized(DensityGrid.Num());
|
||||
GradientMemoValid.SetNumUninitialized(DensityGrid.Num());
|
||||
// Clear every tile even when the reused TArray keeps the same length.
|
||||
// Effacer chaque tuile meme si le TArray reutilise conserve la meme taille.
|
||||
if(GradientMemoValid.Num()>0)
|
||||
FMemory::Memzero(GradientMemoValid.GetData(),
|
||||
static_cast<SIZE_T>(GradientMemoValid.Num())*sizeof(uint8));
|
||||
}
|
||||
auto SampleGradientG = [&](int32 gx, int32 gy, int32 gz) -> float
|
||||
{
|
||||
const float Cached = SampleG(gx, gy, gz);
|
||||
@@ -1353,6 +1377,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
return Cached;
|
||||
}
|
||||
if(OutStageTimings)++OutStageTimings->GradientFallbackDensityCalls;
|
||||
return ShapeWorldContext->DensityAtUnclamped(
|
||||
static_cast<float>(OriginVoxels.X + gx * Step),
|
||||
static_cast<float>(OriginVoxels.Y + gy * Step),
|
||||
@@ -1360,11 +1385,23 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
};
|
||||
auto GradAt = [&](int32 gx, int32 gy, int32 gz) -> FVector
|
||||
{
|
||||
const int32 CacheIndex=((gz+1)*MDim+(gy+1))*MDim+(gx+1);
|
||||
if(bUseGradientMemo&&GradientMemoValid[CacheIndex]!=0)
|
||||
{
|
||||
if(OutStageTimings)++OutStageTimings->GradientMemoHits;
|
||||
return GradientMemo[CacheIndex];
|
||||
}
|
||||
// Densité négative=solide, positive=air → le gradient pointe vers l'air (sortant).
|
||||
return FVector(
|
||||
const FVector Gradient(
|
||||
SampleGradientG(gx + 1, gy, gz) - SampleGradientG(gx - 1, gy, gz),
|
||||
SampleGradientG(gx, gy + 1, gz) - SampleGradientG(gx, gy - 1, gz),
|
||||
SampleGradientG(gx, gy, gz + 1) - SampleGradientG(gx, gy, gz - 1));
|
||||
if(bUseGradientMemo)
|
||||
{
|
||||
GradientMemo[CacheIndex]=Gradient;
|
||||
GradientMemoValid[CacheIndex]=1;
|
||||
}
|
||||
return Gradient;
|
||||
};
|
||||
|
||||
// DC is limited to tagged LOD0 patches. Coarse tiles and a connected patch that reaches a
|
||||
@@ -1401,8 +1438,12 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
GroundTris.Reset();
|
||||
CapTris.Reset();
|
||||
const bool bProfileMesher = VoxelDensityProfile::IsCycleTimingEnabled();
|
||||
const bool bAuditMesherStages=OutStageTimings!=nullptr;
|
||||
uint64 CellClassificationCalls = 0;
|
||||
uint64 SurfaceCellCalls = 0;
|
||||
uint64 AuditCellClassificationCycles=0,AuditGradientCycles=0;
|
||||
uint64 AuditInterpolationCycles=0,AuditDedupStreamCycles=0;
|
||||
uint64 AuditGradientSamples=0,AuditInterpolationSamples=0,AuditDedupStreamSamples=0;
|
||||
uint8 CaseIndexRow[CHUNK_SIZE]{};
|
||||
uint8 SurfaceRow[CHUNK_SIZE]{};
|
||||
uint8 SampleClassificationRow[CHUNK_SIZE]{};
|
||||
@@ -1413,7 +1454,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
{
|
||||
if (ShouldAbortWork()) return FVoxelMeshData();
|
||||
FMemory::Memzero(SampleClassificationRow, sizeof(SampleClassificationRow));
|
||||
const uint64 ClassificationRowStart = bProfileMesher
|
||||
const uint64 ClassificationRowStart = (bProfileMesher||bAuditMesherStages)
|
||||
? FPlatformTime::Cycles64() : 0;
|
||||
int32 ClassificationRowCalls = 0;
|
||||
for (int32 GroupX = 0; GroupX < CellsPerAxis; GroupX += 8)
|
||||
@@ -1454,9 +1495,10 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
CaseIndexRow[CellX] = static_cast<uint8>(CaseIndex);
|
||||
SurfaceRow[CellX] = MCEdgeTable[CaseIndex] != 0;
|
||||
SampleClassificationRow[CellX] = bProfileMesher
|
||||
&& VoxelDensityProfile::ShouldSample(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification);
|
||||
SampleClassificationRow[CellX] =
|
||||
(bProfileMesher&&VoxelDensityProfile::ShouldSample(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification))
|
||||
||(bAuditMesherStages&&(CellClassificationCalls%8u)==0u);
|
||||
++ClassificationRowCalls;
|
||||
++CellClassificationCalls;
|
||||
}
|
||||
@@ -1477,26 +1519,31 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
CaseIndexRow[CellX] = static_cast<uint8>(CaseIndex);
|
||||
SurfaceRow[CellX] = MCEdgeTable[CaseIndex] != 0;
|
||||
SampleClassificationRow[CellX] = bProfileMesher
|
||||
&& VoxelDensityProfile::ShouldSample(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification);
|
||||
SampleClassificationRow[CellX] =
|
||||
(bProfileMesher&&VoxelDensityProfile::ShouldSample(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification))
|
||||
||(bAuditMesherStages&&(CellClassificationCalls%8u)==0u);
|
||||
++ClassificationRowCalls;
|
||||
++CellClassificationCalls;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bProfileMesher && ClassificationRowCalls > 0)
|
||||
if ((bProfileMesher||bAuditMesherStages) && ClassificationRowCalls > 0)
|
||||
{
|
||||
const uint64 RowCycles = FPlatformTime::Cycles64() - ClassificationRowStart;
|
||||
const uint64 PerCellCycles = RowCycles
|
||||
/ static_cast<uint64>(ClassificationRowCalls);
|
||||
for (int32 cx = 0; cx < CellsPerAxis; ++cx)
|
||||
if(bAuditMesherStages)AuditCellClassificationCycles+=RowCycles;
|
||||
if(bProfileMesher)
|
||||
{
|
||||
if (SampleClassificationRow[cx])
|
||||
const uint64 PerCellCycles = RowCycles
|
||||
/ static_cast<uint64>(ClassificationRowCalls);
|
||||
for (int32 cx = 0; cx < CellsPerAxis; ++cx)
|
||||
{
|
||||
VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification,
|
||||
PerCellCycles);
|
||||
if (SampleClassificationRow[cx])
|
||||
{
|
||||
VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherCellClassification,
|
||||
PerCellCycles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1537,9 +1584,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
if (bSampleCell)
|
||||
{
|
||||
VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherGradientNormals,
|
||||
FPlatformTime::Cycles64() - GradientStart);
|
||||
const uint64 StageCycles=FPlatformTime::Cycles64()-GradientStart;
|
||||
if(bProfileMesher)VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherGradientNormals,StageCycles);
|
||||
if(bAuditMesherStages)
|
||||
{AuditGradientCycles+=StageCycles;++AuditGradientSamples;}
|
||||
}
|
||||
|
||||
// Interpolation des positions + normales sur les arêtes traversées. t = point de
|
||||
@@ -1563,6 +1612,7 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
if(ShapeWorldContext&&Step==1&&T>=0.375f&&T<=0.625f)
|
||||
{
|
||||
const FVector Midpoint=(Positions[A]+Positions[B])*0.5f;
|
||||
if(OutStageTimings)++OutStageTimings->EdgeMidpointDensityCalls;
|
||||
const float MidDensity=ShapeWorldContext->DensityAtUnclamped(
|
||||
Midpoint.X/GeometryVoxelSize,Midpoint.Y/GeometryVoxelSize,
|
||||
Midpoint.Z/GeometryVoxelSize);
|
||||
@@ -1611,9 +1661,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
if (bSampleCell)
|
||||
{
|
||||
VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherVertexInterpolation,
|
||||
FPlatformTime::Cycles64() - VertexInterpolationStart);
|
||||
const uint64 StageCycles=FPlatformTime::Cycles64()-VertexInterpolationStart;
|
||||
if(bProfileMesher)VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherVertexInterpolation,StageCycles);
|
||||
if(bAuditMesherStages)
|
||||
{AuditInterpolationCycles+=StageCycles;++AuditInterpolationSamples;}
|
||||
}
|
||||
|
||||
// Génère les triangles avec vertices dédupliqués.
|
||||
@@ -1651,9 +1703,11 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
}
|
||||
if (bSampleCell)
|
||||
{
|
||||
VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherStreamBuilding,
|
||||
FPlatformTime::Cycles64() - StreamBuildingStart);
|
||||
const uint64 StageCycles=FPlatformTime::Cycles64()-StreamBuildingStart;
|
||||
if(bProfileMesher)VoxelDensityProfile::AddSampledMeasurement(
|
||||
VoxelDensityProfile::EBucket::MesherStreamBuilding,StageCycles);
|
||||
if(bAuditMesherStages)
|
||||
{AuditDedupStreamCycles+=StageCycles;++AuditDedupStreamSamples;}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1989,5 +2043,31 @@ FVoxelMeshData UVoxelMarchingCubesMesher::GenerateMesh(FIntVector OriginVoxels,
|
||||
OutputAllocatedBytes);
|
||||
}
|
||||
|
||||
if(OutStageTimings)
|
||||
{
|
||||
const double CycleToMs=FPlatformTime::GetSecondsPerCycle64()*1000.0;
|
||||
const auto ScaleSampledCycles=[CycleToMs](uint64 Cycles,uint64 Samples,uint64 Calls)
|
||||
{
|
||||
return Samples>0?static_cast<double>(Cycles)*static_cast<double>(Calls)
|
||||
/static_cast<double>(Samples)*CycleToMs:0.0;
|
||||
};
|
||||
OutStageTimings->CellClassificationMilliseconds=
|
||||
static_cast<double>(AuditCellClassificationCycles)*CycleToMs;
|
||||
OutStageTimings->GradientNormalsMilliseconds=ScaleSampledCycles(
|
||||
AuditGradientCycles,AuditGradientSamples,SurfaceCellCalls);
|
||||
OutStageTimings->EdgeInterpolationMilliseconds=ScaleSampledCycles(
|
||||
AuditInterpolationCycles,AuditInterpolationSamples,SurfaceCellCalls);
|
||||
OutStageTimings->VertexDedupStreamMilliseconds=ScaleSampledCycles(
|
||||
AuditDedupStreamCycles,AuditDedupStreamSamples,SurfaceCellCalls);
|
||||
const double TotalMilliseconds=static_cast<double>(
|
||||
FPlatformTime::Cycles64()-StageMesherStartCycles)*CycleToMs;
|
||||
OutStageTimings->OtherMilliseconds=FMath::Max(0.0,TotalMilliseconds
|
||||
-OutStageTimings->DensityGridMilliseconds
|
||||
-OutStageTimings->CellClassificationMilliseconds
|
||||
-OutStageTimings->GradientNormalsMilliseconds
|
||||
-OutStageTimings->EdgeInterpolationMilliseconds
|
||||
-OutStageTimings->VertexDedupStreamMilliseconds);
|
||||
}
|
||||
|
||||
return MeshData;
|
||||
}
|
||||
@@ -41,6 +41,21 @@ struct FVoxelSharedDensityGrid
|
||||
}
|
||||
};
|
||||
|
||||
// Optional, sampled timing output for the research audit; null in production calls.
|
||||
// Mesures echantillonnees facultatives pour l'audit; pointeur nul en production.
|
||||
struct FVoxelMarchingCubesStageTimings
|
||||
{
|
||||
double DensityGridMilliseconds = 0.0;
|
||||
double CellClassificationMilliseconds = 0.0;
|
||||
double GradientNormalsMilliseconds = 0.0;
|
||||
double EdgeInterpolationMilliseconds = 0.0;
|
||||
double VertexDedupStreamMilliseconds = 0.0;
|
||||
double OtherMilliseconds = 0.0;
|
||||
uint64 GradientFallbackDensityCalls = 0;
|
||||
uint64 EdgeMidpointDensityCalls = 0;
|
||||
uint64 GradientMemoHits = 0;
|
||||
};
|
||||
|
||||
// Stable plan data copied onto one worker-owned mesher for hybrid LOD0 generation.
|
||||
// Donnees stables du plan copiees dans un mailleur de worker pour le mode hybride LOD0.
|
||||
struct FVoxelHybridSharpFeatureBox
|
||||
@@ -91,7 +106,8 @@ public:
|
||||
int64* OutDensitySampleCount = nullptr,
|
||||
FVoxelSharedDensityGrid* OutPreEditDensityGrid = nullptr,
|
||||
const FVoxelSharedDensityGrid* InSharedDensityGrid = nullptr,
|
||||
std::vector<float>* OutDebugDensitySamples = nullptr);
|
||||
std::vector<float>* OutDebugDensitySamples = nullptr,
|
||||
FVoxelMarchingCubesStageTimings* OutStageTimings = nullptr);
|
||||
|
||||
//=========================================================================
|
||||
// SERVICES (injectés par AVoxelWorld)
|
||||
@@ -161,6 +177,7 @@ public:
|
||||
bool bStandaloneSkipUV = false;
|
||||
bool bStandaloneSkipColors = false;
|
||||
bool bStandaloneSkipMaterialGroups = false;
|
||||
bool bStandaloneEnableGradientMemoization = true;
|
||||
|
||||
// Seuil de l'isosurface dans le champ de densité.
|
||||
// Convention MC: densité < IsoLevel = solide, >= = air.
|
||||
|
||||
Reference in new issue
Block a user