UE 渲染入门 v3

概述

代码版本5.3
https://www.yuque.com/chenweilin-tryw7/gbfomp/ygwycvgmwps8niby?singleDoc# 《UE 渲染入门 v2》
上回书说到,渲染需要一个画布
然后 viewportclient->draw
最后通过 Flush_GameThread 刷新画布

FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->GetFeatureLevel() : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
{
    ViewportClient->Draw(this, &Canvas);
}
Canvas.Flush_GameThread();


Flush_GameThread 中

void FCanvas::Flush_GameThread(bool bForce)
{
    // 。。。

    // FCanvasSortElement compare class
    // 进行排序
    struct FCompareFCanvasSortElement
    {
        FORCEINLINE bool operator()( const FCanvasSortElement& A, const FCanvasSortElement& B ) const
        {
            return B.DepthSortKey < A.DepthSortKey;
        }
    };

    // sort the array of FCanvasSortElement entries so that higher sort keys render first (back-to-front)
    SortedElements.Sort(FCompareFCanvasSortElement());

    bool bEmitCanvasDrawEvents = GetEmitDrawEvents(); // 局部变量没用到?

    // Only create these render commands if we actually have something to draw.
    if(SortedElements.Num() > 0 && !GUsingNullRHI)
    {
        // 有东西要画

        FCanvasRenderThreadScope RenderThreadScope(*this);

        // iterate over the FCanvasSortElements in sorted order and render all the batched items for each entry
        for( int32 Idx=0; Idx < SortedElements.Num(); Idx++ )
        {
            FCanvasSortElement& SortElement = SortedElements[Idx];
            for( int32 BatchIdx=0; BatchIdx < SortElement.RenderBatchArray.Num(); BatchIdx++ )
            {
                FCanvasBaseRenderItem* RenderItem = SortElement.RenderBatchArray[BatchIdx]; // 要画的东西
                if( RenderItem )
                {
                    // mark current render target as dirty since we are drawing to it
                    bRenderTargetDirty |= RenderItem->Render_GameThread(this, RenderThreadScope);

                    RenderThreadScope.DeferredDelete(RenderItem); // 延迟删除
                }
            }
            SortElement.RenderBatchArray.Empty();
        }
    }
    else
    {
        // 。。。
    }

如果 SortedElements 有东西,会需要遍历所有对象。拿到 FCanvasSortElement,这同样也是一个array。
再次遍历获取 FCanvasBaseRenderItem。调用 RenderItem->Render_GameThread
同时上文,也提到了FCanvas中的 SortedElements 是正是画布中要画的对象
如看他的子类,用于渲染各种Item

class FMeshMaterialRenderItem : public FCanvasBaseRenderItem {};
class FMeshMaterialRenderItem2 : public FCanvasBaseRenderItem {};
class FCanvasBatchedElementRenderItem : public FCanvasBaseRenderItem {};
class FCanvasTileRendererItem : public FCanvasBaseRenderItem {};
class FCanvasTriangleRendererItem : public FCanvasBaseRenderItem {};

并且Data字段,例如三角形的 RendererItem

class FCanvasTriangleRendererItem : public FCanvasBaseRenderItem
{
    class FRenderData
    {
        // ...

        struct FTriangleInst
        {
            FCanvasUVTri Tri;
            FHitProxyId HitProxyId;
        };
        TArray<FTriangleInst> Triangles;
    };

    TSharedPtr<FRenderData> Data;
}

能够找到

/** Simple 2d triangle with UVs */
USTRUCT(BlueprintType)
struct FCanvasUVTri
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V0_Pos;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V0_UV;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FLinearColor V0_Color;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V1_Pos;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V1_UV;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FLinearColor V1_Color;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V2_Pos;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FVector2D V2_UV;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
    FLinearColor V2_Color;
};

要绘制的物体,传递向GPU数据的描述结构。

Render_GameThread 会先跑 FCanvasBatchedElementRenderItem,然后在按平台分

bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{   
    checkSlow(Data);
    bool bDirty=false;
    if( Data->BatchedElements.HasPrimsToDraw() ) // 批处理是否需要绘制图元
    {
        bDirty = true;

        // current render target set for the canvas
        // 获取 RT 和 gamma(用于后面颜色计算)
        const FRenderTarget* CanvasRenderTarget = Canvas->GetRenderTarget();
        float Gamma = 1.0f / CanvasRenderTarget->GetDisplayGamma();
        if ( Data->Texture && Data->Texture->bIgnoreGammaConversions )
        {
            Gamma = 1.0f;
        }

        // Render the batched elements.
        // 准备一个绘制参数
        struct FBatchedDrawParameters
        {
            FRenderData* RenderData;
            uint32 bHitTesting : 1;
            uint32 ViewportSizeX;
            uint32 ViewportSizeY;
            float DisplayGamma;
            uint32 AllowedCanvasModes;
            ERHIFeatureLevel::Type FeatureLevel;
            EShaderPlatform ShaderPlatform;
        };
        // all the parameters needed for rendering
        FBatchedDrawParameters DrawParameters =
        {
            Data,
            (uint32)(Canvas->IsHitTesting() ? 1 : 0),
            (uint32)CanvasRenderTarget->GetSizeXY().X,
            (uint32)CanvasRenderTarget->GetSizeXY().Y,
            Gamma,
            Canvas->GetAllowedModes(),
            Canvas->GetFeatureLevel(),
            Canvas->GetShaderPlatform()
        };

        // 添加一个pass,lambda在渲染线程去做
        RenderScope.AddPass(
            TEXT("CanvasBatchedElements"),
            [DrawParameters](FRHICommandList& RHICmdList)
        {
            FSceneView SceneView = FBatchedElements::CreateProxySceneView(DrawParameters.RenderData->Transform.GetMatrix(), FIntRect(0, 0, DrawParameters.ViewportSizeX, DrawParameters.ViewportSizeY));

            FMeshPassProcessorRenderState DrawRenderState;

            // disable depth test & writes
            DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
            DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());

            // draw batched items
            DrawParameters.RenderData->BatchedElements.Draw( // 《==========这里也要看看
                RHICmdList,
                DrawRenderState,
                DrawParameters.FeatureLevel,
                SceneView,
                DrawParameters.bHitTesting,
                DrawParameters.DisplayGamma);

            if(DrawParameters.AllowedCanvasModes & FCanvas::Allow_DeleteOnRender )
            {
                delete DrawParameters.RenderData;
            }
        });
    }
    if( Canvas->GetAllowedModes() & FCanvas::Allow_DeleteOnRender )
    {
        Data = NULL;
    }
    return bDirty;
}

我们的剧情从这里的 RenderScope.AddPass 展开

FRDGBuilder::AddPass

RenderScope.AddPass

    template <typename ExecuteLambdaType>
    void AddPass(const TCHAR* PassName, ExecuteLambdaType&& Lambda)
    {
        EnqueueRenderCommand(
            [PassName, InLambda = Forward<ExecuteLambdaType&&>(Lambda)]
        (FCanvasRenderContext& RenderContext) mutable
        {
            RenderContext.AddPass(RDG_EVENT_NAME("%s", PassName), Forward<ExecuteLambdaType&&>(InLambda));
        });
    }

在渲染线程,运行
GraphBuilder 就是 FRDGBuilder
RDG 是一个自动化 render pass 管理器

    template <typename ExecuteLambdaType, typename ParameterStructType>
    void AddPass(FRDGEventName&& PassName, const ParameterStructType* PassParameters, ExecuteLambdaType&& ExecuteLambda)
    {
        GraphBuilder.AddPass(Forward<FRDGEventName>(PassName), PassParameters, ERDGPassFlags::Raster,
            [LocalScissorRect = ScissorRect, LocalViewportRect = ViewportRect, LocalExecuteLambda = Forward<ExecuteLambdaType&&>(ExecuteLambda)](FRHICommandListImmediate& RHICmdList)
        {
            RHICmdList.SetViewport(static_cast<float>(LocalViewportRect.Min.X), static_cast<float>(LocalViewportRect.Min.Y), 0.0f, static_cast<float>(LocalViewportRect.Max.X), static_cast<float>(LocalViewportRect.Max.Y), 1.0f);

            if (LocalScissorRect.Area() > 0)
            {
                RHICmdList.SetScissorRect(true, static_cast<uint32>(LocalScissorRect.Min.X), static_cast<uint32>(LocalScissorRect.Min.Y), static_cast<uint32>(LocalScissorRect.Max.X), static_cast<uint32>(LocalScissorRect.Max.Y));
            }

            LocalExecuteLambda(RHICmdList);
        });
    }

这里最后会执行 LocalExecuteLambda(RHICmdList);

是一路传参数进来的
RenderScope.AddPass 第二个参数的lambda

bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{   
    checkSlow(Data);
    bool bDirty=false;
    if( Data->BatchedElements.HasPrimsToDraw() )
    {
        // 。。。

        RenderScope.AddPass(
            TEXT("CanvasBatchedElements"),
            [DrawParameters](FRHICommandList& RHICmdList) // 这里的lambda
        {
            FSceneView SceneView = FBatchedElements::CreateProxySceneView(DrawParameters.RenderData->Transform.GetMatrix(), FIntRect(0, 0, DrawParameters.ViewportSizeX, DrawParameters.ViewportSizeY));

            FMeshPassProcessorRenderState DrawRenderState;

            // disable depth test & writes
            DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
            DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());

            // draw batched items
            DrawParameters.RenderData->BatchedElements.Draw(
                RHICmdList,
                DrawRenderState,
                DrawParameters.FeatureLevel,
                SceneView,
                DrawParameters.bHitTesting,
                DrawParameters.DisplayGamma);

            if(DrawParameters.AllowedCanvasModes & FCanvas::Allow_DeleteOnRender )
            {
                delete DrawParameters.RenderData;
            }
        });
            }
    if( Canvas->GetAllowedModes() & FCanvas::Allow_DeleteOnRender )
    {
        Data = NULL;
    }
    return bDirty;
}

进行一些设置后会进入
BatchedElements.Draw
先不管addpass,假装已经加进了,看看是怎么draw的

FBatchedElements::Draw

bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
    const FRelativeViewMatrices RelativeMatrices = FRelativeViewMatrices::Create(View.ViewMatrices);

    // 。。。

参数传递进来了这个,里面有很多矩阵,带Inv的是求逆后的

struct FViewMatrices
{
    FViewMatrices()
    {
        ProjectionMatrix.SetIdentity();
        ProjectionNoAAMatrix.SetIdentity();
        InvProjectionMatrix.SetIdentity();
        ViewMatrix.SetIdentity();
        InvViewMatrix.SetIdentity();
        ViewProjectionMatrix.SetIdentity();
        InvViewProjectionMatrix.SetIdentity();
        HMDViewMatrixNoRoll.SetIdentity();
        TranslatedViewMatrix.SetIdentity();
        InvTranslatedViewMatrix.SetIdentity();
        OverriddenTranslatedViewMatrix.SetIdentity();
        OverriddenInvTranslatedViewMatrix.SetIdentity();
        TranslatedViewProjectionMatrix.SetIdentity();
        InvTranslatedViewProjectionMatrix.SetIdentity();
        ScreenToClipMatrix.SetIdentity();
        PreViewTranslation = FVector::ZeroVector;
        ViewOrigin = FVector::ZeroVector;
        ProjectionScale = FVector2D::ZeroVector;
        TemporalAAProjectionJitter = FVector2D::ZeroVector;
        ScreenScale = 1.f;
    }

    // 。。。
}
FRelativeViewMatrices FRelativeViewMatrices::Create(const FViewMatrices& Matrices, const FViewMatrices& PrevMatrices)
{
    FInitializer Initializer;
    Initializer.ViewToWorld = Matrices.GetInvViewMatrix();
    Initializer.WorldToView = Matrices.GetViewMatrix();
    Initializer.ViewToClip = Matrices.GetProjectionMatrix();
    Initializer.ClipToView = Matrices.GetInvProjectionMatrix();
    Initializer.PrevViewToWorld = PrevMatrices.GetInvViewMatrix();
    Initializer.PrevClipToView = PrevMatrices.GetInvProjectionMatrix();
    return Create(Initializer);
}

这里存了一下各种矩阵,然后create

 FRelativeViewMatrices FRelativeViewMatrices::Create(const FInitializer& Initializer)
{
    const double TileSize = FLargeWorldRenderScalar::GetTileSize(); // 获取瓦片大小,一种将屏幕分割为瓦片的渲染技术

    // We allow a fractional tile position here 我们允许分数瓦片
    // Tile offset is applied beofre WorldToView transform, but after ViewToWorld transform ?
    // This means that if we use a regular tile offset, the remaining offset in the relative matrices may become too large (>TileSize/2) ?
    // Allowing for a fractional tile lets us use the same value for both matrices
    // Quantization factor is somewhat arbitrary...controls distribution of precision between tile fraction and relative offset
    const FVector ViewOrigin = Initializer.ViewToWorld.GetOrigin(); // view的原点,世界坐标下的
    const FVector ViewOriginTile = FLargeWorldRenderScalar::MakeQuantizedTile(ViewOrigin, 8.0); // 相对于瓦片进行量化,获得一个相对于瓦片的位置

    // 生成一些矩阵
    FRelativeViewMatrices Result;
    Result.TilePosition = (FVector3f)ViewOriginTile;
    Result.RelativeWorldToView = FLargeWorldRenderScalar::MakeFromRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.WorldToView);
    Result.ViewToRelativeWorld = FLargeWorldRenderScalar::MakeToRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.ViewToWorld);
    Result.ViewToClip = FMatrix44f(Initializer.ViewToClip);
    Result.ClipToView = FMatrix44f(Initializer.ClipToView);
    Result.RelativeWorldToClip = Result.RelativeWorldToView * Result.ViewToClip;
    Result.ClipToRelativeWorld = Result.ClipToView * Result.ViewToRelativeWorld;

    Result.PrevViewToRelativeWorld = FLargeWorldRenderScalar::MakeClampedToRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.PrevViewToWorld);
    Result.PrevClipToView = FMatrix44f(Initializer.PrevClipToView);
    Result.PrevClipToRelativeWorld = Result.PrevClipToView * Result.PrevViewToRelativeWorld;
    return Result;
}

// 获取瓦片大小,一种将屏幕分割为瓦片的渲染技术
朴素的做法是把所有像素一起提交了,瓦片是把屏幕分成很多块,每块16x16 独立计算,当然同时也会引入瓦片之间的过渡物体。
公式是
瓦片的位置 = (ViewOrigin.XYZ / TileSize x 8 + 0.5) / 8

    static TScalar MakeQuantizedTile(double InValue, double InQuantization)
    {
        return static_cast<TScalar>(FMath::FloorToDouble((InValue / GetTileSize()) * InQuantization + 0.5) / InQuantization);
    }

回来,开头算了一些矩阵相关的数据

bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
    const FRelativeViewMatrices RelativeMatrices = FRelativeViewMatrices::Create(View.ViewMatrices);
    const FMatrix& WorldToClip = View.ViewMatrices.GetViewProjectionMatrix();
    const FMatrix& ClipToWorld = View.ViewMatrices.GetInvViewProjectionMatrix();
    const uint32 ViewportSizeX = View.UnscaledViewRect.Width();
    const uint32 ViewportSizeY = View.UnscaledViewRect.Height();

PSO

bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
    // 。。。

    FGraphicsPipelineStateInitializer GraphicsPSOInit;
    RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
    DrawRenderState.ApplyToPSO(GraphicsPSOInit);
    uint32 StencilRef = DrawRenderState.GetStencilRef();

    GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();

回顾一下DX12的PSO
用一个 D3D12_GRAPHICS_PIPELINE_STATE_DESC 这个结构
来创建 ID3D12PipelineState PSO对象

// Graphics pipeline struct that represents the latest versions of PSO subobjects currently supported by the RHI.
struct FD3D12_GRAPHICS_PIPELINE_STATE_DESC
{
    ID3D12RootSignature *pRootSignature;
    D3D12_SHADER_BYTECODE VS;
    D3D12_SHADER_BYTECODE MS;
    D3D12_SHADER_BYTECODE AS;
    D3D12_SHADER_BYTECODE PS;
    D3D12_SHADER_BYTECODE GS;
#if !D3D12_USE_DERIVED_PSO || D3D12_USE_DERIVED_PSO_SHADER_EXPORTS
    D3D12_BLEND_DESC BlendState;
#endif // #if !D3D12_USE_DERIVED_PSO || D3D12_USE_DERIVED_PSO_SHADER_EXPORTS
#if !D3D12_USE_DERIVED_PSO
    uint32 SampleMask;
    D3D12_RASTERIZER_DESC RasterizerState;
    D3D12_DEPTH_STENCIL_DESC1 DepthStencilState;
#endif // #if !D3D12_USE_DERIVED_PSO
    D3D12_INPUT_LAYOUT_DESC InputLayout;
    D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue;
    D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType;
    D3D12_RT_FORMAT_ARRAY RTFormatArray;
    DXGI_FORMAT DSVFormat;
    DXGI_SAMPLE_DESC SampleDesc;
    uint32 NodeMask;
    D3D12_CACHED_PIPELINE_STATE CachedPSO;
    D3D12_PIPELINE_STATE_FLAGS Flags;

    FD3D12_GRAPHICS_PIPELINE_STATE_STREAM PipelineStateStream() const;
#if PLATFORM_SUPPORTS_MESH_SHADERS
    FD3D12_MESH_PIPELINE_STATE_STREAM MeshPipelineStateStream() const;
#endif
};

这个 DESC描述,有VS PS BlendState 等。一个PSO就是一条管线状态。

看UE这边,名称都很好直接读出意思

class FGraphicsPipelineStateInitializer
{
    // 。。。

    FBoundShaderStateInput          BoundShaderState;
    FRHIBlendState*                 BlendState;
    FRHIRasterizerState*            RasterizerState;
    FRHIDepthStencilState*          DepthStencilState;
    FImmutableSamplerState          ImmutableSamplerState;

    EPrimitiveType                  PrimitiveType;
    uint32                          RenderTargetsEnabled;
    TRenderTargetFormats            RenderTargetFormats;
    TRenderTargetFlags              RenderTargetFlags;
    EPixelFormat                    DepthStencilTargetFormat;
    ETextureCreateFlags             DepthStencilTargetFlag;
    ERenderTargetLoadAction         DepthTargetLoadAction;
    ERenderTargetStoreAction        DepthTargetStoreAction;
    ERenderTargetLoadAction         StencilTargetLoadAction;
    ERenderTargetStoreAction        StencilTargetStoreAction;
    FExclusiveDepthStencil          DepthStencilAccess;
    uint16                          NumSamples;
    ESubpassHint                    SubpassHint;
    uint8                           SubpassIndex;
    EConservativeRasterization      ConservativeRasterization;
    bool                            bDepthBounds;
    uint8                           MultiViewCount;
    bool                            bHasFragmentDensityAttachment;
    EVRSShadingRate                 ShadingRate;

    // Note: these flags do NOT affect compilation of this PSO.
    // The resulting object is invariant with respect to whatever is set here, they are
    // behavior hints.
    // They do not participate in equality comparisons or hashing.
    union
    {
        struct
        {
            uint16                  Reserved            : 14;
            uint16                  bPSOPrecache            : 1;
            uint16                  bFromPSOFileCache   : 1;
        };
        uint16                      Flags;
    };

    // Cached hash off all state data provided at creation time (Only contains hash of data which influences the PSO precaching for the current platform)
    // Created from hashing the state data instead of the pointers which are used during fast runtime cache checking and compares
    uint64                          StatePrecachePSOHash;
};

第一个成员,这里面也有VS, PS 还有些不认识。这里的RHI意味着是跨平台的接口

struct FBoundShaderStateInput
{
    // 。。。
    FRHIVertexDeclaration* VertexDeclarationRHI = nullptr;
    FRHIVertexShader* VertexShaderRHI = nullptr;
    FRHIPixelShader* PixelShaderRHI = nullptr;

    // 。。。

    // 一些其他着色器
#if PLATFORM_SUPPORTS_MESH_SHADERS
    FRHIMeshShader* MeshShaderRHI = nullptr;
    FRHIAmplificationShader* AmplificationShaderRHI = nullptr;
#endif
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
    FRHIGeometryShader* GeometryShaderRHI = nullptr;
#endif
}

回来

bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
    // 。。。

    FGraphicsPipelineStateInitializer GraphicsPSOInit; // 后面大段都是为了填充这个变量,就像DX里面的 FD3D12_GRAPHICS_PIPELINE_STATE_DESC

    RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); // 把之前缓存的数据能赋值的赋值过来
    DrawRenderState.ApplyToPSO(GraphicsPSOInit); // 传递 BlendState 和 DepthStencilState
    uint32 StencilRef = DrawRenderState.GetStencilRef();
    GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();

ApplyCachedRenderTargets


    void ApplyCachedRenderTargets(
        FGraphicsPipelineStateInitializer& GraphicsPSOInit
        )
    {
        GraphicsPSOInit.RenderTargetsEnabled = PersistentState.CachedNumSimultanousRenderTargets; // 缓存的要渲染的目标数

        // 遍历
        for (uint32 i = 0; i < GraphicsPSOInit.RenderTargetsEnabled; ++i)
        {
            if (PersistentState.CachedRenderTargets[i].Texture) // 是贴图
            {
                GraphicsPSOInit.RenderTargetFormats[i] = UE_PIXELFORMAT_TO_UINT8(PersistentState.CachedRenderTargets[i].Texture->GetFormat()); // 格式,比如 PF_B8G8R8A8
                GraphicsPSOInit.RenderTargetFlags[i] = PersistentState.CachedRenderTargets[i].Texture->GetFlags();
            }
            else // 未知
            {
                GraphicsPSOInit.RenderTargetFormats[i] = PF_Unknown;
            }

            if (GraphicsPSOInit.RenderTargetFormats[i] != PF_Unknown)
            {
                GraphicsPSOInit.NumSamples = static_cast<uint16>(PersistentState.CachedRenderTargets[i].Texture->GetNumSamples()); // 采样次数
            }
        }

        if (PersistentState.CachedDepthStencilTarget.Texture) // DepthStencilTarget 是否存在纹理
        {
            GraphicsPSOInit.DepthStencilTargetFormat = PersistentState.CachedDepthStencilTarget.Texture->GetFormat();
            GraphicsPSOInit.DepthStencilTargetFlag = PersistentState.CachedDepthStencilTarget.Texture->GetFlags();
            const FRHITexture2DArray* TextureArray = PersistentState.CachedDepthStencilTarget.Texture->GetTexture2DArray();
        }
        else
        {
            GraphicsPSOInit.DepthStencilTargetFormat = PF_Unknown;
        }

        // 这些都是枚举,通过枚举读意思吧
        GraphicsPSOInit.DepthTargetLoadAction = PersistentState.CachedDepthStencilTarget.DepthLoadAction;
        GraphicsPSOInit.DepthTargetStoreAction = PersistentState.CachedDepthStencilTarget.DepthStoreAction;
        GraphicsPSOInit.StencilTargetLoadAction = PersistentState.CachedDepthStencilTarget.StencilLoadAction;
        GraphicsPSOInit.StencilTargetStoreAction = PersistentState.CachedDepthStencilTarget.GetStencilStoreAction();
        GraphicsPSOInit.DepthStencilAccess = PersistentState.CachedDepthStencilTarget.GetDepthStencilAccess();

        if (GraphicsPSOInit.DepthStencilTargetFormat != PF_Unknown)
        {
            GraphicsPSOInit.NumSamples =  static_cast<uint16>(PersistentState.CachedDepthStencilTarget.Texture->GetNumSamples());
        }

        GraphicsPSOInit.SubpassHint = PersistentState.SubpassHint;
        GraphicsPSOInit.SubpassIndex = PersistentState.SubpassIndex;
        GraphicsPSOInit.MultiViewCount = PersistentState.MultiViewCount;
        GraphicsPSOInit.bHasFragmentDensityAttachment = PersistentState.HasFragmentDensityAttachment;
    }

ApplyToPSO
传递一下值

    FORCEINLINE_DEBUGGABLE void ApplyToPSO(FGraphicsPipelineStateInitializer& GraphicsPSOInit) const
    {
        GraphicsPSOInit.BlendState = BlendState;
        GraphicsPSOInit.DepthStencilState = DepthStencilState;
    }
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
    // 。。。

    // 运行环境判断 return (!IsRunningCommandlet() || IsAllowCommandletRendering()) && !IsRunningDedicatedServer() && !(USE_NULL_RHI || bHasNullRHIOnCommandline);
    if (UNLIKELY(!FApp::CanEverRender()))
    {
        return false;
    }

    // 有东西要画 return( LineVertices.Num() || Points.Num() || Sprites.Num() || MeshElements.Num() || ThickLines.Num() || WireTris.Num() > 0 );
    if( HasPrimsToDraw() )
    {
        // ClipToWorld 是 ViewProject 求逆,看开头。把XYZ坐标轴,变换到世界空间。获取相机的世界位置
        FVector CameraX = ClipToWorld.TransformVector(FVector(1,0,0)).GetSafeNormal();
        FVector CameraY = ClipToWorld.TransformVector(FVector(0,1,0)).GetSafeNormal();
        FVector CameraZ = ClipToWorld.TransformVector(FVector(0,0,1)).GetSafeNormal();

        GraphicsPSOInit.PrimitiveType = PT_TriangleList; // 图元类型是 三角形列表

        // 光栅化状态,是干嘛的,看看DX的就想起来了
        // GPSDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); 
        // GPSDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; // fill mode
        GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();

如果要画这些东西

if( (LineVertices.Num() > 0 || Points.Num() > 0 || ThickLines.Num() > 0 || WireTris.Num() > 0)
            && (Filter & EBlendModeFilter::OpaqueAndMasked))
{

}

画图元

    // Lines/points don't support batched element parameters (yet!)
    // 准备一个批处理参数
    FBatchedElementParameters* BatchedElementParameters = NULL;

    // Draw the line elements.
    // 准备画线
    if( LineVertices.Num() > 0 )
    {
        // Prevent integer overflow to buffer overflow.
        // 线太多了
        if (LineVertices.Num() > UINT32_MAX / sizeof(FSimpleElementVertex))
        {
            UE_LOG(LogBatchedElements, Error, TEXT("Too many line vertices. Will overflow uint32 buffer size. LineVertices: %d"), LineVertices.Num());
            return false;
        }

        // 图元类型改成画线
        GraphicsPSOInit.PrimitiveType = PT_LineList;

        // Set the appropriate pixel shader parameters & shader state for the non-textured elements.
        PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, SE_BLEND_Opaque, RelativeMatrices, BatchedElementParameters, GWhiteTexture, bHitTesting, Gamma, NULL, &View);

        // 。。。
    }

PrepareShaders

/**
 * Sets the appropriate vertex and pixel shader.
 */
void FBatchedElements::PrepareShaders(
    FRHICommandList& RHICmdList,
    FGraphicsPipelineStateInitializer& GraphicsPSOInit,
    uint32 StencilRef,
    ERHIFeatureLevel::Type FeatureLevel,
    ESimpleElementBlendMode BlendMode,
    const FRelativeViewMatrices& ViewMatrices,
    FBatchedElementParameters* BatchedElementParameters,
    const FTexture* Texture,
    bool bHitTesting,
    float Gamma,
    const FDepthFieldGlowInfo* GlowInfo,
    const FSceneView* View,
    float OpacityMaskRefVal
    ) const
{
    // used to mask individual channels and desaturate
    // 单位矩阵的颜色权重
    FMatrix ColorWeights( FPlane(1, 0, 0, 0), FPlane(0, 1, 0, 0), FPlane(0, 0, 1, 0), FPlane(0, 0, 0, 0) );

    float GammaToUse = Gamma;

    ESimpleElementBlendMode MaskedBlendMode = SE_BLEND_Opaque; // 不透明模式

    // 在这个区间内,是 RGBA MASK (rgba遮罩相关的混合模式)
    if(BlendMode >= SE_BLEND_RGBA_MASK_START && BlendMode <= SE_BLEND_RGBA_MASK_END)
    {
        /*
        * Red, Green, Blue and Alpha color weights all initialized to 0
        */
        FPlane R( 0.0f, 0.0f, 0.0f, 0.0f );
        FPlane G( 0.0f, 0.0f, 0.0f, 0.0f );
        FPlane B( 0.0f, 0.0f, 0.0f, 0.0f );
        FPlane A( 0.0f, 0.0f, 0.0f, 0.0f );

        /*
        * Extract the color components from the in BlendMode to determine which channels should be active
        */
        uint32 BlendMask = ( ( uint32 )BlendMode ) - SE_BLEND_RGBA_MASK_START; // 把 START END 映射到 0~31, 然后用二进制位描述信息

        bool bRedChannel = ( BlendMask & ( 1 << 0 ) ) != 0;
        bool bGreenChannel = ( BlendMask & ( 1 << 1 ) ) != 0;
        bool bBlueChannel = ( BlendMask & ( 1 << 2 ) ) != 0;
        bool bAlphaChannel = ( BlendMask & ( 1 << 3 ) ) != 0;
        bool bDesaturate = ( BlendMask & ( 1 << 4 ) ) != 0;
        bool bAlphaOnly = bAlphaChannel && !bRedChannel && !bGreenChannel && !bBlueChannel;
        uint32 NumChannelsOn = ( bRedChannel ? 1 : 0 ) + ( bGreenChannel ? 1 : 0 ) + ( bBlueChannel ? 1 : 0 );
        GammaToUse = bAlphaOnly? 1.0f: Gamma;

        // If we are only to draw the alpha channel, make the Blend state opaque, to allow easy identification of the alpha values
        // 只有 alpha 通道
        if( bAlphaOnly )
        {
            MaskedBlendMode = SE_BLEND_Opaque; // 设置不透明模式

            // 设置 BlendState,DX里面是 GPSDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); 龙书第10章
            SetBlendState(RHICmdList, GraphicsPSOInit, MaskedBlendMode);

            R.W = G.W = B.W = 1.0f;
        }
        else
        {
            // 否则,就要判断是透明模式还是不透明模式了
            MaskedBlendMode = !bAlphaChannel ? SE_BLEND_Opaque : SE_BLEND_Translucent;  // If alpha channel is disabled, do not allow alpha blending
            SetBlendState(RHICmdList, GraphicsPSOInit, MaskedBlendMode);

            // Determine the red, green, blue and alpha components of their respective weights to enable that colours prominence
            R.X = bRedChannel ? 1.0f : 0.0f;
            G.Y = bGreenChannel ? 1.0f : 0.0f;
            B.Z = bBlueChannel ? 1.0f : 0.0f;
            A.W = bAlphaChannel ? 1.0f : 0.0f;

            /*
            * Determine if desaturation is enabled, if so, we determine the output colour based on the number of active channels that are displayed
            * e.g. if Only the red and green channels are being viewed, then the desaturation of the image will be divided by 2
            */
            if( bDesaturate && NumChannelsOn )
            {
                float ValR, ValG, ValB;
                ValR = R.X / NumChannelsOn;
                ValG = G.Y / NumChannelsOn;
                ValB = B.Z / NumChannelsOn;
                R = FPlane( ValR, ValG, ValB, 0 );
                G = FPlane( ValR, ValG, ValB, 0 );
                B = FPlane( ValR, ValG, ValB, 0 );
            }
        }

        ColorWeights = FMatrix( R, G, B, A );
    }

    // 。。。
}

这个 SetBlendState 是啥呢,在DX12中,龙书第10章
同时也可以看看我们自己小引擎是怎么用的

void FTransparentRenderLayer::BuildPSO()
{
    Super::BuildPSO();
    /*
     * 按PSO顺序渲染 PSO 1 -> PSO 2
     * PSO 2 是Src, PSO 1 是Dest
     * 不开混合,PSO2覆盖PSO1
     * RGB混合 = Src * SrcBlend(混合因子) [某种运算符] Dest * DestBlend(混合因子)
     * A混合 = Src * SrcBlendAlpha(混合因子) [某种运算符] Dest * DestBlendAlpha(混合因子)
     * 这里的运算符是指
     * enum D3D12_BLEND_OP
        {
            D3D12_BLEND_OP_ADD  = 1,
            D3D12_BLEND_OP_SUBTRACT = 2, Cd x Fd - Cs x Fs 注意这里是Dest - Src
            D3D12_BLEND_OP_REV_SUBTRACT = 3, Cs x Fs - Cd x Fd 注意这里是Src - Dest
            D3D12_BLEND_OP_MIN  = 4, min(Dest, Src)
            D3D12_BLEND_OP_MAX  = 5 max(Dest, Src)
        }   D3D12_BLEND_OP;
    * 另外还有逻辑操作符 下面的 LogicOp
    * 混合因子是 SrcBlend, DestBlend
     * enum D3D12_BLEND
    {
        D3D12_BLEND_ZERO    = 1,  F(0, 0, 0, 0)
        D3D12_BLEND_ONE = 2, F(1, 1, 1, 1)
        D3D12_BLEND_SRC_COLOR   = 3, F(Rs, Gs, Bs, As)
        D3D12_BLEND_INV_SRC_COLOR   = 4, F(1 - Rs, 1 - Gs, 1 - Bs, 1 - As)
        D3D12_BLEND_SRC_ALPHA   = 5, F(As, As, As, As)
        D3D12_BLEND_INV_SRC_ALPHA   = 6, F(1 - As, 1 - As, 1 - As, 1 - As)
        D3D12_BLEND_DEST_ALPHA  = 7, F(Ad, Ad, Ad, Ad)
        D3D12_BLEND_INV_DEST_ALPHA  = 8, F(1 - Ad, 1 - Ad, 1 - Ad, 1 - Ad)
        D3D12_BLEND_DEST_COLOR  = 9, F(Rd, Gd, Bd, Ad)
        D3D12_BLEND_INV_DEST_COLOR  = 10, F(1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad)
        D3D12_BLEND_SRC_ALPHA_SAT   = 11, F(min(As, 1 - Ad), min(As, 1 - Ad), min(As, 1 - Ad), 1)
        D3D12_BLEND_BLEND_FACTOR    = 14, F(Bs, Gs, Rs, As) 结合 OMSetBlendFactor 使用
        D3D12_BLEND_INV_BLEND_FACTOR    = 15, F(1 - Bs, 1 - Gs, 1 - Rs, 1 - As)
        D3D12_BLEND_SRC1_COLOR  = 16, F(Rs1, Gs1, Bs1, As1)
        D3D12_BLEND_INV_SRC1_COLOR  = 17, F(1 - Rs1, 1 - Gs1, 1 - Bs1, 1 - As1)
        D3D12_BLEND_SRC1_ALPHA  = 18, F(As1, As1, As1, As1)
        D3D12_BLEND_INV_SRC1_ALPHA  = 19, F(1 - As1, 1 - As1, 1 - As1, 1 - As1)
        D3D12_BLEND_ALPHA_FACTOR    = 20, F(Af, Af, Af, Af)
        D3D12_BLEND_INV_ALPHA_FACTOR    = 21 F(1 - Af, 1 - Af, 1 - Af, 1 - Af)
    }   D3D12_BLEND;

     * */

    D3D12_RENDER_TARGET_BLEND_DESC TransparentRenderTargetBlendDesc;
    TransparentRenderTargetBlendDesc.BlendEnable = true;
    TransparentRenderTargetBlendDesc.LogicOpEnable = false; // 逻辑运算, 与混合运算互斥

    TransparentRenderTargetBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
    TransparentRenderTargetBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;

    TransparentRenderTargetBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
    TransparentRenderTargetBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
    TransparentRenderTargetBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
    TransparentRenderTargetBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
    TransparentRenderTargetBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
    TransparentRenderTargetBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; // 遮罩

    IDirectXPipelineState->SetRenderTarget(0, TransparentRenderTargetBlendDesc);

    IDirectXPipelineState->SetFillMode(false);
    IDirectXPipelineState->Build(Transparent);
}

回来

/**
 * Sets the appropriate vertex and pixel shader.
 */
void FBatchedElements::PrepareShaders(
    FRHICommandList& RHICmdList,
    FGraphicsPipelineStateInitializer& GraphicsPSOInit,
    uint32 StencilRef,
    ERHIFeatureLevel::Type FeatureLevel,
    ESimpleElementBlendMode BlendMode,
    const FRelativeViewMatrices& ViewMatrices,
    FBatchedElementParameters* BatchedElementParameters,
    const FTexture* Texture,
    bool bHitTesting,
    float Gamma,
    const FDepthFieldGlowInfo* GlowInfo,
    const FSceneView* View,
    float OpacityMaskRefVal
    ) const
{
    // 。。。

    if( BatchedElementParameters != NULL )
    {
        // Use the vertex/pixel shader that we were given
        ensure(ViewMatrices.TilePosition.IsZero());
        BatchedElementParameters->BindShaders(RHICmdList, GraphicsPSOInit, FeatureLevel, FMatrix(ViewMatrices.RelativeWorldToClip), GammaToUse, ColorWeights, Texture);
    }
    else
    {

    }
}

如果有就build shader去了
但是上面代码看过来这里param确实先是null
如果不是null。
FBatchedElementParameters 有非常多的子类,都实现了 BindShaders 做各自的绑定
搜索 public FBatchedElementParameters

先挑一个看一下 BindShaders

/** Batched element parameters for texture paint shaders used for paint blending and paint mask generation */
class FMeshPaintBatchedElementParameters : public FBatchedElementParameters
{
public:
    /** Binds vertex and pixel shaders for this element */
    virtual void BindShaders(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit, ERHIFeatureLevel::Type InFeatureLevel, const FMatrix& InTransform, const float InGamma, const FMatrix& ColorWeights, const FTexture* Texture) override
    {
        MeshPaintRendering::SetMeshPaintShaders(RHICmdList, GraphicsPSOInit, InFeatureLevel, InTransform, InGamma, ShaderParams);
    }

public:

    /** Shader parameters */
    MeshPaintRendering::FMeshPaintShaderParameters ShaderParams;
};
    /** Binds the mesh paint vertex and pixel shaders to the graphics device */
    void SetMeshPaintShaders(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit, ERHIFeatureLevel::Type InFeatureLevel, const FMatrix& InTransform,
                                           const float InGamma,
                                           const FMeshPaintShaderParameters& InShaderParams )
    {
        // VS
        TShaderMapRef< TMeshPaintVertexShader > VertexShader(GetGlobalShaderMap(InFeatureLevel)); // 通过全局的shader map 获取shader

        // PS
        TMeshPaintPixelShader::FPermutationDomain MeshPaintPermutationVector;
        MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUsePaintBrush>(InShaderParams.PaintBrushTexture != nullptr);
        MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUseRotateTowardDirection>(InShaderParams.bRotateBrushTowardsDirection);
        MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUseFillBucket>(InShaderParams.bUseFillBucket);
        TShaderMapRef<TMeshPaintPixelShader> PixelShader(GetGlobalShaderMap(InFeatureLevel), MeshPaintPermutationVector);

        // 设置PSO的一些数值
        GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GMeshPaintVertexDeclaration.VertexDeclarationRHI;
        GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
        GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
        GraphicsPSOInit.PrimitiveType = PT_TriangleList;

        RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);

        // set pso
        SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);

        // Set vertex shader parameters
        SetShaderParametersLegacyVS(RHICmdList, VertexShader, FMatrix44f(InTransform) );    // LWC_TODO: Precision loss

        // Set pixel shader parameters
        SetShaderParametersLegacyPS(RHICmdList, PixelShader, InGamma, InShaderParams );

        // @todo MeshPaint: Make sure blending/color writes are setup so we can write to ALPHA if needed!
    }

差不多是我们DX 这个阶段

void FOpaqueShadowRenderLayer::BuildShader()
{
    std::vector<ShaderType::FShaderMacro> ShaderMacro;
    BuildingShaderMacro(ShaderMacro);

    std::vector<D3D_SHADER_MACRO> ShaderMacroD3D;
    ShaderType::ToD3DShaderMacro(ShaderMacro, ShaderMacroD3D);

    // 构建Shader
    VertexShader.BuildShaders(L"Shader/Shadow.hlsl", "VSMain", "vs_5_1", ShaderMacroD3D.data());
    PixelShader.BuildShaders(L"Shader/Shadow.hlsl", "PSMain", "ps_5_1", ShaderMacroD3D.data());
    IDirectXPipelineState->BindShader(VertexShader, PixelShader);

    // 输入布局
    InputElementDesc = {
        {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
        {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
    };
    IDirectXPipelineState->BindInputLayout(InputElementDesc.data(), InputElementDesc.size());
}

回来

/**
 * Sets the appropriate vertex and pixel shader.
 */
void FBatchedElements::PrepareShaders(
    FRHICommandList& RHICmdList,
    FGraphicsPipelineStateInitializer& GraphicsPSOInit,
    uint32 StencilRef,
    ERHIFeatureLevel::Type FeatureLevel,
    ESimpleElementBlendMode BlendMode,
    const FRelativeViewMatrices& ViewMatrices,
    FBatchedElementParameters* BatchedElementParameters,
    const FTexture* Texture,
    bool bHitTesting,
    float Gamma,
    const FDepthFieldGlowInfo* GlowInfo,
    const FSceneView* View,
    float OpacityMaskRefVal
    ) const
{
    // 。。。

    if( BatchedElementParameters != NULL )
    {
        // Use the vertex/pixel shader that we were given
        ensure(ViewMatrices.TilePosition.IsZero());
        BatchedElementParameters->BindShaders(RHICmdList, GraphicsPSOInit, FeatureLevel, FMatrix(ViewMatrices.RelativeWorldToClip), GammaToUse, ColorWeights, Texture);
    }
    else
    {

    }
}

我们要开始看else这部分了

/**
 * Sets the appropriate vertex and pixel shader.
 */
void FBatchedElements::PrepareShaders(
    FRHICommandList& RHICmdList,
    FGraphicsPipelineStateInitializer& GraphicsPSOInit,
    uint32 StencilRef,
    ERHIFeatureLevel::Type FeatureLevel,
    ESimpleElementBlendMode BlendMode,
    const FRelativeViewMatrices& ViewMatrices,
    FBatchedElementParameters* BatchedElementParameters,
    const FTexture* Texture,
    bool bHitTesting,
    float Gamma,
    const FDepthFieldGlowInfo* GlowInfo,
    const FSceneView* View,
    float OpacityMaskRefVal
    ) const
{
    // 。。。

    if( BatchedElementParameters != NULL )
    {
        // 。。。
    }
    else
    {
        // 获取和设置 VertexShader
        TShaderMapRef<FSimpleElementVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
        GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GSimpleElementVertexDeclaration.VertexDeclarationRHI;
        GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); // FD3D12VertexShader

        if (bHitTesting) // 是否命中测试,比如关卡编辑器我点一下地板
        {
            // 设置命中测试的混合模式
            SetHitTestingBlendState(RHICmdList, GraphicsPSOInit, BlendMode);

            // PixelShader
            TShaderMapRef<FSimpleElementHitProxyPS> HitTestingPixelShader(GetGlobalShaderMap(FeatureLevel));
            GraphicsPSOInit.BoundShaderState.PixelShaderRHI = HitTestingPixelShader.GetPixelShader();

            // 设置PSO 和 shader参数
            SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
            SetShaderParametersLegacyPS(RHICmdList, HitTestingPixelShader, Texture);
        }
        else
        {
            // 根据不同的混合模式设置不同的PSO参数,就不贴全部代码了

            if (BlendMode == SE_BLEND_Masked) // 混合遮罩
            {
                // 。。。
            }
            // render distance field elements
            else if (
                BlendMode == SE_BLEND_MaskedDistanceField || 
                BlendMode == SE_BLEND_MaskedDistanceFieldShadowed ||
                BlendMode == SE_BLEND_TranslucentDistanceField  ||
                BlendMode == SE_BLEND_TranslucentDistanceFieldShadowed)
            {
                // 。。。
            }
            else if(BlendMode == SE_BLEND_TranslucentAlphaOnly || BlendMode == SE_BLEND_TranslucentAlphaOnlyWriteAlpha)
            {
                // 。。。
            }
            else if(BlendMode >= SE_BLEND_RGBA_MASK_START && BlendMode <= SE_BLEND_RGBA_MASK_END)
            {
                // 。。。
            }
            else
            {
                // 。。。
            }
        }

        // Set the simple element vertex shader parameters
        SetShaderParametersLegacyVS(RHICmdList, VertexShader, ViewMatrices);
    }
}

FBatchedElements::DrawPointElements

调用
DrawPointElements(RHICmdList, WorldToClip, ViewportSizeX, ViewportSizeY, CameraX, CameraY);
绘制点
在游戏中拖一个 spline mesh actor 到场景中会触发

void FBatchedElements::DrawPointElements(FRHICommandList& RHICmdList, const FMatrix& Transform, const uint32 ViewportSizeX, const uint32 ViewportSizeY, const FVector& CameraX, const FVector& CameraY) const
{
    // Draw the point elements.
    if( Points.Num() > 0 )
    {
        // preallocate some memory to directly fill out
        // 为什么乘2,画点其实是画一个正方形,斜切一刀分成两个三角形
        const uint32 NumTris = ((uint32)Points.Num()) * 2; // even if Points.Num() == INT32_MAX, this won't overflow a uint32

        // 两个三角形,每个三角形三个点
        const uint32 NumVertices = NumTris * 3; // but this could

        // Prevent integer overflow to buffer overflow. 点太多了
        if (NumTris > (UINT32_MAX / 3) ||
            NumVertices > UINT32_MAX / sizeof(FSimpleElementVertex))
        {
            UE_LOG(LogBatchedElements, Error, TEXT("Too many points. Will overflow uint32 buffer size. NumPoints: %d"), Points.Num());
            return;
        }

        // 创建缓冲区
        FRHIResourceCreateInfo CreateInfo(TEXT("FBatchedElements_Points"));
        FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * NumVertices, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
        void* VerticesPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * NumVertices, RLM_WriteOnly);

        FSimpleElementVertex* PointVertices = (FSimpleElementVertex*)VerticesPtr;

        // 遍历要画的点(其实是画一个小正方形)
        uint32 VertIdx = 0;
        for(int32 PointIndex = 0; PointIndex < Points.Num(); PointIndex++)
        {
            // TODO: Support quad primitives here
            const FBatchedPoint& Point = Points[PointIndex];
            FVector4 TransformedPosition = Transform.TransformFVector4(Point.Position); // Transform 是 WorldToClip 矩阵,获得了一个裁剪空间的位置

            // 构造出其他全部的6个点
            // Generate vertices for the point such that the post-transform point size is constant.
            const uint32 ViewportMajorAxis = ViewportSizeX;//FMath::Max(ViewportSizeX, ViewportSizeY);
            const FVector WorldPointX = CameraX * Point.Size / ViewportMajorAxis * TransformedPosition.W; // 相机在世界空间的点,乘以(点的大小 占视口的比例) 相对于摄像机缩放了
            const FVector WorldPointY = CameraY * -Point.Size / ViewportMajorAxis * TransformedPosition.W;

            PointVertices[VertIdx + 0] = FSimpleElementVertex(Point.Position + WorldPointX - WorldPointY,FVector2D(1,0),Point.Color,Point.HitProxyColor);
            PointVertices[VertIdx + 1] = FSimpleElementVertex(Point.Position + WorldPointX + WorldPointY,FVector2D(1,1),Point.Color,Point.HitProxyColor);
            PointVertices[VertIdx + 2] = FSimpleElementVertex(Point.Position - WorldPointX - WorldPointY,FVector2D(0,0),Point.Color,Point.HitProxyColor);
            PointVertices[VertIdx + 3] = FSimpleElementVertex(Point.Position + WorldPointX + WorldPointY,FVector2D(1,1),Point.Color,Point.HitProxyColor);
            PointVertices[VertIdx + 4] = FSimpleElementVertex(Point.Position - WorldPointX - WorldPointY,FVector2D(0,0),Point.Color,Point.HitProxyColor);
            PointVertices[VertIdx + 5] = FSimpleElementVertex(Point.Position - WorldPointX + WorldPointY,FVector2D(0,1),Point.Color,Point.HitProxyColor);

            VertIdx += 6;
        }

        RHICmdList.UnlockBuffer(VertexBufferRHI);
        RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
        RHICmdList.DrawPrimitive(0, NumTris, 1); // 把图元,记录在命令队列中,
    }
}

画图元

            // Draw the line elements.
            if( LineVertices.Num() > 0 )
            {
                // Prevent integer overflow to buffer overflow.
                if (LineVertices.Num() > UINT32_MAX / sizeof(FSimpleElementVertex))
                {
                    UE_LOG(LogBatchedElements, Error, TEXT("Too many line vertices. Will overflow uint32 buffer size. LineVertices: %d"), LineVertices.Num());
                    return false;
                }

                GraphicsPSOInit.PrimitiveType = PT_LineList;

                // Set the appropriate pixel shader parameters & shader state for the non-textured elements.
                PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, SE_BLEND_Opaque, RelativeMatrices, BatchedElementParameters, GWhiteTexture, bHitTesting, Gamma, NULL, &View);

                FRHIResourceCreateInfo CreateInfo(TEXT("Lines"));
                FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * LineVertices.Num(), BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
                void* VoidPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * LineVertices.Num(), RLM_WriteOnly);

                FMemory::Memcpy(VoidPtr, LineVertices.GetData(), sizeof(FSimpleElementVertex) * LineVertices.Num());
                RHICmdList.UnlockBuffer(VertexBufferRHI);

                RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);

                int32 MaxVerticesAllowed = ((GDrawUPVertexCheckCount / sizeof(FSimpleElementVertex)) / 2) * 2;
                /*
                hack to avoid a crash when trying to render large numbers of line segments.
                */
                MaxVerticesAllowed = FMath::Min(MaxVerticesAllowed, 64 * 1024);

                int32 MinVertex=0;
                int32 TotalVerts = (LineVertices.Num() / 2) * 2;
                while( MinVertex < TotalVerts )
                {
                    int32 NumLinePrims = FMath::Min( MaxVerticesAllowed, TotalVerts - MinVertex ) / 2;
                    RHICmdList.DrawPrimitive(MinVertex, NumLinePrims, 1);
                    MinVertex += NumLinePrims * 2;
                }

                VertexBufferRHI.SafeRelease();
            }

回到这里,
我们了解了 PrepareShaders
上下的部分和DrawPointElements 很像了
创建一个buffer。
把图元塞进命令队列 DrawPrimitive

其他分支比较类似
比如这个绘制粗线条是绘制,长立方体

if ( ThickLines.Num() > 0 )
{
    // 绘制 4 个面(前,上,左,右),每个面两个三角形,就8个三角形。每个三角形3个点。 8 x 3
    FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * 8 * 3 * NumLinesThisBatch, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);

    for (int i = 0; i < NumLinesThisBatch; ++i)
    {
        // ...   
    }

    RHICmdList.UnlockBuffer(VertexBufferRHI);
    RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
    RHICmdList.DrawPrimitive(0, 8 * NumLinesThisBatch, 1);

}

线框三角形、

if (WireTris.Num() > 0)
{

}

绘制精灵(比如做特效)
什么是精灵

    struct FBatchedSprite
    {
        FVector Position;
        float SizeX;
        float SizeY;
        const FTexture* Texture;
        FLinearColor Color;
        FColor HitProxyColor;
        float U;
        float UL;
        float V;
        float VL;
        float OpacityMaskRefVal;
        uint8 BlendMode;
    };
    TArray<FBatchedSprite> Sprites;

X, Y 位置,贴图,颜色,UV,混合模式等。

if( Sprites.Num() > 0 )
{
    // Sprites don't support batched element parameters (yet!)
    FBatchedElementParameters* BatchedElementParameters = NULL;

    //Sort sprites by blend mode and texture
    struct FCompareSprite
    {
        explicit FCompareSprite(EBlendModeFilter::Type InFilter) : ValidBlendFilter(InFilter) {}

        FORCEINLINE bool operator()( const FBatchedElements::FBatchedSprite& SpriteA, const FBatchedElements::FBatchedSprite& SpriteB ) const
        {
            if (SpriteA.BlendMode != SpriteB.BlendMode)
            {
                const bool bBlendAValid = (ValidBlendFilter & SpriteA.BlendMode) != 0;
                const bool bBlendBValid = (ValidBlendFilter & SpriteB.BlendMode) != 0;
                if (bBlendAValid != bBlendBValid) return bBlendAValid; // we want valid blend modes to sort in front of invalid blend modes
                return SpriteA.BlendMode < SpriteB.BlendMode;
            }
            return SpriteA.Texture < SpriteB.Texture;
        }

        EBlendModeFilter::Type ValidBlendFilter;
    };

    // 获取精灵并排序
    TArray<FBatchedSprite> SortedSprites = Sprites;
    SortedSprites.Sort(FCompareSprite(Filter));

    // count the number of sprites that have valid blend modes
    // (they have been sorted to the front of the list)
    // 计算有合法混合模式的精灵数量
    int32 ValidSpriteCount = 0;
    while (ValidSpriteCount < SortedSprites.Num() &&
        (Filter & GetBlendModeFilter((ESimpleElementBlendMode)SortedSprites[ValidSpriteCount].BlendMode)))
    {
        ++ValidSpriteCount;
    }

    if (ValidSpriteCount > 0)
    {
        // Prevent integer overflow to buffer overflow.
        // 画的太多了
        if (ValidSpriteCount > UINT32_MAX / sizeof(FSimpleElementVertex) * 6)
        {
            UE_LOG(LogBatchedElements, Error, TEXT("Too many sprites. Will overflow uint32 buffer size. ValidSpriteCount: %d"), ValidSpriteCount);
            return false;
        }

        // 创建buffer
        FRHIResourceCreateInfo CreateInfo(TEXT("Sprites"));
        FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * ValidSpriteCount * 6, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
        void* VoidPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * ValidSpriteCount * 6, RLM_WriteOnly);
        FSimpleElementVertex* SpriteList = reinterpret_cast<FSimpleElementVertex*>(VoidPtr);

        // 遍历这么多数量
        for (int32 SpriteIndex = 0; SpriteIndex < ValidSpriteCount; SpriteIndex++)
        {
            // 看起来精灵就是一个有贴图的正方形
            const FBatchedSprite& Sprite = SortedSprites[SpriteIndex];
            FSimpleElementVertex* Vertex = &SpriteList[SpriteIndex * 6];

            // Compute the sprite vertices.
            const FVector WorldSpriteX = CameraX * Sprite.SizeX;
            const FVector WorldSpriteY = CameraY * -Sprite.SizeY * GProjectionSignY;

            const float UStart = Sprite.U / Sprite.Texture->GetSizeX();
            const float UEnd = (Sprite.U + Sprite.UL) / Sprite.Texture->GetSizeX(); // UL 是 U 方向的长度,VL 同理
            const float VStart = Sprite.V / Sprite.Texture->GetSizeY();
            const float VEnd = (Sprite.V + Sprite.VL) / Sprite.Texture->GetSizeY();

            Vertex[0] = FSimpleElementVertex(Sprite.Position + WorldSpriteX - WorldSpriteY, FVector2D(UEnd, VStart), Sprite.Color, Sprite.HitProxyColor);
            Vertex[1] = FSimpleElementVertex(Sprite.Position + WorldSpriteX + WorldSpriteY, FVector2D(UEnd, VEnd), Sprite.Color, Sprite.HitProxyColor);
            Vertex[2] = FSimpleElementVertex(Sprite.Position - WorldSpriteX - WorldSpriteY, FVector2D(UStart, VStart), Sprite.Color, Sprite.HitProxyColor);

            Vertex[3] = FSimpleElementVertex(Sprite.Position + WorldSpriteX + WorldSpriteY, FVector2D(UEnd, VEnd), Sprite.Color, Sprite.HitProxyColor);
            Vertex[4] = FSimpleElementVertex(Sprite.Position - WorldSpriteX - WorldSpriteY, FVector2D(UStart, VStart), Sprite.Color, Sprite.HitProxyColor);
            Vertex[5] = FSimpleElementVertex(Sprite.Position - WorldSpriteX + WorldSpriteY, FVector2D(UStart, VEnd), Sprite.Color, Sprite.HitProxyColor);
        }
        RHICmdList.UnlockBuffer(VertexBufferRHI);

        //First time init
        const FTexture* CurrentTexture = SortedSprites[0].Texture;
        ESimpleElementBlendMode CurrentBlendMode = (ESimpleElementBlendMode)SortedSprites[0].BlendMode;
        int32 BatchStartIndex = 0;
        float CurrentOpacityMask = SortedSprites[0].OpacityMaskRefVal;

        // Start loop at 1, since we've already started the first batch with the first sprite in the list
        for (int32 SpriteIndex = 1; SpriteIndex < ValidSpriteCount + 1; SpriteIndex++)
        {
            // Need to flush the current batch once we hit the end of the list, or if state of this sprite doesn't match current batch
            if (SpriteIndex == ValidSpriteCount ||
                CurrentTexture != SortedSprites[SpriteIndex].Texture ||
                CurrentBlendMode != SortedSprites[SpriteIndex].BlendMode ||
                CurrentOpacityMask != SortedSprites[SpriteIndex].OpacityMaskRefVal)
            {
                const int32 SpriteNum = SpriteIndex - BatchStartIndex;
                const int32 BaseVertex = BatchStartIndex * 6;
                const int32 PrimCount = SpriteNum * 2;
                PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, CurrentBlendMode, RelativeMatrices, BatchedElementParameters, CurrentTexture, bHitTesting, Gamma, NULL, &View, CurrentOpacityMask);
                RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
                RHICmdList.DrawPrimitive(BaseVertex, PrimCount, 1);

                // begin the next batch
                if (SpriteIndex < ValidSpriteCount)
                {
                    BatchStartIndex = SpriteIndex;
                    CurrentTexture = SortedSprites[SpriteIndex].Texture;
                    CurrentBlendMode = (ESimpleElementBlendMode)SortedSprites[SpriteIndex].BlendMode;
                    CurrentOpacityMask = SortedSprites[SpriteIndex].OpacityMaskRefVal;
                }
            }
        }
        VertexBufferRHI.SafeRelease();
    }
}
if( MeshElements.Num() > 0)
{

}

Render_GameThread END

上面一大段结束后
这个Draw结束了

bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{   
    checkSlow(Data);
    bool bDirty=false;
    if( Data->BatchedElements.HasPrimsToDraw() ) // 批处理是否需要绘制图元
    {
        // 。。。

        // 添加一个pass,lambda在渲染线程去做
        RenderScope.AddPass(
            TEXT("CanvasBatchedElements"),
            [DrawParameters](FRHICommandList& RHICmdList)
        {
            // 。。。
            DrawParameters.RenderData->BatchedElements.Draw(
                RHICmdList,
                DrawRenderState,
                DrawParameters.FeatureLevel,
                SceneView,
                DrawParameters.bHitTesting,
                DrawParameters.DisplayGamma);

            // 。。。
        });
    }

    // 。。。

    return bDirty;
}

RenderViewFamilies_RenderThread

前文的这个位置没有仔细深入

BeginRenderingViewFamilies 这个函数准备开始画3D场景了
这个函数前面通过 SendAllEndOfFrameUpdates 确保之前已经画完了
SendAllEndOfFrameUpdates 做了一些tick actor结束帧相关,并且把transform和其他数据传递给了GPU
然后做了一些 emmmmmmmmmmmmmmmmm
然后往渲染线程有了如下调用

void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
    // 。。。

    const uint64 DrawSceneEnqueue = FPlatformTime::Cycles64();
    ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)(
        [LocalSceneRenderers = CopyTemp(SceneRenderers), DrawSceneEnqueue](FRHICommandListImmediate& RHICmdList)
        {
            uint64 SceneRenderStart = FPlatformTime::Cycles64();
            const float StartDelayMillisec = FPlatformTime::ToMilliseconds64(SceneRenderStart - DrawSceneEnqueue);
            CSV_CUSTOM_STAT_GLOBAL(DrawSceneCommand_StartDelay, StartDelayMillisec, ECsvCustomStatOp::Set);
            RenderViewFamilies_RenderThread(RHICmdList, LocalSceneRenderers);
            FlushPendingDeleteRHIResources_RenderThread();
        });
}

RenderViewFamilies_RenderThread

渲染线程开始

/**
 * Helper function performing actual work in render thread.
 *
 * @param SceneRenderers    List of scene renderers to use for rendering.
 */
static void RenderViewFamilies_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    LLM_SCOPE(ELLMTag::SceneRender);

    // All renderers point to the same Scene (calling code asserts this)
    FScene* const Scene = SceneRenderers[0]->Scene;

    FSceneRenderer::RenderThreadBegin(RHICmdList, SceneRenderers);

RenderThreadBegin

void FSceneRenderer::RenderThreadBegin(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    CleanUp(RHICmdList);

CleanUp

void FSceneRenderer::CleanUp(FRHICommandListImmediate& RHICmdList)
{
    if (GSceneRenderCleanUpState.CompletionMode == ESceneRenderCleanUpMode::Immediate || GSceneRenderCleanUpState.Renderers.IsEmpty())
    {
        return;
    }

    if (!GSceneRenderCleanUpState.bWaitForTasksComplete)
    {
        switch (GSceneRenderCleanUpState.CompletionMode)
        {
        case ESceneRenderCleanUpMode::Deferred:
            WaitForTasksAndDeleteSceneRenderers(RHICmdList, GSceneRenderCleanUpState.Renderers);
            break;
        case ESceneRenderCleanUpMode::DeferredAndAsync:
            GSceneRenderCleanUpState.Task->Wait(ENamedThreads::GetRenderThread_Local()); // 等待线程完成
            break;
        }
    }

    FinishCleanUp(RHICmdList);
    GSceneRenderCleanUpState = {};
}

GSceneRenderCleanUpState 是场景清理状态
CompletionMode 是清理模式

struct FSceneRenderCleanUpState
{
    TArray<FSceneRenderer*> Renderers;
    FGraphEventRef Task;
    ESceneRenderCleanUpMode CompletionMode = ESceneRenderCleanUpMode::Immediate;
    bool bWaitForTasksComplete = false;
};

WaitForTasksAndDeleteSceneRenderers

static void WaitForTasksAndDeleteSceneRenderers(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    {
        QUICK_SCOPE_CYCLE_COUNTER(STAT_DeleteSceneRenderer_WaitForTasks);
        RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); // 等待为未完成任务
    }

    DeleteSceneRenderers(SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread::Render); // 删除场景的渲染
}

ImmediateFlush

RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly);

namespace EImmediateFlushType
{
    enum Type
    { 
        WaitForOutstandingTasksOnly  = 0, // 等待未完成的任务
        DispatchToRHIThread          = 1, // 刷新任务会给RHI线程
        FlushRHIThread               = 2, // 刷新RHI线程
        FlushRHIThreadFlushResources = 3 // 刷新RHI线程和资源
    };
};

那么
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); 就是等待未完成任务

FORCEINLINE_DEBUGGABLE void FRHICommandListImmediate::ImmediateFlush(EImmediateFlushType::Type FlushType)
{
    if (FlushType == EImmediateFlushType::WaitForOutstandingTasksOnly)
    {
        WaitForTasks();
    }
    else
    {
        // 。。。
    }
}

如果有任何为完成的任务,就 WaitUntilTasksComplete

void FRHICommandListImmediate::WaitForTasks()
{
    check(IsInRenderingThread());

    if (WaitOutstandingTasks.Num())
    {
        bool bAny = false;
        for (int32 Index = 0; Index < WaitOutstandingTasks.Num(); Index++)
        {
            if (!WaitOutstandingTasks[Index]->IsComplete())
            {
                bAny = true;
                break;
            }
        }

        if (bAny)
        {
            SCOPE_CYCLE_COUNTER(STAT_ExplicitWait);
            ENamedThreads::Type RenderThread_Local = ENamedThreads::GetRenderThread_Local();
            check(!FTaskGraphInterface::Get().IsThreadProcessingTasks(RenderThread_Local));
            FTaskGraphInterface::Get().WaitUntilTasksComplete(WaitOutstandingTasks, RenderThread_Local);
        }

        WaitOutstandingTasks.Reset();
    }
}

WaitForTasksAndDeleteSceneRenderers

回到

static void WaitForTasksAndDeleteSceneRenderers(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    {
        QUICK_SCOPE_CYCLE_COUNTER(STAT_DeleteSceneRenderer_WaitForTasks);
        RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); // 等待为未完成任务
    }

    DeleteSceneRenderers(SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread::Render); // 删除场景的渲染
}

DeleteSceneRenderers

static void DeleteSceneRenderers(const TArray<FSceneRenderer*>& SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread WaitThread)
{
    SCOPED_NAMED_EVENT_TEXT("DeleteSceneRenderer", FColor::Red);

    for (FSceneRenderer* SceneRenderer : SceneRenderers)
    {
        // Wait for all dispatched shadow mesh draw tasks.
        for (int32 PassIndex = 0; PassIndex < SceneRenderer->DispatchedShadowDepthPasses.Num(); ++PassIndex)
        {
            SceneRenderer->DispatchedShadowDepthPasses[PassIndex]->WaitForTasksAndEmpty(WaitThread); // 等待所有阴影网格
        }

        for (FViewInfo& View : SceneRenderer->Views)
        {
            View.WaitForTasks(WaitThread); // 视图绘制完成
        }
    }

    FViewInfo::DestroyAllSnapshots(WaitThread); // 删除所有拍照

    for (FSceneRenderer* SceneRenderer : SceneRenderers)
    {
        delete SceneRenderer;
    }
}

RenderThreadBegin

回来

void FSceneRenderer::RenderThreadBegin(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    CleanUp(RHICmdList);

    // Initialize "AllFamilyViews" and "AllFamilies" array in each scene renderer.  Initialize array in first SceneRenderer, then copy to remaining.
    // 把所有的 ViewInfo 加到0号里面
    for (FSceneRenderer* SceneRenderer : SceneRenderers)
    {
        for (const FViewInfo& ViewInfo : SceneRenderer->Views)
        {
            SceneRenderers[0]->AllFamilyViews.Add(&ViewInfo);
        }
        SceneRenderers[0]->AllFamilies.Add(&SceneRenderer->ViewFamily);
    }
    // 然后复制给后面的
    for (int32 RendererIndex = 1; RendererIndex < SceneRenderers.Num(); RendererIndex++)
    {
        SceneRenderers[RendererIndex]->AllFamilyViews = SceneRenderers[0]->AllFamilyViews;
        SceneRenderers[RendererIndex]->AllFamilies = SceneRenderers[0]->AllFamilies;
    }

    // 特效
    FScene* Scene = SceneRenderers[0]->Scene;
    // Cache the FXSystem for the duration of the scene render
    // UWorld::CleanupWorldInternal() will mark the system as pending kill on the GameThread and then enqueue a delete command
    //-TODO: The call to IsPendingKill should no longer be required as we are caching & using within a single render command
    class FFXSystemInterface* FXSystem = Scene ? Scene->FXSystem : nullptr;
    if (FXSystem && FXSystem->IsPendingKill())
    {
        FXSystem = nullptr;
    }

    for (FSceneRenderer* SceneRenderer : SceneRenderers)
    {
        SceneRenderer->FXSystem = FXSystem;
    }
}

RenderViewFamilies_RenderThread

回来
RenderThreadBegin 看完了
如果前面有没有渲染完成的就等待(阴影网格, 视图,删除所有拍照)
然后手机一下view family
初始化一下FXSystem
有开始,那么最下面也有END

/**
 * Helper function performing actual work in render thread.
 *
 * @param SceneRenderers    List of scene renderers to use for rendering.
 */
static void RenderViewFamilies_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
    LLM_SCOPE(ELLMTag::SceneRender);

    // All renderers point to the same Scene (calling code asserts this)
    FScene* const Scene = SceneRenderers[0]->Scene;

    FSceneRenderer::RenderThreadBegin(RHICmdList, SceneRenderers);

    bool bAnyShowHitProxies = false; // 是否有任何命中代理

#if WITH_DEBUG_VIEW_MODES
    // Flag so we only call FGPUSkinCacheVisualizationData::Update and draw the visualization text once
    bool bUpdatedGPUSkinCacheVisualization = false; // 是否已经更新了GPU的蒙皮缓存
#endif

    // update any resources that needed a deferred update
    FDeferredUpdateResource::UpdateResources(RHICmdList); // 更新所有需要延迟更新的资源,怎么更新的?

    for (FSceneRenderer* SceneRenderer : SceneRenderers)
    {
        const ERHIFeatureLevel::Type FeatureLevel = SceneRenderer->FeatureLevel;
        FSceneViewFamily& ViewFamily = SceneRenderer->ViewFamily;

        // 又看到了 FRDGBuilder, 构造一个
        FRDGBuilder GraphBuilder(
            RHICmdList,
            RDG_EVENT_NAME("SceneRenderer_%s(ViewFamily=%s)",
                ViewFamily.EngineShowFlags.HitProxies ? TEXT("RenderHitProxies") : TEXT("Render"),
                ViewFamily.bResolveScene ? TEXT("Primary") : TEXT("Auxiliary")
            ),
            FSceneRenderer::GetRDGParalelExecuteFlags(FeatureLevel) // 是否允许并行的执行 ERDGBuilderFlags
        );

        // We need to execute the pre-render view extensions before we do any view dependent work.
        // 执行视图扩展,用户可以在这里添加扩展,并在这里执行
        FSceneRenderer::ViewExtensionPreRender_RenderThread(GraphBuilder, SceneRenderer);

        SCOPE_CYCLE_COUNTER(STAT_TotalSceneRenderingTime);
        SCOPED_NAMED_EVENT_TCHAR_CONDITIONAL(*ViewFamily.ProfileDescription, FColor::Red, !ViewFamily.ProfileDescription.IsEmpty());
        const uint64 FamilyRenderStart = FPlatformTime::Cycles64();

#if WITH_DEBUG_VIEW_MODES
        // 允许GPU蒙皮缓存的可视化?比如勾选一些调试选项,会渲染不一样的shader
        const bool bAllowGPUSkinCacheVisualization = AllowDebugViewShaderMode(DVSM_VisualizeGPUSkinCache, ViewFamily.GetShaderPlatform(), ViewFamily.GetFeatureLevel());
        if (bAllowGPUSkinCacheVisualization && SceneRenderer->Views.Num() > 0)
        {
            FViewInfo& View = SceneRenderer->Views[0];

            FGPUSkinCacheVisualizationData& VisualizationData = GetGPUSkinCacheVisualizationData();

            // Only run visualization update once, but set debug flags for all view families if the mode is active
            // Note VisualizationData.Update needs to be called per frame, as || lazy evaluation is used, so need to do it before evaluating VisualizeGPUSkinCache flag
            if (bUpdatedGPUSkinCacheVisualization || VisualizationData.Update(View.CurrentGPUSkinCacheVisualizationMode) || ViewFamily.EngineShowFlags.VisualizeGPUSkinCache)
            {
                // When activating visualization from the command line, enable VisualizeGPUSkinCache.
                ViewFamily.EngineShowFlags.SetVisualizeGPUSkinCache(true);
                ViewFamily.DebugViewShaderMode = DVSM_VisualizeGPUSkinCache;

                // Only draw the visualization info text once.
                if (!bUpdatedGPUSkinCacheVisualization)
                {
                    SceneRenderer->DrawGPUSkinCacheVisualizationInfoText(); // 绘制一些数据文本到屏幕上
                    bUpdatedGPUSkinCacheVisualization = true;
                }
            }
        }
#endif  // WITH_DEBUG_VIEW_MODES

#if WITH_MGPU
        if (ViewFamily.bForceCopyCrossGPU) // copy 多 gpu
        {
            GraphBuilder.EnableForceCopyCrossGPU();
        }
#endif

        if (ViewFamily.EngineShowFlags.HitProxies) // 是否要限速命中代理
        {
            // Render the scene's hit proxies.
            SceneRenderer->RenderHitProxies(GraphBuilder);
            bAnyShowHitProxies = true;
        }
        else
        {
            // Render the scene. // 重点,后文展开 !!!!!!!!!!!!!!!!!
            SceneRenderer->Render(GraphBuilder);
        }

        SceneRenderer->FlushCrossGPUFences(GraphBuilder);

        GraphBuilder.Execute(); // RDG Execute 看他!!!!!!!!!!!!!!!!!!!!!

        if (SceneRenderer->ViewFamily.ProfileSceneRenderTime)
        {
            *SceneRenderer->ViewFamily.ProfileSceneRenderTime = (float)FPlatformTime::ToSeconds64(FPlatformTime::Cycles64() - FamilyRenderStart);
        }
    }

发束,头发渲染

    {
        CSV_SCOPED_TIMING_STAT_EXCLUSIVE(PostRenderCleanUp);

        if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && (SceneRenderers[0]->AllFamilyViews.Num() > 0) && !bAnyShowHitProxies)
        {
            FHairStrandsBookmarkParameters Parameters;
            CreateHairStrandsBookmarkParameters(Scene, SceneRenderers[0]->Views, SceneRenderers[0]->AllFamilyViews, Parameters);
            if (Parameters.HasInstances())
            {
                RunHairStrandsBookmark(EHairStrandsBookmark::ProcessEndOfFrame, Parameters);
            }
        }

        // Only reset per-frame scene state once all views have processed their frame, including those in planar reflections
        for (int32 CacheType = 0; CacheType < UE_ARRAY_COUNT(SceneRenderers[0]->Scene->DistanceFieldSceneData.PrimitiveModifiedBounds); CacheType++)
        {
            ResetAndShrinkModifiedBounds(Scene->DistanceFieldSceneData.PrimitiveModifiedBounds[CacheType]);
        }

        // Immediately issue EndFrame() for all extensions in case any of the outstanding tasks they issued getting out of this frame
        extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;

        for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
        {
            Extension->EndFrame();
        }
    }

光线追踪

#if RHI_RAYTRACING
    // Release the ray tracing scene resources if ray tracing wasn't used
    if (!Scene->RayTracingScene.bUsedThisFrame)
    {
        Scene->RayTracingScene.ResetAndReleaseResources();
    }
#endif  // RHI_RAYTRACING

统计相关,内存啥的

#if STATS
    {
        QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderViewFamily_RenderThread_MemStats);

        // Update scene memory stats that couldn't be tracked continuously
        SET_MEMORY_STAT(STAT_RenderingSceneMemory, Scene->GetSizeBytes());

        SIZE_T ViewStateMemory = 0;
        for (FSceneRenderer* SceneRenderer : SceneRenderers)
        {
            for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ViewIndex++)
            {
                if (SceneRenderer->Views[ViewIndex].State)
                {
                    ViewStateMemory += SceneRenderer->Views[ViewIndex].State->GetSizeBytes();
                }
            }
        }
        SET_MEMORY_STAT(STAT_ViewStateMemory, ViewStateMemory);
        SET_MEMORY_STAT(STAT_LightInteractionMemory, FLightPrimitiveInteraction::GetMemoryPoolSize());
    }
#endif

FRendererOnScreenNotification 的一个事件广播一下
统计相关
RenderThreadEnd 之前函数开头也有个begin

#if !UE_BUILD_SHIPPING
    // Update on screen notifications.
    FRendererOnScreenNotification::Get().Broadcast();
#endif

#if STATS
    QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderViewFamily_RenderThread_RHIGetGPUFrameCycles);
    if (FPlatformProperties::SupportsWindowedMode() == false)
    {
        /** Update STATS with the total GPU time taken to render the last frame. */
        SET_CYCLE_COUNTER(STAT_TotalGPUFrameTime, RHIGetGPUFrameCycles());
    }
#endif

    FSceneRenderer::RenderThreadEnd(RHICmdList, SceneRenderers);
}

FSceneRenderer::RenderThreadEnd

渲染线程结束
大概看了一下,主要是资源的释放,和等待xxx结束

Lumen 全局环境光 光线追踪

光线追踪的思路:

  1. 从光源发射射线,反弹到摄像机就结束,会造成很多浪费,因为很多光没有打到视口上(会有噪点)。
  2. 从摄像机发生射线,开始逆推到光源。反射次数越高,消耗越大。

解决的问题是环境光。

lumen 会经历几个阶段

  • 虚拟光源
  • 光线追踪
  • 光栅化更新
  • 动态更新
        if (ViewFamily.EngineShowFlags.HitProxies)
        {
            // Render the scene's hit proxies.
            SceneRenderer->RenderHitProxies(GraphBuilder);
            bAnyShowHitProxies = true;
        }
        else
        {
            // Render the scene.
            SceneRenderer->Render(GraphBuilder);
        }

从上一回的
SceneRenderer->Render(GraphBuilder);
开始

这东西有两个子类
一个延迟渲染,一个移动端的渲染

class FDeferredShadingSceneRenderer : public FSceneRenderer {};
class FMobileSceneRenderer : public FSceneRenderer {};

FDeferredShadingSceneRenderer::Render

是否渲染,获取渲染输出结构,是否开启nanite

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    if (!ViewFamily.EngineShowFlags.Rendering)
    {
        return;
    }

    // If this is scene capture rendering depth pre-pass, we'll take the shortcut function RenderSceneCaptureDepth if optimization switch is on.
    const ERendererOutput RendererOutput = GetRendererOutput();

    const bool bNaniteEnabled = IsNaniteEnabled();

    // 捕获渲染过程,以便于开发者进行调试和分析
#if !UE_BUILD_SHIPPING
    RenderCaptureInterface::FScopedCapture RenderCapture(GCaptureNextDeferredShadingRendererFrame-- == 0, GraphBuilder, TEXT("DeferredShadingSceneRenderer"));
    // Prevent overflow every 2B frames.
    GCaptureNextDeferredShadingRendererFrame = FMath::Max(-1, GCaptureNextDeferredShadingRendererFrame);
#endif

FSceneRenderer::UpdateScene

FInitViewTaskDatas InitViewTaskDatas = UpdateScene(GraphBuilder, FGlobalDynamicBuffers(DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews));
IVisibilityTaskData* FSceneRenderer::UpdateScene(FRDGBuilder& GraphBuilder, FGlobalDynamicBuffers GlobalDynamicBuffers)
{
    // Needs to run before UpdateAllPrimitiveSceneInfos, as it may call OnVirtualTextureDestroyedCB, which modifies the uniform
    // expression cache. UpdateAllPrimitiveSceneInfos generates mesh draw commands, which use the uniform expression cache.
    FVirtualTextureSystem::Get().CallPendingCallbacks();

    /**
      * UpdateStaticMeshes removes and re-creates cached FMeshDrawCommands.  If there are multiple scene renderers being run together,
      * we need allocated pipeline state IDs not to change, in case async tasks related to prior scene renderers are still in flight
      * (FSubmitNaniteMaterialPassCommandsAnyThreadTask or FDrawVisibleMeshCommandsAnyThreadTask).  So we freeze pipeline state IDs,
      * preventing them from being de-allocated even if their reference count temporarily goes to zero during calls to
      * RemoveCachedMeshDrawCommands followed by CacheMeshDrawCommands (or the Nanite equivalent).
      *
      * Note that on the first scene renderer, we do want to de-allocate items, so they can be permanently released if no longer in use
      * (for example, if there was an impactful change to a render proxy by game logic), but the assumption is that sequential renders
      * of the same scene from different views can't make such changes.
      */
    if (!bIsFirstSceneRenderer)
    {
        FGraphicsMinimalPipelineStateId::FreezeIdTable(true);
    }

    // 更新所以场景基源组件的异步操作
    Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder, GetUpdateAllPrimitiveSceneInfosAsyncOps());

    // 。。。
}
inline EUpdateAllPrimitiveSceneInfosAsyncOps GetUpdateAllPrimitiveSceneInfosAsyncOps()
{
    EUpdateAllPrimitiveSceneInfosAsyncOps AsyncOps = EUpdateAllPrimitiveSceneInfosAsyncOps::None;

    if (GAsyncCreateLightPrimitiveInteractions > 0)
    {
        AsyncOps |= EUpdateAllPrimitiveSceneInfosAsyncOps::CreateLightPrimitiveInteractions;
    }

    if (GAsyncCacheMeshDrawCommands > 0)
    {
        AsyncOps |= EUpdateAllPrimitiveSceneInfosAsyncOps::CacheMeshDrawCommands;
    }

    return AsyncOps;
}
enum class EUpdateAllPrimitiveSceneInfosAsyncOps
{
    None = 0,

    // Cached mesh draw commands are cached asynchronously. 缓存mesh更新命令
    CacheMeshDrawCommands = 1 << 0,

    // Light primitive interactions are created asynchronously. 创建灯光基元交互
    CreateLightPrimitiveInteractions = 1 << 1,

    All = CacheMeshDrawCommands | CreateLightPrimitiveInteractions
};

然后调用 UpdateAllPrimitiveSceneInfos
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder, GetUpdateAllPrimitiveSceneInfosAsyncOps());
这个如何更新内容很多,后文细说

回来

IVisibilityTaskData* FSceneRenderer::UpdateScene(FRDGBuilder& GraphBuilder, FGlobalDynamicBuffers GlobalDynamicBuffers)
{
    // 。。。

    if (!bIsFirstSceneRenderer)
    {
        FGraphicsMinimalPipelineStateId::FreezeIdTable(false);
    }

    // 设置最终要绘制的矩形框
    PrepareViewRectsForRendering(GraphBuilder.RHICmdList);

    InitializeSceneTexturesConfig(ViewFamily.SceneTexturesConfig, ViewFamily);
    FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
    FSceneTexturesConfig::Set(SceneTexturesConfig);

    PrepareViewStateForVisibility(SceneTexturesConfig);

    return LaunchVisibilityTasks(GraphBuilder.RHICmdList, *this, GlobalDynamicBuffers);
}

FSceneRenderer::PrepareViewStateForVisibility

发布与未发布情况下对于时间冻结的操作

void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
#if UE_BUILD_SHIPPING
    const bool bFreezeTemporalHistories = false;
    const bool bFreezeTemporalSequences = false;
#else
    bool bFreezeTemporalHistories = CVarFreezeTemporalHistories.GetValueOnRenderThread() != 0;

    static int32 CurrentFreezeTemporalHistoriesProgress = 0;
    if (CurrentFreezeTemporalHistoriesProgress != CVarFreezeTemporalHistoriesProgress.GetValueOnRenderThread())
    {
        bFreezeTemporalHistories = false;
        CurrentFreezeTemporalHistoriesProgress = CVarFreezeTemporalHistoriesProgress.GetValueOnRenderThread();
    }

    bool bFreezeTemporalSequences = bFreezeTemporalHistories || CVarFreezeTemporalSequences.GetValueOnRenderThread() != 0;
#endif

路径追踪失效计时器的一个值


    // Load this field once so it has a consistent value for all views (and to avoid the atomic load in the loop).
    // While the value may not be perfectly in sync when we render other view families, this is ok as this
    // invalidation mechanism is only used for interactive rendering where we expect to be constantly drawing the scene.
    // Therefore it is acceptable for some view families to be a frame or so behind others.
    uint32 CurrentPathTracingInvalidationCounter = Scene->PathTracingInvalidationCounter.Load();

然后开始遍历所有视图,遍历完函数就结束了
这里面主要是一些【运动模糊相关的】

    // Setup motion blur parameters (also check for camera movement thresholds)
    for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
    {
        FViewInfo& View = Views[ViewIndex];
        FSceneViewState* ViewState = View.ViewState;

        check(View.VerifyMembersChecks());

        // Setup global dither fade in and fade out uniform buffers.
        // 全局抖动淡入淡出 的 uniform buffers
        {
            FDitherUniformShaderParameters DitherUniformShaderParameters;
            DitherUniformShaderParameters.LODFactor = View.GetTemporalLODTransition();
            View.DitherFadeOutUniformBuffer = FDitherUniformBufferRef::CreateUniformBufferImmediate(DitherUniformShaderParameters, UniformBuffer_SingleFrame);

            DitherUniformShaderParameters.LODFactor = View.GetTemporalLODTransition() - 1.0f;
            View.DitherFadeInUniformBuffer = FDitherUniformBufferRef::CreateUniformBufferImmediate(DitherUniformShaderParameters, UniformBuffer_SingleFrame);
        }

        // Once per render increment the occlusion frame counter.
        if (ViewState)
        {
            ViewState->OcclusionFrameCounter++;
        }

        // HighResScreenshot should get best results so we don't do the occlusion optimization based on the former frame
        // 比如截图的时候,和这些情况下,要最好的结果,就需要设置true,会关闭运动模糊
        extern bool GIsHighResScreenshot;
        const bool bIsHitTesting = ViewFamily.EngineShowFlags.HitProxies;
        // Don't test occlusion queries in collision viewmode as they can be bigger then the rendering bounds.
        const bool bCollisionView = ViewFamily.EngineShowFlags.CollisionVisibility || ViewFamily.EngineShowFlags.CollisionPawn;
        if (GIsHighResScreenshot || !DoOcclusionQueries() || bIsHitTesting || bCollisionView || ViewFamily.EngineShowFlags.DisableOcclusionQueries)
        {
            View.bDisableQuerySubmissions = true;
            View.bIgnoreExistingQueries = true;
        }

        // set up the screen area for occlusion
        // 设置屏幕运动模糊的范围
        {
            float OcclusionPixelMultiplier = 1.0f;
            if (UseDownsampledOcclusionQueries())
            {
                OcclusionPixelMultiplier = 1.0f / static_cast<float>(FMath::Square(SceneTexturesConfig.SmallDepthDownsampleFactor)); // 1 / 根号系数
            }
            float NumPossiblePixels = static_cast<float>(View.ViewRect.Width() * View.ViewRect.Height()) * OcclusionPixelMultiplier;
            View.OneOverNumPossiblePixels = NumPossiblePixels > 0.0 ? 1.0f / NumPossiblePixels : 0.0f;
        }

        // Still need no jitter to be set for temporal feedback on SSR (it is enabled even when temporal AA is off).
        // 检查
        check(View.TemporalJitterPixels.X == 0.0f);
        check(View.TemporalJitterPixels.Y == 0.0f);

        // Cache the projection matrix b        
        // Cache the projection matrix before AA is applied
        // 在应用抗锯齿前先缓存一下投影矩阵
        View.ViewMatrices.SaveProjectionNoAAMatrix();

        if (ViewState)
        {
            check(View.bStatePrevViewInfoIsReadOnly);
            View.bStatePrevViewInfoIsReadOnly = ViewFamily.bWorldIsPaused || ViewFamily.EngineShowFlags.HitProxies || bFreezeTemporalHistories;

            ViewState->SetupDistanceFieldTemporalOffset(ViewFamily); // 设置距离场的时间偏移

            if (!View.bStatePrevViewInfoIsReadOnly && !bFreezeTemporalSequences)
            {
                ViewState->FrameIndex++;
            }

            if (View.OverrideFrameIndexValue.IsSet())
            {
                ViewState->FrameIndex = View.OverrideFrameIndexValue.GetValue();
            }
        }

        // Subpixel jitter for temporal AA。这个就是TAA抗锯齿,通过上一帧和这一帧的不同,试图来减少一些锯齿
        int32 CVarTemporalAASamplesValue = CVarTemporalAASamples.GetValueOnRenderThread();
        EMainTAAPassConfig TAAConfig = GetMainTAAPassConfig(View);
        bool bTemporalUpsampling = View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale;

        // Apply a sub pixel offset to the view.
        // 对视图应用子像素偏移
        // IsTemporalAccumulationBasedMethod 是否是 TAA 和 TSR
        if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState && (CVarTemporalAASamplesValue > 0 || bTemporalUpsampling) && View.bAllowTemporalJitter)
        {
            // 一般都是1,如果上下帧
            float EffectivePrimaryResolutionFraction = float(View.ViewRect.Width()) / float(View.GetSecondaryViewRectSize().X);
        }

上一段有点长了,断一下

void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
    // 。。。

    // Setup motion blur parameters (also check for camera movement thresholds)
    for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
    {
        // 。。。

        if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState && (CVarTemporalAASamplesValue > 0 || bTemporalUpsampling) && View.bAllowTemporalJitter)
        {
            // 一般都是1,如果上下帧视图缩小了就会不是
            float EffectivePrimaryResolutionFraction = float(View.ViewRect.Width()) / float(View.GetSecondaryViewRectSize().X);

            // Compute number of TAA samples. 时间累计的抗锯齿的数量【TemporalAASamples】和算法细节有关了
            int32 TemporalAASamples;
            {
                // 设置这个值,根据算法 TemporalAASamples
            }

            // Compute the new sample index in the temporal sequence.
            int32 TemporalSampleIndex = ViewState->TemporalAASampleIndex + 1;
            if (TemporalSampleIndex >= TemporalAASamples || View.bCameraCut)
            {
                TemporalSampleIndex = 0;
            }

            #if !UE_BUILD_SHIPPING
            if (CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread() >= 0)
            {
                TemporalSampleIndex = CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread();
            }
            #endif

            // Updates view state.
            // 更新采样的INDEX
            if (!View.bStatePrevViewInfoIsReadOnly && !bFreezeTemporalSequences)
            {
                ViewState->TemporalAASampleIndex = TemporalSampleIndex;
            }

            // Choose sub pixel sample coordinate in the temporal sequence.
            // 目的是计算 SampleX SampleY 采用的像素 XY坐标
            float SampleX, SampleY;
            if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale)
            {
                // Uniformly distribute temporal jittering in [-.5; .5], because there is no longer any alignement of input and output pixels.
                // 在 [-.5; .5] 中均匀分布的时间抖动,因为输入和输出像素不再对齐。
                SampleX = Halton(TemporalSampleIndex + 1, 2) - 0.5f;
                SampleY = Halton(TemporalSampleIndex + 1, 3) - 0.5f;

                View.MaterialTextureMipBias = -(FMath::Max(-FMath::Log2(EffectivePrimaryResolutionFraction), 0.0f) ) + CVarMinAutomaticViewMipBiasOffset.GetValueOnRenderThread();
                View.MaterialTextureMipBias = FMath::Max(View.MaterialTextureMipBias, CVarMinAutomaticViewMipBias.GetValueOnRenderThread());
            }
            else if( CVarTemporalAASamplesValue == 2 )
            {

            }
            else if( CVarTemporalAASamplesValue == 3 )
            {

            }
            else if( CVarTemporalAASamplesValue == 4 )
            {

            }
            else if( CVarTemporalAASamplesValue == 5 )
            {

            }
            else
            {

            }

            // 
            View.TemporalJitterSequenceLength = TemporalAASamples;
            View.TemporalJitterIndex = TemporalSampleIndex;
            View.TemporalJitterPixels.X = SampleX;
            View.TemporalJitterPixels.Y = SampleY;

            View.ViewMatrices.HackAddTemporalAAProjectionJitter(FVector2D(SampleX * 2.0f / View.ViewRect.Width(), SampleY * -2.0f / View.ViewRect.Height()));

        }

        // 。。。

    } 
}
void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
    // 。。。

    // Setup motion blur parameters (also check for camera movement thresholds)
    for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
    {
        // 。。。

        // Setup a new FPreviousViewInfo from current frame infos.
        // 缓存上一帧的视图信息
        FPreviousViewInfo NewPrevViewInfo;
        {
            NewPrevViewInfo.ViewRect = View.ViewRect;
            NewPrevViewInfo.ViewMatrices = View.ViewMatrices;
            NewPrevViewInfo.ViewRect = View.ViewRect;
        }

        if ( ViewState )
        {
            // 。。。
        }
        else
        {
            // Without a viewstate, we just assume that camera has not moved.
            View.PrevViewInfo = NewPrevViewInfo;
        }
    }
}

if ( ViewState ) 里面的内容

    // update previous frame matrices in case world origin was rebased on this frame
    // 如果 world origin 在此帧上重新建立,则更新前一帧矩阵
    if (!View.OriginOffsetThisFrame.IsZero())
    {
        ViewState->PrevFrameViewInfo.ViewMatrices.ApplyWorldOffset(View.OriginOffsetThisFrame);
    }

    // determine if we are initializing or we should reset the persistent state
    // 确定我们是否正在初始化或者是否应该重置持久状态
    // 准备一些属性
    const float DeltaTime = View.Family->Time.GetRealTimeSeconds() - ViewState->LastRenderTime;
    const bool bFirstFrameOrTimeWasReset = DeltaTime < -0.0001f || ViewState->LastRenderTime < 0.0001f; // 第一帧数,或者时间被重置了
    const bool bIsLargeCameraMovement = IsLargeCameraMovement(
        View,
        ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(),
        ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(),
        75.0f, GCameraCutTranslationThreshold); // 用view matrix 判断相机是否经过了大幅运动
    const bool bResetCamera = (bFirstFrameOrTimeWasReset || View.bCameraCut || bIsLargeCameraMovement || View.bForceCameraVisibilityReset);

    // 光线追踪
#if RHI_RAYTRACING
            static const auto CVarTemporalDenoiser = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PathTracing.TemporalDenoiser.mode"));
            const int TemporalDenoiserMode = CVarTemporalDenoiser ? CVarTemporalDenoiser->GetValueOnAnyThread() : 0;

            // 下面一小段 if else 主要是处理是否离线渲染下的 PathTracingInvalidate 路径追踪的属性重置
            if (View.bIsOfflineRender) // 离线渲染
            {
                // In the offline context, we want precise control over when to restart the path tracer's accumulation to allow for motion blur
                // So we use the camera cut signal only. In particular - we should not use bForceCameraVisibilityReset since this has
                // interactions with the motion blur post process effect in tiled rendering (see comment below).
                if (View.bCameraCut || View.bForcePathTracerReset) // 相机切换 || 重置了路径追踪
                {
                    const bool bClearTemporalDenoisingHistory = (TemporalDenoiserMode == 1) ? View.bCameraCut : true;
                    ViewState->PathTracingInvalidate(bClearTemporalDenoisingHistory); // 重置一些属性,点进去看下
                }
            }
            else
            {
                // for interactive usage - any movement or scene change should restart the path tracer

                // Note: 0.18 deg is the minimum angle for avoiding numerical precision issue (which would cause constant invalidation)
                const bool bIsCameraMove = IsLargeCameraMovement(
                    View,
                    ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(),
                    ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(),
                    0.18f /*degree*/, 0.1f /*cm*/);
                const bool bIsProjMatrixDifferent = View.ViewMatrices.GetProjectionNoAAMatrix() != View.ViewState->PrevFrameViewInfo.ViewMatrices.GetProjectionNoAAMatrix();

                // For each view, we remember what the invalidation counter was set to last time we were here so we can catch all changes
                const bool bNeedsInvalidation = ViewState->PathTracingInvalidationCounter != CurrentPathTracingInvalidationCounter;
                ViewState->PathTracingInvalidationCounter = CurrentPathTracingInvalidationCounter;
                if (bNeedsInvalidation ||
                    bIsProjMatrixDifferent ||
                    bIsCameraMove ||
                    View.bCameraCut ||
                    View.bForceCameraVisibilityReset ||
                    View.bForcePathTracerReset)
                {
                    const bool bClearTemporalDenoisingHistory = (TemporalDenoiserMode == 2) ? View.bCameraCut : true;
                    ViewState->PathTracingInvalidate(bClearTemporalDenoisingHistory);
                }
            }

#endif // RHI_RAYTRACING
            if (bResetCamera) // 重置摄像机?
            {
                View.PrevViewInfo = NewPrevViewInfo;

                // PT: If the motion blur shader is the last shader in the post-processing chain then it is the one that is
                //     adjusting for the viewport offset.  So it is always required and we can't just disable the work the
                //     shader does.  The correct fix would be to disable the effect when we don't need it and to properly mark
                //     the uber-postprocessing effect as the last effect in the chain.

                View.bPrevTransformsReset = true;
            }
            else
            {
                View.PrevViewInfo = ViewState->PrevFrameViewInfo;
            }

            // Replace previous view info of the view state with this frame, clearing out references over render target.
            if (!View.bStatePrevViewInfoIsReadOnly)
            {
                ViewState->PrevFrameViewInfo = NewPrevViewInfo;
            }

            // If the view has a previous view transform, then overwrite the previous view info for the _current_ frame.
            if (View.PreviousViewTransform.IsSet())
            {
                // Note that we must ensure this transform ends up in ViewState->PrevFrameViewInfo else it will be used to calculate the next frame's motion vectors as well
                // 更新view matrix
                View.PrevViewInfo.ViewMatrices.UpdateViewMatrix(View.PreviousViewTransform->GetTranslation(), View.PreviousViewTransform->GetRotation().Rotator());
            }

            // detect conditions where we should reset occlusion(遮挡) queries
            // 是否需要重置遮挡查询
            if (bFirstFrameOrTimeWasReset || 
                ViewState->LastRenderTime + GEngine->PrimitiveProbablyVisibleTime < View.Family->Time.GetRealTimeSeconds() ||
                View.bCameraCut ||
                View.bForceCameraVisibilityReset ||
                IsLargeCameraMovement(
                    View, 
                    FMatrix(ViewState->PrevViewMatrixForOcclusionQuery), 
                    ViewState->PrevViewOriginForOcclusionQuery, 
                    GEngine->CameraRotationThreshold, GEngine->CameraTranslationThreshold))
            {
                View.bIgnoreExistingQueries = true;
                View.bDisableDistanceBasedFadeTransitions = true;
            }

            // Turn on/off round-robin occlusion querying in the ViewState
            // 动态遮挡的查询方法 (被挡住的东西不提交)
            static const auto CVarRROCC = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.RoundRobinOcclusion"));
            const bool bEnableRoundRobin = CVarRROCC ? (CVarRROCC->GetValueOnAnyThread() != false) : false;
            if (bEnableRoundRobin != ViewState->IsRoundRobinEnabled())
            {
                ViewState->UpdateRoundRobin(bEnableRoundRobin);
                View.bIgnoreExistingQueries = true;
            }

            ViewState->PrevViewMatrixForOcclusionQuery = FMatrix44f(View.ViewMatrices.GetViewMatrix());  // LWC_TODO: Precision loss
            ViewState->PrevViewOriginForOcclusionQuery = View.ViewMatrices.GetViewOrigin();

            // store old view matrix and detect conditions where we should reset motion blur 
            // 根据 旧的view matrix 决定 是否需要重置动态模糊
#if RHI_RAYTRACING
            {
                if (bResetCamera || IsLargeCameraMovement(View, ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(), ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(), 0.1f, 0.1f))
                {
                    ViewState->RayTracingNumIterations = 1; // 但是这里改的是光线追踪的迭代次数 emmm
                }
                else
                {
                    ViewState->RayTracingNumIterations++;
                }
            }
#endif // RHI_RAYTRACING

            // we don't use DeltaTime as it can be 0 (in editor) and is computed by subtracting floats (loses precision over time)
            // Clamp DeltaWorldTime to reasonable values for the purposes of motion blur, things like TimeDilation can make it very small
            // Offline renders always control the timestep for the view and always need the timescales calculated.
            if (!ViewFamily.bWorldIsPaused || View.bIsOfflineRender)
            {
                ViewState->UpdateMotionBlurTimeScale(View);
            }

            ViewState->PrevFrameNumber = ViewState->PendingPrevFrameNumber;
            ViewState->PendingPrevFrameNumber = View.Family->FrameNumber;

            // This finishes the update of view state
            ViewState->UpdateLastRenderTime(*View.Family);

            ViewState->UpdateTemporalLODTransition(View);

到这里
if ( ViewState ) 里面的内容 结束了

FDeferredShadingSceneRenderer::Render

这里是updatescene 结束,回到render这一层

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // 。。。

    FInitViewTaskDatas InitViewTaskDatas = UpdateScene(GraphBuilder, FGlobalDynamicBuffers(DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews));

    // GPU场景作用域的helper,会在构造和析构的时候调用 GPUScene 的 BeginRender 和 EndRender ()
    FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);

}
    const bool bUseVirtualTexturing = UseVirtualTexturing(FeatureLevel);

    if (bUseVirtualTexturing && RendererOutput == ERendererOutput::FinalSceneColor)
    {
        FVirtualTextureUpdateSettings Settings;
        Settings.EnableThrottling(!ViewFamily.bOverrideVirtualTextureThrottle);

        VirtualTextureUpdater = FVirtualTextureSystem::Get().BeginUpdate(GraphBuilder, FeatureLevel, Scene, Settings);
        VirtualTextureFeedbackBegin(GraphBuilder, Views, GetActiveSceneTexturesConfig().Extent);
    }

    // Compute & commit the final state of the entire dependency topology of the renderer.
    // 提交渲染器的拓扑状态
    CommitFinalPipelineState();

    // Initialize global system textures (pass-through if already initialized).
    GSystemTextures.InitializeTextures(GraphBuilder.RHICmdList, FeatureLevel);

    {
        if (RendererOutput == ERendererOutput::FinalSceneColor)
        {
            InitViewTaskDatas.LumenFrameTemporaries = &LumenFrameTemporaries;

            // Important that this uses consistent logic throughout the frame, so evaluate once and pass in the flag from here
            // NOTE: Must be done after  system texture initialization
            // TODO: This doesn't take into account the potential for split screen views with separate shadow caches
            const bool bEnableVirtualShadowMaps = UseVirtualShadowMaps(ShaderPlatform, FeatureLevel) && ViewFamily.EngineShowFlags.DynamicShadows;
            VirtualShadowMapArray.Initialize(GraphBuilder, Scene->GetVirtualShadowMapCache(Views[0]), bEnableVirtualShadowMaps, Views[0].bIsSceneCapture);

            if (InitViewTaskDatas.LumenFrameTemporaries)
            {
                BeginUpdateLumenSceneTasks(GraphBuilder, *InitViewTaskDatas.LumenFrameTemporaries);
            }

            BeginGatherLumenLights(InitViewTaskDatas.LumenDirectLighting, InitViewTaskDatas.VisibilityTaskData);
        }

        if (bNaniteEnabled)
        {
            TArray<FConvexVolume, TInlineAllocator<2>> NaniteCullingViews;

            // For now we'll share the same visibility results across all views
            for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
            {
                FViewInfo& View = Views[ViewIndex];
                NaniteCullingViews.Add(View.ViewFrustum); // 如果开启了nanite,就会遍历view,把视锥添加到nanite的裁剪view里面
            }

            FNaniteVisibility& NaniteVisibility = Scene->NaniteVisibility[ENaniteMeshPass::BasePass];
            const FNaniteMaterialCommands& NaniteMaterials = Scene->NaniteMaterials[ENaniteMeshPass::BasePass];
            const FNaniteRasterPipelines& NaniteRasterPipeline = Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass];

            NaniteVisibility.BeginVisibilityFrame();

            NaniteBasePassVisibility.Visibility = &NaniteVisibility;
            NaniteBasePassVisibility.Query = NaniteVisibility.BeginVisibilityQuery( // 可见性查询
                *Scene,
                NaniteCullingViews,
                &NaniteRasterPipeline,
                &NaniteMaterials
            );
        }
    }

上面一段有几个函数进去看看

    struct FPerViewPipelineState
    {
        EDiffuseIndirectMethod DiffuseIndirectMethod; // 漫反射间接方法 包括禁用,SSGI(屏幕空间的全局光照),RTGI(实时全局光照),lumen,插件自定义
        IScreenSpaceDenoiser::EMode DiffuseIndirectDenoiser; // 漫反射计算的去噪方法

        // Method to use for ambient occlusion.
        EAmbientOcclusionMethod AmbientOcclusionMethod; // 环境遮罩的去噪方式,就是我们常说的 AO(Ambient Occlusion环境光遮蔽)这个枚举里面就有SSAO

        // Method to use for reflections. 
        EReflectionsMethod ReflectionsMethod;

        // Method to use for reflections on water.
        EReflectionsMethod ReflectionsMethodWater;

        // Whether there is planar reflection to compose to the reflection. 是否要把平面的反射,合成到反射中
        bool bComposePlanarReflections;

        // Whether need to generate HZB from the depth buffer. 是否用最远的深度缓冲来生成HZB
        bool bFurthestHZB;
        bool bClosestHZB;
    };
void FDeferredShadingSceneRenderer::CommitFinalPipelineState()
{
    // Family pipeline state
    // FFamilyPipelineState 有 Nanite 和 HZB 两种剔除方式,看枚举
    {
        FamilyPipelineState.Set(&FFamilyPipelineState::bNanite, UseNanite(ShaderPlatform)); // TODO: Should this respect ViewFamily.EngineShowFlags.NaniteMeshes?

        // sajdlkasjdljlksj
        static const auto ICVarHZBOcc = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HZBOcclusion"));
        FamilyPipelineState.Set(&FFamilyPipelineState::bHZBOcclusion, ICVarHZBOcc->GetInt() != 0);   
    }

    // 遍历view,
    // 获取 TPipelineState<FPerViewPipelineState>& ViewPipelineState = GetViewPipelineStateWritable(View);
    // 然后设置 FPerViewPipelineState 的属性,属性和含义参考上面结构体说明
    CommitIndirectLightingState();

    // Views pipeline states
    // 遍历view、
    for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
    {
        const FViewInfo& View = Views[ViewIndex];
        // 获取 FPerViewPipelineState 和 上面 CommitIndirectLightingState 类似
        TPipelineState< FPerViewPipelineState>& ViewPipelineState = GetViewPipelineStateWritable(View);

        // Commit HZB state
        {
            const bool bHasSSGI = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::SSGI;
            const bool bUseLumen = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::Lumen 
                || ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::Lumen;

            // Requires FurthestHZB
            ViewPipelineState.Set(&FPerViewPipelineState::bFurthestHZB,
                FamilyPipelineState[&FFamilyPipelineState::bHZBOcclusion] ||
                FamilyPipelineState[&FFamilyPipelineState::bNanite] ||
                ViewPipelineState[&FPerViewPipelineState::AmbientOcclusionMethod] == EAmbientOcclusionMethod::SSAO ||
                ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::SSR ||
                bHasSSGI || bUseLumen);

            ViewPipelineState.Set(&FPerViewPipelineState::bClosestHZB, 
                bHasSSGI || bUseLumen);
        }
    }

    // Commit all the pipeline states.
    // 提交管线状态
    {
        for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
        {
            const FViewInfo& View = Views[ViewIndex];

            GetViewPipelineStateWritable(View).Commit();
        }
        FamilyPipelineState.Commit();
    } 
}

FDeferredShadingSceneRenderer::Render

回到这一层
这个想打印点啥东西呀,还不知道。。。

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // 。。。

    ShaderPrint::BeginViews(GraphBuilder, Views);

    ON_SCOPE_EXIT
    {
        ShaderPrint::EndViews(Views);
    };
    enum class ERendererOutput
    {
        DepthPrepassOnly,   // Only render depth prepass and its related code paths 仅渲染深度预处理及其相关的代码路径
        FinalSceneColor     // Render the whole pipeline 整个管道
    };

    if (RendererOutput == ERendererOutput::FinalSceneColor) // 如果渲染整个pipeline
    {
        // By Copilot
        // 用于准备场景中的距离场信息。距离场是一种数据结构,用于存储每个点到最近表面的距离。在图形渲染中,距离场常常用于实现各种效果,如软阴影、环境光遮蔽(AO)和间接光照等
        // 具体的实现细节可能会因项目和使用的图形引擎不同而有所不同,但大致的工作流程可能包括以下步骤:  
        // - 遍历场景中的所有物体,计算每个物体的距离场。
        // - 将计算出的距离场数据存储到相应的数据结构中,如3D纹理或者其他类型的缓冲区。
        // - 将这些数据提交给渲染管线,以便在后续的渲染步骤中使用。
        PrepareDistanceFieldScene(GraphBuilder, ExternalAccessQueue);

        for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
        {
            FViewInfo& View = Views[ViewIndex];
            RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);

            // shading 能量守恒的初始化操作,反正就是准备参数
            ShadingEnergyConservation::Init(GraphBuilder, View); 

            // FGlintShadingLUTsStateData 是 存储Glint Shading的查找表(LUTs)的状态数据。Glint Shading是一种渲染技术,用于模拟微小表面细节(如头发、布料等)的光泽效果
            FGlintShadingLUTsStateData::Init(GraphBuilder, View);
        }

        // kick off dependent scene updates 
        // 更新每一帧渲染的阴影场景中的活动灯光(等待场景的更新状态完成,更新灯光的激活状态)
        Scene->ShadowScene->UpdateForRenderedFrame(GraphBuilder);
#if RHI_RAYTRACING
        // Initialize ray tracing flags, in case they weren't initialized in the CreateSceneRenderers code path 、
        // 更新光线追踪的标志
        InitializeRayTracingFlags_RenderThread();

        // 光线追踪的几何管理器
        RayTracingGeometryManager.Tick(bHasRayTracingEnableChanged);

        // 是否清空光线追踪集合收集器
        if ((GetRayTracingMode() == ERayTracingMode::Dynamic) && bHasRayTracingEnableChanged)
        {
            Scene->GetRayTracingDynamicGeometryCollection()->Clear();
        }

        // Now that we have updated all the PrimitiveSceneInfos, update the RayTracing mesh commands cache if needed
        // 更新所有的基源组件,如果需要,更新光线追踪mesh的命令缓存
        {
            // 光线追踪的网格命令模式 ERayTracingMeshCommandsMode 光线追踪,路径追踪,光照图追踪
            const ERayTracingMeshCommandsMode CurrentMode = ViewFamily.EngineShowFlags.PathTracing ? ERayTracingMeshCommandsMode::PATH_TRACING : ERayTracingMeshCommandsMode::RAY_TRACING;
            bool bNaniteCoarseMeshStreamingModeChanged = false; // nanite 网格流模式是否发生了变化
#if WITH_EDITOR
            bNaniteCoarseMeshStreamingModeChanged = Nanite::FCoarseMeshStreamingManager::CheckStreamingMode();
#endif // WITH_EDITOR
            const bool bNaniteRayTracingModeChanged = Nanite::GRayTracingManager.CheckModeChanged(); // nanite 光线追踪模式是否发生了变化

            // 需要修改光线追踪的命令模式,命令缓存?
            if (CurrentMode != Scene->CachedRayTracingMeshCommandsMode || bNaniteCoarseMeshStreamingModeChanged || bNaniteRayTracingModeChanged || bHasRayTracingEnableChanged)
            {
                // 等待mesh绘制命令完成
                Scene->WaitForCacheRayTracingPrimitivesTask();

                // In some situations, we need to refresh the cached ray tracing mesh commands because they contain data about the currently bound shader. 
                // This operation is a bit expensive but only happens once as we transition between modes which should be rare.
                // 刷新光线追踪的 mesh命令模式,刷新mesh命令缓存
                Scene->CachedRayTracingMeshCommandsMode = CurrentMode;
                Scene->RefreshRayTracingMeshCommandCache();
                bHasRayTracingEnableChanged = false;
            }

            // 是否要刷新光线追踪的实例
            if (bRefreshRayTracingInstances)
            {
                Scene->WaitForCacheRayTracingPrimitivesTask();

                // In some situations, we need to refresh the cached ray tracing instance.
                // eg: Need to update PrimitiveRayTracingFlags
                // This operation is a bit expensive but only happens once as we transition between modes which should be rare.
                Scene->RefreshRayTracingInstances();
                bRefreshRayTracingInstances = false;
            }

            // nanite光线追踪模式发生了变化
            if (bNaniteRayTracingModeChanged)
            {
                for (FViewInfo& View : Views)
                {
                    if (View.ViewState != nullptr && !View.bIsOfflineRender)
                    {
                        // don't invalidate in the offline case because we only get one attempt at rendering each sample
                        View.ViewState->PathTracingInvalidate();
                    }
                }
            }

            // 有任何光线追踪的pass被激活了
            if (bAnyRayTracingPassEnabled)
            {
                const int32 ReferenceViewIndex = 0;
                FViewInfo& ReferenceView = Views[ReferenceViewIndex];
                FGraphEventRef PrereqTask = CreateCompatibilityGraphEvent(MakeArrayView({ Scene->GetCacheRayTracingPrimitivesTask(), InitViewTaskDatas.VisibilityTaskData->GetFrustumCullTask() }));

                InitViewTaskDatas.RayTracingRelevantPrimitives = Allocator.Create<FRayTracingRelevantPrimitiveTaskData>();
                InitViewTaskDatas.RayTracingRelevantPrimitives->Task = FFunctionGraphTask::CreateAndDispatchWhenReady(
                    [Scene = Scene, &ReferenceView, &RayTracingRelevantPrimitiveList = InitViewTaskDatas.RayTracingRelevantPrimitives->List]()
                    {
                        FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
                        GatherRayTracingRelevantPrimitives(*Scene, ReferenceView, RayTracingRelevantPrimitiveList);
                    }, TStatId(), PrereqTask, ENamedThreads::AnyNormalThreadHiPriTask);
            }
        }

Nanite 相关

    UE::SVT::GetStreamingManager().BeginAsyncUpdate(GraphBuilder);

    bool bUpdateNaniteStreaming = false;
    bool bVisualizeNanite = false;

    // nanite 更新
    if (bNaniteEnabled)
    {
        Nanite::GGlobalResources.Update(GraphBuilder);

        // Only update Nanite streaming residency for the first view when multiple view rendering (nDisplay) is enabled.
        // Streaming requests are still accumulated from the remaining views.
        bUpdateNaniteStreaming =  !ViewFamily.bIsMultipleViewFamily || ViewFamily.bIsFirstViewInMultipleViewFamily;
        if (bUpdateNaniteStreaming)
        {
            Nanite::GStreamingManager.BeginAsyncUpdate(GraphBuilder); // 异步处理流的更新
        }

        FNaniteVisualizationData& NaniteVisualization = GetNaniteVisualizationData();
        if (Views.Num() > 0)
        {
            const FName& NaniteViewMode = Views[0].CurrentNaniteVisualizationMode;
            if (NaniteVisualization.Update(NaniteViewMode))
            {
                // When activating the view modes from the command line, automatically enable the VisualizeNanite show flag for convenience.
                ViewFamily.EngineShowFlags.SetVisualizeNanite(true);
            }

            bVisualizeNanite = NaniteVisualization.IsActive() && ViewFamily.EngineShowFlags.VisualizeNanite;
        }
    }

是否要渲染天空和大气

    if (RendererOutput == ERendererOutput::FinalSceneColor)
    {
        const bool bPathTracedAtmosphere = ViewFamily.EngineShowFlags.PathTracing && Views.Num() > 0 && Views[0].FinalPostProcessSettings.PathTracingEnableReferenceAtmosphere;
        if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags) && !bPathTracedAtmosphere)
        {
            for (int32 LightIndex = 0; LightIndex < NUM_ATMOSPHERE_LIGHTS; ++LightIndex)
            {
                if (Scene->AtmosphereLights[LightIndex])
                {
                    PrepareSunLightProxy(*Scene->GetSkyAtmosphereSceneInfo(),LightIndex, *Scene->AtmosphereLights[LightIndex]);
                }
            }
        }
        else
        {
            Scene->ResetAtmosphereLightsProperties();
        }
    }

GPU掩码,掩码是啥(掩码可以用来控制哪些 GPU 用于执行特定的渲染任务)

#if WITH_MGPU
    ComputeGPUMasks(&GraphBuilder.RHICmdList);
#endif // WITH_MGPU

纹理相关的更新,准备一些资源,还没有提交

    // By default, limit our GPU usage to only GPUs specified in the view masks.
    RDG_GPU_MASK_SCOPE(GraphBuilder, ViewFamily.EngineShowFlags.PathTracing ? FRHIGPUMask::All() : AllViewsGPUMask);
    RDG_EVENT_SCOPE(GraphBuilder, "Scene");
    RDG_GPU_STAT_SCOPE_VERBOSE(GraphBuilder, Unaccounted, *ViewFamily.ProfileDescription);

    if (RendererOutput == ERendererOutput::FinalSceneColor)
    {
        SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Render_Init);
        RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, AllocateRendertargets);

        // Force the subsurface profile texture to be updated.
        UpdateSubsurfaceProfileTexture(GraphBuilder, ShaderPlatform);
        SpecularProfileAtlas::UpdateSpecularProfileTextureAtlas(GraphBuilder, ShaderPlatform);

        // Force the rect light texture & IES texture to be updated.
        RectLightAtlas::UpdateAtlasTexture(GraphBuilder, FeatureLevel);
        IESAtlas::UpdateAtlasTexture(GraphBuilder, FeatureLevel);
    }

    // 场景纹理配置,和RDG系统纹理创建 
    FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
    const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Create(GraphBuilder); // FRDGSystemTextures::Create
    const bool bHasRayTracedOverlay = HasRayTracedOverlay(ViewFamily);
    const bool bAllowStaticLighting = !bHasRayTracedOverlay && IsStaticLightingAllowed();

    // if DDM_AllOpaqueNoVelocity was used, then velocity should have already been rendered as well
    const bool bIsEarlyDepthComplete = (DepthPass.EarlyZPassMode == DDM_AllOpaque || DepthPass.EarlyZPassMode == DDM_AllOpaqueNoVelocity);

    // Use read-only depth in the base pass if we have a full depth prepass.
    const bool bAllowReadOnlyDepthBasePass = bIsEarlyDepthComplete
        && !ViewFamily.EngineShowFlags.ShaderComplexity
        && !ViewFamily.UseDebugViewPS()
        && !ViewFamily.EngineShowFlags.Wireframe
        && !ViewFamily.EngineShowFlags.LightMapDensity;

    const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
        bAllowReadOnlyDepthBasePass
        ? FExclusiveDepthStencil::DepthRead_StencilWrite
        : FExclusiveDepthStencil::DepthWrite_StencilWrite;

    // Find the visible primitives.
    if (GDynamicRHI->RHIIncludeOptionalFlushes())
    {
        GraphBuilder.RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
    }

    FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(GetSceneUniforms(), Scene->GPUScene.IsEnabled(), GraphBuilder);

FDeferredShadingSceneRenderer::BeginInitViews

    {
        RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
        BeginInitViews(GraphBuilder, SceneTexturesConfig, BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get(), InitViewTaskDatas);
    }
/**
 * Initialize scene's views.
 * Check visibility, build visible mesh commands, etc.
 */
void FDeferredShadingSceneRenderer::BeginInitViews(
    FRDGBuilder& GraphBuilder,
    const FSceneTexturesConfig& SceneTexturesConfig,
    FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
    FInstanceCullingManager& InstanceCullingManager,
    FVirtualTextureUpdater* VirtualTextureUpdater,
    FInitViewTaskDatas& TaskDatas)
{
    SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_InitViews, FColor::Emerald);
    SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);
    RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, InitViews_Scene);

    PreVisibilityFrameSetup(GraphBuilder);

    // 。。。

FSceneRenderer::PreVisibilityFrameSetup

/** 
* Performs once per frame setup prior to visibility determination.
*/
void FDeferredShadingSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
    // Possible stencil dither optimization approach
    for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
    {
        FViewInfo& View = Views[ViewIndex];
        View.bAllowStencilDither = DepthPass.bDitheredLODTransitionsUseStencil;
    }

    FSceneRenderer::PreVisibilityFrameSetup(GraphBuilder);
}
void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
    FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;

    // Notify the RHI we are beginning to render a scene.
    // 通知是渲染场景,比如清空渲染目标
    RHICmdList.BeginScene();

GetContext().RHIBeginScene(); // 这里开始跨平台调用

void FRHICommandListImmediate::BeginScene()
{
    check(IsImmediate() && IsInRenderingThread());
    if (Bypass())
    {
        GetContext().RHIBeginScene(); // 这里开始跨平台调用
        return;
    }
    ALLOC_COMMAND(FRHICommandBeginScene)();
    if (!IsRunningRHIInSeparateThread())
    {
        // if we aren't running an RHIThread, there is no good reason to buffer this frame advance stuff and that complicates state management, so flush everything out now
        QUICK_SCOPE_CYCLE_COUNTER(BeginScene_Flush);
        CSV_SCOPED_TIMING_STAT(RHITFlushes, BeginScene);
        FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread);
    }
}

回来RHICmdList.BeginScene(); 继续下去

void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
    FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;

    // Notify the RHI we are beginning to render a scene.
    // 通知是渲染场景,比如清空渲染目标
    RHICmdList.BeginScene();

FSceneRenderer::PreVisibilityFrameSetup

回来
这里我们看完了 RHICmdList.BeginScene(); 然后跳回这一层

  • 头发
  • 粒子特效

    
    void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
    {
    FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;
    
    // Notify the RHI we are beginning to render a scene.
    RHICmdList.BeginScene();
    
    // 没有命中代理,且view > 0 处理头发相关的
    if (Views.Num() > 0 && !ViewFamily.EngineShowFlags.HitProxies)
    {
        FHairStrandsBookmarkParameters Parameters; 
        CreateHairStrandsBookmarkParameters(Scene, Views, AllFamilyViews, Parameters);
    
        if (Parameters.HasInstances())
        {
            // 1. Select appropriate LOD & geometry type
            RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessLODSelection, Parameters);
    
            // 2. Run guide/simulation update
            // If we are rendering from scene capture we don't need to run another time the hair bookmarks.
            if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && Views[0].AllowGPUParticleUpdate())
            {
                RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessGuideInterpolation, Parameters);
            }
        }
    }
    
    // Notify the FX system that the scene is about to perform visibility checks.
    // 特效相关的
    if (FXSystem && Views.IsValidIndex(0))
    {
        FXSystem->PreInitViews(GraphBuilder, Views[0].AllowGPUParticleUpdate() && !ViewFamily.EngineShowFlags.HitProxies, AllFamilies, &ViewFamily);
    }

编辑器下,给灯光画个线

```cpp
#if WITH_EDITOR
    // Draw lines to lights affecting this mesh if its selected.
    if (ViewFamily.EngineShowFlags.LightInfluences)
    {
        Scene->WaitForCreateLightPrimitiveInteractionsTask();

        for (TConstSetBitIterator<> It(Scene->PrimitivesSelected); It; ++It)
        {
            const FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[It.GetIndex()];
            FLightPrimitiveInteraction *LightList = PrimitiveSceneInfo->LightList;
            while (LightList) // 拿到灯
            {
                const FLightSceneInfo* LightSceneInfo = LightList->GetLight();

                bool bDynamic = true;
                bool bRelevant = false;
                bool bLightMapped = true;
                bool bShadowMapped = false;
                PrimitiveSceneInfo->Proxy->GetLightRelevance(LightSceneInfo->Proxy, bDynamic, bRelevant, bLightMapped, bShadowMapped);

                if (bRelevant)
                {
                    // Draw blue for light-mapped lights and orange for dynamic lights
                    const FColor LineColor = bLightMapped ? FColor(0,140,255) : FColor(255,140,0);
                    for (int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
                    {
                        FViewInfo& View = Views[ViewIndex];
                        FViewElementPDI LightInfluencesPDI(&View,nullptr,&View.DynamicPrimitiveCollector);
                        LightInfluencesPDI.DrawLine(PrimitiveSceneInfo->Proxy->GetBounds().Origin, LightSceneInfo->Proxy->GetLightToWorld().GetOrigin(), LineColor, SDPG_World);
                    }
                }
                LightList = LightList->GetNextLight();
            }
        }
    }
#endif
#if RHI_RAYTRACING
// 光线追踪的
    if (Scene && Views.Num())
    {
        const int32 ReferenceViewIndex = 0;
        const FViewInfo& ReferenceView = Views[ReferenceViewIndex];

        Scene->RayTracingScene.InitPreViewTranslation(ReferenceView.ViewMatrices);
        Scene->RayTracingScene.bNeedsDebugInstanceGPUSceneIndexBuffer = IsRayTracingInstanceOverlapEnabled(ReferenceView);
    }
#endif

    // 扩展
    for (const auto& ViewExtension : ViewFamily.ViewExtensions)
    {
        ViewExtension->PreInitViews_RenderThread(GraphBuilder);
    }
}

FSceneRenderer::PreVisibilityFrameSetup 函数结束

FDeferredShadingSceneRenderer::BeginInitViews

回到这里
上面我们讨论完了 PreVisibilityFrameSetup
然后继续

/**
 * Initialize scene's views.
 * Check visibility, build visible mesh commands, etc.
 */
void FDeferredShadingSceneRenderer::BeginInitViews(
    FRDGBuilder& GraphBuilder,
    const FSceneTexturesConfig& SceneTexturesConfig,
    FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
    FInstanceCullingManager& InstanceCullingManager,
    FVirtualTextureUpdater* VirtualTextureUpdater,
    FInitViewTaskDatas& TaskDatas)
{
    SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_InitViews, FColor::Emerald);
    SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);
    RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, InitViews_Scene);

    PreVisibilityFrameSetup(GraphBuilder);

    // Attempt to launch dynamic shadow tasks early before finalizing visibility.
    if (GetRendererOutput() == ERendererOutput::FinalSceneColor)
    {
        BeginInitDynamicShadows(TaskDatas);
    }

    FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;

    // Create GPU-side representation of the view for instance culling.
    // 注册view视图,注册到 实例剔除管理器中 
    for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
    {
        Views[ViewIndex].GPUSceneViewId = InstanceCullingManager.RegisterView(Views[ViewIndex]); // FInstanceCullingManager
    }

    // 特效相关的初始化
    // 在naigara初始化之前先初始化buff
    {
        // This is to init the ViewUniformBuffer before rendering for the Niagara compute shader.
        // This needs to run before ComputeViewVisibility() is called, but the views normally initialize the ViewUniformBuffer after that (at the end of this method).
        if (FXSystem && FXSystem->RequiresEarlyViewUniformBuffer() && Views.IsValidIndex(0))
        {
            // during ISR, instanced view RHI resources need to be initialized first.
            if (FViewInfo* InstancedView = const_cast<FViewInfo*>(Views[0].GetInstancedView()))
            {
                InstancedView->InitRHIResources();
            }
            Views[0].InitRHIResources();
            FXSystem->PostInitViews(GraphBuilder, GetSceneViews(), !ViewFamily.EngineShowFlags.HitProxies);
        }
    }

    // lumen可视化绘制
    LumenScenePDIVisualization();

    // 呃呃呃呃呃呃呃呃呃呃,
    TaskDatas.VisibilityTaskData->ProcessRenderThreadTasks(BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater);

    // Make a second attempt to launch shadow tasks it wasn't able to the first time due to visibility being deferred.
    // 再次尝试启动影子任务,由于可见性被延迟,第一次无法启动。
    if (GetRendererOutput() == ERendererOutput::FinalSceneColor)
    {
        BeginInitDynamicShadows(TaskDatas);
    }

    // This must happen before we start initialising and using views.
    if (Scene)
    {
        // 天空相关的,去更新GPU缓存
        UpdateSkyIrradianceGpuBuffer(GraphBuilder, ViewFamily.EngineShowFlags, Scene->SkyLight, Scene->SkyIrradianceEnvironmentMap);
    }

    // Initialise Sky/View resources before the view global uniform buffer is built.
    // 是否要渲染天空大气的效果
    if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags))
    {
        InitSkyAtmosphereForViews(RHICmdList);
    }

    // 前面有一个pre VisibilityFrameSetup 这里有一个post VisibilityFrameSetup
    // 还是可见性的处理,后面展开
    PostVisibilityFrameSetup(TaskDatas.ILCUpdatePrim);

    {
        QUICK_SCOPE_CYCLE_COUNTER(STAT_InitViews_InitRHIResources);
        // initialize per-view uniform buffer. Do it from back to front because secondary stereo view follows its primary one, but primary needs to know the instanced's params
        for (int32 ViewIndex = Views.Num() - 1; ViewIndex >= 0; --ViewIndex)
        {
            FViewInfo& View = Views[ViewIndex];
            // Set the pre-exposure before initializing the constant buffers. (预曝光值)
            View.UpdatePreExposure();

            // Initialize the view's RHI resources.更新头发的资源,然后再 更新RHI资源
            UpdateHairResources(GraphBuilder, View);
            View.InitRHIResources();
        }
    }

    // 渲染开始了
    {
        QUICK_SCOPE_CYCLE_COUNTER(STAT_InitViews_OnStartRender);
        OnStartRender(RHICmdList); // 里面清空了一下 GVisualizeTexture 的属性
    }

    // 立即刷新现场
    if (GDynamicRHI->RHIIncludeOptionalFlushes())
    {
        RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
    }
}

FDeferredShadingSceneRenderer::Render

回来到这里
BeginInitViews 结束了继续看下去

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // ...

    {
        RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
        BeginInitViews(GraphBuilder, SceneTexturesConfig, BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get(), InitViewTaskDatas);
    }

    // 看到 Extension 就明白这是预留的用户扩展的接口
    extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
    for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
    {
        Extension->BeginFrame();

        for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
        {
            // Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush
            Extension->PrepareView(&Views[ViewIndex]);
        }
    }

    // 为每个试图准备光线追踪的数据,计算光线追踪的贴画的mesh
#if RHI_RAYTRACING
    const int32 ReferenceViewIndex = 0;
    FViewInfo& ReferenceView = Views[ReferenceViewIndex];
    FRayTracingScene& RayTracingScene = Scene->RayTracingScene;
#endif

    if (RendererOutput == ERendererOutput::FinalSceneColor)
    {
#if RHI_RAYTRACING
        // Prepare the scene for rendering this frame.

        // Resets the internal arrays, but does not release any resources.
        // 重置光线追踪的场景
        RayTracingScene.Reset(IsRayTracingInstanceDebugDataEnabled(ReferenceView));

        // 是否为光线追踪的贴花准备数据
        // 贴花,比如在凹凸不平的地面上贴上血
        if (ShouldPrepareRayTracingDecals(*Scene, ViewFamily))
        {
            // Calculate decal grid for ray tracing per view since decal fade is view dependent
            // TODO: investigate reusing the same grid for all views (ie: different callable shader SBT entries for each view so fade alpha is still correct for each view)

            for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
            {
                FViewInfo& View = Views[ViewIndex];
                // 如果要,就在这里创建贴画数据
                View.RayTracingDecalUniformBuffer = CreateRayTracingDecalData(GraphBuilder, *Scene, View, RayTracingScene.NumCallableShaderSlots);
                View.bHasRayTracingDecals = true;
                RayTracingScene.NumCallableShaderSlots += Scene->Decals.Num();
            }
        }
        else
        {
            TRDGUniformBufferRef<FRayTracingDecals> NullRayTracingDecalUniformBuffer = CreateNullRayTracingDecalsUniformBuffer(GraphBuilder);

            for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
            {
                FViewInfo& View = Views[ViewIndex];
                View.RayTracingDecalUniformBuffer = NullRayTracingDecalUniformBuffer;
                View.bHasRayTracingDecals = false;
            }
        }

        // 。。。
#endif // RHI_RAYTRACING

主要看看 CreateRayTracingDecalData 如何创建光线追踪贴花的

TODO 07-04

FDeferredShadingSceneRenderer::Render

跳出来

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // ...
    if (RendererOutput == ERendererOutput::FinalSceneColor)
    {
        // 。。。

        // 支持光线追踪 和 RHI 支持 光追 shader
        if (IsRayTracingEnabled() && RHISupportsRayTracingShaders(ViewFamily.GetShaderPlatform()))
        {
            // Nanite raytracing manager update must run before GPUScene update since it can modify primitive data
            Nanite::GRayTracingManager.Update(); // 

            // 。。。
        }

        // 。。。

    }
#endif // RHI_RAYTRACING

Nanite::GRayTracingManager.Update();

    void FRayTracingManager::Update()
    {
        const bool bUsingNaniteRayTracing = GetRayTracingMode() != ERayTracingMode::Fallback;

        // process PendingRemoves 处理待删除的东西
        {
            TSet<uint32> StillPendingRemoves;

            for (uint32 GeometryId : PendingRemoves)
            {
                FInternalData* Data = Geometries[GeometryId];

                if (Data->bUpdating)
                {
                    // can't remove until update is finished, delay to next frame
                    // 正在更新没法删,先记录一下
                    StillPendingRemoves.Add(GeometryId);
                }
                else
                {
                    if (Data->AuxiliaryDataOffset != INDEX_NONE) // 辅助数据便宜
                    {
                        AuxiliaryDataAllocator.Free(Data->AuxiliaryDataOffset, Data->AuxiliaryDataSize); // 释放
                    }
                    ResourceToRayTracingIdMap.Remove(Data->ResourceId); // remove
                    Geometries.RemoveAt(GeometryId);
                    delete (Data);
                }
            }

            Swap(PendingRemoves, StillPendingRemoves); // 交换一下
        }

        int32 NumPrimitivesScheduled = 0;

        // scheduling pending builds
        // 调度待构建的几何体
        {
            checkf(ScheduledBuilds.IsEmpty(), TEXT("Scheduled builds were not dispatched last frame."));

            for (const FPendingBuild& PendingBuild : PendingBuilds)
            {
                if (NumPrimitivesScheduled >= GNaniteRayTracingMaxBuiltPrimitivesPerFrame) // 数量超过了没帧最大构建限制
                {
                    break;
                }

                FInternalData& Data = *Geometries[PendingBuild.GeometryId];
                Data.RayTracingGeometryRHI = PendingBuild.RayTracingGeometryRHI;

                const FRayTracingGeometryInitializer& Initializer = Data.RayTracingGeometryRHI->GetInitializer();

                NumPrimitivesScheduled += Initializer.TotalPrimitiveCount;

                if (Data.AuxiliaryDataOffset != INDEX_NONE)
                {
                    AuxiliaryDataAllocator.Free(Data.AuxiliaryDataOffset, Data.AuxiliaryDataSize);
                }
                Data.AuxiliaryDataSize = Initializer.TotalPrimitiveCount;
                Data.AuxiliaryDataOffset = AuxiliaryDataAllocator.Allocate(Data.AuxiliaryDataSize);

                for (auto& Primitive : Data.Primitives)
                {
                    if (bUsingNaniteRayTracing)
                    {
                        Primitive->CachedRayTracingInstance.GeometryRHI = Data.RayTracingGeometryRHI;
                    }

                    auto NaniteProxy = static_cast<Nanite::FSceneProxyBase*>(Primitive->Proxy);
                    NaniteProxy->SetRayTracingDataOffset(Data.AuxiliaryDataOffset);

                    Primitive->Scene->GPUScene.AddPrimitiveToUpdate(Primitive->GetIndex(), EPrimitiveDirtyState::ChangedOther); // 把数据加到gpu scene
                }

                ScheduledBuilds.Add(PendingBuild.GeometryId); // 标记 已经调度了 该几何体的构建任务
            }

            // not using RemoveAtSwap to avoid starving requests in the middle
            // not expecting significant number of elements remaining anyway
            // 上面 0~ScheduledBuilds.Num() 拿去调度了
            PendingBuilds.RemoveAt(0, ScheduledBuilds.Num());

            DEC_DWORD_STAT_BY(STAT_NaniteRayTracingPendingBuilds, ScheduledBuilds.Num());
        }

        while (ReadbackBuffersNumPending > 0)
        {
            uint32 Index = (ReadbackBuffersWriteIndex + MaxReadbackBuffers - ReadbackBuffersNumPending) % MaxReadbackBuffers;
            FReadbackData& ReadbackData = ReadbackBuffers[Index];
            if (ReadbackData.MeshDataReadbackBuffer->IsReady())
            {
                ReadbackBuffersNumPending--;

                auto MeshDataReadbackBufferPtr = (const uint32*)ReadbackData.MeshDataReadbackBuffer->Lock(ReadbackData.NumMeshDataEntries * sizeof(uint32)); // 线程锁

                for (int32 GeometryIndex = 0; GeometryIndex < ReadbackData.Entries.Num(); ++GeometryIndex)
                {
                    uint32 GeometryId = ReadbackData.Entries[GeometryIndex];

                    // FInternalData 看起来像描述nanite 几何体的数据
                    FInternalData& Data = *Geometries[GeometryId];

                    auto Header = (const FStreamOutMeshDataHeader*)(MeshDataReadbackBufferPtr + Data.BaseMeshDataOffset);
                    auto Segments = (const FStreamOutMeshDataSegment*)(Header + 1);

                    check(Header->NumClusters <= Data.NumResidentClusters);

                    const uint32 VertexBufferOffset = Header->VertexBufferOffset;
                    const uint32 IndexBufferOffset = Header->IndexBufferOffset;
                    const uint32 NumVertices = Header->NumVertices;

                    if (VertexBufferOffset == 0xFFFFFFFFu || IndexBufferOffset == 0xFFFFFFFFu)
                    {
                        // ran out of space in StreamOut buffers
                        // 说明是空,关闭更新
                        Data.bUpdating = false;
                        Data.BaseMeshDataOffset = -1;

                        check(Data.StagingAuxiliaryDataOffset != INDEX_NONE);
                        Data.StagingAuxiliaryDataOffset = INDEX_NONE;

                        UpdateRequests.Add(GeometryId); // request update again

                        DEC_DWORD_STAT_BY(STAT_NaniteRayTracingInFlightUpdates, 1);
                        INC_DWORD_STAT_BY(STAT_NaniteRayTracingFailedStreamOutRequests, 1);

                        continue;
                    }

                    // 构建初始化器 FRayTracingGeometryInitializer Initializer;
                    FRayTracingGeometryInitializer Initializer;
                    Initializer.DebugName = Data.DebugName;
//                  Initializer.bFastBuild = false;
//                  Initializer.bAllowUpdate = false;
                    Initializer.bAllowCompaction = false; // 是否允许压缩

                    Initializer.IndexBuffer = IndexBuffer->GetRHI();
                    Initializer.IndexBufferOffset = IndexBufferOffset * sizeof(uint32);

                    Initializer.TotalPrimitiveCount = 0;

                    Initializer.Segments.SetNum(Data.NumSegments); // 初始化的段数

                    for (uint32 SegmentIndex = 0; SegmentIndex < Data.NumSegments; ++SegmentIndex)
                    {
                        // 每一段的
                        const uint32 NumIndices = Segments[SegmentIndex].NumIndices; // 顶点数量
                        const uint32 FirstIndex = Segments[SegmentIndex].FirstIndex; // 第一个索引

                        FRayTracingGeometrySegment& Segment = Initializer.Segments[SegmentIndex];
                        Segment.FirstPrimitive = FirstIndex / 3;
                        Segment.NumPrimitives = NumIndices / 3;
                        Segment.VertexBuffer = VertexBuffer->GetRHI();
                        Segment.VertexBufferOffset = VertexBufferOffset * sizeof(FVector3f);
                        Segment.MaxVertices = NumVertices;

                        Initializer.TotalPrimitiveCount += Segment.NumPrimitives;
                    }

                    // 用 FRayTracingGeometryInitializer 创建 FRayTracingGeometryInitializer 集合体
                    FRayTracingGeometryRHIRef RayTracingGeometryRHI = RHICreateRayTracingGeometry(Initializer);

                    // 判断是否图元一帧处理太多了
                    if (NumPrimitivesScheduled < GNaniteRayTracingMaxBuiltPrimitivesPerFrame)
                    {
                        NumPrimitivesScheduled += RayTracingGeometryRHI->GetInitializer().TotalPrimitiveCount;

                        Data.RayTracingGeometryRHI = MoveTemp(RayTracingGeometryRHI);

                        if (Data.AuxiliaryDataOffset != INDEX_NONE)
                        {
                            AuxiliaryDataAllocator.Free(Data.AuxiliaryDataOffset, Data.AuxiliaryDataSize);
                        }
                        // allocate persistent auxiliary range
                        Data.AuxiliaryDataSize = CalculateAuxiliaryDataSizeInUints(Initializer.TotalPrimitiveCount);
                        Data.AuxiliaryDataOffset = AuxiliaryDataAllocator.Allocate(Data.AuxiliaryDataSize);

                        for (auto& Primitive : Data.Primitives)
                        {
                            if (bUsingNaniteRayTracing)
                            {
                                Primitive->CachedRayTracingInstance.GeometryRHI = Data.RayTracingGeometryRHI;
                            }

                            auto NaniteProxy = static_cast<Nanite::FSceneProxyBase*>(Primitive->Proxy);
                            NaniteProxy->SetRayTracingDataOffset(Data.AuxiliaryDataOffset);

                            // 加到GPU Scene里面
                            Primitive->Scene->GPUScene.AddPrimitiveToUpdate(Primitive->GetIndex(), EPrimitiveDirtyState::ChangedOther);
                        }

                        ScheduledBuilds.Add(GeometryId);
                    }
                    else
                    {
                        // 太多了,就往pending里面加
                        FPendingBuild PendingBuild;
                        PendingBuild.GeometryId = GeometryId;
                        PendingBuild.RayTracingGeometryRHI = MoveTemp(RayTracingGeometryRHI);
                        PendingBuilds.Add(MoveTemp(PendingBuild));

                        INC_DWORD_STAT_BY(STAT_NaniteRayTracingPendingBuilds, 1);
                    }
                }

                ReadbackData.Entries.Empty();
                ReadbackData.MeshDataReadbackBuffer->Unlock();
            }
            else
            {
                break;
            }
        }

        INC_DWORD_STAT_BY(STAT_NaniteRayTracingScheduledBuildsNumPrimitives, NumPrimitivesScheduled);
    }

Nanite::GRayTracingManager.Update(); 结束了 回来。

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // ...
    if (RendererOutput == ERendererOutput::FinalSceneColor)
    {
        // 。。。

        // 支持光线追踪 和 RHI 支持 光追 shader
        if (IsRayTracingEnabled() && RHISupportsRayTracingShaders(ViewFamily.GetShaderPlatform()))
        {
            // Nanite raytracing manager update must run before GPUScene update since it can modify primitive data
            Nanite::GRayTracingManager.Update(); // 处理读取缓冲区的数据,根据数据创建更新的几何体,将几何体添加到计划建立的列表

            if (!ViewFamily.EngineShowFlags.PathTracing) // 不支持路径追踪
            {
                // get the default lighting miss shader (to implicitly fill in the MissShader library before the RT pipeline is created)
                // 获取默认的照明缺失着色器(在创建 RT 管道之前隐式填充 MissShader 库)
                GetRayTracingLightingMissShader(ReferenceView.ShaderMap);
                RayTracingScene.NumMissShaderSlots++;
            }

            // 光照函数
            if (ViewFamily.EngineShowFlags.LightFunctions)
            {
                // gather all the light functions that may be used (and also count how many miss shaders we will need)
                FRayTracingLightFunctionMap RayTracingLightFunctionMap;
                if (ViewFamily.EngineShowFlags.PathTracing)
                {
                    RayTracingLightFunctionMap = GatherLightFunctionLightsPathTracing(Scene, ViewFamily.EngineShowFlags, ReferenceView.GetFeatureLevel());
                }
                else
                {
                    RayTracingLightFunctionMap = GatherLightFunctionLights(Scene, ViewFamily.EngineShowFlags, ReferenceView.GetFeatureLevel());
                }
                if (!RayTracingLightFunctionMap.IsEmpty())
                {
                    // If we got some light functions in our map, store them in the RDG blackboard so downstream functions can use them.
                    // The map itself will be strictly read-only from this point on.
                    GraphBuilder.Blackboard.Create<FRayTracingLightFunctionMap>(MoveTemp(RayTracingLightFunctionMap));
                }
            }
        }

        // 。。。

    }
#endif // RHI_RAYTRACING

FXSystem

特效,做一些准备工作 pre render

        // Notify the FX system that the scene is about to be rendered.
        if (FXSystem && Views.IsValidIndex(0))
        {
            SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender);
            GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_FXPreRender));
            FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /*bAllowGPUParticleUpdate*/);

            if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager())
            {
                GPUSortManager->OnPreRender(GraphBuilder);
            }
        }
void FFXSystemSet::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
    for (FFXSystemInterface* FXSystem : FXSystems)
    {
        check(FXSystem);
        FXSystem->PreRender(GraphBuilder, Views, SceneUniformBuffer, bAllowGPUParticleSceneUpdate);
    }
}
void FFXSystem::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
    bAllowGPUParticleSceneUpdate = bAllowGPUParticleSceneUpdate && Views.Num() > 0 && Views[0].AllowGPUParticleUpdate();

    if (RHISupportsGPUParticles() && bAllowGPUParticleSceneUpdate) // RHI 支持GPU粒子,允许GPU粒子场景更新
    {
        RDG_GPU_STAT_SCOPE(GraphBuilder, FXSystemPreRender);
        RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, FXSystem);

        TUniformBufferRef<FViewUniformShaderParameters> ViewUniformBuffer = Views[0].ViewUniformBuffer; // buff
        const FGlobalDistanceFieldParameterData* GlobalDistanceFieldParameterData = GetRendererModule().GetGlobalDistanceFieldParameterData(Views[0]); // 全局距离场参数

        AddPass(
            GraphBuilder,
            RDG_EVENT_NAME("FFXSystem::PreRender"),
            [this, ViewUniformBuffer, GlobalDistanceFieldParameterData](FRHICommandListImmediate& RHICmdList)
            {
                //SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque);

                SCOPED_DRAW_EVENT(RHICmdList, GPUParticles_PreRender);
                UpdateMultiGPUResources(RHICmdList);

                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Prepare));
                PrepareGPUSimulation(RHICmdList);

                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Simulate));
                SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::Main, {}, nullptr);

                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Finalize));
                FinalizeGPUSimulation(RHICmdList);

                if (IsParticleCollisionModeSupported(GetShaderPlatform(), PCM_DistanceField))
                {
                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_PrepareCDF));
                    PrepareGPUSimulation(RHICmdList);

                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_SimulateCDF));
                    SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::CollisionDistanceField, ViewUniformBuffer, GlobalDistanceFieldParameterData);
                    //particles rendered during basepass may need to read pos/velocity buffers; must finalize unless we know for sure that nothing in base pass will read it.
                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_FinalizeCDF));
                    FinalizeGPUSimulation(RHICmdList);
                }
            }
        );
    }
}

看AddPass的Lambda
UpdateMultiGPUResources(RHICmdList);

void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
    if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
    {       
        //Inject particles spawned in the last frame, but only update the attribute textures
        SCOPED_DRAW_EVENT(RHICmdList, ParticleInjection);
        SCOPED_GPU_STAT(RHICmdList, ParticleSimulation);

        // Set render targets.
        // 设置渲染目标
        FRHITexture* InjectRenderTargets[2] = 
        {
            ParticleSimulationResources->RenderAttributesTexture.TextureRHI,
            ParticleSimulationResources->SimulationAttributesTexture.TextureRHI
        };

        FRHIRenderPassInfo RPInfo(2, InjectRenderTargets, ERenderTargetActions::Load_Store);
        RPInfo.ColorRenderTargets[0].ResolveTarget = ParticleSimulationResources->RenderAttributesTexture.TextureRHI;
        RPInfo.ColorRenderTargets[1].ResolveTarget = ParticleSimulationResources->SimulationAttributesTexture.TextureRHI;
        {
            // 改变资源状态
            RHICmdList.Transition({
                FRHITransitionInfo(ParticleSimulationResources->RenderAttributesTexture.TextureRHI, ERHIAccess::Unknown, ERHIAccess::RTV),
                FRHITransitionInfo(ParticleSimulationResources->SimulationAttributesTexture.TextureRHI, ERHIAccess::Unknown, ERHIAccess::RTV)
            });
            // 开启渲染通道
            RHICmdList.BeginRenderPass(RPInfo, TEXT("UpdateMultiGPUResources")); // 设置render target 并清理,如果有就开启遮挡查询

            // 。。。
        }
    }

    // 。。。
}

如何begin一个pass

    FORCEINLINE_DEBUGGABLE void BeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* Name)
    {
        check(!IsInsideRenderPass()); // 避免在一个pass中又启动了一个pass
        check(!IsInsideComputePass()); // 避免在一个计算过程中又启动了一个计算过程

        InInfo.Validate();

        if (Bypass()) // 是否要绕过渲染过程
        {
            GetContext().RHIBeginRenderPass(InInfo, Name); // 这里开始分开图形API了
        }
        else
        {
            // 不需要就存一个名字
            TCHAR* NameCopy  = AllocString(Name);
            ALLOC_COMMAND(FRHICommandBeginRenderPass)(InInfo, NameCopy);
        }

        CacheActiveRenderTargets(InInfo); // 缓存激活的渲染目标
        ResetSubpass(InInfo.SubpassHint); // 
        PersistentState.bInsideRenderPass = true;

        if (InInfo.NumOcclusionQueries) // 存在遮挡查询
        {
            PersistentState.bInsideOcclusionQueryBatch = true;
            GDynamicRHI->RHIBeginOcclusionQueryBatch_TopOfPipe(*this, InInfo.NumOcclusionQueries);
        }
    }

回来

void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
    if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
    {       
        // 。。。

        {
            // 开启渲染通道
            RHICmdList.BeginRenderPass(RPInfo, TEXT("UpdateMultiGPUResources"));

            // viewport
            RHICmdList.SetViewport(0.0f, 0.0f, 0.0f, (float)GParticleSimulationTextureSizeX, (float)GParticleSimulationTextureSizeY, 1.0f);

            // PSO
            FGraphicsPipelineStateInitializer GraphicsPSOInit;
            RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
            GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
            GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
            GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();

            // Inject particles. 生成新的粒子
            InjectNewParticles<true>(RHICmdList, GraphicsPSOInit, this->FeatureLevel, this->LastFrameNewParticles);

            RHICmdList.EndRenderPass();
        }
    }

    // 。。。
}

InjectNewParticles

/**
* Data passed to the GPU to inject a new particle in to the simulation.
*/
struct FNewParticle
{
    FVector3f Position; // 粒子初始化位置
    float RelativeTime; // 生命周期
    FVector3f Velocity; // 初始速度
    float TimeScale; // 粒子的时间缩放
    FVector2f Size; // 粒子大小
    float Rotation; // 粒子初始旋转角度
    float RelativeRotationRate; // 粒子旋转速率
    float DragCoefficient; // 粒子的空气阻力系数
    float VectorFieldScale; // Per-particle vector field scale
    /** Resilience for collision. */ // 碰撞的弹力
    union
    {
        float Resilience; // 弹力
        int32 AllocatedTileIndex;
    } ResilienceAndTileIndex;
    /** Random selection of orbit(轨道) attributes. */ 
    float RandomOrbit;
    /** The offset at which to inject the new particle. */
    FVector2f Offset;
};
/**
 * Injects new particles in to the GPU simulation.
 * @param NewParticles - A list of particles to inject in to the simulation.
 */
template<bool StaticPropertiesOnly>
void InjectNewParticles(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit,  ERHIFeatureLevel::Type FeatureLevel, const TArray<FNewParticle>& NewParticles)
{
    if (GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed) || !CVarSimulateGPUParticles.GetValueOnAnyThread()) // 渲染线程是否被暂停,控制台变量,模拟GPU粒子的开关
    {
        return;
    }

    const int32 MaxParticlesPerDrawCall = GParticleScratchVertexBufferSize / sizeof(FNewParticle); // 最多要画多少个粒子
    FRHIBuffer* ScratchVertexBufferRHI = GParticleScratchVertexBuffer.VertexBufferRHI; // buffer
    int32 ParticleCount = NewParticles.Num(); // 数量
    int32 FirstParticle = 0;

    while ( ParticleCount > 0 )
    {
        // Copy new particles in to the vertex buffer.
        const int32 ParticlesThisDrawCall = FMath::Min<int32>( ParticleCount, MaxParticlesPerDrawCall );
        const void* Src = NewParticles.GetData() + FirstParticle;
        void* Dest = RHICmdList.LockBuffer( ScratchVertexBufferRHI, 0, ParticlesThisDrawCall * sizeof(FNewParticle), RLM_WriteOnly );
        FMemory::Memcpy( Dest, Src, ParticlesThisDrawCall * sizeof(FNewParticle) );
        RHICmdList.UnlockBuffer( ScratchVertexBufferRHI );
        ParticleCount -= ParticlesThisDrawCall;
        FirstParticle += ParticlesThisDrawCall;

        // Grab shaders. 获取shader
        TShaderMapRef<FParticleInjectionVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
        TShaderMapRef<TParticleInjectionPS<StaticPropertiesOnly> > PixelShader(GetGlobalShaderMap(FeatureLevel));

        // PSO set
        GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GParticleInjectionVertexDeclaration.VertexDeclarationRHI;
        GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
        GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
        GraphicsPSOInit.PrimitiveType = PT_TriangleList;

        SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); // 创建或者获取PSO缓存,命令列表设置PSO

        SetShaderParametersLegacyVS(RHICmdList, VertexShader); // 设置shader参数吧

        // Stream 0: New particles.
        RHICmdList.SetStreamSource(
            0,
            ScratchVertexBufferRHI,
            /*Offset=*/ 0
            );

        // Stream 1: TexCoord.
        RHICmdList.SetStreamSource(
            1,
            GParticleTexCoordVertexBuffer.VertexBufferRHI,
            /*Offset=*/ 0
            );

        // Inject particles.
        RHICmdList.DrawIndexedPrimitive(
            GParticleIndexBuffer.IndexBufferRHI,
            /*BaseVertexIndex=*/ 0,
            /*MinIndex=*/ 0,
            /*NumVertices=*/ 4,
            /*StartIndex=*/ 0,
            /*NumPrimitives=*/ 2,
            /*NumInstances=*/ ParticlesThisDrawCall
            );
    }
}
void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
    if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
    {       
        // 。。。

        {
            // 。。。

            // Inject particles. 生成新的粒子
            InjectNewParticles<true>(RHICmdList, GraphicsPSOInit, this->FeatureLevel, this->LastFrameNewParticles);

            RHICmdList.EndRenderPass();
        }
    }

    // Clear out particles from last frame
    LastFrameNewParticles.Reset();

#if WITH_MGPU
    // 多GPU
    PhaseToBroadcastResourceTransfer = EParticleSimulatePhase::Last;
    while (PhaseToBroadcastResourceTransfer > EParticleSimulatePhase::First && NumGPUSimulations[PhaseToBroadcastResourceTransfer] == 0)
    {
        PhaseToBroadcastResourceTransfer = static_cast<EParticleSimulatePhase::Type>(PhaseToBroadcastResourceTransfer - 1);
    }

    PhaseToWaitForResourceTransfer = EParticleSimulatePhase::First;
    while (PhaseToWaitForResourceTransfer < PhaseToBroadcastResourceTransfer && NumGPUSimulations[PhaseToWaitForResourceTransfer] == 0)
    {
        PhaseToWaitForResourceTransfer = static_cast<EParticleSimulatePhase::Type>(PhaseToWaitForResourceTransfer + 1);
    }
#endif
}

FFXSystem::PreRender

回来,上面一大串 看完了 UpdateMultiGPUResources
然后继续下去

void FFXSystem::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
    // 。。。

    if (RHISupportsGPUParticles() && bAllowGPUParticleSceneUpdate) // RHI 支持GPU粒子,允许GPU粒子场景更新
    {
        // 。。。

        AddPass(
            GraphBuilder,
            RDG_EVENT_NAME("FFXSystem::PreRender"),
            [this, ViewUniformBuffer, GlobalDistanceFieldParameterData](FRHICommandListImmediate& RHICmdList)
            {
                //SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque);

                SCOPED_DRAW_EVENT(RHICmdList, GPUParticles_PreRender);
                UpdateMultiGPUResources(RHICmdList);

                // 准备GPU的模拟
                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Prepare));
                PrepareGPUSimulation(RHICmdList); // 设置 当前 FParticleStateTextures 的状态,

                // 模拟GPU粒子
                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Simulate));
                SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::Main, {}, nullptr);

                // 然后完成
                RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Finalize));
                FinalizeGPUSimulation(RHICmdList);

                // 支持例子碰撞
                if (IsParticleCollisionModeSupported(GetShaderPlatform(), PCM_DistanceField))
                {
                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_PrepareCDF));
                    PrepareGPUSimulation(RHICmdList);

                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_SimulateCDF));
                    SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::CollisionDistanceField, ViewUniformBuffer, GlobalDistanceFieldParameterData);
                    //particles rendered during basepass may need to read pos/velocity buffers; must finalize unless we know for sure that nothing in base pass will read it.
                    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_FinalizeCDF));
                    FinalizeGPUSimulation(RHICmdList);
                }
            }
        );
    }
}

这里粒子系统后面再展开吧

有点长了总结一下

BeginRenderingViewFamilies 这个函数准备开始画3D场景了
这个函数前面通过 SendAllEndOfFrameUpdates 确保之前已经画完了
SendAllEndOfFrameUpdates 做了一些tick actor component 结束帧相关,并且把transform和其他数据传递给了GPU
然后做了一些 xxxxxxxxxxxxxxxxx
然后往渲染线程有了如下调用
RenderViewFamilies_RenderThread(RHICmdList, LocalSceneRenderers);

其中有一步
SceneRenderer->Render(GraphBuilder);
这个SceneRenderer可能是延迟渲染的,或者是移动渲染
比如 FDeferredShadingSceneRenderer::Render
这个函数一路向下翻翻翻
翻到了这里FX相关和GPU排序管理器这里

===========================================

然后回来
FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /bAllowGPUParticleUpdate/); 结束了
后面是GPU批次排序

void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
    // 。。。

        // Notify the FX system that the scene is about to be rendered.
        if (FXSystem && Views.IsValidIndex(0))
        {
            SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender);
            GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_FXPreRender));
            FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /*bAllowGPUParticleUpdate*/);
            if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager()) // GPU排序管理器,来处理不同的GPU任务,然后 OnPreRender
            {
                GPUSortManager->OnPreRender(GraphBuilder);
            }
        }

这里面manager也是在addpass
看个结构


    /**
     * Defines a sort batch that regroups several sort tasks created by FGPUSortManager::AddTask(), becoming sort the batch elements.
     * The condition to regroup task in the same batch rely mostly around the key precisions and the sort location (PreRender or PostRenderOpaque).
     * Each SortBatch maps to a single GPU sort task.
     */
    struct FSortBatch
    {
        /** An Id defining this batch. Passed along FGPUSortKeyGenDelegate. */
        int32 Id = INDEX_NONE;
        /** The number of elements (tasks) grouped in this batch. */
        int32 NumElements = 0;

        /**
         * The common requirement flag for this batch. Compatible with each element.
         * As tasks are added, the flag can become more restrictive, and new registered tasks 
         * could require creating new batches if their requirements are too different.
         */
        EGPUSortFlags Flags = EGPUSortFlags::None;

        /** The buffer to hold the final sorted values. */
        FDynamicValueBuffer* DynamicValueBuffer = nullptr;

        /** The scratch buffers used to hold the initial keys and values and perform the GPU sort. */
        FParticleSortBuffers* SortBuffers = nullptr;

        /** This batch processing order in the frame. */
        ESortBatchProcessingOrder ProcessingOrder = ESortBatchProcessingOrder::Undefined;

        // 。。。 
    };
void FGPUSortManager::OnPreRender(FRDGBuilder& GraphBuilder)
{
    RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, GPUSort);
    RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());

    AddPass(
        GraphBuilder,
        RDG_EVENT_NAME("FGPUSortManager::OnPreRender"),
        [this](FRHICommandListImmediate& RHICmdList)
        {
            LLM_SCOPE(ELLMTag::GPUSort);
            FinalizeSortBatches(); // 设置一些标志位
            UpdateSortBuffersPool(RHICmdList); // 更新排序的缓冲区

            if (SortBatches.Num())
            {
                // Sort batches so that the next batch to handle is at the end of the array.
                SortBatches.Sort([](const FSortBatch& A, const FSortBatch& B) { return (uint32)A.ProcessingOrder > (uint32)B.ProcessingOrder; });

                SCOPED_GPU_STAT(RHICmdList, GPUKeyGenAndSort);
                while (SortBatches.Num() && SortBatches.Last().ProcessingOrder == ESortBatchProcessingOrder::KeyGenAndSortAfterPreRender)
                {
                    // Remove the SortBatch but don't remove the SortBuffers from the pool since it can be reused immediately.
                    FSortBatch SortBatch = SortBatches.Pop();
                    FParticleSortBuffers* SortBuffers = SortBuffersPool.Last();
                    SortBatch.SortBuffers = SortBuffers;
                    checkSlow(SortBuffers && SortBatch.GetUsedValueCount() <= SortBuffers->GetSize());

                    SCOPED_DRAW_EVENTF(RHICmdList, GPUSortBatch, TEXT("GPUSort_Batch%d(%s)"), SortBatch.Id, GetPrecisionString(SortBatch.Flags));

                    SortBatch.GenerateKeys(RHICmdList, Callbacks, EGPUSortFlags::KeyGenAfterPreRender);
                    SortBatch.SortAndResolve(RHICmdList, FeatureLevel);

                    // Release the sort batch.
                    checkSlow(SortBatch.DynamicValueBuffer && SortBatch.DynamicValueBuffer->CurrentSortBatchId == SortBatch.Id);
                    SortBatch.DynamicValueBuffer->CurrentSortBatchId = INDEX_NONE;
                    SortBatch.SortBuffers = nullptr;
                }

                for (int32 BatchIndex = SortBatches.Num() - 1; BatchIndex >= 0 && SortBatches[BatchIndex].ProcessingOrder == ESortBatchProcessingOrder::KeyGenAfterPreRenderAndSortAfterPostRenderOpaque; --BatchIndex)
                {
                    // Those sort batches will be processed again after PostRenderOpaque(). Because of this, the particle sort buffers can not be reused.
                    FSortBatch& SortBatch = SortBatches[BatchIndex];
                    FParticleSortBuffers* SortBuffers = SortBuffersPool.Pop();
                    SortBatch.SortBuffers = SortBuffers;
                    checkSlow(SortBuffers && SortBatch.GetUsedValueCount() <= SortBuffers->GetSize());

                    SCOPED_DRAW_EVENTF(RHICmdList, GPUSortBatchPreStep, TEXT("GPUSort_Batch%d(PreStep,%s)"), SortBatch.Id, GetPrecisionString(SortBatch.Flags));

                    SortBatch.GenerateKeys(RHICmdList, Callbacks, EGPUSortFlags::KeyGenAfterPreRender);
                }
            }
            PostPreRenderEvent.Broadcast(RHICmdList);
        }
    );
}

FGPUScene::Update

文档太长有点卡,下一章节从GPUSceneUpdate开始


    {
        RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, UpdateGPUScene);
        RDG_GPU_STAT_SCOPE(GraphBuilder, GPUSceneUpdate);

        if (bIsFirstSceneRenderer) // 第一次刷新RHI
        {
            GraphBuilder.SetFlushResourcesRHI();
        }

        Scene->GPUScene.Update(GraphBuilder, GetSceneUniforms(), *Scene, ExternalAccessQueue); // GPU SCENE Update

        for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) // 遍历VIEW
        {
            FViewInfo& View = Views[ViewIndex];
            RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);

            Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, *Scene, View, ExternalAccessQueue); // 更新动态基元shader数据

            Scene->GPUScene.DebugRender(GraphBuilder, *Scene, GetSceneUniforms(), View);
        }

        InstanceCullingManager.BeginDeferredCulling(GraphBuilder, Scene->GPUScene); // 延迟剔除

        if (Views.Num() > 0)
        {
            FViewInfo& View = Views[0];
            Scene->UpdatePhysicsField(GraphBuilder, View); // 物理
        }
    }
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇