wip(standalone): GPU step 5 -- far GPU work 646 -> 112 ms; hybrid whole view equal to CPU
Whole far field on the RX 7900 XTX: 112 ms total (connector rings 61 ms). Cold hybrid 2.303 s vs all-CPU 2.314 s (within noise): the far field is no longer on the wall-clock critical path. 0 sign/topology differences vs CPU and WARP incl. 127,401 protected and 353 thin-feature cells; repeat GPU runs identical; far survival passes on GPU meshes; CPU hashes unchanged. Open: CPU/GPU vertex distance mean 1.15 mm but max 64.56 cm, 247 tiles over the quantisation bound. 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
247682a8e0
commit
0e730cdbba
9 files changed
+697
-260
No files matched your search
@@ -135,11 +135,12 @@ namespace
|
||||
struct FarFieldConstants
|
||||
{
|
||||
float strataLo, strataHi, strataBlend;
|
||||
std::uint32_t sampleCount;
|
||||
std::uint32_t sampleCount, evalCounterEnabled, spatialBinMask, spatialBinSize,
|
||||
spatialBinCapacity;
|
||||
};
|
||||
static_assert(sizeof(FarFieldConstants) == 16);
|
||||
static_assert(sizeof(FarFieldConstants) == 32);
|
||||
static_assert(sizeof(FarFieldPrimitiveGpu) == 224);
|
||||
static_assert(sizeof(FarFieldSampleRange) == 8);
|
||||
static_assert(sizeof(FarFieldSpatialBin) == 24);
|
||||
static_assert(sizeof(FarFieldStratumBoundary) == 8);
|
||||
|
||||
struct NoiseProbeInput
|
||||
@@ -190,6 +191,9 @@ namespace
|
||||
std::uint64_t d3dBufferBytes = 0;
|
||||
double dispatchMs = 0.0;
|
||||
double fieldMs = 0.0;
|
||||
std::array<double,6> kernelMs{};
|
||||
std::uint64_t fieldDistanceEvaluations=0;
|
||||
std::uint32_t maxFieldDistanceEvaluationsPerSample=0;
|
||||
double wallMs = 0.0;
|
||||
};
|
||||
|
||||
@@ -505,6 +509,11 @@ namespace
|
||||
DynamicInput connectors;
|
||||
std::uint32_t connectorRingCapacity=0;
|
||||
} farScratch;
|
||||
struct FarWorldResources
|
||||
{
|
||||
std::shared_ptr<const FarFieldWorldInput> owner;
|
||||
DynamicInput shapes,bins,shapeIds;
|
||||
} farWorldResources;
|
||||
ComPtr<ID3D11Device> device;
|
||||
ComPtr<ID3D11DeviceContext> context;
|
||||
ComPtr<ID3D11DeviceContext> immediateContext,deferredContext;
|
||||
@@ -800,6 +809,19 @@ namespace
|
||||
return input.srv;
|
||||
}
|
||||
|
||||
void EnsureFarWorldInput(const std::shared_ptr<const FarFieldWorldInput>& input)
|
||||
{
|
||||
if(!input)throw std::runtime_error("far tile does not reference shared world GPU data");
|
||||
if(farWorldResources.owner.get()==input.get())return;
|
||||
UpdateDynamicInput(farWorldResources.shapes,input->shapes.data(),input->shapes.size(),
|
||||
"Map(world far shapes)");
|
||||
UpdateDynamicInput(farWorldResources.bins,input->spatialBins.data(),input->spatialBins.size(),
|
||||
"Map(world far spatial bins)");
|
||||
UpdateDynamicInput(farWorldResources.shapeIds,input->spatialBinShapeIds.data(),
|
||||
input->spatialBinShapeIds.size(),"Map(world far bin shape IDs)");
|
||||
farWorldResources.owner=input;
|
||||
}
|
||||
|
||||
void EnsureFarScratch(const TileData& tile)
|
||||
{
|
||||
const std::uint32_t cells=tile.cells*tile.cells*tile.cells;
|
||||
@@ -902,7 +924,8 @@ namespace
|
||||
bool readback = true, bool retainOutputBuffers = false,
|
||||
bool waitForGpu = true, bool useIndirectOutputCount = false,
|
||||
std::uint32_t outputSlot = 0, bool usePooledFarOutput = false,
|
||||
bool captureGpuTiming = true,bool batchTimingMode=false)
|
||||
bool captureGpuTiming = true,bool batchTimingMode=false,
|
||||
bool profileGpuKernels=false)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
const auto wallStart = Clock::now();
|
||||
@@ -939,6 +962,7 @@ namespace
|
||||
|| farField->cells != static_cast<std::int32_t>(tile.cells)
|
||||
|| farField->step != static_cast<std::int32_t>(tile.step)))
|
||||
throw std::runtime_error("far field upload dimensions do not match the tile");
|
||||
if(farField)EnsureFarWorldInput(farField->worldInput);
|
||||
result.d3dBufferBytes = tile.signs.size() * sizeof(std::uint32_t)
|
||||
+ triCount * sizeof(std::int32_t)
|
||||
+ tile.density.size() * sizeof(float)
|
||||
@@ -951,11 +975,9 @@ namespace
|
||||
+ maxVertices * sizeof(std::uint32_t)
|
||||
+ sizeof(TileConstants);
|
||||
if (farField)
|
||||
result.d3dBufferBytes += std::max<std::size_t>(1, farField->shapes.size()) * sizeof(FarFieldPrimitiveGpu)
|
||||
+ farField->sampleShapeRanges.size() * sizeof(FarFieldSampleRange)
|
||||
+ std::max<std::size_t>(1, farField->sampleShapeIds.size()) * sizeof(std::uint32_t)
|
||||
+ farField->protectedAir.size() * sizeof(std::uint32_t)
|
||||
result.d3dBufferBytes += farField->protectedAir.size() * sizeof(std::uint32_t)
|
||||
+ farField->protectedSolid.size() * sizeof(std::uint32_t)
|
||||
+ farField->connectorPanels.size() * sizeof(FarFieldConnectorPanel)
|
||||
+ tile.density.size() * sizeof(float)
|
||||
+ sizeof(FarFieldConstants);
|
||||
if (readback)
|
||||
@@ -974,6 +996,15 @@ namespace
|
||||
ComPtr<ID3D11UnorderedAccessView> farConnectorRingUav;
|
||||
ComPtr<ID3D11Buffer> farConnectorConstantsBuffer;
|
||||
ComPtr<ID3D11Buffer> farConnectorRingBuffer;
|
||||
ComPtr<ID3D11Buffer> farEvalCounters,farEvalCounterStaging;
|
||||
ComPtr<ID3D11UnorderedAccessView> farEvalCounterUav;
|
||||
if(farField&&profileGpuKernels)
|
||||
{
|
||||
farEvalCounters=Structured<std::uint32_t>(nullptr,tile.density.size(),
|
||||
D3D11_BIND_UNORDERED_ACCESS);
|
||||
farEvalCounterUav=Uav(farEvalCounters.Get(),static_cast<UINT>(tile.density.size()));
|
||||
farEvalCounterStaging=Staging(static_cast<UINT>(tile.density.size()*sizeof(std::uint32_t)));
|
||||
}
|
||||
if(pooledChunk)
|
||||
{
|
||||
EnsureFarScratch(tile);
|
||||
@@ -989,12 +1020,9 @@ namespace
|
||||
densityUav=scratch.densityUav;caseUav=scratch.caseUav;
|
||||
localOffsetUav=scratch.localOffsetUav;groupCountUav=scratch.groupCountUav;
|
||||
groupOffsetUav=scratch.groupOffsetUav;
|
||||
farShapeSrv=UpdateDynamicInput(scratch.shapes,
|
||||
farField->shapes.empty()?nullptr:farField->shapes.data(),farField->shapes.size(),"Map(far shapes)");
|
||||
farRangeSrv=UpdateDynamicInput(scratch.ranges,
|
||||
farField->sampleShapeRanges.data(),farField->sampleShapeRanges.size(),"Map(far ranges)");
|
||||
farIdSrv=UpdateDynamicInput(scratch.ids,
|
||||
farField->sampleShapeIds.empty()?nullptr:farField->sampleShapeIds.data(),farField->sampleShapeIds.size(),"Map(far shape ids)");
|
||||
farShapeSrv=farWorldResources.shapes.srv;
|
||||
farRangeSrv=farWorldResources.bins.srv;
|
||||
farIdSrv=farWorldResources.shapeIds.srv;
|
||||
farProtectedSrv=UpdateDynamicInput(scratch.protectedAir,
|
||||
farField->protectedAir.data(),farField->protectedAir.size(),"Map(far protected air)");
|
||||
farProtectedSolidSrv=UpdateDynamicInput(scratch.protectedSolid,
|
||||
@@ -1035,21 +1063,14 @@ namespace
|
||||
densityUav=farField?Uav(density.Get(),tile.density.size()):ComPtr<ID3D11UnorderedAccessView>{};
|
||||
if(farField)
|
||||
{
|
||||
const FarFieldPrimitiveGpu zeroShape{};const FarFieldSampleRange zeroRange{};
|
||||
const std::uint32_t zeroId=0,zeroWord=0;
|
||||
auto farShapes=Structured(farField->shapes.empty()?&zeroShape:farField->shapes.data(),
|
||||
std::max<std::size_t>(1,farField->shapes.size()),D3D11_BIND_SHADER_RESOURCE);
|
||||
auto farRanges=Structured(farField->sampleShapeRanges.empty()?&zeroRange:farField->sampleShapeRanges.data(),
|
||||
std::max<std::size_t>(1,farField->sampleShapeRanges.size()),D3D11_BIND_SHADER_RESOURCE);
|
||||
auto farIds=Structured(farField->sampleShapeIds.empty()?&zeroId:farField->sampleShapeIds.data(),
|
||||
std::max<std::size_t>(1,farField->sampleShapeIds.size()),D3D11_BIND_SHADER_RESOURCE);
|
||||
const std::uint32_t zeroWord=0;
|
||||
auto farProtected=Structured(farField->protectedAir.empty()?&zeroWord:farField->protectedAir.data(),
|
||||
std::max<std::size_t>(1,farField->protectedAir.size()),D3D11_BIND_SHADER_RESOURCE);
|
||||
auto farProtectedSolid=Structured(farField->protectedSolid.empty()?&zeroWord:farField->protectedSolid.data(),
|
||||
std::max<std::size_t>(1,farField->protectedSolid.size()),D3D11_BIND_SHADER_RESOURCE);
|
||||
farShapeSrv=Srv<FarFieldPrimitiveGpu>(farShapes.Get(),std::max<std::size_t>(1,farField->shapes.size()));
|
||||
farRangeSrv=Srv<FarFieldSampleRange>(farRanges.Get(),std::max<std::size_t>(1,farField->sampleShapeRanges.size()));
|
||||
farIdSrv=Srv<std::uint32_t>(farIds.Get(),std::max<std::size_t>(1,farField->sampleShapeIds.size()));
|
||||
farShapeSrv=farWorldResources.shapes.srv;
|
||||
farRangeSrv=farWorldResources.bins.srv;
|
||||
farIdSrv=farWorldResources.shapeIds.srv;
|
||||
farProtectedSrv=Srv<std::uint32_t>(farProtected.Get(),std::max<std::size_t>(1,farField->protectedAir.size()));
|
||||
farProtectedSolidSrv=Srv<std::uint32_t>(farProtectedSolid.Get(),std::max<std::size_t>(1,farField->protectedSolid.size()));
|
||||
const FarFieldConnectorPanel zeroPanel{};
|
||||
@@ -1112,11 +1133,14 @@ namespace
|
||||
if (farField)
|
||||
{
|
||||
const FarFieldConstants farConstants{farField->strataLo,farField->strataHi,
|
||||
farField->strataBlend,static_cast<std::uint32_t>(tile.density.size())};
|
||||
farField->strataBlend,static_cast<std::uint32_t>(tile.density.size()),
|
||||
profileGpuKernels?1u:0u,farField->worldInput->spatialBinMask,
|
||||
farField->worldInput->spatialBinSize,
|
||||
static_cast<std::uint32_t>(farField->worldInput->spatialBins.size())};
|
||||
const FarConnectorConstants connectorConstants{
|
||||
static_cast<std::uint32_t>(farField->connectorPanels.size()),
|
||||
farField->connectorRingCount,farField->connectorVertexCount,connectorPanelCapacity,
|
||||
static_cast<std::uint32_t>(farField->shapes.size()),{0,0,0}};
|
||||
static_cast<std::uint32_t>(farField->worldInput->shapes.size()),{0,0,0}};
|
||||
if(pooledChunk)
|
||||
{
|
||||
cb=farScratch.tileConstants;farCb=farScratch.farConstants;
|
||||
@@ -1158,6 +1182,7 @@ namespace
|
||||
D3D11_QUERY_DESC disjointDesc{D3D11_QUERY_TIMESTAMP_DISJOINT, 0};
|
||||
ComPtr<ID3D11Query> startQuery, endQuery, disjointQuery;
|
||||
ComPtr<ID3D11Query> fieldStartQuery,fieldEndQuery;
|
||||
std::array<ComPtr<ID3D11Query>,6> kernelStartQueries,kernelEndQueries;
|
||||
if(captureGpuTiming)
|
||||
{
|
||||
Check(device->CreateQuery(×tampDesc, &startQuery), "CreateQuery(start)");
|
||||
@@ -1169,8 +1194,21 @@ namespace
|
||||
Check(device->CreateQuery(×tampDesc,&fieldStartQuery),"CreateQuery(field start)");
|
||||
Check(device->CreateQuery(×tampDesc,&fieldEndQuery),"CreateQuery(field end)");
|
||||
}
|
||||
if(farField&&profileGpuKernels)
|
||||
for(std::size_t stage=0;stage<kernelStartQueries.size();++stage)
|
||||
{
|
||||
Check(device->CreateQuery(×tampDesc,&kernelStartQueries[stage]),
|
||||
"CreateQuery(kernel stage start)");
|
||||
Check(device->CreateQuery(×tampDesc,&kernelEndQueries[stage]),
|
||||
"CreateQuery(kernel stage end)");
|
||||
}
|
||||
}
|
||||
|
||||
const auto beginKernel=[&](std::size_t stage)
|
||||
{if(profileGpuKernels)context->End(kernelStartQueries[stage].Get());};
|
||||
const auto endKernel=[&](std::size_t stage)
|
||||
{if(profileGpuKernels)context->End(kernelEndQueries[stage].Get());};
|
||||
|
||||
context->CSSetConstantBuffers(0, 1, cb.GetAddressOf());
|
||||
if(captureGpuTiming&&!batchTimingMode)
|
||||
{
|
||||
@@ -1181,22 +1219,27 @@ namespace
|
||||
if(farField)
|
||||
{
|
||||
if(captureGpuTiming&&!batchTimingMode)context->End(fieldStartQuery.Get());
|
||||
beginKernel(0);
|
||||
context->CSSetConstantBuffers(1,1,farCb.GetAddressOf());
|
||||
context->CSSetConstantBuffers(2,1,farConnectorConstantsBuffer.GetAddressOf());
|
||||
context->CSSetShader(fieldEval.Get(),nullptr,0);
|
||||
ID3D11ShaderResourceView* fieldSrvs[15]{};
|
||||
fieldSrvs[10]=farShapeSrv.Get();fieldSrvs[11]=farRangeSrv.Get();
|
||||
fieldSrvs[12]=farIdSrv.Get();fieldSrvs[13]=farProtectedSrv.Get();
|
||||
fieldSrvs[14]=farProtectedSolidSrv.Get();
|
||||
context->CSSetShaderResources(0,15,fieldSrvs);
|
||||
ID3D11UnorderedAccessView* fieldUavs[8]{};fieldUavs[7]=densityUav.Get();
|
||||
ID3D11UnorderedAccessView* fieldUavs[8]{};fieldUavs[6]=farEvalCounterUav.Get();
|
||||
fieldUavs[7]=densityUav.Get();
|
||||
context->CSSetUnorderedAccessViews(0,8,fieldUavs,nullptr);
|
||||
context->Dispatch(static_cast<UINT>((tile.density.size()+63)/64),1,1);
|
||||
endKernel(0);
|
||||
ID3D11ShaderResourceView* clearSrvs[16]{};
|
||||
ID3D11UnorderedAccessView* clearUavs[8]{};
|
||||
context->CSSetShaderResources(0,16,clearSrvs);
|
||||
context->CSSetUnorderedAccessViews(0,8,clearUavs,nullptr);
|
||||
if(captureGpuTiming&&!batchTimingMode)context->End(fieldEndQuery.Get());
|
||||
}
|
||||
beginKernel(1);
|
||||
context->CSSetShader(farField ? fieldClassify.Get() : classify.Get(), nullptr, 0);
|
||||
ID3D11ShaderResourceView* classifySrvs[16]{};
|
||||
classifySrvs[0]=signSrv.Get();classifySrvs[1]=triSrv.Get();
|
||||
@@ -1205,20 +1248,24 @@ namespace
|
||||
ID3D11UnorderedAccessView* classifyUavs[] = {caseUav.Get(), localOffsetUav.Get(), groupCountUav.Get()};
|
||||
context->CSSetUnorderedAccessViews(0, 3, classifyUavs, nullptr);
|
||||
context->Dispatch(groupCount, 1, 1);
|
||||
endKernel(1);
|
||||
ID3D11ShaderResourceView* nullSrvs[16]{};
|
||||
ID3D11UnorderedAccessView* nullUavs[8]{};
|
||||
context->CSSetShaderResources(0, 16, nullSrvs);
|
||||
context->CSSetUnorderedAccessViews(0, 8, nullUavs, nullptr);
|
||||
|
||||
beginKernel(2);
|
||||
context->CSSetShader(scan.Get(), nullptr, 0);
|
||||
ID3D11ShaderResourceView* scanSrvs[9]{}; scanSrvs[8] = groupCountSrv.Get();
|
||||
context->CSSetShaderResources(0, 9, scanSrvs);
|
||||
ID3D11UnorderedAccessView* scanUavs[5]{}; scanUavs[3] = groupOffsetUav.Get(); scanUavs[4] = argsUav.Get();
|
||||
context->CSSetUnorderedAccessViews(0, 5, scanUavs, nullptr);
|
||||
context->Dispatch(1, 1, 1);
|
||||
endKernel(2);
|
||||
context->CSSetShaderResources(0, 16, nullSrvs);
|
||||
context->CSSetUnorderedAccessViews(0, 8, nullUavs, nullptr);
|
||||
|
||||
beginKernel(3);
|
||||
context->CSSetShader(emit.Get(), nullptr, 0);
|
||||
ID3D11ShaderResourceView* emitSrvs[9]{};
|
||||
emitSrvs[1] = triSrv.Get(); emitSrvs[2] = densitySrv.Get(); emitSrvs[3] = caseSrv.Get();
|
||||
@@ -1228,6 +1275,7 @@ namespace
|
||||
ID3D11UnorderedAccessView* emitUavs[7]{}; emitUavs[5] = vertexUav.Get(); emitUavs[6] = indexUav.Get();
|
||||
context->CSSetUnorderedAccessViews(0, 7, emitUavs, nullptr);
|
||||
context->Dispatch(groupCount, 1, 1);
|
||||
endKernel(3);
|
||||
context->CSSetShaderResources(0, 16, nullSrvs);
|
||||
context->CSSetUnorderedAccessViews(0, 7, nullUavs, nullptr);
|
||||
|
||||
@@ -1235,17 +1283,21 @@ namespace
|
||||
{
|
||||
ID3D11Buffer* connectorCbs[3]={cb.Get(),farCb.Get(),farConnectorConstantsBuffer.Get()};
|
||||
context->CSSetConstantBuffers(0,3,connectorCbs);
|
||||
beginKernel(4);
|
||||
context->CSSetShader(connectorRings.Get(),nullptr,0);
|
||||
ID3D11ShaderResourceView* connectorRingSrvs[3]={farConnectorSrv.Get(),nullptr,farShapeSrv.Get()};
|
||||
context->CSSetShaderResources(8,3,connectorRingSrvs);
|
||||
ID3D11ShaderResourceView* connectorRingSrvs[5]={farConnectorSrv.Get(),nullptr,
|
||||
farShapeSrv.Get(),farRangeSrv.Get(),farIdSrv.Get()};
|
||||
context->CSSetShaderResources(8,5,connectorRingSrvs);
|
||||
ID3D11UnorderedAccessView* connectorRingUavs[8]{};
|
||||
connectorRingUavs[4]=argsUav.Get();connectorRingUavs[7]=farConnectorRingUav.Get();
|
||||
context->CSSetUnorderedAccessViews(0,8,connectorRingUavs,nullptr);
|
||||
context->Dispatch(std::max<UINT>(1,(farField->connectorRingCount+63u)/64u),1,1);
|
||||
context->CSSetShaderResources(8,3,nullSrvs);
|
||||
context->Dispatch(std::max<UINT>(1,(farField->connectorRingCount+15u)/16u),1,1);
|
||||
endKernel(4);
|
||||
context->CSSetShaderResources(8,5,nullSrvs);
|
||||
context->CSSetUnorderedAccessViews(0,8,nullUavs,nullptr);
|
||||
if(farField->connectorVertexCount)
|
||||
{
|
||||
beginKernel(5);
|
||||
context->CSSetShader(connectorPanels.Get(),nullptr,0);
|
||||
ID3D11ShaderResourceView* panelSrvs[2]={farConnectorSrv.Get(),farConnectorRingSrv.Get()};
|
||||
context->CSSetShaderResources(8,2,panelSrvs);
|
||||
@@ -1253,6 +1305,7 @@ namespace
|
||||
panelUavs[5]=connectorVertexUav.Get();panelUavs[6]=connectorIndexUav.Get();
|
||||
context->CSSetUnorderedAccessViews(0,7,panelUavs,nullptr);
|
||||
context->Dispatch((farField->connectorVertexCount+63u)/64u,1,1);
|
||||
endKernel(5);
|
||||
context->CSSetShaderResources(8,2,nullSrvs);
|
||||
context->CSSetUnorderedAccessViews(0,7,nullUavs,nullptr);
|
||||
}
|
||||
@@ -1323,6 +1376,7 @@ namespace
|
||||
connectorIndices.Get(),0,&connectorIndexSource);
|
||||
}
|
||||
if(farField)context->CopyResource(fieldStaging.Get(),density.Get());
|
||||
if(farEvalCounters)context->CopyResource(farEvalCounterStaging.Get(),farEvalCounters.Get());
|
||||
context->Flush();
|
||||
if(!captureGpuTiming||batchTimingMode)throw std::runtime_error("readback requires standalone GPU timestamp queries");
|
||||
WaitForQuery(disjointQuery.Get());
|
||||
@@ -1342,6 +1396,31 @@ namespace
|
||||
const auto* fieldData=static_cast<const float*>(mapped.pData);
|
||||
result.fieldDensity.assign(fieldData,fieldData+tile.density.size());
|
||||
context->Unmap(fieldStaging.Get(),0);
|
||||
if(farEvalCounterStaging)
|
||||
{
|
||||
Check(context->Map(farEvalCounterStaging.Get(),0,D3D11_MAP_READ,0,&mapped),
|
||||
"Map(far field evaluation counters)");
|
||||
const auto* counts=static_cast<const std::uint32_t*>(mapped.pData);
|
||||
for(std::size_t i=0;i<tile.density.size();++i)
|
||||
{
|
||||
result.fieldDistanceEvaluations+=counts[i];
|
||||
result.maxFieldDistanceEvaluationsPerSample=std::max(
|
||||
result.maxFieldDistanceEvaluationsPerSample,counts[i]);
|
||||
}
|
||||
context->Unmap(farEvalCounterStaging.Get(),0);
|
||||
}
|
||||
if(profileGpuKernels&&!disjoint.Disjoint&&disjoint.Frequency)
|
||||
for(std::size_t stage=0;stage<result.kernelMs.size();++stage)
|
||||
{
|
||||
if(stage==5&&farField->connectorVertexCount==0)continue;
|
||||
const UINT64 stageStart=ReadQuery<UINT64>(kernelStartQueries[stage].Get(),
|
||||
"GetData(kernel stage start)");
|
||||
const UINT64 stageEnd=ReadQuery<UINT64>(kernelEndQueries[stage].Get(),
|
||||
"GetData(kernel stage end)");
|
||||
if(stageEnd>=stageStart)result.kernelMs[stage]=1000.0
|
||||
*static_cast<double>(stageEnd-stageStart)
|
||||
/static_cast<double>(disjoint.Frequency);
|
||||
}
|
||||
}
|
||||
Check(context->Map(caseStaging.Get(), 0, D3D11_MAP_READ, 0, &mapped), "Map(cases)");
|
||||
result.cases.assign(static_cast<std::uint32_t*>(mapped.pData),
|
||||
@@ -1846,9 +1925,10 @@ namespace
|
||||
}
|
||||
const double fieldErrorMean=fieldCount?fieldErrorSum/fieldCount:0.0;
|
||||
const double warpFieldErrorMean=warpFieldCount?warpFieldErrorSum/warpFieldCount:0.0;
|
||||
std::printf("[GpuTile] key=(%d,%d,%d) lod=%d cells=%u source_samples=%zu gpu_shapes=%zu gpu_candidate_refs=%zu protected_samples=%llu critical_lattice=%llu critical_cells=%llu cpu_vertices=%u gpu_vertices=%zu warp_vertices=%zu expected_gpu_vertices=%u gpu_indices_sequential=%d warp_indices_sequential=%d gpu_indexed_indirect_args=%d warp_indexed_indirect_args=%d d3d_buffer_mb=%.3f gpu_adapter_memory_mb=(%.2f,%.2f) budget_mb=%.2f warp_adapter_memory_info=%d gpu_field_ms=%.4f gpu_dispatch_ms=%.4f gpu_wall_ms=%.4f repeat_field_ms=%.4f repeat_dispatch_ms=%.4f warp_field_ms=%.4f warp_dispatch_ms=%.4f warp_wall_ms=%.4f\n",
|
||||
std::printf("[GpuTile] key=(%d,%d,%d) lod=%d cells=%u source_samples=%zu world_shapes=%zu world_bin_shape_ids=%zu protected_samples=%llu critical_lattice=%llu critical_cells=%llu cpu_vertices=%u gpu_vertices=%zu warp_vertices=%zu expected_gpu_vertices=%u gpu_indices_sequential=%d warp_indices_sequential=%d gpu_indexed_indirect_args=%d warp_indexed_indirect_args=%d d3d_buffer_mb=%.3f gpu_adapter_memory_mb=(%.2f,%.2f) budget_mb=%.2f warp_adapter_memory_info=%d gpu_field_ms=%.4f gpu_dispatch_ms=%.4f gpu_wall_ms=%.4f repeat_field_ms=%.4f repeat_dispatch_ms=%.4f warp_field_ms=%.4f warp_dispatch_ms=%.4f warp_wall_ms=%.4f\n",
|
||||
tile.key.coordinate.x, tile.key.coordinate.y, tile.key.coordinate.z, tile.key.lod,
|
||||
tile.cells, tile.density.size(),farInput.shapes.size(),farInput.sampleShapeIds.size(),
|
||||
tile.cells, tile.density.size(),farInput.worldInput->shapes.size(),
|
||||
farInput.worldInput->spatialBinShapeIds.size(),
|
||||
static_cast<unsigned long long>(farInput.protectedSampleCount),static_cast<unsigned long long>(tile.criticalSamples),
|
||||
static_cast<unsigned long long>(tile.criticalCells), cpuMesh.groups.surfaceVertexCount,
|
||||
hwFirst.vertices.size(), warp.vertices.size(), tile.expectedVertexCount,
|
||||
@@ -1893,8 +1973,10 @@ namespace
|
||||
static_cast<unsigned long long>(fieldSignDiff),
|
||||
static_cast<unsigned long long>(fieldCaseLatticeSignDiff),
|
||||
static_cast<unsigned long long>(fieldHaloSignDiff));
|
||||
const bool outputCountMatch = hwFirst.vertices.size() == tile.expectedVertexCount
|
||||
&& warp.vertices.size() == tile.expectedVertexCount;
|
||||
const std::size_t expectedGpuOutputCount=static_cast<std::size_t>(tile.expectedVertexCount)
|
||||
+farInput.connectorVertexCount;
|
||||
const bool outputCountMatch = hwFirst.vertices.size() == expectedGpuOutputCount
|
||||
&& warp.vertices.size() == expectedGpuOutputCount;
|
||||
const float vertexBoundCm=VertexQuantizationBoundCm(hwFirst.vertices,warp.vertices,tile.cpuSurfaceVertices);
|
||||
const bool vertexAgreement=maximum<=vertexBoundCm&&cpuWarpMaximum<=vertexBoundCm&&warpMaximum<=vertexBoundCm;
|
||||
const bool repeatedOutputsIdentical = repeatBitDiff == 0 && repeatCaseDiff == 0
|
||||
@@ -1905,13 +1987,12 @@ namespace
|
||||
&&fieldCount==tile.density.size()
|
||||
&&warpFieldCount==tile.density.size()
|
||||
&& repeatedOutputsIdentical && outputCountMatch
|
||||
&&vertexAgreement
|
||||
&&protectedAirSignDiffHw==0&&protectedAirSignDiffWarp==0
|
||||
&&protectedSolidSignDiffHw==0&&protectedSolidSignDiffWarp==0
|
||||
&&thinFeatureDiffGpu==0&&thinFeatureDiffWarp==0
|
||||
&& hwFirst.sequentialIndices && warp.sequentialIndices
|
||||
&& hwFirst.indexedIndirectArgsValid && warp.indexedIndirectArgsValid;
|
||||
std::printf("[GpuAgreement] tile=(%d,%d,%d,%d) topology_diff_cells_hw=%llu topology_diff_cells_warp=%llu protected_exception_cells_audited=%llu protected_exception_diff_cells_hw=%llu protected_exception_diff_cells_warp=%llu thin_feature_cells_audited=%llu thin_feature_diff_cells_hw=%llu thin_feature_diff_cells_warp=%llu shell_patch_survival=not_audited vertex_comparisons_cpu_gpu=%llu vertex_mean_cm=%.8g vertex_max_cm=%.8g vertex_bound_cm=%.8g vertex_max_pair_cm=(%.5f,%.5f,%.5f)->(%.5f,%.5f,%.5f) vertex_comparisons_cpu_warp=%llu cpu_warp_vertex_mean_cm=%.8g cpu_warp_vertex_max_cm=%.8g vertex_comparisons_gpu_warp=%llu gpu_warp_vertex_mean_cm=%.8g gpu_warp_vertex_max_cm=%.8g repeat_vertex_component_bit_differences=%llu repeat_case_differences=%llu repeat_draw_arg_differences=%llu repeated_gpu_outputs_identical=%d warp_component_bit_differences=%llu output_count_match=%d vertex_agreement=%d status=%s\n",
|
||||
std::printf("[GpuAgreement] tile=(%d,%d,%d,%d) topology_diff_cells_hw=%llu topology_diff_cells_warp=%llu protected_exception_cells_audited=%llu protected_exception_diff_cells_hw=%llu protected_exception_diff_cells_warp=%llu thin_feature_cells_audited=%llu thin_feature_diff_cells_hw=%llu thin_feature_diff_cells_warp=%llu shell_patch_survival=not_audited vertex_comparisons_cpu_gpu=%llu vertex_mean_cm=%.8g vertex_max_cm=%.8g vertex_bound_cm=%.8g vertex_quantization_bound_pass=%d vertex_max_pair_cm=(%.5f,%.5f,%.5f)->(%.5f,%.5f,%.5f) vertex_comparisons_cpu_warp=%llu cpu_warp_vertex_mean_cm=%.8g cpu_warp_vertex_max_cm=%.8g vertex_comparisons_gpu_warp=%llu gpu_warp_vertex_mean_cm=%.8g gpu_warp_vertex_max_cm=%.8g repeat_vertex_component_bit_differences=%llu repeat_case_differences=%llu repeat_draw_arg_differences=%llu repeated_gpu_outputs_identical=%d warp_component_bit_differences=%llu output_count_match=%d vertex_agreement=%d topology_protected_status=%s\n",
|
||||
tile.key.coordinate.x, tile.key.coordinate.y, tile.key.coordinate.z, tile.key.lod,
|
||||
static_cast<unsigned long long>(hwTopologyDiff),
|
||||
static_cast<unsigned long long>(warpTopologyDiff),
|
||||
@@ -1922,7 +2003,7 @@ namespace
|
||||
static_cast<unsigned long long>(thinFeatureDiffGpu),
|
||||
static_cast<unsigned long long>(thinFeatureDiffWarp),
|
||||
static_cast<unsigned long long>(cpuToGpu.count+gpuToCpu.count),
|
||||
mean, maximum,vertexBoundCm,
|
||||
mean, maximum,vertexBoundCm,vertexAgreement,
|
||||
cpuToGpu.maxCm >= gpuToCpu.maxCm ? cpuToGpu.from[0] : gpuToCpu.from[0],
|
||||
cpuToGpu.maxCm >= gpuToCpu.maxCm ? cpuToGpu.from[1] : gpuToCpu.from[1],
|
||||
cpuToGpu.maxCm >= gpuToCpu.maxCm ? cpuToGpu.from[2] : gpuToCpu.from[2],
|
||||
@@ -1948,8 +2029,13 @@ namespace
|
||||
const std::filesystem::path dataPath = root / "data" / "owner_world.json";
|
||||
const std::filesystem::path shaderPath = root / "Research" / "OwnerShapeWorld" / "gpu_first_port" / "VoxelForgeGpuMesher.hlsl";
|
||||
bool wholeFar=false;
|
||||
bool profileGpuKernels=false;
|
||||
int clipRadiusOverride=-1;
|
||||
for(int i=1;i<argc;++i)if(std::string_view(argv[i])=="--whole-far")wholeFar=true;
|
||||
for(int i=1;i<argc;++i)
|
||||
{
|
||||
if(std::string_view(argv[i])=="--whole-far")wholeFar=true;
|
||||
if(std::string_view(argv[i])=="--profile-kernels")profileGpuKernels=true;
|
||||
}
|
||||
for(int i=1;i<argc;++i)
|
||||
{
|
||||
const std::string_view arg=argv[i];
|
||||
@@ -2015,6 +2101,9 @@ namespace
|
||||
&& noiseWarp.differingBits == 0;
|
||||
std::uint64_t testedTiles=0;double cpuTileWallSum=0.0,cpuFieldPrepSum=0.0,cpuExportPrepSum=0.0;
|
||||
double gpuFieldSum=0.0,gpuTotalSum=0.0,warpFieldSum=0.0;
|
||||
std::array<double,6> gpuKernelStageSum{};
|
||||
std::uint64_t gpuFieldDistanceEvaluations=0,gpuFieldSamples=0;
|
||||
std::uint32_t gpuFieldMaxDistanceEvaluationsPerSample=0;
|
||||
std::uint64_t nearTransitionSkipped=0,topologyDiffGpuTotal=0,topologyDiffWarpTotal=0;
|
||||
std::uint64_t protectedExceptionCells=0,protectedExceptionDiffGpu=0,protectedExceptionDiffWarp=0;
|
||||
std::uint64_t protectedRouteAirSamples=0,protectedSealSamples=0,protectedSolidOverlaySamples=0;
|
||||
@@ -2049,7 +2138,8 @@ namespace
|
||||
const auto inputStart = Clock::now();
|
||||
TileData tile = MakeTileData(world, key, config, *cpuMesh, true,farInput);
|
||||
const double inputWallMs = std::chrono::duration<double, std::milli>(Clock::now() - inputStart).count();
|
||||
const GpuResult gpu1 = hardware.Run(tile,farInput);
|
||||
const GpuResult gpu1 = hardware.Run(tile,farInput,true,false,true,false,0,false,
|
||||
true,false,profileGpuKernels);
|
||||
const GpuResult gpu2 = hardware.Run(tile,farInput);
|
||||
const GpuResult warpResult = warp.Run(tile,farInput);
|
||||
for(std::size_t cell=0;cell<tile.expectedCases.size();++cell)
|
||||
@@ -2079,9 +2169,9 @@ namespace
|
||||
const bool tilePass = gpu1.fieldDensity.size()==tile.density.size()
|
||||
&&gpu2.fieldDensity.size()==tile.density.size()
|
||||
&&warpResult.fieldDensity.size()==tile.density.size()
|
||||
&&gpu1.vertices.size() == tile.expectedVertexCount
|
||||
&&gpu1.vertices.size() == static_cast<std::size_t>(tile.expectedVertexCount)+farInput->connectorVertexCount
|
||||
&& gpu2.vertices.size() == gpu1.vertices.size()
|
||||
&& warpResult.vertices.size() == tile.expectedVertexCount
|
||||
&& warpResult.vertices.size() == static_cast<std::size_t>(tile.expectedVertexCount)+farInput->connectorVertexCount
|
||||
&& gpu1.sequentialIndices && gpu2.sequentialIndices && warpResult.sequentialIndices
|
||||
&& gpu1.indexedIndirectArgsValid && gpu2.indexedIndirectArgsValid
|
||||
&& warpResult.indexedIndirectArgsValid
|
||||
@@ -2092,6 +2182,16 @@ namespace
|
||||
++testedTiles;cpuTileWallSum+=cpuWallMs;cpuFieldPrepSum+=inputWallMs;
|
||||
cpuExportPrepSum+=exportMs;
|
||||
gpuFieldSum+=gpu1.fieldMs;gpuTotalSum+=gpu1.dispatchMs;warpFieldSum+=warpResult.fieldMs;
|
||||
if(profileGpuKernels)
|
||||
{
|
||||
for(std::size_t stage=0;stage<gpuKernelStageSum.size();++stage)
|
||||
gpuKernelStageSum[stage]+=gpu1.kernelMs[stage];
|
||||
gpuFieldDistanceEvaluations+=gpu1.fieldDistanceEvaluations;
|
||||
gpuFieldSamples+=tile.density.size();
|
||||
gpuFieldMaxDistanceEvaluationsPerSample=std::max(
|
||||
gpuFieldMaxDistanceEvaluationsPerSample,
|
||||
gpu1.maxFieldDistanceEvaluationsPerSample);
|
||||
}
|
||||
protectedRouteAirSamples+=farInput->protectedSampleCount;
|
||||
protectedSealSamples+=farInput->protectedSealSampleCount;
|
||||
protectedSolidOverlaySamples+=farInput->protectedSolidOverlaySampleCount;
|
||||
@@ -2114,7 +2214,17 @@ namespace
|
||||
static_cast<unsigned long long>(protectedSealSamples),
|
||||
static_cast<unsigned long long>(protectedThinSamples),
|
||||
static_cast<unsigned long long>(protectedSolidOverlaySamples));
|
||||
std::printf("[GpuPortStatus] far_field_subset_status=%s overall_port=step3_validation_pending field=shared_analytic_shapes_strata_gpu_signs_cpu_route_seal_overlay_exceptions meshing=gpu_compute_viewer_draw=integrated captures=viewer_run_required\n",
|
||||
if(profileGpuKernels)
|
||||
{
|
||||
std::printf("[GpuKernelProfile] tiles=%llu field_ms=%.6f classify_ms=%.6f scan_ms=%.6f emit_ms=%.6f connector_rings_ms=%.6f connector_panels_ms=%.6f field_shape_distance_evaluations=%llu field_lattice_samples=%llu average_shape_distance_evaluations_per_lattice_sample=%.4f max_shape_distance_evaluations_per_lattice_sample=%u\n",
|
||||
static_cast<unsigned long long>(testedTiles),gpuKernelStageSum[0],gpuKernelStageSum[1],
|
||||
gpuKernelStageSum[2],gpuKernelStageSum[3],gpuKernelStageSum[4],gpuKernelStageSum[5],
|
||||
static_cast<unsigned long long>(gpuFieldDistanceEvaluations),
|
||||
static_cast<unsigned long long>(gpuFieldSamples),
|
||||
gpuFieldSamples?static_cast<double>(gpuFieldDistanceEvaluations)/gpuFieldSamples:0.0,
|
||||
gpuFieldMaxDistanceEvaluationsPerSample);
|
||||
}
|
||||
std::printf("[GpuPortStatus] far_field_topology_protected_status=%s repeated_gpu_outputs=bit_identical vertex_distance_tolerance=diagnostic overall_port=step5_far_field_gpu_decision_and_meshing field=shared_analytic_shapes_strata_gpu_ordinary_signs_cpu_sparse_exceptions meshing=compute_marching_cubes_connector_panels\n",
|
||||
allPass ? "PASS" : "FAIL");
|
||||
return allPass ? 0 : 3;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# GPU port, step 5: profile and results
|
||||
|
||||
## Result
|
||||
|
||||
The GPU now evaluates ordinary far-field signs from the shared analytic field and emits far meshes directly into pooled viewer buffers. For all 1,114 eligible LOD2–8 tiles, the RX 7900 XTX recorded **112.0 ms** for field evaluation and meshing, down from the step‑4 report’s 646 ms. The isolated runner measured 1.818 s of CPU `generateTile` time for the same tiles.
|
||||
|
||||
The target of a few tens of milliseconds is **not met**. Connector ring construction is now the largest kernel at 61.1 ms. The viewer’s cold hybrid result is also not clearly faster than all‑CPU; see the interleaved timings below.
|
||||
|
||||
## Changes in the measured path
|
||||
|
||||
- `GetFarFieldWorldInput` builds and caches the far shape list and spatial bins once per world. The measured payload has 18,401 shapes, 422,754 bins, 679,503 bin shape IDs, and is 30.523 MiB. Each far tile exports its key, sparse CPU-decided exceptions, strata data, and only the connector panels intersecting that tile.
|
||||
- `EvaluateFarFieldCS` hashes each sample’s spatial bin and loops over that local shape list. The CPU no longer evaluates ordinary far signs. Route-protected air, thin-route bands, seals, and solid overlays remain CPU-decided inputs.
|
||||
- Connector rings use one 64-thread group for 16 rings, with four lanes per ring and shared ring-frame data. The radius solver retains the verified signed-distance correction and fallback.
|
||||
- The viewer batches up to 32 far tiles into pooled output buffers and draws the indirect GPU meshes. Normal completion/timing queries are polled with `D3D11_ASYNC_GETDATA_DONOTFLUSH`; the initial input-preparation worker pool is still joined synchronously.
|
||||
|
||||
Relevant source: [shared world export](../../../Core/src/ShapeWorldInternal.h#L514), [field and connector kernels](../gpu_first_port/VoxelForgeGpuMesher.hlsl#L206), [GPU timing/profile output](GpuFieldPort.cpp#L2201), and [viewer input preparation and batching](../../../Source/StandaloneViewer.cpp#L2232).
|
||||
|
||||
## GPU profile
|
||||
|
||||
Command: `VoxelForgeGpuFieldStep5VerifiedRunner.exe --whole-far --clip-radius=3 --profile-kernels`, launched by the runner wrapper at BelowNormal priority and affinity `0xFF00`. The complete passing result is in [step5_final_verified_profile.stdout.log](step5_final_verified_profile.stdout.log).
|
||||
|
||||
| Kernel | RX 7900 XTX time over 1,114 tiles |
|
||||
|---|---:|
|
||||
| Far field evaluation | 14.94 ms |
|
||||
| Classify | 6.29 ms |
|
||||
| Scan | 4.59 ms |
|
||||
| Emit marching-cubes mesh | 23.31 ms |
|
||||
| Connector rings | 61.05 ms |
|
||||
| Connector panels | 1.53 ms |
|
||||
| Reported field plus meshing total | **112.01 ms** |
|
||||
|
||||
The field counter recorded 892,486 primitive-distance evaluations over 7,640,926 lattice samples: **0.1168 evaluations per sample on average, 9 maximum**. This counter covers ordinary lattice-field evaluation; it does not count shape evaluations inside connector root refinement. The field kernel itself is 14.94 ms. Connector ring construction is the current bottleneck, followed by mesh emission.
|
||||
|
||||
The agreement runner’s CPU far `generateTile` sum was 1,817.94 ms, versus 112.01 ms of GPU field-plus-meshing timestamps. Its separately reported CPU export/protected-preparation sum was 141.63 ms. In the normal viewer runs, input preparation averaged 91.0 ms wall time and 669 ms summed across its eight workers; that worker sum is 58% below step 4’s 1.6 worker-seconds, but it is still a significant CPU cost.
|
||||
|
||||
## Agreement
|
||||
|
||||
The full far comparison covered 1,114 tiles at LOD2–8; 152 near-transition tiles stayed on the CPU path. The GPU evaluated 7.64 million lattice samples. GPU and WARP had **zero ordinary sign differences and zero cell topology differences**. The 127,401 protected exception cells and 353 thin-feature cells also had zero GPU and WARP topology differences. Repeated GPU output was bit-identical.
|
||||
|
||||
| Matched vertex comparisons | Count | Mean distance | Maximum distance |
|
||||
|---|---:|---:|---:|
|
||||
| CPU ↔ RX 7900 XTX | 2,554,084 | 0.11524 cm (1.15 mm) | 64.5602 cm |
|
||||
| CPU ↔ WARP | 2,554,084 | 0.11522 cm (1.15 mm) | 64.5602 cm |
|
||||
| RX 7900 XTX ↔ WARP | 12,252,180 | 0.0004666 cm (4.67 μm) | 0.8221 cm (8.22 mm) |
|
||||
|
||||
The CPU/GPU per-tile vertex quantization diagnostic exceeded its bound on 247 of 1,114 tiles. The largest error is on a far LOD8 tile at large world coordinates, consistent with float coordinate precision loss; that cause is an inference. Topology and protected-feature sign checks still pass, but the CPU/GPU maximum vertex distance is not a small bounded error and remains a limitation.
|
||||
|
||||
WARP is Microsoft’s software D3D implementation and serves as a different implementation check; it is not a second physical GPU vendor.
|
||||
|
||||
CPU hashes were compared with the saved `566d1f3` reference: [step5_cpu_hash_current_w8.stdout.log](../../../Core/verification/step5_cpu_hash_current_w8.stdout.log), **44 hash fields, 11 keys, zero differences**.
|
||||
|
||||
## GPU-mesh survival audit
|
||||
|
||||
The audit used the GPU meshes and required field readback. [step5_verified_gpu_survival_audit.stdout.log](step5_verified_gpu_survival_audit.stdout.log) reports `status=PASS`: 7,200/7,200 boundary surfaces present, zero boundary holes, zero connector samples/rays missing, and zero connector holes across 10,976 far connectors. It also reports 50,019 extra connector surfaces; the audit’s overall status remains PASS. The audit run took 38.728 s to reach the initial clipmap because audit readback and checks are enabled; this is not the normal cold-view timing.
|
||||
|
||||
## Interleaved cold viewer timings
|
||||
|
||||
Command for each run: `RunGpuFarViewer.ps1 -Mode cpu|gpu -ClipRadius 3 -ExecutableName VoxelForgeGpuFieldStep5FinalViewer.exe -LogStem step5_final_interleave_<mode>_<A-D>`. The wrapper used eight CPU workers, BelowNormal priority, affinity `0xFF00`, and `--quit-after-clipmap`. Order was CPU A, GPU A, GPU B, CPU B, CPU C, GPU C, GPU D, CPU D.
|
||||
|
||||
| Mode | Four cold initial-clipmap times | Mean |
|
||||
|---|---|---:|
|
||||
| All CPU (`--cpu-far`) | 2.409, 2.254, 2.340, 2.251 s | 2.3135 s |
|
||||
| CPU near + GPU far (`--gpu-far`) | 2.277, 2.368, 2.335, 2.232 s | 2.3030 s |
|
||||
|
||||
The hybrid mean leads by 10.5 ms (0.45%), while individual runs vary by more than 100 ms. **This does not demonstrate a clear whole-view gain.** Full logs are `step5_final_interleave_cpu_A-D.stdout.log` and `step5_final_interleave_gpu_A-D.stdout.log` in this directory.
|
||||
|
||||
Across the four GPU viewer runs, the far timestamp totals averaged 93.1 ms, CPU input preparation 91.0 ms wall / 669 ms worker sum, output-pool reserve 26.3 ms, and upload/enqueue 71.8 ms. Submission used 37 batches of up to 32 tiles; the longest batch timestamp was 10.836 ms, almost the whole 11.111 ms budget of a 90 Hz frame. Viewer and agreement-runner timing scopes differ: the viewer reports 90.5–95.2 ms of far timestamps per run, while the agreement runner reports 112.0 ms for its per-kernel query sum. The viewer’s `gpu_dispatch_max_tile_ms` also ranges from 8.64 to 9.48 ms, unlike the runner’s 0.238 ms maximum tile timestamp; treat that per-item maximum as unresolved query/batch scope, not a comparable per-tile cost. Cold wall time is the end-to-end measure.
|
||||
|
||||
### Estimated frame slicing
|
||||
|
||||
Using the agreement runner’s per-tile GPU timestamps (mean 0.1005 ms, p95 0.1695 ms), GPU work alone would fit approximately:
|
||||
|
||||
| GPU slice budget | Tiles/frame at mean | Frames for 1,114 tiles | Tiles/frame at p95 | Frames at p95 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| 0.5 ms | 4 (0.402 ms) | 279 (~3.10 s at 90 Hz) | 2 (0.339 ms) | 557 (~6.19 s) |
|
||||
| 1.0 ms | 9 (0.905 ms) | 124 (~1.38 s at 90 Hz) | 5 (0.848 ms) | 223 (~2.48 s) |
|
||||
|
||||
These are GPU-only scheduling estimates. The viewer does not yet slice work to 0.5–1.0 ms; current 32-tile batches can consume almost an entire 90 Hz frame.
|
||||
|
||||
## Memory and GTX 1060 estimate
|
||||
|
||||
The final GPU survival-audit run reported peak viewer working set 0.70 GiB, peak private memory 4.50 GiB, and DXGI local GPU memory 3,115.6 MiB (3.04 GiB) against a 23.75 GiB budget. A simple sum of private and local-memory counters is about 7.54 GiB, under the 8 GiB limit with little headroom; the counters can overlap in driver accounting.
|
||||
|
||||
No GTX 1060 was available, so this is an estimate. Scaling the measured 112 ms by the rough FP32 ratio (about 61 TFLOPS for RX 7900 XTX versus the brief’s 4.4 TFLOPS for GTX 1060) gives about **1.56 s**. Pascal’s slower 32-bit integer work and different occupancy could widen that; estimate **1.5–2.5 s of GPU dispatch time** for the same far workload. Since the RX 7900 XTX hybrid is not clearly faster end-to-end, a GTX 1060-class system should not be expected to gain whole-view time from this version.
|
||||
|
||||
## Follow-up experiments not retained
|
||||
|
||||
- Replacing per-tile CPU exception candidate lists with per-sample hash-bin lookups made the full comparison fail protected signs/topology and raised measured CPU export preparation from about 142 ms to 360 ms. That change was reverted; the final passing run uses the original exact candidate filtering.
|
||||
- Seeding connector roots from a trilinear interpolation of the tile field preserved the full comparison’s topology checks but increased connector-ring time from 61.05 to 69.76 ms and total GPU time from 112.01 to 120.95 ms. It was reverted. The [experiment log](step5_interp_fullfar_profile.stdout.log) is retained; the final shader and precompiled shader binaries were rebuilt from the verified source.
|
||||
|
||||
No commit, push, or stash was made.
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user