// Copyright Epic Games, Inc. All Rights Reserved. #include "HairStrandsTile.h" #include "HairStrandsUtils.h" #include "HairStrandsInterface.h" #include "DataDrivenShaderPlatformInfo.h" #include "Shader.h" #include "GlobalShader.h" #include "ShaderParameters.h" #include "ShaderParameterStruct.h" #include "SceneTextureParameters.h" #include "RenderGraphUtils.h" #include "PostProcess/PostProcessing.h" #include "MeshPassProcessor.inl" #include "ScenePrivate.h" #include "ShaderPrintParameters.h" #include "ShaderPrint.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const TCHAR* ToString(FHairStrandsTiles::ETileType Type) { switch (Type) { case FHairStrandsTiles::ETileType::HairAll: return TEXT("Hair(All)"); case FHairStrandsTiles::ETileType::HairFull: return TEXT("Hair(Full)"); case FHairStrandsTiles::ETileType::HairPartial: return TEXT("Hair(Partial)"); case FHairStrandsTiles::ETileType::Other: return TEXT("Other"); default: return TEXT("Unknown"); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// FHairStrandsTilePassVS::FParameters GetHairStrandsTileParameters(const FViewInfo& InView, const FHairStrandsTiles& InTile, FHairStrandsTiles::ETileType TileType) { FHairStrandsTilePassVS::FParameters Out; Out.TileType = uint32(TileType); Out.bRectPrimitive = InTile.bRectPrimitive ? 1 : 0; Out.ViewMin = InView.ViewRect.Min; Out.ViewInvSize = FVector2f(1.f / InView.ViewRect.Width(), 1.f / InView.ViewRect.Height()); Out.TileDataBuffer = InTile.IsValid() ? InTile.GetTileBufferSRV(TileType) : nullptr; Out.TileIndirectBuffer = InTile.TileIndirectDrawBuffer; return Out; } static ERHIFeatureSupport HairSupportsWaveOps(EShaderPlatform Platform) { // D3D11 / SM5 or preview do not support, or work well with, wave-ops by default (or SM5 preview has issues with wave intrinsics too), that fixes classification and black/wrong tiling. if (Platform == SP_PCD3D_SM5 || FDataDrivenShaderPlatformInfo::GetIsPreviewPlatform(Platform)) { return ERHIFeatureSupport::Unsupported; } return FDataDrivenShaderPlatformInfo::GetSupportsWaveOperations(Platform); } //////////////////////////////////////////////////////////////////////////////////////////////////////////// // Generate indirect draw and indirect dispatch buffers class FHairStrandsTileCopyArgsPassCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsTileCopyArgsPassCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsTileCopyArgsPassCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(FIntPoint, TileCountXY) SHADER_PARAMETER(uint32, TilePerThread_GroupSize) SHADER_PARAMETER(uint32, bRectPrimitive) SHADER_PARAMETER(uint32, TileSize) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TileCountBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileIndirectDrawBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileIndirectDispatchBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileIndirectRayDispatchBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TilePerThreadIndirectDispatchBuffer) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_COPY_ARGS"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsTileCopyArgsPassCS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "MainCS", SF_Compute); void AddHairStrandsCopyArgsTilesPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, FHairStrandsTiles& TileData) { TShaderMapRef ComputeShader(View.ShaderMap); FHairStrandsTileCopyArgsPassCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->TileSize = TileData.TileSize; PassParameters->TileCountXY = TileData.TileCountXY; PassParameters->TilePerThread_GroupSize = TileData.TilePerThread_GroupSize; PassParameters->bRectPrimitive = TileData.bRectPrimitive ? 1 : 0; PassParameters->TileCountBuffer = TileData.TileCountSRV; PassParameters->TileIndirectDrawBuffer = GraphBuilder.CreateUAV(TileData.TileIndirectDrawBuffer, PF_R32_UINT); PassParameters->TileIndirectDispatchBuffer = GraphBuilder.CreateUAV(TileData.TileIndirectDispatchBuffer, PF_R32_UINT); PassParameters->TileIndirectRayDispatchBuffer = GraphBuilder.CreateUAV(TileData.TileIndirectRayDispatchBuffer, PF_R32_UINT); PassParameters->TilePerThreadIndirectDispatchBuffer = GraphBuilder.CreateUAV(TileData.TilePerThreadIndirectDispatchBuffer, PF_R32_UINT); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TileCopyArgs"), ComputeShader, PassParameters, FIntVector(1, 1, 1)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Generate tiles data based on input texture (hair pixel coverage) class FHairStrandsTileGenerationPassCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsTileGenerationPassCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsTileGenerationPassCS, FGlobalShader); class FInputType : SHADER_PERMUTATION_INT("PERMUTATION_INPUT_TYPE", 2); class FWaveOps : SHADER_PERMUTATION_BOOL("PERMUTATION_WAVEOPS"); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(FIntPoint, BufferResolution) SHADER_PARAMETER(uint32, bForceOutputAllTiles) SHADER_PARAMETER(float, TransmittanceThreshold) SHADER_PARAMETER(uint32, IntCoverageThreshold) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputFloatTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, InputUintTexture) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileHairAllBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileHairFullBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileHairPartialBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileOtherBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileCountBuffer) SHADER_PARAMETER_RDG_BUFFER_UAV(RWBuffer, TileClearBuffer) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get() && HairSupportsWaveOps(Parameters.Platform) == ERHIFeatureSupport::Unsupported) { return false; } return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_GENERATION"), 1); FPermutationDomain PermutationVector(Parameters.PermutationId); if (PermutationVector.Get()) { OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations); } } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsTileGenerationPassCS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "TileMainCS", SF_Compute); float GetHairStrandsFullCoverageThreshold(); uint32 GetHairStrandsIntCoverageThreshold(); static FHairStrandsTiles AddHairStrandsGenerateTilesPass_Internal( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FIntPoint& InputResolution, const FRDGTextureRef& InputTexture) { const bool bHasValidInput = InputTexture != nullptr; const bool bUintTexture = InputTexture && InputTexture->Desc.Format == PF_R32_UINT; const bool bWaveOps = GRHISupportsWaveOperations&& GRHIMaximumWaveSize >= 64 && HairSupportsWaveOps(View.GetShaderPlatform()) != ERHIFeatureSupport::Unsupported; FHairStrandsTiles Out; check(FHairStrandsTiles::TilePerThread_GroupSize == 64); // If this value change, we need to update the shaders using check(FHairStrandsTiles::TileSize == 8); // only size supported for now Out.TileCountXY = FIntPoint(FMath::CeilToInt(InputResolution.X / float(FHairStrandsTiles::TileSize)), FMath::CeilToInt(InputResolution.Y / float(FHairStrandsTiles::TileSize))); Out.TileCount = Out.TileCountXY.X * Out.TileCountXY.Y; Out.BufferResolution = InputResolution; Out.bRectPrimitive = GRHISupportsRectTopology; Out.TileCountBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(4, FHairStrandsTiles::TileTypeCount), TEXT("Hair.TileCountBuffer")); Out.TileIndirectDrawBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FHairStrandsTiles::TileTypeCount), TEXT("Hair.TileIndirectDrawBuffer")); Out.TileIndirectDispatchBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FHairStrandsTiles::TileTypeCount), TEXT("Hair.TileIndirectDispatchBuffer")); Out.TileIndirectRayDispatchBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FHairStrandsTiles::TileTypeCount), TEXT("Hair.TileIndirectRayDispatchBuffer")); Out.TilePerThreadIndirectDispatchBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateIndirectDesc(FHairStrandsTiles::TileTypeCount), TEXT("Hair.TilePerThreadIndirectDispatchBuffer")); Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairAll)] = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Out.TileCount), TEXT("Hair.TileDataBuffer(HairAll)")); Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairFull)] = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Out.TileCount), TEXT("Hair.TileDataBuffer(HairFull)")); Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairPartial)] = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Out.TileCount), TEXT("Hair.TileDataBuffer(HairPartial)")); Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::Other)] = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), Out.TileCount), TEXT("Hair.TileDataBuffer(Other)")); FRDGBufferUAVRef TileCountUAV = GraphBuilder.CreateUAV(Out.TileCountBuffer, PF_R32_UINT); AddClearUAVPass(GraphBuilder, TileCountUAV, 0u); FHairStrandsTileGenerationPassCS::FPermutationDomain PermutationVector; PermutationVector.Set(bUintTexture ? 1u : 0u); PermutationVector.Set(bWaveOps); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FHairStrandsTileGenerationPassCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->BufferResolution = View.ViewRect.Size(); PassParameters->bForceOutputAllTiles = bHasValidInput ? 0 : 1; PassParameters->TransmittanceThreshold = 1.f - GetHairStrandsFullCoverageThreshold(); PassParameters->IntCoverageThreshold = GetHairStrandsIntCoverageThreshold(); PassParameters->InputFloatTexture = GSystemTextures.GetBlackDummy(GraphBuilder); PassParameters->InputUintTexture = GSystemTextures.GetZeroUIntDummy(GraphBuilder); if (bHasValidInput && bUintTexture) { PassParameters->InputUintTexture = InputTexture; } else if (bHasValidInput && !bUintTexture) { PassParameters->InputFloatTexture = InputTexture; } PassParameters->TileHairAllBuffer = GraphBuilder.CreateUAV(Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairAll)], PF_R16G16_UINT); PassParameters->TileHairFullBuffer = GraphBuilder.CreateUAV(Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairFull)], PF_R16G16_UINT); PassParameters->TileHairPartialBuffer = GraphBuilder.CreateUAV(Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::HairPartial)], PF_R16G16_UINT); PassParameters->TileOtherBuffer = GraphBuilder.CreateUAV(Out.TileDataBuffer[ToIndex(FHairStrandsTiles::ETileType::Other)], PF_R16G16_UINT); PassParameters->TileCountBuffer = TileCountUAV; FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TileClassification"), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(InputResolution, FHairStrandsTiles::TileSize)); Out.TileCountSRV = GraphBuilder.CreateSRV(Out.TileCountBuffer, PF_R32_UINT); for (uint32 BufferIt = 0; BufferIt < FHairStrandsTiles::TileTypeCount; ++BufferIt) { if (Out.TileDataBuffer[BufferIt]) { Out.TileDataSRV[BufferIt] = GraphBuilder.CreateSRV(Out.TileDataBuffer[BufferIt], PF_R16G16_UINT); } } // Initialize indirect dispatch buffer, based on the indirect draw bugger AddHairStrandsCopyArgsTilesPass(GraphBuilder, View, Out); return Out; } FHairStrandsTiles AddHairStrandsGenerateTilesPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FRDGTextureRef& InputTexture) { return AddHairStrandsGenerateTilesPass_Internal(GraphBuilder, View, InputTexture->Desc.Extent, InputTexture); } FHairStrandsTiles AddHairStrandsGenerateTilesPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FIntPoint& Resolution) { return AddHairStrandsGenerateTilesPass_Internal(GraphBuilder, View, Resolution, nullptr); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool FHairStrandsTilePassVS::ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } void FHairStrandsTilePassVS::ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_VS"), 1); } IMPLEMENT_GLOBAL_SHADER(FHairStrandsTilePassVS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "MainVS", SF_Vertex); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class FHairStrandsTileDebugPrintPassCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsTileDebugPrintPassCS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsTileDebugPrintPassCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, MaxResolution) SHADER_PARAMETER(uint32, TileGroupSize) SHADER_PARAMETER(uint32, TileSize) SHADER_PARAMETER(uint32, TileCount) SHADER_PARAMETER(uint32, TileType) SHADER_PARAMETER(FIntPoint, TileCountXY) SHADER_PARAMETER(uint32, bRectPrimitive) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_DEBUG_PRINT"), 1); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsTileDebugPrintPassCS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "MainCS", SF_Compute); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class FHairStrandsTileDebugPassPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsTileDebugPassPS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsTileDebugPassPS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsTilePassVS::FParameters, TileParameters) SHADER_PARAMETER(FVector3f, DebugColor) SHADER_PARAMETER(FIntPoint, OutputResolution) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return GetMaxSupportedFeatureLevel(Parameters.Platform) >= ERHIFeatureLevel::SM5; //TODO } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_DEBUG"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsTileDebugPassPS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "MainPS", SF_Pixel); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void AddHairStrandsDebugTilePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FRDGTextureRef& ColorTexture, const FHairStrandsTiles& TileData) { const FIntRect Viewport = View.ViewRect; auto DrawDebugTile = [&](FHairStrandsTiles::ETileType TileType) { FHairStrandsTileDebugPassPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->TileParameters = GetHairStrandsTileParameters(View, TileData, TileType); PassParameters->DebugColor = FVector3f(1.f, 0.f, 1.f); switch (TileType) { case FHairStrandsTiles::ETileType::HairAll: PassParameters->DebugColor = FVector3f(1.f, 1.f, 0.f); break; case FHairStrandsTiles::ETileType::HairFull: PassParameters->DebugColor = FVector3f(1.f, 0.f, 0.f); break; case FHairStrandsTiles::ETileType::HairPartial: PassParameters->DebugColor = FVector3f(0.f, 1.f, 0.f); break; case FHairStrandsTiles::ETileType::Other: PassParameters->DebugColor = FVector3f(0.6f, 0.6f, 0.6f); break; } TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); PassParameters->RenderTargets[0] = FRenderTargetBinding(ColorTexture, ERenderTargetLoadAction::ELoad); GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::TileDebugPass(%s)", ToString(TileType)), PassParameters, ERDGPassFlags::Raster, [PassParameters, VertexShader, PixelShader, Viewport, TileType](FRDGAsyncTask, FRHICommandList& RHICmdList) { FHairStrandsTilePassVS::FParameters ParametersVS = PassParameters->TileParameters; FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState::GetRHI(); 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(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); RHICmdList.SetStreamSource(0, nullptr, 0); RHICmdList.DrawPrimitiveIndirect(PassParameters->TileParameters.TileIndirectBuffer->GetRHI(), FHairStrandsTiles::GetIndirectDrawArgOffset(TileType)); }); }; DrawDebugTile(FHairStrandsTiles::ETileType::HairFull); DrawDebugTile(FHairStrandsTiles::ETileType::HairPartial); DrawDebugTile(FHairStrandsTiles::ETileType::Other); if (View.HairStrandsViewData.UniformBuffer || ShaderPrint::IsEnabled(View.ShaderPrintData)) { static FHairStrandsTiles::ETileType TileType = FHairStrandsTiles::ETileType::HairAll; FHairStrandsTileDebugPrintPassCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->MaxResolution = FIntPoint(Viewport.Width(), Viewport.Height()); Parameters->TileType = uint32(TileType); Parameters->TileGroupSize = TileData.GroupSize; Parameters->TileSize = TileData.TileSize; Parameters->TileCount = TileData.TileCount; Parameters->TileCountXY = TileData.TileCountXY; Parameters->bRectPrimitive = TileData.bRectPrimitive ? 1u : 0u; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintUniformBuffer); TShaderMapRef ComputeShader(View.ShaderMap); ClearUnusedGraphResources(ComputeShader, Parameters); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TileDebugPrint(%s)", ToString(TileType)), ComputeShader, Parameters, FIntVector(1, 1, 1)); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class FHairTileClearCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairTileClearCS); SHADER_USE_PARAMETER_STRUCT(FHairTileClearCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, Resolution) SHADER_PARAMETER(FIntPoint, TileCountXY) SHADER_PARAMETER(FIntPoint, ViewRectMin) SHADER_PARAMETER(uint32, TileSize) SHADER_PARAMETER(uint32, TileType) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TileDataBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TileCountBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairTileCount) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTexture) RDG_BUFFER_ACCESS(TileIndirectArgs, ERHIAccess::IndirectArgs) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TILE_CLEAR"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairTileClearCS, "/Engine/Private/HairStrands/HairStrandsVisibilityTile.usf", "TileMainCS", SF_Compute); void AddHairStrandsTileClearPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsTiles& TileData, FHairStrandsTiles::ETileType TileType, FRDGTextureRef OutTexture) { if (!OutTexture || !TileData.IsValid() || TileType == FHairStrandsTiles::ETileType::Count) { return; } FHairTileClearCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Resolution = OutTexture->Desc.Extent; PassParameters->TileCountXY = TileData.TileCountXY; PassParameters->ViewRectMin = View.ViewRect.Min; PassParameters->TileSize = TileData.TileSize; PassParameters->TileType = ToIndex(TileType); PassParameters->TileCountBuffer = TileData.TileCountSRV; PassParameters->TileDataBuffer = TileData.GetTileBufferSRV(TileType); PassParameters->TileIndirectArgs = TileData.TileIndirectDispatchBuffer; PassParameters->OutTexture = GraphBuilder.CreateUAV(OutTexture); FHairTileClearCS::FPermutationDomain PermutationVector; TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::TileClear(%s)", ToString(TileType)), ComputeShader, PassParameters, PassParameters->TileIndirectArgs, TileData.GetIndirectDispatchArgOffset(TileType)); }