• 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、图表

  • 相关阅读:
    python 模块之-time
    asp.net web 通过IHttpAsyncHandler接口进行消息推送
    模拟登陆
    Socket发送文件
    asp.net 在自己指定的文件夹下面弄个App.config来读取配置
    C#多线程数据分布加载
    socket收发消息
    .net分布在指定文件夹的web.confgi或者app.config
    linux 修改oracle字符集
    文件读取草稿(excel,csv)
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/13304817.html
Copyright © 2020-2023  润新知