wip(standalone): GPU step 3 -- GPU decides far rock/air, sparse CPU exceptions, hybrid viewer (H)

1,114 of 1,266 far tiles meshed on the GPU; vs CPU and vs WARP: 0 sign/topology differences,
including the protected/thin exception audit; max symmetric vertex distance 0.125 cm; repeat RX runs
identical; GPU field+mesh 0.029 ms/tile average. Strata survival 7,200/7,200, but connector survival
fails (GPU lacks the connector repair panels). Hybrid cold clipmap 5.74 s vs CPU 2.23 s: dispatch and
readback overhead, not compute (~33 ms of GPU work in total).

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 09:14:22 +02:00
1 parent b61ccff549
commit 782f195bdb
15 files changed
+1018 -173

No files matched your search

@@ -130,9 +130,10 @@ struct FarFieldSampleRange { uint offset; uint count; };
StructuredBuffer<VF_FieldPrimitive> FarShapes : register(t10);
StructuredBuffer<FarFieldSampleRange> FarSampleShapeRanges : register(t11);
StructuredBuffer<uint> FarSampleShapeIds : register(t12);
StructuredBuffer<uint> FarRockAirSigns : register(t13);
StructuredBuffer<uint> FarProtectedAir : register(t14);
StructuredBuffer<uint> FarProtectedAir : register(t13);
StructuredBuffer<uint> FarProtectedSolid : register(t14);
RWStructuredBuffer<float> FarFieldDensity : register(u7);
StructuredBuffer<float> GpuSignDensity : register(t15);
cbuffer FarFieldConstants : register(b1)
{
@@ -186,31 +187,22 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
const float base = min(position.z - FarStrataLo, FarStrataHi - position.z);
float rockDistance = VF_FieldSmoothMin(base, max(cave, overlay), FarStrataBlend);
const uint protectedAir = (FarProtectedAir[index >> 5] >> (index & 31)) & 1;
if (protectedAir != 0) rockDistance = min(rockDistance, -0.0625f);
const uint protectedSolid = (FarProtectedSolid[index >> 5] >> (index & 31)) & 1;
const float rawDensity = -rockDistance;
const uint air = (FarRockAirSigns[index >> 5] >> (index & 31)) & 1;
const float magnitude = max(abs(rawDensity), 0.05f);
FarFieldDensity[index] = air != 0 ? magnitude : -magnitude;
float density = rockDistance >= 0.0f ? -magnitude : magnitude;
if (protectedAir != 0) density = magnitude;
else if (protectedSolid != 0) density = -magnitude;
FarFieldDensity[index] = density;
}
groupshared uint ScanValues[256];
[numthreads(256,1,1)]
void ClassifyCS(uint3 dispatchId : SV_DispatchThreadID,
uint3 groupId : SV_GroupID,
uint3 groupThreadId : SV_GroupThreadID)
void ClassifyAndScan(uint index, uint3 groupId, uint3 groupThreadId, uint caseIndex)
{
const uint index = dispatchId.x;
uint vertexCount = 0;
uint caseIndex = 0;
if (index < CellCount)
{
const uint3 c = uint3(index % CellsPerAxis,
(index / CellsPerAxis) % CellsPerAxis,
index / (CellsPerAxis * CellsPerAxis));
[unroll]
for (uint corner = 0; corner < 8; ++corner)
caseIndex |= LatticeSign(Signs, int3(c) + CornerOffset[corner]) << corner;
[unroll]
for (uint slot = 0; slot < 15; ++slot)
{
@@ -235,6 +227,49 @@ void ClassifyCS(uint3 dispatchId : SV_DispatchThreadID,
GroupVertexCounts[groupId.x] = ScanValues[255];
}
[numthreads(256,1,1)]
void ClassifyCS(uint3 dispatchId : SV_DispatchThreadID,
uint3 groupId : SV_GroupID,
uint3 groupThreadId : SV_GroupThreadID)
{
const uint index = dispatchId.x;
uint caseIndex = 0;
if (index < CellCount)
{
const uint3 c = uint3(index % CellsPerAxis,
(index / CellsPerAxis) % CellsPerAxis,
index / (CellsPerAxis * CellsPerAxis));
[unroll]
for (uint corner = 0; corner < 8; ++corner)
caseIndex |= LatticeSign(Signs, int3(c) + CornerOffset[corner]) << corner;
}
ClassifyAndScan(index, groupId, groupThreadId, caseIndex);
}
uint GpuLatticeSign(int3 p)
{
return GpuSignDensity[SampleIndex(p)] >= 0.0f ? 1 : 0;
}
[numthreads(256,1,1)]
void ClassifyFarCS(uint3 dispatchId : SV_DispatchThreadID,
uint3 groupId : SV_GroupID,
uint3 groupThreadId : SV_GroupThreadID)
{
const uint index = dispatchId.x;
uint caseIndex = 0;
if (index < CellCount)
{
const uint3 c = uint3(index % CellsPerAxis,
(index / CellsPerAxis) % CellsPerAxis,
index / (CellsPerAxis * CellsPerAxis));
[unroll]
for (uint corner = 0; corner < 8; ++corner)
caseIndex |= GpuLatticeSign(int3(c) + CornerOffset[corner]) << corner;
}
ClassifyAndScan(index, groupId, groupThreadId, caseIndex);
}
StructuredBuffer<uint> InputGroupVertexCounts : register(t8);
RWStructuredBuffer<uint> GroupOffsets : register(u3);
RWByteAddressBuffer DrawArgs : register(u4);