智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

UE4 引擎剖析 - 渲染线程

Posted on 2020-07-15 12:19  Bill Yuan  阅读(2053)  评论(0编辑  收藏  举报

转自:https://zhuanlan.zhihu.com/p/78347351

一、渲染线程的初始化:

//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染线程执行体
class FRenderingThread : public FRunnable
{
  // 执行函数
  virtual uint32 Run(void) override
  {
    RenderingThreadMain( TaskGraphBoundSyncEvent );
  }
}
//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染线程初始化流程:
1. FEngineLoop::PreInit()
{
    StartRenderingThread()
}
2. StartRenderingThread()
{
    GRenderingThreadRunnable = new FRenderingThread()
    GRenderingThread = FRunnableThread::Create(GRenderingThreadRunnable, ...)
}

 

二、渲染线程的执行:(基于UE4的任务图系统: FTaskGraphInterface)

//////////////////////////////////////////////////////////////////////////////////////////////
// 任务图系统: 线程基类
class FTaskThreadBase : public FRunnable, FSingleThreadRunnable
{
    protected:
    /** Id / Index of this thread. **/
    ENamedThreads::Type ThreadId;
    /** Array of tasks for this task thread. */
    TArray<FBaseGraphTask*> NewTasks;
    /** back pointer to the owning FWorkerThread **/
    FWorkerThread* OwnerWorker;
}
// 任务图系统: 任意线程类
class FTaskThreadAnyThread : public FTaskThreadBase

//...
    virtual void ProcessTasksUntilQuit(int32 QueueIndex) override
    {
        check(Queue(QueueIndex).StallRestartEvent); // make sure we are started up

        Queue(QueueIndex).QuitForReturn = false;
        verify(++Queue(QueueIndex).RecursionGuard == 1);
        do
        {
            ProcessTasksNamedThread(QueueIndex, FPlatformProcess::SupportsMultithreading());
        } while (!Queue(QueueIndex).QuitForReturn && !Queue(QueueIndex).QuitForShutdown && FPlatformProcess::SupportsMultithreading()); // @Hack - quit now when running with only one thread.
        verify(!--Queue(QueueIndex).RecursionGuard);
    }

//...
    /**
    *    Process tasks until idle. May block if bAllowStall is true
    *    @param QueueIndex, Queue to process tasks from
    *    @param bAllowStall,  if true, the thread will block on the stall event when it runs out of tasks.
    **/
    uint64 ProcessTasks()
    {
        LLM_SCOPE(ELLMTag::TaskGraphTasksMisc);

        TStatId StallStatId;
        bool bCountAsStall = true;
        uint64 ProcessedTasks = 0;
#if STATS
        TStatId StatName;
        FCycleCounter ProcessingTasks;
        StatName = GET_STATID(STAT_TaskGraph_OtherTasks);
        StallStatId = GET_STATID(STAT_TaskGraph_OtherStalls);
        bool bTasksOpen = false;
        if (FThreadStats::IsCollectingData(StatName))
        {
            bTasksOpen = true;
            ProcessingTasks.Start(StatName);
        }
#endif
        verify(++Queue.RecursionGuard == 1);
        bool bDidStall = false;
        while (1)
        {
            FBaseGraphTask* Task = FindWork();
            if (!Task)
            {
#if STATS
                if (bTasksOpen)
                {
                    ProcessingTasks.Stop();
                    bTasksOpen = false;
                }
#endif

                TestRandomizedThreads();
                if (FPlatformProcess::SupportsMultithreading())
                {
                    FScopeCycleCounter Scope(StallStatId);
                    Queue.StallRestartEvent->Wait(MAX_uint32, bCountAsStall);
                    bDidStall = true;
                }
                if (Queue.QuitForShutdown || !FPlatformProcess::SupportsMultithreading())
                {
                    break;
                }
                TestRandomizedThreads();

#if STATS
                if (FThreadStats::IsCollectingData(StatName))
                {
                    bTasksOpen = true;
                    ProcessingTasks.Start(StatName);
                }
#endif
                continue;
            }
            TestRandomizedThreads();
#if YIELD_BETWEEN_TASKS
            // the Win scheduler is ill behaved and will sometimes let BG tasks run even when other tasks are ready....kick the scheduler between tasks
            if (!bDidStall && PriorityIndex == (ENamedThreads::BackgroundThreadPriority >> ENamedThreads::ThreadPriorityShift))
            {
                FPlatformProcess::Sleep(0);
            }
#endif
            bDidStall = false;
            Task->Execute(NewTasks, ENamedThreads::Type(ThreadId));
            ProcessedTasks++;
            TestRandomizedThreads();
            if (Queue.bStallForTuning)
            {
#if STATS
                if (bTasksOpen)
                {
                    ProcessingTasks.Stop();
                    bTasksOpen = false;
                }
#endif
                {
                    FScopeLock Lock(&Queue.StallForTuning);
                }
#if STATS
                if (FThreadStats::IsCollectingData(StatName))
                {
                    bTasksOpen = true;
                    ProcessingTasks.Start(StatName);
                }
#endif
            }
        }
        verify(!--Queue.RecursionGuard);
        return ProcessedTasks;
    }

 

//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染线程的执行流程:
1. uint32 FRenderingThread::Run(void)
{
    RenderingThreadMain( TaskGraphBoundSyncEvent );
}

2. void RenderingThreadMain( FEvent* TaskGraphBoundSyncEvent )
{
    // 任务图系统: 将渲染线程设置为的当前线程
    FTaskGraphInterface::Get().AttachToThread(RenderThread);

    // 任务图系统: 执行当前线程
    FTaskGraphInterface::Get().ProcessThreadUntilRequestReturn(RenderThread);
}

3. void FTaskGraphImplementation::AttachToThread(ENamedThreads::Type CurrentThread)
{
    CurrentThread = ENamedThreads::GetThreadIndex(CurrentThread);
    check(NumTaskThreadsPerSet);
    check(CurrentThread >= 0 && CurrentThread < NumNamedThreads);
    check(!WorkerThreads[CurrentThread].bAttached);
    Thread(CurrentThread).InitializeForCurrentThread();
}

4. void FTaskGraphImplementation::ProcessThreadUntilRequestReturn(ENamedThreads::Type CurrentThread)
{
    int32 QueueIndex = ENamedThreads::GetQueueIndex(CurrentThread);
    CurrentThread = ENamedThreads::GetThreadIndex(CurrentThread);
    check(CurrentThread >= 0 && CurrentThread < NumNamedThreads);
    check(CurrentThread == GetCurrentThread());
    Thread(CurrentThread).ProcessTasksUntilQuit(QueueIndex);
}

5. void FTaskThreadAnyThread::ProcessTasksUntilQuit(int32 QueueIndex)
{
    do
    {
        ProcessTasks();
    } while (!Queue.QuitForShutdown && FPlatformProcess::SupportsMultithreading()); // @Hack - quit now when     running with only one thread.
}

6. void FTaskThreadAnyThread::ProcessTasks()
{
    while (1)
    {
        FBaseGraphTask* Task = FindWork();
        Task->Execute(NewTasks, ENamedThreads::Type(ThreadId));
    }
}

 

备注: FBaseGraphTask就是任务图系统的执行的内容,参考:UE4 引擎剖析 - 多线程

三、计算玩家的视图信息。

//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染目标
class FRenderTarget
{
    protected:
    FTexture2DRHIRef RenderTargetTextureRHI;
}

/**
*封装视区的I/O。
*视区显示是使用独立于平台的RHI实现的。
*/
class FViewport : public FRenderTarget, protected FRenderResource
{
public:
    ENGINE_API void Draw( bool bShouldPresent = true );

protected:
    /** The viewport's client. */
    FViewportClient* ViewportClient;
}

/**
*到视区客户机的抽象接口。
*视区的客户机处理视区接收到的输入,并绘制视区。
*/
class FViewportClient
{
    public:
    virtual void Draw(FViewport* Viewport,FCanvas* Canvas) {}
}

/**
*游戏视区(FViewport)是平台特定的渲染、音频和输入子系统。
*GameViewportClient是引擎与游戏视区的接口。
*只为游戏的每个实例创建一个GameViewportClient。
*唯一的例外是在编辑器(PIE)模式下,只有一个Engine引擎实例,但是有多个GameInstance游戏实例,则会创建多个GameViewportClients视区客户端实例
*职责:
*将输入事件传播到全局交互列表
*/
UCLASS(Within=Engine, transient, config=Engine)
class ENGINE_API UGameViewportClient : public UScriptViewportClient, public FExec
{
public:
    virtual void Draw(FViewport* Viewport,FCanvas* SceneCanvas) override;

public:
    /** The platform-specific viewport which this viewport client is attached to. */
    FViewport* Viewport;

protected:
    /* The relative world context for this viewport */
    UPROPERTY()
    UWorld* World;

    UPROPERTY()
    UGameInstance* GameInstance;
}

/**
* 将一组视图转换为只有不同视图转换和所有者参与者的场景。
*/
class ENGINE_API FSceneViewFamily
{
    /** The render target which the views are being rendered to. */
    FSceneInterface* Scene;
}

/**
* 当视图超出范围时删除其视图的视图族。
*/
class FSceneViewFamilyContext : public FSceneViewFamily
{
}

/**
* 到场景的私有场景管理器实现的接口. 使用GetRendererModule().AllocateScene来创建.
*/
class FSceneInterface
{
    /**
    * 向场景中添加新的基本体组件
    */
    virtual void AddPrimitive(UPrimitiveComponent* Primitive) = 0;

    /**
    * 向场景中添加新的灯光组件
    */
    virtual void AddLight(ULightComponent* Light) = 0;

    /**
    * 将新贴花组件添加到场景中
    */
    virtual void AddDecal(UDecalComponent* Component) = 0;

    /**
    * 将新的指数高度雾组件添加到场景中
    */
    virtual void AddExponentialHeightFog(class UExponentialHeightFogComponent* FogComponent) = 0;

    /**
    * 向场景中添加新的大气雾组件
    */
    virtual void AddAtmosphericFog(class UAtmosphericFogComponent* FogComponent) = 0;

    /**
    * 将风源组件添加到场景中。
    */
    virtual void AddWindSource(class UWindDirectionalSourceComponent* WindComponent) = 0;

    /**
    * 将SpeedTree风计算对象添加到场景中。
    */
    virtual void AddSpeedTreeWind(class FVertexFactory* VertexFactory, const class UStaticMesh* StaticMesh) = 0;
}

/**
* 从场景空间到二维屏幕区域的投影。
*/
class ENGINE_API FSceneView
{
public:
    const FSceneViewFamily* Family;

public:
    FSceneViewInitOptions SceneViewInitOptions;
    FViewMatrices ViewMatrices;
    FViewMatrices ShadowViewMatrices;
}

// Projection data for a FSceneView
struct FSceneViewProjectionData
{
    /** The view origin. */
    FVector ViewOrigin;

    /** Rotation matrix transforming from world space to view space. */
    FMatrix ViewRotationMatrix;

    /** UE4 projection matrix projects such that clip space Z=1 is the near plane, and Z=0 is the infinite far plane. */
    FMatrix ProjectionMatrix;

protected:
    //The unconstrained (no aspect ratio bars applied) view rectangle (also unscaled)
    FIntRect ViewRect;

    // The constrained view rectangle (identical to UnconstrainedUnscaledViewRect if aspect ratio is not constrained)
    FIntRect ConstrainedViewRect;
}

// Construction parameters for a FSceneView
struct FSceneViewInitOptions : public FSceneViewProjectionData
{
}

struct FViewMatrices
{
    // 渲染的基础矩阵
    FMatrix ViewProjectionMatrix;
}

 

//////////////////////////////////////////////////////////////////////////////////////////////
// 主线程计算玩家的视图信息流程:
1. void FEngineLoop::Tick()
{
    GEngine->Tick(FApp::GetDeltaTime(), bIdleMode);
}

2. void UGameEngine::Tick( float DeltaSeconds, bool bIdleMode )
{
    UGameEngine::RedrawViewports()
}

3. void UGameEngine::RedrawViewports( bool bShouldPresent /*= true*/ )
{
    if ( GameViewport != NULL )
    {
        if ( GameViewport->Viewport != NULL )
        {
            GameViewport->Viewport->Draw(bShouldPresent);
        }
    }
}

4. void FViewport::Draw( bool bShouldPresent /*= true */)
{
    UWorld* ViewportWorld = ViewportClient->GetWorld();
    FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->FeatureLevel : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
    Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
    {
        // Make sure the Canvas is not rendered upside down
        Canvas.SetAllowSwitchVerticalAxis(false);
        ViewportClient->Draw(this, &Canvas);
    }
    Canvas.Flush_GameThread();
    ViewportClient->ProcessScreenShots(this);
}

5. void UGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas)
{
    // 1.创建用于将世界场景渲染到视区的渲染目标的视图族
    FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
        InViewport,
        MyWorld->Scene,
        EngineShowFlags)
        .SetRealtimeUpdate(true));

    // 2.计算所有玩家的视图信息。
    for (FLocalPlayerIterator Iterator(GEngine, MyWorld); Iterator; ++Iterator)
    {
        ULocalPlayer* LocalPlayer = *Iterator;
        if (LocalPlayer)
        {
            APlayerController* PlayerController = LocalPlayer->PlayerController;

            const bool bEnableStereo = GEngine->IsStereoscopic3D(InViewport);
            const int32 NumViews = bStereoRendering ? ((ViewFamily.IsMonoscopicFarFieldEnabled()) ? 3 : GEngine->StereoRenderingDevice->GetDesiredNumberOfViews(bStereoRendering)) : 1;

            for (int32 i = 0; i < NumViews; ++i)
            {
                // 计算玩家的视图信息。
                FVector ViewLocation;
                FRotator ViewRotation;

                EStereoscopicPass PassType = bStereoRendering ? GEngine->StereoRenderingDevice->GetViewPassForIndex(bStereoRendering, i) : eSSP_FULL;

                FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, &GameViewDrawer, PassType);
            }
        }
    }

    // 3.绘制玩家视图
    if (!bDisableWorldRendering && !bUIDisableWorldRendering && PlayerViewMap.Num() > 0 && FSlateApplication::Get().GetPlatformApplication()->IsAllowedToRender()) //-V560
    {
        GetRendererModule().BeginRenderingViewFamily(SceneCanvas,&ViewFamily);
    }
    else
    {
        // Make sure RHI resources get flushed if we're not using a renderer
        ENQUEUE_UNIQUE_RENDER_COMMAND( UGameViewportClient_FlushRHIResources,{
            FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
        });
    }
}

5-1. FSceneView* ULocalPlayer::CalcSceneView( class FSceneViewFamily* ViewFamily,
    FVector& OutViewLocation,
    FRotator& OutViewRotation,
    FViewport* Viewport,
    class FViewElementDrawer* ViewDrawer,
    EStereoscopicPass StereoPass)
{
    SCOPE_CYCLE_COUNTER(STAT_CalcSceneView);

    FSceneViewInitOptions ViewInitOptions;

    if (!CalcSceneViewInitOptions(ViewInitOptions, Viewport, ViewDrawer, StereoPass))
    {
        return nullptr;
    }

    // Get the viewpoint...technically doing this twice
    // but it makes GetProjectionData better
    FMinimalViewInfo ViewInfo;
    GetViewPoint(ViewInfo, StereoPass);
    OutViewLocation = ViewInfo.Location;
    OutViewRotation = ViewInfo.Rotation;
    ViewInitOptions.bUseFieldOfViewForLOD = ViewInfo.bUseFieldOfViewForLOD;
    ViewInitOptions.FOV = ViewInfo.FOV;
    ViewInitOptions.DesiredFOV = ViewInfo.DesiredFOV;

    // Fill out the rest of the view init options
    ViewInitOptions.ViewFamily = ViewFamily;
}

5-2. bool ULocalPlayer::CalcSceneViewInitOptions(
    struct FSceneViewInitOptions& ViewInitOptions,
    FViewport* Viewport,
    class FViewElementDrawer* ViewDrawer,
    EStereoscopicPass StereoPass)
{
    // get the projection data
    if (GetProjectionData(Viewport, StereoPass, /*inout*/ ViewInitOptions) == false)
    {
        // Return NULL if this we didn't get back the info we needed
        return false;
    }
}

5-3. bool ULocalPlayer::GetProjectionData(FViewport* Viewport, EStereoscopicPass StereoPass, FSceneViewProjectionData& ProjectionData) const
{
    ProjectionData.SetViewRectangle(UnconstrainedRectangle);

    // Get the viewpoint.
    FMinimalViewInfo ViewInfo;
    GetViewPoint(/*out*/ ViewInfo, StereoPass);

    // Create the view matrix
    ProjectionData.ViewOrigin = StereoViewLocation;
    ProjectionData.ViewRotationMatrix = FInverseRotationMatrix(ViewInfo.Rotation) * FMatrix(
    FPlane(0, 0, 1, 0),
    FPlane(1, 0, 0, 0),
    FPlane(0, 1, 0, 0),
    FPlane(0, 0, 0, 1));
}

 


四、渲染模块绘制玩家视图。

//////////////////////////////////////////////////////////////////////////////////////////////
/**
*渲染器场景是渲染器模块专用的。
*通常这是UWorld的渲染器版本,但也可以创建FScene以在没有UWorld的编辑器中预览。
*场景存储独立于任何视图或帧的渲染器状态,主要操作是添加和删除基本体和灯光。
*/
class FScene : public FSceneInterface
{
public:

    /** An optional world associated with the scene. */
    UWorld* World;

}

/**
*用作场景渲染功能的范围。
*它在游戏线程中由 FSceneViewFamily::BeginRender 初始化,然后传递给呈现线程。
*渲染线程调用 Render(),并在返回时删除场景渲染器。
*/
class FSceneRenderer
{
public:
    virtual void Render(FRHICommandListImmediate& RHICmdList) = 0;
    virtual void RenderHitProxies(FRHICommandListImmediate& RHICmdList) {}

    /** Creates a scene renderer based on the current feature level. */
    static FSceneRenderer* CreateSceneRenderer(const FSceneViewFamily* InViewFamily, FHitProxyConsumer* HitProxyConsumer);

public:
    /** The views being rendered. */
    TArray<FViewInfo> Views;
}

/**
* 实现简单前向着色和相关功能的渲染器。
*/
class FMobileSceneRenderer : public FSceneRenderer
{
public:
    /** 渲染视图族。 */
    virtual void Render(FRHICommandListImmediate& RHICmdList) override;

    /**
    *初始化场景视图。
    *检查可见性,对半透明项进行排序等。
    */
    void InitViews(FRHICommandListImmediate& RHICmdList);
}

/**
* 实现延迟着色管道和相关功能的场景渲染器。
*/
class FDeferredShadingSceneRenderer : public FSceneRenderer
{
    /** 渲染视图族。 */
    virtual void Render(FRHICommandListImmediate& RHICmdList) override;

    /** 确定每个视图可见的基元. */
    bool InitViews(FRHICommandListImmediate& RHICmdList, struct FILCUpdatePrimTaskData& ILCTaskData, FGraphEventArray& SortEvents, FGraphEventArray& UpdateViewCustomDataEvents);
}

/** 具有场景渲染器使用的附加状态的FSceneView. */
class FViewInfo : public FSceneView
{
}
//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染模块绘制玩家视图流程:
1. void FRendererModule::BeginRenderingViewFamily(FCanvas* Canvas, FSceneViewFamily* ViewFamily)
{
    UWorld* World = nullptr;

    FScene* const Scene = ViewFamily->Scene->GetRenderScene();
    if (Scene)
    {
        World = Scene->GetWorld();
        if (World)
        {
            //确保所有渲染代理在启动BeginEnderView族之前都是最新的
            World->SendAllEndOfFrameUpdates();
        }
    }

    if (Scene)
    {
        // 构建场景渲染器。这会将视图族属性复制到自己的结构中。
        FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, Canvas->GetHitProxyConsumer());

        RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
    }
}

// 将所有渲染更新发送到渲染线程。
2. void UWorld::SendAllEndOfFrameUpdates()
{
}

// 基于当前功能级别创建场景渲染器
3. FSceneRenderer* FSceneRenderer::CreateSceneRenderer(const FSceneViewFamily* InViewFamily, FHitProxyConsumer* HitProxyConsumer)
{
    EShadingPath ShadingPath = InViewFamily->Scene->GetShadingPath();
    FSceneRenderer* SceneRenderer = nullptr;

    if (ShadingPath == EShadingPath::Deferred)
    {
        SceneRenderer = new FDeferredShadingSceneRenderer(InViewFamily, HitProxyConsumer);
    }
    else
    {
        check(ShadingPath == EShadingPath::Mobile);
        SceneRenderer = new FMobileSceneRenderer(InViewFamily, HitProxyConsumer);
    }

    return SceneRenderer;
}

/**
* 辅助函数在渲染线程中执行实际工作。
*/
4. static void RenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer)
{
    if(SceneRenderer->ViewFamily.EngineShowFlags.HitProxies)
    {
        // 渲染场景的命中代理。
        SceneRenderer->RenderHitProxies(RHICmdList);
    }
    else
    {
        // 渲染场景。
        SceneRenderer->Render(RHICmdList);
    }
}

/**
* 渲染视图族。
*/
// 移动端渲染器
5. void FMobileSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
    // 找出可见的原始组件。
    InitViews(RHICmdList);

    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLMM_BasePass));
    RenderMobileBasePass(RHICmdList, ViewList);

    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLMM_Occlusion));
    // Issue occlusion queries
    RenderOcclusion(RHICmdList);

    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLMM_SceneEnd));
    RenderFinish(RHICmdList);
}
// 延迟渲染器
void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
    bool bDoInitViewAftersPrepass = InitViews(RHICmdList, ILCTaskData, SortEvents, UpdateViewCustomDataEvents);

    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_BasePass));
    RenderBasePass(RHICmdList, BasePassDepthStencilAccess, ForwardScreenSpaceShadowMask.GetReference());
    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterBasePass));
    ServiceLocalQueue();

    {
        SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_RenderFinish);
        RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_RenderFinish));
        RenderFinish(RHICmdList);
        RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_AfterFrame));
    }
    ServiceLocalQueue();
}

/**
* Initialize scene's views.
* Check visibility, sort translucent items, etc.
*/
6. void FMobileSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList)
{
    RHICmdList.SetCurrentStat(GET_STATID(STAT_CLMM_InitVIews));

    SCOPED_DRAW_EVENT(RHICmdList, InitViews);

    SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);

    FILCUpdatePrimTaskData ILCTaskData;
    PreVisibilityFrameSetup(RHICmdList);
    ComputeViewVisibility(RHICmdList);
    PostVisibilityFrameSetup(ILCTaskData);

    const bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows;

    if (bDynamicShadows && !IsSimpleForwardShadingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel)))
    {
        // Setup dynamic shadows.
        InitDynamicShadows(RHICmdList);
    }

    // if we kicked off ILC update via task, wait and finalize.
    if (ILCTaskData.TaskRef.IsValid())
    {
        Scene->IndirectLightingCache.FinalizeCacheUpdates(Scene, *this, ILCTaskData);
    }

    // initialize per-view uniform buffer. Pass in shadow info as necessary.
    for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
    {
        // Initialize the view's RHI resources.
        Views[ViewIndex].InitRHIResources();

        // Create the directional light uniform buffers
        CreateDirectionalLightUniformBuffers(Views[ViewIndex]);
    }

    // Now that the indirect lighting cache is updated, we can update the primitive precomputed lighting buffers.
    UpdatePrimitivePrecomputedLightingBuffers();

    UpdatePostProcessUsageFlags();

    PostInitViewCustomData();

    OnStartFrame(RHICmdList);
}

7. void FSceneRenderer::OnStartFrame(FRHICommandListImmediate& RHICmdList)
{
    for(FViewInfo& View : Views)
    {
        if(View.ViewState)
        {
            View.ViewState->OnStartFrame(View, ViewFamily);
        }
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////
// StaticMesh的绘制流程

// 延迟渲染器
1. void FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
    RenderBasePass(RHICmdList, BasePassDepthStencilAccess, ForwardScreenSpaceShadowMask.GetReference());
}


/**
* 渲染场景的基础通道
*/
2. bool FDeferredShadingSceneRenderer::RenderBasePass(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, IPooledRenderTarget* ForwardScreenSpaceShadowMask)
{
}

template<typename DrawingPolicyType>
template<InstancedStereoPolicy InstancedStereo>
3. bool TStaticMeshDrawList<DrawingPolicyType>::DrawVisibleInner(
    FRHICommandList& RHICmdList,
    const FViewInfo& View,
    const typename DrawingPolicyType::ContextDataType PolicyContext,
    FDrawingPolicyRenderState& DrawRenderState,
    const TBitArray<SceneRenderingBitArrayAllocator>* const StaticMeshVisibilityMap,
    const TArray<uint64, SceneRenderingAllocator>* const BatchVisibilityArray,
    const StereoPair* const StereoView,
    int32 FirstPolicy, int32 LastPolicy,
    bool bUpdateCounts
)
{
    Count += DrawElement<InstancedStereoPolicy::Disabled>(RHICmdList, View, PolicyContext, DrawRenderState, Element, BatchElementMask,
}

template<typename DrawingPolicyType>
template<InstancedStereoPolicy InstancedStereo>
4. int32 TStaticMeshDrawList<DrawingPolicyType>::DrawElement(
    FRHICommandList& RHICmdList,
    const FViewInfo& View,
    const typename DrawingPolicyType::ContextDataType PolicyContext,
    FDrawingPolicyRenderState& DrawRenderState,
    const FElement& Element,
    uint64 BatchElementMask,
    FDrawingPolicyLink* DrawingPolicyLink,
    bool& bDrawnShared
)
{
    DrawingPolicyLink->DrawingPolicy.DrawMesh(RHICmdList, View, *Element.Mesh, BatchElementIndex, true);
}

5. void FMeshDrawingPolicy::DrawMesh(FRHICommandList& RHICmdList, const FSceneView& View, const FMeshBatch& Mesh, int32 BatchElementIndex, const bool bIsInstancedStereo) const
{
    RHICmdList.DrawIndexedPrimitive(
        BatchElement.IndexBuffer->IndexBufferRHI,
        Mesh.Type,
        BatchElement.BaseVertexIndex,
        0,
        BatchElement.MaxVertexIndex - BatchElement.MinVertexIndex + 1,
        BatchElement.FirstIndex,
        BatchElement.NumPrimitives,
        InstanceCount * GetInstanceFactor()
    );
}

6. void FD3D11DynamicRHI::RHIDrawIndexedPrimitive(FIndexBufferRHIParamRef IndexBufferRHI,uint32 PrimitiveType,int32 BaseVertexIndex,uint32 FirstInstance,uint32 NumVertices,uint32 StartIndex,uint32 NumPrimitives,uint32 NumInstances)
{
    FD3D11IndexBuffer* IndexBuffer = ResourceCast(IndexBufferRHI);

    // called should make sure the input is valid, this avoid hidden bugs
    ensure(NumPrimitives > 0);

    RHI_DRAW_CALL_STATS(PrimitiveType,NumInstances*NumPrimitives);

    GPUProfilingData.RegisterGPUWork(NumPrimitives * NumInstances, NumVertices * NumInstances);

    CommitGraphicsResourceTables();
    CommitNonComputeShaderConstants();

    // determine 16bit vs 32bit indices
    uint32 SizeFormat = sizeof(DXGI_FORMAT);
    const DXGI_FORMAT Format = (IndexBuffer->GetStride() == sizeof(uint16) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT);

    uint32 IndexCount = GetVertexCountForPrimitiveCount(NumPrimitives,PrimitiveType);

    // Verify that we are not trying to read outside the index buffer range
    // test is an optimized version of: StartIndex + IndexCount <= IndexBuffer->GetSize() / IndexBuffer->GetStride()
    checkf((StartIndex + IndexCount) * IndexBuffer->GetStride() <= IndexBuffer->GetSize(),
    TEXT("Start %u, Count %u, Type %u, Buffer Size %u, Buffer stride %u"), StartIndex, IndexCount, PrimitiveType, IndexBuffer->GetSize(), IndexBuffer->GetStride());

    StateCache.SetIndexBuffer(IndexBuffer->Resource, Format, 0);
    VerifyPrimitiveType(PSOPrimitiveType, PrimitiveType);
    StateCache.SetPrimitiveTopology(GetD3D11PrimitiveType(PrimitiveType,bUsingTessellation));

    if (NumInstances > 1 || FirstInstance != 0)
    {
        const uint64 TotalIndexCount = (uint64)NumInstances * (uint64)IndexCount + (uint64)StartIndex;
        checkf(TotalIndexCount <= (uint64)0xFFFFFFFF, TEXT("Instanced Index Draw exceeds maximum d3d11 limit: Total: %llu, NumInstances: %llu, IndexCount: %llu, StartIndex: %llu, FirstInstance: %llu"), TotalIndexCount, NumInstances, IndexCount, StartIndex, FirstInstance);
        Direct3DDeviceIMContext->DrawIndexedInstanced(IndexCount, NumInstances, StartIndex, BaseVertexIndex, FirstInstance);
    }
    else
    {
        Direct3DDeviceIMContext->DrawIndexed(IndexCount,StartIndex,BaseVertexIndex);
    }
}

 

//////////////////////////////////////////////////////////////////////////////////////////////
// 通过宏来创建渲染任务
ENQUEUE_UNIQUE_RENDER_COMMAND

// 通过模板函数来创建渲染任务
template<typename TSTR, typename LAMBDA>
FORCEINLINE_DEBUGGABLE void EnqueueUniqueRenderCommand(LAMBDA&& Lambda)
{
    typedef TEnqueueUniqueRenderCommandType<TSTR, LAMBDA> EURCType;

    #if 0 // UE_SERVER && UE_BUILD_DEBUG
        UE_LOG(LogRHI, Warning, TEXT("Render command '%s' is being executed on a dedicated server."), TSTR::TStr())
    #endif
    // always use a new task for devices that have GUseThreadedRendering=false
    // even when the call is from the rendering thread
    if (GUseThreadedRendering && IsInRenderingThread())
    {
        FRHICommandListImmediate& RHICmdList = GetImmediateCommandList_ForRenderCommand();
        Lambda(RHICmdList);
    }
    else
    {
        if (ShouldExecuteOnRenderThread())
        {
            CheckNotBlockedOnRenderThread();
            TGraphTask<EURCType>::CreateTask().ConstructAndDispatchWhenReady(Forward<LAMBDA>(Lambda));
        }
        else
        {
            EURCType TempCommand(Forward<LAMBDA>(Lambda));
            FScopeCycleCounter EURCMacro_Scope(TempCommand.GetStatId());
            TempCommand.DoTask(ENamedThreads::GameThread, FGraphEventRef());
        }
    }
}

 参考:

1、渲染一个正方体的堆栈信息

2、图表