// Copyright Epic Games, Inc. All Rights Reserved. #include "HairStrandsDebug.h" #include "HairStrandsInterface.h" #include "HairStrandsDeepShadow.h" #include "HairStrandsUtils.h" #include "HairStrandsVoxelization.h" #include "HairStrandsRendering.h" #include "HairStrandsVisibility.h" #include "HairStrandsInterface.h" #include "HairStrandsTile.h" #include "HairStrandsData.h" #include "Shader.h" #include "GlobalShader.h" #include "ShaderParameters.h" #include "ShaderParameterStruct.h" #include "RenderGraphUtils.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/SceneFilterRendering.h" #include "PostProcess/SceneRenderTargets.h" #include "SceneTextureParameters.h" #include "DynamicPrimitiveDrawing.h" #include "CanvasTypes.h" #include "ShaderPrintParameters.h" #include "RenderGraphUtils.h" #include "ShaderPrint.h" #include "ScreenPass.h" #include "ScenePrivate.h" #include "UnrealEngine.h" #include "DataDrivenShaderPlatformInfo.h" #include "GroomVisualizationData.h" static int32 GDeepShadowDebugIndex = 0; static float GDeepShadowDebugScale = 20; static FAutoConsoleVariableRef CVarDeepShadowDebugDomIndex(TEXT("r.HairStrands.DeepShadow.DebugDOMIndex"), GDeepShadowDebugIndex, TEXT("Index of the DOM texture to draw")); static FAutoConsoleVariableRef CVarDeepShadowDebugDomScale(TEXT("r.HairStrands.DeepShadow.DebugDOMScale"), GDeepShadowDebugScale, TEXT("Scaling value for the DeepOpacityMap when drawing the deep shadow stats")); static int32 GHairStrandsDebugPlotBsdf = 0; static FAutoConsoleVariableRef CVarHairStrandsDebugBSDF(TEXT("r.HairStrands.PlotBsdf"), GHairStrandsDebugPlotBsdf, TEXT("Debug view for visualizing hair BSDF.")); static float GHairStrandsDebugPlotBsdfRoughness = 0.3f; static FAutoConsoleVariableRef CVarHairStrandsDebugBSDFRoughness(TEXT("r.HairStrands.PlotBsdf.Roughness"), GHairStrandsDebugPlotBsdfRoughness, TEXT("Change the roughness of the debug BSDF plot.")); static float GHairStrandsDebugPlotBsdfBaseColor = 1; static FAutoConsoleVariableRef CVarHairStrandsDebugBSDFAbsorption(TEXT("r.HairStrands.PlotBsdf.BaseColor"), GHairStrandsDebugPlotBsdfBaseColor, TEXT("Change the base color / absorption of the debug BSDF plot.")); static float GHairStrandsDebugPlotBsdfExposure = 1.1f; static FAutoConsoleVariableRef CVarHairStrandsDebugBSDFExposure(TEXT("r.HairStrands.PlotBsdf.Exposure"), GHairStrandsDebugPlotBsdfExposure, TEXT("Change the exposure of the plot.")); static int32 GHairVirtualVoxel_DebugTraversalType = 0; static FAutoConsoleVariableRef CVarHairVirtualVoxel_DebugTraversalType(TEXT("r.HairStrands.Voxelization.Virtual.DebugTraversalType"), GHairVirtualVoxel_DebugTraversalType, TEXT("Traversal mode (0:linear, 1:mip) for debug voxel visualization.")); static bool TryEnableShaderDrawAndShaderPrint(const FViewInfo& View, uint32 ResquestedShaderDrawElements, uint32 RequestedShaderPrintElements) { // Force ShaderPrint on. ShaderPrint::SetEnabled(true); ShaderPrint::RequestSpaceForCharacters(RequestedShaderPrintElements); ShaderPrint::RequestSpaceForLines(ResquestedShaderDrawElements); return ShaderPrint::IsEnabled(View.ShaderPrintData); } FHairStrandsDebugData::FPlotData FHairStrandsDebugData::CreatePlotData(FRDGBuilder& GraphBuilder) { FHairStrandsDebugData::FPlotData Out; Out.ShadingPointBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(ShadingInfo), MaxShadingPointCount), TEXT("Hair.DebugShadingPoint")); Out.ShadingPointCounter = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Hair.DebugShadingPointCounter")); Out.SampleBuffer = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateStructuredDesc(sizeof(Sample), MaxSampleCount), TEXT("Hair.DebugSample")); Out.SampleCounter = GraphBuilder.CreateBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(uint32), 1), TEXT("Hair.DebugSampleCounter")); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Out.ShadingPointCounter, PF_R32_UINT), 0u); AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Out.SampleCounter, PF_R32_UINT), 0u); return Out; } void FHairStrandsDebugData::SetParameters(FRDGBuilder& GraphBuilder, const FHairStrandsDebugData::FPlotData& In, FHairStrandsDebugData::FWriteParameters& Out) { Out.Debug_MaxSampleCount = FHairStrandsDebugData::MaxSampleCount; Out.Debug_MaxShadingPointCount = FHairStrandsDebugData::MaxShadingPointCount; Out.Debug_ShadingPointBuffer = GraphBuilder.CreateUAV(In.ShadingPointBuffer); Out.Debug_ShadingPointCounter = GraphBuilder.CreateUAV(In.ShadingPointCounter, PF_R32_UINT); Out.Debug_SampleBuffer = GraphBuilder.CreateUAV(In.SampleBuffer); Out.Debug_SampleCounter = GraphBuilder.CreateUAV(In.SampleCounter, PF_R32_UINT); } void FHairStrandsDebugData::SetParameters(FRDGBuilder& GraphBuilder, const FHairStrandsDebugData::FPlotData& In, FHairStrandsDebugData::FReadParameters& Out) { Out.Debug_MaxSampleCount = FHairStrandsDebugData::MaxSampleCount; Out.Debug_MaxShadingPointCount = FHairStrandsDebugData::MaxShadingPointCount; Out.Debug_ShadingPointBuffer = GraphBuilder.CreateSRV(In.ShadingPointBuffer); Out.Debug_ShadingPointCounter = GraphBuilder.CreateSRV(In.ShadingPointCounter, PF_R32_UINT); Out.Debug_SampleBuffer = GraphBuilder.CreateSRV(In.SampleBuffer); Out.Debug_SampleCounter = GraphBuilder.CreateSRV(In.SampleCounter, PF_R32_UINT); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairPrintLODInfoCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairPrintLODInfoCS); SHADER_USE_PARAMETER_STRUCT(FHairPrintLODInfoCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, MaxResolution) SHADER_PARAMETER(FVector3f, GroupColor) SHADER_PARAMETER(uint32, GroupIndex) SHADER_PARAMETER(uint32, GeometryType) SHADER_PARAMETER(uint32, CurveCount) SHADER_PARAMETER(uint32, PointCount) SHADER_PARAMETER(float, CoverageScale) SHADER_PARAMETER(float, ScreenSize) SHADER_PARAMETER(float, LOD) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::All, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { // Skip optimization for avoiding long compilation time due to large UAV writes FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); ShaderPrint::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Debug); OutEnvironment.SetDefine(TEXT("SHADER_LOD_INFO"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairPrintLODInfoCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainCS", SF_Compute); static void AddPrintLODInfoPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairGroupPublicData* Data) { if (!ShaderPrint::IsSupported(View.GetShaderPlatform())) { return; } // Force ShaderPrint on. ShaderPrint::SetEnabled(true); ShaderPrint::RequestSpaceForCharacters(2000); if (!ShaderPrint::IsEnabled(View.ShaderPrintData)) { return; } const uint32 GroupIndex = Data->GetGroupIndex(); const FLinearColor GroupColor = Data->DebugGroupColor; const uint32 IntLODIndex = Data->LODIndex; FHairPrintLODInfoCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->MaxResolution = FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()); Parameters->GroupIndex = GroupIndex; Parameters->LOD = Data->LODIndex; Parameters->GroupColor = FVector3f(GroupColor.R, GroupColor.G, GroupColor.B); Parameters->ScreenSize = Data->DebugScreenSize; Parameters->CurveCount = Data->GetActiveStrandsCurveCount(); Parameters->PointCount = Data->GetActiveStrandsPointCount(); Parameters->CoverageScale = Data->GetActiveStrandsCoverageScale(); switch (Data->VFInput.GeometryType) { case EHairGeometryType::Strands: Parameters->GeometryType = 0; break; case EHairGeometryType::Cards : Parameters->GeometryType = 1; break; case EHairGeometryType::Meshes : Parameters->GeometryType = 2; break; } ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintUniformBuffer); TShaderMapRef ComputeShader(View.ShaderMap); ClearUnusedGraphResources(ComputeShader, Parameters); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::PrintLODInfo(%d/%d)", Parameters->GroupIndex, Parameters->GroupIndex), ComputeShader, Parameters, FIntVector(1, 1, 1)); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairDebugPrintCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairDebugPrintCS); SHADER_USE_PARAMETER_STRUCT(FHairDebugPrintCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, GroupSize) SHADER_PARAMETER(FIntPoint, MaxResolution) SHADER_PARAMETER(uint32, FastResolveMask) SHADER_PARAMETER(uint32, HairMacroGroupCount) SHADER_PARAMETER(uint32, HairVisibilityNodeGroupSize) SHADER_PARAMETER(uint32, AllocatedSampleCount) SHADER_PARAMETER(uint32, HairInstanceCount) SHADER_PARAMETER(float, ResolutionScale) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairInstanceDataBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, InstanceAABBBuffer) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairCountTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairCountUintTexture) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairVisibilityIndirectArgsBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairMacroGroupAABBBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, HairMacroGroupVoxelAlignedAABBBuffer) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, StencilTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LinearSampler) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneUniformParameters, Scene) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { // Skip optimization for avoiding long compilation time due to large UAV writes FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Debug); OutEnvironment.SetDefine(TEXT("SHADER_PRINT"), 1); OutEnvironment.SetDefine(TEXT("VF_SUPPORTS_PRIMITIVE_SCENE_DATA"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairDebugPrintCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainCS", SF_Compute); static void AddDebugHairPrintPass( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo* View, const EGroomViewMode ViewMode, const FHairStrandsVisibilityData& VisibilityData, const FHairStrandsMacroGroupDatas& MacroGroupDatas, const FHairStrandsMacroGroupResources& MacroGroupResources, FRDGTextureSRVRef InStencilTexture) { if (!Scene || !View || !View->HairStrandsViewData.UniformBuffer || !InStencilTexture) return; if (!ShaderPrint::IsSupported(View->GetShaderPlatform())) { return; } if (!TryEnableShaderDrawAndShaderPrint(*View, MacroGroupResources.MacroGroupCount * 32u, 8192u)) { return; } struct FData { uint32 PrimitiveID = ~0; uint32 RegisteredIndex = ~0; uint32 GeometryType = 0; uint32 Pad0 = 0; FVector4f InstanceScreenSphereBound = FVector4f::Zero(); }; // Build mapping Instance -> PrimitiveID to fetch primitive data (i.e., transform & co) const uint32 MacroGroupCount = MacroGroupDatas.Num(); TArray InstanceDatas; InstanceDatas.Reserve(MacroGroupCount * 4u); for (uint32 MacroGroupIndex = 0; MacroGroupIndex < MacroGroupCount; ++MacroGroupIndex) { const FHairStrandsMacroGroupData& MacroGroup = MacroGroupDatas[MacroGroupIndex]; for (const FHairStrandsMacroGroupData::PrimitiveInfo& PrimitiveInfo : MacroGroup.PrimitivesInfos) { if (PrimitiveInfo.PrimitiveSceneProxy) { FData& InstanceData = InstanceDatas.AddDefaulted_GetRef(); InstanceData.PrimitiveID = ~0u; if (const FPrimitiveSceneInfo* SceneInfo = PrimitiveInfo.PrimitiveSceneProxy->GetPrimitiveSceneInfo()) { InstanceData.PrimitiveID = SceneInfo->GetIndex(); } FHairStrandsInstance* Instance = PrimitiveInfo.PublicDataPtr->Instance; check(Instance); const float MaxRectSizeInPixels = FMath::Min(View->UnscaledViewRect.Height(), View->UnscaledViewRect.Width()); const float ContinousLODRadius = PrimitiveInfo.PublicDataPtr->ContinuousLODScreenSize * MaxRectSizeInPixels * 0.5f; // Diameter->Radius InstanceData.GeometryType = Instance->GetHairGeometry(); InstanceData.RegisteredIndex = Instance->RegisteredIndex; InstanceData.InstanceScreenSphereBound = FVector4f(PrimitiveInfo.PublicDataPtr->ContinuousLODScreenPos.X, PrimitiveInfo.PublicDataPtr->ContinuousLODScreenPos.Y, 0.f, ContinousLODRadius); } else { InstanceDatas.AddDefaulted(); } } } FRDGBufferRef InstanceDataBuffer = CreateVertexBuffer(GraphBuilder, TEXT("Hair.Debug.InstanceDatas"), FRDGBufferDesc::CreateBufferDesc(sizeof(FData), InstanceDatas.Num()), InstanceDatas.GetData(), sizeof(FData) * InstanceDatas.Num()); FRDGTextureRef ViewHairCountTexture = VisibilityData.ViewHairCountTexture ? VisibilityData.ViewHairCountTexture : GSystemTextures.GetBlackDummy(GraphBuilder); FRDGTextureRef ViewHairCountUintTexture = VisibilityData.ViewHairCountUintTexture ? VisibilityData.ViewHairCountUintTexture : GSystemTextures.GetBlackDummy(GraphBuilder); const FIntRect Viewport = View->ViewRect; const FIntPoint Resolution(Viewport.Width(), Viewport.Height()); FHairDebugPrintCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ResolutionScale = float(View->ViewRect.Width()) / float(View->UnscaledViewRect.Width()); Parameters->Scene = View->GetSceneUniforms().GetBuffer(GraphBuilder); Parameters->HairInstanceCount = InstanceDatas.Num(); Parameters->HairInstanceDataBuffer = GraphBuilder.CreateSRV(InstanceDataBuffer, PF_R32_UINT); Parameters->InstanceAABBBuffer = Scene->HairStrandsSceneData.TransientResources->GroupAABBSRV; Parameters->GroupSize = GetVendorOptimalGroupSize2D(); Parameters->ViewUniformBuffer = View->ViewUniformBuffer; Parameters->MaxResolution = VisibilityData.CoverageTexture ? VisibilityData.CoverageTexture->Desc.Extent : FIntPoint(0,0); Parameters->AllocatedSampleCount = VisibilityData.MaxNodeCount; Parameters->FastResolveMask = STENCIL_TEMPORAL_RESPONSIVE_AA_MASK; Parameters->HairCountTexture = ViewHairCountTexture; Parameters->HairCountUintTexture = ViewHairCountUintTexture; Parameters->HairVisibilityIndirectArgsBuffer = GraphBuilder.CreateSRV(VisibilityData.NodeIndirectArg, PF_R32_UINT); Parameters->HairVisibilityNodeGroupSize = VisibilityData.NodeGroupSize; Parameters->StencilTexture = InStencilTexture; Parameters->LinearSampler = TStaticSamplerState::GetRHI(); Parameters->HairMacroGroupCount = MacroGroupResources.MacroGroupCount; Parameters->HairStrands = View->HairStrandsViewData.UniformBuffer; Parameters->HairMacroGroupAABBBuffer = GraphBuilder.CreateSRV(MacroGroupResources.MacroGroupAABBsBuffer, PF_R32_SINT); Parameters->HairMacroGroupVoxelAlignedAABBBuffer = GraphBuilder.CreateSRV(MacroGroupResources.MacroGroupVoxelAlignedAABBsBuffer, PF_R32_SINT); ShaderPrint::SetParameters(GraphBuilder, View->ShaderPrintData, Parameters->ShaderPrintUniformBuffer); TShaderMapRef ComputeShader(View->ShaderMap); ClearUnusedGraphResources(ComputeShader, Parameters); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::DebugPrint"), ComputeShader, Parameters, FIntVector(1, 1, 1)); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairDebugShadowCullingCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairDebugShadowCullingCS); SHADER_USE_PARAMETER_STRUCT(FHairDebugShadowCullingCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(uint32, InstanceCount) SHADER_PARAMETER(FVector3f, LightCenter) SHADER_PARAMETER(FVector3f, LightExtent) SHADER_PARAMETER(FMatrix44f, LightToWorld) SHADER_PARAMETER(FMatrix44f, ViewWorldToProj) SHADER_PARAMETER(FMatrix44f, ViewProjToWorld) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, InstanceBoundInWorldSpace) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, InstanceBoundInLightSpace) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, InstanceIntersection) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { // Skip optimization for avoiding long compilation time due to large UAV writes FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Debug); OutEnvironment.SetDefine(TEXT("SHADER_SHADOW_CULLING"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairDebugShadowCullingCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainCS", SF_Compute); static void AddDebugHairShadowCullingPass( FRDGBuilder& GraphBuilder, const FScene* Scene, const FViewInfo* View) { if (!Scene || !View || !View->HairStrandsViewData.UniformBuffer) return; if (!ShaderPrint::IsSupported(View->GetShaderPlatform())) { return; } const uint32 LightCount = View->HairStrandsViewData.DebugData.CullData.DirectionalLights.Num(); uint32 InstanceCount = 0; for (auto It : View->HairStrandsViewData.DebugData.CullData.DirectionalLights) { InstanceCount += It.InstanceBoundInLightSpace.Num(); } if (LightCount == 0 || InstanceCount == 0) { return; } if (!TryEnableShaderDrawAndShaderPrint(*View, 2000u, 2000u)) { return; } auto CreateBoundBuffer = [&](const TArray& In) { TArray Bound; Bound.Reserve(In.Num() * 6); for (auto& B : In) { Bound.Add(B.Min.X); Bound.Add(B.Min.Y); Bound.Add(B.Min.Z); Bound.Add(B.Max.X); Bound.Add(B.Max.Y); Bound.Add(B.Max.Z); } return CreateVertexBuffer(GraphBuilder, TEXT("Hair.Debug.InstanceBounds"), FRDGBufferDesc::CreateBufferDesc(4, Bound.Num()), Bound.GetData(), 4u * Bound.Num()); }; uint32 LightIndex = 0; for (auto It : View->HairStrandsViewData.DebugData.CullData.DirectionalLights) { FRDGBufferRef InstanceBoundInLightSpaceBuffer = CreateBoundBuffer(It.InstanceBoundInLightSpace); FRDGBufferRef InstanceBoundInWorldSpaceBuffer = CreateBoundBuffer(It.InstanceBoundInWorldSpace); FRDGBufferRef InstanceIntersectionBuffer = CreateVertexBuffer(GraphBuilder, TEXT("Hair.Debug.InstanceIntersection"), FRDGBufferDesc::CreateBufferDesc(4, It.InstanceIntersection.Num()), It.InstanceIntersection.GetData(), 4u * It.InstanceIntersection.Num()); FHairDebugShadowCullingCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->InstanceCount = InstanceCount; Parameters->LightCenter = It.Center; Parameters->LightExtent = It.Extent; Parameters->LightToWorld = FMatrix44f(It.LightToWorld); Parameters->ViewWorldToProj = FMatrix44f(View->ViewMatrices.GetViewProjectionMatrix()); Parameters->ViewProjToWorld = FMatrix44f(View->ViewMatrices.GetInvViewProjectionMatrix()); Parameters->InstanceBoundInLightSpace = GraphBuilder.CreateSRV(InstanceBoundInLightSpaceBuffer, PF_R32_FLOAT); Parameters->InstanceBoundInWorldSpace = GraphBuilder.CreateSRV(InstanceBoundInWorldSpaceBuffer, PF_R32_FLOAT); Parameters->InstanceIntersection = GraphBuilder.CreateSRV(InstanceIntersectionBuffer, PF_R32_UINT); Parameters->ViewUniformBuffer = View->ViewUniformBuffer; ShaderPrint::SetParameters(GraphBuilder, View->ShaderPrintData, Parameters->ShaderPrintUniformBuffer); Parameters->HairStrands = View->HairStrandsViewData.UniformBuffer; TShaderMapRef ComputeShader(View->ShaderMap); ClearUnusedGraphResources(ComputeShader, Parameters); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("HairStrands::DebugShadowCulling(%d/%d)", LightIndex++, LightCount), ComputeShader, Parameters, FIntVector(1, 1, 1)); } } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairDebugPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairDebugPS); SHADER_USE_PARAMETER_STRUCT(FHairDebugPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FVector2f, OutputResolution) SHADER_PARAMETER(uint32, FastResolveMask) SHADER_PARAMETER(uint32, DebugMode) SHADER_PARAMETER(int32, SampleIndex) SHADER_PARAMETER(uint32, MaxSampleCount) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairCountTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, HairCountUintTexture) SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D, DepthStencilTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LinearSampler) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_DEBUG_MODE"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairDebugPS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainPS", SF_Pixel); static void AddDebugHairPass( FRDGBuilder& GraphBuilder, const FViewInfo* View, const EGroomViewMode InDebugMode, const FHairStrandsVisibilityData& VisibilityData, FRDGTextureSRVRef InDepthStencilTexture, FRDGTextureRef& OutTarget) { check(OutTarget); check(InDebugMode == EGroomViewMode::TAAResolveType || InDebugMode == EGroomViewMode::SamplePerPixel || InDebugMode == EGroomViewMode::CoverageType || InDebugMode == EGroomViewMode::Coverage || InDebugMode == EGroomViewMode::MaterialDepth || InDebugMode == EGroomViewMode::MaterialBaseColor || InDebugMode == EGroomViewMode::MaterialRoughness || InDebugMode == EGroomViewMode::MaterialSpecular || InDebugMode == EGroomViewMode::MaterialTangent); if (!VisibilityData.CoverageTexture || !VisibilityData.NodeIndex || !VisibilityData.NodeData) return; if (InDebugMode == EGroomViewMode::TAAResolveType && !InDepthStencilTexture) return; const FIntRect Viewport = View->ViewRect; const FIntPoint Resolution(Viewport.Width(), Viewport.Height()); uint32 InternalDebugMode = 0; switch (InDebugMode) { case EGroomViewMode::SamplePerPixel: InternalDebugMode = 0; break; case EGroomViewMode::CoverageType: InternalDebugMode = 1; break; case EGroomViewMode::TAAResolveType: InternalDebugMode = 2; break; case EGroomViewMode::Coverage: InternalDebugMode = 3; break; case EGroomViewMode::MaterialDepth: InternalDebugMode = 4; break; case EGroomViewMode::MaterialBaseColor: InternalDebugMode = 5; break; case EGroomViewMode::MaterialRoughness: InternalDebugMode = 6; break; case EGroomViewMode::MaterialSpecular: InternalDebugMode = 7; break; case EGroomViewMode::MaterialTangent: InternalDebugMode = 8; break; }; FHairDebugPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View->ViewUniformBuffer; Parameters->OutputResolution = Resolution; Parameters->FastResolveMask = STENCIL_TEMPORAL_RESPONSIVE_AA_MASK; Parameters->HairStrands = View->HairStrandsViewData.UniformBuffer; Parameters->DepthStencilTexture = InDepthStencilTexture; Parameters->LinearSampler = TStaticSamplerState::GetRHI(); Parameters->DebugMode = InternalDebugMode; Parameters->SampleIndex = 0; Parameters->RenderTargets[0] = FRenderTargetBinding(OutTarget, ERenderTargetLoadAction::ELoad, 0); TShaderMapRef VertexShader(View->ShaderMap); TShaderMapRef PixelShader(View->ShaderMap); ClearUnusedGraphResources(PixelShader, Parameters); GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::DebugMode(%s)", GetGroomViewModeName(InDebugMode)), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution](FRDGAsyncTask, FRHICommandList& RHICmdList) { 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 = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FDeepShadowVisualizePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDeepShadowVisualizePS); SHADER_USE_PARAMETER_STRUCT(FDeepShadowVisualizePS, FGlobalShader); class FOutputType : SHADER_PERMUTATION_INT("PERMUTATION_OUTPUT_TYPE", 2); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(float, DomScale) SHADER_PARAMETER(FVector2f, OutputResolution) SHADER_PARAMETER(FVector2f, InvOutputResolution) SHADER_PARAMETER(FIntVector4, HairViewRect) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadowDepthTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DeepShadowLayerTexture) SHADER_PARAMETER_SAMPLER(SamplerState, LinearSampler) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_VISUALIZEDOM"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FDeepShadowVisualizePS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "VisualizeDomPS", SF_Pixel); static void AddDebugDeepShadowTexturePass( FRDGBuilder& GraphBuilder, const FViewInfo* View, const FIntRect& HairViewRect, const FHairStrandsDeepShadowData* ShadowData, const FHairStrandsDeepShadowResources* Resources, FRDGTextureRef& OutTarget) { check(OutTarget); const FIntRect Viewport = View->ViewRect; const FIntPoint Resolution(Viewport.Width(), Viewport.Height()); FDeepShadowVisualizePS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->DomScale = GDeepShadowDebugScale; Parameters->OutputResolution = Resolution; Parameters->InvOutputResolution = FVector2f(1.f / Resolution.X, 1.f / Resolution.Y); Parameters->DeepShadowDepthTexture = Resources ? Resources->DepthAtlasTexture : nullptr; Parameters->DeepShadowLayerTexture = Resources ? Resources->LayersAtlasTexture : nullptr; Parameters->LinearSampler = TStaticSamplerState::GetRHI(); Parameters->HairViewRect = FIntVector4(HairViewRect.Min.X, HairViewRect.Min.Y, HairViewRect.Width(), HairViewRect.Height()); Parameters->RenderTargets[0] = FRenderTargetBinding(OutTarget, ERenderTargetLoadAction::ELoad, 0); TShaderMapRef VertexShader(View->ShaderMap); FDeepShadowVisualizePS::FPermutationDomain PermutationVector; PermutationVector.Set(ShadowData ? 0 : 1); TShaderMapRef PixelShader(View->ShaderMap, PermutationVector); ClearUnusedGraphResources(PixelShader, Parameters); GraphBuilder.AddPass( ShadowData ? RDG_EVENT_NAME("DebugDeepShadowTexture") : RDG_EVENT_NAME("DebugHairViewRect"), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution](FRDGAsyncTask, FRHICommandList& RHICmdList) { 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 = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FDeepShadowInfoCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDeepShadowInfoCS); SHADER_USE_PARAMETER_STRUCT(FDeepShadowInfoCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER(FVector2f, OutputResolution) SHADER_PARAMETER(uint32, AllocatedSlotCount) SHADER_PARAMETER(uint32, MacroGroupCount) SHADER_PARAMETER(uint32, bViewRectOptimizeEnabled) SHADER_PARAMETER(uint32, bVoxelizationEnabled) SHADER_PARAMETER(FIntPoint, AtlasResolution) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, MacroGroupAABBBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, ShadowViewInfoBuffer) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); ShaderPrint::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Debug); OutEnvironment.SetDefine(TEXT("SHADER_DOMINFO"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FDeepShadowInfoCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainCS", SF_Compute); static void AddDeepShadowInfoPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsDeepShadowResources& DeepShadowResources, const FHairStrandsMacroGroupResources& MacroGroupResources, FRDGTextureRef& OutputTexture) { if (!ShaderPrint::IsSupported(View.GetShaderPlatform())) { return; } if (DeepShadowResources.TotalAtlasSlotCount == 0) { return; } if (!TryEnableShaderDrawAndShaderPrint(View, DeepShadowResources.TotalAtlasSlotCount * 64, 2000)) { return; } FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); const FIntPoint Resolution(OutputTexture->Desc.Extent); FDeepShadowInfoCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->OutputResolution = Resolution; Parameters->AllocatedSlotCount = DeepShadowResources.TotalAtlasSlotCount; Parameters->MacroGroupCount = MacroGroupResources.MacroGroupCount; Parameters->SceneTextures = SceneTextures; Parameters->bViewRectOptimizeEnabled = IsHairStrandsViewRectOptimEnable() ? 1u : 0u; Parameters->bVoxelizationEnabled = View.HairStrandsViewData.VirtualVoxelResources.IsValid() ? 1u : 0u; Parameters->AtlasResolution = View.HairStrandsViewData.DeepShadowResources.DepthAtlasTexture->Desc.Extent; Parameters->MacroGroupAABBBuffer = GraphBuilder.CreateSRV(MacroGroupResources.MacroGroupAABBsBuffer, PF_R32_SINT); Parameters->ShadowViewInfoBuffer = GraphBuilder.CreateSRV(DeepShadowResources.DeepShadowViewInfoBuffer); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); Parameters->OutputTexture = GraphBuilder.CreateUAV(OutputTexture); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("HairStrands::DeepShadowDebugInfo"), ComputeShader, Parameters, FIntVector(1, 1, 1)); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FVoxelVirtualRaymarchingCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FVoxelVirtualRaymarchingCS); SHADER_USE_PARAMETER_STRUCT(FVoxelVirtualRaymarchingCS, FGlobalShader); class FTraversalType : SHADER_PERMUTATION_INT("PERMUTATION_TRAVERSAL", 2); using FPermutationDomain = TShaderPermutationDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) SHADER_PARAMETER(FVector2f, OutputResolution) SHADER_PARAMETER(uint32, MacroGroupId) SHADER_PARAMETER(uint32, MacroGroupCount) SHADER_PARAMETER(uint32, MaxTotalPageIndexCount) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthBeforeCompositionTexture) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualVoxelParameters, VirtualVoxel) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, TotalValidPageCounter) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutputTexture) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { // Skip optimization for avoiding long compilation time due to large UAV writes FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.CompilerFlags.Add(CFLAG_Debug); OutEnvironment.SetDefine(TEXT("SHADER_VOXEL_DEBUG"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FVoxelVirtualRaymarchingCS, "/Engine/Private/HairStrands/HairStrandsVoxelDebug.usf", "MainCS", SF_Compute); static void AddVoxelPageRaymarchingPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsMacroGroupDatas& MacroGroupDatas, const FHairStrandsVoxelResources& VoxelResources, FRDGTextureRef& OutputTexture) { if (!ShaderPrint::IsSupported(View.GetShaderPlatform())) { return; } if (!TryEnableShaderDrawAndShaderPrint(View, 4000, 2000)) { return; } FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); const FIntPoint Resolution(OutputTexture->Desc.Extent); for (const FHairStrandsMacroGroupData& MacroGroupData : MacroGroupDatas) { FVoxelVirtualRaymarchingCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->OutputResolution = Resolution; Parameters->SceneTextures = SceneTextures; Parameters->MacroGroupId = MacroGroupData.MacroGroupId; Parameters->MacroGroupCount = MacroGroupDatas.Num(); Parameters->MaxTotalPageIndexCount = VoxelResources.Parameters.Common.PageIndexCount; Parameters->VirtualVoxel = VoxelResources.UniformBuffer; Parameters->TotalValidPageCounter = GraphBuilder.CreateSRV(VoxelResources.PageIndexGlobalCounter, PF_R32_UINT); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrintParameters); Parameters->OutputTexture = GraphBuilder.CreateUAV(OutputTexture); Parameters->SceneDepthBeforeCompositionTexture = View.HairStrandsViewData.DebugData.Common.SceneDepthTextureBeforeCompsition; if (Parameters->SceneDepthBeforeCompositionTexture == nullptr) { Parameters->SceneDepthBeforeCompositionTexture = SceneTextures.SceneDepthTexture; } FVoxelVirtualRaymarchingCS::FPermutationDomain PermutationVector; PermutationVector.Set(GHairVirtualVoxel_DebugTraversalType > 0 ? 1 : 0); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); const FIntVector DispatchCount = DispatchCount.DivideAndRoundUp(FIntVector(OutputTexture->Desc.Extent.X, OutputTexture->Desc.Extent.Y, 1), FIntVector(8, 8, 1)); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("HairStrands::VoxelVirtualRaymarching"), ComputeShader, Parameters, DispatchCount); } } /////////////////////////////////////////////////////////////////////////////////////////////////// class FDebugHairTangentCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FDebugHairTangentCS); SHADER_USE_PARAMETER_STRUCT(FDebugHairTangentCS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrint) SHADER_PARAMETER(FVector2f, OutputResolution) SHADER_PARAMETER(FIntPoint, TileCount) SHADER_PARAMETER(uint32, TileSize) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER_SAMPLER(SamplerState, BilinearTextureSampler) SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands) END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Strands, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_TANGENT"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FDebugHairTangentCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainCS", SF_Compute); static void AddDebugHairTangentPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSceneTextures& SceneTextures, FRDGTextureRef& OutputTexture) { if (!ShaderPrint::IsSupported(View.GetShaderPlatform())) { return; } if (!TryEnableShaderDrawAndShaderPrint(View, 64000u, 1000u)) { return; } FDebugHairTangentCS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->HairStrands = View.HairStrandsViewData.UniformBuffer; Parameters->OutputResolution = OutputTexture->Desc.Extent; Parameters->TileSize = 8; Parameters->TileCount = FIntPoint(FMath::FloorToInt(Parameters->OutputResolution.X / Parameters->TileSize), FMath::FloorToInt(Parameters->OutputResolution.X / Parameters->TileSize)); Parameters->SceneTextures = SceneTextures.UniformBuffer; Parameters->BilinearTextureSampler = TStaticSamplerState::GetRHI(); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, Parameters->ShaderPrint); const FIntVector DispatchCount = DispatchCount.DivideAndRoundUp(FIntVector(OutputTexture->Desc.Extent.X, OutputTexture->Desc.Extent.Y, 1), FIntVector(8, 8, 1)); ShaderPrint::RequestSpaceForLines(DispatchCount.X * DispatchCount.Y); TShaderMapRef ComputeShader(View.ShaderMap); FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("HairStrands::DebugTangentCS"), ComputeShader, Parameters, DispatchCount); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairStrandsPlotBSDFPS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsPlotBSDFPS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsPlotBSDFPS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER(FIntPoint, InputCoord) SHADER_PARAMETER(FIntPoint, OutputOffset) SHADER_PARAMETER(FIntPoint, OutputResolution) SHADER_PARAMETER(FIntPoint, MaxResolution) SHADER_PARAMETER(uint32, HairComponents) SHADER_PARAMETER(float, Roughness) SHADER_PARAMETER(float, BaseColor) SHADER_PARAMETER(float, Exposure) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Tool, Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_PLOTBSDF"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsPlotBSDFPS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainPS", SF_Pixel); static void AddPlotBSDFPass( FRDGBuilder& GraphBuilder, const FViewInfo& View, FRDGTextureRef& OutputTexture) { FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); const FIntPoint Resolution(OutputTexture->Desc.Extent); FHairStrandsPlotBSDFPS::FParameters* Parameters = GraphBuilder.AllocParameters(); Parameters->InputCoord = View.CursorPos; Parameters->OutputOffset = FIntPoint(10,100); Parameters->OutputResolution = FIntPoint(256, 256); Parameters->MaxResolution = OutputTexture->Desc.Extent; Parameters->HairComponents = ToBitfield(GetHairComponents()); Parameters->Roughness = GHairStrandsDebugPlotBsdfRoughness; Parameters->BaseColor = GHairStrandsDebugPlotBsdfBaseColor; Parameters->Exposure = GHairStrandsDebugPlotBsdfExposure; Parameters->RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::ELoad); const FIntPoint OutputResolution = SceneTextures.SceneDepthTexture->Desc.Extent; TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); const FGlobalShaderMap* GlobalShaderMap = View.ShaderMap; const FIntRect Viewport = View.ViewRect; ClearUnusedGraphResources(PixelShader, Parameters); GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::BsdfPlot"), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution](FRDGAsyncTask, FRHICommandList& RHICmdList) { 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 = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairStrandsPlotSamplePS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairStrandsPlotSamplePS); SHADER_USE_PARAMETER_STRUCT(FHairStrandsPlotSamplePS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FHairStrandsDebugData::FReadParameters, DebugData) SHADER_PARAMETER(FIntPoint, OutputOffset) SHADER_PARAMETER(FIntPoint, OutputResolution) SHADER_PARAMETER(FIntPoint, MaxResolution) SHADER_PARAMETER(uint32, HairComponents) SHADER_PARAMETER(float, Exposure) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() public: static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Tool, Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_PLOTSAMPLE"), 1); } }; IMPLEMENT_GLOBAL_SHADER(FHairStrandsPlotSamplePS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "MainPS", SF_Pixel); static void AddPlotSamplePass( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FHairStrandsDebugData::FPlotData& DebugData, FRDGTextureRef& OutputTexture) { FSceneTextureParameters SceneTextures = GetSceneTextureParameters(GraphBuilder, View); const FIntPoint Resolution(OutputTexture->Desc.Extent); FHairStrandsPlotSamplePS::FParameters* Parameters = GraphBuilder.AllocParameters(); FHairStrandsDebugData::SetParameters(GraphBuilder, DebugData, Parameters->DebugData); Parameters->ViewUniformBuffer = View.ViewUniformBuffer; Parameters->OutputOffset = FIntPoint(100, 100); Parameters->OutputResolution = FIntPoint(256, 256); Parameters->MaxResolution = OutputTexture->Desc.Extent; Parameters->HairComponents = ToBitfield(GetHairComponents()); Parameters->Exposure = GHairStrandsDebugPlotBsdfExposure; Parameters->RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::ELoad); const FIntPoint OutputResolution = SceneTextures.SceneDepthTexture->Desc.Extent; TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); const FGlobalShaderMap* GlobalShaderMap = View.ShaderMap; const FIntRect Viewport = View.ViewRect; ClearUnusedGraphResources(PixelShader, Parameters); GraphBuilder.AddPass( RDG_EVENT_NAME("HairStrands::SamplePlot"), Parameters, ERDGPassFlags::Raster, [Parameters, VertexShader, PixelShader, Viewport, Resolution](FRDGAsyncTask, FRHICommandList& RHICmdList) { 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 = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); RHICmdList.SetViewport(Viewport.Min.X, Viewport.Min.Y, 0.0f, Viewport.Max.X, Viewport.Max.Y, 1.0f); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *Parameters); DrawRectangle( RHICmdList, 0, 0, Viewport.Width(), Viewport.Height(), Viewport.Min.X, Viewport.Min.Y, Viewport.Width(), Viewport.Height(), Viewport.Size(), Resolution, VertexShader, EDRF_UseTriangleOptimization); }); } /////////////////////////////////////////////////////////////////////////////////////////////////// class FHairVisibilityDebugPPLLCS : public FGlobalShader { DECLARE_GLOBAL_SHADER(FHairVisibilityDebugPPLLCS); SHADER_USE_PARAMETER_STRUCT(FHairVisibilityDebugPPLLCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain<>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer) SHADER_PARAMETER(float, PPLLMeanListElementCountPerPixel) SHADER_PARAMETER(float, PPLLMaxTotalListElementCount) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PPLLCounter) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, PPLLNodeIndex) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer, PPLLNodeData) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, SceneColorTextureUAV) SHADER_PARAMETER_STRUCT_INCLUDE(ShaderPrint::FShaderParameters, ShaderPrintParameters) END_SHADER_PARAMETER_STRUCT() static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector) { return PermutationVector; } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsHairStrandsSupported(EHairStrandsShaderType::Tool, Parameters.Platform) && ShaderPrint::IsSupported(Parameters.Platform); } static EShaderPermutationPrecacheRequest ShouldPrecachePermutation(const FGlobalShaderPermutationParameters& Parameters) { return EShaderPermutationPrecacheRequest::NotPrecached; } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADER_PPLL_DEBUG"), 1); // Skip optimization for avoiding long compilation time due to large UAV writes OutEnvironment.CompilerFlags.Add(CFLAG_Debug); } }; IMPLEMENT_GLOBAL_SHADER(FHairVisibilityDebugPPLLCS, "/Engine/Private/HairStrands/HairStrandsDebug.usf", "VisibilityDebugPPLLCS", SF_Compute); /////////////////////////////////////////////////////////////////////////////////////////////////// uint32 GetHairStrandsMeanSamplePerPixel(EShaderPlatform In); static void InternalRenderHairStrandsDebugInfo( FRDGBuilder& GraphBuilder, FScene* Scene, FViewInfo& View, FHairStrandsBookmarkParameters& Params) { if (!ShaderPrint::IsSupported(View.GetShaderPlatform())) { return; } if (!Params.HasInstances()) { return; } const float YStep = 14; const float ColumnWidth = 200; RDG_EVENT_SCOPE(GraphBuilder, "HairStrandsDebug"); // Only render debug information for the main view const FSceneTextures& SceneTextures = View.GetSceneTextures(); // Bookmark for calling debug rendering from the plugin { const FSceneView* View0 = Params.View; FShaderPrintData* View0ShaderPrintData = Params.ShaderPrintData; Params.View = &View; Params.ShaderPrintData = &View.ShaderPrintData; RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessDebug, Params); Params.View = View0; Params.ShaderPrintData = View0ShaderPrintData; } const EGroomViewMode ViewMode = GetGroomViewMode(View); // Display tangent vector for strands/cards/meshes if (ViewMode == EGroomViewMode::Tangent) { AddDebugHairTangentPass(GraphBuilder, View, SceneTextures, Params.SceneColorTexture); } // Draw LOD info for (const FMeshBatchAndRelevance& Mesh : View.HairStrandsMeshElements) { const FHairGroupPublicData* GroupData = HairStrands::GetHairData(Mesh.Mesh); if (GroupData->bDebugDrawLODInfo) { AddPrintLODInfoPass(GraphBuilder, View, GroupData); } } for (const FMeshBatchAndRelevance& Mesh : View.HairCardsMeshElements) { const FHairGroupPublicData* GroupData = HairStrands::GetHairData(Mesh.Mesh); if (GroupData->bDebugDrawLODInfo) { AddPrintLODInfoPass(GraphBuilder, View, GroupData); } } // Pass this point, all debug rendering concern only hair strands data if (!HairStrands::HasViewHairStrandsData(View)) { return; } const FScreenPassRenderTarget SceneColor(Params.SceneColorTexture, View.ViewRect, ERenderTargetLoadAction::ELoad); const FHairStrandsViewData& HairData = View.HairStrandsViewData; if (GHairStrandsDebugPlotBsdf > 0 || HairData.DebugData.IsPlotDataValid()) { if (GHairStrandsDebugPlotBsdf > 0) { AddPlotBSDFPass(GraphBuilder, View, Params.SceneColorTexture); } if (HairData.DebugData.IsPlotDataValid()) { AddPlotSamplePass(GraphBuilder, View, HairData.DebugData.PlotData, Params.SceneColorTexture); } } float ClusterY = 38; if (View.HairStrandsViewData.DebugData.CullData.bIsValid) { AddDebugHairShadowCullingPass(GraphBuilder, Scene, &View); } if (ViewMode == EGroomViewMode::MacroGroups) { AddDebugHairPrintPass(GraphBuilder, Scene, &View, ViewMode, HairData.VisibilityData, HairData.MacroGroupDatas, HairData.MacroGroupResources, SceneTextures.Stencil); } if (ViewMode == EGroomViewMode::DeepOpacityMaps) { for (const FHairStrandsMacroGroupData& MacroGroup : HairData.MacroGroupDatas) { if (!HairData.DeepShadowResources.DepthAtlasTexture || !HairData.DeepShadowResources.LayersAtlasTexture) { continue; } for (const FHairStrandsDeepShadowData& DeepShadowData : MacroGroup.DeepShadowDatas) { const uint32 DomIndex = GDeepShadowDebugIndex; if (DeepShadowData.AtlasSlotIndex != DomIndex) continue; AddDebugDeepShadowTexturePass(GraphBuilder, &View, FIntRect(), &DeepShadowData, &HairData.DeepShadowResources, Params.SceneColorTexture); } } } // View Rect if (IsHairStrandsViewRectOptimEnable() && ViewMode == EGroomViewMode::MacroGroupScreenRect) { for (const FHairStrandsMacroGroupData& MacroGroupData : HairData.MacroGroupDatas) { AddDebugDeepShadowTexturePass(GraphBuilder, &View, MacroGroupData.ScreenRect, nullptr, nullptr, Params.SceneColorTexture); } const FIntRect TotalRect = ComputeVisibleHairStrandsMacroGroupsRect(View, View.ViewRect, HairData.MacroGroupDatas); AddDebugDeepShadowTexturePass(GraphBuilder, &View, TotalRect, nullptr, nullptr, Params.SceneColorTexture); } // Render Frustum for all lights & macro groups { if ((ViewMode == EGroomViewMode::LightBounds || ViewMode == EGroomViewMode::DeepOpacityMaps)) { AddDeepShadowInfoPass(GraphBuilder, View, HairData.DeepShadowResources, HairData.MacroGroupResources, Params.SceneColorTexture); } } const bool bRunDebugPass = ViewMode == EGroomViewMode::TAAResolveType || ViewMode == EGroomViewMode::SamplePerPixel || ViewMode == EGroomViewMode::CoverageType || ViewMode == EGroomViewMode::Coverage || ViewMode == EGroomViewMode::MaterialDepth || ViewMode == EGroomViewMode::MaterialBaseColor || ViewMode == EGroomViewMode::MaterialRoughness || ViewMode == EGroomViewMode::MaterialSpecular || ViewMode == EGroomViewMode::MaterialTangent; if (bRunDebugPass) { AddDebugHairPass(GraphBuilder, &View, ViewMode, HairData.VisibilityData, SceneTextures.Stencil, Params.SceneColorTexture); AddDebugHairPrintPass(GraphBuilder, Scene, &View, ViewMode, HairData.VisibilityData, HairData.MacroGroupDatas, HairData.MacroGroupResources, SceneTextures.Stencil); } else if (ViewMode == EGroomViewMode::Tile) { check(HairData.VisibilityData.TileData.IsValid()); AddHairStrandsDebugTilePass(GraphBuilder, View, Params.SceneColorTexture, HairData.VisibilityData.TileData); } const bool bIsVoxelMode = ViewMode == EGroomViewMode::VoxelsDensity; if (bIsVoxelMode) { if (HairData.VirtualVoxelResources.IsValid()) { AddVoxelPageRaymarchingPass(GraphBuilder, View, HairData.MacroGroupDatas, HairData.VirtualVoxelResources, Params.SceneColorTexture); } } if (HairData.DebugData.IsPPLLDataValid()) // Check if PPLL rendering is used and its debug view is enabled. { // Force ShaderPrint on. ShaderPrint::SetEnabled(true); ShaderPrint::RequestSpaceForCharacters(256); const FIntPoint PPLLResolution = HairData.DebugData.PPLLData.NodeIndexTexture->Desc.Extent; FHairVisibilityDebugPPLLCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->PPLLMeanListElementCountPerPixel = GetHairStrandsMeanSamplePerPixel(View.GetShaderPlatform()); PassParameters->PPLLMaxTotalListElementCount = HairData.DebugData.PPLLData.NodeDataBuffer->Desc.NumElements; PassParameters->PPLLCounter = HairData.DebugData.PPLLData.NodeCounterTexture; PassParameters->PPLLNodeIndex = HairData.DebugData.PPLLData.NodeIndexTexture; PassParameters->PPLLNodeData = GraphBuilder.CreateSRV(FRDGBufferSRVDesc(HairData.DebugData.PPLLData.NodeDataBuffer)); PassParameters->ViewUniformBuffer = View.ViewUniformBuffer; PassParameters->SceneColorTextureUAV = GraphBuilder.CreateUAV(Params.SceneColorTexture); ShaderPrint::SetParameters(GraphBuilder, View.ShaderPrintData, PassParameters->ShaderPrintParameters); FHairVisibilityDebugPPLLCS::FPermutationDomain PermutationVector; TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FIntVector TextureSize = Params.SceneColorTexture->Desc.GetSize(); TextureSize.Z = 1; FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("HairStrands::PPLLDebug"), ComputeShader, PassParameters, FIntVector::DivideAndRoundUp(TextureSize, FIntVector(8, 8, 1))); } if (ViewMode != EGroomViewMode::None) { AddDrawCanvasPass(GraphBuilder, {}, View, SceneColor, [&View, YStep, ViewMode](FCanvas& Canvas) { float X = 40; float Y = View.ViewRect.Height() - YStep * 3.f; FString Line; if (ViewMode != EGroomViewMode::None) { Line = FString::Printf(TEXT("Hair Debug mode - %s"), GetGroomViewModeName(ViewMode)); } Canvas.DrawShadowedString(X, Y += YStep, *Line, GetStatsFont(), FLinearColor(1, 1, 0)); }); } } void RenderHairStrandsDebugInfo( FRDGBuilder& GraphBuilder, FScene* Scene, TArrayView Views, FHairStrandsBookmarkParameters& Parameters) { bool bHasHairData = false; for (FViewInfo& View : Views) { InternalRenderHairStrandsDebugInfo(GraphBuilder, Scene, View, Parameters); } }