wip(standalone): GPU step 8 partial -- GPU skirts, first-present hold; remote gap open
Luna's session ended on an API "Bad Request" before a final report; state is unverified by me. In the log: GPU clipped-skirt emitter (count/scan/emit, protected signs and critical air honoured), exposed-skirt audit passing at the end; first-present stall reproduced on every launch (2.36-2.59 s, not exe identity/idle/CPU run) and hidden by holding the CPU-ready frame until GPU residency. Remote LOD3-4 band still differs from CPU; draw ranges and the one inspected LOD3 tile match, cause not yet found (one-tile probe mid-fix). 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
90421d0655
commit
f2de8fe9c6
42 files changed
+1381
-59
No files matched your search
@@ -60,7 +60,9 @@ cbuffer FarConnectorConstants : register(b2)
|
||||
uint FarConnectorVertexCount;
|
||||
uint FarConnectorPanelVertexCapacity;
|
||||
uint FarShapeCount;
|
||||
uint3 FarConnectorPadding;
|
||||
uint FarSkirtFaceMask;
|
||||
float FarSkirtDepthVoxels;
|
||||
uint FarConnectorPadding;
|
||||
};
|
||||
|
||||
static const int3 CornerOffset[8] = {
|
||||
@@ -230,6 +232,8 @@ RWStructuredBuffer<FarConnectorRing> FarConnectorRingOutput : register(u7);
|
||||
RWByteAddressBuffer FarConnectorDrawArgs : register(u4);
|
||||
RWByteAddressBuffer FarConnectorVertexBytes : register(u5);
|
||||
RWByteAddressBuffer FarConnectorIndexBytes : register(u6);
|
||||
RWStructuredBuffer<uint> FarSkirtScratch : register(u0);
|
||||
ByteAddressBuffer FarSurfaceVertexInput : register(t19);
|
||||
|
||||
RWStructuredBuffer<uint> FarFieldEvalCounters : register(u6);
|
||||
|
||||
@@ -510,6 +514,7 @@ float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin c
|
||||
{
|
||||
float cave = 100000.0f;
|
||||
float overlay = -100000.0f;
|
||||
bool criticalAir=false;
|
||||
const int3 positionBinCoord=int3(floor(position/max(1.0f,(float)FarSpatialBinSize)));
|
||||
FarFieldSpatialBin range;
|
||||
if(all(positionBinCoord==cachedBinCoord))range=cachedRange;
|
||||
@@ -520,6 +525,25 @@ float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin c
|
||||
const VF_FieldPrimitive shape = FarShapes[FarSpatialBinShapeIds[range.offset + item]];
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0)
|
||||
{
|
||||
// The CPU field forces critical passage air at arbitrary skirt
|
||||
// probes using the containing integer voxel. Match that decided
|
||||
// exception here; the ordinary smooth field remains GPU-evaluated.
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_CRITICAL)!=0
|
||||
&&all(position>=float3(shape.minVoxel[0],shape.minVoxel[1],shape.minVoxel[2]))
|
||||
&&all(position<=float3(shape.maxVoxel[0],shape.maxVoxel[1],shape.maxVoxel[2])))
|
||||
{
|
||||
const int3 decidedPosition=int3(position);
|
||||
float decidedDistance=VF_FieldPrimitiveDistance(shape,
|
||||
(float)decidedPosition.x,(float)decidedPosition.y,(float)decidedPosition.z);
|
||||
if((shape.flags&VF_FIELD_PRIMITIVE_HAS_FLOOR)!=0)
|
||||
{
|
||||
const float decidedFloor=VF_FieldPrimitiveFloorAt(shape,
|
||||
(float)decidedPosition.x,(float)decidedPosition.y);
|
||||
decidedDistance=VF_FieldSmoothMax(decidedDistance,
|
||||
decidedFloor-(float)decidedPosition.z,shape.floorBlendRadius);
|
||||
}
|
||||
criticalAir=criticalAir||decidedDistance<=0.0f;
|
||||
}
|
||||
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)
|
||||
@@ -541,7 +565,174 @@ float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin c
|
||||
}
|
||||
}
|
||||
const float base = min(position.z - FarStrataLo, FarStrataHi - position.z);
|
||||
return VF_FieldSmoothMin(base, max(cave, overlay), FarStrataBlend);
|
||||
float rockDistance=VF_FieldSmoothMin(base,max(cave,overlay),FarStrataBlend);
|
||||
if(criticalAir)rockDistance=min(rockDistance,-1.0f/16.0f);
|
||||
return rockDistance;
|
||||
}
|
||||
|
||||
struct FarSkirtVertex
|
||||
{
|
||||
float3 position;
|
||||
float3 normal;
|
||||
float density;
|
||||
};
|
||||
|
||||
float FarSkirtDensityAtVoxel(float3 position)
|
||||
{
|
||||
const int3 binCoord=int3(floor(position/max(1.0f,(float)FarSpatialBinSize)));
|
||||
const FarFieldSpatialBin range=FarFieldFindBin(position);
|
||||
const float rockDistance=FarRockDistanceAt(position,binCoord,range);
|
||||
float density=-rockDistance;
|
||||
// ShapeWorld clamps arbitrary (off-lattice) density magnitudes near the
|
||||
// surface to 0.05 voxel. Skirt clipping uses those values for edge-root
|
||||
// interpolation, so matching only their signs can move a clipped edge.
|
||||
if(abs(density)<0.05f)density=rockDistance>=0.0f?-0.05f:0.05f;
|
||||
return density;
|
||||
}
|
||||
|
||||
float FarSkirtDensityAtCm(float3 positionCm)
|
||||
{
|
||||
return FarSkirtDensityAtVoxel(positionCm/VoxelSizeCm);
|
||||
}
|
||||
|
||||
FarSkirtVertex FarSkirtInterpolate(FarSkirtVertex a,FarSkirtVertex b,float t)
|
||||
{
|
||||
if(t<=1.0e-6f)return a;
|
||||
if(t>=1.0f-1.0e-6f)return b;
|
||||
FarSkirtVertex result;
|
||||
result.position=lerp(a.position,b.position,t);
|
||||
result.normal=normalize(lerp(a.normal,b.normal,t));
|
||||
result.density=FarSkirtDensityAtCm(result.position);
|
||||
return result;
|
||||
}
|
||||
|
||||
void FarSkirtClipTriangle(FarSkirtVertex a,FarSkirtVertex b,FarSkirtVertex c,
|
||||
out FarSkirtVertex polygon[5],out uint polygonCount)
|
||||
{
|
||||
FarSkirtVertex input[3];input[0]=a;input[1]=b;input[2]=c;
|
||||
polygonCount=0;
|
||||
[unroll]
|
||||
for(uint edge=0;edge<3;++edge)
|
||||
{
|
||||
FarSkirtVertex start=input[edge];
|
||||
FarSkirtVertex end=input[(edge+1)%3];
|
||||
const bool startSolid=start.density<=0.0f;
|
||||
const bool endSolid=end.density<=0.0f;
|
||||
if(startSolid!=endSolid)
|
||||
{
|
||||
const float denominator=start.density-end.density;
|
||||
const float t=abs(denominator)>1.0e-8f
|
||||
?clamp(start.density/denominator,0.0f,1.0f):0.5f;
|
||||
polygon[polygonCount++]=FarSkirtInterpolate(start,end,t);
|
||||
}
|
||||
if(endSolid)polygon[polygonCount++]=end;
|
||||
}
|
||||
}
|
||||
|
||||
uint FarSkirtSourceTriangleOutputCount(FarSkirtVertex a,FarSkirtVertex b,FarSkirtVertex c)
|
||||
{
|
||||
FarSkirtVertex polygon[5];uint polygonCount=0;
|
||||
FarSkirtClipTriangle(a,b,c,polygon,polygonCount);
|
||||
if(polygonCount<3)return 0;
|
||||
uint vertexCount=0;
|
||||
[loop]
|
||||
for(uint fan=1;fan+1<polygonCount;++fan)
|
||||
{
|
||||
const float3 centerVoxel=(polygon[0].position+polygon[fan].position
|
||||
+polygon[fan+1].position)/(3.0f*VoxelSizeCm);
|
||||
if(FarSkirtDensityAtVoxel(centerVoxel)<=0.0f)vertexCount+=6;
|
||||
}
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
uint FarSkirtBoundaryFaceMask(float3 a,float3 b)
|
||||
{
|
||||
const float3 tileMin=float3(OriginX,OriginY,OriginZ)*VoxelSizeCm;
|
||||
const float3 tileMax=(float3(OriginX,OriginY,OriginZ)
|
||||
+float(CellsPerAxis*StepVoxels))*VoxelSizeCm;
|
||||
uint mask=0;
|
||||
if(a.x==tileMin.x&&b.x==tileMin.x)mask|=1u<<0;
|
||||
if(a.x==tileMax.x&&b.x==tileMax.x)mask|=1u<<1;
|
||||
if(a.y==tileMin.y&&b.y==tileMin.y)mask|=1u<<2;
|
||||
if(a.y==tileMax.y&&b.y==tileMax.y)mask|=1u<<3;
|
||||
if(a.z==tileMin.z&&b.z==tileMin.z)mask|=1u<<4;
|
||||
if(a.z==tileMax.z&&b.z==tileMax.z)mask|=1u<<5;
|
||||
return mask;
|
||||
}
|
||||
|
||||
FarSkirtVertex FarSkirtSurfaceVertex(uint vertexId)
|
||||
{
|
||||
const uint byteAddress=(OutputBaseVertex+vertexId)*32;
|
||||
FarSkirtVertex vertex;
|
||||
vertex.position=asfloat(FarSurfaceVertexInput.Load3(byteAddress));
|
||||
vertex.normal=asfloat(FarSurfaceVertexInput.Load3(byteAddress+12));
|
||||
vertex.density=0.0f;
|
||||
return vertex;
|
||||
}
|
||||
|
||||
FarSkirtVertex FarSkirtExtrudedVertex(FarSkirtVertex source)
|
||||
{
|
||||
FarSkirtVertex vertex=source;
|
||||
vertex.position-=source.normal*(max(1.0f,FarSkirtDepthVoxels)*VoxelSizeCm);
|
||||
vertex.density=FarSkirtDensityAtCm(vertex.position);
|
||||
return vertex;
|
||||
}
|
||||
|
||||
uint FarSkirtEdgeOutputCount(uint edgeVertexId)
|
||||
{
|
||||
const uint surfaceVertexCount=min(FarConnectorDrawArgs.Load(OutputArgsByteOffset),OutputVertexCapacity);
|
||||
if(edgeVertexId>=surfaceVertexCount)return 0;
|
||||
const uint triangleBase=edgeVertexId-(edgeVertexId%3);
|
||||
const uint edge=edgeVertexId%3;
|
||||
const FarSkirtVertex a=FarSkirtSurfaceVertex(triangleBase+edge);
|
||||
const FarSkirtVertex b=FarSkirtSurfaceVertex(triangleBase+(edge+1)%3);
|
||||
if((FarSkirtBoundaryFaceMask(a.position,b.position)&FarSkirtFaceMask)==0)return 0;
|
||||
const FarSkirtVertex a2=FarSkirtExtrudedVertex(a);
|
||||
const FarSkirtVertex b2=FarSkirtExtrudedVertex(b);
|
||||
return FarSkirtSourceTriangleOutputCount(a,b,b2)
|
||||
+FarSkirtSourceTriangleOutputCount(a,b2,a2);
|
||||
}
|
||||
|
||||
void FarSkirtStoreVertex(uint outputId,FarSkirtVertex vertex,uint panelBaseVertex)
|
||||
{
|
||||
GpuVertex output;
|
||||
output.position=vertex.position;
|
||||
output.normal=vertex.normal;
|
||||
output.uv=vertex.position.xy/VoxelSizeCm;
|
||||
const uint index=panelBaseVertex+outputId;
|
||||
const uint byteAddress=index*32;
|
||||
FarConnectorVertexBytes.Store3(byteAddress,asuint(output.position));
|
||||
FarConnectorVertexBytes.Store3(byteAddress+12,asuint(output.normal));
|
||||
FarConnectorVertexBytes.Store2(byteAddress+24,asuint(output.uv));
|
||||
FarConnectorIndexBytes.Store(index*4,index);
|
||||
}
|
||||
|
||||
void FarSkirtEmitTriangle(FarSkirtVertex a,FarSkirtVertex b,FarSkirtVertex c,
|
||||
uint panelBaseVertex,uint edgeBase,inout uint edgeCursor)
|
||||
{
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,a,panelBaseVertex);
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,b,panelBaseVertex);
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,c,panelBaseVertex);
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,a,panelBaseVertex);
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,c,panelBaseVertex);
|
||||
FarSkirtStoreVertex(edgeBase+edgeCursor++,b,panelBaseVertex);
|
||||
}
|
||||
|
||||
void FarSkirtEmitSourceTriangle(FarSkirtVertex a,FarSkirtVertex b,FarSkirtVertex c,
|
||||
uint panelBaseVertex,uint edgeBase,inout uint edgeCursor)
|
||||
{
|
||||
FarSkirtVertex polygon[5];uint polygonCount=0;
|
||||
FarSkirtClipTriangle(a,b,c,polygon,polygonCount);
|
||||
if(polygonCount<3)return;
|
||||
[loop]
|
||||
for(uint fan=1;fan+1<polygonCount;++fan)
|
||||
{
|
||||
const float3 centerVoxel=(polygon[0].position+polygon[fan].position
|
||||
+polygon[fan+1].position)/(3.0f*VoxelSizeCm);
|
||||
if(FarSkirtDensityAtVoxel(centerVoxel)<=0.0f)
|
||||
FarSkirtEmitTriangle(polygon[0],polygon[fan],polygon[fan+1],
|
||||
panelBaseVertex,edgeBase,edgeCursor);
|
||||
}
|
||||
}
|
||||
|
||||
float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
@@ -704,6 +895,7 @@ void BuildFarConnectorRingsCS(uint3 groupId : SV_GroupID,
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 28, panelBaseVertex);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 32, 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 36, 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 40, 0);
|
||||
}
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
@@ -880,6 +1072,7 @@ void ScanGroupsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
DrawArgs.Store(OutputArgsByteOffset + 28, 0);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 32, 0);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 36, 0);
|
||||
DrawArgs.Store(OutputArgsByteOffset + 40, 0);
|
||||
}
|
||||
|
||||
StructuredBuffer<float> Density : register(t2);
|
||||
@@ -927,6 +1120,63 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
}
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void ClassifyFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
const uint edgeVertexId=dispatchId.x;
|
||||
if(edgeVertexId>=OutputVertexCapacity)return;
|
||||
FarSkirtScratch[edgeVertexId]=FarSkirtEdgeOutputCount(edgeVertexId);
|
||||
}
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void ScanFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
const uint surfaceVertexCount=min(FarConnectorDrawArgs.Load(OutputArgsByteOffset),
|
||||
OutputVertexCapacity);
|
||||
uint totalSkirtVertices=0;
|
||||
[loop]
|
||||
for(uint edgeVertexId=0;edgeVertexId<surfaceVertexCount;++edgeVertexId)
|
||||
{
|
||||
const uint count=FarSkirtScratch[edgeVertexId];
|
||||
FarSkirtScratch[edgeVertexId]=totalSkirtVertices;
|
||||
totalSkirtVertices+=count;
|
||||
}
|
||||
const uint panelVertexCount=FarConnectorDrawArgs.Load(OutputArgsByteOffset+20);
|
||||
if(panelVertexCount+totalSkirtVertices>FarConnectorPanelVertexCapacity)
|
||||
{
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset+40,1);
|
||||
return;
|
||||
}
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset+40,0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset+20,panelVertexCount+totalSkirtVertices);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset+24,
|
||||
panelVertexCount+totalSkirtVertices>0?1:0);
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void EmitFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
const uint edgeVertexId=dispatchId.x;
|
||||
if(edgeVertexId>=OutputVertexCapacity||FarSkirtFaceMask==0
|
||||
||FarConnectorDrawArgs.Load(OutputArgsByteOffset+40)!=0)return;
|
||||
const uint surfaceVertexCount=min(FarConnectorDrawArgs.Load(OutputArgsByteOffset),
|
||||
OutputVertexCapacity);
|
||||
if(edgeVertexId>=surfaceVertexCount)return;
|
||||
if(FarSkirtEdgeOutputCount(edgeVertexId)==0)return;
|
||||
const uint triangleBase=edgeVertexId-(edgeVertexId%3);
|
||||
const uint edge=edgeVertexId%3;
|
||||
const FarSkirtVertex a=FarSkirtSurfaceVertex(triangleBase+edge);
|
||||
const FarSkirtVertex b=FarSkirtSurfaceVertex(triangleBase+(edge+1)%3);
|
||||
if((FarSkirtBoundaryFaceMask(a.position,b.position)&FarSkirtFaceMask)==0)return;
|
||||
const FarSkirtVertex a2=FarSkirtExtrudedVertex(a);
|
||||
const FarSkirtVertex b2=FarSkirtExtrudedVertex(b);
|
||||
const uint panelBaseVertex=FarConnectorDrawArgs.Load(OutputArgsByteOffset+28);
|
||||
const uint edgeBase=FarConnectorVertexCount+FarSkirtScratch[edgeVertexId];
|
||||
uint edgeCursor=0;
|
||||
FarSkirtEmitSourceTriangle(a,b,b2,panelBaseVertex,edgeBase,edgeCursor);
|
||||
FarSkirtEmitSourceTriangle(a,b2,a2,panelBaseVertex,edgeBase,edgeCursor);
|
||||
}
|
||||
|
||||
// Smoke probe for the dual-language noise path. This is intentionally separate from the mesh
|
||||
// dispatch until shape records can be exported from the exact CPU layout compiler.
|
||||
struct NoiseProbeInput { float3 position; uint seed; };
|
||||
|
||||
Reference in new issue
Block a user