UE4 引擎剖析 - 渲染線程


轉自: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、圖表


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM