wip(standalone): GPU step 6 -- near field on GPU, exact; cold start still slower than CPU
Near LOD0/1 (detail band, relief, root refinement, normals) now evaluated and meshed on the GPU. 960 near tiles: zero sign/topology differences vs CPU and WARP, vertex max 0.0049 mm (was 15 cm); far survival audit passes; CPU hashes 44/44 unchanged. Far connector-panel outlier (64.56 cm, LOD8) remains a tessellation difference, and remote captures show missing far geometry on GPU. Cold readiness (viewer clock, my interleaved runs): CPU 2.51/2.49/2.40 s, GPU 5.05/2.68/2.80 s. The ~2.1 s Present stall hit only the first GPU launch. Viewer spends ~1.13 s before shape compile starts (startup checks), which is the gap between the 0.95 s benchmark and ~2.4 s on-screen. 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
0e730cdbba
commit
b0a14262b1
50 files changed
+1681
-236
No files matched your search
@@ -204,10 +204,25 @@ namespace VoxelForgeCore
|
||||
};
|
||||
static_assert(sizeof(FarFieldSpatialBin) == 24);
|
||||
|
||||
// Layer-local detail parameters for the shared coherent near field. This record stays
|
||||
// float-only so the same values bind to an HLSL StructuredBuffer.
|
||||
struct FarFieldDetailLayerGpu
|
||||
{
|
||||
std::int32_t lo = 0, hi = -1;
|
||||
float surfaceRoughness = 0.0f, roughnessFrequency = 0.0f;
|
||||
float verticalScale = 1.0f, domainWarpStrength = 0.0f;
|
||||
float domainWarpFrequency = 0.0f;
|
||||
std::uint32_t roughnessNoiseType = 0;
|
||||
};
|
||||
static_assert(sizeof(FarFieldDetailLayerGpu) == 32);
|
||||
|
||||
struct FarFieldWorldInput
|
||||
{
|
||||
std::uint32_t spatialBinSize = 32, spatialBinMask = 0;
|
||||
std::uint32_t fieldSeed = 0;
|
||||
std::int32_t detailMinVoxel[3]{}, detailMaxVoxel[3]{};
|
||||
std::vector<FarFieldPrimitiveGpu> shapes;
|
||||
std::vector<FarFieldDetailLayerGpu> detailLayers;
|
||||
// Open-addressed table. count == 0 denotes an empty slot; mask is capacity - 1.
|
||||
std::vector<FarFieldSpatialBin> spatialBins;
|
||||
std::vector<std::uint32_t> spatialBinShapeIds;
|
||||
@@ -236,6 +251,7 @@ namespace VoxelForgeCore
|
||||
{
|
||||
Vec3i origin{};
|
||||
std::int32_t cells = 0, step = 0, sampleDim = 0;
|
||||
bool nearDetailEnabled = false;
|
||||
float strataLo = 0.0f, strataHi = 0.0f, strataBlend = 12.0f;
|
||||
std::shared_ptr<const FarFieldWorldInput> worldInput;
|
||||
// Sparse exception bitset: bit 1 forces route-protected air. Ordinary rock/air
|
||||
@@ -244,6 +260,11 @@ namespace VoxelForgeCore
|
||||
// Sparse exception bitset for exact solid overlays/thin route-adjacent features.
|
||||
std::vector<std::uint32_t> protectedSolid;
|
||||
std::vector<std::uint32_t> protectedThin;
|
||||
// Near tiles mirror the CPU's sparse interpolation proxy cache. UINT_MAX
|
||||
// means analytic-only; zero means the packed fixed fallback; low values
|
||||
// store |SurfaceDistanceQ|. For Q==1 only, bit 30 marks the ambiguous
|
||||
// packed-sign exception and bit 31 carries its CPU rock sign.
|
||||
std::vector<std::uint32_t> nearPackedProxy;
|
||||
std::vector<FarFieldStratumBoundary> stratumBoundaries;
|
||||
std::vector<FarFieldConnectorPanel> connectorPanels;
|
||||
std::uint32_t connectorRingCount = 0, connectorVertexCount = 0;
|
||||
@@ -343,6 +364,8 @@ namespace VoxelForgeCore
|
||||
std::vector<std::uint32_t> farConnectorCandidates(Vec3i minVoxel, Vec3i maxVoxel) const;
|
||||
std::optional<FarFieldTileInput> exportFarFieldTile(const TileKey& key,
|
||||
std::string& error) const;
|
||||
std::optional<FarFieldTileInput> exportGpuFieldTile(const TileKey& key,
|
||||
std::string& error) const;
|
||||
bool isFarFieldTileEligible(const TileKey& key) const;
|
||||
|
||||
bool auditDetailPassing() const;
|
||||
|
||||
@@ -526,9 +526,20 @@ public:
|
||||
auto Data=std::make_shared<VoxelForgeCore::FarFieldWorldInput>();
|
||||
Data->spatialBinSize=static_cast<uint32>(ActiveSpatialBinSize);
|
||||
Data->spatialBinMask=static_cast<uint32>(TableCapacity-1);
|
||||
Data->fieldSeed=static_cast<uint32>(Owner?Owner->Seed:0);
|
||||
for(int32 Axis=0;Axis<3;++Axis)
|
||||
{Data->detailMinVoxel[Axis]=Min[Axis];Data->detailMaxVoxel[Axis]=Max[Axis];}
|
||||
Data->shapes.reserve(SharedFieldPrimitives.size());
|
||||
for(const VF_FieldPrimitive& Shape:SharedFieldPrimitives)
|
||||
Data->shapes.push_back(ToFarFieldGpuPrimitive(Shape));
|
||||
Data->detailLayers.reserve(LayerFields.size());
|
||||
for(const FLayerField& Field:LayerFields)
|
||||
{
|
||||
const FStrateGenerationParams& Params=Field.Params;
|
||||
Data->detailLayers.push_back({Field.Lo,Field.Hi,Params.SurfaceRoughness,
|
||||
Params.RoughnessFrequency,Params.VerticalScale,Params.DomainWarpStrength,
|
||||
Params.DomainWarpFrequency,static_cast<uint32>(Params.RoughnessNoiseType)});
|
||||
}
|
||||
Data->spatialBins.resize(TableCapacity);
|
||||
size_t ReferenceCount=0;
|
||||
for(const auto& Bin:SpatialBins)ReferenceCount+=Bin.second.size();
|
||||
@@ -551,20 +562,22 @@ public:
|
||||
const double UploadMiB=(G.shapes.size()*sizeof(VoxelForgeCore::FarFieldPrimitiveGpu)
|
||||
+G.spatialBins.size()*sizeof(VoxelForgeCore::FarFieldSpatialBin)
|
||||
+G.spatialBinShapeIds.size()*sizeof(uint32))/1048576.0;
|
||||
std::printf("[FarGpuWorldData] shapes=%zu spatial_bins=%zu hash_slots=%zu bin_shape_ids=%zu bin_size=%d bytes=%.3fMiB upload_scope=once_per_world\n",
|
||||
std::printf("[FarGpuWorldData] shapes=%zu spatial_bins=%zu hash_slots=%zu bin_shape_ids=%zu bin_size=%d detail_min=(%d,%d,%d) detail_max=(%d,%d,%d) bytes=%.3fMiB upload_scope=once_per_world\n",
|
||||
G.shapes.size(),SpatialBins.size(),G.spatialBins.size(),G.spatialBinShapeIds.size(),
|
||||
ActiveSpatialBinSize,UploadMiB);
|
||||
ActiveSpatialBinSize,G.detailMinVoxel[0],G.detailMinVoxel[1],G.detailMinVoxel[2],
|
||||
G.detailMaxVoxel[0],G.detailMaxVoxel[1],G.detailMaxVoxel[2],UploadMiB);
|
||||
return CachedFarFieldGpuInput;
|
||||
}
|
||||
|
||||
bool ExportFarFieldTileInput(const FIntVector& Origin,int32 Cells,int32 Step,
|
||||
VoxelForgeCore::FarFieldTileInput& Out,std::string& Error) const
|
||||
bool bNearDetail,VoxelForgeCore::FarFieldTileInput& Out,std::string& Error) const
|
||||
{
|
||||
if(!FarBuilt||Cells<=0||Cells>32||Step<=0)
|
||||
{Error="far field export requires a built far world and a bounded coarse tile";return false;}
|
||||
const int32 Dim=Cells+3;
|
||||
const size_t SampleCount=static_cast<size_t>(Dim)*Dim*Dim;
|
||||
Out={};Error.clear();Out.origin={Origin.X,Origin.Y,Origin.Z};Out.cells=Cells;Out.step=Step;Out.sampleDim=Dim;
|
||||
Out.nearDetailEnabled=bNearDetail;
|
||||
Out.worldInput=GetFarFieldWorldInput(Error);
|
||||
if(!Out.worldInput)return false;
|
||||
if(LayerRanges.empty())
|
||||
@@ -596,6 +609,8 @@ public:
|
||||
Out.protectedAir.assign((SampleCount+31u)/32u,0u);
|
||||
Out.protectedSolid.assign((SampleCount+31u)/32u,0u);
|
||||
Out.protectedThin.assign((SampleCount+31u)/32u,0u);
|
||||
if(bNearDetail)
|
||||
Out.nearPackedProxy.assign(SampleCount,std::numeric_limits<uint32>::max());
|
||||
|
||||
const int32 LatticeMin[3]={Origin.X-Step,Origin.Y-Step,Origin.Z-Step};
|
||||
const int32 LatticeMax[3]={Origin.X+(Cells+1)*Step,Origin.Y+(Cells+1)*Step,
|
||||
@@ -723,7 +738,34 @@ public:
|
||||
for(int32 X=-1;X<=Cells+1;++X,++SampleIndex)
|
||||
{
|
||||
const int32 WX=Origin.X+X*Step,WY=Origin.Y+Y*Step,WZ=Origin.Z+Z*Step;
|
||||
if(WX>=Min[0]&&WX<=Max[0]&&WY>=Min[1]&&WY<=Max[1]&&WZ>=Min[2]&&WZ<=Max[2])
|
||||
if(bNearDetail&&!bAnalyticOnly&&Contains(WX,WY,WZ))
|
||||
{
|
||||
const size_t Column=static_cast<size_t>(WY-Min[1])*Size[0]
|
||||
+static_cast<size_t>(WX-Min[0]);
|
||||
const size_t Zi=static_cast<size_t>(WZ-Min[2]);
|
||||
const size_t Word=Column*WordsPerZ+(Zi>>6);
|
||||
const uint64 Mask=uint64{1}<<(Zi&63u);
|
||||
if((CandidateWords[Word]&Mask)==0)
|
||||
Out.nearPackedProxy[SampleIndex]=0u;
|
||||
else
|
||||
{
|
||||
const size_t Node=Zi*static_cast<size_t>(Size[1])*Size[0]+Column;
|
||||
const int32 Q=std::abs(static_cast<int32>(SurfaceDistanceQ[Node]));
|
||||
const uint32 Magnitude=static_cast<uint32>(std::clamp(Q,1,32767));
|
||||
if(Magnitude==1u)
|
||||
{
|
||||
// At the minimum quantization step a one-ULP GPU sign flip
|
||||
// can change an entire marching-cubes cell. Keep this sparse
|
||||
// ambiguous boundary decision aligned with the packed cache.
|
||||
const bool bRock=(RockWords[Word]&Mask)!=0;
|
||||
Out.nearPackedProxy[SampleIndex]=0x40000000u
|
||||
|(bRock?0x80000000u:0u)|Magnitude;
|
||||
}
|
||||
else Out.nearPackedProxy[SampleIndex]=Magnitude;
|
||||
}
|
||||
}
|
||||
if(!bNearDetail&&WX>=Min[0]&&WX<=Max[0]&&WY>=Min[1]&&WY<=Max[1]
|
||||
&&WZ>=Min[2]&&WZ<=Max[2])
|
||||
{Error="tile lattice intersects the near analytic/detail region; this export is far-field only";return false;}
|
||||
if(InsideCritical(WX,WY,WZ))
|
||||
{
|
||||
@@ -1746,6 +1788,7 @@ private:
|
||||
if(P.bCurvedFloorClip)Out.flags|=VF_FIELD_PRIMITIVE_CURVED_FLOOR;
|
||||
if(P.bCarve)Out.flags|=VF_FIELD_PRIMITIVE_CARVE;
|
||||
if(P.bCritical)Out.flags|=VF_FIELD_PRIMITIVE_CRITICAL;
|
||||
if(P.bPassageSurface)Out.flags|=VF_FIELD_PRIMITIVE_PASSAGE;
|
||||
for(int32 A=0;A<3;++A)
|
||||
{
|
||||
Out.minVoxel[A]=P.Min[A];Out.maxVoxel[A]=P.Max[A];
|
||||
@@ -4081,68 +4124,12 @@ private:
|
||||
float OwnerCaveRoughnessCore(double X,double Y,double Z,
|
||||
const FStrateGenerationParams& Params) const
|
||||
{
|
||||
const uint32 Seed=static_cast<uint32>(Owner->Seed);
|
||||
const float RF=Params.RoughnessFrequency;
|
||||
const float NoiseX=static_cast<float>(X);
|
||||
const float NoiseY=static_cast<float>(Y);
|
||||
const float EffectiveZ=(Params.VerticalScale>0.0f&&Params.VerticalScale!=1.0f)
|
||||
?static_cast<float>(Z)/Params.VerticalScale:static_cast<float>(Z);
|
||||
FVector3f Main(NoiseX*RF+VoxelHash::SeedOffset(Seed,11.3f),
|
||||
NoiseY*RF+VoxelHash::SeedOffset(Seed,13.7f),
|
||||
EffectiveZ*RF+VoxelHash::SeedOffset(Seed,17.1f));
|
||||
FVector3f Fine(NoiseX*RF*3.0f+VoxelHash::SeedOffset(Seed,19.1f)+2000.0f,
|
||||
NoiseY*RF*3.0f+VoxelHash::SeedOffset(Seed,23.7f)+2500.0f,
|
||||
EffectiveZ*RF*3.0f+VoxelHash::SeedOffset(Seed,29.3f)+3000.0f);
|
||||
if(Params.DomainWarpStrength>0.0f)
|
||||
{
|
||||
const float WF=Params.DomainWarpFrequency,WS=Params.DomainWarpStrength;
|
||||
float WX=0.0f,WY=0.0f,WZ=0.0f;
|
||||
VoxelNoise::Perlin3D_x3(
|
||||
NoiseX*WF+VoxelHash::SeedOffset(Seed,5.2f),
|
||||
NoiseY*WF+VoxelHash::SeedOffset(Seed,1.3f),
|
||||
EffectiveZ*WF+VoxelHash::SeedOffset(Seed,9.7f),
|
||||
NoiseX*WF+100.0f+VoxelHash::SeedOffset(Seed,7.7f),
|
||||
NoiseY*WF+200.0f+VoxelHash::SeedOffset(Seed,3.1f),
|
||||
EffectiveZ*WF+300.0f,
|
||||
NoiseX*WF+400.0f,
|
||||
NoiseY*WF+500.0f+VoxelHash::SeedOffset(Seed,11.9f),
|
||||
EffectiveZ*WF+600.0f+VoxelHash::SeedOffset(Seed,13.3f),WX,WY,WZ);
|
||||
WX*=VOXEL_NOISE_SCALE*WS;WY*=VOXEL_NOISE_SCALE*WS;WZ*=VOXEL_NOISE_SCALE*WS;
|
||||
Main+=FVector3f(WX,WY,WZ);Fine+=FVector3f(WX,WY,WZ);
|
||||
}
|
||||
float Rough=0.0f,FineValue=0.0f;
|
||||
switch(Params.RoughnessNoiseType)
|
||||
{
|
||||
case EVoxelNoiseType::Ridged:
|
||||
{
|
||||
float MainFBM=0.0f,MainRidged=0.0f,FineFBM=0.0f,FineRidged=0.0f;
|
||||
VoxelNoise::FractalPair<false,true>(Main.X,Main.Y,Main.Z,3,
|
||||
Fine.X,Fine.Y,Fine.Z,2,MainFBM,MainRidged,FineFBM,FineRidged);
|
||||
Rough=MainRidged;FineValue=FineRidged;
|
||||
break;
|
||||
}
|
||||
case EVoxelNoiseType::Mixed:
|
||||
{
|
||||
float MainFBM=0.0f,MainRidged=0.0f,FineFBM=0.0f,FineRidged=0.0f;
|
||||
VoxelNoise::FractalPair<true,true>(Main.X,Main.Y,Main.Z,3,
|
||||
Fine.X,Fine.Y,Fine.Z,2,MainFBM,MainRidged,FineFBM,FineRidged);
|
||||
Rough=MainFBM*0.5f+MainRidged*0.5f;
|
||||
FineValue=FineFBM*0.5f+FineRidged*0.5f;
|
||||
break;
|
||||
}
|
||||
case EVoxelNoiseType::Cellular:
|
||||
Rough=VoxelNoise::Cellular3D(Main);FineValue=VoxelNoise::Cellular3D(Fine);break;
|
||||
case EVoxelNoiseType::FBM:
|
||||
default:
|
||||
{
|
||||
float MainFBM=0.0f,MainRidged=0.0f,FineFBM=0.0f,FineRidged=0.0f;
|
||||
VoxelNoise::FractalPair<true,false>(Main.X,Main.Y,Main.Z,3,
|
||||
Fine.X,Fine.Y,Fine.Z,2,MainFBM,MainRidged,FineFBM,FineRidged);
|
||||
Rough=MainFBM;FineValue=FineFBM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (Rough+FineValue*0.4f)*VOXEL_NOISE_SCALE*Params.SurfaceRoughness;
|
||||
// Keep CPU prediction and the near-field HLSL on one scalar source. The helper
|
||||
// preserves the legacy seed offsets, octave order, and roughness families.
|
||||
return VF_FieldOwnerRoughnessCore(static_cast<float>(X),static_cast<float>(Y),
|
||||
static_cast<float>(Z),static_cast<uint32>(Owner->Seed),Params.SurfaceRoughness,
|
||||
Params.RoughnessFrequency,Params.VerticalScale,Params.DomainWarpStrength,
|
||||
Params.DomainWarpFrequency,static_cast<uint32>(Params.RoughnessNoiseType));
|
||||
}
|
||||
|
||||
float OwnerCaveRoughness(double X,double Y,double Z,float SurfaceSDF,
|
||||
|
||||
@@ -746,11 +746,30 @@ namespace VoxelForgeCore
|
||||
const int32 cells=std::max(1,impl_->owner->CoarseTileCells);
|
||||
const int32 step=std::max(1,extent/cells);
|
||||
FarFieldTileInput result;
|
||||
if(!impl_->shape->ExportFarFieldTileInput(ToNative(key).OriginVoxels(),cells,step,result,error))
|
||||
if(!impl_->shape->ExportFarFieldTileInput(ToNative(key).OriginVoxels(),cells,step,false,result,error))
|
||||
return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<FarFieldTileInput> World::exportGpuFieldTile(const TileKey& key,
|
||||
std::string& error) const
|
||||
{
|
||||
if(!impl_||!impl_->owner||!impl_->shape||!impl_->shape->IsBuilt())
|
||||
{error="world must be compiled before exporting a GPU field tile";return std::nullopt;}
|
||||
if(key.lod<0||key.lod>8)
|
||||
{error="GPU field export accepts LOD0 through LOD8";return std::nullopt;}
|
||||
if(!impl_->shape->HasFarWorld())
|
||||
{error="world must include the far parent/connector shapes before export";return std::nullopt;}
|
||||
const int32 extent=CHUNK_SIZE<<key.lod;
|
||||
const bool nearDetail=key.lod<impl_->configuration.fullResolutionLodCount;
|
||||
const int32 cells=nearDetail?CHUNK_SIZE:std::max(1,impl_->owner->CoarseTileCells);
|
||||
const int32 step=std::max(1,extent/cells);
|
||||
FarFieldTileInput result;
|
||||
if(!impl_->shape->ExportFarFieldTileInput(ToNative(key).OriginVoxels(),cells,step,
|
||||
nearDetail,result,error))return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool World::isFarFieldTileEligible(const TileKey& key) const
|
||||
{
|
||||
if(!impl_||!impl_->owner||!impl_->shape||!impl_->shape->IsBuilt()
|
||||
|
||||
@@ -68,6 +68,7 @@ static const VF_FieldI32 VF_FIELD_PRIMITIVE_HAS_FLOOR = 8;
|
||||
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CURVED_FLOOR = 16;
|
||||
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CARVE = 32;
|
||||
static const VF_FieldI32 VF_FIELD_PRIMITIVE_CRITICAL = 64;
|
||||
static const VF_FieldI32 VF_FIELD_PRIMITIVE_PASSAGE = 128;
|
||||
|
||||
VF_FIELD_INLINE VF_FieldReal VF_FieldEvaluateFloorProfile(VF_FieldReal a, VF_FieldReal b,
|
||||
VF_FieldReal tangentA, VF_FieldReal tangentB, VF_FieldReal length,
|
||||
@@ -249,6 +250,193 @@ VF_FIELD_INLINE float VF_FieldSurfaceRelief(float x, float y, float z, VF_FieldU
|
||||
return 1.20f * macro + (0.80f + 0.25f * macro) * (0.40f * mid + 0.60f * fine);
|
||||
}
|
||||
|
||||
// Scalar version of VoxelNoise::Perlin3D and its owner-world roughness variant.
|
||||
// The integer hash and operation order are shared so the standalone field and HLSL
|
||||
// evaluate the same authored detail from the same world parameters.
|
||||
VF_FIELD_INLINE VF_FieldU32 VF_FieldVoxelHashMix(VF_FieldU32 x)
|
||||
{
|
||||
x ^= x >> 16; x *= 0x45d9f3bu;
|
||||
x ^= x >> 16; x *= 0x45d9f3bu;
|
||||
x ^= x >> 16;
|
||||
return x;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldVoxelSeedOffset(VF_FieldU32 seed,float siteKey)
|
||||
{
|
||||
const VF_FieldU32 site=VF_FIELD_CAST_U32(siteKey*100.0f+0.5f);
|
||||
return VF_FIELD_CAST_F32(VF_FieldVoxelHashMix(seed^(site*2654435761u))&0x3FFFu);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE VF_FieldU32 VF_FieldPerlinHash(VF_FieldI32 x,VF_FieldI32 y,VF_FieldI32 z)
|
||||
{
|
||||
VF_FieldU32 h=VF_FIELD_CAST_U32(x)*0x9E3779B1u
|
||||
^VF_FIELD_CAST_U32(y)*0x85EBCA77u
|
||||
^VF_FIELD_CAST_U32(z)*0xC2B2AE3Du;
|
||||
h^=h>>15;h*=0x2C1B3C6Du;
|
||||
h^=h>>12;h*=0x297A2D39u;
|
||||
h^=h>>15;
|
||||
return h;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldPerlinFade(float t)
|
||||
{
|
||||
const float inner=t*(t*6.0f-15.0f)+10.0f;
|
||||
const float t3=t*t*t;
|
||||
return t3*inner;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldPerlinGrad(VF_FieldU32 hash,float x,float y,float z)
|
||||
{
|
||||
const VF_FieldU32 h=hash&15u;
|
||||
const float u=(h&8u)==0u?x:y;
|
||||
float v;
|
||||
if((h&12u)==0u)v=y;
|
||||
else if((h&13u)==12u)v=x;
|
||||
else v=z;
|
||||
const float ru=(h&1u)==0u?u:-u;
|
||||
const float rv=(h&2u)==0u?v:-v;
|
||||
return ru+rv;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldPerlinLerp(float a,float b,float t)
|
||||
{ return a+t*(b-a); }
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldPerlin3D(float x,float y,float z)
|
||||
{
|
||||
const float xf=VF_FIELD_FLOOR(x),yf=VF_FIELD_FLOOR(y),zf=VF_FIELD_FLOOR(z);
|
||||
const VF_FieldI32 X=VF_FIELD_CAST_I32(xf),Y=VF_FIELD_CAST_I32(yf),Z=VF_FIELD_CAST_I32(zf);
|
||||
const float fx=x-xf,fy=y-yf,fz=z-zf;
|
||||
const float su=VF_FieldPerlinFade(fx),sv=VF_FieldPerlinFade(fy),sw=VF_FieldPerlinFade(fz);
|
||||
const VF_FieldU32 h000=VF_FieldPerlinHash(X,Y,Z),h100=VF_FieldPerlinHash(X+1,Y,Z);
|
||||
const VF_FieldU32 h010=VF_FieldPerlinHash(X,Y+1,Z),h110=VF_FieldPerlinHash(X+1,Y+1,Z);
|
||||
const VF_FieldU32 h001=VF_FieldPerlinHash(X,Y,Z+1),h101=VF_FieldPerlinHash(X+1,Y,Z+1);
|
||||
const VF_FieldU32 h011=VF_FieldPerlinHash(X,Y+1,Z+1),h111=VF_FieldPerlinHash(X+1,Y+1,Z+1);
|
||||
const float fx1=fx-1.0f,fy1=fy-1.0f,fz1=fz-1.0f;
|
||||
const float n000=VF_FieldPerlinGrad(h000,fx,fy,fz);
|
||||
const float n100=VF_FieldPerlinGrad(h100,fx1,fy,fz);
|
||||
const float n010=VF_FieldPerlinGrad(h010,fx,fy1,fz);
|
||||
const float n110=VF_FieldPerlinGrad(h110,fx1,fy1,fz);
|
||||
const float n001=VF_FieldPerlinGrad(h001,fx,fy,fz1);
|
||||
const float n101=VF_FieldPerlinGrad(h101,fx1,fy,fz1);
|
||||
const float n011=VF_FieldPerlinGrad(h011,fx,fy1,fz1);
|
||||
const float n111=VF_FieldPerlinGrad(h111,fx1,fy1,fz1);
|
||||
const float x00=VF_FieldPerlinLerp(n000,n100,su);
|
||||
const float x10=VF_FieldPerlinLerp(n010,n110,su);
|
||||
const float x01=VF_FieldPerlinLerp(n001,n101,su);
|
||||
const float x11=VF_FieldPerlinLerp(n011,n111,su);
|
||||
const float y0=VF_FieldPerlinLerp(x00,x10,sv);
|
||||
const float y1=VF_FieldPerlinLerp(x01,x11,sv);
|
||||
return VF_FieldPerlinLerp(y0,y1,sw);
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldOwnerFractal(float x,float y,float z,VF_FieldI32 octaves,
|
||||
VF_FieldI32 mode)
|
||||
{
|
||||
float total=0.0f,maxValue=0.0f,frequency=1.0f,amplitude=1.0f,weight=1.0f;
|
||||
for(VF_FieldI32 i=0;i<octaves;++i)
|
||||
{
|
||||
const float noise=VF_FieldPerlin3D(x*frequency,y*frequency,z*frequency);
|
||||
if(mode==0)total+=noise*amplitude;
|
||||
else
|
||||
{
|
||||
float ridge=noise*1.25f;
|
||||
ridge=1.0f-VF_FIELD_ABS(ridge);
|
||||
ridge=ridge*ridge;
|
||||
ridge*=weight;
|
||||
weight=VF_FIELD_CLAMP(ridge*2.0f,0.0f,1.0f);
|
||||
total+=ridge*amplitude;
|
||||
}
|
||||
maxValue+=amplitude;
|
||||
amplitude*=0.5f;
|
||||
frequency*=2.0f;
|
||||
}
|
||||
const float result=total/maxValue;
|
||||
return mode==0?result:result*2.0f-1.0f;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldOwnerCellular(float x,float y,float z)
|
||||
{
|
||||
const VF_FieldI32 cellX=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(x));
|
||||
const VF_FieldI32 cellY=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(y));
|
||||
const VF_FieldI32 cellZ=VF_FIELD_CAST_I32(VF_FIELD_FLOOR(z));
|
||||
const float fracX=x-VF_FIELD_CAST_F32(cellX),fracY=y-VF_FIELD_CAST_F32(cellY),
|
||||
fracZ=z-VF_FIELD_CAST_F32(cellZ);
|
||||
float first=3.402823466e+38f,second=3.402823466e+38f;
|
||||
for(VF_FieldI32 dz=-1;dz<=1;++dz)
|
||||
for(VF_FieldI32 dy=-1;dy<=1;++dy)
|
||||
for(VF_FieldI32 dx=-1;dx<=1;++dx)
|
||||
{
|
||||
const VF_FieldI32 nx=cellX+dx,ny=cellY+dy,nz=cellZ+dz;
|
||||
const VF_FieldU32 h=VF_FieldVoxelHashMix(
|
||||
VF_FIELD_CAST_U32(nx+0x7FFFFFFF)
|
||||
^VF_FieldVoxelHashMix(VF_FIELD_CAST_U32(ny+0x7FFFFFFF)*2654435761u)
|
||||
^VF_FieldVoxelHashMix(VF_FIELD_CAST_U32(nz+0x7FFFFFFF)*374761393u));
|
||||
const float fpx=VF_FIELD_CAST_F32(dx)
|
||||
+VF_FIELD_CAST_F32(h&0xFFFFFFu)*(1.0f/16777216.0f)-fracX;
|
||||
const VF_FieldU32 hy=VF_FieldVoxelHashMix(h^0x12345678u);
|
||||
const VF_FieldU32 hz=VF_FieldVoxelHashMix(h^0x9ABCDEF0u);
|
||||
const float fpy=VF_FIELD_CAST_F32(dy)
|
||||
+VF_FIELD_CAST_F32(hy&0xFFFFFFu)*(1.0f/16777216.0f)-fracY;
|
||||
const float fpz=VF_FIELD_CAST_F32(dz)
|
||||
+VF_FIELD_CAST_F32(hz&0xFFFFFFu)*(1.0f/16777216.0f)-fracZ;
|
||||
const float distanceSquared=fpx*fpx+fpy*fpy+fpz*fpz;
|
||||
if(distanceSquared<first){second=first;first=distanceSquared;}
|
||||
else if(distanceSquared<second)second=distanceSquared;
|
||||
}
|
||||
return (VF_FIELD_SQRT(second)-VF_FIELD_SQRT(first))*2.0f-1.0f;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldOwnerRoughnessCore(float x,float y,float z,
|
||||
VF_FieldU32 seed,float surfaceRoughness,float roughnessFrequency,float verticalScale,
|
||||
float domainWarpStrength,float domainWarpFrequency,VF_FieldU32 noiseType)
|
||||
{
|
||||
if(!(roughnessFrequency>0.0f))return 0.0f;
|
||||
const float effectiveZ=verticalScale>0.0f&&verticalScale!=1.0f?z/verticalScale:z;
|
||||
const float mainX=x*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,11.3f);
|
||||
const float mainY=y*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,13.7f);
|
||||
const float mainZ=effectiveZ*roughnessFrequency+VF_FieldVoxelSeedOffset(seed,17.1f);
|
||||
float fineX=x*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,19.1f)+2000.0f;
|
||||
float fineY=y*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,23.7f)+2500.0f;
|
||||
float fineZ=effectiveZ*roughnessFrequency*3.0f+VF_FieldVoxelSeedOffset(seed,29.3f)+3000.0f;
|
||||
float mx=mainX,my=mainY,mz=mainZ;
|
||||
if(domainWarpStrength>0.0f)
|
||||
{
|
||||
const float wf=domainWarpFrequency,ws=domainWarpStrength;
|
||||
float wx=VF_FieldPerlin3D(x*wf+VF_FieldVoxelSeedOffset(seed,5.2f),
|
||||
y*wf+VF_FieldVoxelSeedOffset(seed,1.3f),
|
||||
effectiveZ*wf+VF_FieldVoxelSeedOffset(seed,9.7f));
|
||||
float wy=VF_FieldPerlin3D(x*wf+100.0f+VF_FieldVoxelSeedOffset(seed,7.7f),
|
||||
y*wf+200.0f+VF_FieldVoxelSeedOffset(seed,3.1f),effectiveZ*wf+300.0f);
|
||||
float wz=VF_FieldPerlin3D(x*wf+400.0f,y*wf+500.0f+VF_FieldVoxelSeedOffset(seed,11.9f),
|
||||
effectiveZ*wf+600.0f+VF_FieldVoxelSeedOffset(seed,13.3f));
|
||||
wx*=1.25f*ws;wy*=1.25f*ws;wz*=1.25f*ws;
|
||||
mx+=wx;my+=wy;mz+=wz;
|
||||
fineX+=wx;fineY+=wy;fineZ+=wz;
|
||||
}
|
||||
float rough=0.0f,fine=0.0f;
|
||||
if(noiseType==1u)
|
||||
{
|
||||
rough=VF_FieldOwnerFractal(mx,my,mz,3,1);
|
||||
fine=VF_FieldOwnerFractal(fineX,fineY,fineZ,2,1);
|
||||
}
|
||||
else if(noiseType==2u)
|
||||
{
|
||||
rough=(VF_FieldOwnerFractal(mx,my,mz,3,0)+VF_FieldOwnerFractal(mx,my,mz,3,1))*0.5f;
|
||||
fine=(VF_FieldOwnerFractal(fineX,fineY,fineZ,2,0)+VF_FieldOwnerFractal(fineX,fineY,fineZ,2,1))*0.5f;
|
||||
}
|
||||
else if(noiseType==3u)
|
||||
{
|
||||
rough=VF_FieldOwnerCellular(mx,my,mz);
|
||||
fine=VF_FieldOwnerCellular(fineX,fineY,fineZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
rough=VF_FieldOwnerFractal(mx,my,mz,3,0);
|
||||
fine=VF_FieldOwnerFractal(fineX,fineY,fineZ,2,0);
|
||||
}
|
||||
return (rough+fine*0.4f)*1.25f*surfaceRoughness;
|
||||
}
|
||||
|
||||
VF_FIELD_INLINE float VF_FieldSmoothUnit(float value)
|
||||
{
|
||||
#if defined(VF_FIELD_HLSL)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$HostProcess = Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass = 'BelowNormal'
|
||||
$HostProcess.ProcessorAffinity = [IntPtr]0xFF00
|
||||
$Standalone = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
|
||||
$Executable = Join-Path $Standalone 'bin\VoxelForgeGpuFieldStep6NearProbe.exe'
|
||||
$Stdout = Join-Path $PSScriptRoot 'step6_cpu_hash_fresh_w8.stdout.log'
|
||||
$Stderr = Join-Path $PSScriptRoot 'step6_cpu_hash_fresh_w8.stderr.log'
|
||||
$Arguments = @('--stream-benchmark','--shape-world','--force-generate','--stream-hash','--workers=8','--clip-radius=3')
|
||||
if (-not (Test-Path -LiteralPath $Executable -PathType Leaf)) { throw "Executable not found: $Executable" }
|
||||
|
||||
for ($Attempt = 0; $Attempt -lt 2; ++$Attempt) {
|
||||
if ($Attempt -eq 1) {
|
||||
Write-Output '0xC0000142 encountered; waiting 60 seconds before the permitted retry.'
|
||||
Start-Sleep -Seconds 60
|
||||
}
|
||||
$Child = Start-Process -FilePath $Executable -ArgumentList $Arguments -WorkingDirectory $Standalone `
|
||||
-WindowStyle Hidden -PassThru -RedirectStandardOutput $Stdout -RedirectStandardError $Stderr
|
||||
$PeakWorkingSet = 0L
|
||||
$PeakPrivate = 0L
|
||||
$OverLimit = $false
|
||||
while (-not $Child.HasExited) {
|
||||
$Child.Refresh()
|
||||
if ($Child.HasExited) { break }
|
||||
$Child.PriorityClass = 'BelowNormal'
|
||||
$Child.ProcessorAffinity = [IntPtr]0xFF00
|
||||
$PeakWorkingSet = [Math]::Max($PeakWorkingSet,$Child.WorkingSet64)
|
||||
$PeakPrivate = [Math]::Max($PeakPrivate,$Child.PrivateMemorySize64)
|
||||
if ($Child.PrivateMemorySize64 -ge 8GB) {
|
||||
$OverLimit = $true
|
||||
$Child.Kill()
|
||||
$Child.WaitForExit()
|
||||
break
|
||||
}
|
||||
Start-Sleep -Milliseconds 100
|
||||
}
|
||||
$Child.WaitForExit()
|
||||
$Child.Refresh()
|
||||
if ($Child.ExitCode -eq -1073741502 -and $Attempt -eq 0) { continue }
|
||||
if ($Child.ExitCode -eq -1073741502) { throw '0xC0000142 repeated after the one retry; stopping.' }
|
||||
if ($OverLimit) { throw 'CPU hash run stopped at the 8 GB process memory limit.' }
|
||||
$Log = Get-Content -LiteralPath $Stdout -Raw
|
||||
if ($Child.ExitCode -ne 0 -or -not $Log.Contains('[StreamFullClipmap]')) {
|
||||
throw "Hash run failed; exit=$($Child.ExitCode) stdout=$Stdout stderr=$Stderr"
|
||||
}
|
||||
Write-Output "RUN_EXIT pid=$($Child.Id) priority=BelowNormal affinity=0xFF00 peak_working_set_gb=$([Math]::Round($PeakWorkingSet/1GB,2)) peak_private_gb=$([Math]::Round($PeakPrivate/1GB,2)) exit_code=$($Child.ExitCode) stdout=$Stdout stderr=$Stderr"
|
||||
exit 0
|
||||
}
|
||||
Reference in new issue
Block a user