一、渲染线程的初始化:



//////////////////////////////////////////////////////////////////////////////////////////////
// 渲染线程执行体
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、渲染一个正方体的堆栈信息

UE4 引擎剖析 - 渲染线程_#if

2、图表

UE4 引擎剖析 - 渲染线程_#if_02