Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironmentRealTimeCapture.cpp
jason hoerner 6144ebf450 Scene renderer no longer contains multiple view families. Instead, multiple view families are handled by creating multiple scene renderers, which are added to a linked list with the first scene renderer as the list head. This more or less reverses CL 19813996, while preserving the optimizations gained from having knowledge of multiple scene renderers. This includes deferring cross GPU resource transfer waits to the last scene render, running ray tracing scene update once, and only flushing resources once. Future optimizations and bug fixes may take advantage of additional information from other scene renderers.
The refactor to get rid of the FSceneTextures and FSceneTexturesConfig global singletons has also been preserved.  The FViewFamilyInfo is used to hold those structures, so client facing API functions that lack a scene renderer reference can still pull the FSceneTextures[Config] from the view family pointer.  All other scene renderer state has been moved from FViewFamilyInfo back into FSceneRenderer.

#jira none
#rnx
#rb ola.olsson krzysztof.narkowicz zach.bethel
#preflight 629f770e233ae0a8f8fb7f2e

[CL 20540730 by jason hoerner in ue5-main branch]
2022-06-07 13:19:54 -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.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.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);
}