转自: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、图表