// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= SubsurfaceTiles.h: Screenspace subsurface scattering tile buffer implementation. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "RendererInterface.h" #include "RenderGraphResources.h" #include "GlobalShader.h" #include "ShaderParameterStruct.h" #include "ScreenPass.h" #include "SceneRendering.h" // Adapted from FHairStrandsTile struct FSubsurfaceTiles { // Support two types of acceleration. It is independent of SSS profile // AFIS: adaptive filtered importance sampling // SEPARABLE: two pass separable filter // PASSTHROUGH: directly pass through because of subpixel SSS // AllNonPassthrough: all subsurface tiles but without passthrough tiles enum class ETileType : uint8 {AFIS, SEPARABLE, PASSTHROUGH, AllNonPassthrough, All, Count}; static const uint32 TileTypeCount = uint32(ETileType::Count); FIntPoint BufferResolution = FIntPoint(0, 0); static const uint32 GroupSize = 64; // Define the size of subsurface tile. @TODO: Set to 16 to use LDS. static const uint32 TileSize = 8; static const uint32 TilePerThread_GroupSize = 64; uint32 TileCount = 0; FIntPoint TileDimension = FIntPoint(0, 0); bool bRectPrimitive = false; // Buffers per tile types FRDGBufferSRVRef TileDataSRV[TileTypeCount] = { nullptr, nullptr }; FRDGBufferRef TileDataBuffer[TileTypeCount] = { nullptr, nullptr }; FRDGBufferSRVRef TileTypeCountSRV = nullptr; FRDGBufferRef TileTypeCountBuffer = nullptr; FRDGBufferRef TileIndirectDispatchBuffer = nullptr; FRDGBufferRef TileIndirectDrawBuffer = nullptr; FRDGBufferRef TileIndirectRayDispatchBuffer = nullptr; FRDGBufferRef TilePerThreadIndirectDispatchBuffer = nullptr; static FORCEINLINE uint32 GetIndirectDrawArgOffset(ETileType Type) { return uint32(Type) * sizeof(FRHIDrawIndirectParameters); } static FORCEINLINE uint32 GetIndirectDispatchArgOffset(ETileType Type) { return uint32(Type) * sizeof(FRHIDispatchIndirectParameters); } bool IsValid() const { return TileCount > 0 && TileDataBuffer[uint32(ETileType::All)];} FRDGBufferRef GetTileBuffer(ETileType Type) const { const uint32 Index = uint32(Type); check(TileDataBuffer[Index] != nullptr); return TileDataBuffer[Index]; } FRDGBufferSRVRef GetTileBufferSRV(ETileType Type) const { const uint32 Index = uint32(Type); check(TileDataSRV[Index] != nullptr); return TileDataSRV[Index]; } }; FORCEINLINE uint32 ToIndex(FSubsurfaceTiles::ETileType Type) { return uint32(Type); } const TCHAR* ToString(FSubsurfaceTiles::ETileType Type); class FSubsurfaceTilePassVS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FSubsurfaceTilePassVS); SHADER_USE_PARAMETER_STRUCT(FSubsurfaceTilePassVS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, ViewMin) SHADER_PARAMETER(FIntPoint, ViewMax) SHADER_PARAMETER(int32, bRectPrimitive) SHADER_PARAMETER(uint32, TileType) SHADER_PARAMETER(FVector2f, ExtentInverse) SHADER_PARAMETER(uint32, OutputFlag) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TileDataBuffer) RDG_BUFFER_ACCESS(TileIndirectBuffer, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters); static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment); }; class FSubsurfaceTileFallbackScreenPassVS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FSubsurfaceTileFallbackScreenPassVS); static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters); FSubsurfaceTileFallbackScreenPassVS() = default; FSubsurfaceTileFallbackScreenPassVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) {} }; FSubsurfaceTilePassVS::FParameters GetSubsurfaceTileParameters(const FScreenPassTextureViewport& TileViewport, const FSubsurfaceTiles& InTile, FSubsurfaceTiles::ETileType TileType); template void AddSubsurfaceTiledScreenPass( FRDGBuilder& GraphBuilder, FRDGEventName&& Name, const FViewInfo& View, typename FSubsurfacePassPS::FParameters* PassParameters, TShaderMapRef& VertexShader, TShaderMapRef& PixelShader, FRHIBlendState* BlendState, FRHIDepthStencilState* DepthStencilState, const FScreenPassTextureViewport SceneViewport, FSubsurfaceTiles::ETileType TileType, const bool bShouldFallbackToFullScreenPass = false) { if (bShouldFallbackToFullScreenPass) { TShaderMapRef FallBackVertexShader(View.ShaderMap); AddDrawScreenPass(GraphBuilder, Forward(Name), View, SceneViewport, SceneViewport, FallBackVertexShader, PixelShader, BlendState, DepthStencilState, PassParameters); } else { GraphBuilder.AddPass( Forward(Name), PassParameters, ERDGPassFlags::Raster, [PassParameters, VertexShader, PixelShader, DepthStencilState, BlendState, Viewport = SceneViewport, TileType](FRDGAsyncTask, FRHICommandList& RHICmdList) { typename FSubsurfacePassVS::FParameters ParametersVS = PassParameters->TileParameters; FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = BlendState; GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = DepthStencilState; GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader(); GraphicsPSOInit.PrimitiveType = PassParameters->TileParameters.bRectPrimitive > 0 ? PT_RectList : PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters); RHICmdList.SetViewport(0.0f, 0.0f, 0.0f, Viewport.Extent.X, Viewport.Extent.Y, 1.0f); RHICmdList.SetStreamSource(0, nullptr, 0); RHICmdList.DrawPrimitiveIndirect(PassParameters->TileParameters.TileIndirectBuffer->GetRHI(), FSubsurfaceTiles::GetIndirectDrawArgOffset(TileType)); }); } } /** Clear the UAV texture to black only when ConditionBuffer[Offset] > 0 */ void AddConditionalClearBlackUAVPass(FRDGBuilder& GraphBuilder, FRDGEventName&& PassName, FRDGTextureRef Texture, const FScreenPassTextureViewport& ScreenPassViewport, FRDGBufferRef ConditionBuffer, uint32 Offset); /** * Code adapted from ScreenSpaceReflectionTiles to reduce Setup SSS cost. * Build lists of 8x8 tiles used by SSS pixels * Mark and build list steps are separated in order to build a more coherent list (z-ordered over a larger region), which is important for the performance of future passes due to neighbor diffusion. */ FSubsurfaceTiles ClassifySSSTiles(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSceneTextures& SceneTextures, const FScreenPassTextureViewportParameters SceneViewportParameters, const FScreenPassTextureViewportParameters SubsurfaceViewportParameters, const bool IsHalfRes);