动机
如果Compute Shader是异步执行的,那么ALT+F12抓帧将无法捕获到该Compute Shader的调用。使用GPUReadback又要多写依托答辩代码,还要依靠Debugger调试。
解决方案
在RDG中插入开始捕获和结束捕获的Pass,IRenderCaptureProvider::Get()会通过UE的ModularFeature,去查找实现了IRenderCaptureProvider类的功能模块,并返回其引用。这里如果配置好了RenderDoc,就会正确返回RenderDocPlugin中的实现。
ENQUEUE_RENDER_COMMAND(QwQVoxel)([this] (FRHICommandListImmediate& CmdList)
{
FRDGBuilder GraphBuilder(CmdList);
FGlobalShaderMap* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
check(ShaderMap);
// RenderDoc Capture
FRDGPassRef BeginCapturePass = GraphBuilder.AddPass(
RDG_EVENT_NAME("BeginCapture"),
ERDGPassFlags::None,
[] (FRHICommandListImmediate& RHICommandListLocal)
{
IRenderCaptureProvider::Get().BeginCapture(&RHICommandListLocal, IRenderCaptureProvider::ECaptureFlags_Launch);
});
FRDGPassRef EndCapturePass = GraphBuilder.AddPass(RDG_EVENT_NAME("End Capture"), ERDGPassFlags::None, [] (FRHICommandListImmediate& RHICmdList)
{
IRenderCaptureProvider::Get().EndCapture(&RHICmdList);
});
GraphBuilder.AddPassDependency(BeginCapturePass, EndCapturePass);
const auto EnsureCaptured = [BeginCapturePass, EndCapturePass, &GraphBuilder](FRDGPassRef& NewPass)
{
if (NewPass)
{
GraphBuilder.AddPassDependency(BeginCapturePass, NewPass);
GraphBuilder.AddPassDependency(NewPass, EndCapturePass);
}
};
// Atomic counter buffer
static constexpr uint32 DEFAULT_COUNTER_VALUES[] { 0, 0, 0, 0 };
FRDGBufferDesc Desc = FRDGBufferDesc::CreateUploadDesc(sizeof(uint32), 4);
Desc.Usage |= EBufferUsageFlags::UnorderedAccess;
FRDGBufferRef CounterBuffer = GraphBuilder.CreateBuffer(Desc, TEXT("Voxel Atomic Counter"));
GraphBuilder.QueueBufferUpload(CounterBuffer, DEFAULT_COUNTER_VALUES, std::size(DEFAULT_COUNTER_VALUES));
FRDGBufferUAVRef CounterBufferUAV = GraphBuilder.CreateUAV(CounterBuffer, EPixelFormat::PF_R32_UINT);
// Uniform buffer
FVoxelMarchingCubeUniformParameters* UniformParameters =GraphBuilder.AllocParameters<FVoxelMarchingCubeUniformParameters>();
UniformParameters->VoxelSize = DimensionX;
UniformParameters->SurfaceIsoValue = 0.f;
UniformParameters->TotalCubes = (DimensionX + 1) * (DimensionY + 1) * (DimensionZ + 1);
TRDGUniformBufferRef<FVoxelMarchingCubeUniformParameters> UniformParametersBuffer = GraphBuilder.CreateUniformBuffer(UniformParameters);
// Nanovdb data buffer
nanovdb::NanoGrid<float>* GridData = HostVdbBuffer.grid<float>();
FRDGBufferDesc GridBufferDesc = FRDGBufferDesc::CreateUploadDesc(sizeof(float), HostVdbBuffer.size());
FRDGBufferRef GridBuffer = GraphBuilder.CreateBuffer(GridBufferDesc, TEXT("Voxel Data Buffer"));
GraphBuilder.QueueBufferUpload(GridBuffer, GridData, HostVdbBuffer.size());
FRDGBufferSRVRef GridBufferSRV = GraphBuilder.CreateSRV(GridBuffer, EPixelFormat::PF_R32_UINT);
// Cube index offset buffer
FRDGBufferDesc CubeIndexOffsetBufferDesc = FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), DimensionX * DimensionY * DimensionZ);
FRDGBufferRef CubeIndexOffsetBuffer = GraphBuilder.CreateBuffer(CubeIndexOffsetBufferDesc, TEXT("Cube Index Offset"));
FRDGBufferUAVRef CubeIndexOffsetBufferUAV = GraphBuilder.CreateUAV(CubeIndexOffsetBuffer, EPixelFormat::PF_R32_UINT);
FRDGBufferSRVRef CubeIndexOffsetBufferSRV = GraphBuilder.CreateSRV(CubeIndexOffsetBuffer, EPixelFormat::PF_R32_UINT);
{
auto CSRef = ShaderMap->GetShader<FVoxelMarchingCubesCalcCubeIndexCS>();
auto* Parameters = GraphBuilder.AllocParameters<FVoxelMarchingCubesCalcCubeIndexCS::FParameters>();
Parameters->Counter = CounterBufferUAV;
Parameters->MarchingCubeParameters = UniformParametersBuffer;
Parameters->SrcVoxelData = GridBufferSRV;
Parameters->OutCubeIndexOffsets = CubeIndexOffsetBufferUAV;
FRDGPassRef CalcPass = FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("Voxel Marching Cubes Calc CubeIndex"), CSRef, Parameters, GetDispatchSize(DimensionX * DimensionY * DimensionZ));
EnsureCaptured(CalcPass);
}
GraphBuilder.Execute();
});
EnsureCaptured函数被调用时会显式声明对应Pass的依赖,以便执行的顺序是正确的。