wip(standalone): GPU step 11 -- per-shader cold cost measured; far EmitCS split, still CPU wins

Cold first-use per shader (queue ms): EvaluateFarField 1757, EmitNear 2429,
FarConnectorRings 784, FarSkirts 507+975; the far EmitCS dropped 2327 -> 3.2
by splitting out the near midpoint fallback into EmitNearCS. Warm-up under
0xFF00 ~2.0 s (worker count and thread priority do not change it; 0xFFFF run:
1.71 s). Whole view median CPU 1.428 s vs hybrid 3.154 s. Audits unchanged.
AMD DxCache was last written 2026-10-06 23:27 -- shaders may no longer be
cached across launches (unverified).

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-07 06:13:59 +02:00
1 parent 10580650cb
commit b6ac950073
19 files changed
+908 -24

No files matched your search

@@ -150,6 +150,27 @@ float EdgeRootParameter(int3 cell,uint edge,StructuredBuffer<float> density,
return VF_FieldRefineRoot(da,db,mid,hasMid);
}
float FarEdgeRootParameter(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];
}
// Far tiles always have StepVoxels > 1. The exact midpoint field fallback in
// EdgeRootParameter is restricted to StepVoxels == 1, so omit that LOD0-only
// branch from the far entry point while retaining every CPU-authored midpoint.
return VF_FieldRefineRoot(da,db,mid,hasMid);
}
float3 EdgePosition(int3 cell,uint edge,float t)
{
const int2 corners = EdgeCorners[edge];
@@ -1086,7 +1107,7 @@ RWByteAddressBuffer VertexBytes : register(u5);
RWByteAddressBuffer IndexBytes : register(u6);
[numthreads(256,1,1)]
void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
void EmitNearCS(uint3 dispatchId : SV_DispatchThreadID)
{
const uint index = dispatchId.x;
if (index >= CellCount) return;
@@ -1120,6 +1141,41 @@ void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
}
}
[numthreads(256,1,1)]
void EmitCS(uint3 dispatchId : SV_DispatchThreadID)
{
const uint index = dispatchId.x;
if (index >= CellCount) return;
const uint caseIndex = Cases[index];
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));
[loop]
for (uint slot = 0; slot < 15; ++slot)
{
uint tableSlot = slot;
if (slot % 3 == 1) tableSlot = slot + 1;
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=FarEdgeRootParameter(int3(cell),edge,Density,MidpointCellIds,MidpointData);
GpuVertex v;
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));
VertexBytes.Store3(byteAddress + 12, asuint(v.normal));
VertexBytes.Store2(byteAddress + 24, asuint(v.uv));
IndexBytes.Store((base + slot) * 4, base + slot);
}
}
[numthreads(64,1,1)]
void ClassifyFarSkirtsCS(uint3 dispatchId : SV_DispatchThreadID)
{