UMG源码笔记2-渲染过程
1.UMG类视图
UMG 控件跟Unity UGUI不太一样,不是所有的控件节点,都能拥有子节点,为了区分这三种控件,整理了下他们基类:
- UWidget : 所有UMG 控件的公共基类,不提供增加子节点功能
- UPanelWidget : 提供了增加子节点功能,可以有多个子节点
- UContentWidget : 继承于 UPanelWidget ,是 UPanelWidget 的一种特例,只能有一个子节点
UMG常用控件的继承关系如下图所示:
UPanelWidget 实现了可以增加节点的功能 AddChild ,然后提供了是否可以增加多个子节点的标记为,
UPanelSlot* UPanelWidget::AddChild(UWidget* Content)
{
if ( Content == nullptr )
{
return nullptr;
}
if ( !bCanHaveMultipleChildren && GetChildrenCount() > 0 )
{
return nullptr;
}
Content->RemoveFromParent();
EObjectFlags NewObjectFlags = RF_Transactional;
if (HasAnyFlags(RF_Transient))
{
NewObjectFlags |= RF_Transient;
}
// 创建 Slot
// GetSlotClass : 获取对应节点的Slot类
UPanelSlot* PanelSlot = NewObject<UPanelSlot>(this, GetSlotClass(), NAME_None, NewObjectFlags);
// Slot->Content : 子节点
// Slot->Parent : 父节点
PanelSlot->Parent = this;
PanelSlot->Content = Content;
Content->Slot = PanelSlot;
Slots.Add(PanelSlot);
OnSlotAdded(PanelSlot);
InvalidateLayoutAndVolatility();
return PanelSlot;
}
UContentWidget 的实现是将 bCanHaveMultipleChildren 设置 false ,达到只有一个子节点的功能。
UContentWidget::UContentWidget(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bCanHaveMultipleChildren = false;
}
其中, GetSlotClass 返回对应控件的 Slot 的类型,下面的表格里给出了对应控件的 Slot 类型,即 GetSlotClass 返回的 Slot 的类型。创建后的 Slot 对象会分别指向父节点跟子节点。
控件 | Slot类 |
---|---|
UWidget/UPanelWidget/UCheckBox/URetainerBox | UPanelSlot |
USafeZone | USafeZoneSlot |
USizeBox | USizeBoxSlot |
UBorder | UBorderSlot |
UButton | UButtonSlot |
UCanvasPanel | UCanvasPanelSlot |
UHorizontalBox | UHorizontalBoxSlot |
UOverlay | UOverlaySlot |
UScrollBox | UScrollBoxSlot |
UGridPanel | UGridSlot |
1.3 Slate控件类视图
Slate 中基础类是 SWidget ,这是个抽象类,不能实例化,此外还有三个继承 SWidget 的基础类,其他控件都是这个三个类的子类型。
- SPanel : 有多个子节点,本身是个抽象类,子类需要定义子节点组织方式
- SLeafWidget : 没有子节点,抽象类,子节点需要重写 Paint 方法
- SCompoundWidget : 可以有一个子节点
class SCompoundWidget : public SWidget
{
FSimpleSlot ChildSlot;
}
class SBoxPanel : public SPanel
{
TPanelChildren<FSlot> Children;
}
template<typename SlotType>
class TPanelChildren : public FChildren, private TIndirectArray< SlotType >
{
}
TPanelChildren
: 实际上是一个 Array<SWidget*>
FSimpleSlot : 持有一个 SWidget*
2 UMG渲染流程
渲染一个SImage的调用栈如下:
2.1 FSlateApplication 渲染
首先介绍下几个关键的类
- UGameEngine : 全局对象 GEngine 类
- FSlateApplication : 单例,游戏窗口类负责渲染 Slate
// 全局单例
class UGameEngine
{
// 游戏窗口句柄
TWeakPtr<class SWindow> GameViewportWindow;
// 游戏视口
UGameViewportClient* GameViewport;
// 游戏GameInstance对象 :WorkingCellGameInstance
UGameInstance* GameInstance;
}
// 全局单例
class FSlateApplication
{
public:
static void Create();
private:
FSlateApplication();
// 保存所有窗口
TArray< TSharedRef<SWindow> > SlateWindows;
}
// 游戏加载前闪屏
class FPreLoadScreenManager
{
TWeakPtr<class SWindow> MainWindow;
void Initialize((FSlateRenderer& InSlateRenderer);
void PassPreLoadScreenWindowBackToGame();
}
FSlateApplication 是一个单例类,会在 FEngineLoop::PreInit 调用时创建:
/// 1
int32 FEngineLoop::PreInit(const TCHAR* CmdLine)
{
const int32 rv1 = PreInitPreStartupScreen(CmdLine);
}
/// 2
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)
{
// ...
FSlateApplication::Create();
}
创建完 FSlateApplication 后,接下来会在 FEngineLoop::PreInitPreStartUpScreen 函数中调用 UGameEngine::CreateGameWindow() 创建游戏窗口
/// 1
TSharedRef<SWindow> UGameEngine::CreateGameWindow()
{
TSharedRef<SWindow> Window = SNew(SWindow);
FSlateApplication::Get().AddWindow( Window, bShowImmediately );
}
/// 2. 如果有**PreLoadScreenManager**,则会在其初始化函数 **Initialize**
/// 的时候创建,并赋值给**MainWindow**
void FPreLoadScreenManager::Initialize(FSlateRenderer& InSlateRenderer)
{
TSharedRef<SWindow> GameWindow = (GameEngine && GameEngine->GameViewportWindow.IsValid()) ?
GameEngine->GameViewportWindow.Pin().ToSharedRef() : UGameEngine::CreateGameWindow();
MainWindow = GameWindow;
}
/// 3. 然后在**PassPreLoadScreenWindowBackToGame**将窗口
/// 赋值给**GameEngine->GameViewportWindow**
void FPreLoadScreenManager::PassPreLoadScreenWindowBackToGame() const
{
GameEngine->GameViewportWindow = MainWindow;
}
创建的 SWindow会加到 FSlateApplication 的 SlateWindown 队列:
TSharedRef<SWindow> FSlateApplication::AddWindow( TSharedRef<SWindow> InSlateWindow,
const bool bShowImmediately )
{
FSlateWindowHelper::ArrangeWindowToFront(SlateWindows, InSlateWindow);
return InSlateWindow;
}
渲染时,会遍历 SlateWindows 列表,依次渲染每个 Window
/// 1
void FSlateApplication::PrivateDrawWindows( TSharedPtr<SWindow> DrawOnlyThisWindow )
{
for( TArray< TSharedRef<SWindow> >::TConstIterator CurrentWindowIt( SlateWindows );
CurrentWindowIt; ++CurrentWindowIt )
{
TSharedRef<SWindow> CurrentWindow = *CurrentWindowIt;
// Only draw visible windows or in off-screen rendering mode
if (bRenderOffScreen || CurrentWindow->IsVisible() )
{
DrawWindowAndChildren( CurrentWindow, DrawWindowArgs );
}
}
}
/// 2
void FSlateApplication::DrawWindowAndChildren( const TSharedRef<SWindow>& WindowToDraw,
FDrawWindowArgs& DrawWindowArgs )
{
MaxLayerId = WindowToDraw->PaintWindow(
GetCurrentTime(),
GetDeltaTime(),
WindowElementList,
FWidgetStyle(),
WindowToDraw->IsEnabled());
}
UGameEngine 初始化时,还会创建 UGameViewportClient
void UGameEngine::Init(IEngineLoop* InEngineLoop)
{
if(GIsClient)
{
ViewportClient = NewObject<UGameViewportClient>(this, GameViewportClientClass);
ViewportClient->Init(*GameInstance->GetWorldContext(), GameInstance);
GameViewport = ViewportClient;
GameInstance->GetWorldContext()->GameViewport = ViewportClient;
}
}
2.2 SWindow 渲染
SWindow 类的组成
class SLATECORE_API SWindow : public SCompoundWidget, public FSlateInvalidationRoot
{
/// Slate 事件检测加速类
TUniquePtr<FHittestGrid> HittestGrid;
SVerticalBox::FSlot* ContentSlot;
TWeakPtr<SWindow> ParentWindowPtr;
TArray< TSharedRef<SWindow> > ChildWindows;
}
SWindow在初始化时,会在 ChildSlot里增加几个 SOverlay
void SWindow::ConstructWindowInternals()
{
this->ChildSlot
[
SAssignNew(WindowOverlay, SOverlay)
.Visibility(EVisibility::SelfHitTestInvisible)
// window background
+ SOverlay::Slot()
[
WindowBackgroundImage.ToSharedRef()
]
// window border
+ SOverlay::Slot()
[
WindowBorder.ToSharedRef()
]
// window outline
+ SOverlay::Slot()
[
WindowOutline.ToSharedRef()
]
];
}
渲染时,从 SlateApplication 对象调用 SWindow 的 PaintWindow 方法
/// 1
int32 SWindow::PaintWindow( double CurrentTime, float DeltaTime,
FSlateWindowElementList& OutDrawElements, const FWidgetStyle& InWidgetStyle,
bool bParentEnabled )
{
FSlateInvalidationResult Result = PaintInvalidationRoot(Context);
}
/// 2
FSlateInvalidationResult FSlateInvalidationRoot::PaintInvalidationRoot(
const FSlateInvalidationContext& Context)
{
CachedMaxLayerId = PaintSlowPath(Context);
}
/// 3
int32 SWindow::PaintSlowPath(const FSlateInvalidationContext& Context)
{
HittestGrid->Clear();
const FSlateRect WindowCullingBounds = GetClippingRectangleInWindow();
const int32 LayerId = 0;
const FGeometry WindowGeometry = GetWindowGeometryInWindow();
int32 MaxLayerId = 0;
MaxLayerId = Paint(*Context.PaintArgs, WindowGeometry, WindowCullingBounds,
*Context.WindowElementList, LayerId, Context.WidgetStyle,
Context.bParentEnabled);
return MaxLayerId;
}
/// 4. 最终调用到基类 SWidget::Paint 函数
int32 SWidget::Paint(const FPaintArgs& Args, const FGeometry& AllottedGeometry,
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
int32 NewLayerId = OnPaint(UpdatedArgs, AllottedGeometry, CullingBounds,
OutDrawElements, LayerId, ContentWidgetStyle, bParentEnabled);
}
/// 5
int32 SWindow::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry,
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
int32 MaxLayer = SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect,
OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
return MaxLayer;
}
不同的节点的OnPaint函数实现不一样
SCompoundWidget只有一个子节点,直接调用子节点的Paint函数
int32 SCompoundWidget::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry,
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId,
const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
if( ArrangedChildren.Num() > 0 )
{
check( ArrangedChildren.Num() == 1 );
FArrangedWidget& TheChild = ArrangedChildren[0];
int32 Layer = 0;
{
Layer = TheChild.Widget->Paint( Args.WithNewParent(this),
TheChild.Geometry, MyCullingRect, OutDrawElements, LayerId + 1,
CompoundedWidgetStyle, ShouldBeEnabled( bParentEnabled ) );
}
return Layer;
}
}
SPanel 有多个子节点,渲染接口如下:
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
{
const FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];
if (!IsChildWidgetCulled(MyCullingRect, CurWidget))
{
const int32 CurWidgetsMaxLayerId = CurWidget.Widget->Paint(NewArgs,
CurWidget.Geometry, MyCullingRect, OutDrawElements, LayerId,
InWidgetStyle, bShouldBeEnabled);
MaxLayerId = FMath::Max(MaxLayerId, CurWidgetsMaxLayerId);
}
}
最终渲染到可渲染的子节点上,例如SImage
/// 1
int32 SImage::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry,
const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements,
int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
const FSlateBrush* ImageBrush = Image.GetImage().Get();
if ((ImageBrush != nullptr) && (ImageBrush->DrawAs != ESlateBrushDrawType::NoDrawType))
{
const bool bIsEnabled = ShouldBeEnabled(bParentEnabled);
const ESlateDrawEffect DrawEffects = bIsEnabled ? ESlateDrawEffect::None :
ESlateDrawEffect::DisabledEffect;
const FLinearColor FinalColorAndOpacity(
InWidgetStyle.GetColorAndOpacityTint() *
ColorAndOpacity.Get().GetColor(InWidgetStyle) *
ImageBrush->GetTint( InWidgetStyle ) );
if (bFlipForRightToLeftFlowDirection && GSlateFlowDirection == EFlowDirection::RightToLeft)
{
const FGeometry FlippedGeometry = AllottedGeometry.MakeChild
(FSlateRenderTransform(FScale2D(-1, 1)));
FSlateDrawElement::MakeBox(OutDrawElements, LayerId,
FlippedGeometry.ToPaintGeometry(), ImageBrush, DrawEffects,
FinalColorAndOpacity);
}
else
{
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(),
ImageBrush, DrawEffects, FinalColorAndOpacity);
}
}
return LayerId;
}
/// 2
/// 最后调用 **FSlateDrawElement** 来生成对应的渲染元素
/// FSlateDrawElement::MakeBox -> FSlateDrawElement::MakeBoxInternal
FSlateDrawElement& FSlateDrawElement::MakeBoxInternal(
FSlateWindowElementList& ElementList,
uint32 InLayer,
const FPaintGeometry& PaintGeometry,
const FSlateBrush* InBrush,
ESlateDrawEffect InDrawEffects,
const FLinearColor& InTint
)
{
/// 记住这个 ElementType,后面还有用到
EElementType ElementType = (InBrush->DrawAs == ESlateBrushDrawType::Border) ?
EElementType::ET_Border : EElementType::ET_Box;
FSlateDrawElement& Element = ElementList.AddUninitialized();
FSlateBoxPayload& BoxPayload = ElementList.CreatePayload<FSlateBoxPayload>(Element);
BoxPayload.SetTint(InTint);
BoxPayload.SetBrush(InBrush);
Element.Init(ElementList, ElementType, InLayer, PaintGeometry, InDrawEffects);
return Element;
}
在SlateApplication中的Renderer中有专门的渲染buffDrawBuffer,调用完OnPaint后都会将自己的渲染数据Element添加到 ElementListd队列中,给后面合批准备数据。
2.3 真正的Render
下面进入真正的渲染流程了
/// 1
void FSlateApplication::PrivateDrawWindows( TSharedPtr<SWindow> DrawOnlyThisWindow )
{
/// 这是上面说的,依次 Paint 每个 SWindow
for( TArray< TSharedRef<SWindow> >::TConstIterator CurrentWindowIt( SlateWindows );
CurrentWindowIt; ++CurrentWindowIt )
{
TSharedRef<SWindow> CurrentWindow = *CurrentWindowIt;
// Only draw visible windows or in off-screen rendering mode
if (bRenderOffScreen || CurrentWindow->IsVisible() )
{
DrawWindowAndChildren( CurrentWindow, DrawWindowArgs );
}
}
/// 开始准备合批,并且生成 render 指令
/// OutDrawBuffer : 之前所有Slate控件的Element数据都在这里
Renderer->DrawWindows( DrawWindowArgs.OutDrawBuffer );
}
/// 2
void FSlateRHIRenderer::DrawWindows(FSlateDrawBuffer& WindowDrawBuffer)
{
DrawWindows_Private(WindowDrawBuffer);
}
/// 3
void FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer& WindowDrawBuffer)
{
/// SlateBlush资源合并图集
if (DoesThreadOwnSlateRendering())
{
ResourceManager->UpdateTextureAtlases();
}
/// 按照 Window 处理 Element 数据
for (int32 ListIndex = 0; ListIndex < WindowElementLists.Num(); ++ListIndex)
{
FSlateWindowElementList& ElementList = *WindowElementLists[ListIndex];
ElementBatcher->AddElements(ElementList);
}
}
/// 4. ElementBatcher 定义
class FSlateRHIRenderer : public FSlateRenderer
{
TUniquePtr<FSlateElementBatcher> ElementBatcher;
}
/// 5
void FSlateElementBatcher::AddElements(FSlateWindowElementList& WindowElementList)
{
AddElementsInternal(WindowElementList.GetUncachedDrawElements(), ViewportSize);
}
/// 6. 之前 SImage 用到的 ElementType ET_Box/ET_Border
enum class EElementType : uint8
{
ET_Box,
ET_DebugQuad,
ET_Text,
ET_ShapedText,
ET_Spline,
ET_Line,
ET_Gradient,
ET_Viewport,
ET_Border,
ET_Custom,
ET_CustomVerts,
ET_PostProcessPass,
/** Total number of draw commands */
ET_Count,
};
void FSlateElementBatcher::AddElementsInternal(const FSlateDrawElementArray& DrawElements,
const FVector2D& ViewportSize)
{
for (const FSlateDrawElement& DrawElement : DrawElements)
{
// Determine what type of element to add
switch ( DrawElement.GetElementType() )
{
// 之前 SImage 用到的 ElementType ET_Box/ET_Border
case EElementType::ET_Box:
{
SCOPED_NAMED_EVENT_TEXT("Slate::AddBoxElement", FColor::Magenta);
STAT(ElementStat_Boxes++);
DrawElement.IsPixelSnapped() ? AddBoxElement<ESlateVertexRounding::Enabled>(DrawElement) :
AddBoxElement<ESlateVertexRounding::Disabled>(DrawElement);
}
break;
case EElementType::ET_Border:
case EElementType::ET_Text:
///...
}
}
}
/// 7
template<ESlateVertexRounding Rounding>
void FSlateElementBatcher::AddBoxElement(const FSlateDrawElement& DrawElement)
{
FSlateRenderBatch& RenderBatch = CreateRenderBatch( Layer, FShaderParams(), Resource,
ESlateDrawPrimitive::TriangleList, ESlateShader::Default, InDrawEffects, DrawFlags,
DrawElement);
// Create 9 quads for the box element based on the following diagram
// ___LeftMargin ___RightMargin
// / /
// +--+-------------+--+
// | |c1 |c2| ___TopMargin
// +--o-------------o--+
// | | | |
// | |c3 |c4|
// +--o-------------o--+
// | | | | ___BottomMargin
// +--+-------------+--+
/// 一共16个顶点数据
RenderBatch.AddVertex( FSlateVertex::Make<Rounding>( RenderTransform, FVector2D( Position.X, Position.Y ),
LocalSize, DrawScale, FVector4(StartUV, Tiling), Tint ) ); //0
/// ...
RenderBatch.AddVertex( FSlateVertex::Make<Rounding>( RenderTransform, FVector2D( EndPos.X, EndPos.Y ),
LocalSize, DrawScale, FVector4(EndUV, Tiling), Tint ) ); //15
// Top
RenderBatch.AddIndex( IndexStart + 0 );
/// ...
RenderBatch.AddIndex( IndexStart + 15 );
}
/// 8. 所有的Element都会存储Layer Shader参数等信息,添加成待合批数据。
FSlateRenderBatch& FSlateElementBatcher::CreateRenderBatch(
int32 Layer,
const FShaderParams& ShaderParams,
const FSlateShaderResource* InResource,
ESlateDrawPrimitive PrimitiveType,
ESlateShader ShaderType,
ESlateDrawEffect DrawEffects,
ESlateBatchDrawFlag DrawFlags,
const FSlateDrawElement& DrawElement)
{
FSlateRenderBatch& NewBatch = CurrentCachedElementList
? CurrentCachedElementList->AddRenderBatch(Layer, ShaderParams, InResource,
PrimitiveType, ShaderType, DrawEffects, DrawFlags, DrawElement.GetSceneIndex())
: BatchData->AddRenderBatch(Layer, ShaderParams, InResource, PrimitiveType,
ShaderType, DrawEffects, DrawFlags, DrawElement.GetSceneIndex());
NewBatch.ClippingState = ResolveClippingState(DrawElement);
return NewBatch;
}
/// 9. 最后新建合批任务,在新建的线程里进行合批
void FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer& WindowDrawBuffer)
{
if (GIsClient && !IsRunningCommandlet() && !GUsingNullRHI)
{
ENQUEUE_RENDER_COMMAND(SlateDrawWindowsCommand)(
[Params, ViewInfo](FRHICommandListImmediate& RHICmdList)
{
/// 10. 切换到渲染线程
Params.Renderer->DrawWindow_RenderThread(RHICmdList, *ViewInfo,
*Params.WindowElementList, Params);
}
);
}
}
/// 10. 切换到渲染线程
/** Draws windows from a FSlateDrawBuffer on the render thread */
void FSlateRHIRenderer::DrawWindow_RenderThread(FRHICommandListImmediate& RHICmdList,
FViewportInfo& ViewportInfo, FSlateWindowElementList& WindowElementList,
const struct FSlateDrawWindowCommandParams& DrawCommandParams)
{
RenderingPolicy->BuildRenderingBuffers(RHICmdList, BatchData);
}
/// 11. 创建渲染Buffers
void FSlateRHIRenderingPolicy::BuildRenderingBuffers(FRHICommandListImmediate& RHICmdList,
FSlateBatchData& InBatchData)
{
/// 12. Slate 数据合批
InBatchData.MergeRenderBatches();
}
/// 12. Slate 数据合批
void FSlateBatchData::MergeRenderBatches()
{
for (int32 TestIndex = BatchIndex + 1; TestIndex < BatchIndices.Num(); ++TestIndex)
{
const TPair<int32, int32>& NextBatchIndexPair = BatchIndices[TestIndex];
FSlateRenderBatch& TestBatch = RenderBatches[NextBatchIndexPair.Key];
if (TestBatch.GetLayer() != CurBatch.GetLayer())
{
// none of the batches will be compatible since we encountered an incompatible layer
break;
}
/// 13. 合批规则
else if (!TestBatch.bIsMerged && CurBatch.IsBatchableWith(TestBatch))
{
CombineBatches(CurBatch, TestBatch, FinalVertexData, FinalIndexData);
}
}
}
/// 13. 合批规则
bool IsBatchableWith(const FSlateRenderBatch& Other) const
{
return
ShaderResource == Other.ShaderResource
&& DrawFlags == Other.DrawFlags
&& ShaderType == Other.ShaderType
&& DrawPrimitiveType == Other.DrawPrimitiveType
&& DrawEffects == Other.DrawEffects
&& ShaderParams == Other.ShaderParams
&& InstanceData == Other.InstanceData
&& InstanceCount == Other.InstanceCount
&& InstanceOffset == Other.InstanceOffset
&& DynamicOffset == Other.DynamicOffset
&& CustomDrawer == Other.CustomDrawer
&& SceneIndex == Other.SceneIndex
&& ClippingState == Other.ClippingState;
}
/// 14. 合批结束
void FSlateRHIRenderingPolicy::BuildRenderingBuffers(FRHICommandListImmediate& RHICmdList,
FSlateBatchData& InBatchData)
{
/// 10/11/12 Draw Element 合批
InBatchData.MergeRenderBatches();
/// 合批结束 开始发送渲染指令
const FSlateVertexArray& FinalVertexData = InBatchData.GetFinalVertexData();
const FSlateIndexArray& FinalIndexData = InBatchData.GetFinalIndexData();
const int32 NumVertices = FinalVertexData.Num();
const int32 NumIndices = FinalIndexData.Num();
if (InBatchData.GetRenderBatches().Num() > 0 && NumVertices > 0 && NumIndices > 0)
{
bool bShouldShrinkResources = false;
RHICmdList.EnqueueLambda([
VertexBuffer = MasterVertexBuffer.VertexBufferRHI.GetReference(),
IndexBuffer = MasterIndexBuffer.IndexBufferRHI.GetReference(),
&InBatchData,
bAbsoluteIndices
](FRHICommandListImmediate& InRHICmdList)
{
SCOPE_CYCLE_COUNTER(STAT_SlateUpdateBufferRTTimeLambda);
// Note: Use "Lambda" prefix to prevent clang/gcc warnings of '-Wshadow' warning
const FSlateVertexArray& LambdaFinalVertexData = InBatchData.GetFinalVertexData();
const FSlateIndexArray& LambdaFinalIndexData = InBatchData.GetFinalIndexData();
const int32 NumBatchedVertices = LambdaFinalVertexData.Num();
const int32 NumBatchedIndices = LambdaFinalIndexData.Num();
uint32 RequiredVertexBufferSize = NumBatchedVertices *
sizeof(FSlateVertex);
uint8* VertexBufferData = (uint8*)InRHICmdList.LockVertexBuffer(VertexBuffer, 0,
RequiredVertexBufferSize, RLM_WriteOnly);
uint32 RequiredIndexBufferSize = NumBatchedIndices * sizeof(SlateIndex);
uint8* IndexBufferData = (uint8*)InRHICmdList.LockIndexBuffer(IndexBuffer, 0,
RequiredIndexBufferSize, RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, LambdaFinalVertexData.GetData(), RequiredVertexBufferSize);
FMemory::Memcpy(IndexBufferData, LambdaFinalIndexData.GetData(), RequiredIndexBufferSize);
InRHICmdList.UnlockVertexBuffer(VertexBuffer);
InRHICmdList.UnlockIndexBuffer(IndexBuffer);
});
}
}
/// 15.回到渲染线程
void FSlateRHIRenderer::DrawWindow_RenderThread(...)
{
/// 12. Slate 数据合批
InBatchData.MergeRenderBatches();
///
const uint32 ViewportWidth = (ViewportRT) ? ViewportRT->GetSizeX() :
ViewportInfo.Width;
const uint32 ViewportHeight = (ViewportRT) ? ViewportRT->GetSizeY() :
ViewportInfo.Height;
FSlateBackBuffer BackBufferTarget(BackBuffer, FIntPoint(ViewportWidth,
ViewportHeight));
/// 16. DrawElements
RenderingPolicy->DrawElements
(
RHICmdList,
BackBufferTarget,
BackBuffer,
PostProcessBuffer,
ViewportInfo.bRequiresStencilTest ? ViewportInfo.DepthStencil : EmptyTarget,
BatchData.GetFirstRenderBatchIndex(),
BatchData.GetRenderBatches(),
RenderParams
);
}
/// 16. DrawElements
void FSlateRHIRenderingPolicy::DrawElements(
FRHICommandListImmediate& RHICmdList,
FSlateBackBuffer& BackBuffer,
FTexture2DRHIRef& ColorTarget,
FTexture2DRHIRef& PostProcessTexture,
FTexture2DRHIRef& DepthStencilTarget,
int32 FirstBatchIndex,
const TArray<FSlateRenderBatch>& RenderBatches,
const FSlateRenderingParams& Params)
{
while (NextRenderBatchIndex != INDEX_NONE)
{
VertexBufferPtr = &MasterVertexBuffer;
IndexBufferPtr = &MasterIndexBuffer;
// ...
// for RHIs that can't handle VertexOffset, we need to offset
// the stream source each time
RHICmdList.SetStreamSource(0, VertexBufferPtr->VertexBufferRHI,
RenderBatch.VertexOffset * sizeof(FSlateVertex));
RHICmdList.DrawIndexedPrimitive(IndexBufferPtr->IndexBufferRHI, 0, 0,
RenderBatch.NumVertices, RenderBatch.IndexOffset, PrimitiveCount,
RenderBatch.InstanceCount);
}
}
至此,就完成了Slate的渲染了。