// Copyright Epic Games, Inc. All Rights Reserved. #include "HeterogeneousVolumes.h" #include "PixelShaderUtils.h" #include "RayTracingDefinitions.h" #include "RayTracingInstance.h" #include "RayTracingInstanceBufferUtil.h" #include "RendererPrivate.h" #include "ScenePrivate.h" #include "PrimitiveDrawingUtils.h" IMPLEMENT_UNIFORM_BUFFER_STRUCT(FSparseVoxelUniformBufferParameters, "SparseVoxelUniformBuffer"); class FCopyTexture3D : public FGlobalShader { DECLARE_GLOBAL_SHADER(FCopyTexture3D); SHADER_USE_PARAMETER_STRUCT(FCopyTexture3D, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform); } static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment ) { OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D()); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Input SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, InputTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler) SHADER_PARAMETER(FIntVector, TextureResolution) // Output SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture3D, RWOutputTexture) END_SHADER_PARAMETER_STRUCT() static int32 GetThreadGroupSize3D() { return 4; } }; IMPLEMENT_GLOBAL_SHADER(FCopyTexture3D, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesPreshadingPipeline.usf", "CopyTexture3D", SF_Compute); void CopyTexture3D( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef Texture, uint32 InputMipLevel, FRDGTextureRef& OutputTexture ) { FRDGTextureDesc OutputTextureDesc = OutputTexture->Desc; const FIntVector TextureResolution(OutputTextureDesc.Extent.X, OutputTextureDesc.Extent.Y, OutputTextureDesc.Depth); FCopyTexture3D::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->TextureResolution = TextureResolution; PassParameters->InputTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(Texture, InputMipLevel)); PassParameters->RWOutputTexture = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(OutputTexture)); } uint32 GroupCountX = FMath::DivideAndRoundUp(TextureResolution.X, FCopyTexture3D::GetThreadGroupSize3D()); uint32 GroupCountY = FMath::DivideAndRoundUp(TextureResolution.Y, FCopyTexture3D::GetThreadGroupSize3D()); uint32 GroupCountZ = FMath::DivideAndRoundUp(TextureResolution.Z, FCopyTexture3D::GetThreadGroupSize3D()); FIntVector GroupCount(GroupCountX, GroupCountY, GroupCountZ); TShaderRef ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("CopyTexture3D"), ComputeShader, PassParameters, GroupCount); } class FClearAllocator : public FGlobalShader { DECLARE_GLOBAL_SHADER(FClearAllocator); SHADER_USE_PARAMETER_STRUCT(FClearAllocator, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWAllocatorBuffer) END_SHADER_PARAMETER_STRUCT() }; IMPLEMENT_GLOBAL_SHADER(FClearAllocator, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesSparseVoxelPipeline.usf", "ClearAllocator", SF_Compute); class FAllocateSparseVoxels : public FGlobalShader { DECLARE_GLOBAL_SHADER(FAllocateSparseVoxels); SHADER_USE_PARAMETER_STRUCT(FAllocateSparseVoxels, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform); } static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment ) { OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D()); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Input SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, VoxelMinTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler) SHADER_PARAMETER(FIntVector, VoxelMinResolution) SHADER_PARAMETER(uint32, MipLevel) // Output SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumVoxelsBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelBuffer) END_SHADER_PARAMETER_STRUCT() static int32 GetThreadGroupSize3D() { return 4; } }; IMPLEMENT_GLOBAL_SHADER(FAllocateSparseVoxels, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesSparseVoxelPipeline.usf", "AllocateSparseVoxels", SF_Compute); class FAllocateSparseVoxelsToRefine : public FGlobalShader { DECLARE_GLOBAL_SHADER(FAllocateSparseVoxelsToRefine); SHADER_USE_PARAMETER_STRUCT(FAllocateSparseVoxelsToRefine, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform); } static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment ) { OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_3D"), GetThreadGroupSize3D()); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Input SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, VoxelMinTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler) SHADER_PARAMETER(FIntVector, VoxelMinResolution) SHADER_PARAMETER(uint32, MipLevel) // Output SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumVoxelsBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumVoxelsToRefineBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelsToRefineBuffer) END_SHADER_PARAMETER_STRUCT() static int32 GetThreadGroupSize3D() { return 4; } }; IMPLEMENT_GLOBAL_SHADER(FAllocateSparseVoxelsToRefine, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesSparseVoxelPipeline.usf", "AllocateSparseVoxelsToRefine", SF_Compute); class FRefineSparseVoxels : public FGlobalShader { DECLARE_GLOBAL_SHADER(FRefineSparseVoxels); SHADER_USE_PARAMETER_STRUCT(FRefineSparseVoxels, FGlobalShader); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return DoesPlatformSupportHeterogeneousVolumes(Parameters.Platform); } static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment ) { OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_1D"), GetThreadGroupSize1D()); OutEnvironment.CompilerFlags.Add(CFLAG_AllowTypedUAVLoads); } BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) // Input SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture3D, VoxelMinTexture) SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler) SHADER_PARAMETER(FIntVector, VolumeResolution) SHADER_PARAMETER(FIntVector, MipVolumeResolution) SHADER_PARAMETER(uint32, MipLevel) // Refinement data SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, NumVoxelsToRefineBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, VoxelsToRefineBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumVoxelsToRefineBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelsToRefineBuffer) // Output SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, RWNumVoxelsBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, RWVoxelBuffer) // Indirect args RDG_BUFFER_ACCESS(IndirectArgs, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() static int32 GetThreadGroupSize1D() { return 64; } }; IMPLEMENT_GLOBAL_SHADER(FRefineSparseVoxels, "/Engine/Private/HeterogeneousVolumes/HeterogeneousVolumesSparseVoxelPipeline.usf", "RefineSparseVoxels", SF_Compute); void GenerateSparseVoxels( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef VoxelMinTexture, FIntVector VolumeResolution, uint32 MipLevel, FRDGBufferRef& NumVoxelsBuffer, FRDGBufferRef& VoxelBuffer ) { FIntVector VoxelMinResolution = FIntVector( VoxelMinTexture->Desc.Extent.X, VoxelMinTexture->Desc.Extent.Y, VoxelMinTexture->Desc.Depth ); NumVoxelsBuffer = GraphBuilder.CreateBuffer( FRDGBufferDesc::CreateIndirectDesc(1), TEXT("HeterogeneousVolumes.NumVoxelsBuffer") ); // Clear allocator { FClearAllocator::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->RWAllocatorBuffer = GraphBuilder.CreateUAV(NumVoxelsBuffer, PF_R32_UINT); } TShaderRef ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("ClearAllocator"), ERDGPassFlags::Compute | ERDGPassFlags::NeverCull, ComputeShader, PassParameters, FIntVector(1) ); } // Generate sets of OOBBs at a candidate MIP level for a given VRE coefficient VoxelBuffer = GraphBuilder.CreateBuffer( FRDGBufferDesc::CreateStructuredDesc(sizeof(FVoxelDataPacked), HeterogeneousVolumes::GetVoxelCount(VoxelMinResolution)), TEXT("HeterogeneousVolumes.VoxelBuffer") ); if (HeterogeneousVolumes::ShouldRefineSparseVoxels()) { // Refinement buffers FRDGBufferRef NumVoxelsToRefineBuffer[2] = { GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("HeterogeneousVolume.NumVoxelsToRefineBuffer1")), GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(1), TEXT("HeterogeneousVolume.NumVoxelsToRefineBuffer2")) }; FRDGBufferRef VoxelsToRefineBuffer[2] = { GraphBuilder.CreateBuffer(VoxelBuffer->Desc, TEXT("HeterogeneousVolume.VoxelsToRefineBuffer1")), GraphBuilder.CreateBuffer(VoxelBuffer->Desc, TEXT("HeterogeneousVolume.VoxelsToRefineBuffer2")) }; uint32 InputIndex = 0; uint32 OutputIndex = 1; // Initial dense dispatch { AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(NumVoxelsToRefineBuffer[OutputIndex], PF_R32_UINT), 0); FAllocateSparseVoxelsToRefine::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->VoxelMinTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(VoxelMinTexture, 0)); PassParameters->VoxelMinResolution = VoxelMinResolution; PassParameters->MipLevel = MipLevel; PassParameters->RWNumVoxelsBuffer = GraphBuilder.CreateUAV(NumVoxelsBuffer, PF_R32_UINT); PassParameters->RWVoxelBuffer = GraphBuilder.CreateUAV(VoxelBuffer); PassParameters->RWNumVoxelsToRefineBuffer = GraphBuilder.CreateUAV(NumVoxelsToRefineBuffer[OutputIndex], PF_R32_UINT); PassParameters->RWVoxelsToRefineBuffer = GraphBuilder.CreateUAV(VoxelsToRefineBuffer[OutputIndex]); } uint32 GroupCountX = FMath::DivideAndRoundUp(VoxelMinResolution.X, FAllocateSparseVoxelsToRefine::GetThreadGroupSize3D()); uint32 GroupCountY = FMath::DivideAndRoundUp(VoxelMinResolution.Y, FAllocateSparseVoxelsToRefine::GetThreadGroupSize3D()); uint32 GroupCountZ = FMath::DivideAndRoundUp(VoxelMinResolution.Z, FAllocateSparseVoxelsToRefine::GetThreadGroupSize3D()); FIntVector GroupCount(GroupCountX, GroupCountY, GroupCountZ); TShaderRef ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("AllocateSparseVoxelsToRefine"), ERDGPassFlags::Compute | ERDGPassFlags::NeverCull, ComputeShader, PassParameters, GroupCount ); } // Flip input/output buffers for next iteration Swap(InputIndex, OutputIndex); for (uint32 MipLevelOffset = 1; MipLevelOffset < VoxelMinTexture->Desc.NumMips; ++MipLevelOffset) { // Clear refinement output buffers AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(NumVoxelsToRefineBuffer[OutputIndex], PF_R32_UINT), 0); FRefineSparseVoxels::FParameters* PassParameters = GraphBuilder.AllocParameters(); { PassParameters->VoxelMinTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(VoxelMinTexture, MipLevelOffset)); FIntVector MipVolumeResolution = FIntVector( VoxelMinResolution.X >> MipLevelOffset, VoxelMinResolution.Y >> MipLevelOffset, VoxelMinResolution.Z >> MipLevelOffset ); PassParameters->VolumeResolution = VolumeResolution; PassParameters->MipVolumeResolution = MipVolumeResolution; PassParameters->MipLevel = MipLevel + MipLevelOffset; // Intermediate refinement buffers PassParameters->NumVoxelsToRefineBuffer = GraphBuilder.CreateSRV(NumVoxelsToRefineBuffer[InputIndex], PF_R32_UINT); PassParameters->VoxelsToRefineBuffer = GraphBuilder.CreateSRV(VoxelsToRefineBuffer[InputIndex]); PassParameters->RWNumVoxelsToRefineBuffer = GraphBuilder.CreateUAV(NumVoxelsToRefineBuffer[OutputIndex], PF_R32_UINT); PassParameters->RWVoxelsToRefineBuffer = GraphBuilder.CreateUAV(VoxelsToRefineBuffer[OutputIndex]); // Sparse voxel output PassParameters->RWNumVoxelsBuffer = GraphBuilder.CreateUAV(NumVoxelsBuffer, PF_R32_UINT); PassParameters->RWVoxelBuffer = GraphBuilder.CreateUAV(VoxelBuffer); PassParameters->IndirectArgs = NumVoxelsToRefineBuffer[InputIndex]; } TShaderRef ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("RefineSparseVoxels"), ERDGPassFlags::Compute, ComputeShader, PassParameters, PassParameters->IndirectArgs, 0 ); // Flip input/output buffers for next iteration Swap(InputIndex, OutputIndex); } } else { FAllocateSparseVoxels::FParameters* PassParameters = GraphBuilder.AllocParameters(); { //PassParameters->VoxelMinTexture = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(VoxelMinTexture, MipLevel)); PassParameters->VoxelMinTexture = GraphBuilder.CreateSRV(VoxelMinTexture); // TODO: Make sure point-sampling isn't causing clipping of the isosurface PassParameters->TextureSampler = TStaticSamplerState::GetRHI(); PassParameters->VoxelMinResolution = VoxelMinResolution; PassParameters->MipLevel = MipLevel; PassParameters->RWNumVoxelsBuffer = GraphBuilder.CreateUAV(NumVoxelsBuffer, PF_R32_UINT); PassParameters->RWVoxelBuffer = GraphBuilder.CreateUAV(VoxelBuffer); } uint32 GroupCountX = FMath::DivideAndRoundUp(VoxelMinResolution.X, FAllocateSparseVoxels::GetThreadGroupSize3D()); uint32 GroupCountY = FMath::DivideAndRoundUp(VoxelMinResolution.Y, FAllocateSparseVoxels::GetThreadGroupSize3D()); uint32 GroupCountZ = FMath::DivideAndRoundUp(VoxelMinResolution.Z, FAllocateSparseVoxels::GetThreadGroupSize3D()); FIntVector GroupCount(GroupCountX, GroupCountY, GroupCountZ); TShaderRef ComputeShader = View.ShaderMap->GetShader(); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("AllocateSparseVoxels"), ERDGPassFlags::Compute | ERDGPassFlags::NeverCull, ComputeShader, PassParameters, GroupCount ); } }