FlushRenderingCommands
void FlushRenderingCommands()
{
if (!GIsRHIInitialized) // RHI必须初始化
{
return;
}
TRACE_CPUPROFILER_EVENT_SCOPE(FlushRenderingCommands); // 开启CPU性能分析
FCoreRenderDelegates::OnFlushRenderingCommandsStart.Broadcast(); // 广播事件,开始刷命令了
FSuspendRenderingTickables SuspendRenderingTickables; // 挂起渲染的tick
// Need to flush GT because render commands from threads other than GT are sent to
// the main queue of GT when RT is disabled
if (!GIsThreadedRendering
&& !FTaskGraphInterface::Get().IsThreadProcessingTasks(ENamedThreads::GameThread)
&& !FTaskGraphInterface::Get().IsThreadProcessingTasks(ENamedThreads::GameThread_Local))
{
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread_Local);
}
// ENQUEUE_RENDER_COMMAND 会让里面的lambda执行在渲染线程中
ENQUEUE_RENDER_COMMAND(FlushPendingDeleteRHIResourcesCmd)([](FRHICommandListImmediate& RHICmdList)
{
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
//double flush to flush out the deferred deletions queued into the ImmediateCmdList
RHICmdList.ImmediateFlush(EImmediateFlushType::FlushRHIThread);
});
// Find the objects which may be cleaned up once the rendering thread command queue has been flushed.
// 等待清空的对象列表
FPendingCleanupObjects* PendingCleanupObjects = GetPendingCleanupObjects();
// Issue a fence command to the rendering thread and wait for it to complete.
// 围栏
FRenderCommandFence Fence;
Fence.BeginFence();
Fence.Wait();
// Delete the objects which were enqueued for deferred cleanup before the command queue flush.
delete PendingCleanupObjects;
FCoreRenderDelegates::OnFlushRenderingCommandsEnd.Broadcast();
}
回忆我们在DX12里面,提交完命令后使用围栏,等待命令完成。类似的功能
ThrowIfFailed(GraphicsCommandList->Close())
ID3D12CommandList* CommandLists[] = {GraphicsCommandList.Get()};
CommandQueue->ExecuteCommandLists(_countof(CommandLists), CommandLists);
WaitGPUCommandQueueComplete();
void CDirectXRenderingEngine::WaitGPUCommandQueueComplete()
{
CurrentFenceValue++;
// set fence value, and wait for GPU to finish
ThrowIfFailed(CommandQueue->Signal(Fence.Get(), CurrentFenceValue))
if (Fence->GetCompletedValue() < CurrentFenceValue)
{
HANDLE EventHandle = CreateEventEx(nullptr, nullptr, false, EVENT_ALL_ACCESS);
ThrowIfFailed(Fence->SetEventOnCompletion(CurrentFenceValue, EventHandle))
WaitForSingleObject(EventHandle, INFINITE); // block the thread until GPU finish
CloseHandle(EventHandle);
}
}
UpdateSingleViewportClient
了解虚幻是怎么渲染一帧的
bool UEditorEngine::UpdateSingleViewportClient(FEditorViewportClient* InViewportClient, const bool bInAllowNonRealtimeViewportToDraw, bool bLinkedOrthoMovement )
{
bool bUpdatedNonRealtimeViewport = false;
if (InViewportClient->Viewport->IsSlateViewport())
{
// When rendering the viewport we need to know whether the final result will be shown on a HDR display. This affects the final post processing step
FSceneViewport *SceneViewport = static_cast<FSceneViewport*>(InViewportClient->Viewport);
TSharedPtr<SWindow> Window = SceneViewport->FindWindow();
if (Window)
{
InViewportClient->Viewport->SetHDRMode(Window->GetIsHDR());
}
}
如果是SlateViewport。那么就cast为 FSceneViewport。
并从中获得 SWindow,设置HDR。
class FSceneViewport : public FViewportFrame, public FViewport, public ISlateViewport, public IViewportRenderTargetProvider
{
};
看一下函数和属性,初步印象,基本都是描述一个窗口的。例如鼠标键盘相关。几何数据,Init Release RHI , 资源等。 [03A-10]
bool UEditorEngine::UpdateSingleViewportClient(FEditorViewportClient* InViewportClient, const bool bInAllowNonRealtimeViewportToDraw, bool bLinkedOrthoMovement )
{
bool bUpdatedNonRealtimeViewport = false;
// 。。。
// Always submit view information for content streaming
// otherwise content for editor view can be streamed out if there are other views (ex: thumbnails)
if (InViewportClient->IsPerspective())
{
float XSize = static_cast<float>(InViewportClient->Viewport->GetSizeXY().X);
IStreamingManager::Get().AddViewInformation( InViewportClient->GetViewLocation(), XSize, XSize / FMath::Tan(FMath::DegreesToRadians(InViewportClient->ViewFOV * 0.5f)) );
}
如果是透视视口。视场角的屏幕大小
Tan 0.5FOV = XSize / FOVScreenSize
bool UEditorEngine::UpdateSingleViewportClient(FEditorViewportClient* InViewportClient, const bool bInAllowNonRealtimeViewportToDraw, bool bLinkedOrthoMovement )
{
bool bUpdatedNonRealtimeViewport = false;
// ...
// Only allow viewports to be drawn if we are not throttling for slate UI responsiveness or if the viewport client requested a redraw
// Note about bNeedsRedraw: Redraws can happen during some Slate events like checking a checkbox in a menu to toggle a view mode in the viewport. In those cases we need to show the user the results immediately
if( FSlateThrottleManager::Get().IsAllowingExpensiveTasks() || InViewportClient->bNeedsRedraw ) // 允许昂贵的认为,并且需要渲染
{
// Switch to the world used by the viewport before its drawn
FScopedConditionalWorldSwitcher WorldSwitcher( InViewportClient ); // 游戏浏览的时候,临时切到另一个世界,过作用域再切回来
// Add view information for perspective viewports.
if( InViewportClient->IsPerspective() ) // 透视
{
if (UWorld* ViewportClientWorld = InViewportClient->GetWorld())
{
ViewportClientWorld->ViewLocationsRenderedLastFrame.Add(InViewportClient->GetViewLocation()); // 记录上一次的视口位置
}
// If we're currently simulating in editor, then we'll need to make sure that sub-levels are streamed in.
// When using PIE, this normally happens by UGameViewportClient::Draw(). But for SIE, we need to do
// this ourselves!
if( PlayWorld != NULL && bIsSimulatingInEditor && InViewportClient->IsSimulateInEditorViewport() ) // SIE
{
// Update level streaming.
InViewportClient->GetWorld()->UpdateLevelStreaming(); // 更新关卡流,为了无缝关卡切换用
// Also make sure hit proxies are refreshed for SIE viewports, as the user may be trying to grab an object or widget manipulator that's moving!
if( InViewportClient->IsRealtime() )
{
// @todo simulate: This may cause simulate performance to be worse in cases where you aren't needing to interact with gizmos. Consider making this optional.
InViewportClient->RequestInvalidateHitProxy( InViewportClient->Viewport ); // 实时的命中代理无效,例如SIE模式下,点视口都是无效的。
}
}
}
// Redraw the viewport if it's realtime.
if( InViewportClient->IsRealtime() )
{
InViewportClient->Viewport->Draw();
InViewportClient->bNeedsRedraw = false;
InViewportClient->bNeedsLinkedRedraw = false;
}
// Redraw any linked ortho viewports that need to be updated this frame.
else if( InViewportClient->IsOrtho() && bLinkedOrthoMovement && InViewportClient->IsVisible() ) // 正交
{
if( InViewportClient->bNeedsLinkedRedraw || InViewportClient->bNeedsRedraw )
{
// Redraw this viewport
InViewportClient->Viewport->Draw();
InViewportClient->bNeedsLinkedRedraw = false;
InViewportClient->bNeedsRedraw = false;
}
else
{
// This viewport doesn't need to be redrawn. Skip this frame and increment the number of frames we skipped.
InViewportClient->FramesSinceLastDraw++;
}
}
// Redraw the viewport if there are pending redraw, and we haven't already drawn one viewport this frame.
else if (InViewportClient->bNeedsRedraw && bInAllowNonRealtimeViewportToDraw)
{
InViewportClient->Viewport->Draw();
InViewportClient->bNeedsRedraw = false;
bUpdatedNonRealtimeViewport = true;
}
else if(UWorld* World = GetWorld())
{
// We're not rendering but calculate the view anyway so that we can cache the last "rendered" view info in the UWorld.
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(InViewportClient->Viewport, InViewportClient->GetScene(), InViewportClient->EngineShowFlags));
FSceneView* View = InViewportClient->CalcSceneView(&ViewFamily);
FWorldCachedViewInfo& WorldViewInfo = World->CachedViewInfoRenderedLastFrame.AddDefaulted_GetRef();
WorldViewInfo.ViewMatrix = View->ViewMatrices.GetViewMatrix();
WorldViewInfo.ProjectionMatrix = View->ViewMatrices.GetProjectionMatrix();
WorldViewInfo.ViewProjectionMatrix = View->ViewMatrices.GetViewProjectionMatrix();
WorldViewInfo.ViewToWorld = View->ViewMatrices.GetInvViewMatrix();
World->LastRenderTime = World->GetTimeSeconds();
}
if (InViewportClient->bNeedsInvalidateHitProxy)
{
InViewportClient->Viewport->InvalidateHitProxy();
InViewportClient->bNeedsInvalidateHitProxy = false;
}
}
return bUpdatedNonRealtimeViewport;
}
FViewport::Draw
void FViewport::Draw( bool bShouldPresent /*= true */)
{
SCOPED_NAMED_EVENT(FViewport_Draw, FColor::Red); // 性能分析作用域事件
UWorld* World = GetClient()->GetWorld();
// Ignore reentrant draw calls, since we can only redraw one viewport at a time.
static bool bReentrant = false; // 是否正在进行重入绘制
if(!bReentrant)
{
// See what screenshot related features are required
static const auto CVarDumpFrames = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.BufferVisualizationDumpFrames"));
// bTakeHighResScreenShot 会在函数 bool FViewport::TakeHighResScreenShot() 中被设置
GIsHighResScreenshot = GIsHighResScreenshot || bTakeHighResScreenShot;
// FScreenshotRequest::IsScreenshotRequested() 会在 FScreenshotRequest::RequestScreenshot 被设置
bool bAnyScreenshotsRequired = FScreenshotRequest::IsScreenshotRequested() || GIsHighResScreenshot || GIsDumpingMovie;
bool bBufferVisualizationDumpingRequired = bAnyScreenshotsRequired && CVarDumpFrames && CVarDumpFrames->GetValueOnGameThread();
// if this is a game viewport, and game rendering is disabled, then we don't want to actually draw anything
if ( World && World->IsGameWorld() && !bIsGameRenderingEnabled)
{
// since we aren't drawing the viewport, we still need to update streaming
World->UpdateLevelStreaming();
}
else
{
if( GIsHighResScreenshot ) // 高清截图
{
const bool bShowUI = false;
const bool bAddFilenameSuffix = GetHighResScreenshotConfig().FilenameOverride.IsEmpty();
const bool bHDRScreenshot = GetSceneHDREnabled();
FScreenshotRequest::RequestScreenshot( FString(), bShowUI, bAddFilenameSuffix, bHDRScreenshot);
HighResScreenshot();
}
else if(bAnyScreenshotsRequired && bBufferVisualizationDumpingRequired) // 是否要截图 && 缓冲区可视化
{
// request the screenshot early so we have the name setup that BufferVisualization can dump it's content
const bool bShowUI = false;
const bool bAddFilenameSuffix = true;
FScreenshotRequest::RequestScreenshot( FString(), bShowUI, bAddFilenameSuffix );
}
if( SizeX > 0 && SizeY > 0 ) // 视口有大小
{
static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.VSync")); // 垂直同步
bool bLockToVsync = CVar->GetValueOnGameThread() != 0;
ULocalPlayer* Player = (GEngine && World) ? GEngine->GetFirstGamePlayer(World) : NULL;
if ( Player )
{
bLockToVsync |= (Player && Player->PlayerController && Player->PlayerController->bCinematicMode);
}
EnqueueBeginRenderFrame(bShouldPresent); // 渲染线程开始帧
// Calculate gamethread time (excluding idle time)
// 时间计算
{
static uint64 LastFrameUpdated = MAX_uint64;
if (GFrameCounter != LastFrameUpdated)
{
static uint32 Lastimestamp = 0;
static bool bStarted = false;
uint32 CurrentTime = FPlatformTime::Cycles();
FThreadIdleStats& GameThread = FThreadIdleStats::Get();
if (bStarted)
{
uint32 ThreadTime = CurrentTime - Lastimestamp;
// add any stalls via sleep or fevent
GGameThreadTime = (ThreadTime > GameThread.Waits) ? (ThreadTime - GameThread.Waits) : ThreadTime;
GGameThreadWaitTime = GameThread.Waits;
}
else
{
bStarted = true;
}
LastFrameUpdated = GFrameCounter;
Lastimestamp = CurrentTime;
GameThread.Reset();
}
}
UWorld* ViewportWorld = ViewportClient->GetWorld();
// 把世界画在画布上
FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->GetFeatureLevel() : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
{
ViewportClient->Draw(this, &Canvas); // 把自己绘制到画布上
}
Canvas.Flush_GameThread(); // 刷新游戏线程
UGameViewportClient::OnViewportRendered().Broadcast(this); // 广播渲染已经完成了
ViewportClient->ProcessScreenShots(this); // 如果是截图,这里会处理截图的内容
// Slate doesn't present immediately. Tag the viewport as requiring vsync so that it happens.
SetRequiresVsync(bLockToVsync);
EnqueueEndRenderFrame(bLockToVsync, bShouldPresent);
GInputLatencyTimer.GameThreadTrigger = false;
}
}
// Reset the camera cut flags if we are in a viewport that has a world
if (World)
{
for( FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator )
{
APlayerController* PlayerController = Iterator->Get();
if (PlayerController && PlayerController->PlayerCameraManager)
{
// 设置一下变量的值,有其他用。比如是否要用上一帧的遮挡查询运动模糊
PlayerController->PlayerCameraManager->bGameCameraCutThisFrame = false;
}
}
}
// countdown the present delay, and then stop the movie at the end
// this doesn't need to be on rendering thread as long as we have a long enough delay (2 or 3 frames), because
// the rendering thread will never be more than one frame behind
if (PresentAndStopMovieDelay > 0)
{
PresentAndStopMovieDelay--;
// stop any playing movie
if (PresentAndStopMovieDelay == 0)
{
// Enable game rendering again if it isn't already.
bIsGameRenderingEnabled = true;
}
}
}
}
FEditorViewportClient::Draw
UWorld* ViewportWorld = ViewportClient->GetWorld();
FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->GetFeatureLevel() : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
{
ViewportClient->Draw(this, &Canvas);
}
Canvas.Flush_GameThread();
上面渲染那一小块。准备一个画布,然后把 ViewportClient 渲染上去。
这个Draw是这个地方
void FEditorViewportClient::Draw(FViewport InViewport, FCanvas Canvas)
void FEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
// 备份视口,如果有 InViewport 就用,否则就是之前的
FViewport* ViewportBackup = Viewport;
Viewport = InViewport ? InViewport : Viewport;
UWorld* World = GetWorld();
FGameTime Time;
if (!World || (GetScene() != World->Scene) || UseAppTime())
{
Time = FGameTime::GetTimeSinceAppStart();
}
else
{
Time = World->GetTime();
}
// Early out if we are changing maps in editor as there is no reason to render the scene and it may not even be valid (For unsaved maps)
// 地图正准备更改,就不渲染了
if (World && World->IsPreparingMapChange())
{
return;
}
// Allow HMD to modify the view later, just before rendering
// 是否立体渲染(指VR)
const bool bStereoRendering = GEngine->IsStereoscopic3D( InViewport );
FCanvas* DebugCanvas = Viewport->GetDebugCanvas();
if (DebugCanvas)
{
DebugCanvas->SetScaledToRenderTarget(bStereoRendering);
DebugCanvas->SetStereoRendering(bStereoRendering);
}
Canvas->SetScaledToRenderTarget(bStereoRendering);
Canvas->SetStereoRendering(bStereoRendering);
FEngineShowFlags UseEngineShowFlags = EngineShowFlags;
if (OverrideShowFlagsFunc)
{
OverrideShowFlagsFunc(UseEngineShowFlags);
}
// 。。。
}
FSceneViewFamily
/**
* A view family which deletes its views when it goes out of scope.
*/
class FSceneViewFamilyContext : public FSceneViewFamily
{
public:
/** Initialization constructor. */
FSceneViewFamilyContext( const ConstructionValues& CVS)
: FSceneViewFamily(CVS)
{}
/** Destructor. */
ENGINE_API virtual ~FSceneViewFamilyContext();
};
先理解这个对象,主要就是 FSceneViewFamily 包含一些构造参数了。
/**
* A set of views into a scene which only have different view transforms and owner actors.
*/
class FSceneViewFamily
{
};
表示一组视图
类内一个struct
用于描述创建FSceneViewFamily实例的参数
/**
* Helper struct for creating FSceneViewFamily instances
* If created with specifying a time it will retrieve them from the world in the given scene.
*
* @param InRenderTarget The render target which the views are being rendered to.
* @param InScene The scene being viewed.
* @param InShowFlags The show flags for the views.
*
*/
struct ConstructionValues
{
ENGINE_API ConstructionValues(
const FRenderTarget* InRenderTarget,
FSceneInterface* InScene,
const FEngineShowFlags& InEngineShowFlags
);
/** The views which make up the family. */
const FRenderTarget* RenderTarget;
/** The render target which the views are being rendered to. */
FSceneInterface* Scene;
// 。。。
};
回到这个类
class FSceneViewFamily
{
public:
// 组成这个视图组的视图
TArray<const FSceneView*> Views;
// 视图模式枚举,比如DX中我们会描述把三角形处理为线框,面,显然为basecolor,的那个枚举
EViewModeIndex ViewMode;
// RT
const FRenderTarget* RenderTarget;
这里的 FRenderTarget 也值得一看
FRenderTarget:
/**
* A render target.
*/
class FRenderTarget
{
public:
FRenderTarget() {};
virtual ~FRenderTarget() {};
ENGINE_API virtual const FTextureRHIRef& GetRenderTargetTexture() const; // 返回 RenderTargetTextureRHI,可以看到整个类只有这一个属性,都在描述它
ENGINE_API virtual FUnorderedAccessViewRHIRef GetRenderTargetUAV() const; // 新构造一个FUnorderedAccessViewRHIRef(),然后返回
// 返回一个RDG纹理,RDG是 UE5的一个render pass 管理系统,这里pass是渲染层的意思
ENGINE_API virtual FRDGTextureRef GetRenderTargetTexture(FRDGBuilder& GraphBuilder) const;
// 获取 RT 的一些属性,比如大小,gama,色域,是否HDR等
virtual FIntPoint GetSizeXY() const = 0;
ENGINE_API virtual float GetDisplayGamma() const;
virtual EDisplayColorGamut GetDisplayColorGamut() const { return EDisplayColorGamut::sRGB_D65; }
virtual EDisplayOutputFormat GetDisplayOutputFormat() const { return EDisplayOutputFormat::SDR_sRGB; }
virtual bool GetSceneHDREnabled() const { return false; }
//
virtual void ProcessToggleFreezeCommand() {};
virtual bool HasToggleFreezeCommand() { return false; };
// 读取像素到 xxx
ENGINE_API bool ReadPixels(TArray< FColor >& OutImageData,FReadSurfaceDataFlags InFlags = FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX), FIntRect InRect = FIntRect(0, 0, 0, 0) );
ENGINE_API bool ReadPixelsPtr(FColor* OutImageBytes, FReadSurfaceDataFlags InFlags = FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX), FIntRect InSrcRect = FIntRect(0, 0, 0, 0));
ENGINE_API bool ReadFloat16Pixels(TArray<FFloat16Color>& OutputBuffer,ECubeFace CubeFace=CubeFace_PosX);
ENGINE_API bool ReadLinearColorPixels(TArray<FLinearColor>& OutputBuffer, FReadSurfaceDataFlags InFlags = FReadSurfaceDataFlags(RCM_MinMax, CubeFace_MAX), FIntRect InRect = FIntRect(0, 0, 0, 0));
ENGINE_API bool ReadLinearColorPixelsPtr(FLinearColor* OutImageBytes, FReadSurfaceDataFlags InFlags = FReadSurfaceDataFlags(RCM_MinMax, CubeFace_MAX), FIntRect InRect = FIntRect(0, 0, 0, 0));
virtual FRHIGPUMask GetGPUMask(FRHICommandListImmediate& RHICmdList) const { return FRHIGPUMask::GPU0(); }
protected:
FTextureRHIRef RenderTargetTextureRHI;
bool ReadFloat16Pixels(FFloat16Color* OutImageBytes,ECubeFace CubeFace=CubeFace_PosX);
};
回来继续
class FSceneViewFamily
{
public:
// 组成这个视图组的视图
TArray<const FSceneView*> Views;
// 视图模式枚举,比如DX中我们会描述把三角形处理为线框,面,显然为basecolor,的那个枚举
EViewModeIndex ViewMode;
// RT
const FRenderTarget* RenderTarget;
FEngineShowFlags EngineShowFlags; // 标志位
FGameTime Time; // 时间
// 计数
uint32 FrameNumber;
uint64 FrameCounter = 0;
bool bAdditionalViewFamily; // 当前视图组是否是附加的
bool bRealtimeUpdate; // 是否实时更新
bool bDeferClear; // 是否延迟清除后缓冲区
bool bResolveScene; // 是否把场景渲染的结果复制给RT
bool bMultiGPUForkAndJoin; // 是否使用相同的GPU Mask渲染
bool bForceCopyCrossGPU = false; // 强制把GPU中复制到一个持久资源
bool bIsMultipleViewFamily = false; // 是否是单帧中的多个视图组之一。会影响遮挡逻辑
bool bIsFirstViewInMultipleViewFamily = true; // 是否多视图组的第一个视图
bool bIsSceneTexturesInitialized = false;
bool bIsViewFamilyInfo = false; // 是否是视图组的信息?
ESceneCaptureSource SceneCaptureSource; // 场景捕获的来源,例如 ESceneCaptureSource::SCS_SceneColorHDR 以 SceneColor (HDR) in RGB, Inv Opacity in A 输出颜色
ESceneCaptureCompositeMode SceneCaptureCompositeMode; // 场景捕捉的合成方式,覆盖,叠加,合成
bool bThumbnailRendering = false; // 是否用于缩略图渲染
bool bWorldIsPaused;
bool bIsHDR;
bool bRequireMultiView; // 是否为 scenecolor and depth 分配多视图
float GammaCorrection; // 是否使用gama矫正值
FExposureSettings ExposureSettings; // 相机设置的曝光设置
TArray<TSharedRef<class ISceneViewExtension, ESPMode::ThreadSafe> > ViewExtensions; // 渲染线程中修改视图参数的扩展
FDisplayInternalsData DisplayInternalsData; // 调试用的一个面板内容,控制台修改 0 1
/**
* Secondary view fraction to support High DPI monitor still with same primary screen percentage range for temporal
* upscale to test content consistently in editor no mater of the HighDPI scale.
*/
float SecondaryViewFraction;
ESecondaryScreenPercentageMethod SecondaryScreenPercentageMethod;
int8 LandscapeLODOverride; // 覆盖视口landscape LOD 的标志
bool bCurrentlyBeingEdited; // 场景是否可编辑
bool bOverrideVirtualTextureThrottle; // 是否禁用虚拟纹理的标志
int32 VirtualTextureFeedbackFactor;
bool bDrawBaseInfo; // 是否应绘制基本附件体积
bool bNullifyWorldSpacePosition; // 是否把着色器世界空间的位置强设为0
FString ProfileDescription; // Unreal Insights 分析的视图系列的可选描述
float* ProfileSceneRenderTime; // 场景渲染的时间追踪
ENGINE_API ERHIFeatureLevel::Type GetFeatureLevel() const; // 获取RHI的特性级别
EShaderPlatform GetShaderPlatform() const { return GShaderPlatformForFeatureLevel[GetFeatureLevel()]; } // 获取着色器的平台
FORCEINLINE EDebugViewShaderMode GetDebugViewShaderMode() const { return DVSM_None; }
FORCEINLINE int32 GetViewModeParam() const { return -1; }
FORCEINLINE FName GetViewModeParamName() const { return NAME_None; }
FORCEINLINE bool UseDebugViewVSDSHS() const { return false; }
FORCEINLINE bool UseDebugViewPS() const { return false; }
ENGINE_API bool SupportsScreenPercentage() const; // 返回的视图,是否支持屏幕的百分比
FORCEINLINE bool AllowTranslucencyAfterDOF() const { return bAllowTranslucencyAfterDOF; } // 是否运行景深之后渲染半透明的物体
ORCEINLINE bool AllowStandardTranslucencySeparated() const { return bAllowStandardTranslucencySeparated; } // 允许标准半透明分离?
FORCEINLINE const ISceneViewFamilyScreenPercentage* GetScreenPercentageInterface() const { return ScreenPercentageInterface; } // 屏幕的百分比接口
FORCEINLINE void SetScreenPercentageInterface(ISceneViewFamilyScreenPercentage* InScreenPercentageInterface);
// 扩展数据
template<typename TExtensionData> TExtensionData* GetExtentionData()
template<typename TExtensionData> TExtensionData* GetOrCreateExtentionData()
FORCEINLINE void SetTemporalUpscalerInterface(UE::Renderer::Private::ITemporalUpscaler* InTemporalUpscalerInterface)
FORCEINLINE const UE::Renderer::Private::ITemporalUpscaler* GetTemporalUpscalerInterface() const
FORCEINLINE void SetSecondarySpatialUpscalerInterface(ISpatialUpscaler* InSpatialUpscalerInterface)
FORCEINLINE const ISpatialUpscaler* GetSecondarySpatialUpscalerInterface() const
void SetSceneRenderer(ISceneRenderer* NewSceneRenderer) { SceneRenderer = NewSceneRenderer; }
ISceneRenderer* GetSceneRenderer() const { check(SceneRenderer); return SceneRenderer; }
ISceneRenderer* SceneRenderer; // The scene renderer that is rendering this view family. This is only initialized in the rendering thread's copies of the FSceneViewFamily.
ISceneViewFamilyScreenPercentage* ScreenPercentageInterface; /** Interface to handle screen percentage of the views of the family. */
/** Renderer private interfaces, automatically have same lifetime as FSceneViewFamily. */ // 采样器接口
UE::Renderer::Private::ITemporalUpscaler* TemporalUpscalerInterface;
ISpatialUpscaler* PrimarySpatialUpscalerInterface;
ISpatialUpscaler* SecondarySpatialUpscalerInterface;
bool bAllowTranslucencyAfterDOF;
bool bAllowStandardTranslucencySeparated;
bool bIsInFocus = true;
反正就这么个东西。。。
FCanvas
先看这一小段成员
/**
* Encapsulates the canvas state.
*/
class FCanvas
{
private:
// ...
TArray<int32> DepthSortKeyStack; // 深度排序Key。使用的一直是top的key
TArray<FTransformEntry> TransformStack; // 存储矩阵的栈,最底部的是投影矩阵
FIntRect ViewRect; /** View rect for the render target */
FIntRect ScissorRect; /** Scissor rect for the render target */
FRenderTarget* RenderTarget; /** Current render target used by the canvas */
FHitProxyConsumer* HitProxyConsumer; // 收集点击命中的东西
TRefCountPtr<HHitProxy> CurrentHitProxy; // 命中代理
FSceneInterface* Scene;
// Enum for canvas features that are allowed
enum ECanvasAllowModes
{
Allow_Flush = 1 << 0, // flushing and rendering
Allow_DeleteOnRender = 1 << 1 // delete the render batches when rendering
};
uint32 AllowedModes; // 画布开关, ECanvasAllowModes 就在上面
bool bRenderTargetDirty; // 画布是否为脏, true if the render target has been rendered to since last calling SetRenderTarget(),避免多次渲染
FGameTime Time;
bool bScaledToRenderTarget; // true, if Canvas should be scaled to whole render target
ERHIFeatureLevel::Type FeatureLevel; // Feature level that we are currently rendering with
bool bStereoRendering; // true, if Canvas should be rendered in stereo 立体渲染VR
bool bUseInternalTexture; // true, if Canvas is being rendered in its own texture
FIntPoint ParentSize;
enum ECanvasDrawMode
{
CDM_DeferDrawing, // 延迟渲染
CDM_ImmediateDrawing // 马上渲染
};
ECanvasDrawMode DrawMode;
float DPIScale;
void Construct(); // Shared construction function
// ...
};
FCanvers里面有一个类,主要包括 DepthSortKey 和一个 == 方法。
还有 TArray<class FCanvasBaseRenderItem*> RenderBatchArray;
/**
* Contains all of the batched elements that need to be rendered at a certain depth sort key
*/
class FCanvasSortElement
{
public:
// ...
bool operator==(const FCanvasSortElement& Other) const { return DepthSortKey == Other.DepthSortKey; }
int32 DepthSortKey;
/** list of batches that should be rendered at this sort key level */
// 渲染的批量元素合集的类
TArray<class FCanvasBaseRenderItem*> RenderBatchArray;
};
关于 FCanvasBaseRenderItem
/**
* Base interface for canvas items which can be batched for rendering
*/
class FCanvasBaseRenderItem
{
public:
virtual ~FCanvasBaseRenderItem()
{}
virtual bool Render_RenderThread(FCanvasRenderContext& RenderContext, FMeshPassProcessorRenderState& DrawRenderState, const FCanvas* Canvas) = 0;
virtual bool Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope) = 0;
virtual class FCanvasBatchedElementRenderItem* GetCanvasBatchedElementRenderItem() { return NULL; }
virtual class FCanvasTileRendererItem* GetCanvasTileRendererItem() { return NULL; }
virtual class FCanvasTriangleRendererItem* GetCanvasTriangleRendererItem() { return NULL; }
};
这里有 FCanvasTileRendererItem。Tile 瓦片,渲染一个很大的东西的时候,可以让他一块一块的出现,就是瓦片的概念。
这个类继承了 FCanvasBaseRenderItem,并重写了一些接口。同时 FCanvasTriangleRendererItem 和 FCanvasBatchedElementRenderItem 也一样
class FCanvasTileRendererItem : public FCanvasBaseRenderItem
{
TSharedPtr<FRenderData> Data;
}
里面有个 FRenderData。里面就有 TArray
我们看下三角形这个类
class FCanvasTriangleRendererItem : public FCanvasBaseRenderItem
{
class FRenderData
{
// ...
struct FTriangleInst
{
FCanvasUVTri Tri;
FHitProxyId HitProxyId;
};
TArray<FTriangleInst> Triangles;
};
TSharedPtr<FRenderData> Data;
}
看看 FCanvasUVTri,
/** Simple 2d triangle with UVs */
USTRUCT(BlueprintType)
struct FCanvasUVTri
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V0_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V0_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V0_Color;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V1_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V1_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V1_Color;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V2_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V2_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V2_Color;
};
回去FCanvas
/**
* Encapsulates the canvas state.
*/
class FCanvas
{
public:
class FCanvasSortElement
{
public:
bool operator==(const FCanvasSortElement& Other) const { return DepthSortKey == Other.DepthSortKey; }
int32 DepthSortKey;
TArray<class FCanvasBaseRenderItem*> RenderBatchArray;
};
TArray<FCanvasSortElement> SortedElements; // 从后往前排序的 Batched canvas elements
TMap<int32,int32> SortedElementLookupMap; // Map from sortkey to array index of SortedElements for faster lookup of existing entries
int32 LastElementIndex; // 最后一个元素的索引 Store index of last Element off to avoid semi expensive Find()
ENGINE_API FCanvasSortElement& GetSortElement(int32 DepthSortKey); // Get the sort element for the given sort key. Allocates a new entry if one does not exist
};
/**
* Encapsulates the canvas state.
*/
class FCanvas
{
public:
// 批处理的元素类型
enum EElementType
{
ET_Line,
ET_Triangle,
ET_MAX
};
ENGINE_API static FCanvas* Create(FRDGBuilder& GraphBuilder, FRDGTextureRef InRenderTarget, FHitProxyConsumer* InHitProxyConsumer, const FGameTime& Time, ERHIFeatureLevel::Type InFeatureLevel, float InDPIScale = 1.0f);
ENGINE_API FCanvas(FRenderTarget* InRenderTarget, FHitProxyConsumer* InHitProxyConsumer, UWorld* InWorld, ERHIFeatureLevel::Type InFeatureLevel, ECanvasDrawMode DrawMode = CDM_DeferDrawing, float InDPIScale = 1.0f);
ENGINE_API FCanvas(FRenderTarget* InRenderTarget, FHitProxyConsumer* InHitProxyConsumer, const FGameTime& Time, ERHIFeatureLevel::Type InFeatureLevel, float InDPIScale = 1.0f);
ENGINE_API ~FCanvas();
// 混合模式 转为 简单的混合模式。
ENGINE_API static ESimpleElementBlendMode BlendToSimpleElementBlend(EBlendMode BlendMode);
// TODO
ENGINE_API FBatchedElements* GetBatchedElements(EElementType InElementType, FBatchedElementParameters* InBatchedElementParameters = NULL, const FTexture* Texture = NULL, ESimpleElementBlendMode BlendMode = SE_BLEND_MAX, const FDepthFieldGlowInfo& GlowInfo = FDepthFieldGlowInfo(), bool bApplyDPIScale = true);
// Add Item
ENGINE_API void AddTileRenderItem(float X, float Y, float SizeX, float SizeY, float U, float V, float SizeU, float SizeV, const FMaterialRenderProxy* MaterialRenderProxy, FHitProxyId HitProxyId, bool bFreezeTime, FColor InColor);
ENGINE_API void AddTriangleRenderItem(const FCanvasUVTri& Tri, const FMaterialRenderProxy* MaterialRenderProxy, FHitProxyId HitProxyId, bool bFreezeTime);
ENGINE_API void Flush_RenderThread(FRHICommandListImmediate& RHICmdList, bool bForce = false);
ENGINE_API void Flush_RenderThread(FRDGBuilder& GraphBuilder, bool bForce = false);
ENGINE_API void Flush_GameThread(bool bForce = false);
ENGINE_API void PushRelativeTransform(const FMatrix& Transform);
ENGINE_API void PushAbsoluteTransform(const FMatrix& Transform);
ENGINE_API void PopTransform();
ENGINE_API void SetBaseTransform(const FMatrix& Transform);
ENGINE_API static FMatrix CalcBaseTransform2D(uint32 ViewSizeX, uint32 ViewSizeY);
ENGINE_API static FMatrix CalcBaseTransform3D(uint32 ViewSizeX, uint32 ViewSizeY, float fFOV, float NearPlane);
ENGINE_API static FMatrix CalcViewMatrix(uint32 ViewSizeX, uint32 ViewSizeY, float fFOV);
ENGINE_API static FMatrix CalcProjectionMatrix(uint32 ViewSizeX, uint32 ViewSizeY, float fFOV, float NearPlane);
FMatrix GetTransform() const;
const FMatrix& GetBottomTransform() const;
const FMatrix& GetFullTransform() const;
ENGINE_API void CopyTransformStack(const FCanvas& Copy);
ENGINE_API void SetRenderTarget_GameThread(FRenderTarget* NewRenderTarget);
ENGINE_API void SetRenderTargetRect(const FIntRect& ViewRect);
ENGINE_API void SetRenderTargetScissorRect( const FIntRect& ScissorRect );
void SetRenderTargetDirty(bool bDirty);
ENGINE_API void SetHitProxy(HHitProxy* HitProxy);
// HitProxy Accessors.
FHitProxyId GetHitProxyId() const { return CurrentHiProxy ? CurrentHitProxy->Id : FHitProxyId(); }
FHitProxyConsumer* GetHitProxyConsumer() const { return HitProxyConsumer; }
bool IsHitTesting() const { return HitProxyConsumer != NULL; }
// sort key
void PushDepthSortKey(int32 InSortKey);
int32 PopDepthSortKey();
int32 TopDepthSortKey();
bool HasBatchesToRender() const;
void ClearBatchesToRender();
public:
float AlphaModulate;
/** Entry for the transform stack which stores a matrix and its CRC for faster comparisons */
class FTransformEntry
{
public:
FTransformEntry(const FMatrix& InMatrix)
: Matrix(InMatrix)
{
MatrixCRC = FCrc::MemCrc_DEPRECATED(&Matrix, sizeof(FMatrix));
}
FORCEINLINE void SetMatrix(const FMatrix& InMatrix)
{
Matrix = InMatrix;
MatrixCRC = FCrc::MemCrc_DEPRECATED(&Matrix, sizeof(FMatrix));
}
FORCEINLINE const FMatrix& GetMatrix() const
{
return Matrix;
}
FORCEINLINE uint32 GetMatrixCRC() const
{
return MatrixCRC;
}
private:
FMatrix Matrix;
uint32 MatrixCRC;
};
/** returns the transform stack */
FORCEINLINE const TArray<FTransformEntry>& GetTransformStack() const
{
return TransformStack;
}
FORCEINLINE const FIntRect& GetViewRect() const
{
return ViewRect;
}
FORCEINLINE void SetScaledToRenderTarget(bool bScale = true)
{
bScaledToRenderTarget = bScale;
}
FORCEINLINE bool IsScaledToRenderTarget() const { return bScaledToRenderTarget; }
FORCEINLINE void SetStereoRendering(bool bStereo = true)
{
bStereoRendering = bStereo;
}
FORCEINLINE bool IsStereoRendering() const { return bStereoRendering; }
FORCEINLINE void SetUseInternalTexture(const bool bInUseInternalTexture)
{
bUseInternalTexture = bInUseInternalTexture;
}
FORCEINLINE bool IsUsingInternalTexture() const { return bUseInternalTexture; }
FORCEINLINE void SetParentCanvasSize(FIntPoint InParentSize)
{
ParentSize = InParentSize;
}
FORCEINLINE FIntPoint GetParentCanvasSize() const { return ParentSize; }
float GetDPIScale() const { return bStereoRendering ? 1.0f : DPIScale; }
public:
/**
* Draw a CanvasItem
*
* @param Item Item to draw
*/
ENGINE_API void DrawItem(FCanvasItem& Item);
/**
* Draw a CanvasItem at the given coordinates
*
* @param Item Item to draw
* @param InPosition Position to draw item
*/
ENGINE_API void DrawItem(FCanvasItem& Item, const FVector2D& InPosition);
/**
* Draw a CanvasItem at the given coordinates
*
* @param Item Item to draw
* @param X X Position to draw item
* @param Y Y Position to draw item
*/
ENGINE_API void DrawItem(FCanvasItem& Item, float X, float Y);
/**
* Clear the canvas
*
* @param Color Color to clear with.
*/
ENGINE_API void Clear(const FLinearColor& Color);
/**
* Draw arbitrary aligned rectangle.
*
* @param X - X position to draw tile at
* @param Y - Y position to draw tile at
* @param SizeX - Width of tile
* @param SizeY - Height of tile
* @param U - Horizontal position of the upper left corner of the portion of the texture to be shown(texels)
* @param V - Vertical position of the upper left corner of the portion of the texture to be shown(texels)
* @param SizeU - The width of the portion of the texture to be drawn. This value is in texels.
* @param SizeV - The height of the portion of the texture to be drawn. This value is in texels.
* @param Color - tint applied to tile
* @param Texture - Texture to draw
* @param AlphaBlend - true to alphablend
*/
ENGINE_API void DrawTile(double X, double Y, double SizeX, double SizeY, float U, float V, float SizeU, float SizeV, const FLinearColor& Color, const FTexture* Texture = NULL, bool AlphaBlend = true);
ENGINE_API void DrawTile(double X, double Y, double SizeX, double SizeY, float U, float V, float SizeU, float SizeV, const FLinearColor& Color, const FTexture* Texture, ESimpleElementBlendMode BlendMode);
/**
* Draw an string centered on given location.
* This function is being deprecated. a FCanvasTextItem should be used instead.
*
* @param StartX - X point
* @param StartY - Y point
* @param Text - Text to draw
* @param Font - Font to use
* @param Color - Color of the text
* @param ShadowColor - Shadow color to draw underneath the text (ignored for distance field fonts)
* @return total size in pixels of text drawn
*/
ENGINE_API int32 DrawShadowedString(double StartX, double StartY, const TCHAR* Text, const UFont* Font, const FLinearColor& Color, const FLinearColor& ShadowColor = FLinearColor::Black );
ENGINE_API int32 DrawShadowedText(double StartX, double StartY, const FText& Text, const UFont* Font, const FLinearColor& Color, const FLinearColor& ShadowColor = FLinearColor::Black );
ENGINE_API void WrapString( FTextSizingParameters& Parameters, const float InCurX, const TCHAR* const pText, TArray<FWrappedStringElement>& out_Lines, FCanvasWordWrapper::FWrappedLineData* const OutWrappedLineData = nullptr);
ENGINE_API void DrawNGon(const FVector2D& Center, const FColor& Color, int32 NumSides, float Radius);
/**
* Contains all of the batched elements that need to be rendered at a certain depth sort key
*/
class FCanvasSortElement
{
public:
// ...
bool operator==(const FCanvasSortElement& Other) const { return DepthSortKey == Other.DepthSortKey; }
int32 DepthSortKey;
/** list of batches that should be rendered at this sort key level */
TArray<class FCanvasBaseRenderItem*> RenderBatchArray;
};
/** Batched canvas elements to be sorted for rendering. Sort order is back-to-front */
TArray<FCanvasSortElement> SortedElements;
/** Map from sortkey to array index of SortedElements for faster lookup of existing entries */
TMap<int32,int32> SortedElementLookupMap;
/** Store index of last Element off to avoid semi expensive Find() */
int32 LastElementIndex;
/**
* Get the sort element for the given sort key. Allocates a new entry if one does not exist
*
* @param DepthSortKey - the key used to find the sort element entry
* @return sort element entry
*/
ENGINE_API FCanvasSortElement& GetSortElement(int32 DepthSortKey);
friend class FCanvasRenderContext;
friend class FCanvasRenderThreadScope;
};
[04-08]
FBatchedElements
批处理元素
/** Batched elements for later rendering. */
class FBatchedElements
{
public:
// 向批处理添加 xxx
ENGINE_API void AddLine(const FVector& Start,const FVector& End,const FLinearColor& Color,FHitProxyId HitProxyId, float Thickness = 0.0f, float DepthBias = 0.0f, bool bScreenSpace = false);
ENGINE_API void AddTranslucentLine(const FVector& Start, const FVector& End, const FLinearColor& Color, FHitProxyId HitProxyId, float Thickness = 0.0f, float DepthBias = 0.0f, bool bScreenSpace = false);
ENGINE_API void AddPoint(const FVector& Position,float Size,const FLinearColor& Color,FHitProxyId HitProxyId);
ENGINE_API int32 AddVertex(const FVector4& InPosition, const FVector2D& InTextureCoordinate, const FLinearColor& InColor, FHitProxyId HitProxyId);
ENGINE_API int32 AddVertexf(const FVector4f& InPosition, const FVector2f& InTextureCoordinate, const FLinearColor& InColor, FHitProxyId HitProxyId);
ENGINE_API void AddTriangle(int32 V0,int32 V1,int32 V2,const FTexture* Texture,EBlendMode BlendMode);
ENGINE_API void AddTriangle(int32 V0, int32 V1, int32 V2, const FTexture* Texture, ESimpleElementBlendMode BlendMode, const FDepthFieldGlowInfo& GlowInfo = FDepthFieldGlowInfo());
ENGINE_API void AddTriangle(int32 V0,int32 V1,int32 V2,FBatchedElementParameters* BatchedElementParameters,ESimpleElementBlendMode BlendMode);
ENGINE_API void AddTriangleExtensive(int32 V0,int32 V1,int32 V2,FBatchedElementParameters* BatchedElementParameters,const FTexture* Texture,ESimpleElementBlendMode BlendMode, const FDepthFieldGlowInfo& GlowInfo = FDepthFieldGlowInfo());
// 添加 预留空间
ENGINE_API void AddReserveTriangles(int32 NumMeshTriangles,const FTexture* Texture,ESimpleElementBlendMode BlendMode);
ENGINE_API void ReserveTriangles(int32 NumMeshTriangles,const FTexture* Texture,ESimpleElementBlendMode BlendMode);
ENGINE_API void AddReserveVertices(int32 NumMeshVerts);
ENGINE_API void ReserveVertices(int32 NumMeshVerts);
ENGINE_API void AddReserveLines(int32 NumLines, bool bDepthBiased = false, bool bThickLines = false);
ENGINE_API void AddSprite(
const FVector& Position,
float SizeX,
float SizeY,
const FTexture* Texture,
const FLinearColor& Color,
FHitProxyId HitProxyId,
float U,
float UL,
float V,
float VL,
uint8 BlendMode,
float OpacityMaskRefVal
);
// 绘制批处理的元素
ENGINE_API bool Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma = 1.0f, EBlendModeFilter::Type Filter = EBlendModeFilter::All) const;
// 创建FSceneView 代理
static ENGINE_API FSceneView CreateProxySceneView(const FMatrix& ProjectionMatrix, const FIntRect& ViewRect);
// 有没有东西要画
FORCEINLINE bool HasPrimsToDraw() const
{
return( LineVertices.Num() || Points.Num() || Sprites.Num() || MeshElements.Num() || ThickLines.Num() || WireTris.Num() > 0 );
}
private:
// 画点
ENGINE_API void DrawPointElements(FRHICommandList& RHICmdList, const FMatrix& Transform, const uint32 ViewportSizeX, const uint32 ViewportSizeY, const FVector& CameraX, const FVector& CameraY) const;
// 下面是存储批处理元素的Array。点啊,瓦片呀,精灵呀
TArray<FSimpleElementVertex> LineVertices;
struct FBatchedPoint
{
FVector Position;
float Size;
FColor Color;
FColor HitProxyColor;
};
TArray<FBatchedPoint> Points;
struct FBatchedWireTris
{
float DepthBias;
};
TArray<FBatchedWireTris> WireTris;
mutable TResourceArray<FSimpleElementVertex> WireTriVerts;
struct FBatchedThickLines
{
FVector Start;
FVector End;
float Thickness;
FLinearColor Color;
FColor HitProxyColor;
float DepthBias;
uint32 bScreenSpace;
};
TArray<FBatchedThickLines> ThickLines;
struct FBatchedSprite
{
FVector Position;
float SizeX;
float SizeY;
const FTexture* Texture;
FLinearColor Color;
FColor HitProxyColor;
float U;
float UL;
float V;
float VL;
float OpacityMaskRefVal;
uint8 BlendMode;
};
TArray<FBatchedSprite> Sprites;
struct FBatchedMeshElement
{
/** starting index in vertex buffer for this batch */
uint32 MinVertex;
/** largest vertex index used by this batch */
uint32 MaxVertex;
/** index buffer for triangles */
TArray<uint16,TInlineAllocator<6> > Indices;
/** all triangles in this batch draw with the same texture */
const FTexture* Texture;
/** Parameters for this batched element */
TRefCountPtr<FBatchedElementParameters> BatchedElementParameters;
/** all triangles in this batch draw with the same blend mode */
ESimpleElementBlendMode BlendMode;
/** all triangles in this batch draw with the same depth field glow (depth field blend modes only) */
FDepthFieldGlowInfo GlowInfo;
};
/** Max number of mesh index entries that will fit in a DrawPriUP call */
int32 MaxMeshIndicesAllowed;
/** Max number of mesh vertices that will fit in a DrawPriUP call */
int32 MaxMeshVerticesAllowed;
TArray<FBatchedMeshElement,TInlineAllocator<2> > MeshElements;
TArray<FSimpleElementVertex,TInlineAllocator<4> > MeshVertices;
/**
* Sets the appropriate vertex and pixel shader.
*/
ENGINE_API void PrepareShaders(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
uint32 StencilRef,
ERHIFeatureLevel::Type FeatureLevel,
ESimpleElementBlendMode BlendMode,
const FRelativeViewMatrices& ViewMatrices,
FBatchedElementParameters* BatchedElementParameters,
const FTexture* Texture,
bool bHitTesting,
float Gamma,
const FDepthFieldGlowInfo* GlowInfo = nullptr,
const FSceneView* View = nullptr,
float OpacityMaskRefVal = .5f
) const;
回到
void FEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
// 。。。
// Setup a FSceneViewFamily/FSceneView for the viewport.
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
Canvas->GetRenderTarget(),
GetScene(),
UseEngineShowFlags)
.SetTime(Time)
.SetRealtimeUpdate( IsRealtime() && FSlateThrottleManager::Get().IsAllowingExpensiveTasks() )
.SetViewModeParam( ViewModeParam, ViewModeParamName ) );
//。。。
}
这里初始化了一个 FSceneViewFamilyContext 一组视图对象,并根据情况设置了这个变量的属性
继续
FSceneViewExtensionContext ViewExtensionContext(InViewport);
ViewExtensionContext.bStereoEnabled = true;
ViewFamily.ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(ViewExtensionContext);
for (auto ViewExt : ViewFamily.ViewExtensions)
{
ViewExt->SetupViewFamily(ViewFamily);
}
这里for的 ViewExtensions 是
TArray<TSharedRef<class ISceneViewExtension, ESPMode::ThreadSafe> > ViewExtensions;
他会寻找所有继承了 ISceneViewExtension 的类,然后 SetupViewFamily
比如 这个类就会在这里被遍历
class FLandscapeSceneViewExtension : public FSceneViewExtensionBase
class FSceneViewExtensionBase : public ISceneViewExtension, public TSharedFromThis<FSceneViewExtensionBase, ESPMode::ThreadSafe>
我们可以参考这种方式把自己的一些渲染扩展嵌入进去
一些变量,并且继续在赋值 ViewFamily
EViewModeIndex CurrentViewMode = GetViewMode();
ViewFamily.ViewMode = CurrentViewMode;
const bool bVisualizeBufferEnabled = CurrentViewMode == VMI_VisualizeBuffer && CurrentBufferVisualizationMode != NAME_None;
const bool bRayTracingDebugEnabled = CurrentViewMode == VMI_RayTracingDebug && CurrentRayTracingDebugVisualizationMode != NAME_None;
const bool bVisualizeGPUSkinCache = CurrentViewMode == VMI_VisualizeGPUSkinCache && CurrentGPUSkinCacheVisualizationMode != NAME_None;
const bool bCanDisableTonemapper = bVisualizeBufferEnabled || bVisualizeGPUSkinCache || (bRayTracingDebugEnabled && !FRayTracingDebugVisualizationMenuCommands::DebugModeShouldBeTonemapped(CurrentRayTracingDebugVisualizationMode));
// 设置flag
EngineShowFlagOverride(ESFIM_Editor, ViewFamily.ViewMode, ViewFamily.EngineShowFlags, bCanDisableTonemapper);
EngineShowFlagOrthographicOverride(IsPerspective(), ViewFamily.EngineShowFlags);
UpdateLightingShowFlags( ViewFamily.EngineShowFlags );
还在赋值 ViewFamily
ViewFamily.ExposureSettings = ExposureSettings;
ViewFamily.LandscapeLODOverride = LandscapeLODOverride;
// Setup the screen percentage and upscaling method for the view family.
{
checkf(ViewFamily.GetScreenPercentageInterface() == nullptr,
TEXT("Some code has tried to set up an alien screen percentage driver, that could be wrong if not supported very well by the RHI."));
// If not doing VR rendering, apply DPI derived resolution fraction even if show flag is disabled
if (!bStereoRendering && SupportsLowDPIPreview() && IsLowDPIPreview() && ViewFamily.SupportsScreenPercentage())
{
ViewFamily.SecondaryViewFraction = GetDPIDerivedResolutionFraction();
}
}
FSceneView* View = nullptr;
// Stereo rendering
// 如果是VR就要渲染两张图
const bool bStereoDeviceActive = bStereoRendering && GEngine->StereoRenderingDevice.IsValid();
int32 NumViews = bStereoRendering ? GEngine->StereoRenderingDevice->GetDesiredNumberOfViews(bStereoRendering) : 1;
for( int StereoViewIndex = 0; StereoViewIndex < NumViews; ++StereoViewIndex )
{
View = CalcSceneView( &ViewFamily, bStereoRendering ? StereoViewIndex : INDEX_NONE);
// 。。。
}
看这里
View = CalcSceneView( &ViewFamily, bStereoRendering ? StereoViewIndex : INDEX_NONE);
用 ViewFamily 获得一个 FSceneView
注意断点调试的时候,实际跑的是子类的 FLevelEditorViewportClient 关卡编辑器的viewport client
FSceneView* FLevelEditorViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily, const int32 StereoViewIndex)
{
bWasControlledByOtherViewport = false; // 是否被其他viewport控制
// set all other matching viewports to my location, if the LOD locking is enabled,
// unless another viewport already set me this frame (otherwise they fight)
// 是否锁定LOD
if (GEditor->bEnableLODLocking)
{
for (FLevelEditorViewportClient* ViewportClient : GEditor->GetLevelViewportClients()) // 拿到关卡编辑器视口客户端
{
//only change camera for a viewport that is looking at the same scene
if (ViewportClient == NULL || GetScene() != ViewportClient->GetScene())
{
continue;
}
// go over all other level viewports
if (ViewportClient->Viewport && ViewportClient != this)
{
// force camera of same-typed viewports
if (ViewportClient->GetViewportType() == GetViewportType()) // 关卡编辑器,上面一排有一个可以显示出正顶侧三视图,主视口位置动了,同步更新其他的视口。这里的set属性的意思
{
ViewportClient->SetViewLocation( GetViewLocation() );
ViewportClient->SetViewRotation( GetViewRotation() );
ViewportClient->SetOrthoZoom( GetOrthoZoom() );
// don't let this other viewport update itself in its own CalcSceneView
ViewportClient->bWasControlledByOtherViewport = true;
}
// when we are LOD locking, ortho views get their camera position from this view, so make sure it redraws
else if (IsPerspective() && !ViewportClient->IsPerspective()) // 当前是透视,其他不是
{
// don't let this other viewport update itself in its own CalcSceneView
ViewportClient->bWasControlledByOtherViewport = true;
}
}
// if the above code determined that this viewport has changed, delay the update unless
// an update is already in the pipe
if (ViewportClient->bWasControlledByOtherViewport && ViewportClient->TimeForForceRedraw == 0.0)
{
ViewportClient->TimeForForceRedraw = FPlatformTime::Seconds() + 0.9 + FMath::FRand() * 0.2; // 延迟更新视口
}
}
}
// FSceneView 是场景空间到2D屏幕的一个投影
FSceneView* View = FEditorViewportClient::CalcSceneView(ViewFamily, StereoViewIndex);
View->ViewActor = ActorLocks.GetLock().GetLockedActor(); // 摄像机看着的对象
View->SpriteCategoryVisibility = SpriteCategoryVisibility; // bit array 控制 sprite 可见性
View->bCameraCut = bEditorCameraCut; // 摄像机是否能在编辑器中被切换
View->bHasSelectedComponents = GEditor->GetSelectedComponentCount() > 0; // 关卡编辑器选中物体,物体detail中选中了组件
return View;
}
后面我们从这里继续
FSceneView* View = FEditorViewportClient::CalcSceneView(ViewFamily, StereoViewIndex);
FSceneView* FEditorViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily, const int32 StereoViewIndex)
{
const bool bStereoRendering = StereoViewIndex != INDEX_NONE; // 立体渲染,VR
FSceneViewInitOptions ViewInitOptions;
// ...
}
FSceneViewInitOptions
看 FSceneViewInitOptions
// Construction parameters for a FSceneView
struct FSceneViewInitOptions : public FSceneViewProjectionData
FSceneViewProjectionData
// Projection data for a FSceneView
struct FSceneViewProjectionData
{
FVector ViewOrigin; // 视图的原点 /** The view origin. */
FMatrix ViewRotationMatrix; // 世界空间 -》 视口空间 的旋转矩阵 /** Rotation matrix transforming from world space to view space. */
FMatrix ProjectionMatrix; // 投影矩阵 /** UE projection matrix projects such that clip space Z=1 is the near plane, and Z=0 is the infinite far plane. */
protected:
FIntRect ViewRect; //The unconstrained (no aspect ratio bars applied) view rectangle (also unscaled)
FIntRect ConstrainedViewRect; // The constrained view rectangle (identical to UnconstrainedUnscaledViewRect if aspect ratio is not constrained)
public:
void SetViewRectangle(const FIntRect& InViewRect); // set ViewRect = ConstrainedViewRect = InViewRect
void SetConstrainedViewRectangle(const FIntRect& InViewRect); // set ConstrainedViewRect = InViewRect;
bool IsValidViewRectangle() const; // ConstrainedViewRect.Min.X 和 ConstrainedViewRect.Min.Y >= 0 且 ConstrainedViewRect.Width() 和 ConstrainedViewRect.Height() > 0
bool IsPerspectiveProjection() const; // return ProjectionMatrix.M[3][3] < 1.0f;
FMatrix ComputeViewProjectionMatrix() const { return FTranslationMatrix(-ViewOrigin) * ViewRotationMatrix * ProjectionMatrix; } // VP
};
FSceneViewInitOptions
// Construction parameters for a FSceneView
struct FSceneViewInitOptions : public FSceneViewProjectionData
{
const FSceneViewFamily* ViewFamily;
FSceneViewStateInterface* SceneViewStateInterface;
const AActor* ViewActor; // 视图所属的玩家actor
int32 PlayerIndex; // 玩家索引
FViewElementDrawer* ViewElementDrawer; // 提供场景交互接口
// 视图的属性
FLinearColor BackgroundColor;
FLinearColor OverlayColor;
FLinearColor ColorScale;
// 立体渲染(VR)的属性
EStereoscopicPass StereoPass; /** For stereoscopic rendering, whether or not this is a full pass(全部渲染), or a primary(主要渲染通道) / secondary pass(共用渲染通道) */
int32 StereoViewIndex; /** For stereoscopic rendering, a unique index to identify the view across view families */
float WorldToMetersScale; /** Conversion from world units (uu) to meters, so we can scale motion to the world appropriately */
// 视口位置和旋转 /** The view origin and rotation without any stereo offsets applied to it */
FVector ViewLocation;
FRotator ViewRotation;
// 隐藏 和 显示组件的ID集合
TSet<FPrimitiveComponentId> HiddenPrimitives;
TOptional<TSet<FPrimitiveComponentId>> ShowOnlyPrimitives; /** The primitives which are visible for this view. If the array is not empty, all other primitives will be hidden. */
FIntPoint CursorPos; // 鼠标位置 -1,-1 if not setup
float LODDistanceFactor; // LOD距离缩放
float OverrideFarClippingPlaneDistance; // /** If > 0, overrides the view's far clipping plane with a plane at the specified distance. */
FVector OriginOffsetThisFrame; /** World origin offset value. Non-zero only for a single frame when origin is rebased */
bool bInCameraCut; // /** Was there a camera cut this frame? */
bool bUseFieldOfViewForLOD; /** Whether to use FOV when computing mesh LOD. */
// FOV相关 /** Actual field of view and that desired by the camera originally */
float FOV;
float DesiredFOV;
bool bUseFauxOrthoViewPos; /** In case of ortho, generate a fake view position that has a non-zero W component. The view position will be derived based on the view matrix. */
bool bIsSceneCapture; /** Whether this view is being used to render a scene capture. */
bool bIsSceneCaptureCube; /** Whether the scene capture is a cube map (bIsSceneCapture will also be set). */
bool bSceneCaptureUsesRayTracing; /** Whether this view uses ray tracing, for views that are used to render a scene capture. */
bool bIsReflectionCapture; /** Whether this view is being used to render a reflection capture. */
bool bIsPlanarReflection; /** Whether this view is being used to render a planar reflection. */
#if WITH_EDITOR
uint64 EditorViewBitflag; /** default to 0'th view index, which is a bitfield of 1 */
FVector OverrideLODViewOrigin; /** this can be specified for ortho views so that it's min draw distance/LOD parenting etc, is controlled by a perspective viewport */
bool bDisableGameScreenPercentage; /** Whether game screen percentage should be disabled. */
#endif
// ...
};
描述视图所属的玩家,鼠标,视图的各种属性,比如背景,位置旋转FOV,描述如何渲染这个视图的bool
回到这里
FSceneView* FEditorViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily, const int32 StereoViewIndex)
{
const bool bStereoRendering = StereoViewIndex != INDEX_NONE;
FSceneViewInitOptions ViewInitOptions;
// 。。。
FSceneView* View = new FSceneView(ViewInitOptions);
}
可以注意到这一段都是为了往 ViewInitOptions 填参数,然后最后构造一个view
FSceneView* FEditorViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily, const int32 StereoViewIndex)
{
FSceneViewInitOptions ViewInitOptions;
// FViewportCameraTransform view Location, Rotation, LookAt, StartLocation,可以看出来这是描述摄像机的
FViewportCameraTransform& ViewTransform = GetViewTransform();
// ELevelViewportType 视图类型 ,XY, XZ, YZ, 透视
const ELevelViewportType EffectiveViewportType = GetViewportType();
// Apply view modifiers.
FEditorViewportViewModifierParams ViewModifierParams;
{
// ...
}
// 。。。
}
看这个结构,主要 FMinimalViewInfo ViewInfo; 内容比较多
/** Parameter struct for editor viewport view modifiers */
struct FEditorViewportViewModifierParams
{
FMinimalViewInfo ViewInfo;
// 后处理混合,输入一个后处理的设置,和混合权重
void AddPostProcessBlend(const FPostProcessSettings& Settings, float Weight)
{
check(PostProcessSettings.Num() == PostProcessBlendWeights.Num());
PostProcessSettings.Add(Settings);
PostProcessBlendWeights.Add(Weight);
}
private:
TArray<FPostProcessSettings> PostProcessSettings;
TArray<float> PostProcessBlendWeights;
friend class FEditorViewportClient;
};
USTRUCT(BlueprintType)
struct FMinimalViewInfo
{
GENERATED_USTRUCT_BODY()
// 都是摄像机的一些属性,为了篇幅看起来不太多,清晰一点,我把注解去掉了
FVector Location;
FRotator Rotation;
float FOV;
float DesiredFOV;
// 正交模式 宽度,近景,远景层面的距离
float OrthoWidth; /** The desired width (in world units) of the orthographic view (ignored in Perspective mode) */
float OrthoNearClipPlane; /** The near plane distance of the orthographic view (in world units) */
float OrthoFarClipPlane; /** The far plane distance of the orthographic view (in world units) */
// 透视模式 的近景和横纵比
float PerspectiveNearClipPlane; /** near plane distance of the perspective view (in world units). Set to a negative value to use the default global value of GNearClippingPlane */
float AspectRatio; // Aspect Ratio (Width/Height)
// Aspect ratio axis constraint override
TOptional<EAspectRatioAxisConstraint> AspectRatioAxisConstraint;
uint32 bConstrainAspectRatio:1; // If bConstrainAspectRatio is true, black bars will be added if the destination view has a different aspect ratio than this camera requested.
uint32 bUseFieldOfViewForLOD:1; // If true, account for the field of view angle when computing which level of detail to use for meshes.
TEnumAsByte<ECameraProjectionMode::Type> ProjectionMode; // The type of camera,是透视还是正交
// 后处理参数
float PostProcessBlendWeight; /** Indicates if PostProcessSettings should be applied. */
struct FPostProcessSettings PostProcessSettings; /** Post-process settings to use if PostProcessBlendWeight is non-zero. */
//
FVector2D OffCenterProjectionOffset; /** Off-axis / off-center projection offset as proportion of screen dimensions */
TOptional<FTransform> PreviousViewTransform; /** Optional transform to be considered as this view's previous transform */
// ...
};
FSceneView* FEditorViewportClient::CalcSceneView(FSceneViewFamily* ViewFamily, const int32 StereoViewIndex)
{
// 。。。
// Apply view modifiers.
FEditorViewportViewModifierParams ViewModifierParams;
// Location Rotation FOV
{
ViewModifierParams.ViewInfo.Location = ViewTransform.GetLocation();
ViewModifierParams.ViewInfo.Rotation = ViewTransform.GetRotation();
if (bUseControllingActorViewInfo)
{
ViewModifierParams.ViewInfo.FOV = ControllingActorViewInfo.FOV;
}
else
{
ViewModifierParams.ViewInfo.FOV = ViewFOV;
}
if (bShouldApplyViewModifiers)
{
ViewModifiers.Broadcast(ViewModifierParams);
}
}
// 临时保存一下
const FVector ModifiedViewLocation = ViewModifierParams.ViewInfo.Location;
FRotator ModifiedViewRotation = ViewModifierParams.ViewInfo.Rotation;
const float ModifiedViewFOV = ViewModifierParams.ViewInfo.FOV;
//
if (bUseControllingActorViewInfo)
{
ControllingActorViewInfo.Location = ViewModifierParams.ViewInfo.Location;
ControllingActorViewInfo.Rotation = ViewModifierParams.ViewInfo.Rotation;
ControllingActorViewInfo.FOV = ViewModifierParams.ViewInfo.FOV;
}
//
ViewInitOptions.ViewOrigin = ModifiedViewLocation;
// 。。。
}
VR相关
// Apply head tracking! Note that this won't affect what the editor *thinks* the view location and rotation is, it will
// only affect the rendering of the scene.
if( bStereoRendering && GEngine->XRSystem.IsValid() && GEngine->XRSystem->IsHeadTrackingAllowed() )
{
FQuat CurrentHmdOrientation;
FVector CurrentHmdPosition;
GEngine->XRSystem->GetCurrentPose(IXRTrackingSystem::HMDDeviceId, CurrentHmdOrientation, CurrentHmdPosition );
const FQuat VisualRotation = ModifiedViewRotation.Quaternion() * CurrentHmdOrientation; // 四元数乘法 等于角度相加
ModifiedViewRotation = VisualRotation.Rotator();
ModifiedViewRotation.Normalize();
}
FIntPoint ViewportSize = Viewport->GetSizeXY();
// 限制范围
ViewportSize.X = FMath::Max(ViewportSize.X, 1);
ViewportSize.Y = FMath::Max(ViewportSize.Y, 1);
FIntPoint ViewportOffset(0, 0);
// We expect some size to avoid problems with the view rect manipulation
if (ViewportSize.X > 50 && ViewportSize.Y > 50)
{
int32 Value = CVarEditorViewportTest.GetValueOnGameThread(); // 获取一个测试命令变量的值
if (Value)
{
int InsetX = ViewportSize.X / 4;
int InsetY = ViewportSize.Y / 4;
// this allows to test various typical view port situations
// 出于测试目的的数值修改
switch (Value)
{
case 1: ViewportOffset.X += InsetX; ViewportOffset.Y += InsetY; ViewportSize.X -= InsetX * 2; ViewportSize.Y -= InsetY * 2; break;
case 2: ViewportOffset.Y += InsetY; ViewportSize.Y -= InsetY * 2; break;
case 3: ViewportOffset.X += InsetX; ViewportSize.X -= InsetX * 2; break;
case 4: ViewportSize.X /= 2; ViewportSize.Y /= 2; break;
case 5: ViewportSize.X /= 2; ViewportSize.Y /= 2; ViewportOffset.X += ViewportSize.X; break;
case 6: ViewportSize.X /= 2; ViewportSize.Y /= 2; ViewportOffset.Y += ViewportSize.Y; break;
case 7: ViewportSize.X /= 2; ViewportSize.Y /= 2; ViewportOffset.X += ViewportSize.X; ViewportOffset.Y += ViewportSize.Y; break;
}
}
}
// 赋值
ViewInitOptions.SetViewRectangle(FIntRect(ViewportOffset, ViewportOffset + ViewportSize));
宽高的调整约束
// no matter how we are drawn (forced or otherwise), reset our time here
TimeForForceRedraw = 0.0;
// 宽高比的调整
const bool bConstrainAspectRatio = bUseControllingActorViewInfo && ControllingActorViewInfo.bConstrainAspectRatio; // 调整宽度,高度是否要变换
EAspectRatioAxisConstraint AspectRatioAxisConstraint = GetDefault<ULevelEditorViewportSettings>()->AspectRatioAxisConstraint; // 约束宽高比的方式
if (bUseControllingActorViewInfo && ControllingActorAspectRatioAxisConstraint.IsSet())
{
AspectRatioAxisConstraint = ControllingActorAspectRatioAxisConstraint.GetValue();
}
通过 WorldSettings 设置 WorldToMetersScale
AWorldSettings* WorldSettings = nullptr;
if( GetScene() != nullptr && GetScene()->GetWorld() != nullptr )
{
WorldSettings = GetScene()->GetWorld()->GetWorldSettings();
}
if( WorldSettings != nullptr )
{
ViewInitOptions.WorldToMetersScale = WorldSettings->WorldToMeters;
}
计算透视和正交矩阵
if (bUseControllingActorViewInfo)
{
// @todo vreditor: Not stereo friendly yet
ViewInitOptions.ViewRotationMatrix = FInverseRotationMatrix(ModifiedViewRotation) * FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
FMinimalViewInfo::CalculateProjectionMatrixGivenView(ControllingActorViewInfo, AspectRatioAxisConstraint, Viewport, /*inout*/ ViewInitOptions);
}
else
{
//
if (EffectiveViewportType == LVT_Perspective)
{
// If stereo rendering is enabled, update the size and offset appropriately for this pass
// @todo vreditor: Also need to update certain other use cases of ViewFOV like culling, streaming, etc. (needs accessor)
// VR 跳过
if( bStereoRendering )
{
int32 X = 0;
int32 Y = 0;
uint32 SizeX = ViewportSize.X;
uint32 SizeY = ViewportSize.Y;
GEngine->StereoRenderingDevice->AdjustViewRect( StereoViewIndex, X, Y, SizeX, SizeY );
const FIntRect StereoViewRect = FIntRect( X, Y, X + SizeX, Y + SizeY );
ViewInitOptions.SetViewRectangle( StereoViewRect );
GEngine->StereoRenderingDevice->CalculateStereoViewOffset( StereoViewIndex, ModifiedViewRotation, ViewInitOptions.WorldToMetersScale, ViewInitOptions.ViewOrigin );
}
// Calc view rotation matrix 计算view矩阵
ViewInitOptions.ViewRotationMatrix = CalcViewRotationMatrix(ModifiedViewRotation);
// 。。。
}
else
{
// 。。。
}
if (bConstrainAspectRatio)
{
ViewInitOptions.SetConstrainedViewRectangle(Viewport->CalculateViewExtents(AspectRatio, ViewInitOptions.GetViewRect()));
}
}
看 ViewInitOptions.ViewRotationMatrix = CalcViewRotationMatrix(ModifiedViewRotation);
FMatrix FEditorViewportClient::CalcViewRotationMatrix(const FRotator& InViewRotation) const
{
const FViewportCameraTransform& ViewTransform = GetViewTransform();
if (bUsingOrbitCamera)
{
// @todo vreditor: Not stereo friendly yet
// 带轨迹的相机, 这么算
return FTranslationMatrix(ViewTransform.GetLocation()) * ViewTransform.ComputeOrbitMatrix();
}
else
{
// Create the view matrix
return FInverseRotationMatrix(InViewRotation);
}
}
看 FInverseRotationMatrix
template<typename T>
FORCEINLINE TInverseRotationMatrix<T>::TInverseRotationMatrix(const TRotator<T>& Rot)
: TMatrix<T>(
TMatrix<T>( // Yaw
TPlane<T>(+FMath::Cos(Rot.Yaw * UE_PI / 180.f), -FMath::Sin(Rot.Yaw * UE_PI / 180.f), 0.0f, 0.0f),
TPlane<T>(+FMath::Sin(Rot.Yaw * UE_PI / 180.f), +FMath::Cos(Rot.Yaw * UE_PI / 180.f), 0.0f, 0.0f),
TPlane<T>(0.0f, 0.0f, 1.0f, 0.0f),
TPlane<T>(0.0f, 0.0f, 0.0f, 1.0f)) *
TMatrix<T>( // Pitch
TPlane<T>(+FMath::Cos(Rot.Pitch * UE_PI / 180.f), 0.0f, -FMath::Sin(Rot.Pitch * UE_PI / 180.f), 0.0f),
TPlane<T>(0.0f, 1.0f, 0.0f, 0.0f),
TPlane<T>(+FMath::Sin(Rot.Pitch * UE_PI / 180.f), 0.0f, +FMath::Cos(Rot.Pitch * UE_PI / 180.f), 0.0f),
TPlane<T>(0.0f, 0.0f, 0.0f, 1.0f)) *
TMatrix<T>( // Roll
TPlane<T>(1.0f, 0.0f, 0.0f, 0.0f),
TPlane<T>(0.0f, +FMath::Cos(Rot.Roll * UE_PI / 180.f), +FMath::Sin(Rot.Roll * UE_PI / 180.f), 0.0f),
TPlane<T>(0.0f, -FMath::Sin(Rot.Roll * UE_PI / 180.f), +FMath::Cos(Rot.Roll * UE_PI / 180.f), 0.0f),
TPlane<T>(0.0f, 0.0f, 0.0f, 1.0f))
)
{ }
假设绕X, Y, Z 三个轴旋转的角度为 Yaw, Row,Pitch。View矩阵为
回来
if (bUseControllingActorViewInfo)
{
// 。。。
}
else
{
//
if (EffectiveViewportType == LVT_Perspective)
{
// 。。。
// Calc view rotation matrix
ViewInitOptions.ViewRotationMatrix = CalcViewRotationMatrix(ModifiedViewRotation);
// Rotate view 90 degrees 乘一个单位矩阵?
ViewInitOptions.ViewRotationMatrix = ViewInitOptions.ViewRotationMatrix * FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
if( bStereoRendering ) // VR 先不看
{
// @todo vreditor: bConstrainAspectRatio is ignored in this path, as it is in the game client as well currently
// Let the stereoscopic rendering device handle creating its own projection matrix, as needed
ViewInitOptions.ProjectionMatrix = GEngine->StereoRenderingDevice->GetStereoProjectionMatrix(StereoViewIndex);
ViewInitOptions.StereoPass = GEngine->StereoRenderingDevice->GetViewPassForIndex(bStereoRendering, StereoViewIndex);
}
else
{
const float MinZ = GetNearClipPlane();
const float MaxZ = MinZ;
// Avoid zero ViewFOV's which cause divide by zero's in projection matrix
const float MatrixFOV = FMath::Max(0.001f, ModifiedViewFOV) * (float)PI / 360.0f; // 弧度的一个角
if (bConstrainAspectRatio) // 是否约束屏幕的宽高比
{
if ((bool)ERHIZBuffer::IsInverted) // 近景是否大于远景,那么API就在使用反向的Z缓冲区
{
ViewInitOptions.ProjectionMatrix = FReversedZPerspectiveMatrix( // 算投影矩阵
MatrixFOV,
MatrixFOV,
1.0f,
AspectRatio,
MinZ,
MaxZ
);
}
else
{
ViewInitOptions.ProjectionMatrix = FPerspectiveMatrix( // 算投影矩阵
MatrixFOV,
MatrixFOV,
1.0f,
AspectRatio,
MinZ,
MaxZ
);
}
}
else
{
float XAxisMultiplier;
float YAxisMultiplier;
// X 轴为1 还是 Y轴为1
if (((ViewportSize.X > ViewportSize.Y) && (AspectRatioAxisConstraint == AspectRatio_MajorAxisFOV)) || (AspectRatioAxisConstraint == AspectRatio_MaintainXFOV))
{
//if the viewport is wider than it is tall
XAxisMultiplier = 1.0f;
YAxisMultiplier = ViewportSize.X / (float)ViewportSize.Y;
}
else
{
//if the viewport is taller than it is wide
XAxisMultiplier = ViewportSize.Y / (float)ViewportSize.X;
YAxisMultiplier = 1.0f;
}
if ((bool)ERHIZBuffer::IsInverted) // 近景是否大于远景,那么API就在使用反向的Z缓冲区
{
ViewInitOptions.ProjectionMatrix = FReversedZPerspectiveMatrix( // 投影矩阵
MatrixFOV,
MatrixFOV,
XAxisMultiplier,
YAxisMultiplier,
MinZ,
MaxZ
);
}
else
{
ViewInitOptions.ProjectionMatrix = FPerspectiveMatrix( // 投影矩阵
MatrixFOV,
MatrixFOV,
XAxisMultiplier,
YAxisMultiplier,
MinZ,
MaxZ
);
}
}
}
}
else
{
// 。。。
}
if (bConstrainAspectRatio)
{
ViewInitOptions.SetConstrainedViewRectangle(Viewport->CalculateViewExtents(AspectRatio, ViewInitOptions.GetViewRect()));
}
}
ERHIZBuffer::IsInverted 反向Z是为啥呢?
是为了提升深度精度的利用率。传统的是近是0,远是1。反向就是近是1,远是0。
这个点进去就是我们推的投影矩阵公式
template<typename T>
FORCEINLINE TReversedZPerspectiveMatrix<T>::TReversedZPerspectiveMatrix(T HalfFOVX, T HalfFOVY, T MultFOVX, T MultFOVY, T MinZ, T MaxZ)
: TMatrix<T>(
TPlane<T>(MultFOVX / FMath::Tan(HalfFOVX), 0.0f, 0.0f, 0.0f),
TPlane<T>(0.0f, MultFOVY / FMath::Tan(HalfFOVY), 0.0f, 0.0f),
TPlane<T>(0.0f, 0.0f, ((MinZ == MaxZ) ? 0.0f : MinZ / (MinZ - MaxZ)), 1.0f),
TPlane<T>(0.0f, 0.0f, ((MinZ == MaxZ) ? MinZ : -MaxZ * MinZ / (MinZ - MaxZ)), 0.0f)
)
{ }
if (bUseControllingActorViewInfo)
{
// 。。。
}
else
{
//
if (EffectiveViewportType == LVT_Perspective)
{
// 。。。
}
else
{
// 正交
static_assert((bool)ERHIZBuffer::IsInverted, "Check all the Rotation Matrix transformations!");
FMatrix::FReal ZScale = 0.5f / UE_OLD_HALF_WORLD_MAX; // LWC_TODO: WORLD_MAX misuse?
FMatrix::FReal ZOffset = UE_OLD_HALF_WORLD_MAX;
//The divisor for the matrix needs to match the translation code.
// 为了屏幕尺寸发生变化的时候,它也发生变化
const float Zoom = GetOrthoUnitsPerPixel(Viewport);
float OrthoWidth = Zoom * ViewportSize.X / 2.0f;
float OrthoHeight = Zoom * ViewportSize.Y / 2.0f;
if (EffectiveViewportType == LVT_OrthoXY)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(1, 0, 0, 0),
FPlane(0, -1, 0, 0),
FPlane(0, 0, -1, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoXZ)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(1, 0, 0, 0),
FPlane(0, 0, -1, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoYZ)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoNegativeXY)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(-1, 0, 0, 0),
FPlane(0, -1, 0, 0),
FPlane(0, 0, 1, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoNegativeXZ)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(-1, 0, 0, 0),
FPlane(0, 0, 1, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoNegativeYZ)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(0, 0, -1, 0),
FPlane(-1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
}
else if (EffectiveViewportType == LVT_OrthoFreelook)
{
ViewInitOptions.ViewRotationMatrix = FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
}
else
{
// Unknown viewport type
check(false);
}
ViewInitOptions.ProjectionMatrix = FReversedZOrthoMatrix(
OrthoWidth,
OrthoHeight,
ZScale,
ZOffset
);
}
if (bConstrainAspectRatio)
{
ViewInitOptions.SetConstrainedViewRectangle(Viewport->CalculateViewExtents(AspectRatio, ViewInitOptions.GetViewRect()));
}
}
最开始,还漏一个
if (bUseControllingActorViewInfo)
{
// @todo vreditor: Not stereo friendly yet
ViewInitOptions.ViewRotationMatrix = FInverseRotationMatrix(ModifiedViewRotation) * FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
FMinimalViewInfo::CalculateProjectionMatrixGivenView(ControllingActorViewInfo, AspectRatioAxisConstraint, Viewport, /*inout*/ ViewInitOptions);
}
使用控制角色的view info
void FMinimalViewInfo::CalculateProjectionMatrixGivenView(const FMinimalViewInfo& ViewInfo, TEnumAsByte<enum EAspectRatioAxisConstraint> AspectRatioAxisConstraint, FViewport* Viewport, FSceneViewProjectionData& InOutProjectionData)
{
FIntRect ViewExtents = Viewport->CalculateViewExtents(ViewInfo.AspectRatio, InOutProjectionData.GetViewRect());
CalculateProjectionMatrixGivenViewRectangle(ViewInfo, AspectRatioAxisConstraint, ViewExtents, InOutProjectionData);
}
void FMinimalViewInfo::CalculateProjectionMatrixGivenViewRectangle(const FMinimalViewInfo& ViewInfo, TEnumAsByte<enum EAspectRatioAxisConstraint> AspectRatioAxisConstraint, const FIntRect& ConstrainedViewRectangle, FSceneViewProjectionData& InOutProjectionData)
{
// Create the projection matrix (and possibly constrain the view rectangle)
if (ViewInfo.bConstrainAspectRatio)
{
// Enforce a particular aspect ratio for the render of the scene.
// Results in black bars at top/bottom etc.
InOutProjectionData.SetConstrainedViewRectangle(ConstrainedViewRectangle);
InOutProjectionData.ProjectionMatrix = ViewInfo.CalculateProjectionMatrix();
}
else
{
float XAxisMultiplier;
float YAxisMultiplier;
const FIntRect& ViewRect = InOutProjectionData.GetViewRect();
const int32 SizeX = ViewRect.Width();
const int32 SizeY = ViewRect.Height();
// Get effective aspect ratio axis constraint.
AspectRatioAxisConstraint = ViewInfo.AspectRatioAxisConstraint.Get(AspectRatioAxisConstraint);
// If x is bigger, and we're respecting x or major axis, AND mobile isn't forcing us to be Y axis aligned
const bool bMaintainXFOV =
((SizeX > SizeY) && (AspectRatioAxisConstraint == AspectRatio_MajorAxisFOV)) ||
(AspectRatioAxisConstraint == AspectRatio_MaintainXFOV) ||
(ViewInfo.ProjectionMode == ECameraProjectionMode::Orthographic);
if (bMaintainXFOV)
{
// If the viewport is wider than it is tall
XAxisMultiplier = 1.0f;
YAxisMultiplier = SizeX / (float)SizeY;
}
else
{
// If the viewport is taller than it is wide
XAxisMultiplier = SizeY / (float)SizeX;
YAxisMultiplier = 1.0f;
}
float MatrixHalfFOV;
if (!bMaintainXFOV && ViewInfo.AspectRatio != 0.f && !CVarUseLegacyMaintainYFOV.GetValueOnGameThread())
{
// The view-info FOV is horizontal. But if we have a different aspect ratio constraint, we need to
// adjust this FOV value using the aspect ratio it was computed with, so we that we can compute the
// complementary FOV value (with the *effective* aspect ratio) correctly.
const float HalfXFOV = FMath::DegreesToRadians(FMath::Max(0.001f, ViewInfo.FOV) / 2.f);
const float HalfYFOV = FMath::Atan(FMath::Tan(HalfXFOV) / ViewInfo.AspectRatio);
MatrixHalfFOV = HalfYFOV;
}
else
{
// Avoid divide by zero in the projection matrix calculation by clamping FOV.
// Note the division by 360 instead of 180 because we want the half-FOV.
MatrixHalfFOV = FMath::Max(0.001f, ViewInfo.FOV) * (float)UE_PI / 360.0f;
}
if (ViewInfo.ProjectionMode == ECameraProjectionMode::Orthographic)
{
const float OrthoWidth = ViewInfo.OrthoWidth / 2.0f * XAxisMultiplier;
const float OrthoHeight = (ViewInfo.OrthoWidth / 2.0f) / YAxisMultiplier;
const float NearPlane = ViewInfo.OrthoNearClipPlane;
const float FarPlane = ViewInfo.OrthoFarClipPlane;
const float ZScale = 1.0f / (FarPlane - NearPlane);
const float ZOffset = -NearPlane;
InOutProjectionData.ProjectionMatrix = FReversedZOrthoMatrix(
OrthoWidth,
OrthoHeight,
ZScale,
ZOffset
);
}
else
{
const float ClippingPlane = ViewInfo.GetFinalPerspectiveNearClipPlane();
InOutProjectionData.ProjectionMatrix = FReversedZPerspectiveMatrix(
MatrixHalfFOV,
MatrixHalfFOV,
XAxisMultiplier,
YAxisMultiplier,
ClippingPlane,
ClippingPlane
);
}
}
if (!ViewInfo.OffCenterProjectionOffset.IsZero())
{
const float Left = -1.0f + ViewInfo.OffCenterProjectionOffset.X;
const float Right = Left + 2.0f;
const float Bottom = -1.0f + ViewInfo.OffCenterProjectionOffset.Y;
const float Top = Bottom + 2.0f;
InOutProjectionData.ProjectionMatrix.M[2][0] = (Left + Right) / (Left - Right);
InOutProjectionData.ProjectionMatrix.M[2][1] = (Bottom + Top) / (Bottom - Top);
}
}
===END===
保护性设置
if (!ViewInitOptions.IsValidViewRectangle())
{
// Zero sized rects are invalid, so fake to 1x1 to avoid asserts later on
ViewInitOptions.SetViewRectangle(FIntRect(0, 0, 1, 1));
}
VR
// Allocate our stereo view state on demand, so that only viewports that actually use stereo features have one
const int32 ViewStateIndex = (StereoViewIndex != INDEX_NONE) ? StereoViewIndex : 0;
if (bStereoRendering)
{
if (StereoViewStates.Num() <= ViewStateIndex)
{
StereoViewStates.SetNum(ViewStateIndex + 1);
}
if (StereoViewStates[ViewStateIndex].GetReference() == nullptr)
{
FSceneInterface* Scene = GetScene();
StereoViewStates[ViewStateIndex].Allocate(Scene ? Scene->GetFeatureLevel() : GMaxRHIFeatureLevel);
}
}
一堆变量的赋值
ViewInitOptions.ViewFamily = ViewFamily;
ViewInitOptions.SceneViewStateInterface = ( (ViewStateIndex == 0) ? ViewState.GetReference() : StereoViewStates[ViewStateIndex].GetReference() );
ViewInitOptions.StereoViewIndex = StereoViewIndex;
ViewInitOptions.ViewElementDrawer = this;
ViewInitOptions.BackgroundColor = GetBackgroundColor();
ViewInitOptions.EditorViewBitflag = (uint64)1 << ViewIndex, // send the bit for this view - each actor will check it's visibility bits against this
// for ortho views to steal perspective view origin
ViewInitOptions.OverrideLODViewOrigin = FVector::ZeroVector;
ViewInitOptions.FOV = ModifiedViewFOV;
if (bUseControllingActorViewInfo)
{
ViewInitOptions.bUseFieldOfViewForLOD = ControllingActorViewInfo.bUseFieldOfViewForLOD;
ViewInitOptions.FOV = ControllingActorViewInfo.FOV;
}
ViewInitOptions.OverrideFarClippingPlaneDistance = FarPlane;
ViewInitOptions.CursorPos = CurrentMousePos;
获取 r.Test.ConstrainedView 这个值
最后 ViewInitOptions.SetConstrainedViewRectangle(ConstrainedViewRect); 修改 FIntRect ConstrainedViewRect; 这个值
干嘛用的在PIE运行敲一下,看下效果就知道了
static const auto CVarVSync = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Test.ConstrainedView"));
int32 Value = CVarVSync->GetValueOnGameThread();
if (Value)
{
// 。。。
ViewInitOptions.SetConstrainedViewRectangle(ConstrainedViewRect);
}
FSceneView* View = new FSceneView(ViewInitOptions);
然后 FSceneView 就出来了
FSceneView* View = new FSceneView(ViewInitOptions);
// ViewLocation 和 ViewRotation
View->ViewLocation = ModifiedViewLocation;
View->ViewRotation = ModifiedViewRotation;
// Color初始化
View->SubduedSelectionOutlineColor = GEngine->GetSubduedSelectionOutlineColor();
const UEditorStyleSettings* EditorStyle = GetDefault<UEditorStyleSettings>();
check(View->AdditionalSelectionOutlineColors.Num() <= UE_ARRAY_COUNT(EditorStyle->AdditionalSelectionColors))
for (int OutlineColorIndex = 0; OutlineColorIndex < View->AdditionalSelectionOutlineColors.Num(); ++OutlineColorIndex)
{
View->AdditionalSelectionOutlineColors[OutlineColorIndex] = EditorStyle->AdditionalSelectionColors[OutlineColorIndex];
}
// 加进视图里面
int32 FamilyIndex = ViewFamily->Views.Add(View);
check(FamilyIndex == View->StereoViewIndex || View->StereoViewIndex == INDEX_NONE);
// 后处理相关
View->StartFinalPostprocessSettings( View->ViewLocation );
if (bUseControllingActorViewInfo)
{
// Pass on the previous view transform of the controlling actor to the view
View->PreviousViewTransform = ControllingActorViewInfo.PreviousViewTransform;
View->OverridePostProcessSettings(ControllingActorViewInfo.PostProcessSettings, ControllingActorViewInfo.PostProcessBlendWeight);
for (int32 ExtraPPBlendIdx = 0; ExtraPPBlendIdx < ControllingActorExtraPostProcessBlends.Num(); ++ExtraPPBlendIdx)
{
FPostProcessSettings const& PPSettings = ControllingActorExtraPostProcessBlends[ExtraPPBlendIdx];
float const Weight = ControllingActorExtraPostProcessBlendWeights[ExtraPPBlendIdx];
View->OverridePostProcessSettings(PPSettings, Weight);
}
}
else
{
OverridePostProcessSettings(*View);
}
if (ViewModifierParams.ViewInfo.PostProcessBlendWeight > 0.f)
{
View->OverridePostProcessSettings(ViewModifierParams.ViewInfo.PostProcessSettings, ViewModifierParams.ViewInfo.PostProcessBlendWeight);
}
const int32 PPNum = FMath::Min(ViewModifierParams.PostProcessSettings.Num(), ViewModifierParams.PostProcessBlendWeights.Num());
for (int32 PPIndex = 0; PPIndex < PPNum; ++PPIndex)
{
const FPostProcessSettings& PPSettings = ViewModifierParams.PostProcessSettings[PPIndex];
const float PPWeight = ViewModifierParams.PostProcessBlendWeights[PPIndex];
View->OverridePostProcessSettings(PPSettings, PPWeight);
}
View->EndFinalPostprocessSettings(ViewInitOptions);
// 允许自定义一些扩展
for (int ViewExt = 0; ViewExt < ViewFamily->ViewExtensions.Num(); ViewExt++)
{
ViewFamily->ViewExtensions[ViewExt]->SetupView(*ViewFamily, *View);
}
return View;
CalcSceneView End
回退到这里了
void FEditorViewportClient::Draw(FViewport InViewport, FCanvas Canvas)
上面一堆是对于 FSceneViewFamilyContext ViewFamily 做初始化
然后遍历 NumViews 根据是否VR,VR有两。
CalcSceneView 我们创建了 FSceneView 然后继续
void FEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
// 。。。
// 上面一堆是对于 FSceneViewFamilyContext ViewFamily 做初始化
// 然后创建 View
FSceneView* View = nullptr;
// Stereo rendering
const bool bStereoDeviceActive = bStereoRendering && GEngine->StereoRenderingDevice.IsValid();
int32 NumViews = bStereoRendering ? GEngine->StereoRenderingDevice->GetDesiredNumberOfViews(bStereoRendering) : 1;
for( int StereoViewIndex = 0; StereoViewIndex < NumViews; ++StereoViewIndex )
{
View = CalcSceneView( &ViewFamily, bStereoRendering ? StereoViewIndex : INDEX_NONE);
SetupViewForRendering(ViewFamily,*View);
// 。。。
}
}
SetupViewForRendering
void FLevelEditorViewportClient::SetupViewForRendering( FSceneViewFamily& ViewFamily, FSceneView& View )
{
FEditorViewportClient::SetupViewForRendering( ViewFamily, View );
// 。。。
}
看父类
void FEditorViewportClient::SetupViewForRendering(FSceneViewFamily& ViewFamily, FSceneView& View)
{
// View 一些参数设置,像素查看器的设置。 略
}
void FLevelEditorViewportClient::SetupViewForRendering( FSceneViewFamily& ViewFamily, FSceneView& View )
{
FEditorViewportClient::SetupViewForRendering( ViewFamily, View );
ViewFamily.bDrawBaseInfo = bDrawBaseInfo;
ViewFamily.bCurrentlyBeingEdited = bCurrentlyEditingThroughMovementWidget;
// Don't use fading or color scaling while we're in light complexity mode, since it may change the colors!
// 复杂灯光情况下的设置
if(!ViewFamily.EngineShowFlags.LightComplexity)
{
if(bEnableFading)
{
View.OverlayColor = FadeColor;
View.OverlayColor.A = FMath::Clamp(FadeAmount, 0.f, 1.f);
}
if(bEnableColorScaling)
{
View.ColorScale = FLinearColor(FVector3f{ ColorScale });
}
}
// 拖拽
TSharedPtr<FDragDropOperation> DragOperation = FSlateApplication::Get().GetDragDroppingContent();
if (!(DragOperation.IsValid() && DragOperation->IsOfType<FBrushBuilderDragDropOp>()))
{
// Hide the builder brush when not in geometry mode
ViewFamily.EngineShowFlags.SetBuilderBrush(false);
}
// Update the listener.
// 播放的时候是否要播音乐
if (bHasAudioFocus)
{
UpdateAudioListener(View);
}
}
回来
void FEditorViewportClient::Draw(FViewport* InViewport, FCanvas* Canvas)
{
// 。。。
// 上面一堆是对于 FSceneViewFamilyContext ViewFamily 做初始化
// 然后创建 View
FSceneView* View = nullptr;
// Stereo rendering
const bool bStereoDeviceActive = bStereoRendering && GEngine->StereoRenderingDevice.IsValid();
int32 NumViews = bStereoRendering ? GEngine->StereoRenderingDevice->GetDesiredNumberOfViews(bStereoRendering) : 1;
for( int StereoViewIndex = 0; StereoViewIndex < NumViews; ++StereoViewIndex )
{
View = CalcSceneView( &ViewFamily, bStereoRendering ? StereoViewIndex : INDEX_NONE);
SetupViewForRendering(ViewFamily,*View);
// 这里继续
FSlateRect SafeFrame; // 视口的约束矩形
View->CameraConstrainedViewRect = View->UnscaledViewRect;
// 根据视口大小和DPI缩放算出一个矩形,保证窗口不会超过编辑器
if (CalculateEditorConstrainedViewRect(SafeFrame, Viewport, Canvas->GetDPIScale()))
{
View->CameraConstrainedViewRect = FIntRect(SafeFrame.Left, SafeFrame.Top, SafeFrame.Right, SafeFrame.Bottom);
}
if (World)
{
// 更新上一帧的view info
FWorldCachedViewInfo& WorldViewInfo = World->CachedViewInfoRenderedLastFrame.AddDefaulted_GetRef();
WorldViewInfo.ViewMatrix = View->ViewMatrices.GetViewMatrix();
WorldViewInfo.ProjectionMatrix = View->ViewMatrices.GetProjectionMatrix();
WorldViewInfo.ViewProjectionMatrix = View->ViewMatrices.GetViewProjectionMatrix();
WorldViewInfo.ViewToWorld = View->ViewMatrices.GetInvViewMatrix();
World->LastRenderTime = World->GetTimeSeconds();
}
}
}
// 设置 Screen 百分比 接口
{
// If a screen percentage interface was not set by one of the view extension, then set the legacy one.
// 设置 Screen 百分比 接口
if (ViewFamily.GetScreenPercentageInterface() == nullptr)
{
float GlobalResolutionFraction = 1.0f;
// Apply preview resolution fraction. Supported in stereo for VR Editor Mode only
if ((!bStereoRendering || bInVREditViewMode) &&
SupportsPreviewResolutionFraction() && ViewFamily.SupportsScreenPercentage())
{
if (PreviewResolutionFraction.IsSet() && bIsPreviewingResolutionFraction)
{
GlobalResolutionFraction = PreviewResolutionFraction.GetValue();
}
else
{
GlobalResolutionFraction = GetDefaultPrimaryResolutionFractionTarget();
}
// Force screen percentage's engine show flag to be turned on for preview screen percentage.
ViewFamily.EngineShowFlags.ScreenPercentage = (GlobalResolutionFraction != 1.0);
}
// In editor viewport, we ignore r.ScreenPercentage and FPostProcessSettings::ScreenPercentage by design.
ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(
ViewFamily, GlobalResolutionFraction));
}
check(ViewFamily.GetScreenPercentageInterface() != nullptr);
}
约束了宽高比,会把画布先化成黑色的
if (IsAspectRatioConstrained())
{
// Clear the background to black if the aspect ratio is constrained, as the scene view won't write to all pixels.
Canvas->Clear(FLinearColor::Black);
}
BeginRenderingViewFamily
GetRendererModule().BeginRenderingViewFamily(Canvas,&ViewFamily);
void FRendererModule::BeginRenderingViewFamily(FCanvas* Canvas, FSceneViewFamily* ViewFamily)
{
BeginRenderingViewFamilies(Canvas, TArrayView<FSceneViewFamily*>(&ViewFamily, 1));
}
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
TRACE_CPUPROFILER_EVENT_SCOPE(BeginRenderingViewFamily);
check(Canvas);
// 验证,多个view families的scene必须一样。一个view families可以理解为一个摄像机,一个families里面可以有多个各种的视图
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
check(ViewFamily);
check(ViewFamily->Scene == ViewFamilies[0]->Scene);
}
FScene* const Scene = ViewFamilies[0]->Scene ? ViewFamilies[0]->Scene->GetRenderScene() : nullptr;
if (Scene)
{
World = Scene->GetWorld();
if (World)
{
// Guarantee that all render proxies are up to date before kicking off a BeginRenderViewFamily.
World->SendAllEndOfFrameUpdates(); // 确保渲染代理都是最新的
GetNaniteVisualizationData().Pick(World);
}
}
UWorld::SendAllEndOfFrameUpdates
SendAllEndOfFrameUpdates 在渲染开始之前,确保渲染代理都是最新的
/**
* Send all render updates to the rendering thread.
*/
void UWorld::SendAllEndOfFrameUpdates()
{
SCOPED_NAMED_EVENT(UWorld_SendAllEndOfFrameUpdates, FColor::Yellow);
SCOPE_CYCLE_COUNTER(STAT_PostTickComponentUpdate);
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(EndOfFrameUpdates);
CSV_SCOPED_SET_WAIT_STAT(EndOfFrameUpdates);
// Allow systems to complete async work that could introduce additional components to end of frame updates
FWorldDelegates::OnWorldPreSendAllEndOfFrameUpdates.Broadcast(this);
if (!HasEndOfFrameUpdates()) // 确保延迟组件更新到了渲染线程
{
return;
}
// 。。。
}
bool UWorld::HasEndOfFrameUpdates() const
{
return ComponentsThatNeedEndOfFrameUpdate_OnGameThread.Num() > 0 || ComponentsThatNeedEndOfFrameUpdate.Num() > 0 || bMaterialParameterCollectionInstanceNeedsDeferredUpdate;
}
这里其实在进入 UpdateSingleViewportClient 之前,就已经 World->SendAllEndOfFrameUpdates(); 过了
/////////////////////////////
// Redraw viewports.
{
// Gather worlds that need EOF updates
// This must be done in two steps as the object hash table is locked during ForEachObjectOfClass so any NewObject calls would fail
TArray<UWorld*, TInlineAllocator<4>> WorldsToEOFUpdate;
ForEachObjectOfClass(UWorld::StaticClass(), [&WorldsToEOFUpdate](UObject* WorldObj)
{
UWorld* World = CastChecked<UWorld>(WorldObj);
if (World->HasEndOfFrameUpdates())
{
WorldsToEOFUpdate.Add(World);
}
});
// Make sure deferred component updates have been sent to the rendering thread.
for (UWorld* World : WorldsToEOFUpdate)
{
World->SendAllEndOfFrameUpdates();
}
}
// UpdateSingleViewportClient
/**
* Send all render updates to the rendering thread.
*/
void UWorld::SendAllEndOfFrameUpdates()
{
// 。。。
// 遍历所有需要末尾帧同步的组件列表
// Wait for tasks that are generating data for the render proxies, but are not awaited in any TickFunctions
// E.g., see cloth USkeletalMeshComponent::UpdateClothStateAndSimulate
for (UActorComponent* Component : ComponentsThatNeedPreEndOfFrameSync)
{
if (Component)
{
check(!IsValid(Component) || Component->GetMarkedForPreEndOfFrameSync());
if (IsValid(Component))
{
Component->OnPreEndOfFrameSync(); // 执行
}
FMarkComponentEndOfFrameUpdateState::ClearMarkedForPreEndOfFrameSync(Component); // 清除
}
}
ComponentsThatNeedPreEndOfFrameSync.Reset(); // reset
//If we call SendAllEndOfFrameUpdates during a tick, we must ensure that all marked objects have completed any async work etc before doing the updates.
// 如果这个函数在tick中被调用,那么就对所有组件 需要的 OnEndOfFrameUpdateDuringTick
if (bInTick)
{
SCOPE_CYCLE_COUNTER(STAT_OnEndOfFrameUpdateDuringTick);
//If this proves too slow we can possibly have the mark set a complimentary bit array that marks which components need this call? Or just another component array?
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate)
{
if (Component)
{
Component->OnEndOfFrameUpdateDuringTick();
}
}
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate_OnGameThread)
{
if (Component)
{
Component->OnEndOfFrameUpdateDuringTick();
}
}
}
// Issue a GPU event to wrap GPU work done during SendAllEndOfFrameUpdates, like skin cache updates
// 皮肤缓存的更新,比如蒙皮
FSendAllEndOfFrameUpdates SendAllEndOfFrameUpdates(Scene);
BeginSendEndOfFrameUpdatesDrawEvent(SendAllEndOfFrameUpdates);
// update all dirty components. 更新所有标记为脏的组件
FGuardValue_Bitfield(bPostTickComponentUpdate, true);
// 收集合并 LocalComponentsThatNeedEndOfFrameUpdate
static TArray<UActorComponent*> LocalComponentsThatNeedEndOfFrameUpdate;
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_PostTickComponentUpdate_Gather);
check(IsInGameThread() && !LocalComponentsThatNeedEndOfFrameUpdate.Num());
LocalComponentsThatNeedEndOfFrameUpdate.Append(ComponentsThatNeedEndOfFrameUpdate);
}
// 是否有并行的通知事件
// 控制台变量 允许异步渲染线程更新 在游戏线程更新的时候 && 上面收集的组件数量 > 工作线程数量 && 工作线程数量 > 2
const bool IsUsingParallelNotifyEvents = CVarAllowAsyncRenderThreadUpdatesDuringGamethreadUpdates.GetValueOnGameThread() > 0 &&
LocalComponentsThatNeedEndOfFrameUpdate.Num() > FTaskGraphInterface::Get().GetNumWorkerThreads() &&
FTaskGraphInterface::Get().GetNumWorkerThreads() > 2;
if (IsUsingParallelNotifyEvents)
{
#if WITH_EDITOR
FObjectCacheEventSink::BeginQueueNotifyEvents();
#endif
ParallelForWithPreWork(LocalComponentsThatNeedEndOfFrameUpdate.Num(), ParallelWork, GTWork);
#if WITH_EDITOR
// Any remaining events will be flushed with this call
FObjectCacheEventSink::EndQueueNotifyEvents();
#endif
}
else
{
GTWork();
ParallelFor(LocalComponentsThatNeedEndOfFrameUpdate.Num(), ParallelWork);
}
看 GTWork
GTWork
auto GTWork = [this]()
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_PostTickComponentUpdate_ForcedGameThread);
// To avoid any problems in case of reentrancy during the deferred update pass, we gather everything and clears the buffers first
// Reentrancy can occur if a render update need to force wait on an async resource and a progress bar ticks the game-thread during that time.
TArray< UActorComponent*> DeferredUpdates;
DeferredUpdates.Reserve(ComponentsThatNeedEndOfFrameUpdate_OnGameThread.Num());
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate_OnGameThread)
{
if (Component)
{
if (Component->IsRegistered() && !Component->IsTemplate() && IsValid(Component))
{
DeferredUpdates.Add(Component);
}
// 确保标记帧在结束线程
check(!IsValid(Component) || Component->GetMarkedForEndOfFrameUpdateState() == EComponentMarkedForEndOfFrameUpdateState::MarkedForGameThread);
// 已经添加进来了,就可以设置为未标记
FMarkComponentEndOfFrameUpdateState::Set(Component, INDEX_NONE, EComponentMarkedForEndOfFrameUpdateState::Unmarked);
}
}
ComponentsThatNeedEndOfFrameUpdate_OnGameThread.Reset();
ComponentsThatNeedEndOfFrameUpdate.Reset();
// We are only regenerating render state here, not components
FStaticMeshComponentBulkReregisterContext ReregisterContext(Scene, DeferredUpdates, EBulkReregister::RenderState);
for (UActorComponent* Component : DeferredUpdates)
{
Component->DoDeferredRenderUpdates_Concurrent();
}
};
然后对刚刚的array执行 DoDeferredRenderUpdates_Concurrent
void UActorComponent::DoDeferredRenderUpdates_Concurrent()
{
LLM_SCOPE(ELLMTag::SceneRender);
LLM_SCOPED_TAG_WITH_OBJECT_IN_SET(GetOutermost(), ELLMTagSet::Assets);
checkf(!IsUnreachable(), TEXT("%s"), *GetFullName());
checkf(!IsTemplate(), TEXT("%s"), *GetFullName());
checkf(IsValid(this), TEXT("%s"), *GetFullName());
FScopeCycleCounterUObject ContextScope(this);
FScopeCycleCounterUObject AdditionalScope(STATS ? AdditionalStatObject() : nullptr); // 统计用的
if(!IsRegistered())
{
UE_LOG(LogActorComponent, Log, TEXT("UpdateComponent: (%s) Not registered, Aborting."), *GetPathName());
return;
}
if(bRenderStateDirty) // 组件是否已经把整个状态发送到渲染线程了
{
SCOPE_CYCLE_COUNTER(STAT_PostTickComponentRecreate);
RecreateRenderState_Concurrent();
checkf(!bRenderStateDirty, TEXT("Failed to route CreateRenderState_Concurrent (%s)"), *GetFullName());
}
else // 如果没有
{
SCOPE_CYCLE_COUNTER(STAT_PostTickComponentLW);
// 是否xxx是dirty的,如果是就调用 SendXXX_Concurrent
if(bRenderTransformDirty)
{
// Update the component's transform if the actor has been moved since it was last updated.
SendRenderTransform_Concurrent();
}
if(bRenderDynamicDataDirty)
{
SendRenderDynamicData_Concurrent();
}
if (bRenderInstancesDirty)
{
SendRenderInstanceData_Concurrent();
}
}
}
比如
void UPrimitiveComponent::SendRenderTransform_Concurrent()
{
UpdateBounds(); // 更新物体边界,包围盒,用于碰撞AABB之类的,从 UStaticMeshComponent::UpdateBounds() 开始
// 。。。
}
UPrimitiveComponent 叫基元组件
包围盒从这里开始看。
void UStaticMeshComponent::UpdateBounds()
{
NotifyIfStaticMeshChanged();
Super::UpdateBounds();
}
然后
void UPrimitiveComponent::SendRenderTransform_Concurrent()
{
UpdateBounds();
// If the primitive isn't hidden update its transform.
const bool bDetailModeAllowsRendering = DetailMode <= GetCachedScalabilityCVars().DetailMode; // 细节模式是否允许被渲染
if( bDetailModeAllowsRendering && (ShouldRender() || bCastHiddenShadow || bAffectIndirectLightingWhileHidden || bRayTracingFarField))
{
// Update the scene info's transform for this primitive.
GetWorld()->Scene->UpdatePrimitiveTransform(this);
}
Super::SendRenderTransform_Concurrent();
}
看
GetWorld()->Scene->UpdatePrimitiveTransform(this);
FScene::UpdatePrimitiveTransform
记录一些时间
void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)
{
SCOPE_CYCLE_COUNTER(STAT_UpdatePrimitiveTransformGT);
SCOPED_NAMED_EVENT(FScene_UpdatePrimitiveTransform, FColor::Yellow);
// Save the world transform for next time the primitive is added to the scene
const float WorldTime = GetWorld()->GetTimeSeconds();
float DeltaTime = WorldTime - Primitive->LastSubmitTime;
if (DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f)
{
// Time was reset?
Primitive->LastSubmitTime = WorldTime;
}
else if (DeltaTime > 0.0001f)
{
// First call for the new frame?
Primitive->LastSubmitTime = WorldTime;
}
// 。。。
}
void FScene::UpdatePrimitiveTransform(UPrimitiveComponent* Primitive)
{
// ...
if (Primitive->SceneProxy) // 这个代理,相当于从 UPrimitiveComponent 里面拷贝了一些数据给渲染线程用
{
}
else // 如果没有就要去加
{
// If the primitive doesn't have a scene info object yet, it must be added from scratch.
AddPrimitive(Primitive);
}
如果有
if (Primitive->SceneProxy)
{
// Check if the primitive needs to recreate its proxy for the transform update.
// 是否创建资源代理来更新transform
if (Primitive->ShouldRecreateProxyOnUpdateTransform()) // return (Mobility != EComponentMobility::Movable);
{
// Re-add the primitive from scratch to recreate the primitive's proxy.
RemovePrimitive(Primitive);
AddPrimitive(Primitive);
}
else
{
// root position
FVector AttachmentRootPosition = Primitive->GetActorPositionForRenderer();
// 构造一个更新参数
struct FPrimitiveUpdateParams
{
FScene* Scene;
FPrimitiveSceneProxy* PrimitiveSceneProxy;
FBoxSphereBounds WorldBounds;
FBoxSphereBounds LocalBounds;
FMatrix LocalToWorld;
TOptional<FTransform> PreviousTransform;
FVector AttachmentRootPosition;
};
FPrimitiveUpdateParams UpdateParams;
UpdateParams.Scene = this;
UpdateParams.PrimitiveSceneProxy = Primitive->SceneProxy;
UpdateParams.WorldBounds = Primitive->Bounds;
UpdateParams.LocalToWorld = Primitive->GetRenderMatrix(); // world matrix
UpdateParams.AttachmentRootPosition = AttachmentRootPosition;
UpdateParams.LocalBounds = Primitive->GetLocalBounds();
UpdateParams.PreviousTransform = FMotionVectorSimulation::Get().GetPreviousTransform(Primitive);
// Help track down primitive with bad bounds way before the it gets to the renderer.
ensureMsgf(!UpdateParams.WorldBounds.BoxExtent.ContainsNaN() && !UpdateParams.WorldBounds.Origin.ContainsNaN() && !FMath::IsNaN(UpdateParams.WorldBounds.SphereRadius) && FMath::IsFinite(UpdateParams.WorldBounds.SphereRadius),
TEXT("NaNs found on Bounds for Primitive %s: Owner: %s, Resource: %s, Level: %s, Origin: %s, BoxExtent: %s, SphereRadius: %f"),
*Primitive->GetName(),
*Primitive->SceneProxy->GetOwnerName().ToString(),
*Primitive->SceneProxy->GetResourceName().ToString(),
*Primitive->SceneProxy->GetLevelName().ToString(),
*UpdateParams.WorldBounds.Origin.ToString(),
*UpdateParams.WorldBounds.BoxExtent.ToString(),
UpdateParams.WorldBounds.SphereRadius
);
bool bPerformUpdate = true;
// Redundant 冗余更新,和dirty是类似的意思
const bool bAllowSkip = GSkipRedundantTransformUpdate && Primitive->SceneProxy->CanSkipRedundantTransformUpdates();
if (bAllowSkip || GWarningOnRedundantTransformUpdate)
{
// 设置为冗余的更新_任何线程,主要检查这些变量值是不是和本身发生了变化,如果没变当然不用更新了
if (Primitive->SceneProxy->WouldSetTransformBeRedundant_AnyThread(
UpdateParams.LocalToWorld,
UpdateParams.WorldBounds,
UpdateParams.LocalBounds,
UpdateParams.AttachmentRootPosition))
{
// 变量和本身比没有变化,而且跳过更新,那就 bPerformUpdate = false;
if (bAllowSkip)
{
// Do not perform the transform update!
bPerformUpdate = false;
}
else
{
// Not skipping, and warnings are enabled.
UE_LOG(LogRenderer, Warning,
TEXT("Redundant UpdatePrimitiveTransform for Primitive %s: Owner: %s, Resource: %s, Level: %s"),
*Primitive->GetName(),
*Primitive->SceneProxy->GetOwnerName().ToString(),
*Primitive->SceneProxy->GetResourceName().ToString(),
*Primitive->SceneProxy->GetLevelName().ToString()
);
}
}
}
// 需要更新,让渲染线程去执行。带着参数过去
if (bPerformUpdate)
{
ENQUEUE_RENDER_COMMAND(UpdateTransformCommand)(
[UpdateParams](FRHICommandListImmediate& RHICmdList)
{
FScopeCycleCounter Context(UpdateParams.PrimitiveSceneProxy->GetStatId());
UpdateParams.Scene->UpdatePrimitiveTransform_RenderThread(
UpdateParams.PrimitiveSceneProxy,
UpdateParams.WorldBounds,
UpdateParams.LocalBounds,
UpdateParams.LocalToWorld,
UpdateParams.AttachmentRootPosition,
UpdateParams.PreviousTransform
);
}
);
}
}
}
这个必须要在渲染线程里面执行
void FScene::UpdatePrimitiveTransform_RenderThread(FPrimitiveSceneProxy* PrimitiveSceneProxy, const FBoxSphereBounds& WorldBounds, const FBoxSphereBounds& LocalBounds, const FMatrix& LocalToWorld, const FVector& AttachmentRootPosition, const TOptional<FTransform>& PreviousTransform)
{
check(IsInRenderingThread());
#if VALIDATE_PRIMITIVE_PACKED_INDEX
if (AddedPrimitiveSceneInfos.Find(PrimitiveSceneProxy->GetPrimitiveSceneInfo()) != nullptr)
{
check(PrimitiveSceneProxy->GetPrimitiveSceneInfo()->PackedIndex == INDEX_NONE);
}
else
{
check(PrimitiveSceneProxy->GetPrimitiveSceneInfo()->PackedIndex != INDEX_NONE);
}
check(RemovedPrimitiveSceneInfos.Find(PrimitiveSceneProxy->GetPrimitiveSceneInfo()) == nullptr);
#endif
// 游戏线程带过来的参数,更新到渲染线程的 UpdatedTransforms 里面
UpdatedTransforms.Update(PrimitiveSceneProxy, { WorldBounds, LocalBounds, LocalToWorld, AttachmentRootPosition });
// 上一个transform是有意义的,要重叠一下
if (PreviousTransform.IsSet())
{
OverridenPreviousTransforms.Update(PrimitiveSceneProxy->GetPrimitiveSceneInfo(), PreviousTransform.GetValue().ToMatrixWithScale());
}
}
另外一个分支,允许用代理更新transform
if (Primitive->ShouldRecreateProxyOnUpdateTransform()) // return (Mobility != EComponentMobility::Movable);
{
// Re-add the primitive from scratch to recreate the primitive's proxy.
RemovePrimitive(Primitive);
AddPrimitive(Primitive);
}
FScene::RemovePrimitive
void FScene::RemovePrimitive(UPrimitiveComponent* Primitive)
{
// If the bulk reregister flag is set, add / remove will be handled in bulk by the FStaticMeshComponentBulkReregisterContext
if (Primitive->bBulkReregister)
{
return;
}
BatchRemovePrimitives(MakeArrayView(&Primitive, 1));
}
void FScene::BatchRemovePrimitives(TArrayView<UPrimitiveComponent*> InPrimitives)
{
SCOPE_CYCLE_COUNTER(STAT_RemoveScenePrimitiveGT);
SCOPED_NAMED_EVENT(FScene_RemovePrimitive, FColor::Yellow);
struct FPrimitiveRemoveInfo
{
FPrimitiveSceneInfo* PrimitiveSceneInfo;
FThreadSafeCounter* AttachmentCounter;
};
// 维护 RemoveInfos
TArray<FPrimitiveRemoveInfo, TInlineAllocator<1>> RemoveInfos;
RemoveInfos.Reserve(InPrimitives.Num());
for (UPrimitiveComponent* Primitive : InPrimitives)
{
FPrimitiveSceneProxy* PrimitiveSceneProxy = Primitive->SceneProxy;
if (PrimitiveSceneProxy)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = PrimitiveSceneProxy->GetPrimitiveSceneInfo();
// Disassociate the primitive's scene proxy.
Primitive->SceneProxy = NULL;
RemoveInfos.Add({ PrimitiveSceneInfo, &Primitive->AttachmentCounter });
}
}
// 让渲染线程处理 RemoveInfos
if (RemoveInfos.Num())
{
// Send a command to the rendering thread to remove the primitives from the scene.
FScene* Scene = this;
ENQUEUE_RENDER_COMMAND(FRemovePrimitiveCommand)(
[Scene, RemoveInfos = MoveTemp(RemoveInfos)](FRHICommandList&)
{
for (const FPrimitiveRemoveInfo& RemoveInfo : RemoveInfos)
{
RemoveInfo.PrimitiveSceneInfo->Proxy->DestroyRenderThreadResources();
Scene->RemovePrimitiveSceneInfo_RenderThread(RemoveInfo.PrimitiveSceneInfo);
RemoveInfo.AttachmentCounter->Decrement();
}
});
}
}
RemovePrimitiveSceneInfo_RenderThread
void FScene::RemovePrimitiveSceneInfo_RenderThread(FPrimitiveSceneInfo* PrimitiveSceneInfo)
{
check(IsInRenderingThread()); // 这里是渲染线程
if (AddedPrimitiveSceneInfos.Remove(PrimitiveSceneInfo))
{
// 渲染线程移除xxx
check(PrimitiveSceneInfo->PackedIndex == INDEX_NONE);
UpdatedTransforms.Remove(PrimitiveSceneInfo->Proxy);
UpdatedCustomPrimitiveParams.Remove(PrimitiveSceneInfo->Proxy);
OverridenPreviousTransforms.Remove(PrimitiveSceneInfo);
UpdatedOcclusionBoundsSlacks.Remove(PrimitiveSceneInfo->Proxy);
DistanceFieldSceneDataUpdates.Remove(PrimitiveSceneInfo);
UpdatedAttachmentRoots.Remove(PrimitiveSceneInfo);
{
SCOPED_NAMED_EVENT(FScene_DeletePrimitiveSceneInfo, FColor::Red);
// Delete the PrimitiveSceneInfo on the game thread after the rendering thread has processed its removal.
// This must be done on the game thread because the hit proxy references (and possibly other members) need to be freed on the game thread.
struct DeferDeleteHitProxies : FDeferredCleanupInterface
{
DeferDeleteHitProxies(TArray<TRefCountPtr<HHitProxy>>&& InHitProxies) : HitProxies(MoveTemp(InHitProxies)) {}
TArray<TRefCountPtr<HHitProxy>> HitProxies;
};
// 渲染线程已经移除了,命中代理可能引用了其他成员,需要在游戏线程中释放PrimitiveSceneInfo
BeginCleanup(new DeferDeleteHitProxies(MoveTemp(PrimitiveSceneInfo->HitProxies)));
delete PrimitiveSceneInfo->Proxy;
delete PrimitiveSceneInfo;
}
}
else // 如果删除失败了,RemovedPrimitiveSceneInfos 会添加一个
{
check(PrimitiveSceneInfo->PackedIndex != INDEX_NONE);
check(RemovedPrimitiveSceneInfos.Find(PrimitiveSceneInfo) == nullptr);
RemovedPrimitiveSceneInfos.FindOrAdd(PrimitiveSceneInfo);
}
}
FScene::AddPrimitive
void FScene::AddPrimitive(UPrimitiveComponent* Primitive)
{
// If the bulk reregister flag is set, add / remove will be handled in bulk by the FStaticMeshComponentBulkReregisterContext
if (Primitive->bBulkReregister)
{
return;
}
BatchAddPrimitives(MakeArrayView(&Primitive, 1));
}
void FScene::BatchAddPrimitives(TArrayView<UPrimitiveComponent*> InPrimitives)
{
// ...
struct FCreateRenderThreadParameters
{
FCreateRenderThreadParameters(
FPrimitiveSceneInfo* InPrimitiveSceneInfo,
FPrimitiveSceneProxy* InPrimitiveSceneProxy,
FMatrix InRenderMatrix,
FBoxSphereBounds InWorldBounds,
FVector InAttachmentRootPosition,
FBoxSphereBounds InLocalBounds,
TOptional<FTransform> InPreviousTransform)
: PrimitiveSceneInfo(InPrimitiveSceneInfo), PrimitiveSceneProxy(InPrimitiveSceneProxy), RenderMatrix(InRenderMatrix), WorldBounds(InWorldBounds),
AttachmentRootPosition(InAttachmentRootPosition), LocalBounds(InLocalBounds), PreviousTransform(InPreviousTransform)
{}
FPrimitiveSceneInfo* PrimitiveSceneInfo;
FPrimitiveSceneProxy* PrimitiveSceneProxy;
FMatrix RenderMatrix;
FBoxSphereBounds WorldBounds;
FVector AttachmentRootPosition;
FBoxSphereBounds LocalBounds;
TOptional<FTransform> PreviousTransform;
};
// 目的是构造这个 ParamsList
TArray<FCreateRenderThreadParameters, TInlineAllocator<1>> ParamsList;
ParamsList.Reserve(InPrimitives.Num());
for (UPrimitiveComponent* Primitive : InPrimitives)
{
checkf(!Primitive->IsUnreachable(), TEXT("%s"), *Primitive->GetFullName());
const float WorldTime = GetWorld()->GetTimeSeconds();
// Save the world transform for next time the primitive is added to the scene
// 上一次提交的时间
float DeltaTime = WorldTime - Primitive->LastSubmitTime;
if ( DeltaTime < -0.0001f || Primitive->LastSubmitTime < 0.0001f )
{
// Time was reset?
Primitive->LastSubmitTime = WorldTime;
}
else if ( DeltaTime > 0.0001f )
{
// First call for the new frame?
Primitive->LastSubmitTime = WorldTime;
}
checkf(!Primitive->SceneProxy, TEXT("Primitive has already been added to the scene!"));
// Create the primitive's scene proxy.
// 获取场景的 基源组件
FPrimitiveSceneProxy* PrimitiveSceneProxy = Primitive->CreateSceneProxy();
Primitive->SceneProxy = PrimitiveSceneProxy;
if(!PrimitiveSceneProxy)
{
// Primitives which don't have a proxy are irrelevant to the scene manager.
continue;
}
// Create the primitive scene info. new 一个 FPrimitiveSceneInfo
FPrimitiveSceneInfo* PrimitiveSceneInfo = new FPrimitiveSceneInfo(Primitive, this);
PrimitiveSceneProxy->PrimitiveSceneInfo = PrimitiveSceneInfo;
// Cache the primitives initial transform.
// 世界矩阵 和 根位置
FMatrix RenderMatrix = Primitive->GetRenderMatrix();
FVector AttachmentRootPosition = Primitive->GetActorPositionForRenderer();
// 加进去
ParamsList.Emplace(
PrimitiveSceneInfo,
PrimitiveSceneProxy,
RenderMatrix,
Primitive->Bounds,
AttachmentRootPosition,
Primitive->GetLocalBounds(),
// If this primitive has a simulated previous transform, ensure that the velocity data for the scene representation is correct.
FMotionVectorSimulation::Get().GetPreviousTransform(Primitive)
);
// Help track down primitive with bad bounds way before the it gets to the Renderer
ensureMsgf(!Primitive->Bounds.ContainsNaN(),
TEXT("Nans found on Bounds for Primitive %s: Origin %s, BoxExtent %s, SphereRadius %f"), *Primitive->GetName(), *Primitive->Bounds.Origin.ToString(), *Primitive->Bounds.BoxExtent.ToString(), Primitive->Bounds.SphereRadius);
INC_DWORD_STAT_BY( STAT_GameToRendererMallocTotal, PrimitiveSceneProxy->GetMemoryFootprint() + PrimitiveSceneInfo->GetMemoryFootprint() );
// Verify the primitive is valid
VerifyProperPIEScene(Primitive, World);
// Increment the attachment counter, the primitive is about to be attached to the scene.
// attachment counter++, 组件准备要附加到场景里面了
Primitive->AttachmentCounter.Increment();
}
}
准备把 ParamsList 丢到渲染线程了
然后再渲染线程set transform
// Create any RenderThreadResources required and send a command to the rendering thread to add the primitive to the scene.
FScene* Scene = this;
ENQUEUE_RENDER_COMMAND(AddPrimitiveCommand)(
[ParamsList = MoveTemp(ParamsList), Scene](FRHICommandListImmediate& RHICmdList)
{
for (const FCreateRenderThreadParameters& Params : ParamsList)
{
FPrimitiveSceneProxy* SceneProxy = Params.PrimitiveSceneProxy;
FScopeCycleCounter Context(SceneProxy->GetStatId());
SceneProxy->SetTransform(Params.RenderMatrix, Params.WorldBounds, Params.LocalBounds, Params.AttachmentRootPosition);
// Create any RenderThreadResources required.
SceneProxy->CreateRenderThreadResources();
Scene->AddPrimitiveSceneInfo_RenderThread(Params.PrimitiveSceneInfo, Params.PreviousTransform);
}
});
看下 AddPrimitiveSceneInfo_RenderThread。
void FScene::AddPrimitiveSceneInfo_RenderThread(FPrimitiveSceneInfo* PrimitiveSceneInfo, const TOptional<FTransform>& PreviousTransform)
{
check(IsInRenderingThread());
check(PrimitiveSceneInfo->PackedIndex == INDEX_NONE);
check(AddedPrimitiveSceneInfos.Find(PrimitiveSceneInfo) == nullptr);
AddedPrimitiveSceneInfos.FindOrAdd(PrimitiveSceneInfo);
if (PreviousTransform.IsSet())
{
OverridenPreviousTransforms.Update(PrimitiveSceneInfo, PreviousTransform.GetValue().ToMatrixWithScale());
}
}
回到 void UWorld::SendAllEndOfFrameUpdates()
else
{
GTWork();
ParallelFor(LocalComponentsThatNeedEndOfFrameUpdate.Num(), ParallelWork);
}
看 ParallelFor,用于在多线程中执行相同的任务
多线程中执行一些延迟渲染的更新任务
auto ParallelWork = [IsUsingParallelNotifyEvents](int32 Index)
{
TRACE_CPUPROFILER_EVENT_SCOPE(DeferredRenderUpdates);
FOptionalTaskTagScope Scope(ETaskTag::EParallelGameThread);
#if WITH_EDITOR
if (!IsInParallelGameThread() && IsInGameThread() && IsUsingParallelNotifyEvents)
{
FObjectCacheEventSink::ProcessQueuedNotifyEvents();
}
#endif
UActorComponent* NextComponent = LocalComponentsThatNeedEndOfFrameUpdate[Index];
if (NextComponent)
{
if (NextComponent->IsRegistered() && !NextComponent->IsTemplate() && IsValid(NextComponent))
{
NextComponent->DoDeferredRenderUpdates_Concurrent();
}
check(!IsValid(NextComponent) || NextComponent->GetMarkedForEndOfFrameUpdateState() == EComponentMarkedForEndOfFrameUpdateState::Marked);
FMarkComponentEndOfFrameUpdateState::Set(NextComponent, INDEX_NONE, EComponentMarkedForEndOfFrameUpdateState::Unmarked);
}
};
DeferredUpdateRenderState
材质参数
然后 结束 UWorld::SendAllEndOfFrameUpdates
for (UMaterialParameterCollectionInstance* ParameterCollectionInstance : ParameterCollectionInstances)
{
if (ParameterCollectionInstance)
{
ParameterCollectionInstance->DeferredUpdateRenderState(false);
}
}
bMaterialParameterCollectionInstanceNeedsDeferredUpdate = false;
LocalComponentsThatNeedEndOfFrameUpdate.Reset();
EndSendEndOfFrameUpdatesDrawEvent(SendAllEndOfFrameUpdates);
void UMaterialParameterCollectionInstance::DeferredUpdateRenderState(bool bRecreateUniformBuffer)
{
checkf(bNeedsRenderStateUpdate || !bRecreateUniformBuffer, TEXT("DeferredUpdateRenderState was told to recreate the uniform buffer, but there's nothing to update"));
if (bNeedsRenderStateUpdate && World.IsValid())
{
// Propagate the new values to the rendering thread
TArray<FVector4f> ParameterData;
GetParameterData(ParameterData);
Resource->GameThread_UpdateContents(Collection.IsValid() ? Collection->StateId : FGuid(), ParameterData, GetFName(), bRecreateUniformBuffer);
}
bNeedsRenderStateUpdate = false;
}
获取材质参数,然后 GameThread_UpdateContents
让渲染线程 Resource->UpdateContents(InGuid, Data, InOwnerName, bRecreateUniformBuffer);
void FMaterialParameterCollectionInstanceResource::GameThread_UpdateContents(const FGuid& InGuid, const TArray<FVector4f>& Data, const FName& InOwnerName, bool bRecreateUniformBuffer)
{
FMaterialParameterCollectionInstanceResource* Resource = this;
ENQUEUE_RENDER_COMMAND(UpdateCollectionCommand)(
[InGuid, Data, InOwnerName, Resource, bRecreateUniformBuffer](FRHICommandListImmediate& RHICmdList)
{
Resource->UpdateContents(InGuid, Data, InOwnerName, bRecreateUniformBuffer);
}
);
}
void FMaterialParameterCollectionInstanceResource::UpdateContents(const FGuid& InId, const TArray<FVector4f>& Data, const FName& InOwnerName, bool bRecreateUniformBuffer)
{
Id = InId;
OwnerName = InOwnerName;
if (InId != FGuid() && Data.Num() > 0)
{
const uint32 NewSize = Data.GetTypeSize() * Data.Num();
check(UniformBufferLayout == nullptr || UniformBufferLayout->Resources.Num() == 0);
if (!bRecreateUniformBuffer && IsValidRef(UniformBuffer))
{
check(NewSize == UniformBufferLayout->ConstantBufferSize);
check(UniformBuffer->GetLayoutPtr() == UniformBufferLayout);
FRHICommandListImmediate::Get().UpdateUniformBuffer(UniformBuffer, Data.GetData());
}
else
{
FRHIUniformBufferLayoutInitializer UniformBufferLayoutInitializer(TEXT("MaterialParameterCollectionInstanceResource"));
UniformBufferLayoutInitializer.ConstantBufferSize = NewSize;
UniformBufferLayoutInitializer.ComputeHash();
UniformBufferLayout = RHICreateUniformBufferLayout(UniformBufferLayoutInitializer); // 层级,用于管理 UniformBuffer
UniformBuffer = RHICreateUniformBuffer(Data.GetData(), UniformBufferLayout, UniformBuffer_MultiFrame);
}
}
}
UniformBuffer 统一buff,可以简单理解为常量缓冲区
UniformBufferLayout = RHICreateUniformBufferLayout(UniformBufferLayoutInitializer);
直接new
FORCEINLINE FUniformBufferLayoutRHIRef RHICreateUniformBufferLayout(const FRHIUniformBufferLayoutInitializer& Initializer)
{
LLM_SCOPE_BYNAME(TEXT("RHIMisc/CreateUniformBufferLayout"));
return new FRHIUniformBufferLayout(Initializer);
}
创建统一缓冲区(常量缓冲区)
UniformBuffer = RHICreateUniformBuffer(Data.GetData(), UniformBufferLayout, UniformBuffer_MultiFrame);
FORCEINLINE FUniformBufferRHIRef RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation = EUniformBufferValidation::ValidateResources)
{
LLM_SCOPE_BYNAME(TEXT("RHIMisc/CreateUniformBuffer"));
return GDynamicRHI->RHICreateUniformBuffer(Contents, Layout, Usage, Validation);
}
然后进入不同跨平台的接口
RHICreateUniformBuffer
可以看下D3D11的
FUniformBufferRHIRef FD3D12DynamicRHI::RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation)
{
SCOPE_CYCLE_COUNTER(STAT_D3D12UpdateUniformBufferTime);
if (Contents && Validation == EUniformBufferValidation::ValidateResources)
{
ValidateShaderParameterResourcesRHI(Contents, *Layout);
}
// 。。。
首先 ValidateShaderParameterResourcesRHI 会验证渲染参数
void ValidateShaderParameterResourcesRHI(const void* Contents, const FRHIUniformBufferLayout& Layout)
{
for (int32 Index = 0, Count = Layout.Resources.Num(); Index < Count; ++Index)
{
const auto Parameter = Layout.Resources[Index];
FRHIResource* Resource = GetShaderParameterResourceRHI(Contents, Parameter.MemberOffset, Parameter.MemberType);
const bool bSRV =
Parameter.MemberType == UBMT_SRV ||
Parameter.MemberType == UBMT_RDG_TEXTURE_SRV ||
Parameter.MemberType == UBMT_RDG_BUFFER_SRV;
// Allow null SRV's in uniform buffers for feature levels that don't support SRV's in shaders
if (GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1 && bSRV)
{
continue;
}
checkf(Resource, TEXT("Null resource entry in uniform buffer parameters: %s.Resources[%u], ResourceType 0x%x."), *Layout.GetDebugName(), Index, Parameter.MemberType);
}
}
看看 FRHIUniformBufferLayout,在便利这个资源
/** The layout of a uniform buffer in memory. */
struct FRHIUniformBufferLayout : public FRHIResource
{
/** The list of all resource inlined into the shader parameter structure. */
const TArray<FRHIUniformBufferResource> Resources; // 统一buff的资源
然后 GetShaderParameterResourceRHI 来获取 FRHIResource
这里的 Contents就是,这里的 ParameterData。一路传进来的
void UMaterialParameterCollectionInstance::DeferredUpdateRenderState(bool bRecreateUniformBuffer)
{
checkf(bNeedsRenderStateUpdate || !bRecreateUniformBuffer, TEXT("DeferredUpdateRenderState was told to recreate the uniform buffer, but there's nothing to update"));
if (bNeedsRenderStateUpdate && World.IsValid())
{
// Propagate the new values to the rendering thread
TArray<FVector4f> ParameterData;
GetParameterData(ParameterData);
Resource->GameThread_UpdateContents(Collection.IsValid() ? Collection->StateId : FGuid(), ParameterData, GetFName(), bRecreateUniformBuffer);
}
bNeedsRenderStateUpdate = false;
}
遍历Resources,通过 GetShaderParameterResourceRHI 创建 FRHIResource。
Contents 是材质参数
这个 Parameter.MemberType 是这样的
/** The base type of a value in a shader parameter structure. */
enum EUniformBufferBaseType : uint8
{
UBMT_INVALID,
// Invalid type when trying to use bool, to have explicit error message to programmer on why
// they shouldn't use bool in shader parameter structures.
UBMT_BOOL,
// Parameter types.
UBMT_INT32,
UBMT_UINT32,
UBMT_FLOAT32,
// RHI resources not tracked by render graph.
UBMT_TEXTURE,
UBMT_SRV,
UBMT_UAV,
UBMT_SAMPLER,
// Resources tracked by render graph.
UBMT_RDG_TEXTURE,
UBMT_RDG_TEXTURE_ACCESS,
UBMT_RDG_TEXTURE_ACCESS_ARRAY,
UBMT_RDG_TEXTURE_SRV,
UBMT_RDG_TEXTURE_UAV,
UBMT_RDG_BUFFER_ACCESS,
UBMT_RDG_BUFFER_ACCESS_ARRAY,
UBMT_RDG_BUFFER_SRV,
UBMT_RDG_BUFFER_UAV,
UBMT_RDG_UNIFORM_BUFFER,
// Nested structure.
UBMT_NESTED_STRUCT,
// Structure that is nested on C++ side, but included on shader side.
UBMT_INCLUDED_STRUCT,
// GPU Indirection reference of struct, like is currently named Uniform buffer.
UBMT_REFERENCED_STRUCT,
// Structure dedicated to setup render targets for a rasterizer pass.
UBMT_RENDER_TARGET_BINDING_SLOTS,
EUniformBufferBaseType_Num,
EUniformBufferBaseType_NumBits = 5,
};
然后是特性级别的判断。
回到
FUniformBufferRHIRef FD3D11DynamicRHI::RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout* Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation)
{
if (Contents && Validation == EUniformBufferValidation::ValidateResources)
{
ValidateShaderParameterResourcesRHI(Contents, *Layout);
}
FD3D11UniformBuffer* NewUniformBuffer = nullptr;
const uint32 NumBytes = Layout->ConstantBufferSize;
if (NumBytes > 0)
{
// Constant buffers must also be 16-byte aligned.
// 字节对齐校验,字节大小校验
check(Align(NumBytes,16) == NumBytes);
check(Align(Contents,16) == Contents);
check(NumBytes <= D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16);
check(NumBytes < (1 << NumPoolBuckets));
SCOPE_CYCLE_COUNTER(STAT_D3D11UpdateUniformBufferTime);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (IsPoolingEnabled() && (IsInRenderingThread() || IsInRHIThread()) && !UE::Tasks::Private::IsThreadRetractingTask()) // 启用缓冲池,渲染线程,RHI线程
PRAGMA_ENABLE_DEPRECATION_WARNINGS
{
const bool bAllocatedFromPool = true;
if (ShouldNotEnqueueRHICommand()) // 是否把RHI命令加入队列
{
TRefCountPtr<ID3D11Buffer> UniformBufferResource = CreateAndUpdatePooledUniformBuffer( // 申请一个DX11的Buffer,并且是从缓冲池
Direct3DDevice.GetReference(),
Direct3DDeviceIMContext.GetReference(),
Contents,
NumBytes);
NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, UniformBufferResource, FRingAllocation(), bAllocatedFromPool); // 新buff
}
else
{
NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, nullptr, FRingAllocation(), bAllocatedFromPool);
void* CPUContent = nullptr;
if (Contents)
{
CPUContent = FMemory::Malloc(NumBytes);
FMemory::Memcpy(CPUContent, Contents, NumBytes);
}
// 然后在RIH线程中
RunOnRHIThread(
[NewUniformBuffer, CPUContent, NumBytes]()
{
// 从 CPUContent 里面
NewUniformBuffer->Resource = CreateAndUpdatePooledUniformBuffer(
D3D11RHI_DEVICE,
D3D11RHI_IMMEDIATE_CONTEXT,
CPUContent,
NumBytes);
FMemory::Free(CPUContent);
});
}
}
else
{
// No pooling
// 。。。
}
}
else
{
// This uniform buffer contains no constants, only a resource table.
const bool bAllocatedFromPool = false;
NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, nullptr, FRingAllocation(), bAllocatedFromPool);
}
// 。。。
return NewUniformBuffer;
}
static TRefCountPtr<ID3D11Buffer> CreateAndUpdatePooledUniformBuffer(
FD3D11Device* Device,
FD3D11DeviceContext* Context,
const void* Contents,
uint32 NumBytes)
{
// 。。。
}
如果没有缓存池
else
{
// No pooling
D3D11_BUFFER_DESC Desc;
Desc.ByteWidth = NumBytes;
Desc.Usage = D3D11_USAGE_DYNAMIC;
Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
Desc.MiscFlags = 0;
Desc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA ImmutableData;
ImmutableData.pSysMem = Contents;
ImmutableData.SysMemPitch = ImmutableData.SysMemSlicePitch = 0;
TRefCountPtr<ID3D11Buffer> UniformBufferResource;
VERIFYD3D11RESULT_EX(Direct3DDevice->CreateBuffer(&Desc, Contents ? &ImmutableData : nullptr,UniformBufferResource.GetInitReference()), Direct3DDevice);
const bool bAllocatedFromPool = false;
NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, UniformBufferResource, FRingAllocation(), bAllocatedFromPool);
INC_DWORD_STAT(STAT_D3D11NumImmutableUniformBuffers);
}
RHIUpdateUniformBuffer
回到这里
void FMaterialParameterCollectionInstanceResource::UpdateContents(const FGuid& InId, const TArray<FVector4f>& Data, const FName& InOwnerName, bool bRecreateUniformBuffer)
{
Id = InId;
OwnerName = InOwnerName;
if (InId != FGuid() && Data.Num() > 0)
{
const uint32 NewSize = Data.GetTypeSize() * Data.Num();
check(UniformBufferLayout == nullptr || UniformBufferLayout->Resources.Num() == 0);
if (!bRecreateUniformBuffer && IsValidRef(UniformBuffer))
{
check(NewSize == UniformBufferLayout->ConstantBufferSize);
check(UniformBuffer->GetLayoutPtr() == UniformBufferLayout);
FRHICommandListImmediate::Get().UpdateUniformBuffer(UniformBuffer, Data.GetData());
}
else
{
FRHIUniformBufferLayoutInitializer UniformBufferLayoutInitializer(TEXT("MaterialParameterCollectionInstanceResource"));
UniformBufferLayoutInitializer.ConstantBufferSize = NewSize;
UniformBufferLayoutInitializer.ComputeHash();
UniformBufferLayout = RHICreateUniformBufferLayout(UniformBufferLayoutInitializer); // 层级,用于管理 UniformBuffer
UniformBuffer = RHICreateUniformBuffer(Data.GetData(), UniformBufferLayout, UniformBuffer_MultiFrame);
}
}
}
FRHICommandListImmediate::Get().UpdateUniformBuffer(UniformBuffer, Data.GetData());
FORCEINLINE void UpdateUniformBuffer(FRHIUniformBuffer* UniformBufferRHI, const void* Contents)
{
FRHICommandListScopedPipelineGuard ScopedPipeline(*this);
GDynamicRHI->RHIUpdateUniformBuffer(*this, UniformBufferRHI, Contents);
}
看看这个
void FD3D11DynamicRHI::RHIUpdateUniformBuffer(FRHICommandListBase& RHICmdList, FRHIUniformBuffer* UniformBufferRHI, const void* Contents)
{
check(UniformBufferRHI);
// cast buff,获得layout,验证是不是合法的SRV
FD3D11UniformBuffer* UniformBuffer = ResourceCast(UniformBufferRHI);
const FRHIUniformBufferLayout& Layout = UniformBufferRHI->GetLayout();
ValidateShaderParameterResourcesRHI(Contents, Layout);
const uint32 ConstantBufferSize = Layout.ConstantBufferSize;
const int32 NumResources = Layout.Resources.Num();
check(UniformBuffer->GetResourceTable().Num() == NumResources);
if (RHICmdList.Bypass()) // 命令是否要绕过RHI的命令列表
{
UpdateUniformBufferContents(Direct3DDevice, Direct3DDeviceIMContext, UniformBuffer, Contents, ConstantBufferSize);
// UniformBuffer->GetResourceTable() 到shader参数 做一个映射
for (int32 Index = 0; Index < NumResources; ++Index)
{
const auto Parameter = Layout.Resources[Index];
UniformBuffer->GetResourceTable()[Index] = GetShaderParameterResourceRHI(Contents, Parameter.MemberOffset, Parameter.MemberType);
}
}
else
{
// 。。。
}
}
拷贝数据
void UpdateUniformBufferContents(FD3D11Device* Direct3DDevice, FD3D11DeviceContext* Context, FD3D11UniformBuffer* UniformBuffer, const void* Contents, uint32 ConstantBufferSize)
{
// Update the contents of the uniform buffer.
if (ConstantBufferSize > 0)
{
// Constant buffers must also be 16-byte aligned.
check(Align(Contents, 16) == Contents);
D3D11_MAPPED_SUBRESOURCE MappedSubresource;
// Discard previous results since we always do a full update
VERIFYD3D11RESULT_EX(Context->Map(UniformBuffer->Resource.GetReference(), 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedSubresource), Direct3DDevice);
check(MappedSubresource.RowPitch >= ConstantBufferSize);
FMemory::Memcpy(MappedSubresource.pData, Contents, ConstantBufferSize);
Context->Unmap(UniformBuffer->Resource.GetReference(), 0);
}
}
void FD3D11DynamicRHI::RHIUpdateUniformBuffer(FRHICommandListBase& RHICmdList, FRHIUniformBuffer* UniformBufferRHI, const void* Contents)
{
// 。。。
if (RHICmdList.Bypass())
{
// 。。。
}
else
{
FRHIResource** CmdListResources = nullptr;
void* CmdListConstantBufferData = nullptr;
if (NumResources > 0)
{
CmdListResources = (FRHIResource**)RHICmdList.Alloc(sizeof(FRHIResource*) * NumResources, alignof(FRHIResource*));
for (int32 Index = 0; Index < NumResources; ++Index)
{
const auto Parameter = Layout.Resources[Index];
CmdListResources[Index] = GetShaderParameterResourceRHI(Contents, Parameter.MemberOffset, Parameter.MemberType);
}
}
if (ConstantBufferSize > 0)
{
CmdListConstantBufferData = (void*)RHICmdList.Alloc(ConstantBufferSize, 16);
FMemory::Memcpy(CmdListConstantBufferData, Contents, ConstantBufferSize);
}
RHICmdList.EnqueueLambda([Direct3DDeviceIMContext = Direct3DDeviceIMContext.GetReference(),
Direct3DDevice = Direct3DDevice.GetReference(),
UniformBuffer,
CmdListResources,
NumResources,
CmdListConstantBufferData,
ConstantBufferSize](FRHICommandListBase&)
{
UpdateUniformBufferContents(Direct3DDevice, Direct3DDeviceIMContext, UniformBuffer, CmdListConstantBufferData, ConstantBufferSize);
// Update resource table.
for (int32 ResourceIndex = 0; ResourceIndex < NumResources; ++ResourceIndex)
{
UniformBuffer->GetResourceTable()[ResourceIndex] = CmdListResources[ResourceIndex];
}
});
RHICmdList.RHIThreadFence(true);
}
}
总结 UWorld::SendAllEndOfFrameUpdates
/**
* Send all render updates to the rendering thread.
*/
void UWorld::SendAllEndOfFrameUpdates()
要把所有要渲染的东西加到渲染队列里面了
首先必须是最后一帧
if (!HasEndOfFrameUpdates())
{
return;
}
遍历各种 UActorComponent 做结束帧相关逻辑
// Wait for tasks that are generating data for the render proxies, but are not awaited in any TickFunctions
// E.g., see cloth USkeletalMeshComponent::UpdateClothStateAndSimulate
for (UActorComponent* Component : ComponentsThatNeedPreEndOfFrameSync)
{
if (Component)
{
check(!IsValid(Component) || Component->GetMarkedForPreEndOfFrameSync());
if (IsValid(Component))
{
Component->OnPreEndOfFrameSync();
}
FMarkComponentEndOfFrameUpdateState::ClearMarkedForPreEndOfFrameSync(Component);
}
}
ComponentsThatNeedPreEndOfFrameSync.Reset();
//If we call SendAllEndOfFrameUpdates during a tick, we must ensure that all marked objects have completed any async work etc before doing the updates.
if (bInTick)
{
SCOPE_CYCLE_COUNTER(STAT_OnEndOfFrameUpdateDuringTick);
//If this proves too slow we can possibly have the mark set a complimentary bit array that marks which components need this call? Or just another component array?
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate)
{
if (Component)
{
Component->OnEndOfFrameUpdateDuringTick();
}
}
for (UActorComponent* Component : ComponentsThatNeedEndOfFrameUpdate_OnGameThread)
{
if (Component)
{
Component->OnEndOfFrameUpdateDuringTick();
}
}
}
然后
BeginSendEndOfFrameUpdatesDrawEvent(SendAllEndOfFrameUpdates);
EndSendEndOfFrameUpdatesDrawEvent(SendAllEndOfFrameUpdates);
包起来一堆代码
static TArray<UActorComponent*> LocalComponentsThatNeedEndOfFrameUpdate; // 本地组件是否需要结束帧的更新
做一些延迟更新相关的,比如transform,其他数据,就是把数据拷贝到渲染线程的代理对象
还有处理材质参数
BeginRenderingViewFamilies
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
TRACE_CPUPROFILER_EVENT_SCOPE(BeginRenderingViewFamily);
check(Canvas);
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
check(ViewFamily);
check(ViewFamily->Scene == ViewFamilies[0]->Scene);
}
UWorld* World = nullptr;
FScene* const Scene = ViewFamilies[0]->Scene ? ViewFamilies[0]->Scene->GetRenderScene() : nullptr;
if (Scene)
{
World = Scene->GetWorld();
if (World)
{
// Guarantee that all render proxies are up to date before kicking off a BeginRenderViewFamily.
World->SendAllEndOfFrameUpdates();
GetNaniteVisualizationData().Pick(World); // 给nanite手机一些数据用
}
}
// 统计的
ENQUEUE_RENDER_COMMAND(SetRtWaitCriticalPath)(
[](FRHICommandList& RHICmdList)
{
// Rendering is up and running now, so waits are considered part of the RT critical path
FThreadIdleStats::BeginCriticalPath();
});
// 更新统一表达式的缓存
FUniformExpressionCacheAsyncUpdateScope AsyncUpdateScope;
// Vedio random access memery VRAM的全称
// 更新一些参数,用于优化
ENQUEUE_RENDER_COMMAND(UpdateFastVRamConfig)(
[](FRHICommandList& RHICmdList)
{
GFastVRamConfig.Update();
});
// Flush the canvas first.
// 刷新游戏线程,确保绘制已经完成了
Canvas->Flush_GameThread();
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
// 。。。
// FrameNumber 更新
if (Scene)
{
// We allow caching of per-frame, per-scene data
if (ViewFamilies[0]->bIsFirstViewInMultipleViewFamily)
{
Scene->IncrementFrameNumber();
}
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
ViewFamily->FrameNumber = Scene->GetFrameNumber();
}
}
else
{
// this is passes to the render thread, better access that than GFrameNumberRenderThread
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
ViewFamily->FrameNumber = GFrameNumber;
}
}
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
// 。。。
// 对所有 family的 ViewExtensions 扩展做 BeginRenderViewFamily
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
// If optional parameter OverrideFrameCounter is set, use its value instead of GFrameCounter.
ViewFamily->FrameCounter = ViewFamily->OverrideFrameCounter.IsSet() ? ViewFamily->OverrideFrameCounter.GetValue() : GFrameCounter;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
{
extern TSharedRef<ISceneViewExtension, ESPMode::ThreadSafe> GetRendererViewExtension();
ViewFamily->ViewExtensions.Add(GetRendererViewExtension());
}
#endif // !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Force the upscalers to be set no earlier than ISceneViewExtension::BeginRenderViewFamily();
check(ViewFamily->GetTemporalUpscalerInterface() == nullptr);
check(ViewFamily->GetPrimarySpatialUpscalerInterface() == nullptr);
check(ViewFamily->GetSecondarySpatialUpscalerInterface() == nullptr);
for (int ViewExt = 0; ViewExt < ViewFamily->ViewExtensions.Num(); ViewExt++)
{
ViewFamily->ViewExtensions[ViewExt]->BeginRenderViewFamily(*ViewFamily);
}
checkf(!(ViewFamily->GetTemporalUpscalerInterface() != nullptr && ViewFamily->GetPrimarySpatialUpscalerInterface() != nullptr),
TEXT("Conflict setting up a third party primary spatial upscaler or temporal upscaler."));
}
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
// 。。。
if (Scene)
{
// Set the world's "needs full lighting rebuild" flag if the scene has any uncached static lighting interactions.
if(World)
{
// Note: reading NumUncachedStaticLightingInteractions on the game thread here which is written to by the rendering thread
// This is reliable because the RT uses interlocked mechanisms to update it
World->SetMapNeedsLightingFullyRebuilt(Scene->NumUncachedStaticLightingInteractions, Scene->NumUnbuiltReflectionCaptures); // 需要完全构建光照的标志
}
// Construct the scene renderers. This copies the view family attributes into its own structures.
TArray<FSceneRenderer*> SceneRenderers;
TArray<const FSceneViewFamily*> ViewFamiliesConst;
for (FSceneViewFamily* ViewFamily : ViewFamilies)
{
ViewFamiliesConst.Add(ViewFamily);
}
// 创建场景渲染器 SceneRenderers
FSceneRenderer::CreateSceneRenderers(ViewFamiliesConst, Canvas->GetHitProxyConsumer(), SceneRenderers);
}
void FSceneRenderer::CreateSceneRenderers(TArrayView<const FSceneViewFamily*> InViewFamilies, FHitProxyConsumer* HitProxyConsumer, TArray<FSceneRenderer*>& OutSceneRenderers)
{
// 校验和准备参数
OutSceneRenderers.Empty(InViewFamilies.Num());
if (!InViewFamilies.Num())
{
return;
}
const FSceneInterface* Scene = InViewFamilies[0]->Scene;
check(Scene);
EShadingPath ShadingPath = Scene->GetShadingPath();
// 对 FSceneViewFamily 创建 SceneRenderer
for (int32 FamilyIndex = 0; FamilyIndex < InViewFamilies.Num(); FamilyIndex++)
{
const FSceneViewFamily* InViewFamily = InViewFamilies[FamilyIndex];
check(InViewFamily);
check(InViewFamily->Scene == Scene);
if (ShadingPath == EShadingPath::Deferred)
{
// FDeferredShadingSceneRenderer 混个眼熟
FDeferredShadingSceneRenderer* SceneRenderer = new FDeferredShadingSceneRenderer(InViewFamily, HitProxyConsumer);
OutSceneRenderers.Add(SceneRenderer);
}
else
{
check(ShadingPath == EShadingPath::Mobile);
OutSceneRenderers.Add(new FMobileSceneRenderer(InViewFamily, HitProxyConsumer));
}
OutSceneRenderers.Last()->bIsFirstSceneRenderer = (FamilyIndex == 0);
OutSceneRenderers.Last()->bIsLastSceneRenderer = (FamilyIndex == InViewFamilies.Num() - 1);
}
#if RHI_RAYTRACING
// Update ray tracing flags. We need to do this on the render thread, since "AnyRayTracingPassEnabled" accesses CVars which
// are only visible on the render thread.
if (ShadingPath == EShadingPath::Deferred)
{
// 射线最终相关内容
ENQUEUE_RENDER_COMMAND(InitializeMultiRendererRayTracingFlags)(
[SceneRenderers = CopyTemp(OutSceneRenderers)](FRHICommandList& RHICmdList)
{
// For multi-view-family scene rendering, we need to determine which scene renderer will update the ray tracing
// scene. This will be the first view family that uses ray tracing, and subsequent families that use ray
// tracing can skip that step. If the optimization to update the ray tracing scene once is disabled, we'll
// update it for all scene renders.
bool bShouldUpdateRayTracingScene = true;
const bool bRayTracingSceneUpdateOnce = CVarRayTracingSceneUpdateOnce.GetValueOnRenderThread() != 0;
// 遍历 SceneRenderers
for (int32 RendererIndex = 0; RendererIndex < SceneRenderers.Num(); RendererIndex++)
{
FDeferredShadingSceneRenderer* SceneRenderer = (FDeferredShadingSceneRenderer*)SceneRenderers[RendererIndex];
SceneRenderer->InitializeRayTracingFlags_RenderThread(); // 对于每一个跑初始化
if (SceneRenderer->bAnyRayTracingPassEnabled)
{
SceneRenderer->bShouldUpdateRayTracingScene = bShouldUpdateRayTracingScene;
if (bRayTracingSceneUpdateOnce)
{
bShouldUpdateRayTracingScene = false;
}
}
}
// Clear flag that tracks whether ray tracing was used this frame
SceneRenderers[0]->Scene->RayTracingScene.bUsedThisFrame = false;
});
}
#endif // RHI_RAYTRACING
}
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
// 。。。
if (Scene)
{
// 。。。
// 创建场景渲染器 SceneRenderers
FSceneRenderer::CreateSceneRenderers(ViewFamiliesConst, Canvas->GetHitProxyConsumer(), SceneRenderers);
// 是否启用了命中代理
bool bShowHitProxies = false;
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
if (SceneRenderer->ViewFamily.EngineShowFlags.HitProxies)
{
bShowHitProxies = true;
break;
}
}
if (!bShowHitProxies)
{
USceneCaptureComponent::UpdateDeferredCaptures(Scene); // 延迟更新和当前场景相关的一些 场景捕捉组件
// 平面反射相关的
for (int32 ReflectionIndex = 0; ReflectionIndex < Scene->PlanarReflections_GameThread.Num(); ReflectionIndex++)
{
UPlanarReflectionComponent* ReflectionComponent = Scene->PlanarReflections_GameThread[ReflectionIndex];
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
Scene->UpdatePlanarReflectionContents(ReflectionComponent, *SceneRenderer);
}
}
}
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
SceneRenderer->ViewFamily.DisplayInternalsData.Setup(World); // 显示内部数据,根据一些命令行变量,决定如何显示一些内容
}
FSceneRenderer::PreallocateCrossGPUFences(SceneRenderers); // 预分配画GPU的围栏
const uint64 DrawSceneEnqueue = FPlatformTime::Cycles64();
ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)(
[LocalSceneRenderers = CopyTemp(SceneRenderers), DrawSceneEnqueue](FRHICommandListImmediate& RHICmdList)
{
uint64 SceneRenderStart = FPlatformTime::Cycles64();
const float StartDelayMillisec = FPlatformTime::ToMilliseconds64(SceneRenderStart - DrawSceneEnqueue);
CSV_CUSTOM_STAT_GLOBAL(DrawSceneCommand_StartDelay, StartDelayMillisec, ECsvCustomStatOp::Set);
RenderViewFamilies_RenderThread(RHICmdList, LocalSceneRenderers); // 渲染场景
FlushPendingDeleteRHIResources_RenderThread(); // 一些同步操作
});
// Force kick the RT if we've got RT polling on.
// This saves us having to wait until the polling period before the scene draw starts executing.
if (GRenderThreadPollingOn)
{
FTaskGraphInterface::Get().WakeNamedThread(ENamedThreads::GetRenderThread()); // 强制唤醒渲染线程
}
}
}