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:
Fr0zkaandClaude Opus 5.5 committed 2026-10-06 20:03:10 +02:00
1 parent 0e730cdbba
commit b0a14262b1
50 files changed
+1681 -236

No files matched your search

@@ -27,9 +27,32 @@ cbuffer TileConstants : register(b0)
float VoxelSizeCm;
uint OutputBaseVertex;
uint OutputArgsByteOffset;
uint ConstantPad;
uint NearFieldMode;
uint OutputVertexCapacity;
uint3 TileConstantsPadding;
};
cbuffer FarFieldConstants : register(b1)
{
float FarStrataLo;
float FarStrataHi;
float FarStrataBlend;
uint FarSampleCount;
uint FarEvalCounterEnabled;
uint FarSpatialBinMask;
uint FarSpatialBinSize;
uint FarSpatialBinCapacity;
uint FarNearDetailEnabled;
uint FarFieldSeed;
uint FarDetailLayerCount;
uint FarFieldConstantPad;
int4 FarDetailMinVoxel;
int4 FarDetailMaxVoxel;
};
float NearFieldDensityAt(float3 position, uint clampNearZero,out uint distanceEvaluations);
StructuredBuffer<float> FarNearNormalDensityInput : register(t18);
cbuffer FarConnectorConstants : register(b2)
{
uint FarConnectorCount;
@@ -73,58 +96,74 @@ float LatticeValue(StructuredBuffer<float> density, int3 p)
return density[SampleIndex(p)];
}
float GradientLatticeValue(StructuredBuffer<float> density,int3 p)
{
const float cached=LatticeValue(density,p);
if(NearFieldMode!=0&&StepVoxels==1&&abs(abs(cached)-4.125f)<1.0e-4f)
{
// The CPU mesher replaces the fixed proxy-band fallback with the
// unclamped analytic field when it samples gradients. Match that
// normal path while retaining packed lattice values for classification
// and edge positions.
return FarNearNormalDensityInput[SampleIndex(p)];
}
return cached;
}
float3 LatticeGradient(StructuredBuffer<float> density, int3 p)
{
return float3(
LatticeValue(density, p + int3(1,0,0)) - LatticeValue(density, p - int3(1,0,0)),
LatticeValue(density, p + int3(0,1,0)) - LatticeValue(density, p - int3(0,1,0)),
LatticeValue(density, p + int3(0,0,1)) - LatticeValue(density, p - int3(0,0,1)));
GradientLatticeValue(density, p + int3(1,0,0)) - GradientLatticeValue(density, p - int3(1,0,0)),
GradientLatticeValue(density, p + int3(0,1,0)) - GradientLatticeValue(density, p - int3(0,1,0)),
GradientLatticeValue(density, p + int3(0,0,1)) - GradientLatticeValue(density, p - int3(0,0,1)));
}
float3 EdgePosition(int3 cell, uint edge, StructuredBuffer<float> density,
StructuredBuffer<uint> midpointIds,
StructuredBuffer<MidpointRecord> midpointRecords)
float EdgeRootParameter(int3 cell,uint edge,StructuredBuffer<float> density,
StructuredBuffer<uint> midpointIds,StructuredBuffer<MidpointRecord> midpointRecords)
{
const int2 corners=EdgeCorners[edge];
const int3 pa=cell+CornerOffset[corners.x];
const int3 pb=cell+CornerOffset[corners.y];
const float da=LatticeValue(density,pa),db=LatticeValue(density,pb);
const uint midId=midpointIds[CellIndex(uint3(cell))];
float mid=0.0f;uint hasMid=0;
if(midId!=0xffffffffu)
{
const MidpointRecord record=midpointRecords[midId];
hasMid=(record.metadata.x>>edge)&1;
mid=record.values[edge>>2][edge&3];
}
else if(NearFieldMode!=0&&StepVoxels==1)
{
const float linearT=VF_FieldRefineRoot(da,db,0.0f,0);
if(linearT>=0.375f&&linearT<=0.625f)
{
const float3 midpoint=(float3(OriginX,OriginY,OriginZ)
+float3((pa+pb)*StepVoxels)*0.5f);
uint ignoredEvaluations=0;
mid=NearFieldDensityAt(midpoint,0,ignoredEvaluations);
hasMid=1;
}
}
return VF_FieldRefineRoot(da,db,mid,hasMid);
}
float3 EdgePosition(int3 cell,uint edge,float t)
{
const int2 corners = EdgeCorners[edge];
const int3 pa = cell + CornerOffset[corners.x];
const int3 pb = cell + CornerOffset[corners.y];
const float da = LatticeValue(density, pa);
const float db = LatticeValue(density, pb);
const uint midId = midpointIds[CellIndex(uint3(cell))];
float mid = 0.0f;
uint hasMid = 0;
if (midId != 0xffffffffu)
{
const MidpointRecord record = midpointRecords[midId];
hasMid = (record.metadata.x >> edge) & 1;
mid = record.values[edge >> 2][edge & 3];
}
const float t = VF_FieldRefineRoot(da, db, mid, hasMid);
const float3 originCm = float3(OriginX, OriginY, OriginZ) * VoxelSizeCm;
const float3 a = originCm + float3(pa * StepVoxels) * VoxelSizeCm;
const float3 b = originCm + float3(pb * StepVoxels) * VoxelSizeCm;
return lerp(a, b, t);
}
float3 EdgeNormal(int3 cell, uint edge, StructuredBuffer<float> density,
StructuredBuffer<uint> midpointIds,
StructuredBuffer<MidpointRecord> midpointRecords)
float3 EdgeNormal(int3 cell,uint edge,float t,StructuredBuffer<float> density)
{
const int2 corners = EdgeCorners[edge];
const int3 pa = cell + CornerOffset[corners.x];
const int3 pb = cell + CornerOffset[corners.y];
const float da = LatticeValue(density, pa);
const float db = LatticeValue(density, pb);
const uint midId = midpointIds[CellIndex(uint3(cell))];
float mid = 0.0f;
uint hasMid = 0;
if (midId != 0xffffffffu)
{
const MidpointRecord record = midpointRecords[midId];
hasMid = (record.metadata.x >> edge) & 1;
mid = record.values[edge >> 2][edge & 3];
}
const float t = VF_FieldRefineRoot(da, db, mid, hasMid);
const float3 ga = LatticeGradient(density, pa);
const float3 gb = LatticeGradient(density, pb);
const float3 g = lerp(ga, gb, t);
@@ -145,6 +184,17 @@ struct FarFieldSpatialBin
uint count;
uint padding;
};
struct FarFieldDetailLayer
{
int lo;
int hi;
float surfaceRoughness;
float roughnessFrequency;
float verticalScale;
float domainWarpStrength;
float domainWarpFrequency;
uint roughnessNoiseType;
};
struct FarFieldConnectorPanel
{
float3 start; float radius;
@@ -171,6 +221,9 @@ StructuredBuffer<uint> FarProtectedAir : register(t13);
StructuredBuffer<uint> FarProtectedSolid : register(t14);
RWStructuredBuffer<float> FarFieldDensity : register(u7);
StructuredBuffer<float> GpuSignDensity : register(t15);
StructuredBuffer<FarFieldDetailLayer> FarDetailLayers : register(t16);
StructuredBuffer<uint> FarNearPackedProxy : register(t17);
RWStructuredBuffer<float> FarNearNormalDensityOutput : register(u5);
StructuredBuffer<FarFieldConnectorPanel> FarConnectorPanels : register(t8);
StructuredBuffer<FarConnectorRing> FarConnectorRingInput : register(t9);
RWStructuredBuffer<FarConnectorRing> FarConnectorRingOutput : register(u7);
@@ -178,18 +231,6 @@ RWByteAddressBuffer FarConnectorDrawArgs : register(u4);
RWByteAddressBuffer FarConnectorVertexBytes : register(u5);
RWByteAddressBuffer FarConnectorIndexBytes : register(u6);
cbuffer FarFieldConstants : register(b1)
{
float FarStrataLo;
float FarStrataHi;
float FarStrataBlend;
uint FarSampleCount;
uint FarEvalCounterEnabled;
uint FarSpatialBinMask;
uint FarSpatialBinSize;
uint FarSpatialBinCapacity;
};
RWStructuredBuffer<uint> FarFieldEvalCounters : register(u6);
uint FarFieldBinHash(int3 coord)
@@ -223,6 +264,169 @@ FarFieldSpatialBin FarFieldFindBin(float3 position)
return emptyBin;
}
float NearOwnerRoughnessAtStep(float3 position,float surfaceSdf,float caveInfluence,
FarFieldDetailLayer layer,uint anchorStep)
{
const float depth=layer.surfaceRoughness*2.0f;
if(!(depth>0.0f)||abs(surfaceSdf)>=depth||!(layer.roughnessFrequency>0.0f))return 0.0f;
const int step=max(1,(int)anchorStep);
const int gridZ=(int)floor(position.z/(float)step)*step;
const float sampleZ0=max((float)gridZ,(float)layer.lo);
const float sampleZ1=min((float)(gridZ+step),(float)layer.hi);
float total;
if(sampleZ1>sampleZ0)
{
const float lower=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ0,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
const float upper=VF_FieldOwnerRoughnessCore(position.x,position.y,sampleZ1,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
const float alpha=(position.z-sampleZ0)/(sampleZ1-sampleZ0);
total=lower+(upper-lower)*alpha;
}
else
total=VF_FieldOwnerRoughnessCore(position.x,position.y,position.z,
FarFieldSeed,layer.surfaceRoughness,layer.roughnessFrequency,layer.verticalScale,
layer.domainWarpStrength,layer.domainWarpFrequency,layer.roughnessNoiseType);
if(surfaceSdf<0.0f)
total=lerp(total,min(total,0.0f),clamp(caveInfluence,0.0f,1.0f));
const float fade=1.0f-abs(surfaceSdf)/depth;
return total*fade*fade;
}
float NearOwnerRoughness(float3 position,float surfaceSdf,float caveInfluence,
float passageInfluence,FarFieldDetailLayer layer)
{
const float passage=saturate(passageInfluence);
const float anchored=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,8);
if(passage<=0.40f)return anchored;
const float direct=NearOwnerRoughnessAtStep(position,surfaceSdf,caveInfluence,layer,2);
if(passage>=0.60f)return direct;
const float directWeight=VF_FieldSmoothUnit((passage-0.40f)/0.20f);
const float anchoredWeight=1.0f-directWeight;
const float normalization=1.0f/sqrt(anchoredWeight*anchoredWeight+directWeight*directWeight);
return (anchoredWeight*anchored+directWeight*direct)*normalization;
}
float NearFieldDensityAt(float3 position,uint clampNearZero,out uint distanceEvaluations)
{
const FarFieldSpatialBin range=FarFieldFindBin(position);
float cave=100000.0f,overlay=-100000.0f;
float caveFloorInfluence=0.0f,cavePassageInfluence=0.0f;
distanceEvaluations=0;
[loop]
for(uint item=0;item<range.count;++item)
{
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
{
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
++distanceEvaluations;
float primitiveFloorInfluence=0.0f;
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
{
const float floor=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
const float floorT=(abs(position.z-floor)-shape.floorBlendRadius)/12.0f;
primitiveFloorInfluence=1.0f-VF_FieldSmoothUnit(floorT);
distance=VF_FieldSmoothMax(distance,floor-position.z,shape.floorBlendRadius);
}
const float metadataWidth=max(shape.blendRadius,6.0f);
const float primitiveWeight=VF_FieldSmoothUnit(0.5f+(cave-distance)/(2.0f*metadataWidth));
caveFloorInfluence=lerp(caveFloorInfluence,primitiveFloorInfluence,primitiveWeight);
if(cavePassageInfluence>0.0f||(shape.flags&VF_FIELD_PRIMITIVE_PASSAGE)!=0)
cavePassageInfluence=lerp(cavePassageInfluence,
(shape.flags&VF_FIELD_PRIMITIVE_PASSAGE)!=0?1.0f:0.0f,primitiveWeight);
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
}
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
{
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
++distanceEvaluations;
}
}
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
const float carveOrOverlay=max(cave,overlay);
const float caveVsOverlay=VF_FieldSmoothUnit(0.5f+(cave-overlay)/12.0f);
const float carveInfluence=VF_FieldSmoothUnit(0.5f+(base-carveOrOverlay)/12.0f);
const float caveInfluence=carveInfluence*caveVsOverlay;
const float floorInfluence=caveInfluence*caveFloorInfluence;
const float passageInfluence=caveInfluence*cavePassageInfluence;
const float surfaceSdf=base<=carveOrOverlay?base:(cave>=overlay?cave:overlay);
float rockDistance=VF_FieldSmoothMin(base,carveOrOverlay,FarStrataBlend);
if(NearFieldMode!=0
&&all(position>=float3(FarDetailMinVoxel.xyz))
&&all(position<=float3(FarDetailMaxVoxel.xyz))
&&abs(rockDistance)<=4.125f)
{
const float macro=VF_FieldValueNoise(position.x,position.y,position.z,32,
FarFieldSeed^0xB5297A4Du);
const float mid=VF_FieldValueNoise(position.x,position.y,position.z,16,
FarFieldSeed^0x63D83595u);
const float fine=VF_FieldValueNoise(position.x,position.y,position.z,4,
FarFieldSeed^0xA511E9B3u);
const float relief=1.20f*macro+(0.80f+0.25f*macro)*(0.40f*mid+0.60f*fine);
const float passageBandRelief=1.20f*macro+(0.80f+0.25f*macro)*(0.40f*mid);
float ownerRelief=0.0f;
[loop]
for(uint layerIndex=0;layerIndex<FarDetailLayerCount;++layerIndex)
{
const FarFieldDetailLayer layer=FarDetailLayers[layerIndex];
if(position.z>=layer.lo&&position.z<=layer.hi)
{ ownerRelief=NearOwnerRoughness(position,surfaceSdf,caveInfluence,
passageInfluence,layer);break; }
}
const float caveFactor=saturate(caveInfluence);
const float floorFactor=min(caveFactor,saturate(floorInfluence));
const float passageFactor=saturate(passageInfluence);
const float nonPassage=1.0f-passageFactor;
const float strataWeight=nonPassage*(1.0f-caveFactor);
const float caveWeight=nonPassage*(caveFactor-floorFactor);
const float floorWeight=nonPassage*floorFactor;
const float detail=strataWeight*(1.35f*(relief+0.5f*ownerRelief))
+caveWeight*relief+floorWeight*(0.55f*relief)
+passageFactor*(0.75f*passageBandRelief);
float ownerBound=0.0f;
[loop]
for(uint layerIndex=0;layerIndex<FarDetailLayerCount;++layerIndex)
{
const FarFieldDetailLayer layer=FarDetailLayers[layerIndex];
if(position.z>=layer.lo&&position.z<=layer.hi)
{
const float depth=layer.surfaceRoughness*2.0f;
if(depth>0.0f&&abs(surfaceSdf)<depth&&layer.roughnessFrequency>0.0f)
{
const float fade=1.0f-abs(surfaceSdf)/depth;
ownerBound=3.15f*1.25f*layer.surfaceRoughness*fade*fade;
}
break;
}
}
const float reliefBound=1.20f*abs(macro)+(0.80f+0.25f*macro);
const float contrast=1.35f*(1.0f-caveFactor)+1.00f*(caveFactor-floorFactor)+0.55f*floorFactor;
const float localSupremum=min(3.9f,(reliefBound+0.5f*ownerBound)*contrast);
const float edge=clamp(localSupremum+0.125f,1.5f,4.125f);
const float distance=abs(rockDistance);
float fade=1.0f;
if(distance>1.5f)
{
const float width=edge-1.5f;
fade=width>0.0f?VF_FieldSmoothUnit((edge-distance)/width):0.0f;
}
rockDistance+=clamp(detail,-3.9f,3.9f)*fade;
}
float density=-rockDistance;
if(clampNearZero!=0&&abs(density)<0.05f)
density=rockDistance>=0.0f?-0.05f:0.05f;
return density;
}
[numthreads(64,1,1)]
void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
{
@@ -233,50 +437,72 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
const uint z = index / (SampleDim * SampleDim);
const float3 position = float3(OriginX, OriginY, OriginZ)
+ float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels;
const FarFieldSpatialBin range = FarFieldFindBin(position);
float cave = 100000.0f;
float overlay = -100000.0f;
uint distanceEvaluations = 0;
[loop]
for (uint item = 0; item < range.count; ++item)
float density;
uint distanceEvaluations=0;
float nearNormalDensity=0.0f;
if(NearFieldMode!=0)
{
const VF_FieldPrimitive shape = FarShapes[FarSpatialBinShapeIds[range.offset + item]];
if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0)
nearNormalDensity=NearFieldDensityAt(position,0,distanceEvaluations);
density=nearNormalDensity;
if(abs(density)<0.05f)
density=density<0.0f?-0.05f:(density>0.0f?0.05f:-0.05f);
const uint packedProxy=FarNearPackedProxy[index];
if(packedProxy!=0xffffffffu)
{
if (position.x < shape.minVoxel[0] - 6 || position.x > shape.maxVoxel[0] + 6
|| position.y < shape.minVoxel[1] - 6 || position.y > shape.maxVoxel[1] + 6
|| position.z < shape.minVoxel[2] - 6 || position.z > shape.maxVoxel[2] + 6)
continue;
float distance = VF_FieldPrimitiveDistance(shape,
position.x, position.y, position.z);
++distanceEvaluations;
if ((shape.flags & VF_FIELD_PRIMITIVE_HAS_FLOOR) != 0)
{
const float floorZ = VF_FieldPrimitiveFloorAt(shape, position.x, position.y);
distance = VF_FieldSmoothMax(distance, floorZ - position.z,
shape.floorBlendRadius);
}
cave = VF_FieldSmoothMin(cave, distance, shape.blendRadius);
}
else if (position.x >= shape.minVoxel[0] && position.x <= shape.maxVoxel[0]
&& position.y >= shape.minVoxel[1] && position.y <= shape.maxVoxel[1]
&& position.z >= shape.minVoxel[2] && position.z <= shape.maxVoxel[2])
{
overlay = max(overlay, -VF_FieldPrimitiveDistance(shape,
position.x, position.y, position.z));
++distanceEvaluations;
// The CPU uploads sparse interpolation magnitudes. The GPU decides
// ordinary signs, except for the one-quantum band where float sign
// noise can flip an entire cell against the packed CPU cache.
const bool packedSignOverride=(packedProxy&0x40000000u)!=0u;
const bool packedRockSign=(packedProxy&0x80000000u)!=0u;
const uint magnitudeQ=packedProxy&0x3fffffffu;
const float magnitude=magnitudeQ==0u?4.125f:(float)magnitudeQ*(1.0f/256.0f);
const bool negative=packedSignOverride?packedRockSign:(density<=0.0f);
density=negative?-magnitude:magnitude;
}
}
const float base = min(position.z - FarStrataLo, FarStrataHi - position.z);
float rockDistance = VF_FieldSmoothMin(base, max(cave, overlay), FarStrataBlend);
else
{
const FarFieldSpatialBin range=FarFieldFindBin(position);
float cave=100000.0f,overlay=-100000.0f;
[loop]
for(uint item=0;item<range.count;++item)
{
const VF_FieldPrimitive shape=FarShapes[FarSpatialBinShapeIds[range.offset+item]];
if((shape.flags&VF_FIELD_PRIMITIVE_CARVE)!=0)
{
if(position.x<shape.minVoxel[0]-6||position.x>shape.maxVoxel[0]+6
||position.y<shape.minVoxel[1]-6||position.y>shape.maxVoxel[1]+6
||position.z<shape.minVoxel[2]-6||position.z>shape.maxVoxel[2]+6)continue;
float distance=VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z);
++distanceEvaluations;
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
{
const float floorZ=VF_FieldPrimitiveFloorAt(shape,position.x,position.y);
distance=VF_FieldSmoothMax(distance,floorZ-position.z,shape.floorBlendRadius);
}
cave=VF_FieldSmoothMin(cave,distance,shape.blendRadius);
}
else if(position.x>=shape.minVoxel[0]&&position.x<=shape.maxVoxel[0]
&&position.y>=shape.minVoxel[1]&&position.y<=shape.maxVoxel[1]
&&position.z>=shape.minVoxel[2]&&position.z<=shape.maxVoxel[2])
{
overlay=max(overlay,-VF_FieldPrimitiveDistance(shape,position.x,position.y,position.z));
++distanceEvaluations;
}
}
const float base=min(position.z-FarStrataLo,FarStrataHi-position.z);
const float rockDistance=VF_FieldSmoothMin(base,max(cave,overlay),FarStrataBlend);
const float rawDensity=-rockDistance;
const float magnitude=max(abs(rawDensity),0.05f);
density=rockDistance>=0.0f?-magnitude:magnitude;
}
const uint protectedAir = (FarProtectedAir[index >> 5] >> (index & 31)) & 1;
const uint protectedSolid = (FarProtectedSolid[index >> 5] >> (index & 31)) & 1;
const float rawDensity = -rockDistance;
const float magnitude = max(abs(rawDensity), 0.05f);
float density = rockDistance >= 0.0f ? -magnitude : magnitude;
if (protectedAir != 0) density = magnitude;
else if (protectedSolid != 0) density = -magnitude;
const float protectedMinimum=NearFieldMode!=0?1.0f/256.0f:0.05f;
if(protectedAir!=0)density=max(abs(density),protectedMinimum);
else if(protectedSolid!=0)density=-max(abs(density),protectedMinimum);
FarFieldDensity[index] = density;
FarNearNormalDensityOutput[index]=nearNormalDensity;
if (FarEvalCounterEnabled != 0) FarFieldEvalCounters[index] = distanceEvaluations;
}
@@ -320,12 +546,12 @@ float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin c
float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
{
// Keep the bounded signed-distance refinement, but stop once the residual
// reaches the same acceptance bound used by the original fallback. A
// spatial-bin cache avoids re-hashing nearby probes.
const float3 nominalPosition=center+direction*nominalRadius;
const int3 cachedBinCoord=int3(floor(nominalPosition/max(1.0f,(float)FarSpatialBinSize)));
const FarFieldSpatialBin cachedRange=FarFieldFindBin(nominalPosition);
// Keep the bounded signed-distance refinement, but stop once the residual
// reaches the same acceptance bound used by the original fallback. A
// spatial-bin cache avoids re-hashing nearby probes.
const float maxOffset=max(16.0f,nominalRadius*0.75f);
const float minRadius=max(0.0f,nominalRadius-maxOffset);
const float maxRadius=nominalRadius+maxOffset;
@@ -470,8 +696,8 @@ void BuildFarConnectorRingsCS(uint3 groupId : SV_GroupID,
}
if (ringId == 0)
{
const uint maxVerticesPerTile = CellCount * 15;
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
const uint slot = OutputVertexCapacity > 0
? OutputBaseVertex / OutputVertexCapacity : 0;
const uint panelBaseVertex = slot * FarConnectorPanelVertexCapacity;
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 20, FarConnectorVertexCount);
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 24, FarConnectorVertexCount > 0 ? 1 : 0);
@@ -547,8 +773,8 @@ void EmitFarConnectorPanelsCS(uint3 dispatchId : SV_DispatchThreadID)
vertex.normal = ring.normals[cornerIndex].xyz;
const float connectorLength = length(connector.end - connector.start);
vertex.uv = float2(ring.t * connectorLength / 256.0f, (float)side / 4.0f);
const uint maxVerticesPerTile = CellCount * 15;
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
const uint slot = OutputVertexCapacity > 0
? OutputBaseVertex / OutputVertexCapacity : 0;
const uint base = slot * FarConnectorPanelVertexCapacity;
const uint byteAddress = (base + vertexId) * 32;
FarConnectorVertexBytes.Store3(byteAddress, asuint(vertex.position));
@@ -644,7 +870,7 @@ void ScanGroupsCS(uint3 dispatchId : SV_DispatchThreadID)
GroupOffsets[i] = offset;
offset += InputGroupVertexCounts[i];
}
DrawArgs.Store(OutputArgsByteOffset + 0, offset);
DrawArgs.Store(OutputArgsByteOffset + 0, min(offset, OutputVertexCapacity));
DrawArgs.Store(OutputArgsByteOffset + 4, 1);
DrawArgs.Store(OutputArgsByteOffset + 8, OutputBaseVertex);
DrawArgs.Store(OutputArgsByteOffset + 12, 0);
@@ -672,11 +898,13 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
const uint index = dispatchId.x;
if (index >= CellCount) return;
const uint caseIndex = Cases[index];
const uint base = OutputBaseVertex + LocalOffsets[index] + PrefixGroups[index / 256];
const uint localBase = LocalOffsets[index] + PrefixGroups[index / 256];
if(localBase>=OutputVertexCapacity)return;
const uint base = OutputBaseVertex + localBase;
const uint3 cell = uint3(index % CellsPerAxis,
(index / CellsPerAxis) % CellsPerAxis,
index / (CellsPerAxis * CellsPerAxis));
[unroll]
[loop]
for (uint slot = 0; slot < 15; ++slot)
{
uint tableSlot = slot;
@@ -684,10 +912,12 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
else if (slot % 3 == 2) tableSlot = slot - 1;
const int edgeValue = Triangles[caseIndex * 16 + tableSlot];
if (edgeValue < 0) break;
if(localBase+slot>=OutputVertexCapacity)break;
const uint edge = uint(edgeValue);
const float rootT=EdgeRootParameter(int3(cell),edge,Density,MidpointCellIds,MidpointData);
GpuVertex v;
v.position = EdgePosition(int3(cell), edge, Density, MidpointCellIds, MidpointData);
v.normal = EdgeNormal(int3(cell), edge, Density, MidpointCellIds, MidpointData);
v.position = EdgePosition(int3(cell),edge,rootT);
v.normal = EdgeNormal(int3(cell),edge,rootT,Density);
v.uv = v.position.xy / VoxelSizeCm;
const uint byteAddress = (base + slot) * 32;
VertexBytes.Store3(byteAddress, asuint(v.position));