research(standalone): GPU first port -- marching cubes in D3D11 compute, exact vs WARP; field still CPU
- Compute shaders classify cells from CPU sign bits, scan output offsets, emit vertex/index buffers and indirect-draw args. Two 32^3 tiles: 0 topology differences hardware vs WARP; two runs on the RX 7900 XTX identical; max CPU/GPU vertex distance 0.000122 cm (LOD0), 0.000244 cm (LOD1). - GPU kernels 0.049 / 0.071 ms vs CPU tile 2.33 / 1.99 ms, but CPU input preparation dominates (7.25 / 2.84 ms wall): no whole-view gain until the field itself runs on the GPU. - VoxelForgeFieldShared.h: first shared C++/HLSL field math (integer noise hash, ...). CPU path unchanged. 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
566d1f342a
commit
d4b4377d30
6 files changed
+1809
No files matched your search
@@ -0,0 +1,105 @@
|
||||
[CmdletBinding()]
|
||||
param([switch]$SkipCore)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$HostProcess = Get-Process -Id $PID
|
||||
$HostProcess.PriorityClass = 'BelowNormal'
|
||||
$HostProcess.ProcessorAffinity = [IntPtr]0xFF00
|
||||
$Standalone = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..'))
|
||||
$BuildDir = Join-Path $Standalone 'Build\VoxelForgeGpuFirstPort'
|
||||
$WrapperDir = Join-Path $BuildDir 'wrappers'
|
||||
$BinDir = Join-Path $Standalone 'bin'
|
||||
$Output = Join-Path $BinDir 'VoxelForgeGpuFirstPort.exe'
|
||||
$CoreLib = Join-Path $BuildDir 'VoxelForgeGpuPortCore.lib'
|
||||
$VcVars = 'E:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat'
|
||||
|
||||
if (-not (Test-Path -LiteralPath $VcVars -PathType Leaf)) { throw "MSVC setup script missing: $VcVars" }
|
||||
[IO.Directory]::CreateDirectory($BuildDir) | Out-Null
|
||||
[IO.Directory]::CreateDirectory($WrapperDir) | Out-Null
|
||||
[IO.Directory]::CreateDirectory($BinDir) | Out-Null
|
||||
|
||||
$Manifest = Join-Path $Standalone 'Core\core_sources.txt'
|
||||
$Sources = @(Get-Content -LiteralPath $Manifest | ForEach-Object { $_.Trim() } |
|
||||
Where-Object { $_ -and -not $_.StartsWith('#') })
|
||||
$CompileSources = [Collections.Generic.List[string]]::new()
|
||||
foreach ($SourceName in $Sources) {
|
||||
$Source = [IO.Path]::GetFullPath((Join-Path $Standalone $SourceName))
|
||||
if (-not $Source.StartsWith($Standalone, [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "Core source escapes Standalone: $SourceName"
|
||||
}
|
||||
if ($SourceName.Replace('\','/') -eq 'Core/src/VoxelForgeCore.cpp') {
|
||||
$CompileSources.Add($Source)
|
||||
continue
|
||||
}
|
||||
$WrapperName = 'gpu_vfcore_' + ($SourceName.Replace('\','/') -replace '[^A-Za-z0-9]+','_')
|
||||
$WrapperPath = Join-Path $WrapperDir ($WrapperName + '.cpp')
|
||||
$SourceInclude = $Source.Replace('\','/')
|
||||
$WrapperText = @"
|
||||
#include "CoreNamespacePreamble.h"
|
||||
namespace vfcore::detail
|
||||
{
|
||||
#include "$SourceInclude"
|
||||
}
|
||||
"@
|
||||
[IO.File]::WriteAllText($WrapperPath, $WrapperText, [Text.UTF8Encoding]::new($false))
|
||||
$CompileSources.Add($WrapperPath)
|
||||
}
|
||||
|
||||
$IncludeArgs = @(
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Core\include')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Core\src')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Compat')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source\VoxelForge\Public')),
|
||||
('/I"{0}"' -f (Join-Path $Standalone 'Source\VoxelForge\Private'))
|
||||
)
|
||||
$Common = @('/nologo','/std:c++20','/O2','/arch:AVX2','/fp:precise','/EHsc','/MP8','/D_CRT_SECURE_NO_WARNINGS') + $IncludeArgs
|
||||
$CoreRsp = Join-Path $BuildDir 'gpu_core_compile.rsp'
|
||||
[IO.File]::WriteAllLines($CoreRsp, $Common + @('/c') + @($CompileSources | ForEach-Object { '"{0}"' -f $_ }), [Text.UTF8Encoding]::new($false))
|
||||
$ObjectNames = @($CompileSources | ForEach-Object { '"{0}.obj"' -f [IO.Path]::GetFileNameWithoutExtension($_) })
|
||||
$LibRsp = Join-Path $BuildDir 'gpu_core_library.rsp'
|
||||
[IO.File]::WriteAllLines($LibRsp, @('/nologo',('/OUT:"{0}"' -f $CoreLib)) + $ObjectNames, [Text.UTF8Encoding]::new($false))
|
||||
$RunnerRsp = Join-Path $BuildDir 'gpu_runner_link.rsp'
|
||||
$Runner = Join-Path $PSScriptRoot 'GpuFirstPort.cpp'
|
||||
$RunnerRspLines = $Common + @(
|
||||
('/Fe"{0}"' -f $Output),
|
||||
('"{0}"' -f $Runner),
|
||||
('"{0}"' -f $CoreLib),
|
||||
'/link','d3d11.lib','dxgi.lib','d3dcompiler.lib','user32.lib','gdi32.lib','psapi.lib'
|
||||
)
|
||||
[IO.File]::WriteAllLines($RunnerRsp, $RunnerRspLines, [Text.UTF8Encoding]::new($false))
|
||||
|
||||
Push-Location $BuildDir
|
||||
try {
|
||||
$BuildCommands = [Collections.Generic.List[string]]::new()
|
||||
if (-not $SkipCore) {
|
||||
$BuildCommands.Add(('call "{0}" >nul && cl.exe @"{1}"' -f $VcVars, $CoreRsp))
|
||||
$BuildCommands.Add(('call "{0}" >nul && lib.exe @"{1}"' -f $VcVars, $LibRsp))
|
||||
}
|
||||
$BuildCommands.Add(('call "{0}" >nul && cl.exe @"{1}"' -f $VcVars, $RunnerRsp))
|
||||
foreach ($BuildCommand in $BuildCommands) {
|
||||
$attempt = 0
|
||||
do {
|
||||
& $env:ComSpec /d /s /c $BuildCommand
|
||||
$code = $LASTEXITCODE
|
||||
if ($code -eq -1073741502 -and $attempt -eq 0) {
|
||||
Write-Host '0xC0000142 encountered; waiting 60 seconds before the single permitted retry.'
|
||||
Start-Sleep -Seconds 60
|
||||
$attempt++
|
||||
continue
|
||||
}
|
||||
if ($code -eq -1073741502) { throw '0xC0000142 repeated after the single retry; stopping.' }
|
||||
if ($code -ne 0) { throw "GPU port build command failed with exit code $code" }
|
||||
break
|
||||
} while ($true)
|
||||
}
|
||||
if (-not $SkipCore) {
|
||||
& (Join-Path $Standalone 'Core\CheckCoreSymbols.ps1') -LibraryPath $CoreLib
|
||||
if ($LASTEXITCODE -ne 0) { throw "GPU port Core symbol check failed with exit code $LASTEXITCODE" }
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $Output -PathType Leaf)) { throw "GPU runner missing: $Output" }
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
Write-Host "Built $CoreLib"
|
||||
Write-Host "Built $Output"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,50 @@
|
||||
# VoxelForge GPU first port
|
||||
|
||||
## Status
|
||||
|
||||
This is a passing **GPU meshing prototype**, not the complete field-to-view GPU port. It takes the owner world’s field samples from the CPU, then classifies cells, scans output offsets, and emits render buffers with D3D11 compute shaders. The owner-specific shape-distance and relief evaluation still runs on the CPU. The viewer, full-view comparison, chunk-crossing GPU timing, and six GPU captures are not integrated.
|
||||
|
||||
The CPU implementation files were not changed. `HEAD` remained `566d1f3`. The new shared header is used by this harness and the HLSL, but is not included by the canonical Core implementation, preserving its existing output path.
|
||||
|
||||
## What runs on each side
|
||||
|
||||
- The CPU compiles the world and decides tile keys. For each tested tile it supplies the exact packed air/rock sign words, lattice density samples, and the LOD0 analytic edge-midpoint values selected by the CPU mesher’s root rule. The GPU never chooses a sign or feature topology.
|
||||
- `ClassifyCS` builds marching-cubes cases from the sign words. `ScanGroupsCS` creates deterministic compact output offsets and a five-word `DrawIndexedInstancedIndirect` record. `EmitCS` writes positions, gradient normals, UVs, and a sequential index buffer.
|
||||
- The vertex and index outputs are D3D11 raw buffers with vertex/index bind flags. The indirect-argument buffer has the D3D11 indirect-argument flag. The harness validates all three, then reads the results back for comparison; it does not issue a render draw.
|
||||
- [VoxelForgeFieldShared.h](../../../Core/src/VoxelForgeFieldShared.h) contains integer noise hashing, value noise, the relief sum, smooth-min helpers, and the shared root-refinement rule in a C++/HLSL-compatible subset. A 4,096-point probe was bit-identical between its C++ harness implementation, the RX 7900 XTX, and WARP. The owner’s actual `ShapeWorldInternal.h` field evaluator is not yet shared or called by this header. The probe therefore validates the shared helper source, not cross-checks against the canonical owner field.
|
||||
|
||||
The field split is consequently incomplete: the current GPU vertices interpolate CPU-computed density samples. GPU shape distances, partition-of-unity detail blending, owner roughness, and final field signs are future work. Collision remains on the CPU.
|
||||
|
||||
## Tile agreement and timings
|
||||
|
||||
Run: [first_run.log](first_run.log). The harness built the owner snapshot with the current three-ring clip (`2,226` desired tiles), then audited the nearest LOD0 and LOD1 tiles. Each tile contains 32³ cells and 42,875 lattice samples including the gradient halo.
|
||||
|
||||
| Tile | GPU dispatch (three kernels) | RX 7900 D3D wall (upload, dispatch, readback) | CPU field-input preparation | CPU `generateTile` reference | CPU/GPU topology differences | CPU/GPU mean / max vertex distance | GPU/WARP mean / max vertex distance |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| LOD0 `(-2,-15,-3)` | 0.049 ms | 3.538 ms | 3.712 ms | 2.329 ms | 0 | 4.52e-7 / 0.0001221 cm | 2.17e-7 / 0.0001221 cm |
|
||||
| LOD1 `(-1,-10,-2)` | 0.071 ms | 2.208 ms | 0.632 ms | 1.992 ms | 0 | 1.26e-6 / 0.0002442 cm | 2.45e-7 / 0.0002442 cm |
|
||||
|
||||
The vertex metric is the symmetric nearest-vertex distance between CPU surface vertices and GPU triangle-soup vertices; it is not a continuous-surface Hausdorff measurement. CPU output has 1,103 and 2,237 surface vertices; GPU output has 6,192 and 12,504 vertices respectively, with sequential indices. Each comparison covered all 32,768 cells. Both hardware and WARP had zero case differences. Running the RX 7900 path twice produced zero differing vertex-component bits, cases, or indirect arguments. GPU/WARP float components were not bit-identical (8,296 and 16,287 differing components), while their maximum nearest-vertex distances remained at most 0.0002442 cm.
|
||||
|
||||
The critical-air scan checked all 512 LOD0 keys in the compiled view and found no protected-air samples, so there was no route-critical tile to select. The two tested tiles had zero critical cells. Overall topology was identical for every cell tested because the CPU sign bits feed the integer classifier directly. The GPU harness did not separately locate and exercise a 1–2 voxel wall; the existing CPU whole-view audit reports zero thin sheets, but that is not a GPU thin-feature capture.
|
||||
|
||||
On the RX 7900, the actual compute work is below 0.1 ms per tested tile, so those kernels fit inside a 0.5–1 ms 90 Hz work slice. CPU input preparation alone took 0.63–3.71 ms, and the current upload/readback path took 2.21–3.54 ms wall. This prototype does not yet demonstrate a sliced field-to-render frame.
|
||||
|
||||
For context, the current CPU baseline report records a 0.992 s mean cold whole view for 2,226 tiles: [Round 9 report](../speed_round/round9/REPORT.md). The current CPU crossing report records 105 ms crossing wall and 478 ms aggregate worker CPU for crossing 1, including 128 LOD1 meshes: [chunk results](../../../Core/verification/chunk_fast_column_results.md). These are CPU reference results; this prototype did **not** measure a GPU whole view or GPU chunk crossing.
|
||||
|
||||
### GTX 1060-class estimate
|
||||
|
||||
The RX 7900 XTX measured 0.049–0.071 ms for the tested 32³ classify/scan/emit work. Scaling that measured interval by the nominal FP32 peak ratio (61.4 TFLOPS for the RX 7900 XTX and the owner-provided 4.4 TFLOPS for a GTX 1060-class card) gives about **0.7–1.0 ms per tested tile’s compute kernels**. This is a rough shader-only estimate, not a measured result or a whole-view estimate. The kernel is not purely FP32-bound, Pascal integer throughput and driver overhead differ, and the actual GPU field evaluator is not present; its cost is not included. AMD’s published RX 7900 XTX specification lists 61.4 TFLOPS: [AMD product specifications](https://www.amd.com/en/products/graphics/desktops/radeon/7000-series/amd-radeon-rx-7900xtx.html).
|
||||
|
||||
## Memory and build
|
||||
|
||||
Process peak working set was 428.65 MiB; the largest sampled private usage was 658.75 MiB. Requested D3D11 buffers, including readback staging, totaled 17.82–17.99 MiB per tile; only one tile’s buffers were live per dispatch. DXGI also returned adapter-wide local-memory usage and budget, but those counters include other applications and are not used as a per-process measurement. The observed process plus tile buffers stayed well below the 8 GiB limit.
|
||||
|
||||
The isolated build outputs are `Standalone\Build\VoxelForgeGpuFirstPort\VoxelForgeGpuPortCore.lib` and `Standalone\bin\VoxelForgeGpuFirstPort.exe`. The build used `/MP8`; the runner and build shell set BelowNormal priority and affinity `0xFF00`. The Core library’s Unreal-symbol check passed. No `0xC0000142` occurred. Build output retains the existing `vswhere.exe` setup diagnostic and a `TEXT` macro redefinition warning from the compatibility header.
|
||||
|
||||
## Remaining work for the requested port
|
||||
|
||||
1. Export exact CPU layout and decision data into compact GPU tile records, then move shared shape-distance, relief, and field evaluation into the common C++/HLSL source without changing CPU hashes.
|
||||
2. Keep CPU-owned sign and protected-feature masks authoritative while evaluating the smooth field on GPU; add a GPU audit tile containing real route-critical and thin-feature samples.
|
||||
3. Integrate the produced buffers and indirect draw record into the viewer/render graph, then capture and compare the six fixed views.
|
||||
4. Measure a cold GPU whole view and a new-slab chunk crossing with GPU timestamps plus full wall time, then measure sliced multi-tile work. The current per-tile numbers do not establish a whole-view speedup.
|
||||
@@ -0,0 +1,244 @@
|
||||
#define VF_FIELD_HLSL 1
|
||||
#include "../../../Core/src/VoxelForgeFieldShared.h"
|
||||
|
||||
struct MidpointRecord
|
||||
{
|
||||
uint4 metadata;
|
||||
float4 values[3];
|
||||
};
|
||||
|
||||
struct GpuVertex
|
||||
{
|
||||
float3 position;
|
||||
float3 normal;
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
cbuffer TileConstants : register(b0)
|
||||
{
|
||||
uint CellsPerAxis;
|
||||
uint SampleDim;
|
||||
uint CellCount;
|
||||
uint GroupCount;
|
||||
int OriginX;
|
||||
int OriginY;
|
||||
int OriginZ;
|
||||
int StepVoxels;
|
||||
float VoxelSizeCm;
|
||||
float3 ConstantPad;
|
||||
};
|
||||
|
||||
static const int3 CornerOffset[8] = {
|
||||
int3(0,0,0), int3(1,0,0), int3(1,1,0), int3(0,1,0),
|
||||
int3(0,0,1), int3(1,0,1), int3(1,1,1), int3(0,1,1)
|
||||
};
|
||||
|
||||
static const int2 EdgeCorners[12] = {
|
||||
int2(0,1), int2(1,2), int2(2,3), int2(3,0),
|
||||
int2(4,5), int2(5,6), int2(6,7), int2(7,4),
|
||||
int2(0,4), int2(1,5), int2(2,6), int2(3,7)
|
||||
};
|
||||
|
||||
uint CellIndex(uint3 c)
|
||||
{
|
||||
return (c.z * CellsPerAxis + c.y) * CellsPerAxis + c.x;
|
||||
}
|
||||
|
||||
uint SampleIndex(int3 p)
|
||||
{
|
||||
uint3 q = uint3(p + 1);
|
||||
return (q.z * SampleDim + q.y) * SampleDim + q.x;
|
||||
}
|
||||
|
||||
uint LatticeSign(StructuredBuffer<uint> signs, int3 p)
|
||||
{
|
||||
uint i = SampleIndex(p);
|
||||
return (signs[i >> 5] >> (i & 31)) & 1;
|
||||
}
|
||||
|
||||
float LatticeValue(StructuredBuffer<float> density, int3 p)
|
||||
{
|
||||
return density[SampleIndex(p)];
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
float3 EdgePosition(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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
const float magnitude = length(g);
|
||||
return magnitude > 1.0e-20f ? g / magnitude : float3(0,0,1);
|
||||
}
|
||||
|
||||
StructuredBuffer<uint> Signs : register(t0);
|
||||
StructuredBuffer<int> TriTable : register(t1);
|
||||
RWStructuredBuffer<uint> CellCases : register(u0);
|
||||
RWStructuredBuffer<uint> CellLocalOffsets : register(u1);
|
||||
RWStructuredBuffer<uint> GroupVertexCounts : register(u2);
|
||||
|
||||
groupshared uint ScanValues[256];
|
||||
|
||||
[numthreads(256,1,1)]
|
||||
void ClassifyCS(uint3 dispatchId : SV_DispatchThreadID,
|
||||
uint3 groupId : SV_GroupID,
|
||||
uint3 groupThreadId : SV_GroupThreadID)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (TriTable[caseIndex * 16 + slot] < 0) break;
|
||||
++vertexCount;
|
||||
}
|
||||
CellCases[index] = caseIndex;
|
||||
}
|
||||
ScanValues[groupThreadId.x] = vertexCount;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
[unroll]
|
||||
for (uint offset = 1; offset < 256; offset <<= 1)
|
||||
{
|
||||
const uint add = groupThreadId.x >= offset ? ScanValues[groupThreadId.x - offset] : 0;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
ScanValues[groupThreadId.x] += add;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
}
|
||||
if (index < CellCount)
|
||||
CellLocalOffsets[index] = ScanValues[groupThreadId.x] - vertexCount;
|
||||
if (groupThreadId.x == 255)
|
||||
GroupVertexCounts[groupId.x] = ScanValues[255];
|
||||
}
|
||||
|
||||
StructuredBuffer<uint> InputGroupVertexCounts : register(t8);
|
||||
RWStructuredBuffer<uint> GroupOffsets : register(u3);
|
||||
RWByteAddressBuffer DrawArgs : register(u4);
|
||||
|
||||
[numthreads(1,1,1)]
|
||||
void ScanGroupsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
uint offset = 0;
|
||||
for (uint i = 0; i < GroupCount; ++i)
|
||||
{
|
||||
GroupOffsets[i] = offset;
|
||||
offset += InputGroupVertexCounts[i];
|
||||
}
|
||||
DrawArgs.Store(0, offset);
|
||||
DrawArgs.Store(4, 1);
|
||||
DrawArgs.Store(8, 0);
|
||||
DrawArgs.Store(12, 0);
|
||||
DrawArgs.Store(16, 0);
|
||||
}
|
||||
|
||||
StructuredBuffer<float> Density : register(t2);
|
||||
StructuredBuffer<uint> Cases : register(t3);
|
||||
StructuredBuffer<uint> LocalOffsets : register(t4);
|
||||
StructuredBuffer<uint> PrefixGroups : register(t5);
|
||||
StructuredBuffer<int> Triangles : register(t1);
|
||||
StructuredBuffer<uint> MidpointCellIds : register(t6);
|
||||
StructuredBuffer<MidpointRecord> MidpointData : register(t7);
|
||||
RWByteAddressBuffer VertexBytes : register(u5);
|
||||
RWByteAddressBuffer IndexBytes : register(u6);
|
||||
|
||||
[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 base = LocalOffsets[index] + PrefixGroups[index / 256];
|
||||
const uint3 cell = uint3(index % CellsPerAxis,
|
||||
(index / CellsPerAxis) % CellsPerAxis,
|
||||
index / (CellsPerAxis * CellsPerAxis));
|
||||
[unroll]
|
||||
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;
|
||||
const uint edge = uint(edgeValue);
|
||||
GpuVertex v;
|
||||
v.position = EdgePosition(int3(cell), edge, Density, MidpointCellIds, MidpointData);
|
||||
v.normal = EdgeNormal(int3(cell), edge, Density, MidpointCellIds, MidpointData);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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; };
|
||||
StructuredBuffer<NoiseProbeInput> NoiseInputs : register(t9);
|
||||
RWStructuredBuffer<float> NoiseOutputs : register(u6);
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void NoiseProbeCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
const NoiseProbeInput input = NoiseInputs[dispatchId.x];
|
||||
NoiseOutputs[dispatchId.x] = VF_FieldSurfaceRelief(
|
||||
input.position.x, input.position.y, input.position.z, input.seed);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
[GpuProcessMemory] stage=after_snapshot working_set_mb=30.75 peak_working_set_mb=35.45 private_mb=22.21
|
||||
[GridStairDistanceRepair] exact_endpoint_proxies=34 occupancy_corrections=2
|
||||
[Walkability] PASS 5642/5642 passage centerline samples remain open; native landing floors retained
|
||||
[FarParents] pitch=2048 rooms=6050 links=18401 clip=(-57344,-57344)..(49152,49152)
|
||||
[FarShapeStage] parent_shapes=9.145 ms spatial_bins=35.649 ms total=44.794 ms primitives=18401
|
||||
[ShapeProfile] local_primitives=37.698ms line_bins=3.984ms rock_lines_wall=21.393ms rock_lines_worker=140.496ms candidate_axes_wall=50.826ms candidate_axes_worker=355.792ms candidate_merge_wall=14.305ms candidate_merge_worker=54.843ms candidate_merge_serial=1.372ms detail_lines_wall=216.813ms detail_lines_worker=1709.270ms
|
||||
[ShapeAllocation] words=36.71MiB candidate_axis_words=36.71MiB proxy=263.57MiB primitives=11.25MiB spatial_bins=422754 spatial_bin_refs=679503 spatial_bin_capacity=1810768 inline_bins=392944 overflow_bins=29810 far_primitive_build=9.145ms far_bin_rebuild=35.649ms
|
||||
[ShapeCompile] primitives=18401 rooms=77 tunnels=84 passages=2 nodes=138188413 packed=36.71 MiB surface_proxy_nodes=6755507 proxy_address_capacity=263.57 MiB workers=8
|
||||
[ShapeStage] local-shapes=41.704 ms intervals+signs=95.925 ms surface-band=232.287 ms far-parents=44.794 ms total=419.984 ms candidates=9389017 flips=392124 walk=5642/5642
|
||||
[RoomFloorJoinAudit] automatic_room_overlaps=10 thin_positive_support_overlays=0 status=PASS
|
||||
[FloorSlopeJoinAudit] raw_unmarked_max_delta=4.43634 vox generated_tangent_delta=0.00000 vox bend_direction_slope_change=0.80702 vox marked_max_delta=0.00000 vox marked_joins=0 threshold=0.750 status=PASS
|
||||
[FloorProfileBlend] c1_segments=374 max_curve_deviation=0.993 vox threshold=2.000 status=PASS
|
||||
[TunnelRadiusAudit] shared_joins=280 legacy_fixed_segment_max_step=2.9156 vox smoothed_profile_max_step=1.7487 vox tapered_endpoint_max_delta=0.000000 vox radius_slope_join_delta=0.00000000 floor_endpoint_max_delta=0.000000 vox floor_slope_join_delta=0.00000 unmarked_floor_slope_delta=0.00000 marked_floor_slope_delta=0.00000 marked_slope_joins=0 smooth_union_growth_bound=0.2500 vox thresholds=(0.005,0.000001,0.005,0.25) status=PASS
|
||||
[TunnelBendAudit] sharpest_bend=47.29 deg centerline=(611.64,72.10,-213.54) vox tangent=(-0.8106,-0.4443,0.3814) camera_yaw=-118.73 camera_pitch=22.42
|
||||
[GpuPortWorld] desired_tiles=2226 camera_vox=(-46.450,-472.885,-79.381) full_resolution_lods=2 coarse_cells=16 shape_compile_ms=420.016 shape_stats_ms=(41.704,95.925,232.287,44.794) memory_limit_mb=8192
|
||||
[GpuProcessMemory] stage=after_shape_compile working_set_mb=245.78 peak_working_set_mb=245.78 private_mb=409.59
|
||||
[GpuDevices] hardware="AMD Radeon RX 7900 XTX" warp=Microsoft_WARP feature_level=11_0
|
||||
[SharedFieldMath] probes=4096 cpu_gpu_mean_abs=0 cpu_gpu_max_abs=0 cpu_gpu_bit_differences=0 repeat_max_abs=0 repeat_bit_differences=0 warp_mean_abs=0 warp_max_abs=0 warp_bit_differences=0 hash_integer_source=shared
|
||||
[RouteCriticalSearch] lod0_tiles=512 result=no_protected_air_samples_found
|
||||
[GpuTile] key=(-2,-15,-3) lod=0 cells=32 source_samples=42875 critical_lattice=0 critical_cells=0 cpu_vertices=1103 gpu_vertices=6192 warp_vertices=6192 expected_gpu_vertices=6192 gpu_indices_sequential=1 warp_indices_sequential=1 gpu_indexed_indirect_args=1 warp_indexed_indirect_args=1 d3d_buffer_mb=17.815 gpu_adapter_memory_mb=(11.56,12.56) budget_mb=23748.98 warp_adapter_memory_info=1 gpu_dispatch_ms=0.0490 gpu_wall_ms=3.5378 repeat_dispatch_ms=0.0447 warp_dispatch_ms=65.4443 warp_wall_ms=80.8484
|
||||
[GpuAgreement] tile=(-2,-15,-3,0) topology_diff_cells_hw=0 topology_diff_cells_warp=0 route_critical_cells_audited=0 route_critical_diff_cells_hw=0 route_critical_diff_cells_warp=0 thin_feature_diff_cells_hw=0 vertex_mean_cm=4.5180239e-07 vertex_max_cm=0.00012207031 vertex_max_pair_cm=(-1075.00000,-11225.00000,-1857.88184)->(-1075.00000,-11225.00000,-1857.88171) warp_vertex_mean_cm=2.1685618e-07 warp_vertex_max_cm=0.00012207031 repeat_vertex_component_bit_differences=0 repeat_case_differences=0 repeat_draw_arg_differences=0 repeated_gpu_outputs_identical=1 warp_component_bit_differences=8296 output_count_match=1 status=PASS
|
||||
[GpuPipelineWall] key=(-2,-15,-3) lod=0 cpu_field_input_prep_ms=3.7120 hardware_total_ms=7.2498 warp_total_ms=84.5604 (GPU wall includes input upload and output readback)
|
||||
[CpuTileReference] key=(-2,-15,-3) lod=0 wall_ms=2.3287 mode=single_generateTile_api cpu_surface_vertices=1103 cpu_indices=7872
|
||||
[GpuProcessMemory] stage=after_tile_pair working_set_mb=404.26 peak_working_set_mb=407.15 private_mb=619.71
|
||||
[GpuTile] key=(-1,-10,-2) lod=1 cells=32 source_samples=42875 critical_lattice=0 critical_cells=0 cpu_vertices=2237 gpu_vertices=12504 warp_vertices=12504 expected_gpu_vertices=12504 gpu_indices_sequential=1 warp_indices_sequential=1 gpu_indexed_indirect_args=1 warp_indexed_indirect_args=1 d3d_buffer_mb=17.990 gpu_adapter_memory_mb=(31.56,31.56) budget_mb=23748.98 warp_adapter_memory_info=1 gpu_dispatch_ms=0.0711 gpu_wall_ms=2.2075 repeat_dispatch_ms=0.0440 warp_dispatch_ms=64.7658 warp_wall_ms=84.1260
|
||||
[GpuAgreement] tile=(-1,-10,-2,1) topology_diff_cells_hw=0 topology_diff_cells_warp=0 route_critical_cells_audited=0 route_critical_diff_cells_hw=0 route_critical_diff_cells_warp=0 thin_feature_diff_cells_hw=0 vertex_mean_cm=1.2568044e-06 vertex_max_cm=0.00024414062 vertex_max_pair_cm=(-1250.00000,-15850.00000,-2761.93750)->(-1250.00000,-15850.00000,-2761.93726) warp_vertex_mean_cm=2.4474895e-07 warp_vertex_max_cm=0.00024414062 repeat_vertex_component_bit_differences=0 repeat_case_differences=0 repeat_draw_arg_differences=0 repeated_gpu_outputs_identical=1 warp_component_bit_differences=16287 output_count_match=1 status=PASS
|
||||
[GpuPipelineWall] key=(-1,-10,-2) lod=1 cpu_field_input_prep_ms=0.6316 hardware_total_ms=2.8391 warp_total_ms=84.7576 (GPU wall includes input upload and output readback)
|
||||
[CpuTileReference] key=(-1,-10,-2) lod=1 wall_ms=1.9918 mode=single_generateTile_api cpu_surface_vertices=2237 cpu_indices=16128
|
||||
[GpuProcessMemory] stage=after_tile_pair working_set_mb=426.37 peak_working_set_mb=428.65 private_mb=658.75
|
||||
[GpuPortStatus] subset_status=PASS overall_port=INCOMPLETE work=one_nearby_lod0_and_lod1_tile field=cpu_exact_lattice_plus_cpu_midpoint_records meshing=gpu_compute_vertex_index_buffers viewer_draw=not_integrated captures=not_integrated
|
||||
Reference in new issue
Block a user