From 247682a8e0bfb89e6846f64d53bcfb15317f0d52 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Tue, 6 Oct 2026 11:01:03 +0200 Subject: [PATCH] wip(standalone): GPU step 4 -- batched far generation, connector panels on GPU; far survival passes Up to 32 tiles per deferred command list, one polled event per batch, no per-tile waits; CPU upload/enqueue 723 -> 46 ms. GPU far meshes pass far survival (7,200 strata boundaries, 10,976 connectors, 0 missing/holes). But hybrid cold 4.36 s vs CPU 2.29 s: whole-tile GPU timestamps sum to 646 ms (0.58 ms/tile; step 3's 0.03 ms covered part of the work only), divergent per-thread shape loops; CPU far-input export 1.6 worker-s. CPU 44/44 hashes unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ --- .../include/VoxelForgeCore/VoxelForgeCore.h | 16 + Standalone/Core/src/ShapeWorldInternal.h | 104 ++- .../gpu_field_port/BuildGpuFieldViewer.ps1 | 21 +- .../gpu_field_port/GpuFarViewer.h | 27 +- .../gpu_field_port/GpuFieldPort.cpp | 855 +++++++++++++++--- .../gpu_field_port/RunGpuFarViewer.ps1 | 7 +- .../gpu_field_port/STEP4_REPORT.md | 76 ++ .../gpu_field_port/StartGpuFarViewer.ps1 | 6 +- .../precompiled/BuildFarConnectorRingsCS.cso | Bin 0 -> 733428 bytes .../gpu_field_port/precompiled/ClassifyCS.cso | Bin 0 -> 8908 bytes .../precompiled/ClassifyFarCS.cso | Bin 0 -> 8712 bytes .../gpu_field_port/precompiled/EmitCS.cso | Bin 0 -> 107076 bytes .../precompiled/EmitFarConnectorPanelsCS.cso | Bin 0 -> 5524 bytes .../precompiled/EvaluateFarFieldCS.cso | Bin 0 -> 15232 bytes .../precompiled/NoiseProbeCS.cso | Bin 0 -> 17184 bytes .../precompiled/ScanGroupsCS.cso | Bin 0 -> 2028 bytes .../gpu_first_port/VoxelForgeGpuMesher.hlsl | 255 +++++- Standalone/Source/StandaloneViewer.cpp | 343 ++++++- 18 files changed, 1519 insertions(+), 191 deletions(-) create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP4_REPORT.md create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyFarCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarConnectorPanelsCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EvaluateFarFieldCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/NoiseProbeCS.cso create mode 100644 Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ScanGroupsCS.cso diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 4582906..db2341e 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -205,6 +205,20 @@ namespace VoxelForgeCore float z = 0.0f, halfWidth = 8.0f; }; + // Sparse connector repair panels for the GPU far renderer. Each record describes + // the tile-clipped axial interval and output ranges; signs and source radii are + // evaluated by the GPU from the far shape field. + struct FarFieldConnectorPanel + { + float start[3]{}, radius = 0.0f; + float end[3]{}, tMin = 0.0f; + float tMax = 1.0f; + std::uint32_t ringCount = 0, ringOffset = 0, vertexOffset = 0; + std::uint32_t axialSampleStart = 0, axialSampleCount = 0; + std::uint32_t padding[2]{}; + }; + static_assert(sizeof(FarFieldConnectorPanel) == 64); + struct FarFieldTileInput { Vec3i origin{}; @@ -222,6 +236,8 @@ namespace VoxelForgeCore std::vector protectedSolid; std::vector protectedThin; std::vector stratumBoundaries; + std::vector connectorPanels; + std::uint32_t connectorRingCount = 0, connectorVertexCount = 0; std::uint64_t protectedSampleCount = 0; std::uint64_t protectedSolidSampleCount = 0; std::uint64_t protectedSealSampleCount = 0; diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index c9f8eaf..31524a5 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -471,7 +471,6 @@ public: {Error="far field export requires a built far world and a bounded coarse tile";return false;} const int32 Dim=Cells+3; const size_t SampleCount=static_cast(Dim)*Dim*Dim; - std::vector> PerSample(SampleCount); std::vector Used(Primitives.size(),0); Out={};Out.origin={Origin.X,Origin.Y,Origin.Z};Out.cells=Cells;Out.step=Step;Out.sampleDim=Dim; if(LayerRanges.empty()) @@ -505,6 +504,76 @@ public: Out.protectedSolid.assign((SampleCount+31u)/32u,0u); Out.protectedThin.assign((SampleCount+31u)/32u,0u); + const auto& Connectors=GetFarConnectorSegments(); + // CPU repair panels are only used by the survival path at LOD6 and above. + // Keep this sparse repair out of LOD2..5, where the base GPU surface has + // sufficient resolution and the CPU path emits no panel geometry. + if(Cells*Step>=(CHUNK_SIZE<<6)) + { + std::vector ConnectorCandidates; + FindFarConnectorCandidates( + FIntVector(Origin.X-64,Origin.Y-64,Origin.Z-64), + FIntVector(Origin.X+Cells*Step+64,Origin.Y+Cells*Step+64, + Origin.Z+Cells*Step+64),ConnectorCandidates); + const double TileMin[3]={static_cast(Origin.X),static_cast(Origin.Y), + static_cast(Origin.Z)}; + const double TileMax[3]={TileMin[0]+Cells*Step,TileMin[1]+Cells*Step, + TileMin[2]+Cells*Step}; + for(const uint32 ConnectorIndex:ConnectorCandidates) + { + if(ConnectorIndex>=Connectors.size())continue; + const FFarConnectorSegment& Connector=Connectors[ConnectorIndex]; + const double Start[3]={Connector.Start.X,Connector.Start.Y,Connector.Start.Z}; + const double Delta[3]={Connector.End.X-Connector.Start.X, + Connector.End.Y-Connector.Start.Y,Connector.End.Z-Connector.Start.Z}; + double TMin=0.0,TMax=1.0;bool Intersects=true; + for(int32 Axis=0;Axis<3;++Axis) + { + if(std::abs(Delta[Axis])<1.0e-9) + {if(Start[Axis]TileMax[Axis])Intersects=false;continue;} + double A=(TileMin[Axis]-Start[Axis])/Delta[Axis]; + double B=(TileMax[Axis]-Start[Axis])/Delta[Axis]; + if(A>B)std::swap(A,B); + TMin=std::max(TMin,A);TMax=std::min(TMax,B); + if(TMin>TMax){Intersects=false;break;} + } + const double Length=std::sqrt(Delta[0]*Delta[0]+Delta[1]*Delta[1]+Delta[2]*Delta[2]); + if(!Intersects||Length<=1.0e-6||TMax-TMin<=1.0e-7)continue; + const double ShapeMargin=static_cast(Connector.Radius)+64.0; + for(uint32 ShapeId=0;ShapeIdHi){Overlaps=false;break;} + } + if(Overlaps)Used[ShapeId]=1; + } + const uint32 AxialSampleCount=std::max(1,static_cast(std::ceil(Length/128.0))); + const double FirstValue=std::ceil(TMin*AxialSampleCount-0.5-1.0e-8); + const double LastValue=std::floor(TMax*AxialSampleCount-0.5+1.0e-8); + const uint32 FirstSample=static_cast(std::clamp(FirstValue,0.0, + static_cast(AxialSampleCount))); + const uint32 SampleCount=LastValue>=FirstValue?static_cast(std::max(0.0, + std::min(LastValue,static_cast(AxialSampleCount-1))-FirstValue+1.0)):0; + const uint32 RingCount=SampleCount+2; + const uint64 VertexCount=static_cast(RingCount-1)*24u; + if(Out.connectorVertexCount+VertexCount>std::numeric_limits::max()) + {Error="far connector panel output exceeds the 32-bit tile limit";return false;} + VoxelForgeCore::FarFieldConnectorPanel Panel{}; + for(int32 Axis=0;Axis<3;++Axis) + {Panel.start[Axis]=static_cast(Start[Axis]);Panel.end[Axis]=static_cast(Start[Axis]+Delta[Axis]);} + Panel.radius=Connector.Radius;Panel.tMin=static_cast(TMin);Panel.tMax=static_cast(TMax); + Panel.ringCount=RingCount;Panel.ringOffset=Out.connectorRingCount; + Panel.vertexOffset=Out.connectorVertexCount; + Panel.axialSampleStart=FirstSample;Panel.axialSampleCount=AxialSampleCount; + Out.connectorRingCount+=Panel.ringCount;Out.connectorVertexCount+=static_cast(VertexCount); + Out.connectorPanels.push_back(Panel); + } + } + size_t SampleIndex=0; for(int32 Z=-1;Z<=Cells+1;++Z) for(int32 Y=-1;Y<=Cells+1;++Y) @@ -561,9 +630,17 @@ public: } } const auto Found=SpatialBins.find(PointBin(WX,WY,WZ)); - if(Found==SpatialBins.end())continue; - auto& Ids=PerSample[SampleIndex]; - Ids.assign(Found->second.begin(),Found->second.end()); + if(Found==SpatialBins.end()) + { + Out.sampleShapeRanges[SampleIndex]={static_cast(Out.sampleShapeIds.size()),0}; + continue; + } + const auto& Ids=Found->second; + if(Out.sampleShapeIds.size()+Ids.size()>std::numeric_limits::max()) + {Error="far tile candidate list exceeds the GPU 32-bit offset limit";return false;} + Out.sampleShapeRanges[SampleIndex]={static_cast(Out.sampleShapeIds.size()), + static_cast(Ids.size())}; + Out.sampleShapeIds.insert(Out.sampleShapeIds.end(),Ids.begin(),Ids.end()); for(const uint32 Id:Ids)if(Id(S.floorCurveEndBlend); Out.shapes.push_back(G); } - uint64 CandidateCount=0; - for(const auto& Ids:PerSample)CandidateCount+=Ids.size(); - if(CandidateCount>std::numeric_limits::max()) - {Error="far tile candidate list exceeds the GPU 32-bit offset limit";return false;} - Out.sampleShapeIds.reserve(static_cast(CandidateCount)); - for(size_t I=0;I(Out.sampleShapeIds.size()), - static_cast(Ids.size())}; - for(const uint32 Id:Ids) - { - if(Id>=Remap.size()||Remap[Id]==std::numeric_limits::max()) - {Error="far tile shape remap is inconsistent";return false;} - Out.sampleShapeIds.push_back(Remap[Id]); - } + if(Id>=Remap.size()||Remap[Id]==std::numeric_limits::max()) + {Error="far tile shape remap is inconsistent";return false;} + Id=Remap[Id]; } return true; } diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 index e2f66ee..5478e05 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/BuildGpuFieldViewer.ps1 @@ -9,8 +9,11 @@ $BuildHostProcess = Get-Process -Id $PID $BuildHostProcess.PriorityClass = 'BelowNormal' $BuildHostProcess.ProcessorAffinity = [IntPtr]0xFF00 $Root = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) -$BuildDir = Join-Path $Root 'Build\VoxelForgeGpuFarHybridViewer' +$BuildStem = [IO.Path]::GetFileNameWithoutExtension($OutputName) +$BuildDir = Join-Path $Root ('Build\'+$BuildStem) $BinDir = Join-Path $Root 'bin' +$ShaderSource = Join-Path $Root 'Research\OwnerShapeWorld\gpu_first_port\VoxelForgeGpuMesher.hlsl' +$PrecompiledShaderDir = Join-Path $Root 'Research\OwnerShapeWorld\gpu_field_port\precompiled' [IO.Directory]::CreateDirectory($BuildDir) | Out-Null [IO.Directory]::CreateDirectory($BinDir) | Out-Null @@ -110,6 +113,22 @@ if ($Target -eq 'All') { $LegacyObjects = @($LegacySources | ForEach-Object { '"{0}.obj"' -f [IO.Path]::GetFileNameWithoutExtension($_) }) $LegacyRspLines = $CommonFlags + @('/c') + @($LegacySources | ForEach-Object { '"{0}"' -f $_ }) [IO.File]::WriteAllLines($LegacyRsp,$LegacyRspLines,[Text.UTF8Encoding]::new($false)) + if (-not (Test-Path -LiteralPath $ShaderSource -PathType Leaf)) { throw "GPU shader source is missing: $ShaderSource" } + $FxCRoot = 'C:\Program Files (x86)\Windows Kits\10\bin' + $FxC = Get-ChildItem -LiteralPath $FxCRoot -Filter fxc.exe -Recurse -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match '\\x64\\fxc\.exe$' } | + Sort-Object -Property FullName -Descending | Select-Object -First 1 + if ($FxC) { + [IO.Directory]::CreateDirectory($PrecompiledShaderDir) | Out-Null + foreach ($Entry in @('EvaluateFarFieldCS','ClassifyCS','ClassifyFarCS','ScanGroupsCS','EmitCS','NoiseProbeCS','BuildFarConnectorRingsCS','EmitFarConnectorPanelsCS')) { + $ShaderBinary = Join-Path $PrecompiledShaderDir ($Entry+'.cso') + $CommandLine = '"{0}" /nologo /T cs_5_0 /E {1} /O3 /Gis /Ges /Fo "{2}" "{3}"' -f $FxC.FullName,$Entry,$ShaderBinary,$ShaderSource + Invoke-FieldViewerBuildCommand $CommandLine "precompile HLSL entry $Entry" + } + Write-Host "Precompiled viewer compute shaders into $PrecompiledShaderDir" + } else { + Write-Warning 'fxc.exe was not found; the viewer will fall back to one-time runtime shader compilation.' + } } $ViewerRsp = Join-Path $BuildDir 'viewer-link.rsp' diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h index 7eaa417..3a79739 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFarViewer.h @@ -15,14 +15,39 @@ namespace VoxelForgeGpuFarViewer { struct CpuVertex { float position[3]{}, normal[3]{}; }; struct CpuMesh { std::vector vertices; std::vector indices; }; + struct GpuTimingQueries + { + ID3D11Query* start=nullptr; + ID3D11Query* end=nullptr; + ID3D11Query* disjoint=nullptr; + }; + bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,std::uint32_t tileSlots,std::uint32_t cells, + std::string& error); + bool QueryFarGpuMemory(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,std::uint64_t& usageBytes, + std::uint64_t& budgetBytes,std::string& error); + bool BeginFarTileBatch(ID3D11Device* device,ID3D11DeviceContext* immediateContext, + const std::filesystem::path& shaderPath,std::string& error); // Returned D3D11 buffers carry one reference each; the caller releases them. // Vertex records keep the 32-byte compute layout (position/normal/uv). The // first 24 bytes match the standalone viewer's input layout. bool BuildFarTile(ID3D11Device* device, ID3D11DeviceContext* context, VoxelForgeCore::World& world, const VoxelForgeCore::TileKey& key, + const VoxelForgeCore::FarFieldTileInput* preparedInput, const std::filesystem::path& shaderPath, ID3D11Buffer** vertices, ID3D11Buffer** indices, ID3D11Buffer** indirectArgs, - ID3D11Query** completionEvent, + ID3D11Buffer** connectorVertices,ID3D11Buffer** connectorIndices, + std::uint32_t outputSlot, std::uint32_t outputCapacitySlots, + std::uint32_t* indirectArgsByteOffset, + ID3D11Query** completionEvent,GpuTimingQueries* timingQueries, CpuMesh* auditReadback, double* enqueueWallMs, double* gpuDispatchMs, std::string& error); + bool SubmitFarTileBatch(ID3D11Device* device,ID3D11DeviceContext* immediateContext, + const std::filesystem::path& shaderPath,ID3D11Query** completionEvent, + GpuTimingQueries* timingQueries,std::string& error); + bool ResolveGpuTiming(ID3D11DeviceContext* context,GpuTimingQueries& queries, + double& dispatchMs,std::uint64_t* timestampFrequency=nullptr); + bool ResolveGpuTimestampPair(ID3D11DeviceContext* context,GpuTimingQueries& queries, + std::uint64_t timestampFrequency,double& dispatchMs); } diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp index a601fce..e1a9cae 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/GpuFieldPort.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ using int32 = std::int32_t; #include #include #include +#include "GpuFarViewer.h" using Microsoft::WRL::ComPtr; using Clock = std::chrono::steady_clock; @@ -98,6 +100,21 @@ namespace }; static_assert(sizeof(MidpointRecord) == 64); + struct FarConnectorRingRecord + { + float corners[4][4]{}; + float normals[4][4]{}; + float t=0.0f,padding[3]{}; + }; + static_assert(sizeof(FarConnectorRingRecord)==144); + + struct FarConnectorConstants + { + std::uint32_t connectorCount=0,ringCount=0,vertexCount=0,panelVertexCapacity=0; + std::uint32_t shapeCount=0,padding[3]{}; + }; + static_assert(sizeof(FarConnectorConstants)==32); + struct GpuVertex { float position[3]; @@ -110,7 +127,8 @@ namespace { std::uint32_t cells, sampleDim, cellCount, groupCount; std::int32_t originX, originY, originZ, stepVoxels; - float voxelSizeCm, pad[3]{}; + float voxelSizeCm; + std::uint32_t outputBaseVertex=0, outputArgsByteOffset=0, pad=0; }; static_assert(sizeof(TileConstants) == 48); @@ -159,9 +177,12 @@ namespace std::vector cases; std::vector fieldDensity; ComPtr vertexBuffer, indexBuffer, indirectArgs; + ComPtr connectorVertexBuffer,connectorIndexBuffer; + ComPtr startTimestamp,endTimestamp,disjointTimestamp; std::array drawArgs{}; bool sequentialIndices = false; bool indexedIndirectArgsValid = false; + std::uint32_t outputBaseVertex = 0, outputArgsByteOffset = 0; bool hasAdapterMemoryInfo = false; std::uint64_t adapterMemoryBeforeBytes = 0; std::uint64_t adapterMemoryAfterBytes = 0; @@ -455,39 +476,133 @@ namespace struct DeviceRun { + struct FarOutputChunk + { + ComPtr vertices, indices, args; + ComPtr vertexUav, indexUav, argsUav; + ComPtr connectorVertices,connectorIndices; + ComPtr connectorVertexUav,connectorIndexUav; + }; + struct DynamicInput + { + ComPtr buffer; + ComPtr srv; + std::size_t capacity=0,stride=0; + }; + struct FarScratch + { + std::uint32_t sampleCount=0,cellCount=0,groupCount=0; + ComPtr signs,tri,density,midpointIds,midpointRecords; + ComPtr cases,localOffsets,groupCounts,groupOffsets; + ComPtr tileConstants,farConstants; + ComPtr connectorConstants,connectorRings; + ComPtr signSrv,triSrv,densitySrv,midpointIdSrv,midpointRecordSrv; + ComPtr caseSrv,localOffsetSrv,groupCountSrv,groupOffsetSrv; + ComPtr connectorRingSrv; + ComPtr densityUav,caseUav,localOffsetUav,groupCountUav,groupOffsetUav; + ComPtr connectorRingUav; + DynamicInput shapes,ranges,ids,protectedAir,protectedSolid; + DynamicInput connectors; + std::uint32_t connectorRingCapacity=0; + } farScratch; ComPtr device; ComPtr context; + ComPtr immediateContext,deferredContext; ComPtr fieldEval, classify, fieldClassify, scan, emit, noiseProbe; + ComPtr connectorRings,connectorPanels; ComPtr memoryAdapter; std::string name; UINT64 timestampFrequency = 0; + std::vector farOutputChunks; + std::uint32_t farOutputCapacitySlots=0,farOutputCells=0; + std::uint32_t farOutputSlotsPerChunk=128,farMaxVerticesPerTile=0; + static constexpr std::uint32_t FarPanelVertexCapacity=32768; void CompileShaders(const std::filesystem::path& shaderPath) { - ComPtr code = CompileShader(shaderPath, "EvaluateFarFieldCS"); + const auto Load=[&](const char* entry) + { + const std::filesystem::path binary=shaderPath.parent_path().parent_path() + /"gpu_field_port"/"precompiled"/(std::string(entry)+".cso"); + ComPtr code; + if(std::filesystem::exists(binary) + &&SUCCEEDED(D3DReadFileToBlob(binary.c_str(),&code)))return code; + return CompileShader(shaderPath,entry); + }; + ComPtr code = Load("EvaluateFarFieldCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &fieldEval), "CreateComputeShader(EvaluateFarFieldCS)"); - code = CompileShader(shaderPath, "ClassifyCS"); + code = Load("ClassifyCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &classify), "CreateComputeShader(ClassifyCS)"); - code = CompileShader(shaderPath, "ClassifyFarCS"); + code = Load("ClassifyFarCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &fieldClassify), "CreateComputeShader(ClassifyFarCS)"); - code = CompileShader(shaderPath, "ScanGroupsCS"); + code = Load("ScanGroupsCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &scan), "CreateComputeShader(ScanGroupsCS)"); - code = CompileShader(shaderPath, "EmitCS"); + code = Load("EmitCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &emit), "CreateComputeShader(EmitCS)"); - code = CompileShader(shaderPath, "NoiseProbeCS"); + code = Load("NoiseProbeCS"); Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &noiseProbe), "CreateComputeShader(NoiseProbeCS)"); + code = Load("BuildFarConnectorRingsCS"); + Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &connectorRings), "CreateComputeShader(BuildFarConnectorRingsCS)"); + code = Load("EmitFarConnectorPanelsCS"); + Check(device->CreateComputeShader(code->GetBufferPointer(), code->GetBufferSize(), nullptr, &connectorPanels), "CreateComputeShader(EmitFarConnectorPanelsCS)"); } void InitializeExternal(ID3D11Device* inDevice, ID3D11DeviceContext* inContext, const std::filesystem::path& shaderPath) { device = inDevice; - context = inContext; - if (!device || !context) throw std::runtime_error("GPU far viewer requires a live D3D11 device/context"); + immediateContext = inContext; + context = immediateContext; + if (!device || !immediateContext) throw std::runtime_error("GPU far viewer requires a live D3D11 device/context"); + Check(device->CreateDeferredContext(0,&deferredContext),"CreateDeferredContext(far tile batches)"); name = "viewer-device"; + ComPtr dxgiDevice; + ComPtr adapter; + if(SUCCEEDED(device.As(&dxgiDevice))&&SUCCEEDED(dxgiDevice->GetAdapter(&adapter))) + adapter.As(&memoryAdapter); CompileShaders(shaderPath); } + void BeginFarTileBatch() + { + if(!deferredContext)throw std::runtime_error("far tile deferred context is unavailable"); + context=deferredContext; + } + + void EndFarTileBatch() + { + context=immediateContext; + } + + void SubmitFarTileBatch(ID3D11Query** completionEvent, + VoxelForgeGpuFarViewer::GpuTimingQueries* timingQueries) + { + if(!completionEvent||!timingQueries||!deferredContext||!immediateContext) + throw std::runtime_error("far tile batch submission arguments are invalid"); + *completionEvent=nullptr;*timingQueries=VoxelForgeGpuFarViewer::GpuTimingQueries{}; + ComPtr commands; + Check(deferredContext->FinishCommandList(FALSE,&commands),"FinishCommandList(far tile batch)"); + context=immediateContext; + D3D11_QUERY_DESC timestampDesc{D3D11_QUERY_TIMESTAMP,0}; + D3D11_QUERY_DESC disjointDesc{D3D11_QUERY_TIMESTAMP_DISJOINT,0}; + D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; + ComPtr start,end,disjoint,event; + Check(device->CreateQuery(×tampDesc,&start),"CreateQuery(far batch start)"); + Check(device->CreateQuery(×tampDesc,&end),"CreateQuery(far batch end)"); + Check(device->CreateQuery(&disjointDesc,&disjoint),"CreateQuery(far batch disjoint)"); + Check(device->CreateQuery(&eventDesc,&event),"CreateQuery(far batch event)"); + immediateContext->Begin(disjoint.Get()); + immediateContext->End(start.Get()); + immediateContext->ExecuteCommandList(commands.Get(),TRUE); + immediateContext->End(end.Get()); + immediateContext->End(disjoint.Get()); + immediateContext->End(event.Get()); + *completionEvent=event.Detach(); + timingQueries->start=start.Detach(); + timingQueries->end=end.Detach(); + timingQueries->disjoint=disjoint.Detach(); + } + void Initialize(const std::filesystem::path& shaderPath, bool warp) { D3D_FEATURE_LEVEL requested[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; @@ -604,6 +719,149 @@ namespace return result; } + void EnsureFarOutputPool(std::uint32_t tileSlots,std::uint32_t cells) + { + if(tileSlots==0||cells==0)throw std::runtime_error("far output pool dimensions must be non-zero"); + if(tileSlots<=farOutputCapacitySlots&&cells==farOutputCells)return; + const std::uint64_t maxVertices64=static_cast(cells)*cells*cells*15ull; + if(maxVertices64>std::numeric_limits::max()) + throw std::runtime_error("far output tile vertex capacity exceeds 32-bit limits"); + farMaxVerticesPerTile=static_cast(maxVertices64); + farOutputCapacitySlots=std::max(tileSlots,farOutputCapacitySlots); + farOutputCells=cells; + const std::uint32_t chunkCount=(farOutputCapacitySlots+farOutputSlotsPerChunk-1)/farOutputSlotsPerChunk; + std::vector chunks; + chunks.reserve(chunkCount); + for(std::uint32_t chunkIndex=0;chunkIndex(slots)*farMaxVerticesPerTile*sizeof(GpuVertex); + const std::uint64_t indexBytes=static_cast(slots)*farMaxVerticesPerTile*sizeof(std::uint32_t); + const std::uint64_t argsBytes=static_cast(slots)*10u*sizeof(std::uint32_t); + if(vertexBytes>std::numeric_limits::max()||indexBytes>std::numeric_limits::max()) + throw std::runtime_error("far output chunk exceeds the D3D11 buffer size limit"); + FarOutputChunk chunk; + chunk.vertices=RawMeshBuffer(static_cast(vertexBytes), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_VERTEX_BUFFER); + chunk.indices=RawMeshBuffer(static_cast(indexBytes), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_INDEX_BUFFER); + chunk.args=RawMeshBuffer(static_cast(argsBytes),D3D11_BIND_UNORDERED_ACCESS, + D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS|D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS); + chunk.vertexUav=RawUav(chunk.vertices.Get(),static_cast(vertexBytes/sizeof(std::uint32_t))); + chunk.indexUav=RawUav(chunk.indices.Get(),static_cast(indexBytes/sizeof(std::uint32_t))); + chunk.argsUav=RawUav(chunk.args.Get(),static_cast(argsBytes/sizeof(std::uint32_t))); + const std::uint64_t connectorVertexBytes=static_cast(slots) + *FarPanelVertexCapacity*sizeof(GpuVertex); + const std::uint64_t connectorIndexBytes=static_cast(slots) + *FarPanelVertexCapacity*sizeof(std::uint32_t); + chunk.connectorVertices=RawMeshBuffer(static_cast(connectorVertexBytes), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_VERTEX_BUFFER); + chunk.connectorIndices=RawMeshBuffer(static_cast(connectorIndexBytes), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_INDEX_BUFFER); + chunk.connectorVertexUav=RawUav(chunk.connectorVertices.Get(), + static_cast(connectorVertexBytes/sizeof(std::uint32_t))); + chunk.connectorIndexUav=RawUav(chunk.connectorIndices.Get(), + static_cast(connectorIndexBytes/sizeof(std::uint32_t))); + chunks.push_back(std::move(chunk)); + } + farOutputChunks=std::move(chunks); + } + + template + ComPtr UpdateDynamicInput(DynamicInput& input, + const T* data,std::size_t count,const char* label) + { + const std::size_t used=std::max(1,count); + if(input.stride&&input.stride!=sizeof(T))throw std::runtime_error("dynamic input stride changed"); + if(used>input.capacity) + { + std::size_t capacity=1; + while(capacitystd::numeric_limits::max()) + throw std::runtime_error(std::string(label)+" exceeds the D3D11 buffer size limit"); + D3D11_BUFFER_DESC desc{};desc.ByteWidth=static_cast(capacity*sizeof(T)); + desc.Usage=D3D11_USAGE_DYNAMIC;desc.BindFlags=D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags=D3D11_CPU_ACCESS_WRITE;desc.MiscFlags=D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + desc.StructureByteStride=sizeof(T); + ComPtr buffer;Check(device->CreateBuffer(&desc,nullptr,&buffer),label); + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};srvDesc.Format=DXGI_FORMAT_UNKNOWN; + srvDesc.ViewDimension=D3D11_SRV_DIMENSION_BUFFER; + srvDesc.Buffer.NumElements=static_cast(capacity); + ComPtr srv; + Check(device->CreateShaderResourceView(buffer.Get(),&srvDesc,&srv),"CreateShaderResourceView(dynamic far input)"); + input.buffer=std::move(buffer);input.srv=std::move(srv);input.capacity=capacity;input.stride=sizeof(T); + } + D3D11_MAPPED_SUBRESOURCE mapped{}; + Check(context->Map(input.buffer.Get(),0,D3D11_MAP_WRITE_DISCARD,0,&mapped),label); + if(count)std::memcpy(mapped.pData,data,count*sizeof(T)); + else std::memset(mapped.pData,0,sizeof(T)); + context->Unmap(input.buffer.Get(),0); + return input.srv; + } + + void EnsureFarScratch(const TileData& tile) + { + const std::uint32_t cells=tile.cells*tile.cells*tile.cells; + const std::uint32_t groups=(cells+255u)/256u; + const std::uint32_t samples=static_cast(tile.dim*tile.dim*tile.dim); + if(farScratch.sampleCount==samples&&farScratch.cellCount==cells)return; + FarScratch scratch{}; + scratch.sampleCount=samples;scratch.cellCount=cells;scratch.groupCount=groups; + const std::uint32_t dummySign=0; + scratch.signs=Structured(&dummySign,1,D3D11_BIND_SHADER_RESOURCE); + std::array triData{}; + for(std::size_t c=0;c<256;++c) + for(std::size_t s=0;s<16;++s)triData[c*16+s]=MCTriTable[c][s]; + scratch.tri=Structured(triData.data(),triData.size(),D3D11_BIND_SHADER_RESOURCE); + scratch.density=Structured(nullptr,samples,D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); + std::vector invalidIds(cells,InvalidIndex); + scratch.midpointIds=Structured(invalidIds.data(),invalidIds.size(),D3D11_BIND_SHADER_RESOURCE); + const MidpointRecord zeroRecord{}; + scratch.midpointRecords=Structured(&zeroRecord,1,D3D11_BIND_SHADER_RESOURCE); + scratch.cases=Structured(nullptr,cells,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + scratch.localOffsets=Structured(nullptr,cells,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + scratch.groupCounts=Structured(nullptr,groups,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + scratch.groupOffsets=Structured(nullptr,groups,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + scratch.signSrv=Srv(scratch.signs.Get(),1); + scratch.triSrv=Srv(scratch.tri.Get(),256u*16u); + scratch.densitySrv=Srv(scratch.density.Get(),samples); + scratch.midpointIdSrv=Srv(scratch.midpointIds.Get(),cells); + scratch.midpointRecordSrv=Srv(scratch.midpointRecords.Get(),1); + scratch.caseSrv=Srv(scratch.cases.Get(),cells); + scratch.localOffsetSrv=Srv(scratch.localOffsets.Get(),cells); + scratch.groupCountSrv=Srv(scratch.groupCounts.Get(),groups); + scratch.groupOffsetSrv=Srv(scratch.groupOffsets.Get(),groups); + scratch.densityUav=Uav(scratch.density.Get(),samples); + scratch.caseUav=Uav(scratch.cases.Get(),cells); + scratch.localOffsetUav=Uav(scratch.localOffsets.Get(),cells); + scratch.groupCountUav=Uav(scratch.groupCounts.Get(),groups); + scratch.groupOffsetUav=Uav(scratch.groupOffsets.Get(),groups); + const auto DynamicConstant=[&](UINT bytes,const char* label) + { + D3D11_BUFFER_DESC desc{};desc.ByteWidth=bytes;desc.Usage=D3D11_USAGE_DYNAMIC; + desc.BindFlags=D3D11_BIND_CONSTANT_BUFFER;desc.CPUAccessFlags=D3D11_CPU_ACCESS_WRITE; + ComPtr buffer;Check(device->CreateBuffer(&desc,nullptr,&buffer),label);return buffer; + }; + scratch.tileConstants=DynamicConstant(sizeof(TileConstants),"CreateBuffer(tile constants pool)"); + scratch.farConstants=DynamicConstant(sizeof(FarFieldConstants),"CreateBuffer(far constants pool)"); + scratch.connectorConstants=DynamicConstant(sizeof(FarConnectorConstants), + "CreateBuffer(far connector constants pool)"); + farScratch=std::move(scratch); + } + + void EnsureConnectorRingScratch(std::uint32_t ringCount) + { + const std::uint32_t needed=std::max(1,ringCount); + if(needed<=farScratch.connectorRingCapacity)return; + std::uint32_t capacity=1;while(capacity(nullptr,capacity, + D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); + farScratch.connectorRingSrv=Srv(farScratch.connectorRings.Get(),capacity); + farScratch.connectorRingUav=Uav(farScratch.connectorRings.Get(),capacity); + farScratch.connectorRingCapacity=capacity; + } + ComPtr RawUav(ID3D11Buffer* buffer, UINT dwordCount) { D3D11_UNORDERED_ACCESS_VIEW_DESC desc{}; @@ -642,7 +900,9 @@ namespace GpuResult Run(const TileData& tile, const FarFieldTileInput* farField = nullptr, bool readback = true, bool retainOutputBuffers = false, - bool waitForGpu = true, bool useIndirectOutputCount = false) + bool waitForGpu = true, bool useIndirectOutputCount = false, + std::uint32_t outputSlot = 0, bool usePooledFarOutput = false, + bool captureGpuTiming = true,bool batchTimingMode=false) { using namespace std::chrono; const auto wallStart = Clock::now(); @@ -662,6 +922,19 @@ namespace } (void)edge; const std::size_t maxVertices = static_cast(cellCount) * 15; + std::uint32_t outputBaseVertex=0,outputArgsByteOffset=0; + FarOutputChunk* pooledChunk=nullptr; + if(usePooledFarOutput) + { + if(!farField||farOutputCells!=tile.cells||outputSlot>=farOutputCapacitySlots + ||farOutputChunks.empty()) + throw std::runtime_error("far output pool was not reserved for this tile slot"); + const std::uint32_t chunkIndex=outputSlot/farOutputSlotsPerChunk; + const std::uint32_t localSlot=outputSlot%farOutputSlotsPerChunk; + pooledChunk=&farOutputChunks[chunkIndex]; + outputBaseVertex=localSlot*farMaxVerticesPerTile; + outputArgsByteOffset=localSlot*10u*sizeof(std::uint32_t); + } if (farField && (farField->sampleDim != static_cast(tile.dim) || farField->cells != static_cast(tile.cells) || farField->step != static_cast(tile.step))) @@ -690,114 +963,224 @@ namespace + std::max(1, tile.expectedVertexCount) * sizeof(GpuVertex) + std::max(1, tile.expectedVertexCount) * sizeof(std::uint32_t) + 5 * sizeof(std::uint32_t); - auto signs = Structured(tile.signs.data(), tile.signs.size(), D3D11_BIND_SHADER_RESOURCE); - auto triBuffer = Structured(tri.data(), triCount, D3D11_BIND_SHADER_RESOURCE); - auto density = Structured(farField ? static_cast(nullptr) : tile.density.data(), - tile.density.size(), D3D11_BIND_SHADER_RESOURCE - | (farField ? D3D11_BIND_UNORDERED_ACCESS : 0)); - auto midpointIds = Structured(tile.midpointIds.data(), tile.midpointIds.size(), D3D11_BIND_SHADER_RESOURCE); - const MidpointRecord zeroRecord{}; - auto midpointRecords = Structured(tile.midpointRecords.empty() ? &zeroRecord : tile.midpointRecords.data(), - std::max(1, tile.midpointRecords.size()), D3D11_BIND_SHADER_RESOURCE); - auto cases = Structured(nullptr, cellCount, - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE); - auto localOffsets = Structured(nullptr, cellCount, - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE); - auto groupCounts = Structured(nullptr, groupCount, - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE); - auto groupOffsets = Structured(nullptr, groupCount, - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE); - auto args = RawMeshBuffer(5 * sizeof(std::uint32_t), D3D11_BIND_UNORDERED_ACCESS, - D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS | D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS); - auto vertices = RawMeshBuffer(static_cast(maxVertices * sizeof(GpuVertex)), - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VERTEX_BUFFER); - auto indices = RawMeshBuffer(static_cast(maxVertices * sizeof(std::uint32_t)), - D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_INDEX_BUFFER); - const FarFieldPrimitiveGpu zeroShape{}; - const FarFieldSampleRange zeroRange{}; - const std::uint32_t zeroId=0,zeroWord=0; - auto farShapes = farField ? Structured(farField->shapes.empty()?&zeroShape:farField->shapes.data(), - std::max(1,farField->shapes.size()),D3D11_BIND_SHADER_RESOURCE) - : ComPtr{}; - auto farRanges = farField ? Structured(farField->sampleShapeRanges.empty()?&zeroRange:farField->sampleShapeRanges.data(), - std::max(1,farField->sampleShapeRanges.size()),D3D11_BIND_SHADER_RESOURCE) - : ComPtr{}; - auto farIds = farField ? Structured(farField->sampleShapeIds.empty()?&zeroId:farField->sampleShapeIds.data(), - std::max(1,farField->sampleShapeIds.size()),D3D11_BIND_SHADER_RESOURCE) - : ComPtr{}; - auto farProtected = farField ? Structured(farField->protectedAir.empty()?&zeroWord:farField->protectedAir.data(), - std::max(1,farField->protectedAir.size()),D3D11_BIND_SHADER_RESOURCE) - : ComPtr{}; - auto farProtectedSolid = farField ? Structured(farField->protectedSolid.empty()?&zeroWord:farField->protectedSolid.data(), - std::max(1,farField->protectedSolid.size()),D3D11_BIND_SHADER_RESOURCE) - : ComPtr{}; - auto signSrv = Srv(signs.Get(), tile.signs.size()); - auto triSrv = Srv(triBuffer.Get(), triCount); - auto densitySrv = Srv(density.Get(), tile.density.size()); - auto farShapeSrv = farField ? Srv(farShapes.Get(),std::max(1,farField->shapes.size())) - : ComPtr{}; - auto farRangeSrv = farField ? Srv(farRanges.Get(),std::max(1,farField->sampleShapeRanges.size())) - : ComPtr{}; - auto farIdSrv = farField ? Srv(farIds.Get(),std::max(1,farField->sampleShapeIds.size())) - : ComPtr{}; - auto farProtectedSrv = farField ? Srv(farProtected.Get(),std::max(1,farField->protectedAir.size())) - : ComPtr{}; - auto farProtectedSolidSrv = farField ? Srv(farProtectedSolid.Get(),std::max(1,farField->protectedSolid.size())) - : ComPtr{}; - auto midpointIdSrv = Srv(midpointIds.Get(), tile.midpointIds.size()); - auto midpointRecordSrv = Srv(midpointRecords.Get(), std::max(1, tile.midpointRecords.size())); - auto caseSrv = Srv(cases.Get(), cellCount); - auto localOffsetSrv = Srv(localOffsets.Get(), cellCount); - auto groupCountSrv = Srv(groupCounts.Get(), groupCount); - auto groupOffsetSrv = Srv(groupOffsets.Get(), groupCount); - auto caseUav = Uav(cases.Get(), cellCount); - auto localOffsetUav = Uav(localOffsets.Get(), cellCount); - auto groupCountUav = Uav(groupCounts.Get(), groupCount); - auto groupOffsetUav = Uav(groupOffsets.Get(), groupCount); - auto densityUav = farField ? Uav(density.Get(),tile.density.size()) - : ComPtr{}; - auto argsUav = RawUav(args.Get(), 5); - auto vertexUav = RawUav(vertices.Get(), static_cast(maxVertices * sizeof(GpuVertex) / sizeof(std::uint32_t))); - auto indexUav = RawUav(indices.Get(), static_cast(maxVertices)); + ComPtr signs,triBuffer,density,midpointIds,midpointRecords; + ComPtr cases,localOffsets,groupCounts,groupOffsets; + ComPtr signSrv,triSrv,densitySrv,farShapeSrv,farRangeSrv,farIdSrv; + ComPtr farProtectedSrv,farProtectedSolidSrv; + ComPtr farConnectorSrv,farConnectorRingSrv; + ComPtr midpointIdSrv,midpointRecordSrv,caseSrv,localOffsetSrv; + ComPtr groupCountSrv,groupOffsetSrv; + ComPtr caseUav,localOffsetUav,groupCountUav,groupOffsetUav,densityUav; + ComPtr farConnectorRingUav; + ComPtr farConnectorConstantsBuffer; + ComPtr farConnectorRingBuffer; + if(pooledChunk) + { + EnsureFarScratch(tile); + FarScratch& scratch=farScratch; + signs=scratch.signs;triBuffer=scratch.tri;density=scratch.density; + midpointIds=scratch.midpointIds;midpointRecords=scratch.midpointRecords; + cases=scratch.cases;localOffsets=scratch.localOffsets; + groupCounts=scratch.groupCounts;groupOffsets=scratch.groupOffsets; + signSrv=scratch.signSrv;triSrv=scratch.triSrv;densitySrv=scratch.densitySrv; + midpointIdSrv=scratch.midpointIdSrv;midpointRecordSrv=scratch.midpointRecordSrv; + caseSrv=scratch.caseSrv;localOffsetSrv=scratch.localOffsetSrv; + groupCountSrv=scratch.groupCountSrv;groupOffsetSrv=scratch.groupOffsetSrv; + 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)"); + farProtectedSrv=UpdateDynamicInput(scratch.protectedAir, + farField->protectedAir.data(),farField->protectedAir.size(),"Map(far protected air)"); + farProtectedSolidSrv=UpdateDynamicInput(scratch.protectedSolid, + farField->protectedSolid.data(),farField->protectedSolid.size(),"Map(far protected solid)"); + farConnectorSrv=UpdateDynamicInput(scratch.connectors, + farField->connectorPanels.empty()?nullptr:farField->connectorPanels.data(), + farField->connectorPanels.size(),"Map(far connector panels)"); + EnsureConnectorRingScratch(farField->connectorRingCount); + farConnectorRingBuffer=scratch.connectorRings; + farConnectorRingSrv=scratch.connectorRingSrv; + farConnectorRingUav=scratch.connectorRingUav; + } + else + { + signs=Structured(tile.signs.data(),tile.signs.size(),D3D11_BIND_SHADER_RESOURCE); + triBuffer=Structured(tri.data(),triCount,D3D11_BIND_SHADER_RESOURCE); + density=Structured(farField?static_cast(nullptr):tile.density.data(), + tile.density.size(),D3D11_BIND_SHADER_RESOURCE|(farField?D3D11_BIND_UNORDERED_ACCESS:0)); + midpointIds=Structured(tile.midpointIds.data(),tile.midpointIds.size(),D3D11_BIND_SHADER_RESOURCE); + const MidpointRecord zeroRecord{}; + midpointRecords=Structured(tile.midpointRecords.empty()?&zeroRecord:tile.midpointRecords.data(), + std::max(1,tile.midpointRecords.size()),D3D11_BIND_SHADER_RESOURCE); + cases=Structured(nullptr,cellCount,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + localOffsets=Structured(nullptr,cellCount,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + groupCounts=Structured(nullptr,groupCount,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + groupOffsets=Structured(nullptr,groupCount,D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE); + signSrv=Srv(signs.Get(),tile.signs.size()); + triSrv=Srv(triBuffer.Get(),triCount); + densitySrv=Srv(density.Get(),tile.density.size()); + midpointIdSrv=Srv(midpointIds.Get(),tile.midpointIds.size()); + midpointRecordSrv=Srv(midpointRecords.Get(),std::max(1,tile.midpointRecords.size())); + caseSrv=Srv(cases.Get(),cellCount); + localOffsetSrv=Srv(localOffsets.Get(),cellCount); + groupCountSrv=Srv(groupCounts.Get(),groupCount); + groupOffsetSrv=Srv(groupOffsets.Get(),groupCount); + caseUav=Uav(cases.Get(),cellCount);localOffsetUav=Uav(localOffsets.Get(),cellCount); + groupCountUav=Uav(groupCounts.Get(),groupCount);groupOffsetUav=Uav(groupOffsets.Get(),groupCount); + densityUav=farField?Uav(density.Get(),tile.density.size()):ComPtr{}; + 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(1,farField->shapes.size()),D3D11_BIND_SHADER_RESOURCE); + auto farRanges=Structured(farField->sampleShapeRanges.empty()?&zeroRange:farField->sampleShapeRanges.data(), + std::max(1,farField->sampleShapeRanges.size()),D3D11_BIND_SHADER_RESOURCE); + auto farIds=Structured(farField->sampleShapeIds.empty()?&zeroId:farField->sampleShapeIds.data(), + std::max(1,farField->sampleShapeIds.size()),D3D11_BIND_SHADER_RESOURCE); + auto farProtected=Structured(farField->protectedAir.empty()?&zeroWord:farField->protectedAir.data(), + std::max(1,farField->protectedAir.size()),D3D11_BIND_SHADER_RESOURCE); + auto farProtectedSolid=Structured(farField->protectedSolid.empty()?&zeroWord:farField->protectedSolid.data(), + std::max(1,farField->protectedSolid.size()),D3D11_BIND_SHADER_RESOURCE); + farShapeSrv=Srv(farShapes.Get(),std::max(1,farField->shapes.size())); + farRangeSrv=Srv(farRanges.Get(),std::max(1,farField->sampleShapeRanges.size())); + farIdSrv=Srv(farIds.Get(),std::max(1,farField->sampleShapeIds.size())); + farProtectedSrv=Srv(farProtected.Get(),std::max(1,farField->protectedAir.size())); + farProtectedSolidSrv=Srv(farProtectedSolid.Get(),std::max(1,farField->protectedSolid.size())); + const FarFieldConnectorPanel zeroPanel{}; + auto farConnectors=Structured(farField->connectorPanels.empty()?&zeroPanel: + farField->connectorPanels.data(),std::max(1, + farField->connectorPanels.size()),D3D11_BIND_SHADER_RESOURCE); + farConnectorSrv=Srv(farConnectors.Get(), + std::max(1,farField->connectorPanels.size())); + const std::uint32_t ringCapacity=std::max(1,farField->connectorRingCount); + farConnectorRingBuffer=Structured(nullptr,ringCapacity, + D3D11_BIND_SHADER_RESOURCE|D3D11_BIND_UNORDERED_ACCESS); + farConnectorRingSrv=Srv(farConnectorRingBuffer.Get(),ringCapacity); + farConnectorRingUav=Uav(farConnectorRingBuffer.Get(),ringCapacity); + } + } + ComPtr args,vertices,indices; + ComPtr argsUav,vertexUav,indexUav; + ComPtr connectorVertices,connectorIndices; + ComPtr connectorVertexUav,connectorIndexUav; + const std::uint32_t connectorPanelCapacity=pooledChunk?FarPanelVertexCapacity: + std::max(1,farField?farField->connectorVertexCount:0); + if(farField&&farField->connectorVertexCount>connectorPanelCapacity) + throw std::runtime_error("far connector repair output exceeds the pooled panel capacity"); + if(pooledChunk) + { + args=pooledChunk->args;vertices=pooledChunk->vertices;indices=pooledChunk->indices; + argsUav=pooledChunk->argsUav;vertexUav=pooledChunk->vertexUav;indexUav=pooledChunk->indexUav; + connectorVertices=pooledChunk->connectorVertices;connectorIndices=pooledChunk->connectorIndices; + connectorVertexUav=pooledChunk->connectorVertexUav;connectorIndexUav=pooledChunk->connectorIndexUav; + } + else + { + args = RawMeshBuffer(10 * sizeof(std::uint32_t), D3D11_BIND_UNORDERED_ACCESS, + D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS | D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS); + vertices = RawMeshBuffer(static_cast(maxVertices * sizeof(GpuVertex)), + D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_VERTEX_BUFFER); + indices = RawMeshBuffer(static_cast(maxVertices * sizeof(std::uint32_t)), + D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_INDEX_BUFFER); + argsUav=RawUav(args.Get(),10); + vertexUav=RawUav(vertices.Get(),static_cast(maxVertices*sizeof(GpuVertex)/sizeof(std::uint32_t))); + indexUav=RawUav(indices.Get(),static_cast(maxVertices)); + connectorVertices=RawMeshBuffer(connectorPanelCapacity*sizeof(GpuVertex), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_VERTEX_BUFFER); + connectorIndices=RawMeshBuffer(connectorPanelCapacity*sizeof(std::uint32_t), + D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_INDEX_BUFFER); + connectorVertexUav=RawUav(connectorVertices.Get(),connectorPanelCapacity*sizeof(GpuVertex)/sizeof(std::uint32_t)); + connectorIndexUav=RawUav(connectorIndices.Get(),connectorPanelCapacity); + } D3D11_BUFFER_DESC cbDesc{}; cbDesc.ByteWidth = sizeof(TileConstants); cbDesc.Usage = D3D11_USAGE_DEFAULT; cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; TileConstants constants{tile.cells, tile.dim, cellCount, groupCount, - tile.origin[0], tile.origin[1], tile.origin[2], static_cast(tile.step), 25.0f,{}}; - D3D11_SUBRESOURCE_DATA cbData{}; cbData.pSysMem = &constants; + tile.origin[0], tile.origin[1], tile.origin[2], static_cast(tile.step), + 25.0f,outputBaseVertex,outputArgsByteOffset,0}; + result.outputBaseVertex=outputBaseVertex; + result.outputArgsByteOffset=outputArgsByteOffset; ComPtr cb; - Check(device->CreateBuffer(&cbDesc, &cbData, &cb), "CreateBuffer(constants)"); ComPtr farCb; if (farField) { const FarFieldConstants farConstants{farField->strataLo,farField->strataHi, farField->strataBlend,static_cast(tile.density.size())}; - D3D11_BUFFER_DESC farDesc{};farDesc.ByteWidth=sizeof(FarFieldConstants); - farDesc.Usage=D3D11_USAGE_DEFAULT;farDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER; - D3D11_SUBRESOURCE_DATA farData{};farData.pSysMem=&farConstants; - Check(device->CreateBuffer(&farDesc,&farData,&farCb),"CreateBuffer(far constants)"); + const FarConnectorConstants connectorConstants{ + static_cast(farField->connectorPanels.size()), + farField->connectorRingCount,farField->connectorVertexCount,connectorPanelCapacity, + static_cast(farField->shapes.size()),{0,0,0}}; + if(pooledChunk) + { + cb=farScratch.tileConstants;farCb=farScratch.farConstants; + D3D11_MAPPED_SUBRESOURCE tileMapped{}; + Check(context->Map(cb.Get(),0,D3D11_MAP_WRITE_DISCARD,0,&tileMapped),"Map(tile constants)"); + std::memcpy(tileMapped.pData,&constants,sizeof(constants));context->Unmap(cb.Get(),0); + D3D11_MAPPED_SUBRESOURCE farMapped{}; + Check(context->Map(farCb.Get(),0,D3D11_MAP_WRITE_DISCARD,0,&farMapped),"Map(far constants)"); + std::memcpy(farMapped.pData,&farConstants,sizeof(farConstants));context->Unmap(farCb.Get(),0); + farConnectorConstantsBuffer=farScratch.connectorConstants; + D3D11_MAPPED_SUBRESOURCE connectorMapped{}; + Check(context->Map(farConnectorConstantsBuffer.Get(),0,D3D11_MAP_WRITE_DISCARD,0, + &connectorMapped),"Map(far connector constants)"); + std::memcpy(connectorMapped.pData,&connectorConstants,sizeof(connectorConstants)); + context->Unmap(farConnectorConstantsBuffer.Get(),0); + } + else + { + D3D11_SUBRESOURCE_DATA cbData{};cbData.pSysMem=&constants; + Check(device->CreateBuffer(&cbDesc,&cbData,&cb),"CreateBuffer(constants)"); + D3D11_BUFFER_DESC farDesc{};farDesc.ByteWidth=sizeof(FarFieldConstants); + farDesc.Usage=D3D11_USAGE_DEFAULT;farDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER; + D3D11_SUBRESOURCE_DATA farData{};farData.pSysMem=&farConstants; + Check(device->CreateBuffer(&farDesc,&farData,&farCb),"CreateBuffer(far constants)"); + D3D11_BUFFER_DESC connectorDesc{};connectorDesc.ByteWidth=sizeof(FarConnectorConstants); + connectorDesc.Usage=D3D11_USAGE_DEFAULT;connectorDesc.BindFlags=D3D11_BIND_CONSTANT_BUFFER; + D3D11_SUBRESOURCE_DATA connectorData{};connectorData.pSysMem=&connectorConstants; + Check(device->CreateBuffer(&connectorDesc,&connectorData,&farConnectorConstantsBuffer), + "CreateBuffer(far connector constants)"); + } + } + else + { + D3D11_SUBRESOURCE_DATA cbData{};cbData.pSysMem=&constants; + Check(device->CreateBuffer(&cbDesc,&cbData,&cb),"CreateBuffer(constants)"); } D3D11_QUERY_DESC timestampDesc{D3D11_QUERY_TIMESTAMP, 0}; D3D11_QUERY_DESC disjointDesc{D3D11_QUERY_TIMESTAMP_DISJOINT, 0}; ComPtr startQuery, endQuery, disjointQuery; ComPtr fieldStartQuery,fieldEndQuery; - Check(device->CreateQuery(×tampDesc, &startQuery), "CreateQuery(start)"); - Check(device->CreateQuery(×tampDesc, &endQuery), "CreateQuery(end)"); - Check(device->CreateQuery(&disjointDesc, &disjointQuery), "CreateQuery(disjoint)"); - if(farField) + if(captureGpuTiming) { - Check(device->CreateQuery(×tampDesc,&fieldStartQuery),"CreateQuery(field start)"); - Check(device->CreateQuery(×tampDesc,&fieldEndQuery),"CreateQuery(field end)"); + Check(device->CreateQuery(×tampDesc, &startQuery), "CreateQuery(start)"); + Check(device->CreateQuery(×tampDesc, &endQuery), "CreateQuery(end)"); + if(!batchTimingMode) + Check(device->CreateQuery(&disjointDesc, &disjointQuery), "CreateQuery(disjoint)"); + if(farField&&!batchTimingMode) + { + Check(device->CreateQuery(×tampDesc,&fieldStartQuery),"CreateQuery(field start)"); + Check(device->CreateQuery(×tampDesc,&fieldEndQuery),"CreateQuery(field end)"); + } } context->CSSetConstantBuffers(0, 1, cb.GetAddressOf()); - context->Begin(disjointQuery.Get()); - context->End(startQuery.Get()); + if(captureGpuTiming&&!batchTimingMode) + { + context->Begin(disjointQuery.Get()); + context->End(startQuery.Get()); + } + else if(captureGpuTiming)context->End(startQuery.Get()); if(farField) { - context->End(fieldStartQuery.Get()); + if(captureGpuTiming&&!batchTimingMode)context->End(fieldStartQuery.Get()); context->CSSetConstantBuffers(1,1,farCb.GetAddressOf()); context->CSSetShader(fieldEval.Get(),nullptr,0); ID3D11ShaderResourceView* fieldSrvs[15]{}; @@ -812,7 +1195,7 @@ namespace ID3D11UnorderedAccessView* clearUavs[8]{}; context->CSSetShaderResources(0,16,clearSrvs); context->CSSetUnorderedAccessViews(0,8,clearUavs,nullptr); - context->End(fieldEndQuery.Get()); + if(captureGpuTiming&&!batchTimingMode)context->End(fieldEndQuery.Get()); } context->CSSetShader(farField ? fieldClassify.Get() : classify.Get(), nullptr, 0); ID3D11ShaderResourceView* classifySrvs[16]{}; @@ -847,8 +1230,38 @@ namespace context->Dispatch(groupCount, 1, 1); context->CSSetShaderResources(0, 16, nullSrvs); context->CSSetUnorderedAccessViews(0, 7, nullUavs, nullptr); - context->End(endQuery.Get()); - context->End(disjointQuery.Get()); + + if(farField) + { + ID3D11Buffer* connectorCbs[3]={cb.Get(),farCb.Get(),farConnectorConstantsBuffer.Get()}; + context->CSSetConstantBuffers(0,3,connectorCbs); + context->CSSetShader(connectorRings.Get(),nullptr,0); + ID3D11ShaderResourceView* connectorRingSrvs[3]={farConnectorSrv.Get(),nullptr,farShapeSrv.Get()}; + context->CSSetShaderResources(8,3,connectorRingSrvs); + ID3D11UnorderedAccessView* connectorRingUavs[8]{}; + connectorRingUavs[4]=argsUav.Get();connectorRingUavs[7]=farConnectorRingUav.Get(); + context->CSSetUnorderedAccessViews(0,8,connectorRingUavs,nullptr); + context->Dispatch(std::max(1,(farField->connectorRingCount+63u)/64u),1,1); + context->CSSetShaderResources(8,3,nullSrvs); + context->CSSetUnorderedAccessViews(0,8,nullUavs,nullptr); + if(farField->connectorVertexCount) + { + context->CSSetShader(connectorPanels.Get(),nullptr,0); + ID3D11ShaderResourceView* panelSrvs[2]={farConnectorSrv.Get(),farConnectorRingSrv.Get()}; + context->CSSetShaderResources(8,2,panelSrvs); + ID3D11UnorderedAccessView* panelUavs[7]{}; + panelUavs[5]=connectorVertexUav.Get();panelUavs[6]=connectorIndexUav.Get(); + context->CSSetUnorderedAccessViews(0,7,panelUavs,nullptr); + context->Dispatch((farField->connectorVertexCount+63u)/64u,1,1); + context->CSSetShaderResources(8,2,nullSrvs); + context->CSSetUnorderedAccessViews(0,7,nullUavs,nullptr); + } + } + if(captureGpuTiming) + { + context->End(endQuery.Get()); + if(!batchTimingMode)context->End(disjointQuery.Get()); + } if (readback) { @@ -861,7 +1274,11 @@ namespace * sizeof(GpuVertex))); auto indexStaging = Staging(static_cast(readbackCapacity * sizeof(std::uint32_t))); - auto argsStaging = Staging(5 * sizeof(std::uint32_t)); + auto argsStaging = Staging(10 * sizeof(std::uint32_t)); + auto connectorVertexStaging=farField&&farField->connectorVertexCount + ?Staging(farField->connectorVertexCount*sizeof(GpuVertex)):ComPtr{}; + auto connectorIndexStaging=farField&&farField->connectorVertexCount + ?Staging(farField->connectorVertexCount*sizeof(std::uint32_t)):ComPtr{}; if (QueryLocalMemory(localUsage, localBudget)) { result.hasAdapterMemoryInfo = true; @@ -874,21 +1291,45 @@ namespace if (copyBytes != 0) { const D3D11_BOX box{0, 0, 0, copyBytes, 1, 1}; + const UINT64 vertexStart=static_cast(outputBaseVertex)*sizeof(GpuVertex); + D3D11_BOX vertexSource{static_cast(vertexStart),0,0, + static_cast(vertexStart+copyBytes),1,1}; context->CopySubresourceRegion(vertexStaging.Get(), 0, 0, 0, 0, - vertices.Get(), 0, &box); + vertices.Get(), 0, &vertexSource); const D3D11_BOX indexBox{0, 0, 0, (useIndirectOutputCount?readbackCapacity:tile.expectedVertexCount)*sizeof(std::uint32_t),1,1}; + D3D11_BOX indexSource=indexBox; + indexSource.left=outputBaseVertex*sizeof(std::uint32_t); + indexSource.right=indexSource.left+indexBox.right; context->CopySubresourceRegion(indexStaging.Get(), 0, 0, 0, 0, - indices.Get(), 0, &indexBox); + indices.Get(), 0, &indexSource); + } + const D3D11_BOX argsSource{outputArgsByteOffset,0,0, + outputArgsByteOffset+10u*sizeof(std::uint32_t),1,1}; + context->CopySubresourceRegion(argsStaging.Get(),0,0,0,0,args.Get(),0,&argsSource); + std::uint32_t connectorBase=0; + if(farField&&farField->connectorVertexCount) + { + connectorBase=pooledChunk?outputBaseVertex/farMaxVerticesPerTile*FarPanelVertexCapacity:0; + const UINT vertexBytes=farField->connectorVertexCount*sizeof(GpuVertex); + const UINT indexBytes=farField->connectorVertexCount*sizeof(std::uint32_t); + const D3D11_BOX connectorVertexSource{connectorBase*sizeof(GpuVertex),0,0, + connectorBase*sizeof(GpuVertex)+vertexBytes,1,1}; + const D3D11_BOX connectorIndexSource{connectorBase*sizeof(std::uint32_t),0,0, + connectorBase*sizeof(std::uint32_t)+indexBytes,1,1}; + context->CopySubresourceRegion(connectorVertexStaging.Get(),0,0,0,0, + connectorVertices.Get(),0,&connectorVertexSource); + context->CopySubresourceRegion(connectorIndexStaging.Get(),0,0,0,0, + connectorIndices.Get(),0,&connectorIndexSource); } - context->CopyResource(argsStaging.Get(), args.Get()); if(farField)context->CopyResource(fieldStaging.Get(),density.Get()); context->Flush(); + if(!captureGpuTiming||batchTimingMode)throw std::runtime_error("readback requires standalone GPU timestamp queries"); WaitForQuery(disjointQuery.Get()); const D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint = ReadQuery(disjointQuery.Get(), "GetData(disjoint)"); const UINT64 start = ReadQuery(startQuery.Get(), "GetData(start timestamp)"); const UINT64 end = ReadQuery(endQuery.Get(), "GetData(end timestamp)"); - result.dispatchMs = disjoint.Disjoint ? 0.0 : + result.dispatchMs = disjoint.Disjoint ? 0.0 : 1000.0 * static_cast(end - start) / static_cast(disjoint.Frequency); D3D11_MAPPED_SUBRESOURCE mapped{}; if(farField) @@ -907,13 +1348,18 @@ namespace static_cast(mapped.pData) + cellCount); context->Unmap(caseStaging.Get(), 0); Check(context->Map(argsStaging.Get(), 0, D3D11_MAP_READ, 0, &mapped), "Map(args)"); - std::copy_n(static_cast(mapped.pData), 5, result.drawArgs.begin()); + std::array allArgs{}; + std::copy_n(static_cast(mapped.pData),10,allArgs.begin()); + std::copy_n(allArgs.begin(),5,result.drawArgs.begin()); context->Unmap(argsStaging.Get(), 0); result.indexedIndirectArgsValid = (useIndirectOutputCount ?result.drawArgs[0]<=maxVertices :result.drawArgs[0] == tile.expectedVertexCount) - && result.drawArgs[1] == 1 && result.drawArgs[2] == 0 + && result.drawArgs[1] == 1 && result.drawArgs[2] == outputBaseVertex && result.drawArgs[3] == 0 && result.drawArgs[4] == 0; + result.indexedIndirectArgsValid &= allArgs[5]==(farField?farField->connectorVertexCount:0) + &&allArgs[6]==(farField&&farField->connectorVertexCount?1u:0u) + &&allArgs[7]==connectorBase&&allArgs[8]==connectorBase&&allArgs[9]==0; const std::uint32_t outputCount = std::min(result.drawArgs[0], static_cast(maxVertices)); // The staging buffer contains the GPU output. Only retain the prefix named by @@ -926,12 +1372,27 @@ namespace const auto* indexData = static_cast(mapped.pData); result.sequentialIndices = useIndirectOutputCount||outputCount == tile.expectedVertexCount; for (std::uint32_t i = 0; i < outputCount; ++i) - result.sequentialIndices &= indexData[i] == i; + result.sequentialIndices &= indexData[i] == outputBaseVertex+i; context->Unmap(indexStaging.Get(), 0); + if(farField&&farField->connectorVertexCount) + { + Check(context->Map(connectorVertexStaging.Get(),0,D3D11_MAP_READ,0,&mapped), + "Map(far connector panel vertices)"); + const auto* connectorData=static_cast(mapped.pData); + result.vertices.insert(result.vertices.end(),connectorData, + connectorData+farField->connectorVertexCount); + context->Unmap(connectorVertexStaging.Get(),0); + Check(context->Map(connectorIndexStaging.Get(),0,D3D11_MAP_READ,0,&mapped), + "Map(far connector panel indices)"); + const auto* connectorIndexData=static_cast(mapped.pData); + for(std::uint32_t i=0;iconnectorVertexCount;++i) + result.sequentialIndices &= connectorIndexData[i]==connectorBase+i; + context->Unmap(connectorIndexStaging.Get(),0); + } } else { - if(waitForGpu) + if(waitForGpu&&captureGpuTiming&&!batchTimingMode) { context->Flush(); WaitForQuery(disjointQuery.Get()); @@ -948,12 +1409,20 @@ namespace /static_cast(disjoint.Frequency); } } + else if(captureGpuTiming) + { + result.startTimestamp=std::move(startQuery); + result.endTimestamp=std::move(endQuery); + if(!batchTimingMode)result.disjointTimestamp=std::move(disjointQuery); + } } if (retainOutputBuffers) { result.vertexBuffer = vertices; result.indexBuffer = indices; result.indirectArgs = args; + result.connectorVertexBuffer=connectorVertices; + result.connectorIndexBuffer=connectorIndices; } result.wallMs = duration(Clock::now() - wallStart).count(); return result; @@ -1651,15 +2120,81 @@ namespace } } -#include "GpuFarViewer.h" - namespace VoxelForgeGpuFarViewer { + namespace + { + std::unique_ptr FarViewerRunner; + ID3D11Device* FarViewerRunnerDevice=nullptr; + + DeviceRun& GetFarViewerRunner(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath) + { + if(!FarViewerRunner||FarViewerRunnerDevice!=device) + { + FarViewerRunner=std::make_unique(); + FarViewerRunner->InitializeExternal(device,context,shaderPath); + FarViewerRunnerDevice=device; + } + return *FarViewerRunner; + } + } + + bool ReserveFarOutputPool(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,std::uint32_t tileSlots,std::uint32_t cells, + std::string& error) + { + try + { + GetFarViewerRunner(device,context,shaderPath).EnsureFarOutputPool(tileSlots,cells); + return true; + } + catch(const std::exception& e){error=e.what();return false;} + } + + bool QueryFarGpuMemory(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,std::uint64_t& usageBytes, + std::uint64_t& budgetBytes,std::string& error) + { + try + { + return GetFarViewerRunner(device,context,shaderPath).QueryLocalMemory(usageBytes,budgetBytes); + } + catch(const std::exception& e){error=e.what();return false;} + } + + bool BeginFarTileBatch(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,std::string& error) + { + try + { + GetFarViewerRunner(device,context,shaderPath).BeginFarTileBatch(); + return true; + } + catch(const std::exception& e){error=e.what();return false;} + } + + bool SubmitFarTileBatch(ID3D11Device* device,ID3D11DeviceContext* context, + const std::filesystem::path& shaderPath,ID3D11Query** completionEvent, + GpuTimingQueries* timingQueries,std::string& error) + { + try + { + GetFarViewerRunner(device,context,shaderPath).SubmitFarTileBatch(completionEvent,timingQueries); + return true; + } + catch(const std::exception& e){error=e.what();return false;} + } + bool BuildFarTile(ID3D11Device* device, ID3D11DeviceContext* context, VoxelForgeCore::World& world, const VoxelForgeCore::TileKey& key, + const FarFieldTileInput* preparedInput, const std::filesystem::path& shaderPath, ID3D11Buffer** vertices, ID3D11Buffer** indices, ID3D11Buffer** indirectArgs, - ID3D11Query** completionEvent, + ID3D11Buffer** connectorVertices,ID3D11Buffer** connectorIndices, + std::uint32_t outputSlot,std::uint32_t outputCapacitySlots, + std::uint32_t* indirectArgsByteOffset,ID3D11Query** completionEvent, + GpuTimingQueries* timingQueries, CpuMesh* auditReadback, double* enqueueWallMs, double* gpuDispatchMs, std::string& error) { @@ -1667,21 +2202,19 @@ namespace VoxelForgeGpuFarViewer if (vertices) *vertices = nullptr; if (indices) *indices = nullptr; if (indirectArgs) *indirectArgs = nullptr; + if (connectorVertices) *connectorVertices=nullptr; + if (connectorIndices) *connectorIndices=nullptr; + if (indirectArgsByteOffset) *indirectArgsByteOffset = 0; if (completionEvent) *completionEvent = nullptr; + if (timingQueries) *timingQueries={}; if (enqueueWallMs) *enqueueWallMs = 0.0; if (gpuDispatchMs) *gpuDispatchMs = 0.0; try { - if (!vertices || !indices || !indirectArgs || !completionEvent) + if (!vertices || !indices || !indirectArgs || !connectorVertices || !connectorIndices + ||(auditReadback&&!completionEvent)) throw std::runtime_error("far tile output pointers are required"); - static std::unique_ptr runner; - static ID3D11Device* runnerDevice = nullptr; - if (!runner || runnerDevice != device) - { - runner = std::make_unique(); - runner->InitializeExternal(device, context, shaderPath); - runnerDevice = device; - } + DeviceRun& runner=GetFarViewerRunner(device,context,shaderPath); const WorldConfiguration& config = world.configuration(); std::uint32_t cells = 0; @@ -1699,10 +2232,19 @@ namespace VoxelForgeGpuFarViewer tile.signs.resize((sampleCount + 31u) / 32u); tile.midpointIds.assign(cellCount, InvalidIndex); - auto farInput = world.exportFarFieldTile(key, error); - if (!farInput) return false; - GpuResult result = runner->Run(tile, &*farInput,auditReadback!=nullptr,true, - true,auditReadback!=nullptr); + std::optional localFarInput; + if(!preparedInput) + { + localFarInput=world.exportFarFieldTile(key,error); + if(!localFarInput)return false; + preparedInput=&*localFarInput; + } + const std::uint32_t outputCapacity=outputCapacitySlots?outputCapacitySlots:outputSlot+1; + runner.EnsureFarOutputPool(outputCapacity,tile.cells); + const bool captureGpuTiming=true; + const bool batchTimingMode=completionEvent==nullptr; + GpuResult result = runner.Run(tile, preparedInput,auditReadback!=nullptr,true, + auditReadback!=nullptr,auditReadback!=nullptr,outputSlot,true,captureGpuTiming,batchTimingMode); if(auditReadback) { auditReadback->vertices.resize(result.vertices.size()); @@ -1717,14 +2259,26 @@ namespace VoxelForgeGpuFarViewer auditReadback->indices.resize(result.vertices.size()); std::iota(auditReadback->indices.begin(),auditReadback->indices.end(),0u); } - D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; - ComPtr completion; - Check(device->CreateQuery(&eventDesc,&completion),"CreateQuery(far tile completion)"); - context->End(completion.Get()); + if(completionEvent) + { + D3D11_QUERY_DESC eventDesc{D3D11_QUERY_EVENT,0}; + ComPtr completion; + Check(device->CreateQuery(&eventDesc,&completion),"CreateQuery(far completion)"); + context->End(completion.Get()); + *completionEvent=completion.Detach(); + } *vertices = result.vertexBuffer.Detach(); *indices = result.indexBuffer.Detach(); *indirectArgs = result.indirectArgs.Detach(); - *completionEvent = completion.Detach(); + *connectorVertices=result.connectorVertexBuffer.Detach(); + *connectorIndices=result.connectorIndexBuffer.Detach(); + if(indirectArgsByteOffset)*indirectArgsByteOffset=result.outputArgsByteOffset; + if(timingQueries&&result.startTimestamp) + { + timingQueries->start=result.startTimestamp.Detach(); + timingQueries->end=result.endTimestamp.Detach(); + timingQueries->disjoint=result.disjointTimestamp.Detach(); + } if (enqueueWallMs) *enqueueWallMs = std::chrono::duration(Clock::now()-wallStart).count(); if (gpuDispatchMs) *gpuDispatchMs = result.dispatchMs; return true; @@ -1735,6 +2289,51 @@ namespace VoxelForgeGpuFarViewer return false; } } + + bool ResolveGpuTiming(ID3D11DeviceContext* context,GpuTimingQueries& queries,double& dispatchMs, + std::uint64_t* timestampFrequency) + { + dispatchMs=0.0; + if(timestampFrequency)*timestampFrequency=0; + if(!context||!queries.start||!queries.end||!queries.disjoint)return true; + D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjoint{};UINT64 start=0,end=0; + HRESULT hr=context->GetData(queries.disjoint,&disjoint,sizeof(disjoint),D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_FALSE)return false; + Check(hr,"GetData(async timestamp disjoint)"); + hr=context->GetData(queries.start,&start,sizeof(start),D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_FALSE)return false; + Check(hr,"GetData(async start timestamp)"); + hr=context->GetData(queries.end,&end,sizeof(end),D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_FALSE)return false; + Check(hr,"GetData(async end timestamp)"); + if(timestampFrequency&&!disjoint.Disjoint)*timestampFrequency=disjoint.Frequency; + if(!disjoint.Disjoint&&disjoint.Frequency) + dispatchMs=1000.0*static_cast(end-start)/static_cast(disjoint.Frequency); + queries.start->Release();queries.start=nullptr; + queries.end->Release();queries.end=nullptr; + queries.disjoint->Release();queries.disjoint=nullptr; + return true; + } + + bool ResolveGpuTimestampPair(ID3D11DeviceContext* context,GpuTimingQueries& queries, + std::uint64_t timestampFrequency,double& dispatchMs) + { + dispatchMs=0.0; + if(!context||!queries.start||!queries.end||!timestampFrequency)return true; + UINT64 start=0,end=0; + HRESULT hr=context->GetData(queries.start,&start,sizeof(start),D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_FALSE)return false; + Check(hr,"GetData(async tile start timestamp)"); + hr=context->GetData(queries.end,&end,sizeof(end),D3D11_ASYNC_GETDATA_DONOTFLUSH); + if(hr==S_FALSE)return false; + Check(hr,"GetData(async tile end timestamp)"); + if(end>=start)dispatchMs=1000.0*static_cast(end-start) + /static_cast(timestampFrequency); + queries.start->Release();queries.start=nullptr; + queries.end->Release();queries.end=nullptr; + if(queries.disjoint){queries.disjoint->Release();queries.disjoint=nullptr;} + return true; + } } #ifndef VF_GPU_FIELD_PORT_NO_MAIN diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunGpuFarViewer.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunGpuFarViewer.ps1 index c94ce61..9b9fec4 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunGpuFarViewer.ps1 +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/RunGpuFarViewer.ps1 @@ -3,7 +3,8 @@ param( [ValidateSet('gpu','cpu','audit')][string]$Mode='gpu', [string]$LogStem='gpu_far_viewer', [int]$ClipRadius=3, - [string]$CaptureName='' + [string]$CaptureName='', + [string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe' ) $ErrorActionPreference='Stop' @@ -12,7 +13,9 @@ $HostProcess=Get-Process -Id $PID $HostProcess.PriorityClass='BelowNormal' $HostProcess.ProcessorAffinity=[IntPtr]0xFF00 $StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) -$ExecutablePath=Join-Path $StandaloneRoot 'bin\VoxelForgeGpuFarHybridViewer.exe' +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename, not a path.'} +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) $StdoutPath=Join-Path $PSScriptRoot ($LogStem+'.stdout.log') $StderrPath=Join-Path $PSScriptRoot ($LogStem+'.stderr.log') if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU far viewer not found: $ExecutablePath"} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP4_REPORT.md b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP4_REPORT.md new file mode 100644 index 0000000..3f89cfa --- /dev/null +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/STEP4_REPORT.md @@ -0,0 +1,76 @@ +# GPU far streaming, step 4 + +## Result + +The GPU far viewer now records up to 32 tiles into a deferred D3D11 command list, executes one list per update batch, and polls one completion event per batch. The normal path has no per-tile GPU wait or readback. Persistent pooled mesh buffers remain in use, including a separate connector-panel pool drawn with the base far mesh. **This removes most of the CPU submission overhead, but the measured whole-view cold time is still slower than all-CPU.** + +The viewer keeps CPU generation for LOD0/1 and 152 transition tiles. GPU field evaluation and marching cubes cover the other 1,114 far tiles. Press **H** to switch between CPU-far and GPU-far. + +## Connector and topology evidence + +The final viewer binary passed `RunGpuFarViewer.ps1 -Mode audit -ClipRadius 3`. The GPU mesh survival audit reported: + +- 7,200/7,200 strata-boundary surfaces, with zero missing, extra, or hole counts. +- 10,976/10,976 far connectors audited; 178,351 connector samples and 710,047 rays, with zero missing samples and zero connector holes. +- `status=PASS` and `mesh_source=GPU field_readback=required`. + +The audit reports 49,585 connector-ray `extra` hits; those are tolerated by the existing repair-panel survival criteria. The missing and hole counts, which gate survival, are zero. + +The batch refactor only changes viewer submission and timing. It does not change field evaluation, sign overrides, or marching-cubes cases. The prior step-3 whole-far agreement covered all 1,114 tiles: zero ordinary sign differences, zero cell-configuration differences against the RX 7900 XTX and WARP, zero protected/thin topology differences, and repeat RX output was bit-identical. Those agreement results are recorded in [the step-3 report](REPORT.md); they were not rerun for this submission-only change. + +`CompareFarCpuHashes.ps1` still reports 44/44 hash fields equal with zero differences against the retained W8 reference logs. That script compares the saved `566d1f3` reference log to the saved current-path log; it is not a fresh CPU-path hash run. + +## Cold timings + +Commands, all with `--workers=8`, BelowNormal priority, affinity `0xFF00`, and clip radius 3: + +```powershell +RunGpuFarViewer.ps1 -Mode cpu -LogStem step4_memory_cpu_A -ClipRadius 3 -ExecutableName VoxelForgeGpuFarStep4Viewer.exe +RunGpuFarViewer.ps1 -Mode gpu -LogStem step4_memory_gpu_A -ClipRadius 3 -ExecutableName VoxelForgeGpuFarStep4Viewer.exe +RunGpuFarViewer.ps1 -Mode gpu -LogStem step4_memory_gpu_B -ClipRadius 3 -ExecutableName VoxelForgeGpuFarStep4Viewer.exe +RunGpuFarViewer.ps1 -Mode cpu -LogStem step4_memory_cpu_B -ClipRadius 3 -ExecutableName VoxelForgeGpuFarStep4Viewer.exe +``` + +| Cold clipmap | A | B | Mean | +| --- | ---: | ---: | ---: | +| All CPU, 2,226 tiles | 2.189 s | 2.387 s | 2.288 s | +| CPU near + GPU far, 1,114 GPU tiles | 4.359 s | 4.361 s | 4.360 s | + +The hybrid is 2.072 s slower on average (1.91x). CPU near/transition work took 3.214 s and 3.114 s wall, despite 1.566 s and 1.698 s of worker time summed across workers. The all-CPU runs took 2.093 s and 2.154 s wall for the full 2,226 tiles. This near-stream timing remains a major unexplained contributor to the hybrid result. + +CPU tile-worker time fell from a 4.745 s mean to 1.632 s, saving 3.113 worker-seconds. Parallel far-input export used another 1.598 worker-seconds on average, leaving about 1.515 worker-seconds net before main-thread upload/enqueue and pool work. This worker-time reduction did not reduce whole-view wall time. + +The batch design reduced summed CPU upload/enqueue time from about 723 ms before batching to 46.5 ms and 45.0 ms in the final runs. Each normal run used 37 submissions for 1,114 tiles, at most 32 tiles per submission. The command list still contains per-tile compute dispatch sequences; this is command-list batching rather than a single multi-tile shader dispatch. + +## GPU timing and 90 Hz slicing + +Two query levels are reported separately. Per-tile timestamp pairs bracket each tile's field, classify, emit, and connector compute sequence. A separate timestamp set brackets command-list execution, including its deferred resource updates. + +| Measurement across 1,114 GPU tiles | Run A | Run B | Mean | +| --- | ---: | ---: | ---: | +| Tile dispatch timestamp sum | 640.7 ms | 650.5 ms | 645.6 ms | +| Mean dispatch time per tile | 0.575 ms | 0.584 ms | 0.580 ms | +| Slowest tile dispatch | 3.924 ms | 3.990 ms | 3.990 ms | +| Command-list execution timestamp sum | 762.4 ms | 817.3 ms | 789.8 ms | +| Slowest command-list span (up to 32 tiles) | 57.6 ms | 62.5 ms | 60.1 ms | + +For 0.5 ms of compute per 90 Hz frame, the measured dispatch sum needs about 1,292 frame slices (14.4 s). For 1 ms per frame it needs about 646 slices (7.2 s). At the mean tile cost, that is 0.86 tiles per 0.5 ms slice or 1.72 tiles per 1 ms slice. These are budget estimates from summed dispatch timestamps, not a VR frame-pacing run; a tile cannot be divided by the current scheduler, and the slowest tile alone took about 4 ms. The current 32-tile batches are not frame-budgeted: their largest measured execution span was 62.5 ms. + +## Memory and limits + +The final timed runs peaked at 0.94 GiB and 0.85 GiB working set, and 4.66 GiB and 4.60 GiB private process memory. DXGI sampled local GPU-memory peaks of 3,689.8 MiB and 3,690.9 MiB (3.60 GiB) against a reported 23,749 MiB (23.2 GiB) budget. The higher run's working set plus local GPU residency was about 4.54 GiB. Private-byte totals and DXGI residency can overlap because the driver accounts for resource backing separately; they are reported separately rather than added as if disjoint. + +No `0xC0000142` occurred. The final viewer and audit runners exited with code 0. The audit mode uses immediate-context readback and is intentionally synchronous, so its 41.6 s cold time and 1.50 s query sum are not comparable to the normal no-readback viewer path. + +## GTX 1060-class estimate + +Using the user's 4.4 TFLOPS figure and AMD's published 61.4 TFLOPS FP32 peak for the RX 7900 XTX, simple throughput scaling gives roughly a 14x ratio ([AMD RX 7900 XTX specifications](https://www.amd.com/en/products/graphics/desktops/radeon/7000-series/amd-radeon-rx-7900xtx.html)). Applied to these measurements, that estimates about 8.1 ms average GPU dispatch per far tile and about 9.0 s summed dispatch time for 1,114 tiles. Pascal's slower 32-bit integer multiply and this shader's divergent shape loops make **roughly 9–15 s for the far dispatch work** a more practical, still very coarse estimate. This is not a GTX 1060 measurement. It implies no whole-view speedup over the measured 2.29 s CPU path. + +## Logs and implementation + +- Final interleaved timings: `step4_memory_cpu_A/B.stdout.log`, `step4_memory_gpu_A/B.stdout.log`. +- Final GPU connector audit: `step4_final_gpu_survival_audit.stdout.log`. +- Batching and timestamp code: [GpuFarViewer.h](GpuFarViewer.h), [GpuFieldPort.cpp](GpuFieldPort.cpp), and [StandaloneViewer.cpp](../../../Source/StandaloneViewer.cpp). +- Connector repair compute stages: [VoxelForgeGpuMesher.hlsl](../gpu_first_port/VoxelForgeGpuMesher.hlsl). + +The CPU reference path was left unchanged. All work and generated output remain under `Standalone/`; nothing was committed, pushed, or stashed. diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/StartGpuFarViewer.ps1 b/Standalone/Research/OwnerShapeWorld/gpu_field_port/StartGpuFarViewer.ps1 index 4da3e0e..2f0b683 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_field_port/StartGpuFarViewer.ps1 +++ b/Standalone/Research/OwnerShapeWorld/gpu_field_port/StartGpuFarViewer.ps1 @@ -1,9 +1,11 @@ [CmdletBinding()] -param([int]$ClipRadius=3) +param([int]$ClipRadius=3,[string]$ExecutableName='VoxelForgeGpuFarHybridViewer.exe') $ErrorActionPreference='Stop' $StandaloneRoot=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..')) -$ExecutablePath=Join-Path $StandaloneRoot 'bin\VoxelForgeGpuFarHybridViewer.exe' +if([IO.Path]::GetFileName($ExecutableName)-ne$ExecutableName-or[IO.Path]::GetExtension($ExecutableName)-ne'.exe') +{throw 'ExecutableName must be an executable filename, not a path.'} +$ExecutablePath=Join-Path $StandaloneRoot ('bin\'+$ExecutableName) $StdoutPath=Join-Path $PSScriptRoot 'owner_hybrid_viewer.stdout.log' $StderrPath=Join-Path $PSScriptRoot 'owner_hybrid_viewer.stderr.log' if(-not(Test-Path -LiteralPath $ExecutablePath -PathType Leaf)){throw "GPU far viewer not found: $ExecutablePath"} diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/BuildFarConnectorRingsCS.cso new file mode 100644 index 0000000000000000000000000000000000000000..4f774ca7862ff1490f2698d5ef16c7dc1e6a7e47 GIT binary patch literal 733428 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

&X*!eK8Kq>GxHgsw8I`+_9eDG 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

m4+;;u!J!A; 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 C-B5!7 literal 0 HcmV?d00001 diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/ClassifyCS.cso new file mode 100644 index 0000000000000000000000000000000000000000..96933c6d85ebe29d14613e111a97a62ad956290f GIT binary patch literal 8908 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 literal 0 HcmV?d00001 diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitCS.cso new file mode 100644 index 0000000000000000000000000000000000000000..96ecfebfede3ab193ed501914d0df73e23f6e0de GIT binary patch literal 107076 zcmeI5UyNVJb>C-yEAB3TW*CZf5qfK|*OKALvQdz#EiMu^yrN`Ah8;-|EyC_gMOl=^ zfD#FkN}2u%7IL8ibs7^!62K`OxJE#wFi-?RgSZb#T|WiLi(>@HOCAiNNgi6Xg;5B7 zOV4L!zW1Ixdw;+EEluvO-5Km}=iZq!XU?2CbI#11JNG{I*|Sf+@8AB9>F*!?ou|I` zU;gGl|M_43r@vSVAw2bsrEtLFkJ<0(wGcjQzgO+|yZ?SEJpa@u&KktkLilg1!C+3FB<+~}8v8wBzYp5)ko^e%!pebv#eUTHK#sZQ_tr@M#KHX9 zNdB_R|Mii4UxzhgOa0Je$?^9eNAg80Fn?U+x0Ytj?#lnE<-;G9vCCK=W%Gd0zZ~k(4^5PA<`}Eb%U%c|{E3e$Rbn`}d?)s%y zF5mk3>sQ~nb|d`4<(IErz5M#kCof&O^2wL``=>76ycjNAx_n#u>GU1xPg#RKJbgy` z?INANBK=!MI(gt@4qxS^g?dtOul@@;6Azl9Fl$Gxl7ldxOMqPc=?}Rz5a6eW(a@B zWC3Xy4_~v&&tH7)+LcRBU4AWq;mNCSynZvpL6a_@y?*%%mtX&Eu(1Qa$y|K@%kZTF zpUd$-4(D%Px_05}txH#KgczN_{Ld~u`C548%GHa8@tcNoc`nW~*8XSTxOwf3n`do| zr=gk?p1A&n8;sgC;)BNOa~G}Y-~1CRM^HcfdA?UaDgNLmu3UQU60L)l?b}!4ZEXAz zgirAz?J99ynTc!req0pS{eo+a2k4b=V+bsW4~nIMpTG2ZWb?I4H!r@7@_oZ3>tn|J zr6{W!4GD+;|Fq%#rHfbIFdZFbCzU~V9*FwQ!|M87`g=^Gu8e|8|zyB? zg96v+e+7R{8XV6|j!u=&NWzAyN4OKmE%Vr`~PZ-*lt+(Qn(hoc?G!;v-RuV=XROar`nAv7yzi}H+( zw?p$K(V_fDGn}sj{#hNXszWfU>zS~2kaoZiV(NqsQ^$vA!jXgFyCHxhw7s)_i&%c;;Dqz&XjC$aDobN_faw)$AkJ9qoLu}0xJ9yV65D{qp@pR*XAJR91B zqJ7=U(RSYk?p6P?T?^;d`geLZgQ- zX;izRjk@EQ+HCAD1^7dnhof%b{aAuOz?J*~PVl~N^x;ouxrVPEhWUl`UE=wXFHM#w zQO`YO^_1{dJiv3t%G=>q2m1qqMdx_jjP7&4G7bw(9=4eSOI;rJgoAw{>p19K&-B7u zg3V8&{_}b8g+5x!I)}Ut-tyq#3t2yudGLHA+*(sx&mB%}Ub%fc(g(cqX&?E-7k;D9 z58>8oSzd5uS=xQ)w-zh=U})CP=Jt!ObH9DNlkub0V) zlP%R<@(6^Pp3t~<3SR9k`3KLezSswe?qowp`1h@oS!O*?h4*xpb@Jc`G3_8e-1L3s z>r&Z`%mZMRCf*voaeR&CowB(D`oq_?wR|tLMpq|m(3aYpY?*LGzPC}JJ6;&xNW;`(U=KI*>)a^xg6g!QXJmg#6JFIRo_HAQ^t=)5sXX^0PcY(Xs5 z0L?3#jr})!YrhYLjW}lnChEXEUwHS+4ImMR;XdH=ffHVpA@+C;BF>$ZPrb}Vxc7CU zGc|7+t5f;Js?(>9)v0{d$-R}1e8_oGc10U$hC9;B^@Rh8r_hzl4%*_&-78y6J@nCC#r)(+$2b!fXzOkc8cj8SYCVba<^3*LT}_CU4^ zxG~#B|0CbN9BC20jgi@|)ECt4^#$cLZ}2vYRB(`1;w6oCo8Inh7vmtOZK&LsE+-FI z%?oDLxsmMv`NEfZZF@$^wv6RzKA}9?x3zq-BbG&89RFNPKGa_5R9fHOWV?_%wO24| zFXhzEskIka*)F>rty8v(eBo1jWlwV1$iI0olu&e5FWJ? zJc6MvLb6@$fj9GgE}LyvT7v^8c;Vz8dX9%{(|7Xq%E5lUN*VM9{mM`4EW3=jDC^gF z;z0=qyoVG0<$4S_(Wo}`>($vbQm&*?vbY_Mf+JppMh{=oXuGj%M~#ojp4wq?ly%C@ z<1_i&J!pTLCe@45i}7V4o9XBnM?u4_#!U(N7}<-ZHny0e8roM<)kppX|^xh?GXuHd0Y9M zbhhWS!8&;!wqe<-nzX$fLkq$4d43!E{VF2Tyl-n_pb*?;+=He@xO$0-pKAiX0;4^Dq!lX@pP4X4)m@D&o_T8ypsFyk9uZ9!v zeaY%zJWX2rXW{SVAJ>!C{#jt&nbse}y2#_X8J=Mw__~l4@T!gEJ(lT!U*!FDUR#lO z)bgN7GDb}J)zG}>Y`%uyJO@KedD=tZo;KerKi1ENwF9zqCWBNF_cnDs-?x?a>*(hW zz8(6u)J|Zaoj}aJ{71lS^&eeZeaGkljyX4NCI)sVzAfNAZFcMG-`=-1k2dR0!ew@akuUUABm#lt*XdLcancpt^klr+Uv^Ffuq2o#)61;&Af)hlI>RS6Y`R{R0eqAUqz@S{)uK zE%}m?Zvj~A(`Xlghga|@WM5Oxk13WH;mAK3UYvf%V9CGm6Dgjwc9R2p{`}<26CPsf zAeMh4=XEr|57M+3?#tvhoDmGNZS?2;v7V-tjAPOoq8zjbCtEC@u>S@hsb@^4MDNLL z^E15>t9aVDJi$X3u%4IX!9S}nwwHYQOEhL89j*4?l3gH=y5Onsp3Z(2^56%t;m!L| z$u9>kUzf^i|BZa5iMK}YD!O4jFRdSPUe{;TOPc(0qJ^|fx9ugSJxbpem6N}Oyhn2z z)TYDD{5m(Bze)JimQ&&BLtn|XU06!ISvizwowQb&`HtfM^w0cA>De3ThK}6M7z?-d z&^!!z&@P%i9{4L+GmtaFqkT57zf`B+XLDuqK3jSo#g`LK>W8img;%4m=X?47g6S&s z;?VE%>H+1iWL>vmrvb{emCeRVrn!=x7Fg}_Z4LnqDqH$`u-VwdVV%jp1h1Im5ijcN zp)SfOrmU6jd|q!Y_hU+Mn%}DoG4p6*D;wwcKF@GO{ta!W?PFw{dDQqxo9NSJi?f`~ zeC`4oJ?uQ2S=zzI%FeQ{=zNcN%w|F#asj=_6+!xqJYU}1%mI8WoB8A8>k&`V80*(+ z>Py*7c%(FJrfd!AC&MGt=Z&X~HLcYq?aFlv|ze7 z$<eyc z^_zS>)TwqGpQ5aTUwp>alHG_-@@`3IkY_q4%h(mb5%^KkGPxVz!=7>$O?(KzJa zY#N7fV|-3D?u5^SH4$WWI&?ceN4be5hWy)oKBxVA=+T^S(k8;j+(`w=1AC@;?@dr_XLQ+`3NX#=`#(%L@@lIKL*(1%0XcpHXr z;PG(CJ!N+$&qF?Y8o>{~L}QXuu6Li$Y2Cy)pl-sVe1=}}y3_~cdH(gXvb%hS4g&GR zcGlr+?#JEdbJ81k%V+U=Kl53<-djFz^$~k{4&mC`J6Q&7FFEQHbPW5-^vy=-_rW!1 zmyWcut17EB?U8$5kL;`Bo%?#W)+1U@g=>dIgM2-rWzt#$24V9|>FZ(cx3bwpD8}yb zyhn%a!WTq5Y3*MQjm7A#JI>d`SO!k(=i}L56yeP8A{^d-W8ecEQ^Ntgcm#g=aIC*# zo_sjR^ZXwicj6xco_-=MwK4KEt-hJ_?)L1{FWFpxwji6- zrFpNHwMlDrTb_P)IhfBPKfrtY1s_Q(^)F>ToaB0E^Mv8~Ea;xJJ$=wgP~Eg|5gLJm z*Tjp^=;75jnvbb&Xrpdm{h22CLz;)9Zs0u}`~j}y4{(C_b)%c>*{39X@FmG?+LJ&Y z=?emR@KC<6G7p~1w}cJA-xO`Z|0idkXutM(Xg9J3kKh~r5kaJ=^<4j$`XO~_5+M^*~WuVEzkM!9mcxJL0`A6I2*MTSEQRaR3{5W~) z55d&4Pf0G&gW5waWeFWXn)VdAR+?l<`+T`s05{UChuS$P>Jk9(8v82h>? z@9PjA`SQpUjOt8tw(+wR^7s03!b$z4->b7vnwyUKrsxmm7{u~T`7}SrHi*G_A$X;- zr9Vvbma#fnpOBx{DTDJ%F>S0))uouSMo;GtGkkH3igw_Bb!*bSw1e><6)aQu%++pgoBmcq)y}zIYO=o_tPI1`JVl%V`raYpIbih*x4sv ze!FK#)Sh|IpOj~xBL4gg2|W4X{9(tYk7eF!Jv(XdjXw<9WZ$_)CJ0%#g)x8FdZs1h z=UBYWqAWPP?H69s!0Y`(;*7h$4N)2PR$Eu)v0GM$QK>CE#29tu{_Nu$WvWg z%Wrq~X?%W}+S~gE)n3V!Y&E=4d#M{8A=_n_v?uBuu2ojH%crTmvM0(De(uL$rnz6$ zZq*^)P);!G85S7u`no8;?CKDn`{56xF6y7PUZp?x<9by#a?%=s7RG3VcJbG*L&bZo z8NEDvIQ3PzX7un`4xIlW?Q6qRcuTN2${ppzXY`wI1N4(mOb~5c(+1!@ZM1|@q0z&aG-};+H+&YaX^ZFIB5k0|z2o!HPZQ~C zsiBnTz4RX>o$dK-vKQqUdc8l4J!$mTq_uw*B+tNkKEprKybQqmF*xveIIBCt?M$9~ zK9@9tpL$c>d&%ddHyEp(d@aalXcw>dE1#i}u=jl4>PKHxo}pd5-p_m%ulIvLY?eHa z`NPmL@@w6VKaBD}s{XJ=&psUwC*pe?(6cM{%&^zDt);QnB;0X+Gjt|!tl^mx9}mw> zzmd~eXL}s@&Ip`;8pEME3wiRJp|8NPo!<-{MLGG+(D{@jCfpCd8TfxZ{ATI>fV5|x zk4*NB7=G_39~troO~kZA^GwFAQLeLe}6Bys192 z-an^(M)Am46nR9feMa)obHvzvVy%@`4tX>Ai)%ReTzzDntkw4enmqXZV`K;!kxW$| z+3uYgt3I;if0EtAh9ZXp`{~Xw`8i6m#mS}&;9EVflI*mhoN0@h-{9LKu0Aq2g{|n% zg<+o;IWv}QOy3vu6M^ys*)HiBV(FbwePoeGX0Kx`TV2X@^^qn1R3BNtUiE8ggeU35 zC`R2{InM>RT~hE?NY_}10uuH1r}EyS?2F7K4XV=#^Lc47J}13_PL%#LUE2B9tB=gqU7Jnp3T)B6@R7Y}c;w@{vk!e_ z%SIn`?WT{6eZlG@OY4X7{1@{P=1+P)kN(kE)m()bn`dX+R)5%YWYtI3&o})VqMS$4 zCp(!7Yi>&aF-HNnKObg#H>PCA6eR~gSYe-d`WZm>LWuw zeBjxKkoUUsdM(ZafI+U1xzhh5o>m`O@1H1n1phQ1wa6fbL;tSF7oV}hS>-X4`JdE#Qz(YL`k4~bW2=Ei0*K=g`92s)46w-Smxn|rFGcK{) z)kns+PL3RWDD#6c1Yh72an?=UJ|ny{z18_+fhVo{$PynUU*eniQ+;IBN5+08?+~m$ zvLp-j9GMA&1?f)aGw6+ajx5cYr9(sZ6EdeW+Y{$@%Bw!IehntxU~>rK4cF6br)8gH zm-H+rFqD@J+U*(PsprV*IWpvffc&sV)w-87bCSOXFi zt8VlGG77w}yQIRU%*UQ3AzNJ@vWV^%X>LU}Mcgsiib*u2bxzaoP@EjTYSkSeb=g88$V)#u{ zScE9*1T_!c}ki`tQ};2LY`pAppRpk?jtjtB*{4 z4!d-Y?Aul$-w`9+*@r$dY&>-BrjLy8v(bn+m9~;l;SNM|Tp`If{K76P?vT~oXKP!gZR3BOL-(=lVePlLxWJAOg z*=TePdb;|^(w@h=Uv7*eN%wvvY#g|5ykVZn^DM+0VPmaKTiI+ZN6$%btonTTsLc5j;|B|K@~V$4*)D7t>j~B**g1lD!!`B|7}hPsvW2pNz!d9|dXCJ_ z{ieO{>LWuw2*?j}0IhpTLv9FuZH~MFSFU@36MUYBb^fP$XRs*8T9BZ+CF|7V>n>>o zjyj1w+$bVAV;8uc z>5XgCcXGMX7b%^Q>fX!qdeui3JKg^6hTgy?bn;cdM;2MX@<7_-@N4yZ@vg=18}|3d z8uq7;jPq1?cG*Xk-WOBPks%iKdt_Dvg8Xwma30zxQ=Ii!rW#lR391g z;Y0P2mHUj(RVqRo=xUM6V|G)eLEUG-xi_K!-44b-OTf+!b^VWrTxrjXe8|IxyX8sEcF8acjtR#2xpTky|WL`k#VLAx_0v% z*+Hwbo+Hci?|P0b?-AYInxp#2x^-{$k=fiTt}AoQ`UJaMePqUjB2*t)^^rYcGNJLp z-hK6vb-L8v!POi*oAn5568OV94SwZ)j{(dy!>kvur$T+#qCXSHepEe2miFbUkF4Jt zS~j_OAwSDvmvO8SGuBw!>)W8nPxXr z>Lbf*^vwn~#UNhYjqe5?d*2NR)A}T$?)ER$2XSuZ>Sk_1n)W$?V{St1_mgMQ=;5;* zbZ4;YyA~VcVhZpXJF^I%fh%QUKA-Em7BicpoS>?*R{`JNDM6P~Ww< zJMvu5k?jtjtB*{44!h(dYfO=SFjsnKANt7fS3%cq`pDj6bygo)o_|*#S)BKm^Gt>e z^M67)pZCw@RUg@qKg`+d_sIIa>@<(8=g8tfa{sE2tlVd;=g6v$tkb0f{}*ZuamC@&)v{}Y2TptlhPvaknUvw`5{fRA~<3XhfD#tGkKo<4nXkt zH0~vztB;JnAoQ|uWcpC5dP!jbMfhJu%zWINsYOLhJfeX~<}mYg8hrJR!IX%oFA zoPiDCMCZMdXVFMK`$2Ea;&TYs*4%qJ<2h?|4DaZq%tpAjs(j7aFW7r8XrGns@{~q7 z${%sh2g-IS{#0lVoHYmn{X@{4pKD}g?XONBSKi@~dPK{qaP5%jd-o?3F3~bEJj1X% z=g3-P&Lg?vJNwW_HZ|IyYd3vl)LDIGdH!8}WYtGzdT-gr1h$X3evhpG_V)?n$GKlQ z5_red3;A7e*c5CuvFyUqNFKH$+1~0SGgb{k^^sK{*%Kxa8cX#YS<)xy3=7>pBm6-x zS07pRks$----_!Z)>)R=lWihC z+_Yzmd~;7L~k7YyT`9N>MmwG5)L)hj>ZeV;Mi=2poT%pFceA{FKy9Kqqsii*mc^ zBcnalN0#+m^^sK{Sstqzv(-mdePkirTK$2^MckLQ=TH>0CVMdS&&cuKshIcMx4Zq2 zqo&#)4=2`Mvhu96C$0S}LiLeVAK4Qo6LB9V%UL}~mgYppmTsRB{-Bo`W2_&zXWnFC z0K?uHFwCtyY<<__#)0~-#jK_%?5VFAYPSz5i;Q)A@@V)fAa4=(O%7@ z1=3*ik8AB;st@Aa%+<}@f;11OKJ)vWvuK>}yQw}h`hWnRS!XT6XW&X%n9t|xBZJ2T zbQ1DkePpxrMmi5m^<9hNbJ(SKEpFQ2I*}{I z7ghSV_S#2=zY4l`(?@pL>a0GpJpZmfvN-QA=a~$dMb;dQANE%!ZNJ{Yo)P>Um-*Ok zt~pdUv{5(nX7BrA9!{EvqrJd;IMzGBmHYut@Jp@TSXjB1=Jxeni~U|UvQWQA7R6)m zulmTUkL(GPh^P}y$KcngJ~Go4Z5$>8{}+1J`@P0s(9_jNmi9c}{c;nTi0=JJs6H}$ zgvf^G`qf8PePoY^_5kOin8#`UhE1;TTI^**wu!i&BQqvgU@bwYKC&byv;0r2nbO=p zva4HXaR(2#TMGh*-6t&Ke*#YM8qci9pko$|lo_LuGKeLZgQ-X{_HPYmAF2 ztQ$|Ok8GYi_r9C*JzYb2-b4&FAVP6Q9E_`N+O*itJ;# z(*5Wo`w64%-ucMZ-otxT2SGz^ocXn=QFv?)9KSgM z9Qz-Nv9-@;+;$n(vp>w!25(gVvRw}6kndSBca9??emS+vDGqYawmSMqar(LHH< z+eSKk+SR=0$=;;}u9mAAvK4)zBIOP_h%a6|7uEMeIT^svnwSm^h#CmgKas|cOz znO^uq(0dhq9(;jM(gEak@Ro<%yx_iB;q!pq2)EYMR?cS8=9SyGBYi$U?sev~TNi$# z&)2yx%Bj50cnP=4((XIIwOCo58H0unZ$2|dnm;qf{iL;jMezPGU>F=a``dnkWli3uiw(<%1z-yV~{hjL^7Fy#{quTS%M z4Px-UF6w120=%yiovC@tSe?ozR-Ha=tWMRX7YiEyipyxI8RcXqGLeJs!5114~^V_Jo6#-Gn}5o;DK` zq~FQ&kH8)8x8(P3;LR(pMY71C5srM?9FMOHTX1kL87|-kb&+6>jV(kLl+LG2-(4w@yE&cmL)fU0XE>X_##XaQOU-;CPQ{m}DU&*xHSxI=ff}uPijf4Ih8o52$M)fobUO2%6&06zu?boH;M_Mc< zl#(up?duP+w!@&o_IE{<1OEvGLPQ%&Bp7<_4 zM?GdX47w+6qMtI{6H5L8?`eb2q?K@#^>FYRxVz!=7>(fXY3%t&`)1SF!_DGzqH!mD zPW#t)!{^9z8e*a3-|RMQzt57|&~$qkXR}PH?=>d1hap*j9zW)|7mpK-J^#Q(p6_F1 zfi&%PP-YgNfh+eqJbX!GaO(=gIa8j&tGVK&O>+v;_z_AzlRn;t?fFdEyOC$gc^d6D z($~|tmwZlk5?bc*85#3!0Diyfjb&(Sdl{I;=hAobnWZDx`G+dDE;jHJ2uBsWt0u-xyQgVNZkV z$>mOkR}YCk)})kQ$uJYcGYouV?AY34x$HTMS*NaSHW3WEKOA11ekbRHk1%QNUk=C! zaL4oalDX!6h15G~L-@8`*rs6@?zppIvuNvti^>H@FF`&-neE{my^piO>AHrTtesP{wz{ sc%LJ<|GNp|C+zo+?B-K;|2g~33cpAJ7yh+f|4;jU)TQn8^0XEBf9D~u(f|Me literal 0 HcmV?d00001 diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarConnectorPanelsCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/EmitFarConnectorPanelsCS.cso new file mode 100644 index 0000000000000000000000000000000000000000..58f2b37c5e86564a873accfec1cc8b06f0ef6a6c GIT binary patch literal 5524 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 XIVY}H%x5}dt6Y%xAou;GH8%V&(!Wu& literal 0 HcmV?d00001 diff --git a/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/NoiseProbeCS.cso b/Standalone/Research/OwnerShapeWorld/gpu_field_port/precompiled/NoiseProbeCS.cso new file mode 100644 index 0000000000000000000000000000000000000000..784d61911da4cd99826f86f06a81969c4a0c4372 GIT binary patch literal 17184 zcmeI3--}&W70377>0~Ci)sFF{O)ZSJ3^7GXwBitKd($+r#n_aj$@I;(CZ*A|6O$>X zAR5{#MIV)7D)ga9RN_C-r-F$1(25TtKKLLAqWGddh=Nqt=j`*n`#XEioqOj}kZLzM zbMDz|t?ypH_S*aGdvo6-_Z~WyzWB#0|N7EresSyb&mQ^uTQ|(4l$H*qG#B?f<2v0- zX+5qNxb98q_vhod-2QzPoIBg{bK%n?3wlE;o7xpV8iqCxWJ{& z#swz*KaM}((dhq6^C|OU%Zo5Y`*$?I-+uVqnWxS?y`Ber=)&6LS%Y+b?c&+>vujVM zC(f-sw*K)HI?Mzz*QmBD#<+Oq%;|Lc%WD@-r)w#_Kls~$JA@7gX=hv?XjukvKN=VQ z#E&zLMZI*sr8~qw<3ToJH#YvZ<*RLMEC>Ve%vSpRD&#+V_VX9kF0MVXzO?7~hnMa@ zdgAEPi6Z@w4wLEJHm>j&x5H+#>g%gI1F=tC}5D(FQbL_9be(T#SP3Os$58oZ>W&YkE z=L`&4GNr(q7oPaIBe$GZu1~*AsiuSTkx%^jYr29pjT$f9lXSs1?Rek1dhP{X;EWni zwoPK+O1jj?*Vy{fby=b}h=+9fuJ*b+?sp$dm*(JOb{u(c=D(%tgLYfGbR&Gk8$SAa zgOvXC(^I#+_UtEL`TniHf0EB#eXD6O|6CiDy>TbL+`&58(&#(T@{kv3>~f-Uhj%#* z7Kop4MMsv{jlv~f)8Z1NQMlvu)tIsq+oki#v^0Nj>krvi`#^Hs|`t&x{`;=SL!Eol-3)>pOhkBbzQ#eU|N{l)yhiWTdc3&(r!zO zJ3rKZRv%-LtGchY`Y_1umRJ5inHJ_5hc(Z2%{FHHkrRzE;9X8jO&xjr`M|J{VZDaVeJM9Wl|Ju@p=AL_;t4jrh3}OL#q&PDeJ^nC$Z!*JJ5&li+$R zYus`AuC_Ug9C=KQk-hF8Xv%B!JDC>enJdUi$C-irQ*308IL~rgXmXu-;{C{G|8(PK z|4fIQ{nL$`{WBVO)$GI;n(Wm)zz*4o9T~HXhd-TXIW0CmkpEmZ`ykH=mjO5XU^-l{ z!8PtUeLdE$Yo70mbG02SmCqOt{UdK;z8C!+E9&{{Q41F8+7Ud%`Ko$9A9&h#^a3{; ze}`A_&3DZErVd0+xE@-I(O~nrhjbdm-*Sz@fxoM!5$7H-9W_n1x-a2pF5#OTgB*9C z|B@SDSBhU9cT`=O1h<|yjyq1@u6ZN*cjpc3@j&(9Iy#J>0>OoG*{^p4L#xYIp=g_>%Ea=q4}J1xk+&A8t=H{ z^sV1(WoOQ}!Q6AL?ZRBC-rL0=_t+QYx$XL68~NsYRK=DU;xqTP`jE@IkKWh4)AphM zKH_#}U+4E*{2|(k?d#QtU$U=@U!0cb_F$ooTks6$xW{mBc$vAa_;$rm_ur(>tDQOYTJp!s~EZ#%eNpKIK4`nsJO6McNXxIXf=_bG;2 z)6fBb(4b`KuuAG7#uyJroL_Nm!4YBjAl zh(E)|JIKcOkciWU+7GL*L)gL`kWHy$@2^(n+#ixY8#wzBwoJG55B5)o06`?QM0o ztariL;Oh-i(wVZ(m%I8_)4ut~+wpeHWZ&T`1+kz%e9`NJ9&){WUuNxH1e8z@xey_IFIjBy&u`Uo|Qe4<9cmI4n16cE8JY0 zyK!^OM(eBE>|>#8Ue9uEcADy@_rV{}!*k%8Mm_d(V6Zv+nT>mIuz9|3bqs zlziR}Zk`J{-)r1)`l=ovhwZ8d*#}u4Xv|yg#RnV5a~_|!-d}vsjqCLQIeg&qTj6>= zaG6Q;^;o-|UJo2o^}zcWCww-n2Q`g)klJ%%e5*Pb_hw$huXEz&dqJxeK??M8>3nxK zI#pA2*Ew;E(O~nrH;6xWM$G3?IPl9x*OhoMX4uhuhwC)?Z8beK|7!3-qwa8)cj`Tx z=M=dHPl-=HccM=;VfyzDPDi$$Q<8<2^JzNVdQLfRo>QZ7yXKVSb>1gC@Q-L(f9u@I zr#xN(?oSKD`_+Mn>0e9Qi%k36Fth&y9=ouOa!l5ZR97d$23ro-*3 zU&8hH(`)9Pio(HaLuJ8H0&-KxJr}tqS`Ig%EENJ8z_ue4>44dA?^E)&2h}W>j zYy5hQ+29jR_0`8G<~#?j`L2=rL0@|x;(Oi3ukH8^pKvSLhR4@H^nj<%1^PsjKAsCs zFS)R>ZVA_O!R02wt!uM%nnYjaf{%%u=YrD|C+~wlp0_;vt^FCU+vHFB(4E$0H~pfI z@+Umd+@JJGZW7!se}Vz7&n^0DT=~;!$hkkACVzS#{O-@1miJEO3@UVJ@?9~3zs8-7 zKIxk5H+kygj=c2e^X^S~U)Ff>i)Z|vB-f)Fx89R%2iMn#8aKyjw7&Yjp_a>W%HQca zP2YuiUx`y0NB)3DoZO$}9t~R7uX}A|j7isAcpW$Ub2{AGpN>0DU(N4OXM;Z3pW8j( z-K@X-ne4YE;p<`EOIOu<^dOgJO`ni$_UX{XuXx3r7vW6_;0@PM*PN?Hu{XV-V1RCiw-p9EuUpeSKik4 zPMWthZk@N=!S%eYaqGOTakW>J96pe}U1!6b*^<*-MHC5qxJPXaDCqH^{jpmBo6)7XR0lA?ajIuyELbYPs;B;YFU3z zu4(m}v>n{+>s)7Q+;RG9J|f4MnvXpD+2%`gIR|ydsTfI)d&$M33-NOvpR3+qa2Q6n)?@lyg;wk9dEol^8snH+3%n2g<9T>4Rnt25R-s7~kKtP5HM<`^oejF? zM%4S^|94QIbanlAha3%kJTK@IO>(}sayqhIc?=IU&tv+wgWHwIU`(R#s60kq^PJDv zN9?56|4$y5?|6#%>G<-r%=r{PDqxuaEeFuVzJ2}YRe+%RNB~QwHmQ2Z$yieFJ zZatqJH|OVq zX8&~KX8(-V*XM(5LDyXGx_*~sc@<|d=-LZ@wRpahE7^DBoY~ZTxdv`LpO5i}^^bTU zHK3d2P@x8w8ZgIu8o%)K7B`^MW_6ffC#6Pk#=f&Ik?poG( z;z}$2%ryE$Q|x_T;dEr{^-8kP@;sXkw_dLtH_x-txLTKMU9&&`JMXUjHt%JJ=9BBF z*w^_0Pl;2`d-~wlotzoJuPAdyvL#N_;pRAXz=p(+aI0--I#PI(a1%HDVqQiQm^@%?4z}?$;g`P5| z`pTHyxCdHU$3=Eh+?BMiKm09Oyu;CYgZLBe+~#t7X0y3g;paEsIC_s_fDC@1 zN&l|19@+H6Ant1a-@gCcxTz zqTtDk6g?EFm0WsLP*45=9=!DERq!C5JP7)I*?sL2+NR*Z@ZQXP@0*#oZ#Iz{i;uki zbYY{TKhPQozH3|h`5_=8Kbl1vu?|7Iu!%s2ph@`W65|uQ4I&}LenYX95vVlkad|7U z1IJt<8l|rrszLjp5$H)M8iNpp9rEnI5c<`cf}farxZA+-PU_+Ez_EVfg3!bAt^(KY z<9i63qUuuXHvC=itGtKdc7Wr1sE2zG-1%eRJ_D!uaJ#T6sxGzufjjmJ7hr@E{{l$%Z-cB0U=vvN}%&KW+q#|+}Z-KAjVChG#db&``8;M*&$WVQm?!$Dq zvsP|7S4>H+=#VdXB;S4Txd#@#^_J+4QCcw9jJz$?MbF(c;swd(%`_M*;0ztf`GuTi z2Lm*gwhecFJO{^R?5<V7kxt-h05Bjb6aV6Z#ZQvbe=%X=fY zWSO>^b+o>@OWM?oKBMU?>5O4%akEguj9XfC;A&K+^~sx@BMR5q8e5+lPjz8%lK6u? zLi|ox0*h#C;+Y1^o%v0+S!&;#$5*t!byWWcZ2Ki)u(@BsN8}uC0K=D|bXdB>gVhNH zAcjDV;NKHSN;upJ49}eNu-DeLJ!D84LM>i=TjWHiSA)U^VlI>z${Ptk4PKP>2QSV_ zEw92hd42UrxS1G=IZ|m>XkKw*H#&iEGd}sdv6-^>C literal 0 HcmV?d00001 diff --git a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl index 2f252fa..598c55b 100644 --- a/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl +++ b/Standalone/Research/OwnerShapeWorld/gpu_first_port/VoxelForgeGpuMesher.hlsl @@ -25,7 +25,19 @@ cbuffer TileConstants : register(b0) int OriginZ; int StepVoxels; float VoxelSizeCm; - float3 ConstantPad; + uint OutputBaseVertex; + uint OutputArgsByteOffset; + uint ConstantPad; +}; + +cbuffer FarConnectorConstants : register(b2) +{ + uint FarConnectorCount; + uint FarConnectorRingCount; + uint FarConnectorVertexCount; + uint FarConnectorPanelVertexCapacity; + uint FarShapeCount; + uint3 FarConnectorPadding; }; static const int3 CornerOffset[8] = { @@ -127,6 +139,25 @@ RWStructuredBuffer CellLocalOffsets : register(u1); RWStructuredBuffer GroupVertexCounts : register(u2); struct FarFieldSampleRange { uint offset; uint count; }; +struct FarFieldConnectorPanel +{ + float3 start; float radius; + float3 end; float tMin; + float tMax; + uint ringCount; + uint ringOffset; + uint vertexOffset; + uint axialSampleStart; + uint axialSampleCount; + uint2 padding; +}; +struct FarConnectorRing +{ + float4 corners[4]; + float4 normals[4]; + float t; + float3 padding; +}; StructuredBuffer FarShapes : register(t10); StructuredBuffer FarSampleShapeRanges : register(t11); StructuredBuffer FarSampleShapeIds : register(t12); @@ -134,6 +165,12 @@ StructuredBuffer FarProtectedAir : register(t13); StructuredBuffer FarProtectedSolid : register(t14); RWStructuredBuffer FarFieldDensity : register(u7); StructuredBuffer GpuSignDensity : register(t15); +StructuredBuffer FarConnectorPanels : register(t8); +StructuredBuffer FarConnectorRingInput : register(t9); +RWStructuredBuffer FarConnectorRingOutput : register(u7); +RWByteAddressBuffer FarConnectorDrawArgs : register(u4); +RWByteAddressBuffer FarConnectorVertexBytes : register(u5); +RWByteAddressBuffer FarConnectorIndexBytes : register(u6); cbuffer FarFieldConstants : register(b1) { @@ -196,6 +233,205 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID) FarFieldDensity[index] = density; } +float FarRockDistanceAt(float3 position) +{ + float cave = 100000.0f; + float overlay = -100000.0f; + [loop] + for (uint shapeId = 0; shapeId < FarShapeCount; ++shapeId) + { + const VF_FieldPrimitive shape = FarShapes[shapeId]; + if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0) + { + 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) + continue; + float distance = VF_FieldPrimitiveDistance(shape, position.x, position.y, position.z); + if ((shape.flags & VF_FIELD_PRIMITIVE_HAS_FLOOR) != 0) + { + const float floorZ = VF_FieldPrimitiveFloorAt(shape, position.x, position.y); + distance = VF_FieldSmoothMax(distance, floorZ - position.z, shape.floorBlendRadius); + } + cave = VF_FieldSmoothMin(cave, distance, shape.blendRadius); + } + else if (position.x >= shape.minVoxel[0] && position.x <= shape.maxVoxel[0] + && position.y >= shape.minVoxel[1] && position.y <= shape.maxVoxel[1] + && position.z >= shape.minVoxel[2] && position.z <= shape.maxVoxel[2]) + { + overlay = max(overlay, -VF_FieldPrimitiveDistance(shape, + position.x, position.y, position.z)); + } + } + const float base = min(position.z - FarStrataLo, FarStrataHi - position.z); + return VF_FieldSmoothMin(base, max(cave, overlay), FarStrataBlend); +} + +float FarConnectorRadius(float3 center, float3 direction, float nominalRadius) +{ + // The coarse far field is a signed-distance composition. Two bounded + // distance steps follow its connector wall closely without a CPU readback + // or a long radial scan for each sparse panel ring. + const float maxOffset=max(16.0f,nominalRadius*0.75f); + const float minRadius=max(0.0f,nominalRadius-maxOffset); + const float maxRadius=nominalRadius+maxOffset; + float radius=nominalRadius; + [unroll] + for(uint refine=0;refine<3;++refine) + { + const float distance=FarRockDistanceAt(center+direction*radius); + const float nextDistance=FarRockDistanceAt(center+direction*(radius+4.0f)); + const float slope=(nextDistance-distance)*0.25f; + const float correction=abs(slope)>0.125f?distance/slope:distance; + radius=clamp(radius-correction,minRadius,maxRadius); + } + const float residual=FarRockDistanceAt(center+direction*radius); + if(abs(residual)>1.0f) + { + const float searchMin=max(0.0f,nominalRadius-maxOffset-8.0f); + const float searchMax=nominalRadius+maxOffset; + float previous=searchMin; + float previousDistance=FarRockDistanceAt(center+direction*previous); + float bestRadius=radius; + float bestDifference=100000.0f; + [loop] + for(uint sample=1;sample<=32;++sample) + { + const float distance=min(searchMin+sample*8.0f,searchMax); + if(distance<=previous+1.0e-5f)break; + const float currentDistance=FarRockDistanceAt(center+direction*distance); + if((previousDistance>=0.0f)!=(currentDistance>=0.0f)) + { + float lo=previous,hi=distance; + const bool previousSolid=previousDistance>=0.0f; + [unroll] + for(uint refine=0;refine<8;++refine) + { + const float mid=(lo+hi)*0.5f; + if((FarRockDistanceAt(center+direction*mid)>=0.0f)==previousSolid)lo=mid; + else hi=mid; + } + const float crossing=(lo+hi)*0.5f; + const float difference=abs(crossing-nominalRadius); + if(difference<=maxOffset&&difference=searchMax-1.0e-5f)break; + } + radius=bestRadius; + } + return radius; +} + +[numthreads(64,1,1)] +void BuildFarConnectorRingsCS(uint3 dispatchId : SV_DispatchThreadID) +{ + const uint ringId = dispatchId.x; + if (ringId == 0) + { + const uint maxVerticesPerTile = CellCount * 15; + const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0; + const uint panelBaseVertex = slot * FarConnectorPanelVertexCapacity; + FarConnectorDrawArgs.Store(OutputArgsByteOffset + 20, FarConnectorVertexCount); + FarConnectorDrawArgs.Store(OutputArgsByteOffset + 24, FarConnectorVertexCount > 0 ? 1 : 0); + FarConnectorDrawArgs.Store(OutputArgsByteOffset + 28, panelBaseVertex); + FarConnectorDrawArgs.Store(OutputArgsByteOffset + 32, 0); + FarConnectorDrawArgs.Store(OutputArgsByteOffset + 36, 0); + } + if (ringId >= FarConnectorRingCount) return; + + FarFieldConnectorPanel connector = FarConnectorPanels[0]; + [loop] + for (uint i = 0; i < FarConnectorCount; ++i) + { + const FarFieldConnectorPanel candidate = FarConnectorPanels[i]; + if (ringId >= candidate.ringOffset && ringId < candidate.ringOffset + candidate.ringCount) + { + connector = candidate; + break; + } + } + const float3 start = connector.start; + const float3 delta = connector.end - start; + const float connectorLength = length(delta); + const float3 axis = delta / max(connectorLength, 1.0e-8f); + float3 u = cross(axis, float3(0,0,1)); + if (dot(u,u) < 0.01f) u = cross(axis, float3(0,1,0)); + u = normalize(u); + const float3 v = normalize(cross(axis,u)); + const uint localRing = ringId - connector.ringOffset; + float t; + if (localRing == 0) t = connector.tMin; + else if (localRing + 1 == connector.ringCount) t = connector.tMax; + else t = ((float)(connector.axialSampleStart + localRing - 1) + 0.5f) + / max(1.0f, (float)connector.axialSampleCount); + const float3 center = start + delta * t; + const float ru = FarConnectorRadius(center,u,connector.radius); + const float rmu = FarConnectorRadius(center,-u,connector.radius); + const float rv = FarConnectorRadius(center,v,connector.radius); + const float rmv = FarConnectorRadius(center,-v,connector.radius); + FarConnectorRing ring; + ring.corners[0] = float4(center + u*ru + v*rv, 0); + ring.corners[1] = float4(center - u*rmu + v*rv, 0); + ring.corners[2] = float4(center - u*rmu - v*rmv, 0); + ring.corners[3] = float4(center + u*ru - v*rmv, 0); + ring.normals[0] = float4(-normalize(u+v),0); + ring.normals[1] = float4(-normalize(-u+v),0); + ring.normals[2] = float4(-normalize(-u-v),0); + ring.normals[3] = float4(-normalize(u-v),0); + ring.t = t; + ring.padding = 0; + FarConnectorRingOutput[ringId] = ring; +} + +[numthreads(64,1,1)] +void EmitFarConnectorPanelsCS(uint3 dispatchId : SV_DispatchThreadID) +{ + const uint vertexId = dispatchId.x; + if (vertexId >= FarConnectorVertexCount) return; + FarFieldConnectorPanel connector = FarConnectorPanels[0]; + [loop] + for (uint i = 0; i < FarConnectorCount; ++i) + { + const FarFieldConnectorPanel candidate = FarConnectorPanels[i]; + const uint candidateCount = (candidate.ringCount - 1) * 24; + if (vertexId >= candidate.vertexOffset && vertexId < candidate.vertexOffset + candidateCount) + { + connector = candidate; + break; + } + } + const uint localVertex = vertexId - connector.vertexOffset; + const uint interval = localVertex / 24; + const uint cornerInQuad = localVertex % 6; + const uint side = (localVertex / 6) % 4; + const uint nextSide = (side + 1) & 3; + const uint ringA = connector.ringOffset + interval; + const uint ringB = ringA + 1; + uint ringIndex, cornerIndex; + if (cornerInQuad == 0) { ringIndex=ringA; cornerIndex=side; } + else if (cornerInQuad == 1) { ringIndex=ringB; cornerIndex=side; } + else if (cornerInQuad == 2) { ringIndex=ringA; cornerIndex=nextSide; } + else if (cornerInQuad == 3) { ringIndex=ringA; cornerIndex=nextSide; } + else if (cornerInQuad == 4) { ringIndex=ringB; cornerIndex=side; } + else { ringIndex=ringB; cornerIndex=nextSide; } + const FarConnectorRing ring = FarConnectorRingInput[ringIndex]; + GpuVertex vertex; + vertex.position = ring.corners[cornerIndex].xyz * VoxelSizeCm; + vertex.normal = ring.normals[cornerIndex].xyz; + const float connectorLength = length(connector.end - connector.start); + vertex.uv = float2(ring.t * connectorLength / 256.0f, (float)side / 4.0f); + const uint maxVerticesPerTile = CellCount * 15; + const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0; + const uint base = slot * FarConnectorPanelVertexCapacity; + const uint byteAddress = (base + vertexId) * 32; + FarConnectorVertexBytes.Store3(byteAddress, asuint(vertex.position)); + FarConnectorVertexBytes.Store3(byteAddress + 12, asuint(vertex.normal)); + FarConnectorVertexBytes.Store2(byteAddress + 24, asuint(vertex.uv)); + FarConnectorIndexBytes.Store((base + vertexId) * 4, base + vertexId); +} + groupshared uint ScanValues[256]; void ClassifyAndScan(uint index, uint3 groupId, uint3 groupThreadId, uint caseIndex) @@ -283,11 +519,16 @@ void ScanGroupsCS(uint3 dispatchId : SV_DispatchThreadID) 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); + DrawArgs.Store(OutputArgsByteOffset + 0, offset); + DrawArgs.Store(OutputArgsByteOffset + 4, 1); + DrawArgs.Store(OutputArgsByteOffset + 8, OutputBaseVertex); + DrawArgs.Store(OutputArgsByteOffset + 12, 0); + DrawArgs.Store(OutputArgsByteOffset + 16, 0); + DrawArgs.Store(OutputArgsByteOffset + 20, 0); + DrawArgs.Store(OutputArgsByteOffset + 24, 0); + DrawArgs.Store(OutputArgsByteOffset + 28, 0); + DrawArgs.Store(OutputArgsByteOffset + 32, 0); + DrawArgs.Store(OutputArgsByteOffset + 36, 0); } StructuredBuffer Density : register(t2); @@ -306,7 +547,7 @@ 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 uint base = OutputBaseVertex + LocalOffsets[index] + PrefixGroups[index / 256]; const uint3 cell = uint3(index % CellsPerAxis, (index / CellsPerAxis) % CellsPerAxis, index / (CellsPerAxis * CellsPerAxis)); diff --git a/Standalone/Source/StandaloneViewer.cpp b/Standalone/Source/StandaloneViewer.cpp index 77292dc..6fd9b02 100644 --- a/Standalone/Source/StandaloneViewer.cpp +++ b/Standalone/Source/StandaloneViewer.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1352,10 +1353,24 @@ namespace ID3D11Buffer* VertexBuffer=nullptr; ID3D11Buffer* IndexBuffer=nullptr; ID3D11Buffer* IndirectArgs=nullptr; + ID3D11Buffer* ConnectorVertexBuffer=nullptr; + ID3D11Buffer* ConnectorIndexBuffer=nullptr; ID3D11Query* CompletionEvent=nullptr; + VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{}; uint64 VertexCapacityBytes=0; + uint32 OutputSlot=0,IndirectArgsByteOffset=0,BatchIndex=UINT32_MAX; double GpuDispatchMs=0.0; bool bGpuComplete=false; + bool bTimingResolved=false; + }; + struct FGpuFarBatch + { + ID3D11Query* CompletionEvent=nullptr; + VoxelForgeGpuFarViewer::GpuTimingQueries TimingQueries{}; + uint32 TileCount=0; + uint64 TimestampFrequency=0; + double GpuDispatchMs=0.0; + bool bGpuComplete=false,bTimingResolved=false; }; uint64 HashStandaloneFile(const std::string& Path) @@ -1439,6 +1454,7 @@ namespace std::unordered_map,FStandaloneBatchKeyHash> BatchMembers; std::unordered_map StreamingBatches; std::unordered_map GpuFarTiles; + std::vector GpuFarBatches; std::deque GpuFarQueue; std::unordered_set GpuFarQueued; std::unordered_set GpuFarEligibleTiles; @@ -1474,6 +1490,16 @@ namespace uint64 GpuFarTilesSubmitted=0; double GpuFarGpuDispatchMs=0.0; double GpuFarGpuDispatchMaxMs=0.0; + double GpuFarSubmissionSpanMs=0.0,GpuFarSubmissionSpanMaxMs=0.0; + uint64 GpuFarLocalMemoryPeakBytes=0,GpuFarLocalMemoryBudgetBytes=0; + double GpuFarPoolReserveMs=0.0; + uint64 GpuFarSubmissionBatches=0; + uint32 GpuFarMaxTilesPerSubmissionBatch=0; + uint32 GpuFarOutputCapacitySlots=0,GpuFarNextOutputSlot=0; + std::vector GpuFarFreeOutputSlots; + std::unordered_map GpuFarPreparedInputs; + std::unordered_map GpuFarPreparationErrors; + double GpuFarPreparationWallMs=0.0,GpuFarPreparationWorkerMs=0.0; VoxelForgeCore::DetailVariant ShapeDetailVariant=VoxelForgeCore::DetailVariant::coherent; bool bWireframe=false; bool bCaptured=false; @@ -1531,6 +1557,41 @@ namespace if(SwapChain)SwapChain->Release();if(Context)Context->Release();if(Device)Device->Release(); } + static void ReleaseGpuFarTile(FGpuFarTile& Tile) + { + if(Tile.VertexBuffer)Tile.VertexBuffer->Release(); + if(Tile.IndexBuffer)Tile.IndexBuffer->Release(); + if(Tile.IndirectArgs)Tile.IndirectArgs->Release(); + if(Tile.ConnectorVertexBuffer)Tile.ConnectorVertexBuffer->Release(); + if(Tile.ConnectorIndexBuffer)Tile.ConnectorIndexBuffer->Release(); + if(Tile.CompletionEvent)Tile.CompletionEvent->Release(); + if(Tile.TimingQueries.start)Tile.TimingQueries.start->Release(); + if(Tile.TimingQueries.end)Tile.TimingQueries.end->Release(); + if(Tile.TimingQueries.disjoint)Tile.TimingQueries.disjoint->Release(); + Tile={}; + } + + static void ReleaseGpuFarBatch(FGpuFarBatch& Batch) + { + if(Batch.CompletionEvent)Batch.CompletionEvent->Release(); + if(Batch.TimingQueries.start)Batch.TimingQueries.start->Release(); + if(Batch.TimingQueries.end)Batch.TimingQueries.end->Release(); + if(Batch.TimingQueries.disjoint)Batch.TimingQueries.disjoint->Release(); + Batch={}; + } + + void SampleGpuFarMemory() + { + if(!bGpuFarEnabled||!Device||!Context)return; + uint64 Usage=0,Budget=0;std::string Error; + if(VoxelForgeGpuFarViewer::QueryFarGpuMemory(Device,Context,GpuFarShaderPath, + Usage,Budget,Error)) + { + GpuFarLocalMemoryPeakBytes=std::max(GpuFarLocalMemoryPeakBytes,Usage); + GpuFarLocalMemoryBudgetBytes=Budget; + } + } + void ReleaseMeshes() { for(auto& Pair:StreamingBatches) @@ -1540,16 +1601,20 @@ namespace } StreamingBatches.clear();BatchMembers.clear();ResidentTiles.clear(); for(auto& Pair:GpuFarTiles) - { - if(Pair.second.VertexBuffer)Pair.second.VertexBuffer->Release(); - if(Pair.second.IndexBuffer)Pair.second.IndexBuffer->Release(); - if(Pair.second.IndirectArgs)Pair.second.IndirectArgs->Release(); - if(Pair.second.CompletionEvent)Pair.second.CompletionEvent->Release(); - } - GpuFarTiles.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear(); + ReleaseGpuFarTile(Pair.second); + GpuFarTiles.clear(); + for(FGpuFarBatch& Batch:GpuFarBatches)ReleaseGpuFarBatch(Batch); + GpuFarBatches.clear();GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear(); + GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0; + GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear(); + GpuFarPreparationWallMs=0.0;GpuFarPreparationWorkerMs=0.0; GpuFarAudit.reset();bGpuFarAuditReported=false; GpuFarTilesSubmitted=0;GpuFarEnqueueWallSeconds=0.0;GpuFarGpuDispatchMs=0.0; - GpuFarGpuDispatchMaxMs=0.0;bGpuFarFirstTileReported=false; + GpuFarGpuDispatchMaxMs=0.0;GpuFarSubmissionSpanMs=0.0;GpuFarSubmissionSpanMaxMs=0.0; + GpuFarLocalMemoryPeakBytes=0;GpuFarLocalMemoryBudgetBytes=0; + bGpuFarFirstTileReported=false; + GpuFarPoolReserveMs=0.0; + GpuFarSubmissionBatches=0;GpuFarMaxTilesPerSubmissionBatch=0; DirtyBatchSet.clear();DesiredTiles.clear();DesiredTileSet.clear();CrossingWork.clear(); DeferredLeaverTiles.clear(); for(int32 I=0;I<=MaxLevels;++I) @@ -2104,13 +2169,59 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; auto It=GpuFarTiles.find(Key); if(It==GpuFarTiles.end())return false; FGpuFarTile& Tile=It->second; - if(Tile.bGpuComplete)return true; - if(Tile.CompletionEvent&&Context + if(Tile.BatchIndex!=UINT32_MAX&&Tile.BatchIndexGetData(Batch.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK) + { + Batch.bGpuComplete=true; + Batch.CompletionEvent->Release();Batch.CompletionEvent=nullptr; + } + if(Batch.bGpuComplete&&!Batch.bTimingResolved) + { + if(VoxelForgeGpuFarViewer::ResolveGpuTiming(Context,Batch.TimingQueries, + Batch.GpuDispatchMs,&Batch.TimestampFrequency)) + { + Batch.bTimingResolved=true; + GpuFarSubmissionSpanMs+=Batch.GpuDispatchMs; + GpuFarSubmissionSpanMaxMs=std::max(GpuFarSubmissionSpanMaxMs,Batch.GpuDispatchMs); + } + } + if(Batch.bGpuComplete&&Batch.bTimingResolved&&!Tile.bTimingResolved + &&Batch.TimestampFrequency) + { + double TileDispatchMs=0.0; + if(VoxelForgeGpuFarViewer::ResolveGpuTimestampPair(Context,Tile.TimingQueries, + Batch.TimestampFrequency,TileDispatchMs)) + { + Tile.GpuDispatchMs=TileDispatchMs;Tile.bTimingResolved=true; + GpuFarGpuDispatchMs+=TileDispatchMs; + GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,TileDispatchMs); + } + } + return Batch.bGpuComplete; + } + if(!Tile.bGpuComplete&&Tile.CompletionEvent&&Context &&Context->GetData(Tile.CompletionEvent,nullptr,0,D3D11_ASYNC_GETDATA_DONOTFLUSH)==S_OK) { Tile.bGpuComplete=true; Tile.CompletionEvent->Release();Tile.CompletionEvent=nullptr; } + if(Tile.bGpuComplete&&!Tile.bTimingResolved) + { + double AsyncDispatchMs=0.0; + if(VoxelForgeGpuFarViewer::ResolveGpuTiming(Context,Tile.TimingQueries,AsyncDispatchMs)) + { + if(Tile.GpuDispatchMs==0.0) + { + Tile.GpuDispatchMs=AsyncDispatchMs; + GpuFarGpuDispatchMs+=AsyncDispatchMs; + GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,AsyncDispatchMs); + } + Tile.bTimingResolved=true; + } + } return Tile.bGpuComplete; } return ResidentTiles.contains(Key); @@ -2119,20 +2230,98 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; void ScheduleGpuFarTiles(const std::vector& Keys) { GpuFarQueue.clear();GpuFarQueued.clear();GpuFarEligibleTiles.clear(); + GpuFarPreparedInputs.clear();GpuFarPreparationErrors.clear(); if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld())return; std::vector CpuTiles;CpuTiles.reserve(Keys.size()); + std::vector Eligible;Eligible.reserve(Keys.size()); for(const FVoxelTileKey& Key:Keys) { if(Key.LevelFullResClipLevels){CpuTiles.push_back(Key);continue;} const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level}; if(ShapeWorld->isFarFieldTileEligible(CoreKey)) { - GpuFarEligibleTiles.insert(Key); - if(!GpuFarTiles.contains(Key)) - {GpuFarQueue.push_back(Key);GpuFarQueued.insert(Key);} + Eligible.push_back(Key); } else CpuTiles.push_back(Key); } + if(Eligible.size()>GpuFarOutputCapacitySlots) + { + for(auto& Pair:GpuFarTiles)ReleaseGpuFarTile(Pair.second); + GpuFarTiles.clear();GpuFarFreeOutputSlots.clear();GpuFarNextOutputSlot=0; + GpuFarOutputCapacitySlots=static_cast(Eligible.size()); + } + if(!Eligible.empty()&&GpuFarOutputCapacitySlots==0) + GpuFarOutputCapacitySlots=static_cast(Eligible.size()); + if(!Eligible.empty()) + { + std::string PoolError;const auto ReserveStart=std::chrono::steady_clock::now(); + if(!VoxelForgeGpuFarViewer::ReserveFarOutputPool(Device,Context,GpuFarShaderPath, + GpuFarOutputCapacitySlots,static_cast(World->CoarseTileCells),PoolError)) + { + std::fprintf(stderr,"[GpuFarPoolError] %s\n",PoolError.c_str()); + bGpuFarEnabled=false;GpuFarOutputCapacitySlots=0; + CpuTiles=Keys;Eligible.clear();GpuFarTiles.clear(); + } + GpuFarPoolReserveMs+=std::chrono::duration( + std::chrono::steady_clock::now()-ReserveStart).count(); + SampleGpuFarMemory(); + } + std::vector NeedPreparation;NeedPreparation.reserve(Eligible.size()); + for(const FVoxelTileKey& Key:Eligible) + if(!GpuFarTiles.contains(Key))NeedPreparation.push_back(Key); + if(!NeedPreparation.empty()) + { + const auto PrepareStart=std::chrono::steady_clock::now(); + std::vector> Inputs(NeedPreparation.size()); + std::vector Errors(NeedPreparation.size()); + const std::size_t ThreadCount=std::min(8,NeedPreparation.size()); + std::atomic Next{0}; + std::vector WorkerElapsed(ThreadCount,0.0); + std::vector Workers;Workers.reserve(ThreadCount); + for(std::size_t Worker=0;Worker=NeedPreparation.size())break; + const FVoxelTileKey& Key=NeedPreparation[Index]; + const VoxelForgeCore::TileKey CoreKey{{Key.Coord.X,Key.Coord.Y,Key.Coord.Z},Key.Level}; + try { Inputs[Index]=ShapeWorld->exportFarFieldTile(CoreKey,Errors[Index]); } + catch(const std::exception& Error) { Errors[Index]=Error.what(); } + } + WorkerElapsed[Worker]=std::chrono::duration( + std::chrono::steady_clock::now()-WorkerStart).count(); + }); + } + for(std::thread& Worker:Workers)Worker.join(); + const double PreparationMs=std::chrono::duration( + std::chrono::steady_clock::now()-PrepareStart).count(); + double WorkerSumMs=0.0;for(const double Elapsed:WorkerElapsed)WorkerSumMs+=Elapsed; + GpuFarPreparationWallMs+=PreparationMs;GpuFarPreparationWorkerMs+=WorkerSumMs; + std::size_t Failed=0; + for(std::size_t Index=0;IndexSetDesiredTileFilter(CpuTiles); const std::size_t CpuTransitionFallbacks=static_cast(std::count_if( Keys.begin(),Keys.end(),[&](const FVoxelTileKey& Key) @@ -2145,27 +2334,67 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; { if(!bGpuFarEnabled||!ShapeWorld||!ShapeWorld->hasFarWorld()||!Device||!Context ||GpuFarQueue.empty())return; + const bool bDeferredBatch=!bGpuFarAuditEnabled; + if(bDeferredBatch) + { + std::string BatchError; + if(!VoxelForgeGpuFarViewer::BeginFarTileBatch(Device,Context,GpuFarShaderPath,BatchError)) + { + std::fprintf(stderr,"[GpuFarBatchError] begin: %s\n",BatchError.c_str()); + bGpuFarEnabled=false;return; + } + } const auto Start=std::chrono::steady_clock::now(); int32 Built=0; + std::vector BuiltKeys;BuiltKeys.reserve(32); do { const FVoxelTileKey Key=GpuFarQueue.front();GpuFarQueue.pop_front();GpuFarQueued.erase(Key); if(!DesiredTileSet.contains(Key)||GpuFarTiles.contains(Key))continue; FGpuFarTile Tile; + if(!GpuFarFreeOutputSlots.empty()) + {Tile.OutputSlot=GpuFarFreeOutputSlots.back();GpuFarFreeOutputSlots.pop_back();} + else if(GpuFarNextOutputSlot=6)?&AuditMesh:nullptr, - &EnqueueMs,&GpuDispatchMs,Error)) + const auto PreparedInputIt=GpuFarPreparedInputs.find(Key); + const VoxelForgeCore::FarFieldTileInput* PreparedInput=PreparedInputIt==GpuFarPreparedInputs.end() + ?nullptr:&PreparedInputIt->second; + if(!PreparedInput) { - std::fprintf(stderr,"[GpuFarTileError] key=(%d,%d,%d,lod%d): %s\n", - Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Error.c_str()); + const auto PrepError=GpuFarPreparationErrors.find(Key); + std::fprintf(stderr,"[GpuFarInputError] key=(%d,%d,%d,lod%d): %s\n", + Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level, + PrepError==GpuFarPreparationErrors.end()?"prepared far input is missing":PrepError->second.c_str()); + GpuFarFreeOutputSlots.push_back(Tile.OutputSlot); bGpuFarEnabled=false; break; } + if(!VoxelForgeGpuFarViewer::BuildFarTile(Device,Context,*ShapeWorld,CoreKey, + PreparedInput, + GpuFarShaderPath,&Tile.VertexBuffer,&Tile.IndexBuffer,&Tile.IndirectArgs, + &Tile.ConnectorVertexBuffer,&Tile.ConnectorIndexBuffer, + Tile.OutputSlot,GpuFarOutputCapacitySlots,&Tile.IndirectArgsByteOffset, + bDeferredBatch?nullptr:&Tile.CompletionEvent, + &Tile.TimingQueries, + (bGpuFarAuditEnabled&&Key.Level>=6)?&AuditMesh:nullptr, + &EnqueueMs,&GpuDispatchMs,Error)) + { + GpuFarFreeOutputSlots.push_back(Tile.OutputSlot); + std::fprintf(stderr,"[GpuFarTileError] key=(%d,%d,%d,lod%d): %s\n", + Key.Coord.X,Key.Coord.Y,Key.Coord.Z,Key.Level,Error.c_str()); + if(PreparedInputIt!=GpuFarPreparedInputs.end())GpuFarPreparedInputs.erase(PreparedInputIt); + bGpuFarEnabled=false; + break; + } + if(PreparedInputIt!=GpuFarPreparedInputs.end())GpuFarPreparedInputs.erase(PreparedInputIt); if(bGpuFarAuditEnabled&&GpuFarAudit&&Key.Level>=6) { FStandaloneCpuTile AuditTile;AuditTile.Key=Key; @@ -2182,10 +2411,15 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; Tile.VertexCapacityBytes=Cells*Cells*Cells*15u*32u; Tile.GpuDispatchMs=GpuDispatchMs; GpuFarEnqueueWallSeconds+=EnqueueMs/1000.0; - GpuFarGpuDispatchMs+=GpuDispatchMs; - GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs); + if(GpuDispatchMs>0.0) + { + GpuFarGpuDispatchMs+=GpuDispatchMs; + GpuFarGpuDispatchMaxMs=std::max(GpuFarGpuDispatchMaxMs,GpuDispatchMs); + Tile.bTimingResolved=true; + } ++GpuFarTilesSubmitted;++Built; GpuFarTiles.emplace(Key,Tile); + BuiltKeys.push_back(Key); if(!bGpuFarFirstTileReported) { bGpuFarFirstTileReported=true; @@ -2195,8 +2429,42 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; std::fflush(stdout); } } - while(Built<1&& !GpuFarQueue.empty() - &&std::chrono::duration(std::chrono::steady_clock::now()-Start).count()<1.0); + while(Built<32&&!GpuFarQueue.empty() + &&std::chrono::duration(std::chrono::steady_clock::now()-Start).count()<4.0); + if(Built>0) + { + if(bDeferredBatch) + { + FGpuFarBatch Batch;Batch.TileCount=static_cast(Built); + std::string BatchError; + if(!VoxelForgeGpuFarViewer::SubmitFarTileBatch(Device,Context,GpuFarShaderPath, + &Batch.CompletionEvent,&Batch.TimingQueries,BatchError)) + { + std::fprintf(stderr,"[GpuFarBatchError] submit: %s\n",BatchError.c_str()); + bGpuFarEnabled=false; + } + else + { + const uint32 BatchIndex=static_cast(GpuFarBatches.size()); + GpuFarBatches.push_back(Batch); + for(const FVoxelTileKey& Key:BuiltKeys) + { + auto It=GpuFarTiles.find(Key); + if(It!=GpuFarTiles.end())It->second.BatchIndex=BatchIndex; + } + ++GpuFarSubmissionBatches; + GpuFarMaxTilesPerSubmissionBatch=std::max(GpuFarMaxTilesPerSubmissionBatch, + static_cast(Built)); + } + } + else + { + ++GpuFarSubmissionBatches; + GpuFarMaxTilesPerSubmissionBatch=std::max(GpuFarMaxTilesPerSubmissionBatch, + static_cast(Built)); + } + } + SampleGpuFarMemory(); } void ToggleFarRenderer() @@ -2423,10 +2691,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; for(auto It=GpuFarTiles.begin();It!=GpuFarTiles.end();) { if(DesiredTileSet.contains(It->first)){++It;continue;} - if(It->second.VertexBuffer)It->second.VertexBuffer->Release(); - if(It->second.IndexBuffer)It->second.IndexBuffer->Release(); - if(It->second.IndirectArgs)It->second.IndirectArgs->Release(); - if(It->second.CompletionEvent)It->second.CompletionEvent->Release(); + GpuFarFreeOutputSlots.push_back(It->second.OutputSlot); + ReleaseGpuFarTile(It->second); It=GpuFarTiles.erase(It); } ScheduleGpuFarTiles(DesiredTiles); @@ -2786,11 +3052,19 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; static_cast(TotalIndices),StreamingBatches.size()); if(bGpuFarEnabled) { + SampleGpuFarMemory(); const double AverageGpuMs=GpuFarTilesSubmitted ?GpuFarGpuDispatchMs/static_cast(GpuFarTilesSubmitted):0.0; - std::printf("[GpuFarTiming] submitted_tiles=%llu cpu_export_upload_enqueue_wall_ms=%.3f gpu_timestamp_total_ms=%.3f gpu_timestamp_mean_per_one_tile_frame_ms=%.6f gpu_timestamp_max_tile_ms=%.6f frame_budget_90hz_ms=11.111\n", + std::printf("[GpuFarTiming] submitted_tiles=%llu submission_batches=%llu max_tiles_per_batch=%u pooled_output_reserve_wall_ms=%.3f cpu_input_prepare_wall_ms=%.3f cpu_input_worker_sum_ms=%.3f cpu_upload_enqueue_sum_ms=%.3f gpu_dispatch_timestamp_total_ms=%.3f gpu_dispatch_mean_per_tile_ms=%.6f gpu_dispatch_max_tile_ms=%.6f gpu_submission_span_total_ms=%.3f gpu_submission_span_max_batch_ms=%.3f frame_budget_90hz_ms=11.111\n", static_cast(GpuFarTilesSubmitted), - GpuFarEnqueueWallSeconds*1000.0,GpuFarGpuDispatchMs,AverageGpuMs,GpuFarGpuDispatchMaxMs); + static_cast(GpuFarSubmissionBatches),GpuFarMaxTilesPerSubmissionBatch, + GpuFarPoolReserveMs,GpuFarPreparationWallMs,GpuFarPreparationWorkerMs, + GpuFarEnqueueWallSeconds*1000.0, + GpuFarGpuDispatchMs,AverageGpuMs,GpuFarGpuDispatchMaxMs, + GpuFarSubmissionSpanMs,GpuFarSubmissionSpanMaxMs); + std::printf("[GpuFarMemory] dxgi_local_sampled_peak_mb=%.1f dxgi_local_budget_mb=%.1f\n", + GpuFarLocalMemoryPeakBytes/(1024.0*1024.0), + GpuFarLocalMemoryBudgetBytes/(1024.0*1024.0)); } ReportTileTimings();std::fflush(stdout); if(bQuitAfterClipmap&&CapturePath.empty())PostMessageA(Window,WM_CLOSE,0,0); @@ -2908,7 +3182,14 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75; const UINT Stride=32,Offset=0; Context->IASetVertexBuffers(0,1,&Tile.VertexBuffer,&Stride,&Offset); Context->IASetIndexBuffer(Tile.IndexBuffer,DXGI_FORMAT_R32_UINT,0); - Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs,0); + Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs,Tile.IndirectArgsByteOffset); + if(Tile.ConnectorVertexBuffer&&Tile.ConnectorIndexBuffer) + { + Context->IASetVertexBuffers(0,1,&Tile.ConnectorVertexBuffer,&Stride,&Offset); + Context->IASetIndexBuffer(Tile.ConnectorIndexBuffer,DXGI_FORMAT_R32_UINT,0); + Context->DrawIndexedInstancedIndirect(Tile.IndirectArgs, + Tile.IndirectArgsByteOffset+5u*sizeof(uint32)); + } } } CaptureRenderedFrame();