处理ShadowMapAtlases
FSceneRenderer::RenderShadowDepthMapAtlases(RHICmdList);
void FSceneRenderer::RenderShadowDepthMapAtlases(FRHICommandListImmediate& RHICmdList)
{
check(RHICmdList.IsOutsideRenderPass());
// Perform setup work on all GPUs in case any cached shadows are being updated this
// frame. We revert to the AllViewsGPUMask for uncached shadows.
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
bool bCanUseParallelDispatch = RHICmdList.IsImmediate() && // translucent shadows are draw on the render thread, using a recursive cmdlist (which is not immediate)
GRHICommandList.UseParallelAlgorithms() && CVarParallelShadows.GetValueOnRenderThread();
for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num(); AtlasIndex++)
{
const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.ShadowMapAtlases[AtlasIndex];
FSceneRenderTargetItem& RenderTarget = ShadowMapAtlas.RenderTargets.DepthTarget->GetRenderTargetItem();
FIntPoint AtlasSize = ShadowMapAtlas.RenderTargets.DepthTarget->GetDesc().Extent;
GVisualizeTexture.SetCheckPoint(RHICmdList, ShadowMapAtlas.RenderTargets.DepthTarget.GetReference());
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepths, TEXT("Atlas%u %ux%u"), AtlasIndex, AtlasSize.X, AtlasSize.Y);
auto BeginShadowRenderPass = [this, &RenderTarget, &SceneContext](FRHICommandList& InRHICmdList, bool bPerformClear)
{
check(RenderTarget.TargetableTexture->GetDepthClearValue() == 1.0f);
ERenderTargetLoadAction DepthLoadAction = bPerformClear ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad;
FRHIRenderPassInfo RPInfo(RenderTarget.TargetableTexture, MakeDepthStencilTargetActions(MakeRenderTargetActions(DepthLoadAction, ERenderTargetStoreAction::EStore), ERenderTargetActions::DontLoad_DontStore), nullptr, FExclusiveDepthStencil::DepthWrite_StencilNop);
if (!GSupportsDepthRenderTargetWithoutColorRenderTarget)
{
RPInfo.ColorRenderTargets[0].Action = ERenderTargetActions::DontLoad_DontStore;
RPInfo.ColorRenderTargets[0].RenderTarget = SceneContext.GetOptionalShadowDepthColorSurface(InRHICmdList, RPInfo.DepthStencilRenderTarget.DepthStencilTarget->GetTexture2D()->GetSizeX(), RPInfo.DepthStencilRenderTarget.DepthStencilTarget->GetTexture2D()->GetSizeY());
InRHICmdList.Transition(FRHITransitionInfo(RPInfo.ColorRenderTargets[0].RenderTarget, ERHIAccess::Unknown, ERHIAccess::RTV));
}
InRHICmdList.Transition(FRHITransitionInfo(RPInfo.DepthStencilRenderTarget.DepthStencilTarget, ERHIAccess::Unknown, ERHIAccess::DSVWrite));
InRHICmdList.BeginRenderPass(RPInfo, TEXT("ShadowMapAtlases"));
};
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> ParallelShadowPasses;
TArray<FProjectedShadowInfo*, SceneRenderingAllocator> SerialShadowPasses;
// Gather our passes here to minimize switching renderpasses
for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex];
const bool bDoParallelDispatch = bCanUseParallelDispatch &&
(ProjectedShadowInfo->IsWholeSceneDirectionalShadow() || CVarParallelShadowsNonWholeScene.GetValueOnRenderThread());
if (bDoParallelDispatch)
{
ParallelShadowPasses.Add(ProjectedShadowInfo);
}
else
{
SerialShadowPasses.Add(ProjectedShadowInfo);
}
}
FLightSceneProxy* CurrentLightForDrawEvent = NULL;
#if WANTS_DRAW_MESH_EVENTS
FDrawEvent LightEvent;
#endif
if (ParallelShadowPasses.Num() > 0)
{
{
// Clear before going wide.
SCOPED_DRAW_EVENT(RHICmdList, SetShadowRTsAndClear);
BeginShadowRenderPass(RHICmdList, true);
RHICmdList.EndRenderPass();
}
for (int32 ShadowIndex = 0; ShadowIndex < ParallelShadowPasses.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = ParallelShadowPasses[ShadowIndex];
SCOPED_GPU_MASK(RHICmdList, GetGPUMaskForShadow(ProjectedShadowInfo));
if (!CurrentLightForDrawEvent || ProjectedShadowInfo->GetLightSceneInfo().Proxy != CurrentLightForDrawEvent)
{
if (CurrentLightForDrawEvent)
{
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
STOP_DRAW_EVENT(LightEvent);
}
CurrentLightForDrawEvent = ProjectedShadowInfo->GetLightSceneInfo().Proxy;
FString LightNameWithLevel;
GetLightNameForDrawEvent(CurrentLightForDrawEvent, LightNameWithLevel);
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
BEGIN_DRAW_EVENTF(
RHICmdList,
LightNameEvent,
LightEvent,
*LightNameWithLevel);
}
ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene);
ProjectedShadowInfo->TransitionCachedShadowmap(RHICmdList, Scene);
ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, true);
}
}
if (CurrentLightForDrawEvent)
{
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
STOP_DRAW_EVENT(LightEvent);
}
CurrentLightForDrawEvent = nullptr;
if (SerialShadowPasses.Num() > 0)
{
bool bShadowDepthCleared = ParallelShadowPasses.Num() > 0;
bool bForceSingleRenderPass = CVarShadowForceSerialSingleRenderPass.GetValueOnAnyThread() != 0;
if (bForceSingleRenderPass)
{
SCOPED_GPU_MASK(RHICmdList, AllViewsGPUMask);
BeginShadowRenderPass(RHICmdList, !bShadowDepthCleared);
}
for (int32 ShadowIndex = 0; ShadowIndex < SerialShadowPasses.Num(); ShadowIndex++)
{
FProjectedShadowInfo* ProjectedShadowInfo = SerialShadowPasses[ShadowIndex];
SCOPED_GPU_MASK(RHICmdList, GetGPUMaskForShadow(ProjectedShadowInfo));
if (!CurrentLightForDrawEvent || ProjectedShadowInfo->GetLightSceneInfo().Proxy != CurrentLightForDrawEvent)
{
if (CurrentLightForDrawEvent)
{
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
STOP_DRAW_EVENT(LightEvent);
}
CurrentLightForDrawEvent = ProjectedShadowInfo->GetLightSceneInfo().Proxy;
FString LightNameWithLevel;
GetLightNameForDrawEvent(CurrentLightForDrawEvent, LightNameWithLevel);
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
BEGIN_DRAW_EVENTF(
RHICmdList,
LightNameEvent,
LightEvent,
*LightNameWithLevel);
}
ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene);
ProjectedShadowInfo->TransitionCachedShadowmap(RHICmdList, Scene);
#if WITH_MGPU
// In case the first shadow is view-dependent, ensure we do the clear on all GPUs.
FRHIGPUMask GPUMaskForRenderPass = RHICmdList.GetGPUMask();
if (ShadowIndex == 0)
{
// This ensures that we don't downgrade the GPU mask if the first shadow is a
// cached whole scene shadow.
GPUMaskForRenderPass |= AllViewsGPUMask;
}
#endif
if (!bForceSingleRenderPass)
{
SCOPED_GPU_MASK(RHICmdList, GPUMaskForRenderPass);
BeginShadowRenderPass(RHICmdList, ShadowIndex == 0 && !bShadowDepthCleared);
}
ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, false);
if (!bForceSingleRenderPass)
{
RHICmdList.EndRenderPass();
}
}
if (bForceSingleRenderPass)
{
SCOPED_GPU_MASK(RHICmdList, AllViewsGPUMask);
RHICmdList.EndRenderPass();
}
}
if (CurrentLightForDrawEvent)
{
SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All());
STOP_DRAW_EVENT(LightEvent);
CurrentLightForDrawEvent = NULL;
}
RHICmdList.Transition(FRHITransitionInfo(RenderTarget.TargetableTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
}
void FProjectedShadowInfo::SetupShadowUniformBuffers(FRHICommandListImmediate& RHICmdList, FScene* Scene, FLightPropagationVolume* LPV)
{
const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel;
if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Deferred)
{
FShadowDepthPassUniformParameters ShadowDepthPassParameters;
SetupShadowDepthPassUniformBuffer(this, RHICmdList, *ShadowDepthView, ShadowDepthPassParameters, LPV);
if (IsWholeSceneDirectionalShadow() && !bReflectiveShadowmap)
{
check(GetShadowDepthType() == CSMShadowDepthType);
Scene->UniformBuffers.CSMShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters);
}
ShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters);
if (DependentView)
{
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginRenderView(DependentView);
}
}
}
// This needs to be done for both mobile and deferred
UploadDynamicPrimitiveShaderDataForView(RHICmdList, *Scene, *ShadowDepthView);
}
void FProjectedShadowInfo::TransitionCachedShadowmap(FRHICommandListImmediate& RHICmdList, FScene* Scene)
{
if (CacheMode == SDCM_MovablePrimitivesOnly)
{
const FCachedShadowMapData& CachedShadowMapData = Scene->CachedShadowMaps.FindChecked(GetLightSceneInfo().Id);
if (CachedShadowMapData.bCachedShadowMapHasPrimitives && CachedShadowMapData.ShadowMap.IsValid())
{
RHICmdList.Transition(FRHITransitionInfo(CachedShadowMapData.ShadowMap.DepthTarget->GetRenderTargetItem().ShaderResourceTexture, ERHIAccess::Unknown, ERHIAccess::SRVGraphics));
}
}
}
void FProjectedShadowInfo::RenderDepth(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer, FBeginShadowRenderPassFunction BeginShadowRenderPass, bool bDoParallelDispatch)
{
#if WANTS_DRAW_MESH_EVENTS
FString EventName;
if (GetEmitDrawEvents())
{
GetShadowTypeNameForDrawEvent(EventName);
EventName += FString(TEXT(" ")) + FString::FromInt(ResolutionX) + TEXT("x") + FString::FromInt(ResolutionY);
}
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepthActor, *EventName);
#endif
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderWholeSceneShadowDepthsTime, bWholeSceneShadow);
CONDITIONAL_SCOPE_CYCLE_COUNTER(STAT_RenderPerObjectShadowDepthsTime, !bWholeSceneShadow);
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderShadowDepth);
RenderDepthInner(RHICmdList, SceneRenderer, BeginShadowRenderPass, bDoParallelDispatch);
}
void FProjectedShadowInfo::RenderDepthInner(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer, FBeginShadowRenderPassFunction BeginShadowRenderPass, bool bDoParallelDispatch)
{
const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel;
FRHIUniformBuffer* PassUniformBuffer = ShadowDepthPassUniformBuffer;
const bool bIsWholeSceneDirectionalShadow = IsWholeSceneDirectionalShadow();
if (bIsWholeSceneDirectionalShadow)
{
// CSM shadow depth cached mesh draw commands are all referencing the same view uniform buffer. We need to update it before rendering each cascade.
ShadowDepthView->ViewUniformBuffer.UpdateUniformBufferImmediate(*ShadowDepthView->CachedViewUniformShaderParameters);
if (DependentView)
{
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginRenderView(DependentView);
}
}
}
if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Mobile)
{
FMobileShadowDepthPassUniformParameters ShadowDepthPassParameters;
SetupShadowDepthPassUniformBuffer(this, RHICmdList, *ShadowDepthView, ShadowDepthPassParameters);
SceneRenderer->Scene->UniformBuffers.MobileCSMShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters);
MobileShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters);
PassUniformBuffer = SceneRenderer->Scene->UniformBuffers.MobileCSMShadowDepthPassUniformBuffer;
}
FMeshPassProcessorRenderState DrawRenderState(*ShadowDepthView, PassUniformBuffer);
SetStateForShadowDepth(bReflectiveShadowmap, bOnePassPointLightShadow, DrawRenderState);
SetStateForView(RHICmdList);
if (CacheMode == SDCM_MovablePrimitivesOnly)
{
// In parallel mode we will not have a renderpass active at this point.
if (bDoParallelDispatch)
{
BeginShadowRenderPass(RHICmdList, false);
}
// Copy in depths of static primitives before we render movable primitives
CopyCachedShadowMap(RHICmdList, DrawRenderState, SceneRenderer, *ShadowDepthView);
if (bDoParallelDispatch)
{
RHICmdList.EndRenderPass();
}
}
if (bDoParallelDispatch)
{
check(IsInRenderingThread());
// Parallel encoding requires its own renderpass.
check(RHICmdList.IsOutsideRenderPass());
// parallel version
bool bFlush = CVarRHICmdFlushRenderThreadTasksShadowPass.GetValueOnRenderThread() > 0
|| CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0;
FScopedCommandListWaitForTasks Flusher(bFlush);
// Dispatch commands
{
FShadowParallelCommandListSet ParallelCommandListSet(RHICmdList, *ShadowDepthView, !bFlush, *this, BeginShadowRenderPass);
ShadowDepthPass.DispatchDraw(&ParallelCommandListSet, RHICmdList);
}
// Renderpass must be closed once we get here.
check(RHICmdList.IsOutsideRenderPass());
}
else
{
// We must have already opened the renderpass by the time we get here.
check(RHICmdList.IsInsideRenderPass());
ShadowDepthPass.DispatchDraw(nullptr, RHICmdList);
// Renderpass must still be open when we reach here
check(RHICmdList.IsInsideRenderPass());
}
}
void FParallelMeshDrawCommandPass::DispatchDraw(FParallelCommandListSet* ParallelCommandListSet, FRHICommandList& RHICmdList) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(ParallelMdcDispatchDraw);
if (MaxNumDraws <= 0)
{
return;
}
FRHIVertexBuffer* PrimitiveIdsBuffer = PrimitiveIdVertexBufferPoolEntry.BufferRHI;
const int32 BasePrimitiveIdsOffset = 0;
if (ParallelCommandListSet)
{
if (TaskContext.bUseGPUScene)
{
// Queue a command on the RHI thread which will upload PrimitiveIdVertexBuffer after finishing FMeshDrawCommandPassSetupTask.
FRHICommandListImmediate &RHICommandList = GetImmediateCommandList_ForRenderCommand();
if (TaskEventRef.IsValid())
{
RHICommandList.AddDispatchPrerequisite(TaskEventRef);
}
RHICommandList.EnqueueLambda([
VertexBuffer = PrimitiveIdsBuffer,
VertexBufferData = TaskContext.PrimitiveIdBufferData,
VertexBufferDataSize = TaskContext.PrimitiveIdBufferDataSize,
PrimitiveIdVertexBufferPoolEntry = PrimitiveIdVertexBufferPoolEntry](FRHICommandListImmediate& CmdList)
{
// Upload vertex buffer data.
void* RESTRICT Data = (void* RESTRICT)CmdList.LockVertexBuffer(VertexBuffer, 0, VertexBufferDataSize, RLM_WriteOnly);
FMemory::Memcpy(Data, VertexBufferData, VertexBufferDataSize);
CmdList.UnlockVertexBuffer(VertexBuffer);
FMemory::Free(VertexBufferData);
});
RHICommandList.RHIThreadFence(true);
bPrimitiveIdBufferDataOwnedByRHIThread = true;
}
const ENamedThreads::Type RenderThread = ENamedThreads::GetRenderThread();
FGraphEventArray Prereqs;
if (ParallelCommandListSet->GetPrereqs())
{
Prereqs.Append(*ParallelCommandListSet->GetPrereqs());
}
if (TaskEventRef.IsValid())
{
Prereqs.Add(TaskEventRef);
}
// Distribute work evenly to the available task graph workers based on NumEstimatedDraws.
// Every task will then adjust it's working range based on FVisibleMeshDrawCommandProcessTask results.
const int32 NumThreads = FMath::Min<int32>(FTaskGraphInterface::Get().GetNumWorkerThreads(), ParallelCommandListSet->Width);
const int32 NumTasks = FMath::Min<int32>(NumThreads, FMath::DivideAndRoundUp(MaxNumDraws, ParallelCommandListSet->MinDrawsPerCommandList));
const int32 NumDrawsPerTask = FMath::DivideAndRoundUp(MaxNumDraws, NumTasks);
for (int32 TaskIndex = 0; TaskIndex < NumTasks; TaskIndex++)
{
const int32 StartIndex = TaskIndex * NumDrawsPerTask;
const int32 NumDraws = FMath::Min(NumDrawsPerTask, MaxNumDraws - StartIndex);
checkSlow(NumDraws > 0);
FRHICommandList* CmdList = ParallelCommandListSet->NewParallelCommandList();
FGraphEventRef AnyThreadCompletionEvent = TGraphTask<FDrawVisibleMeshCommandsAnyThreadTask>::CreateTask(&Prereqs, RenderThread)
.ConstructAndDispatchWhenReady(*CmdList, TaskContext.MeshDrawCommands, TaskContext.MinimalPipelineStatePassSet, PrimitiveIdsBuffer, BasePrimitiveIdsOffset, TaskContext.bDynamicInstancing, TaskContext.InstanceFactor, TaskIndex, NumTasks);
ParallelCommandListSet->AddParallelCommandList(CmdList, AnyThreadCompletionEvent, NumDraws);
}
}
else
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_MeshPassDrawImmediate);
WaitForMeshPassSetupTask();
if (TaskContext.bUseGPUScene)
{
// Can immediately upload vertex buffer data, as there is no parallel draw task.
void* RESTRICT Data = RHILockVertexBuffer(PrimitiveIdVertexBufferPoolEntry.BufferRHI, 0, TaskContext.PrimitiveIdBufferDataSize, RLM_WriteOnly);
FMemory::Memcpy(Data, TaskContext.PrimitiveIdBufferData, TaskContext.PrimitiveIdBufferDataSize);
RHIUnlockVertexBuffer(PrimitiveIdVertexBufferPoolEntry.BufferRHI);
}
SubmitMeshDrawCommandsRange(TaskContext.MeshDrawCommands, TaskContext.MinimalPipelineStatePassSet, PrimitiveIdsBuffer, BasePrimitiveIdsOffset, TaskContext.bDynamicInstancing, 0, TaskContext.MeshDrawCommands.Num(), TaskContext.InstanceFactor, RHICmdList);
}
}