Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironmentRealTimeCapture.cpp
mihnea balta 21d8858521 Fix crash when using time-sliced sky captures.
The code was never allocating the second sky render target because it was using the wrong index in the null check (ConvolvedSkyRenderTargetReadyIndex is 0 at that point).

#jira UE-149630
#rnx
#lockdown Michal.Valient
#preflight https://horde.devtools.epicgames.com/job/628f8239e17a02240dd06378
#rb Jason.Hoerner

#ROBOMERGE-AUTHOR: mihnea.balta
#ROBOMERGE-SOURCE: CL 20379597 in //UE5/Release-5.0/... via CL 20381835
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246)

[CL 20385099 by mihnea balta in ue5-main branch]
2022-05-26 16:44:59 -04:00

1072 lines
50 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Functionality for capturing and pre-filtering a sky env map in real time.
=============================================================================*/
#include "ReflectionEnvironmentCapture.h"
#include "ClearQuad.h"
#include "MeshPassProcessor.h"
#include "PrimitiveSceneProxy.h"
#include "MeshPassProcessor.inl"
#include "ScenePrivate.h"
#include "SkyPassRendering.h"
#include "RenderGraphUtils.h"
#include "VolumetricCloudRendering.h"
#include "VolumetricCloudProxy.h"
#include "FogRendering.h"
#include "GPUScene.h"
#include "ScreenPass.h"
#if WITH_EDITOR
#include "CanvasTypes.h"
#include "RenderTargetTemp.h"
#endif
extern float GReflectionCaptureNearPlane;
DECLARE_GPU_STAT(CaptureConvolveSkyEnvMap);
static TAutoConsoleVariable<int32> CVarRealTimeReflectionCaptureTimeSlicing(
TEXT("r.SkyLight.RealTimeReflectionCapture.TimeSlice"), 1,
TEXT("When enabled, the real-time sky light capture and convolutions will by distributed over several frames to lower the per-frame cost. Value in [1,6]."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRealTimeReflectionCaptureTimeSlicingSkyCloudCubeFacePerFrame(
TEXT("r.SkyLight.RealTimeReflectionCapture.TimeSlice.SkyCloudCubeFacePerFrame"), 6,
TEXT("When enabled, the real-time sky light capture and convolutions will by distributed over several frames to lower the per-frame cost."),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRealTimeReflectionCaptureShadowFromOpaque(
TEXT("r.SkyLight.RealTimeReflectionCapture.ShadowFromOpaque"), 0,
TEXT("Opaque meshes cast shadow from directional lights onto sky and clouds when enabled.\n"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<int32> CVarRealTimeReflectionCaptureDepthBuffer(
TEXT("r.SkyLight.RealTimeReflectionCapture.DepthBuffer"), 1,
TEXT("When enabled, the real-time sky light capture will have a depth buffer, this is for multiple meshes to be cover each other correctly. The height fog will also be applied according to the depth buffer."),
ECVF_RenderThreadSafe);
class FDownsampleCubeFaceCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FDownsampleCubeFaceCS);
SHADER_USE_PARAMETER_STRUCT(FDownsampleCubeFaceCS, FGlobalShader);
static const uint32 ThreadGroupSize = 8;
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, MipIndex)
SHADER_PARAMETER(uint32, NumMips)
SHADER_PARAMETER(int32, CubeFace)
SHADER_PARAMETER(int32, FaceThreadGroupSize)
SHADER_PARAMETER(FIntPoint, ValidDispatchCoord)
SHADER_PARAMETER_RDG_TEXTURE_SRV(TextureCube, SourceCubemapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, SourceCubemapSampler)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTextureMipColor)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return GetMaxSupportedFeatureLevel(Parameters.Platform) >= ERHIFeatureLevel::SM5; }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), ThreadGroupSize);
OutEnvironment.SetDefine(TEXT("USE_COMPUTE"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FDownsampleCubeFaceCS, "/Engine/Private/ReflectionEnvironmentShaders.usf", "DownsampleCS", SF_Compute);
class FConvolveSpecularFaceCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FConvolveSpecularFaceCS);
SHADER_USE_PARAMETER_STRUCT(FConvolveSpecularFaceCS, FGlobalShader);
static const uint32 ThreadGroupSize = 8;
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(uint32, MipIndex)
SHADER_PARAMETER(uint32, NumMips)
SHADER_PARAMETER(int32, CubeFaceOffset)
SHADER_PARAMETER(int32, CubeFace)
SHADER_PARAMETER(int32, FaceThreadGroupSize)
SHADER_PARAMETER(FIntPoint, ValidDispatchCoord)
SHADER_PARAMETER_RDG_TEXTURE_SRV(TextureCube, SourceCubemapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, SourceCubemapSampler)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTextureMipColor)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return GetMaxSupportedFeatureLevel(Parameters.Platform) >= ERHIFeatureLevel::SM5; }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), ThreadGroupSize);
OutEnvironment.SetDefine(TEXT("USE_COMPUTE"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FConvolveSpecularFaceCS, "/Engine/Private/ReflectionEnvironmentShaders.usf", "FilterCS", SF_Compute);
class FComputeSkyEnvMapDiffuseIrradianceCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FComputeSkyEnvMapDiffuseIrradianceCS);
SHADER_USE_PARAMETER_STRUCT(FComputeSkyEnvMapDiffuseIrradianceCS, FGlobalShader);
// 8*8=64 threads in a group.
// Each thread uses 4*7*RGB sh float => 84 bytes shared group memory.
// 64 * 84 = 5376 bytes which fits dx11 16KB shared memory limitation. 6144 with vector alignement in shared memory and it still fits
// Low occupancy on a single CU.
static const uint32 ThreadGroupSizeX = 8;
static const uint32 ThreadGroupSizeY = 8;
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE_SRV(TextureCube, SourceCubemapTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, SourceCubemapSampler)
SHADER_PARAMETER_RDG_BUFFER_UAV(RWStructuredBuffer, OutIrradianceEnvMapSH)
SHADER_PARAMETER(float, UniformSampleSolidAngle)
SHADER_PARAMETER(uint32, MipIndex)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return GetMaxSupportedFeatureLevel(Parameters.Platform) >= ERHIFeatureLevel::SM5; }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_X"), ThreadGroupSizeX);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE_Y"), ThreadGroupSizeY);
OutEnvironment.SetDefine(TEXT("SHADER_DIFFUSE_TO_SH"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FComputeSkyEnvMapDiffuseIrradianceCS, "/Engine/Private/ReflectionEnvironmentShaders.usf", "ComputeSkyEnvMapDiffuseIrradianceCS", SF_Compute);
class FApplyLowerHemisphereColor : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FApplyLowerHemisphereColor);
SHADER_USE_PARAMETER_STRUCT(FApplyLowerHemisphereColor, FGlobalShader);
static const uint32 ThreadGroupSize = 8;
using FPermutationDomain = TShaderPermutationDomain<>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FLinearColor, LowerHemisphereSolidColor)
SHADER_PARAMETER(FIntPoint, ValidDispatchCoord)
SHADER_PARAMETER(int32, FaceThreadGroupSize)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, OutTextureMipColor)
END_SHADER_PARAMETER_STRUCT()
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return GetMaxSupportedFeatureLevel(Parameters.Platform) >= ERHIFeatureLevel::SM5; }
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZE"), ThreadGroupSize);
OutEnvironment.SetDefine(TEXT("USE_COMPUTE"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FApplyLowerHemisphereColor, "/Engine/Private/ReflectionEnvironmentShaders.usf", "ApplyLowerHemisphereColorCS", SF_Compute);
class FRenderRealTimeReflectionHeightFogVS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderRealTimeReflectionHeightFogVS);
SHADER_USE_PARAMETER_STRUCT(FRenderRealTimeReflectionHeightFogVS, FGlobalShader);;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
END_SHADER_PARAMETER_STRUCT()
using FPermutationDomain = TShaderPermutationDomain<>;
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return true;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("REALTIME_REFLECTION_HEIGHT_FOG"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FRenderRealTimeReflectionHeightFogVS, "/Engine/Private/ReflectionEnvironmentShaders.usf", "RenderRealTimeReflectionHeightFogVS", SF_Vertex);
class FRenderRealTimeReflectionHeightFogPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FRenderRealTimeReflectionHeightFogPS);
SHADER_USE_PARAMETER_STRUCT(FRenderRealTimeReflectionHeightFogPS, FGlobalShader);
class FDepthTexture : SHADER_PERMUTATION_BOOL("PERMUTATION_DEPTHTEXTURE");
using FPermutationDomain = TShaderPermutationDomain<FDepthTexture>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FFogUniformParameters, FogStruct)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, DepthTexture)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return true;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("REALTIME_REFLECTION_HEIGHT_FOG"), 1);
}
};
IMPLEMENT_GLOBAL_SHADER(FRenderRealTimeReflectionHeightFogPS, "/Engine/Private/ReflectionEnvironmentShaders.usf", "RenderRealTimeReflectionHeightFogPS", SF_Pixel);
void FScene::ValidateSkyLightRealTimeCapture(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FRDGTextureRef SceneColorTexture)
{
#if WITH_EDITOR
if (!GAreScreenMessagesEnabled)
{
return;
}
bool bSkyMeshInMainPassExist = false;
bool bSkyMeshInRealTimeSkyCaptureExtist = false;
const int32 SkyRealTimeReflectionOnlyMeshBatcheCount = View.SkyMeshBatches.Num();
for (int32 MeshBatchIndex = 0; MeshBatchIndex < SkyRealTimeReflectionOnlyMeshBatcheCount; ++MeshBatchIndex)
{
const FSkyMeshBatch& SkyMeshBatch = View.SkyMeshBatches[MeshBatchIndex];
bSkyMeshInMainPassExist |= SkyMeshBatch.bVisibleInMainPass;
bSkyMeshInRealTimeSkyCaptureExtist |= SkyMeshBatch.bVisibleInRealTimeSkyCapture;
}
if (!bSkyMeshInMainPassExist || !bSkyMeshInRealTimeSkyCaptureExtist)
{
AddDrawCanvasPass(GraphBuilder, {}, View, FScreenPassRenderTarget(SceneColorTexture, View.ViewRect, ERenderTargetLoadAction::ELoad), [this, &View, bSkyMeshInMainPassExist, bSkyMeshInRealTimeSkyCaptureExtist](FCanvas& Canvas)
{
FLinearColor TextColor(1.0f, 0.5f, 0.0f);
if (View.bSceneHasSkyMaterial && !bSkyMeshInMainPassExist)
{
Canvas.DrawShadowedString(100.0f, 100.0f, TEXT("At least one mesh with a sky material is in the scene but none are rendered in main view."), GetStatsFont(), TextColor);
}
if (View.bSceneHasSkyMaterial && !bSkyMeshInRealTimeSkyCaptureExtist && SkyLight && SkyLight->bRealTimeCaptureEnabled)
{
Canvas.DrawShadowedString(100.0f, 110.0f, TEXT("At least one mesh with a sky material is in the scene but none are rendered in the real-time sky light reflection."), GetStatsFont(), TextColor);
}
});
}
#endif
}
BEGIN_SHADER_PARAMETER_STRUCT(FCaptureSkyMeshReflectionPassParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FViewShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FInstanceCullingDrawParams, InstanceCullingDrawParams)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FOpaqueBasePassUniformParameters, BasePass)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
void FScene::AllocateAndCaptureFrameSkyEnvMap(
FRDGBuilder& GraphBuilder, FSceneRenderer& SceneRenderer, FViewInfo& MainView,
bool bShouldRenderSkyAtmosphere, bool bShouldRenderVolumetricCloud, FInstanceCullingManager& InstanceCullingManager)
{
check(SkyLight && SkyLight->bRealTimeCaptureEnabled && !SkyLight->bHasStaticLighting);
// Ignore viewfamilies without the Atmosphere showflag enabled as the sky capture may fail otherwise
// as well as all views being "scene captures" which cannot be used to update the sky light data.
if (MainView.bIsSceneCapture || !MainView.Family->EngineShowFlags.Atmosphere)
{
return;
}
const bool bIsNewFrame = GFrameNumberRenderThread != RealTimeSlicedReflectionCaptureFrameNumber;
RealTimeSlicedReflectionCaptureFrameNumber = GFrameNumberRenderThread;
RDG_EVENT_SCOPE(GraphBuilder, "CaptureConvolveSkyEnvMap");
RDG_GPU_STAT_SCOPE(GraphBuilder, CaptureConvolveSkyEnvMap);
const uint32 CubeWidth = SkyLight->CaptureCubeMapResolution;
const uint32 CubeMipCount = FMath::CeilLogTwo(CubeWidth) + 1;
// Make a snapshot we are going to use for the 6 cubemap faces and set it up.
// Note: cube view is not meant to be sent to lambdas because we only create a single one. You should only send the ViewUniformBuffer around.
FViewInfo& CubeView = *MainView.CreateSnapshot();
CubeView.FOV = 90.0f;
// Note: We cannot override exposure because sky input texture are using exposure
// Other view data clean up
CubeView.StereoPass = EStereoscopicPass::eSSP_FULL;
CubeView.DrawDynamicFlags = EDrawDynamicFlags::ForceLowestLOD;
CubeView.MaterialTextureMipBias = 0;
FViewMatrices::FMinimalInitializer SceneCubeViewInitOptions;
SceneCubeViewInitOptions.ConstrainedViewRect = FIntRect(FIntPoint(0, 0), FIntPoint(CubeWidth, CubeWidth));
FBox VolumeBounds[TVC_MAX];
CubeView.CachedViewUniformShaderParameters = MakeUnique<FViewUniformShaderParameters>();
CubeView.SetupUniformBufferParameters(
VolumeBounds,
TVC_MAX,
*CubeView.CachedViewUniformShaderParameters);
const FMatrix CubeProjectionMatrix = GetCubeProjectionMatrix(CubeView.FOV * 0.5f, (float)CubeWidth, GReflectionCaptureNearPlane);
CubeView.UpdateProjectionMatrix(CubeProjectionMatrix);
FPooledRenderTargetDesc SkyCubeTexDesc = FPooledRenderTargetDesc::CreateCubemapDesc(CubeWidth,
PF_FloatR11G11B10, FClearValueBinding::Black, TexCreate_TargetArraySlicesIndependently,
TexCreate_ShaderResource | TexCreate_UAV | TexCreate_RenderTargetable, false, 1, CubeMipCount, false);
FRDGExternalAccessQueue ExternalAccessQueue;
const bool bTimeSlicedRealTimeCapture = CVarRealTimeReflectionCaptureTimeSlicing.GetValueOnRenderThread() > 0;
const bool CubeResolutionInvalidated = ConvolvedSkyRenderTargetReadyIndex < 0 || (ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex].IsValid() && ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex]->GetDesc().GetSize().X != CubeWidth);
if (!ConvolvedSkyRenderTarget[0].IsValid() || CubeResolutionInvalidated)
{
// Always allocated
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, SkyCubeTexDesc, ConvolvedSkyRenderTarget[0], TEXT("SkyLight.ConvolvedSkyRenderTarget0"));
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, SkyCubeTexDesc, CapturedSkyRenderTarget, TEXT("SkyLight.CapturedSkyRenderTarget"));
}
if (bTimeSlicedRealTimeCapture && (!ConvolvedSkyRenderTarget[1].IsValid() || CubeResolutionInvalidated))
{
// Additional allocation for time slicing
GRenderTargetPool.FindFreeElement(GraphBuilder.RHICmdList, SkyCubeTexDesc, ConvolvedSkyRenderTarget[1], TEXT("SkyLight.ConvolvedSkyRenderTarget1"));
}
auto ClearCubeFace = [&](FRDGTextureRef SkyCubeTexture, int32 CubeFace)
{
FRenderTargetParameters* Parameters = GraphBuilder.AllocParameters<FRenderTargetParameters>();
Parameters->RenderTargets[0] = FRenderTargetBinding(SkyCubeTexture, ERenderTargetLoadAction::ENoAction, 0, CubeFace);
FLinearColor ClearColor = FLinearColor::Black;
GraphBuilder.AddPass(
RDG_EVENT_NAME("ClearSkyRenderTarget"),
Parameters,
ERDGPassFlags::Raster,
[Parameters, ClearColor](FRHICommandList& RHICmdList)
{
DrawClearQuad(RHICmdList, ClearColor);
});
};
auto RenderCubeFaces_SkyCloud = [&](bool bExecuteSky, bool bExecuteCloud, TRefCountPtr<IPooledRenderTarget>& SkyRenderTarget, int32 StartCubeFace, int32 EndCubeFace)
{
FScene* Scene = MainView.Family->Scene->GetRenderScene();
FRDGTextureRef SkyCubeTexture = GraphBuilder.RegisterExternalTexture(SkyRenderTarget, TEXT("SkyRenderTarget"));
if (bExecuteSky || bExecuteCloud)
{
FRDGTextureRef BlackDummy2dTex = GraphBuilder.RegisterExternalTexture(GSystemTextures.BlackDummy);
FRDGTextureRef BlackDummy3dTex = GraphBuilder.RegisterExternalTexture(GSystemTextures.VolumetricBlackDummy);
const bool CaptureShadowFromOpaque = CVarRealTimeReflectionCaptureShadowFromOpaque.GetValueOnRenderThread() > 0;
FSkyAtmosphereRenderContext SkyRC;
const FAtmosphereSetup* AtmosphereSetup = nullptr;
if (bShouldRenderSkyAtmosphere)
{
FSkyAtmosphereRenderSceneInfo& SkyInfo = *GetSkyAtmosphereSceneInfo();
const FSkyAtmosphereSceneProxy& SkyAtmosphereSceneProxy = SkyInfo.GetSkyAtmosphereSceneProxy();
// Global data constant between faces
AtmosphereSetup = &SkyAtmosphereSceneProxy.GetAtmosphereSetup();
SkyRC.bFastSky = false;
SkyRC.bFastAerialPerspective = false;
SkyRC.bFastAerialPerspectiveDepthTest = false;
SkyRC.bSecondAtmosphereLightEnabled = IsSecondAtmosphereLightEnabled();
// Enable opaque shadow on sky if needed
SkyRC.bShouldSampleOpaqueShadow = false;
if (CaptureShadowFromOpaque)
{
SkyAtmosphereLightShadowData LightShadowData;
SkyRC.bShouldSampleOpaqueShadow = ShouldSkySampleAtmosphereLightsOpaqueShadow(*Scene, SceneRenderer.ActiveViewFamily->VisibleLightInfos, LightShadowData);
GetSkyAtmosphereLightsUniformBuffers(GraphBuilder, SkyRC.LightShadowShaderParams0UniformBuffer, SkyRC.LightShadowShaderParams1UniformBuffer,
LightShadowData, CubeView, SkyRC.bShouldSampleOpaqueShadow, UniformBuffer_SingleDraw);
}
SkyRC.bUseDepthBoundTestIfPossible = false;
SkyRC.bForceRayMarching = true; // We do not have any valid view LUT
SkyRC.bDepthReadDisabled = true;
SkyRC.bDisableBlending = true;
SkyRC.TransmittanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetTransmittanceLutTexture());
SkyRC.MultiScatteredLuminanceLut = GraphBuilder.RegisterExternalTexture(SkyInfo.GetMultiScatteredLuminanceLutTexture());
}
FCloudRenderContext CloudRC;
if (bShouldRenderVolumetricCloud)
{
FVolumetricCloudRenderSceneInfo& CloudInfo = *GetVolumetricCloudSceneInfo();
FVolumetricCloudSceneProxy& CloudSceneProxy = CloudInfo.GetVolumetricCloudSceneProxy();
if (CloudSceneProxy.GetCloudVolumeMaterial())
{
FMaterialRenderProxy* CloudVolumeMaterialProxy = CloudSceneProxy.GetCloudVolumeMaterial()->GetRenderProxy();
CloudRC.CloudInfo = &CloudInfo;
CloudRC.CloudVolumeMaterialProxy = CloudVolumeMaterialProxy;
CloudRC.SceneDepthZ = GSystemTextures.GetMaxFP16Depth(GraphBuilder);
CloudRC.MainView = &CubeView; /// This is only accessing data that is not changing between view orientation. Such data are accessed from the ViewUniformBuffer. See CubeView comment above.
CloudRC.bShouldViewRenderVolumetricRenderTarget = false;
CloudRC.bIsReflectionRendering = true;
CloudRC.bIsSkyRealTimeReflectionRendering = true;
CloudRC.bSecondAtmosphereLightEnabled = IsSecondAtmosphereLightEnabled();
CloudRC.bSkipAtmosphericLightShadowmap = !CaptureShadowFromOpaque;
if (CaptureShadowFromOpaque)
{
FLightSceneInfo* AtmosphericLight0Info = Scene->AtmosphereLights[0];
FLightSceneProxy* AtmosphericLight0 = AtmosphericLight0Info ? AtmosphericLight0Info->Proxy : nullptr;
const FProjectedShadowInfo* ProjectedShadowInfo0 = nullptr;
if (AtmosphericLight0Info)
{
ProjectedShadowInfo0 = GetFirstWholeSceneShadowMap(SceneRenderer.ActiveViewFamily->VisibleLightInfos[AtmosphericLight0Info->Id]);
}
// Get the main view shadow info for the cloud shadows in reflection.
if (!CloudRC.bSkipAtmosphericLightShadowmap && AtmosphericLight0 && ProjectedShadowInfo0)
{
SetVolumeShadowingShaderParameters(GraphBuilder, CloudRC.LightShadowShaderParams0, MainView, AtmosphericLight0Info, ProjectedShadowInfo0);
}
else
{
SetVolumeShadowingDefaultShaderParameters(GraphBuilder, CloudRC.LightShadowShaderParams0);
}
}
else
{
SetVolumeShadowingDefaultShaderParameters(GraphBuilder, CloudRC.LightShadowShaderParams0);
}
}
else
{
bShouldRenderVolumetricCloud = false; // Disable cloud rendering
}
}
for (int32 CubeFace = StartCubeFace; CubeFace < EndCubeFace; CubeFace++)
{
RDG_EVENT_SCOPE(GraphBuilder, "Capture Face=%d", CubeFace);
SkyRC.RenderTargets[0] = FRenderTargetBinding(SkyCubeTexture, ERenderTargetLoadAction::ELoad, 0, CubeFace);
const FMatrix CubeViewRotationMatrix = CalcCubeFaceViewRotationMatrix((ECubeFace)CubeFace);
SceneCubeViewInitOptions.ViewRotationMatrix = CubeViewRotationMatrix;
SceneCubeViewInitOptions.ViewOrigin = SkyLight->CapturePosition;
SceneCubeViewInitOptions.ProjectionMatrix = CubeProjectionMatrix;
FViewMatrices CubeViewMatrices = FViewMatrices(SceneCubeViewInitOptions);
CubeView.SetupCommonViewUniformBufferParameters(
*CubeView.CachedViewUniformShaderParameters,
FIntPoint(CubeWidth, CubeWidth),
1,
FIntRect(FIntPoint(0, 0), FIntPoint(CubeWidth, CubeWidth)),
CubeViewMatrices,
CubeViewMatrices);
// Notify the fact that we render a reflection, e.g. remove sun disk.
CubeView.CachedViewUniformShaderParameters->RenderingReflectionCaptureMask = 1.0f;
// Notify the fact that we render a reflection, e.g. use special exposure.
CubeView.CachedViewUniformShaderParameters->RealTimeReflectionCapture = 1.0f;
// We have rendered a sky dome with identity rotation at the SkyLight position for the capture.
if (AtmosphereSetup)
{
FVector3f SkyCameraTranslatedWorldOrigin;
FMatrix44f SkyViewLutReferential;
FVector4f TempSkyPlanetData;
if (MainView.bSceneHasSkyMaterial)
{
// Setup a constant referential for each of the faces of the dynamic reflection capture.
// This is to have the FastSkyViewLUT match the one generated specifically for the capture point of view.
const FVector3f SkyViewLutReferentialForward = FVector3f(1.0f, 0.0f, 0.0f);
const FVector3f SkyViewLutReferentialRight = FVector3f(0.0f, 0.0f, -1.0f);
AtmosphereSetup->ComputeViewData(
SkyLight->CapturePosition, MainView.ViewMatrices.GetPreViewTranslation(), SkyViewLutReferentialForward, SkyViewLutReferentialRight,
SkyCameraTranslatedWorldOrigin, TempSkyPlanetData, SkyViewLutReferential);
CubeView.CachedViewUniformShaderParameters->SkyViewLutTexture = RealTimeReflectionCaptureSkyAtmosphereViewLutTexture->GetRHI();
}
else
{
// Else if there is no sky material, we assume that no material is sampling the FastSkyViewLUT texture in the sky light reflection (bFastSky=bFastAerialPerspective=false).
// But, we still need to udpate the sky parameters on the view according to the sky light capture position
const FVector3f SkyViewLutReferentialForward = FVector3f(1.0f, 0.0f, 0.0f);
const FVector3f SkyViewLutReferentialRight = FVector3f(0.0f, 0.0f, -1.0f);
// LWC_TODO: SkyPlanetTranslatedWorldCenterAndViewHeight is FVector4f because it's from a shader, and will have lost precision already.
AtmosphereSetup->ComputeViewData(
SkyLight->CapturePosition, MainView.ViewMatrices.GetPreViewTranslation(), SkyViewLutReferentialForward, SkyViewLutReferentialRight,
SkyCameraTranslatedWorldOrigin, TempSkyPlanetData, SkyViewLutReferential);
}
CubeView.CachedViewUniformShaderParameters->SkyPlanetTranslatedWorldCenterAndViewHeight = TempSkyPlanetData;
CubeView.CachedViewUniformShaderParameters->SkyCameraTranslatedWorldOrigin = SkyCameraTranslatedWorldOrigin;
CubeView.CachedViewUniformShaderParameters->SkyViewLutReferential = SkyViewLutReferential;
}
if (HasSkyAtmosphere()&& (MainView.bSceneHasSkyMaterial || HasVolumetricCloud())
&& RealTimeReflectionCaptureCamera360APLutTexture.IsValid()) // we also check that because it seems it can happen for some view setup UE-107270, TODO find a repro for a proper fix.
{
CubeView.CachedViewUniformShaderParameters->CameraAerialPerspectiveVolume = RealTimeReflectionCaptureCamera360APLutTexture->GetRHI();
}
else
{
CubeView.CachedViewUniformShaderParameters->CameraAerialPerspectiveVolume = GSystemTextures.VolumetricBlackDummy->GetRHI();
}
CubeView.CreateViewUniformBuffers(*CubeView.CachedViewUniformShaderParameters);
if (CubeView.bSceneHasSkyMaterial)
{
GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, this, CubeView, ExternalAccessQueue);
}
SkyRC.ViewUniformBuffer = CubeView.ViewUniformBuffer;
SkyRC.ViewMatrices = &CubeViewMatrices;
SkyRC.SkyAtmosphereViewLutTexture = BlackDummy2dTex;
SkyRC.SkyAtmosphereCameraAerialPerspectiveVolume = BlackDummy3dTex;
SkyRC.Viewport = FIntRect(FIntPoint(0, 0), FIntPoint(CubeWidth, CubeWidth));
SkyRC.bLightDiskEnabled = false;
SkyRC.bRenderSkyPixel = true;
SkyRC.AerialPerspectiveStartDepthInCm = 0.01f;
SkyRC.NearClippingDistance = 0.01f;
SkyRC.FeatureLevel = FeatureLevel;
FCloudShadowAOData CloudShadowAOData;
GetCloudShadowAOData(GetVolumetricCloudSceneInfo(), CubeView, GraphBuilder, CloudShadowAOData);
SkyRC.bShouldSampleCloudShadow = CloudShadowAOData.bShouldSampleCloudShadow;
SkyRC.VolumetricCloudShadowMap[0] = CloudShadowAOData.VolumetricCloudShadowMap[0];
SkyRC.VolumetricCloudShadowMap[1] = CloudShadowAOData.VolumetricCloudShadowMap[1];
SkyRC.bShouldSampleCloudSkyAO = CloudShadowAOData.bShouldSampleCloudSkyAO;
SkyRC.VolumetricCloudSkyAO = CloudShadowAOData.VolumetricCloudSkyAO;
const bool bUseDepthBuffer = CVarRealTimeReflectionCaptureDepthBuffer.GetValueOnRenderThread() > 0;
FRDGTextureRef CubeDepthTexture = nullptr;
if (bExecuteSky)
{
if(MainView.bSceneHasSkyMaterial || bShouldRenderSkyAtmosphere)
{
// If there are any mesh tagged as IsSky then we render them only, otherwise we simply render the sky atmosphere itself.
if (MainView.bSceneHasSkyMaterial)
{
RDG_EVENT_SCOPE(GraphBuilder, "Capture Sky Materials", CubeFace);
auto* PassParameters = GraphBuilder.AllocParameters<FCaptureSkyMeshReflectionPassParameters>();
PassParameters->View = CubeView.GetShaderParameters();
PassParameters->RenderTargets = SkyRC.RenderTargets;
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, MainView, 0);
// Setup the depth buffer
if (bUseDepthBuffer)
{
FRDGTextureDesc CubeDepthTextureDesc = FRDGTextureDesc::Create2D(FIntPoint(CubeWidth, CubeWidth), PF_DepthStencil,
MainView.GetSceneTexturesConfig().DepthClearValue,
TexCreate_DepthStencilTargetable | TexCreate_ShaderResource);
CubeDepthTexture = GraphBuilder.CreateTexture(CubeDepthTextureDesc, TEXT("SkyLight.CubeDepthTexture"));
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(CubeDepthTexture, ERenderTargetLoadAction::EClear, FExclusiveDepthStencil::DepthWrite_StencilNop);
}
AddSimpleMeshPass(GraphBuilder, PassParameters, Scene, MainView, &InstanceCullingManager, RDG_EVENT_NAME("CaptureSkyMeshReflection"), SkyRC.Viewport,
[&MainView, CubeViewUniformBuffer = CubeView.ViewUniformBuffer, bUseDepthBuffer, Scene](FDynamicPassMeshDrawListContext* DynamicMeshPassContext)
{
FMeshPassProcessorRenderState DrawRenderState;
FExclusiveDepthStencil::Type BasePassDepthStencilAccess_Sky = bUseDepthBuffer ? FExclusiveDepthStencil::Type(Scene->DefaultBasePassDepthStencilAccess | FExclusiveDepthStencil::DepthWrite)
: FExclusiveDepthStencil::Type(Scene->DefaultBasePassDepthStencilAccess & ~FExclusiveDepthStencil::DepthWrite);
SetupBasePassState(BasePassDepthStencilAccess_Sky, false, DrawRenderState);
FSkyPassMeshProcessor PassMeshProcessor(Scene, nullptr, DrawRenderState, DynamicMeshPassContext);
const int32 SkyRealTimeReflectionOnlyMeshBatcheCount = MainView.SkyMeshBatches.Num();
for (int32 MeshBatchIndex = 0; MeshBatchIndex < SkyRealTimeReflectionOnlyMeshBatcheCount; ++MeshBatchIndex)
{
FSkyMeshBatch& SkyMeshBatch = MainView.SkyMeshBatches[MeshBatchIndex];
if (!SkyMeshBatch.bVisibleInRealTimeSkyCapture)
{
continue;
}
const FMeshBatch* MeshBatch = SkyMeshBatch.Mesh;
const FPrimitiveSceneProxy* PrimitiveSceneProxy = SkyMeshBatch.Proxy;
const FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
// Real time sky light capture cannot render dynamic meshes for now.
// For those to be rendered we would need to specify a view to the PassMeshProcessor creation above.
// Dynamic draws uses temporary per frame & per view data (appended at the end of the GPUScene buffer).
// But the view is transient and data on it can morph, and correct data would need to be added to FGPUScenePrimitiveCollector (see UploadDynamicPrimitiveShaderDataForViewInternal)
bool bSkipDynamicMesh = false;
for (auto& Element : MeshBatch->Elements)
{
if (Element.PrimitiveIdMode == PrimID_DynamicPrimitiveShaderData)
{
bSkipDynamicMesh = true;
}
}
if (bSkipDynamicMesh)
{
continue;
}
const uint64 DefaultBatchElementMask = ~0ull;
PassMeshProcessor.AddMeshBatch(*MeshBatch, DefaultBatchElementMask, PrimitiveSceneProxy);
}
});
}
else
{
RDG_EVENT_SCOPE(GraphBuilder, "Capture Sky Raw", CubeFace);
FSceneTextureShaderParameters SceneTextures = CreateSceneTextureShaderParameters(GraphBuilder, &SceneRenderer.GetActiveSceneTextures(), SceneRenderer.FeatureLevel, ESceneTextureSetupMode::SceneDepth);
SceneRenderer.RenderSkyAtmosphereInternal(GraphBuilder, SceneTextures, SkyRC);
}
// Also render the height fog as part of the sky render pass when time slicing is enabled.
if (Scene && Scene->ExponentialFogs.Num() > 0)
{
FRenderRealTimeReflectionHeightFogVS::FPermutationDomain VsPermutationVector;
TShaderMapRef<FRenderRealTimeReflectionHeightFogVS> VertexShader(GetGlobalShaderMap(SkyRC.FeatureLevel), VsPermutationVector);
FRenderRealTimeReflectionHeightFogPS::FPermutationDomain PsPermutationVector;
PsPermutationVector.Set<FRenderRealTimeReflectionHeightFogPS::FDepthTexture>(CubeDepthTexture != nullptr);
TShaderMapRef<FRenderRealTimeReflectionHeightFogPS> PixelShader(GetGlobalShaderMap(SkyRC.FeatureLevel), PsPermutationVector);
FRenderRealTimeReflectionHeightFogPS::FParameters* PsPassParameters = GraphBuilder.AllocParameters<FRenderRealTimeReflectionHeightFogPS::FParameters>();
PsPassParameters->ViewUniformBuffer = CubeView.ViewUniformBuffer;
PsPassParameters->RenderTargets = SkyRC.RenderTargets;
PsPassParameters->DepthTexture = CubeDepthTexture != nullptr ? CubeDepthTexture : BlackDummy2dTex;
PsPassParameters->FogStruct = CreateFogUniformBuffer(GraphBuilder, CubeView);
ClearUnusedGraphResources(PixelShader, PsPassParameters);
// Render height fog at an infinite distance since real time reflections does not have a depth buffer for now.
// Volumetric fog is not supported in such reflections.
GraphBuilder.AddPass(
RDG_EVENT_NAME("DistantHeightFog"),
PsPassParameters,
ERDGPassFlags::Raster,
[PsPassParameters, VertexShader, PixelShader, CubeWidth](FRHICommandList& RHICmdListLambda)
{
RHICmdListLambda.SetViewport(0.0f, 0.0f, 0.0f, CubeWidth, CubeWidth, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdListLambda.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_SourceAlpha, BO_Add, BF_Zero, BF_One>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GEmptyVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdListLambda, GraphicsPSOInit, 0);
FRenderRealTimeReflectionHeightFogVS::FParameters VsPassParameters;
VsPassParameters.ViewUniformBuffer = PsPassParameters->ViewUniformBuffer;
SetShaderParameters(RHICmdListLambda, VertexShader, VertexShader.GetVertexShader(), VsPassParameters);
SetShaderParameters(RHICmdListLambda, PixelShader, PixelShader.GetPixelShader(), *PsPassParameters);
RHICmdListLambda.DrawPrimitive(0, 1, 1);
});
}
}
else
{
ClearCubeFace(SkyCubeTexture, CubeFace);
}
}
if (bShouldRenderVolumetricCloud && bExecuteCloud)
{
CloudRC.ViewUniformBuffer = CubeView.ViewUniformBuffer;
CloudRC.RenderTargets[0] = SkyRC.RenderTargets[0];
// CloudRC.RenderTargets[1] = Null target will skip export
CloudRC.VolumetricCloudShadowTexture[0] = CloudShadowAOData.VolumetricCloudShadowMap[0];
CloudRC.VolumetricCloudShadowTexture[1] = CloudShadowAOData.VolumetricCloudShadowMap[1];
SceneRenderer.RenderVolumetricCloudsInternal(GraphBuilder, CloudRC, InstanceCullingManager);
}
}
// Render lower hemisphere color
if (SkyLight->bLowerHemisphereIsSolidColor)
{
FApplyLowerHemisphereColor::FPermutationDomain PermutationVector;
TShaderMapRef<FApplyLowerHemisphereColor> ComputeShader(GetGlobalShaderMap(FeatureLevel), PermutationVector);
const uint32 MipIndex = 0;
const uint32 Mip0Resolution = SkyCubeTexture->Desc.GetSize().X;
FApplyLowerHemisphereColor::FParameters* PassParameters = GraphBuilder.AllocParameters<FApplyLowerHemisphereColor::FParameters>();
PassParameters->ValidDispatchCoord = FIntPoint(Mip0Resolution, Mip0Resolution);
PassParameters->LowerHemisphereSolidColor = SkyLight->LowerHemisphereColor;
PassParameters->OutTextureMipColor = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyCubeTexture, MipIndex));
FIntVector NumGroups = FIntVector::DivideAndRoundUp(FIntVector(Mip0Resolution, Mip0Resolution, 1), FIntVector(FApplyLowerHemisphereColor::ThreadGroupSize, FApplyLowerHemisphereColor::ThreadGroupSize, 1));
// The groupd size per face with padding
PassParameters->FaceThreadGroupSize = NumGroups.X * FConvolveSpecularFaceCS::ThreadGroupSize;
// We are going to dispatch once for all faces
NumGroups.X *= 6;
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("ApplyLowerHemisphereColor"), ComputeShader, PassParameters, NumGroups);
}
}
else
{
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
{
ClearCubeFace(SkyCubeTexture, CubeFace);
}
}
};
auto RenderCubeFaces_GenCubeMips = [&](uint32 CubeMipStart, uint32 CubeMipEnd, TRefCountPtr<IPooledRenderTarget>& SkyRenderTarget)
{
check(CubeMipStart > 0); // Never write to mip0 as it has just been redered into
FRDGTextureRef SkyCubeTexture = GraphBuilder.RegisterExternalTexture(SkyRenderTarget, TEXT("SkyRenderTarget"));
FDownsampleCubeFaceCS::FPermutationDomain PermutationVector;
TShaderMapRef<FDownsampleCubeFaceCS> ComputeShader(GetGlobalShaderMap(FeatureLevel), PermutationVector);
for (uint32 MipIndex = CubeMipStart; MipIndex <= CubeMipEnd; MipIndex++)
{
const uint32 MipResolution = 1 << (CubeMipCount - MipIndex - 1);
FRDGTextureSRVRef SkyCubeTextureSRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::CreateForMipLevel(SkyCubeTexture, MipIndex - 1)); // slice/face selection is useless so remove from CreateForMipLevel
FDownsampleCubeFaceCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FDownsampleCubeFaceCS::FParameters>();
PassParameters->MipIndex = MipIndex;
PassParameters->NumMips = CubeMipCount;
PassParameters->CubeFace = 0; // unused
PassParameters->ValidDispatchCoord = FIntPoint(MipResolution, MipResolution);
PassParameters->SourceCubemapSampler = TStaticSamplerState<SF_Point>::GetRHI();
PassParameters->SourceCubemapTexture = SkyCubeTextureSRV;
PassParameters->OutTextureMipColor = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(SkyCubeTexture, MipIndex));
FIntVector NumGroups = FIntVector::DivideAndRoundUp(FIntVector(MipResolution, MipResolution, 1), FIntVector(FDownsampleCubeFaceCS::ThreadGroupSize, FDownsampleCubeFaceCS::ThreadGroupSize, 1));
// The groupd size per face with padding
PassParameters->FaceThreadGroupSize = NumGroups.X * FDownsampleCubeFaceCS::ThreadGroupSize;
// We are going to dispatch once for all faces
NumGroups.X *= 6;
// Dispatch with GenerateMips: reading from a slice through SRV and writing into lower mip through UAV.
ClearUnusedGraphResources(ComputeShader, PassParameters);
GraphBuilder.AddPass(
Forward<FRDGEventName>(RDG_EVENT_NAME("MipGen")),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, ComputeShader, NumGroups](FRHICommandList& RHICmdList)
{
FComputeShaderUtils::Dispatch(RHICmdList, ComputeShader, *PassParameters, NumGroups);
});
}
};
auto RenderCubeFaces_SpecularConvolution = [&](uint32 CubeMipStart, uint32 CubeMipEnd, uint32 FaceStart, uint32 FaceCount, TRefCountPtr<IPooledRenderTarget>& DstRenderTarget, TRefCountPtr<IPooledRenderTarget>& SrcRenderTarget)
{
check((FaceStart + FaceCount) <= 6);
FRDGTextureRef RDGSrcRenderTarget = GraphBuilder.RegisterExternalTexture(SrcRenderTarget);
FRDGTextureRef RDGDstRenderTarget = GraphBuilder.RegisterExternalTexture(DstRenderTarget);
FRDGTextureSRVRef RDGSrcRenderTargetSRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(RDGSrcRenderTarget));
FDownsampleCubeFaceCS::FPermutationDomain PermutationVector;
TShaderMapRef<FConvolveSpecularFaceCS> ComputeShader(GetGlobalShaderMap(FeatureLevel), PermutationVector);
for (uint32 MipIndex = CubeMipStart; MipIndex <= CubeMipEnd; MipIndex++)
{
const uint32 MipResolution = 1 << (CubeMipCount - MipIndex - 1);
FConvolveSpecularFaceCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FConvolveSpecularFaceCS::FParameters>();
PassParameters->MipIndex = MipIndex;
PassParameters->NumMips = CubeMipCount;
PassParameters->CubeFace = 0; // unused
PassParameters->CubeFaceOffset = int(FaceStart);
PassParameters->ValidDispatchCoord = FIntPoint(MipResolution, MipResolution);
PassParameters->SourceCubemapSampler = TStaticSamplerState<SF_Point>::GetRHI();
PassParameters->SourceCubemapTexture = RDGSrcRenderTargetSRV;
PassParameters->OutTextureMipColor = GraphBuilder.CreateUAV(FRDGTextureUAVDesc(RDGDstRenderTarget, MipIndex));
FIntVector NumGroups = FIntVector::DivideAndRoundUp(FIntVector(MipResolution, MipResolution, 1), FIntVector(FConvolveSpecularFaceCS::ThreadGroupSize, FConvolveSpecularFaceCS::ThreadGroupSize, 1));
// The groupd size per face with padding
PassParameters->FaceThreadGroupSize = NumGroups.X * FConvolveSpecularFaceCS::ThreadGroupSize;
// We are going to dispatch once for all faces
NumGroups.X *= FaceCount;
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("Convolve"), ComputeShader, PassParameters, NumGroups);
}
};
auto RenderCubeFaces_DiffuseIrradiance = [&](TRefCountPtr<IPooledRenderTarget>& SourceCubemap)
{
FRDGTextureRef SourceCubemapTexture = GraphBuilder.RegisterExternalTexture(SourceCubemap);
FRDGTextureSRVRef SourceCubemapTextureSRV = GraphBuilder.CreateSRV(FRDGTextureSRVDesc::Create(SourceCubemapTexture));
FRDGBuffer* SkyIrradianceEnvironmentMapRDG = GraphBuilder.RegisterExternalBuffer(SkyIrradianceEnvironmentMap);
TShaderMapRef<FComputeSkyEnvMapDiffuseIrradianceCS> ComputeShader(GetGlobalShaderMap(FeatureLevel));
const float SampleCount = FComputeSkyEnvMapDiffuseIrradianceCS::ThreadGroupSizeX * FComputeSkyEnvMapDiffuseIrradianceCS::ThreadGroupSizeY;
const float UniformSampleSolidAngle = 4.0f * PI / SampleCount; // uniform distribution
FComputeSkyEnvMapDiffuseIrradianceCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FComputeSkyEnvMapDiffuseIrradianceCS::FParameters>();
PassParameters->SourceCubemapSampler = TStaticSamplerState<SF_Point>::GetRHI();
PassParameters->SourceCubemapTexture = SourceCubemapTextureSRV;
PassParameters->OutIrradianceEnvMapSH = GraphBuilder.CreateUAV(SkyIrradianceEnvironmentMapRDG);
PassParameters->UniformSampleSolidAngle = UniformSampleSolidAngle;
// For 64 uniform samples on the unit sphere, we roughly have 10 samples per face.
// Considering mip generation and bilinear sampling, we can assume 10 samples is enough to integrate 10*4=40 texels.
// With that, we target integration of 16*16 face.
const uint32 Log2_16 = 4; // FMath::Log2(16.0f)
PassParameters->MipIndex = uint32(FMath::Log2(float(CapturedSkyRenderTarget->GetDesc().GetSize().X))) - Log2_16;
const FIntVector NumGroups = FIntVector(1, 1, 1);
FComputeShaderUtils::AddPass(GraphBuilder, RDG_EVENT_NAME("ComputeSkyEnvMapDiffuseIrradianceCS"), ComputeShader, PassParameters, NumGroups);
ExternalAccessQueue.Add(SkyIrradianceEnvironmentMapRDG, ERHIAccess::SRVMask);
};
const uint32 LastMipLevel = CubeMipCount - 1;
// Ensure the main view got the full cubemap by running all the capture operations for the first frame.
// This ensures a proper initial state when time-slicing the steps.
// Update the firt frame detection state variable
if (bTimeSlicedRealTimeCapture)
{
// Go to next state iff this is a new frame
if (bIsNewFrame)
{
switch (RealTimeSlicedReflectionCaptureFirstFrameState)
{
case ERealTimeSlicedReflectionCaptureFirstFrameState::INIT:
RealTimeSlicedReflectionCaptureFirstFrameState = ERealTimeSlicedReflectionCaptureFirstFrameState::FIRST_FRAME;
break;
case ERealTimeSlicedReflectionCaptureFirstFrameState::FIRST_FRAME:
RealTimeSlicedReflectionCaptureFirstFrameState = ERealTimeSlicedReflectionCaptureFirstFrameState::BEYOND_FIRST_FRAME;
break;
default:
break;
}
}
}
else
{
// Reset the time-slicing first frame detection state when not time-slicing.
RealTimeSlicedReflectionCaptureFirstFrameState = ERealTimeSlicedReflectionCaptureFirstFrameState::INIT;
}
if (!bTimeSlicedRealTimeCapture
|| (RealTimeSlicedReflectionCaptureFirstFrameState < ERealTimeSlicedReflectionCaptureFirstFrameState::BEYOND_FIRST_FRAME))
{
// Generate a full cube map in a single frame for the first frame.
// Perf number are for a 128x128x6 a cubemap on PS4 with sky and cloud and default settings
// Since it is entirely generated each frame when time slicing is not enabled, we always use cubemap index 0 always allocated above
ConvolvedSkyRenderTargetReadyIndex = 0;
// 0.60ms (0.12ms for faces with the most clouds)
RenderCubeFaces_SkyCloud(true, true, CapturedSkyRenderTarget, 0, CubeFace_MAX);
// 0.05ms
RenderCubeFaces_GenCubeMips(1, LastMipLevel, CapturedSkyRenderTarget);
// 0.80ms total (0.30ms for mip0, 0.20ms for mip1+2, 0.30ms for remaining mips)
RenderCubeFaces_SpecularConvolution(0, LastMipLevel, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex], CapturedSkyRenderTarget);
// 0.015ms
RenderCubeFaces_DiffuseIrradiance(ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex]);
// Reset Scene time slicing state so that it starts from the beginning if/when we get out of non-time-sliced.
RealTimeSlicedReflectionCaptureState = -1; // Value of -1 indicates this is the first time-sliced iteration.
// The sky just changed, so invalidate these textures, so that the path tracer can rebuild them
PathTracingSkylightTexture.SafeRelease();
PathTracingSkylightPdf.SafeRelease();
}
else
{
// Each frame we capture the sky and work in ProcessedSkyRenderTarget to generate the specular convolution.
// Once done, we copy the result into ConvolvedSkyRenderTarget and generate the sky irradiance SH from there.
// On the first frame, we always fully initialise the convolution so ConvolvedSkyRenderTargetReadyIndex should already be valid.
check(ConvolvedSkyRenderTargetReadyIndex >= 0 && ConvolvedSkyRenderTargetReadyIndex <= 1);
const int32 ConvolvedSkyRenderTargetWorkIndex = 1 - ConvolvedSkyRenderTargetReadyIndex;
const int32 TimeSliceCount = 12;
#define DEBUG_TIME_SLICE 0
#if DEBUG_TIME_SLICE
RealTimeSlicedReflectionCaptureState = -1;
RealTimeSlicedReflectionCaptureStateStep = 0;
while(true)
{
if (RealTimeSlicedReflectionCaptureState+1 >= TimeSliceCount)
{
break;
}
#endif
// Update the current time-slicing state if this is a new frame and if the current step is done.
// Note: RealTimeSlicedReflectionCaptureState will initially be -1.
if (bIsNewFrame && bRealTimeSlicedReflectionCaptureStateStepDone)
{
if (++RealTimeSlicedReflectionCaptureState >= TimeSliceCount)
{
RealTimeSlicedReflectionCaptureState = 0;
RealTimeSlicedReflectionCaptureStateStep = 0;
}
}
bRealTimeSlicedReflectionCaptureStateStepDone = true;
const int32 SkyCloudFrameStepCount = FMath::Clamp(CVarRealTimeReflectionCaptureTimeSlicingSkyCloudCubeFacePerFrame.GetValueOnRenderThread(), int32(1), int32(CubeFace_MAX));
const int32 SkyCloudStartSubStep = FMath::Clamp(RealTimeSlicedReflectionCaptureStateStep, int32(0), int32(CubeFace_MAX - 1));
const int32 SkyCloudEndSubStep = FMath::Clamp(RealTimeSlicedReflectionCaptureStateStep + SkyCloudFrameStepCount, int32(0), int32(CubeFace_MAX));
if (RealTimeSlicedReflectionCaptureState <= 0)
{
RDG_EVENT_SCOPE(GraphBuilder, "RenderSky StartFace=%d EndFace=%d", SkyCloudStartSubStep, SkyCloudEndSubStep);
RenderCubeFaces_SkyCloud(true, false, CapturedSkyRenderTarget, SkyCloudStartSubStep, SkyCloudEndSubStep);
bRealTimeSlicedReflectionCaptureStateStepDone = SkyCloudEndSubStep >= CubeFace_MAX;
RealTimeSlicedReflectionCaptureStateStep = bRealTimeSlicedReflectionCaptureStateStepDone ? 0 : SkyCloudEndSubStep;
}
else if (RealTimeSlicedReflectionCaptureState == 1)
{
RDG_EVENT_SCOPE(GraphBuilder, "RenderCloud StartFace=%d EndFace=%d", SkyCloudStartSubStep, SkyCloudEndSubStep);
RenderCubeFaces_SkyCloud(false, true, CapturedSkyRenderTarget, SkyCloudStartSubStep, SkyCloudEndSubStep);
bRealTimeSlicedReflectionCaptureStateStepDone = SkyCloudEndSubStep >= CubeFace_MAX;
RealTimeSlicedReflectionCaptureStateStep = bRealTimeSlicedReflectionCaptureStateStepDone ? 0 : SkyCloudEndSubStep;
}
else if (RealTimeSlicedReflectionCaptureState == 2)
{
RDG_EVENT_SCOPE(GraphBuilder, "GenCubeMips");
RenderCubeFaces_GenCubeMips(1, LastMipLevel, CapturedSkyRenderTarget);
}
else if (RealTimeSlicedReflectionCaptureState == 3)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip0Face01");
RenderCubeFaces_SpecularConvolution(0, 0, 0, 2, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget); // convolution of mip0, face 0, 1
}
else if (RealTimeSlicedReflectionCaptureState == 4)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip0Face23");
RenderCubeFaces_SpecularConvolution(0, 0, 2, 2, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget); // convolution of mip0, face 2, 3
}
else if (RealTimeSlicedReflectionCaptureState == 5)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip0Face45");
RenderCubeFaces_SpecularConvolution(0, 0, 4, 2, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget); // convolution of mip0, face 4, 5
}
else if (RealTimeSlicedReflectionCaptureState == 6)
{
if (LastMipLevel >= 1)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip1");
RenderCubeFaces_SpecularConvolution(1, 1, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
}
else if (RealTimeSlicedReflectionCaptureState == 7)
{
if (LastMipLevel >= 2)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip2");
RenderCubeFaces_SpecularConvolution(2, 2, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
}
else if (RealTimeSlicedReflectionCaptureState == 8)
{
if (LastMipLevel >= 3)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip3");
RenderCubeFaces_SpecularConvolution(3, 3, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
}
else if (RealTimeSlicedReflectionCaptureState == 9)
{
if (LastMipLevel >= 5)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip45");
RenderCubeFaces_SpecularConvolution(4, 5, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
else if (LastMipLevel >= 4)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip4");
RenderCubeFaces_SpecularConvolution(4, 4, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
}
else if (RealTimeSlicedReflectionCaptureState == 10)
{
if (LastMipLevel >= 6)
{
RDG_EVENT_SCOPE(GraphBuilder, "ConvolutionMip6Etc");
RenderCubeFaces_SpecularConvolution(6, LastMipLevel, 0, 6, ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex], CapturedSkyRenderTarget);
}
}
else if (RealTimeSlicedReflectionCaptureState == 11)
{
RDG_EVENT_SCOPE(GraphBuilder, "DiffuseIrradiance");
// Update the sky irradiance SH buffer.
RenderCubeFaces_DiffuseIrradiance(ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetWorkIndex]);
// Now use the new cubemap
ConvolvedSkyRenderTargetReadyIndex = ConvolvedSkyRenderTargetWorkIndex;
// The sky just changed, so invalidate these textures, so that the path tracer can rebuild them
PathTracingSkylightTexture.SafeRelease();
PathTracingSkylightPdf.SafeRelease();
}
#if DEBUG_TIME_SLICE
}
#endif
}
if (ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex])
{
ExternalAccessQueue.Add(GraphBuilder.RegisterExternalTexture(ConvolvedSkyRenderTarget[ConvolvedSkyRenderTargetReadyIndex]), ERHIAccess::SRVMask);
}
ExternalAccessQueue.Submit(GraphBuilder);
}