378 lines
17 KiB
Markdown
378 lines
17 KiB
Markdown
---
|
||
title: Untitled
|
||
date: 2024-09-26 18:41:24
|
||
excerpt:
|
||
tags:
|
||
rating: ⭐
|
||
---
|
||
# RenderBasePass()
|
||
传入RenderBasePass()的DepthStencil逻辑如下:
|
||
```c++
|
||
const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
|
||
bAllowReadOnlyDepthBasePass
|
||
? FExclusiveDepthStencil::DepthRead_StencilWrite
|
||
: FExclusiveDepthStencil::DepthWrite_StencilWrite;
|
||
```
|
||
|
||
|
||
FDeferredShadingSceneRenderer::RenderBasePass() =>
|
||
FDeferredShadingSceneRenderer::RenderBasePassInternal() =>
|
||
|
||
|
||
FBasePassMeshProcessor::TryAddMeshBatch =>
|
||
|
||
## 大致流程
|
||
1. 创建MRT并绑定、取得深度缓存。
|
||
```c++
|
||
const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess);
|
||
|
||
TStaticArray<FTextureRenderTargetBinding, MaxSimultaneousRenderTargets> BasePassTextures;
|
||
uint32 BasePassTextureCount = SceneTextures.GetGBufferRenderTargets(BasePassTextures);
|
||
Strata::AppendStrataMRTs(*this, BasePassTextureCount, BasePassTextures);
|
||
TArrayView<FTextureRenderTargetBinding> BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
|
||
FRDGTextureRef BasePassDepthTexture = SceneTextures.Depth.Target;
|
||
```
|
||
2. GBuffer Clear
|
||
```c++
|
||
GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster,
|
||
[PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList)
|
||
{
|
||
// If no fast-clear action was used, we need to do an MRT shader clear.
|
||
if (ColorLoadAction == ERenderTargetLoadAction::ENoAction)
|
||
{
|
||
const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets;
|
||
FLinearColor ClearColors[MaxSimultaneousRenderTargets];
|
||
FRHITexture* Textures[MaxSimultaneousRenderTargets];
|
||
int32 TextureIndex = 0;
|
||
|
||
RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget)
|
||
{
|
||
FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI();
|
||
ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor();
|
||
Textures[TextureIndex] = TextureRHI;
|
||
++TextureIndex;
|
||
});
|
||
|
||
// Clear color only; depth-stencil is fast cleared.
|
||
DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0);
|
||
}
|
||
});
|
||
```
|
||
3. RenderTargetBindingSlots
|
||
```c++
|
||
// Render targets bindings should remain constant at this point.
|
||
FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
|
||
BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);
|
||
```
|
||
4. RenderBasePassInternal()
|
||
5. RenderAnisotropyPass()
|
||
|
||
# MeshDraw
|
||
## RenderBasePassInternal()
|
||
RenderNaniteBasePass()为一个Lambda,最终调用**Nanite::DrawBasePass()** 渲染Nanite物体的BasePass。其他相关渲染代码如下:
|
||
```c++
|
||
SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime);
|
||
RDG_EVENT_SCOPE(GraphBuilder, "BasePass");
|
||
RDG_GPU_STAT_SCOPE(GraphBuilder, Basepass);
|
||
|
||
const bool bDrawSceneViewsInOneNanitePass = Views.Num() > 1 && Nanite::ShouldDrawSceneViewsInOneNanitePass(Views[0]);
|
||
if (bParallelBasePass)//并行渲染模式
|
||
{
|
||
RDG_WAIT_FOR_TASKS_CONDITIONAL(GraphBuilder, IsBasePassWaitForTasksEnabled());
|
||
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
||
{
|
||
FViewInfo& View = Views[ViewIndex];
|
||
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
||
View.BeginRenderView();
|
||
|
||
const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
|
||
|
||
FMeshPassProcessorRenderState DrawRenderState;
|
||
SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState);
|
||
|
||
FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||
PassParameters->View = View.GetShaderParameters();
|
||
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled);
|
||
PassParameters->RenderTargets = BasePassRenderTargets;
|
||
PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass);
|
||
|
||
const bool bShouldRenderView = View.ShouldRenderView();
|
||
if (bShouldRenderView)
|
||
{
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("BasePassParallel"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||
[this, &View, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||
{
|
||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(PassParameters));
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
});
|
||
}
|
||
|
||
const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones
|
||
if (bShouldRenderViewForNanite)
|
||
{
|
||
// Should always have a full Z prepass with Nanite
|
||
check(ShouldRenderPrePass());
|
||
//渲染Nanite物体BasePass
|
||
RenderNaniteBasePass(View, ViewIndex);
|
||
}
|
||
|
||
//渲染编辑器相关的图元物体
|
||
RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager);
|
||
|
||
//渲染大气
|
||
if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere)
|
||
{
|
||
FOpaqueBasePassParameters* SkyPassPassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||
SkyPassPassParameters->BasePass = PassParameters->BasePass;
|
||
SkyPassPassParameters->RenderTargets = BasePassRenderTargets;
|
||
SkyPassPassParameters->View = View.GetShaderParameters();
|
||
SkyPassPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassPassParameters->InstanceCullingDrawParams);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("SkyPassParallel"),
|
||
SkyPassPassParameters,
|
||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||
[this, &View, SkyPassPassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||
{
|
||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_BasePass), View, FParallelCommandListBindings(SkyPassPassParameters));
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(&ParallelCommandListSet, RHICmdList, &SkyPassPassParameters->InstanceCullingDrawParams);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
|
||
{
|
||
FViewInfo& View = Views[ViewIndex];
|
||
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
|
||
RDG_EVENT_SCOPE_CONDITIONAL(GraphBuilder, Views.Num() > 1, "View%d", ViewIndex);
|
||
View.BeginRenderView();
|
||
|
||
const bool bLumenGIEnabled = GetViewPipelineState(View).DiffuseIndirectMethod == EDiffuseIndirectMethod::Lumen;
|
||
|
||
FMeshPassProcessorRenderState DrawRenderState;
|
||
SetupBasePassState(BasePassDepthStencilAccess, ViewFamily.EngineShowFlags.ShaderComplexity, DrawRenderState);
|
||
|
||
FOpaqueBasePassParameters* PassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||
PassParameters->View = View.GetShaderParameters();
|
||
PassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||
PassParameters->BasePass = CreateOpaqueBasePassUniformBuffer(GraphBuilder, View, ViewIndex, ForwardBasePassTextures, DBufferTextures, bLumenGIEnabled);
|
||
PassParameters->RenderTargets = BasePassRenderTargets;
|
||
PassParameters->RenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, View, FVariableRateShadingImageManager::EVRSPassType::BasePass);
|
||
|
||
const bool bShouldRenderView = View.ShouldRenderView();
|
||
if (bShouldRenderView)
|
||
{
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("BasePass"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster,
|
||
[this, &View, PassParameters](FRHICommandList& RHICmdList)
|
||
{
|
||
SetStereoViewport(RHICmdList, View, 1.0f);
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::BasePass].DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
}
|
||
);
|
||
}
|
||
|
||
const bool bShouldRenderViewForNanite = bNaniteEnabled && (!bDrawSceneViewsInOneNanitePass || ViewIndex == 0); // when bDrawSceneViewsInOneNanitePass, the first view should cover all the other atlased ones
|
||
if (bShouldRenderViewForNanite)
|
||
{
|
||
// Should always have a full Z prepass with Nanite
|
||
check(ShouldRenderPrePass());
|
||
|
||
RenderNaniteBasePass(View, ViewIndex);
|
||
}
|
||
|
||
RenderEditorPrimitives(GraphBuilder, PassParameters, View, DrawRenderState, InstanceCullingManager);
|
||
|
||
if (bShouldRenderView && View.Family->EngineShowFlags.Atmosphere)
|
||
{
|
||
FOpaqueBasePassParameters* SkyPassParameters = GraphBuilder.AllocParameters<FOpaqueBasePassParameters>();
|
||
SkyPassParameters->BasePass = PassParameters->BasePass;
|
||
SkyPassParameters->RenderTargets = BasePassRenderTargets;
|
||
SkyPassParameters->View = View.GetShaderParameters();
|
||
SkyPassParameters->ReflectionCapture = View.ReflectionCaptureUniformBuffer;
|
||
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].BuildRenderingCommands(GraphBuilder, Scene->GPUScene, SkyPassParameters->InstanceCullingDrawParams);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("SkyPass"),
|
||
SkyPassParameters,
|
||
ERDGPassFlags::Raster,
|
||
[this, &View, SkyPassParameters](FRHICommandList& RHICmdList)
|
||
{
|
||
SetStereoViewport(RHICmdList, View, 1.0f);
|
||
View.ParallelMeshDrawCommandPasses[EMeshPass::SkyPass].DispatchDraw(nullptr, RHICmdList, &SkyPassParameters->InstanceCullingDrawParams);
|
||
}
|
||
);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### SetDepthStencilStateForBasePass()
|
||
```c++
|
||
void SetDepthStencilStateForBasePass(
|
||
FMeshPassProcessorRenderState& DrawRenderState,
|
||
ERHIFeatureLevel::Type FeatureLevel,
|
||
bool bDitheredLODTransition,
|
||
const FMaterial& MaterialResource,
|
||
bool bEnableReceiveDecalOutput,
|
||
bool bForceEnableStencilDitherState)
|
||
{
|
||
const bool bMaskedInEarlyPass = (MaterialResource.IsMasked() || bDitheredLODTransition) && MaskedInEarlyPass(GShaderPlatformForFeatureLevel[FeatureLevel]);
|
||
if (bEnableReceiveDecalOutput)
|
||
{
|
||
if (bMaskedInEarlyPass)
|
||
{
|
||
SetDepthStencilStateForBasePass_Internal<false, CF_Equal>(DrawRenderState, FeatureLevel);
|
||
}
|
||
else if (DrawRenderState.GetDepthStencilAccess() & FExclusiveDepthStencil::DepthWrite)
|
||
{
|
||
SetDepthStencilStateForBasePass_Internal<true, CF_GreaterEqual>(DrawRenderState, FeatureLevel);
|
||
}
|
||
else
|
||
{
|
||
SetDepthStencilStateForBasePass_Internal<false, CF_GreaterEqual>(DrawRenderState, FeatureLevel);
|
||
}
|
||
}
|
||
else if (bMaskedInEarlyPass)
|
||
{
|
||
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Equal>::GetRHI());
|
||
}
|
||
|
||
if (bForceEnableStencilDitherState)
|
||
{
|
||
SetDepthStencilStateForBasePass_Internal<false, CF_Equal>(DrawRenderState, FeatureLevel);
|
||
}
|
||
}
|
||
```
|
||
## AnisotropyPass
|
||
Anisotropy的RT设置:
|
||
- RenderTarget:SceneTextures.GBufferF。
|
||
- DepthStencil:SceneTextures.Depth.Target。**ERenderTargetLoadAction::ELoad**、**FExclusiveDepthStencil::DepthRead_StencilNop**
|
||
|
||
### 管线状态
|
||
在FAnisotropyMeshProcessor::CollectPSOInitializers()中:
|
||
```c++
|
||
ETextureCreateFlags GBufferFCreateFlags;
|
||
EPixelFormat GBufferFPixelFormat = FSceneTextures::GetGBufferFFormatAndCreateFlags(GBufferFCreateFlags);
|
||
AddRenderTargetInfo(GBufferFPixelFormat, GBufferFCreateFlags, RenderTargetsInfo);
|
||
SetupDepthStencilInfo(PF_DepthStencil, SceneTexturesConfig.DepthCreateFlags, ERenderTargetLoadAction::ELoad,
|
||
ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop, RenderTargetsInfo);
|
||
```
|
||
|
||
```c++
|
||
void SetupDepthStencilInfo(
|
||
EPixelFormat DepthStencilFormat,
|
||
ETextureCreateFlags DepthStencilCreateFlags,
|
||
ERenderTargetLoadAction DepthTargetLoadAction,
|
||
ERenderTargetLoadAction StencilTargetLoadAction,
|
||
FExclusiveDepthStencil DepthStencilAccess,
|
||
FGraphicsPipelineRenderTargetsInfo& RenderTargetsInfo)
|
||
{
|
||
// Setup depth stencil state
|
||
RenderTargetsInfo.DepthStencilTargetFormat = DepthStencilFormat;
|
||
RenderTargetsInfo.DepthStencilTargetFlag = DepthStencilCreateFlags;
|
||
|
||
RenderTargetsInfo.DepthTargetLoadAction = DepthTargetLoadAction;
|
||
RenderTargetsInfo.StencilTargetLoadAction = StencilTargetLoadAction;
|
||
RenderTargetsInfo.DepthStencilAccess = DepthStencilAccess;
|
||
|
||
const ERenderTargetStoreAction StoreAction = EnumHasAnyFlags(RenderTargetsInfo.DepthStencilTargetFlag, TexCreate_Memoryless) ? ERenderTargetStoreAction::ENoAction : ERenderTargetStoreAction::EStore;
|
||
RenderTargetsInfo.DepthTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingDepth() ? StoreAction : ERenderTargetStoreAction::ENoAction;
|
||
RenderTargetsInfo.StencilTargetStoreAction = RenderTargetsInfo.DepthStencilAccess.IsUsingStencil() ? StoreAction : ERenderTargetStoreAction::ENoAction;
|
||
}
|
||
```
|
||
|
||
### ParallelRendering
|
||
AnisotropyPass支持并行渲染,并行渲染的判断逻辑为
|
||
```c++
|
||
const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread();
|
||
```
|
||
看得出判断条件是:
|
||
1. 显卡是否支持并行渲染。
|
||
2. CVar(r.ParallelBasePass)是否开启并行渲染。
|
||
|
||
从AnisotropyPass可以看得出并行渲染与一般渲染的差别在于:
|
||
1. FRenderTargetBinding绑定时的ERenderTargetLoadAction不同,**并行为ELoad**;**普通渲染为EClear**。
|
||
2. 调用AddPass添加了**ERDGPassFlags::SkipRenderPass**标记。
|
||
3. 并行渲染会在AddPass中构建**FRDGParallelCommandListSet ParallelCommandListSet**,并作为传入**DispatchDraw**;普通渲染传递nullptr。
|
||
4. 普通渲染会额外调用**SetStereoViewport(RHICmdList, View);**,本质是调用RHICmdList.SetViewport来设置View。
|
||
|
||
### Code
|
||
```c++
|
||
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderAnisotropyPass);
|
||
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_RenderAnisotropyPass, FColor::Emerald);
|
||
SCOPE_CYCLE_COUNTER(STAT_AnisotropyPassDrawTime);
|
||
RDG_GPU_STAT_SCOPE(GraphBuilder, RenderAnisotropyPass);
|
||
|
||
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
||
{
|
||
FViewInfo& View = Views[ViewIndex];
|
||
|
||
if (View.ShouldRenderView())
|
||
{
|
||
FParallelMeshDrawCommandPass& ParallelMeshPass = View.ParallelMeshDrawCommandPasses[EMeshPass::AnisotropyPass];
|
||
|
||
if (!ParallelMeshPass.HasAnyDraw())
|
||
{
|
||
continue;
|
||
}
|
||
|
||
View.BeginRenderView();
|
||
|
||
auto* PassParameters = GraphBuilder.AllocParameters<FAnisotropyPassParameters>();
|
||
PassParameters->View = View.GetShaderParameters();
|
||
PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneTextures.Depth.Target, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthRead_StencilNop);
|
||
|
||
ParallelMeshPass.BuildRenderingCommands(GraphBuilder, Scene->GPUScene, PassParameters->InstanceCullingDrawParams);
|
||
if (bDoParallelPass)
|
||
{
|
||
AddClearRenderTargetPass(GraphBuilder, SceneTextures.GBufferF);
|
||
|
||
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::ELoad);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("AnisotropyPassParallel"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass,
|
||
[this, &View, &ParallelMeshPass, PassParameters](const FRDGPass* InPass, FRHICommandListImmediate& RHICmdList)
|
||
{
|
||
FRDGParallelCommandListSet ParallelCommandListSet(InPass, RHICmdList, GET_STATID(STAT_CLP_AnisotropyPass), View, FParallelCommandListBindings(PassParameters));
|
||
|
||
ParallelMeshPass.DispatchDraw(&ParallelCommandListSet, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
});
|
||
}
|
||
else
|
||
{
|
||
PassParameters->RenderTargets[0] = FRenderTargetBinding(SceneTextures.GBufferF, ERenderTargetLoadAction::EClear);
|
||
|
||
GraphBuilder.AddPass(
|
||
RDG_EVENT_NAME("AnisotropyPass"),
|
||
PassParameters,
|
||
ERDGPassFlags::Raster,
|
||
[this, &View, &ParallelMeshPass, PassParameters](FRHICommandList& RHICmdList)
|
||
{
|
||
SetStereoViewport(RHICmdList, View);
|
||
|
||
ParallelMeshPass.DispatchDraw(nullptr, RHICmdList, &PassParameters->InstanceCullingDrawParams);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
``` |