standalone: speed round closed -- full-payload whole view 2.16 s @8 workers; UV/colour/groups cost 0.62 s
Takeover after the PC reset. Verified at 1 and 8 workers: density, geometry, full payload and topology hashes equal for all nine outputs; routes 5642/5642; crater transaction 17 ms; player neighbourhood mesh-ready 23 ms after streaming starts. - The plugin's materials consume UV, the packed biome/slope/blend colours and the ceiling/ground sections, so the viewer-only skips stay off by default (diagnostic only: 1.53 s without them). - UVs staged as FVector2f (matches the game stream; -20 MB, no speed change). Far-bin reserve 24x. - LOD6 is the long pole: 32.7 M density samples, 1.83 M vertices, ~1.1 s. 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
ee5b05b357
commit
6ec4a8f856
10 files changed
+417
-166
No files matched your search
@@ -42,9 +42,94 @@ struct FStandaloneCpuTile
|
||||
bool bHasDensityHash=false;
|
||||
uint64 MeshHash=14695981039346656037ull;
|
||||
bool bHasMeshHash=false;
|
||||
uint64 PayloadHash=14695981039346656037ull;
|
||||
bool bHasPayloadHash=false;
|
||||
uint64 TopologyHash=14695981039346656037ull;
|
||||
bool bHasTopologyHash=false;
|
||||
uint64 MeshPayloadAllocatedBytes=0;
|
||||
std::array<uint64,VoxelDensityProfile::BucketCount> ProfileCycles{};
|
||||
};
|
||||
|
||||
inline uint64 HashStandaloneMeshPayload(const FVoxelMeshData& Mesh)
|
||||
{
|
||||
uint64 Hash=14695981039346656037ull;
|
||||
const auto AddWord=[&](uint64 Word,int32 Bytes)
|
||||
{
|
||||
for(int32 I=0;I<Bytes;++I)
|
||||
{
|
||||
Hash^=static_cast<uint8>(Word>>(I*8));
|
||||
Hash*=1099511628211ull;
|
||||
}
|
||||
};
|
||||
const auto AddDouble=[&](double Value)
|
||||
{ AddWord(std::bit_cast<uint64>(Value),8); };
|
||||
AddWord(static_cast<uint64>(Mesh.Vertices.Num()),8);
|
||||
for(const FVector& Position:Mesh.Vertices)
|
||||
{ AddDouble(Position.X);AddDouble(Position.Y);AddDouble(Position.Z); }
|
||||
AddWord(static_cast<uint64>(Mesh.Normals.Num()),8);
|
||||
for(const FVector& Normal:Mesh.Normals)
|
||||
{ AddDouble(Normal.X);AddDouble(Normal.Y);AddDouble(Normal.Z); }
|
||||
AddWord(static_cast<uint64>(Mesh.UVs.Num()),8);
|
||||
for(const auto& UV:Mesh.UVs)
|
||||
{ AddDouble(static_cast<double>(UV.X));AddDouble(static_cast<double>(UV.Y)); }
|
||||
AddWord(static_cast<uint64>(Mesh.Colors.Num()),8);
|
||||
for(const FColor& Color:Mesh.Colors)
|
||||
{ AddWord(Color.R,1);AddWord(Color.G,1);AddWord(Color.B,1);AddWord(Color.A,1); }
|
||||
AddWord(static_cast<uint64>(Mesh.Triangles.Num()),8);
|
||||
for(const int32 Index:Mesh.Triangles)AddWord(static_cast<uint32>(Index),4);
|
||||
AddWord(static_cast<uint32>(Mesh.NumSurfaceVertices),4);
|
||||
AddWord(static_cast<uint32>(Mesh.NumSkirtTriangles),4);
|
||||
AddWord(static_cast<uint32>(Mesh.NumCeilingTriangles),4);
|
||||
return Hash;
|
||||
}
|
||||
|
||||
inline uint64 HashStandaloneMeshTopology(const FVoxelMeshData& Mesh)
|
||||
{
|
||||
uint64 Hash=14695981039346656037ull;
|
||||
const auto AddWord=[&](uint64 Word,int32 Bytes)
|
||||
{
|
||||
for(int32 I=0;I<Bytes;++I)
|
||||
{
|
||||
Hash^=static_cast<uint8>(Word>>(I*8));
|
||||
Hash*=1099511628211ull;
|
||||
}
|
||||
};
|
||||
using FTriangleKey=std::array<uint64,3>;
|
||||
const auto VertexKey=[&](int32 Index)
|
||||
{
|
||||
uint64 VertexHash=14695981039346656037ull;
|
||||
const auto AddVertexWord=[&](uint64 Word)
|
||||
{
|
||||
for(int32 Byte=0;Byte<8;++Byte)
|
||||
{
|
||||
VertexHash^=static_cast<uint8>(Word>>(Byte*8));
|
||||
VertexHash*=1099511628211ull;
|
||||
}
|
||||
};
|
||||
if(Index<0||Index>=Mesh.Vertices.Num()||Index>=Mesh.Normals.Num())
|
||||
{ AddVertexWord(UINT64_MAX);return VertexHash; }
|
||||
const FVector& Position=Mesh.Vertices[Index];
|
||||
const FVector& Normal=Mesh.Normals[Index];
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Position.X)));
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Position.Y)));
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Position.Z)));
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Normal.X)));
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Normal.Y)));
|
||||
AddVertexWord(std::bit_cast<uint64>(static_cast<double>(Normal.Z)));
|
||||
return VertexHash;
|
||||
};
|
||||
std::vector<FTriangleKey> Triangles;
|
||||
Triangles.reserve(static_cast<size_t>(Mesh.Triangles.Num()/3));
|
||||
for(int32 I=0;I+2<Mesh.Triangles.Num();I+=3)
|
||||
Triangles.push_back({VertexKey(Mesh.Triangles[I]),
|
||||
VertexKey(Mesh.Triangles[I+1]),VertexKey(Mesh.Triangles[I+2])});
|
||||
std::sort(Triangles.begin(),Triangles.end());
|
||||
AddWord(static_cast<uint64>(Triangles.size()),8);
|
||||
for(const FTriangleKey& Triangle:Triangles)
|
||||
for(const uint64 VertexHash:Triangle)AddWord(VertexHash,8);
|
||||
return Hash;
|
||||
}
|
||||
|
||||
inline uint64 HashStandaloneCpuTileMesh(const FStandaloneCpuTile& Tile)
|
||||
{
|
||||
uint64 Hash=14695981039346656037ull;
|
||||
@@ -310,9 +395,12 @@ public:
|
||||
explicit FStandaloneStreamingGenerator(FStandaloneOwnerWorld& InWorld,int32 InWorkerCount=8,
|
||||
bool bInHashDensity=false,
|
||||
const FStandaloneMeshCache* InMeshCache=nullptr,
|
||||
int32 InMaxLevelFilter=-1)
|
||||
int32 InMaxLevelFilter=-1,
|
||||
bool bInSkipUvAndColors=false,
|
||||
bool bInSkipMaterialGroups=false)
|
||||
:World(&InWorld),WorkerCount(FMath::Clamp(InWorkerCount,1,16)),bHashDensity(bInHashDensity),
|
||||
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter)
|
||||
MeshCache(InMeshCache),MaxLevelFilter(InMaxLevelFilter),
|
||||
bSkipUvAndColors(bInSkipUvAndColors),bSkipMaterialGroups(bInSkipMaterialGroups)
|
||||
{
|
||||
VoxelGenLOD::InitializeConsoleSwitches();
|
||||
UVoxelMarchingCubesMesher::InitializeConsoleSwitches();
|
||||
@@ -454,7 +542,8 @@ private:
|
||||
}
|
||||
UVoxelMarchingCubesMesher Mesher;
|
||||
Mesher.SetGenerator(World->Generator.get());
|
||||
Mesher.bStandaloneCompactPayload=true;
|
||||
Mesher.bStandaloneSkipUvAndColors=bSkipUvAndColors;
|
||||
Mesher.bStandaloneSkipMaterialGroups=bSkipMaterialGroups;
|
||||
Mesher.bGenerateSkirts=World->bGenerateSkirts;
|
||||
Mesher.SkirtCells=static_cast<float>(World->SkirtCells);
|
||||
Mesher.LODOctaveDrop=World->LODOctaveDrop;
|
||||
@@ -490,6 +579,16 @@ private:
|
||||
}
|
||||
}
|
||||
Out.DensitySamples=static_cast<uint64>(FMath::Max<int64>(0,SampleCount));
|
||||
Out.MeshPayloadAllocatedBytes=static_cast<uint64>(Mesh.Vertices.GetAllocatedSize())
|
||||
+static_cast<uint64>(Mesh.Normals.GetAllocatedSize())
|
||||
+static_cast<uint64>(Mesh.UVs.GetAllocatedSize())
|
||||
+static_cast<uint64>(Mesh.Colors.GetAllocatedSize())
|
||||
+static_cast<uint64>(Mesh.Triangles.GetAllocatedSize());
|
||||
if(bHashDensity)
|
||||
{
|
||||
Out.PayloadHash=HashStandaloneMeshPayload(Mesh);Out.bHasPayloadHash=true;
|
||||
Out.TopologyHash=HashStandaloneMeshTopology(Mesh);Out.bHasTopologyHash=true;
|
||||
}
|
||||
Out.Vertices.reserve(static_cast<size_t>(Mesh.Vertices.Num()));
|
||||
for(int32 I=0;I<Mesh.Vertices.Num();++I)
|
||||
{
|
||||
@@ -534,6 +633,8 @@ private:
|
||||
bool bHashDensity=false;
|
||||
const FStandaloneMeshCache* MeshCache=nullptr;
|
||||
int32 MaxLevelFilter=-1;
|
||||
bool bSkipUvAndColors=false;
|
||||
bool bSkipMaterialGroups=false;
|
||||
std::atomic<uint64> CacheHitCount{0};
|
||||
mutable std::mutex Mutex;
|
||||
std::condition_variable Wake;
|
||||
|
||||
Reference in new issue
Block a user