代码改变世界

slate相关

2022-06-11 21:43  kk20161206  阅读(246)  评论(0编辑  收藏  举报

 

 这张图片真特别好。注意中间部分的几个函数的参数是windowElementList.今天尝试看最左边的paintInvalidationRoot这部分,半天没有看明白。有空继续看吧。

Layer的传递部分往后还要继续看。

 

 

合批

 

progressBar 不能合批,因为填充部分涉及clip,如下:去掉if就能合批了。
if (PushTransformedClip(OutDrawElements, AllottedGeometry, BorderPaddingRef, FVector2D(1, 0), FSlateRect(ClampedFraction, 0, 0, 1)))
                {
                    // Draw Fill
                    FSlateDrawElement::MakeBox(
                        OutDrawElements,
                        RetLayerId++,
                        AllottedGeometry.ToPaintGeometry(
                            FVector2D::ZeroVector,
                            FVector2D( AllottedGeometry.GetLocalSize().X, AllottedGeometry.GetLocalSize().Y )),
                        CurrentFillImage,
                        DrawEffects,
                        FillColorAndOpacitySRGB,
                        Depth
                        );

                    OutDrawElements.PopClip();
                }
                break;

 

  1. STextBlock,整体能合批。 slate合批机制一文中说的不对,而且并不需要开启simpleMode。
  2. SRichTextBlock,每一行每一块都会另开一层绘制。错误,两行三行都是同一个dc,第二行用不同的字体也还是能合批;
  3. SProgressBar自身逻辑会分别另开一层Layer绘制BackgroundImage和FillImage,LayerId+ 2。但return LayerId + 1
border等优化
新手引导那个的优化
雷达
 
 
 
整体结构

FSlateDrawBUffer里实现一个drawBuffer for slate,存放所有的FSlateWindowElementList,SlateDrawBuffer.h文件中:
 TArray< TSharedRef<FSlateWindowElementList> > WindowElementLists;

// List of window element lists that we store from the previous frame 
// that we restore if they're requested again.
TArray< TSharedRef<FSlateWindowElementList> > WindowElementListsPool;

通过AddWindow得到了windowElementList,如下函数:

FSlateWindowElementList& AddWindowElementList(TSharedRef<SWindow> ForWindow); //如果pool里有这个wiNdow对应的,则将该list挪到list数组,将pool里那个删掉。如果没有,则new一个放到list数组。
void RemoveUnusedWindowElements(const TArray<SWindow*>& AllWindows);//遍历所有list对应的paintWindow,如果该window无效,则从list删除。

调用的地方:

void FSlateApplication::DrawWindowAndChildren( const TSharedRef<SWindow>& WindowToDraw, FDrawWindowArgs& DrawWindowArgs )

  父:

void FSlateApplication::PrivateDrawWindows( TSharedPtr<SWindow> DrawOnlyThisWindow )

 SLateRHIRender类,slateD3DRenderer.h类,SlateOpenGLRenderer类,都包含DrawBuffer类型数据。其中,FSlateRHIRenderer里包含了数组、指针:

/** Keep a pointer around for when we have deferred drawing happening */
    FSlateDrawBuffer* EnqueuedWindowDrawBuffer;

    /** Double buffered draw buffers so that the rendering thread can be rendering windows while the game thread is setting up for next frame */
    FSlateDrawBuffer DrawBuffers[NumDrawBuffers];

 

FSlateWindowElementList类,代表一个顶层的window和他的所有draw elements。

获取其绘制的窗口:

SWindow* GetPaintWindow() const

batchData:

FSlateBatchData BatchData;

drawElement数组:

FSlateDrawElementArray UncachedDrawElements;

要渲染到的窗口,可能和绘制的窗口不一样:

SWindow* RenderTargetWindow;

裁剪管理类:

FSlateClippingManager ClippingManager;
TArray<FWidgetDrawElementState, TInlineAllocator<50>> WidgetDrawStack;
TArray<FSlateCachedElementData*, TInlineAllocator<4>> CachedElementDataList;

TArray<int32, TInlineAllocator<4>> CachedElementDataListStack; //liist和stack是对应的,push进去一个data就会放到stack里面一个index。

几个缓存相关函数:

/**
     * Pushes the current widget that is painting onto the widget stack so we know what elements belong to each widget
     * This information is used for caching later.
     *
     */
    SLATECORE_API void PushPaintingWidget(const SWidget& CurrentWidget, int32 StartingLayerId, FSlateCachedElementsHandle& CurrentCacheHandle);

    /**
     * Pops the current painted widget off the stack
     * @return true if an element was added while the widget was pushed
     */
    SLATECORE_API FSlateCachedElementsHandle PopPaintingWidget(const SWidget& CurrentWidget);
//上两个函数会把cacheHandle和对应的widget放到wdigetDrawElementState的数组里和弹出来。 /** Pushes cached element data onto the stack. Any draw elements cached after will use this cached element data until popped */ void PushCachedElementData(FSlateCachedElementData& CachedElementData); void PopCachedElementData();

 

其中的FSlateCachedElementData类型包含了

TSparseArray<FSlateRenderBatch> CachedBatches;

    TArray<TSharedPtr<FSlateCachedElementList>> CachedElementLists;

    TArray<FSlateCachedElementList*, TInlineAllocator<50>> ListsWithNewData;

    TArray<FSlateCachedClipState> CachedClipStates;


FSlateCachedElementsHandle FSlateCachedElementData::AddCache(const SWidget* Widget);//该函数传入一个widget,返回一个包含cachedElementList的handle。

//如果当前裁剪状态不为空,则list里加入当前cached裁剪状态。新添加元素的cachedClippingState设为该状态。

FSlateRenderBatch& FSlateCachedElementData::AddCachedRenderBatch(FSlateRenderBatch&& NewBatch, int32& OutIndex); //cachedBatches里面新加这个newBatch。

void FSlateCachedElementData::RemoveCachedRenderBatches(const TArray<int32>& CachedRenderBatchIndices); //从CachedBatches里面删除数组的index位置的元素。

FSlateDrawElement& FSlateCachedElementData::AddCachedElement(FSlateCachedElementsHandle& CacheHandle, const FSlateClippingManager& ParentClipManager, const SWidget* CurrentWidget); //cachedhandle的nlist的drawelements里面新建一个drawelement,isCached为true。listWithNewData里面加入这个list。如果当前裁剪状态为存在,则list加入缓存裁剪状态。newelement的裁剪状态设为该状态,返回这个新建的drawelement.

void RemoveList(FSlateCachedElementsHandle& CacheHandle);

FSLateCachedElementList中包含了它的父类elementData,和用来创建batch的源drawElement数据,创建batches之后,batch数据存在此类的fastPathRenderingData中,返回的renderbatch放到parentData的batch数组中,list里只放indices数组。这些drawElement所属的widget,快速渲染的缓存数据。后者包含clip列表,vertex数组,index数组等。

/** List of source draw elements to create batches from */
    FSlateDrawElementArray DrawElements;

    TArray<int32> CachedRenderBatchIndices;

    /** The widget whose draw elements are in this list */
    const SWidget* OwningWidget;

    FSlateCachedElementData* ParentData;

    FSlateCachedFastPathRenderingData* CachedRenderingData;

FSlateRenderBatch& AddRenderBatch(int32 InLayer, const FShaderParams& InShaderParams, const FSlateShaderResource* InResource, ESlateDrawPrimitive InPrimitiveType, ESlateShader InShaderType, ESlateDrawEffect InDrawEffects, ESlateBatchDrawFlag InDrawFlags, int8 SceneIndex);

SLATECORE_API void DestroyCachedData();

struct FSlateCachedElementsHandle这个结构通过上面的FSLateCachedElementList构造的:

struct FSlateCachedElementsHandle
{
    friend struct FSlateCachedElementData;

    static FSlateCachedElementsHandle Invalid;
    void ClearCachedElements();
    void RemoveFromCache();

    bool IsOwnedByWidget(const SWidget* Widget) const;

    bool IsValid() const { return Ptr.IsValid(); }

    bool operator!=(FSlateCachedElementsHandle& Other) const { return Ptr != Other.Ptr; }

    FSlateCachedElementsHandle() {}
private:
    FSlateCachedElementsHandle(TSharedRef<FSlateCachedElementList>& DataPtr)
        : Ptr(DataPtr)
    {
    }

private:
    TWeakPtr<FSlateCachedElementList> Ptr;
};

 

其中的FSLateCachedFastPathRenderingData类包含了这些东西:

TArray<FSlateCachedClipState, TInlineAllocator<1>> CachedClipStates;
FSlateVertexArray Vertices;
FSlateIndexArray Indices;
FSlateRenderBatch& FSlateCachedElementList::AddRenderBatch(int32 InLayer, const FShaderParams& InShaderParams, const FSlateShaderResource* InResource, ESlateDrawPrimitive InPrimitiveType, ESlateShader InShaderType, ESlateDrawEffect InDrawEffects, ESlateBatchDrawFlag InDrawFlags, int8 SceneIndex)
{
    FSlateRenderBatch NewRenderBatch(InLayer, InShaderParams, InResource, InPrimitiveType, InShaderType, InDrawEffects, InDrawFlags, SceneIndex, &CachedRenderingData->Vertices, &CachedRenderingData->Indices, CachedRenderingData->Vertices.Num(), CachedRenderingData->Indices.Num());
    int32 RenderBatchIndex = INDEX_NONE;
    FSlateRenderBatch& AddedBatchRef = ParentData->AddCachedRenderBatch(MoveTemp(NewRenderBatch), RenderBatchIndex);
    
    check(RenderBatchIndex != INDEX_NONE);

    CachedRenderBatchIndices.Add(RenderBatchIndex);

    return AddedBatchRef;
    
    //return CachedBatches.Emplace_GetRef(InLayer, InShaderParams, InResource, InPrimitiveType, InShaderType, InDrawEffects, InDrawFlags, SceneIndex, &CachedRenderingData->Vertices, &CachedRenderingData->Indices, CachedRenderingData->Vertices.Num(), CachedRenderingData->Indices.Num());
}

 看看这个函数调用的地方,哪些东西存有cache数据。

 

slate调用堆栈

 

 privateDrawWindows函数里创建FDrawWindowArgs类型参数,然后作为引用类型传入DrawWindowAndChildren函数,该函数参数为SWindow引用类型参数和前面创建的FDrawWIndowArgs类型参数,后者里面包含DrawBuffer,其类内部有windowList成员,通过这个DrawWindowAndCHildren函数,将window对应的slateWindowElementList放到了DrawBuffer里面了就。这个DrawWindowANdChildren函数 还会递归调用这个window的所有children。执行完这个函数之后,DrawWIndowArgs.OutDrawBuffer里面就存放了数据。privateDrawWindows函数里面最后一句执行了Renderer->DrawWindows(DrawWindowArgs.OutDrawBuffer);

FSlateRHIRenderer::DrawWindows(FSlateDrawBuffer& WindowDrawBuffer)

{

  DrawWindows_Private(WindowDrawBuffer);

}

在这个DrawWindows_Private函数里面,对WIndowDrawBuffer里的每个slatewindowElementList,执行ElementBatcher->AddElements(ElementList);(FSLateELmentBatcher类型的ElementBatcher) 在ElementBatcher.cpp文件中,该函数对于windowElementList的uncachedDrawElements直接AddElementsInternal参数是drawElements数组和viewportSIze。该函数根据drawElement类型,Add不同的element。AddBOxElement内部,创建FSlateRenderBatch。该函数内部

FSlateElementBatcher::CreateRenderBatch,

 

 

该函数遍历cachedElementData的所有listsWIthNewData,对于每个

FSlateCachedElementList,

 

 

调用AddElementsInternal,将这些list里的DrawElements传入函数中去。

 

 

privateDrawWindows里面有个DrawPrepass设置dpiScale*ApplicationScale.

AddElement函数会创建一个FSlateRenderBatch类,里面放入vertex和index,createRenderBatch函数用来创建这个FSlateRenderBatch对象,判断currentCachedElementList是否为空,如果不为空,则currentCachedElementList的AddBatch,,否则BatchData的AddBatch,BatchData属于FSlateBatchData类,是FSlateElementBatch类型。如果是前者,FSlateCachedElementList的AddBatch,新建的RenderBatch会放到FSlateCachedElementData中;如果是后者,FSlateBatchData的renderbatches里会放入新建的renderBatch。

SlateElementBatcher作为一个中间枢纽的存在,他AddElements的时候,batchData指针指向FSlateWindowElementList elementList的batchData或

CachedElementDataList

,完成后清空,再处理下一个windowList。FSlateElementBatcher能创建FSlateRenderBatch。

SLATECORE_API void AddElements( FSlateWindowElementList& ElementList );
void AddElementsInternal(const FSlateDrawElementArray& DrawElements, const FVector2D& ViewportSize);
void AddCachedElements(FSlateCachedElementData& CachedElementData, const FVector2D& ViewportSize);
FSlateRenderBatch& CreateRenderBatch(
        int32 Layer,
        const FShaderParams& ShaderParams,
        const FSlateShaderResource* InResource,
        ESlateDrawPrimitive PrimitiveType,
        ESlateShader ShaderType,
        ESlateDrawEffect DrawEffects,
        ESlateBatchDrawFlag DrawFlags,
        const FSlateDrawElement& DrawElement);
//具体创建vertices的函数
/** 
     * Creates vertices necessary to draw a string (one quad per character)
     */
    template<ESlateVertexRounding Rounding>
    void AddTextElement( const FSlateDrawElement& DrawElement );
private:
/** Uncached Batch data currently being filled in */
    FSlateBatchData* BatchData;

    /** Cached batches currently being filled in */
    FSlateCachedElementList* CurrentCachedElementList;

FSLateBtachData属于windowLIst。还能合renderBatch。

FSlateElementBatch代表一系列batch到一起的slateDrawElement渲染需要的一些信息,shaderResource、shaderParam、DrawFlag、PrimitiveType、shaderType、DrawEffect、instanceCount、instanceOffset、FBatchKey用来决定是否两个batch能合并,BATCH里element的数量,顶点数组index,index数组索引。引用的地方没找到,哈哈哈。估计弃用了。删掉后确实没问题。

 

FSlateDrawElement类存渲染drawElement需要的数据,makeBoxInternal,makeText等,内部会调用其Init函数,里面会计算这个drawElement的

RenderTransform,
Position,
 
ElementType,
DrawEffects,
BatchFlags
Scale等。
 
 
DrawWindow_RenderThread函数
里面这个void FSlateRHIRenderingPolicy::BuildRenderingBuffers(FRHICommandListImmediate& RHICmdList, FSlateBatchData& InBatchData)设置vertexBuffer和indexBuffer。调用InBatchData.MergeRenderBatches();进行合批。
 
 然后调用
RenderingPolicy->DrawElements。

 
 
在SlateRHIRenderer.cpp文件中,DrawWindows——private函数把ElementList加入到ElementBatch里:
/**
* Creates necessary resources to render a window and sends draw commands to the rendering thread
*
* @param WindowDrawBuffer	The buffer containing elements to draw
*/
void FSlateRHIRenderer::DrawWindows_Private(FSlateDrawBuffer& WindowDrawBuffer)
{
	checkSlow(IsThreadSafeForSlateRendering());


	FSlateRHIRenderingPolicy* Policy = RenderingPolicy.Get();
	ENQUEUE_RENDER_COMMAND(SlateBeginDrawingWindowsCommand)(
		[Policy](FRHICommandListImmediate& RHICmdList)
	{
		Policy->BeginDrawingWindows();
	}
	);

	// Update texture atlases if needed and safe
	if (DoesThreadOwnSlateRendering())
	{
		ResourceManager->UpdateTextureAtlases();
	}

	const TSharedRef<FSlateFontCache> FontCache = SlateFontServices->GetFontCache();

	// Iterate through each element list and set up an RHI window for it if needed
	const TArray<TSharedRef<FSlateWindowElementList>>& WindowElementLists = WindowDrawBuffer.GetWindowElementLists();
	for (int32 ListIndex = 0; ListIndex < WindowElementLists.Num(); ++ListIndex)
	{
		FSlateWindowElementList& ElementList = *WindowElementLists[ListIndex];

		SWindow* Window = ElementList.GetRenderWindow();

		if (Window)
		{
			const FVector2D WindowSize = Window->GetViewportSize();
			if (WindowSize.X > 0 && WindowSize.Y > 0)
			{
				// Add all elements for this window to the element batcher
				ElementBatcher->AddElements(ElementList);

				// Update the font cache with new text after elements are batched
				FontCache->UpdateCache();

				bool bLockToVsync = ElementBatcher->RequiresVsync();
				bool bRequiresStencilTest = ElementList.GetBatchData().IsStencilClippingRequired();

				bool bForceVsyncFromCVar = false;
				if (GIsEditor)
				{
					static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSyncEditor"));
					bForceVsyncFromCVar = (CVar->GetInt() != 0);
				}
				else
				{
					static IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.VSync"));
					bForceVsyncFromCVar = (CVar->GetInt() != 0);
				}

				bLockToVsync |= bForceVsyncFromCVar;

				// All elements for this window have been batched and rendering data updated
				ElementBatcher->ResetBatches();

				// The viewport had better exist at this point  
				FViewportInfo* ViewInfo = WindowToViewportInfo.FindChecked(Window);

				if (Window->IsViewportSizeDrivenByWindow())
				{
					// Resize the viewport if needed
					ConditionalResizeViewport(ViewInfo, ViewInfo->DesiredWidth, ViewInfo->DesiredHeight, IsViewportFullscreen(*Window));
				}

				
				if (bRequiresStencilTest || bRequiresStencilTest != ViewInfo->bRequiresStencilTest)
				{
					ViewInfo->ConditionallyUpdateDepthBuffer(bRequiresStencilTest, ViewInfo->DesiredWidth, ViewInfo->DesiredHeight);
				}
//……
}

  TSharedRef<FSlateRenderDataHandle, ESPMode::ThreadSafe> FSlateRHIRenderer::CacheElementRenderData这个函数里也会把ElementList加入到ELementBatcher里。

  Slate3DRenderer函数也会把ElementList加入到ELementBatcher里:

void FSlate3DRenderer::DrawWindow_GameThread(FSlateDrawBuffer& DrawBuffer)
{
	check( IsInGameThread() );

	const TSharedRef<FSlateFontCache> FontCache = SlateFontServices->GetGameThreadFontCache();

	const TArray<TSharedRef<FSlateWindowElementList>>& WindowElementLists = DrawBuffer.GetWindowElementLists();

	for (int32 WindowIndex = 0; WindowIndex < WindowElementLists.Num(); WindowIndex++)
	{
		FSlateWindowElementList& ElementList = *WindowElementLists[WindowIndex];

		SWindow* Window = ElementList.GetPaintWindow();

		if (Window)
		{
			const FVector2D WindowSize = Window->GetSizeInScreen();
			if (WindowSize.X > 0 && WindowSize.Y > 0)
			{
				// Add all elements for this window to the element batcher
				ElementBatcher->AddElements(ElementList);
//……
}

  

加入的这些WindowElementList在FSlateDrawBuffer类了:

void FSlateElementBatcher::AddElements(FSlateWindowElementList& WindowElementList) { SCOPED_NAMED_EVENT_TEXT("Slate::AddElements", FColor::Magenta); SCOPE_CYCLE_COUNTER(STAT_SlateAddElements); ElmementStat_Other = 0; ElmementStat_Boxes = 0; ElmementStat_Borders = 0; ElmementStat_Text = 0; ElmementStat_ShapedText = 0; ElmementStat_Line = 0; ElmementStat_CachedBuffer = 0; BatchData = &WindowElementList.GetBatchData(); DrawLayer = &WindowElementList.GetRootDrawLayer(); FVector2D ViewportSize = WindowElementList.GetPaintWindow()->GetViewportSize(); ClippingStates = &WindowElementList.ClippingManager.GetClippingStates(); BatchData->DetermineIsStencilClippingRequired(*ClippingStates); AddElementsInternal(WindowElementList.GetRootDrawLayer().DrawElements, ViewportSize); TMap< TSharedPtr<FSlateDrawLayerHandle, ESPMode::ThreadSafe>, TSharedPtr<FSlateDrawLayer> >& DrawLayers = WindowElementList.GetChildDrawLayers(); for ( auto& Entry : DrawLayers ) { DrawLayer = Entry.Value.Get(); AddElementsInternal(DrawLayer->DrawElements, ViewportSize); } // Done with the element list BatchData = nullptr; DrawLayer = nullptr; ClippingStates = nullptr; const int32 ElmementStat_All = ElmementStat_Boxes + ElmementStat_Borders + ElmementStat_Text + ElmementStat_ShapedText + ElmementStat_Line + ElmementStat_CachedBuffer + ElmementStat_Other; INC_DWORD_STAT_BY(STAT_SlateElements, ElmementStat_All); INC_DWORD_STAT_BY(STAT_SlateElements_Box, ElmementStat_Boxes); INC_DWORD_STAT_BY(STAT_SlateElements_Border, ElmementStat_Borders); INC_DWORD_STAT_BY(STAT_SlateElements_Text, ElmementStat_Text); INC_DWORD_STAT_BY(STAT_SlateElements_ShapedText, ElmementStat_ShapedText); INC_DWORD_STAT_BY(STAT_SlateElements_Line, ElmementStat_Line); INC_DWORD_STAT_BY(STAT_SlateElements_CachedBuffer, ElmementStat_CachedBuffer); INC_DWORD_STAT_BY(STAT_SlateElements_Other, ElmementStat_Other); }

  这个windowElementLists是怎么构造的:

FSlateWindowElementList& FSlateDrawBuffer::AddWindowElementList(TSharedRef<SWindow> ForWindow)
{
	for ( int32 WindowIndex = 0; WindowIndex < WindowElementListsPool.Num(); ++WindowIndex )
	{
		TSharedRef<FSlateWindowElementList> ExistingElementList = WindowElementListsPool[WindowIndex];

		if (ExistingElementList->GetPaintWindow() == &ForWindow.Get())
		{
			WindowElementLists.Add(ExistingElementList);
			WindowElementListsPool.RemoveAtSwap(WindowIndex);

			ExistingElementList->ResetElementBuffers();

			return *ExistingElementList;
		}
	}

	TSharedRef<FSlateWindowElementList> WindowElements = MakeShared<FSlateWindowElementList>(ForWindow);
	WindowElementLists.Add(WindowElements);

	return *WindowElements;
}

  上面倒数第三行代码可以看到,是从SWindow类型的ForWIndow直接得到的。没找到相关代码。

  全局搜索这个list,发现许多ui的东西的Paint方法里都涉及到这个类型的参数,并且最后会有一个FSlateDraw Element加入到这个list里:

int32 FTextSearchHighlighter::OnPaint(const FPaintArgs& Args, const FTextLayout::FLineView& Line, const float OffsetX, const float Width, const FTextBlockStyle& DefaultStyle, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	const FVector2D Location(Line.Offset.X + OffsetX, Line.Offset.Y);

	// If we've not been set to an explicit color, calculate a suitable one from the linked color
	FLinearColor SelectionBackgroundColorAndOpacity = DefaultStyle.HighlightColor * InWidgetStyle.GetColorAndOpacityTint();
	SelectionBackgroundColorAndOpacity.A *= 0.2f;

	// The block size and offset values are pre-scaled, so we need to account for that when converting the block offsets into paint geometry
	const float InverseScale = Inverse(AllottedGeometry.Scale);

	if (Width > 0.0f)
	{
		// Draw the actual highlight rectangle
		FSlateDrawElement::MakeBox(
			OutDrawElements,
			++LayerId,
			AllottedGeometry.ToPaintGeometry(TransformVector(InverseScale, FVector2D(Width, FMath::Max(Line.Size.Y, Line.TextHeight))), FSlateLayoutTransform(TransformPoint(InverseScale, Location))),
			&DefaultStyle.HighlightShape,
			bParentEnabled && bHasKeyboardFocus ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect,
			SelectionBackgroundColorAndOpacity
			);
	}

	return LayerId;
}

  

 

另外,

ElementBatcher->AddElements(ElementList)

又调用了:

void FSlateElementBatcher::AddElementsInternal(const TArray<FSlateDrawElement>& DrawElements, const FVector2D& ViewportSize)
{
	checkSlow(DrawLayer);

	for ( int32 DrawElementIndex = 0; DrawElementIndex < DrawElements.Num(); ++DrawElementIndex )
	{
		const FSlateDrawElement& DrawElement = DrawElements[DrawElementIndex];

		// Determine what type of element to add
		switch ( DrawElement.GetElementType() )
		{
		case FSlateDrawElement::ET_Box:
			ElmementStat_Boxes++;
			DrawElement.IsPixelSnapped() ? AddBoxElement<ESlateVertexRounding::Enabled>(DrawElement) : AddBoxElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Border:
			ElmementStat_Borders++;
			DrawElement.IsPixelSnapped() ? AddBorderElement<ESlateVertexRounding::Enabled>(DrawElement) : AddBorderElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Text:
			ElmementStat_Text++;
			DrawElement.IsPixelSnapped() ? AddTextElement<ESlateVertexRounding::Enabled>(DrawElement) : AddTextElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_ShapedText:
			ElmementStat_ShapedText++;
			DrawElement.IsPixelSnapped() ? AddShapedTextElement<ESlateVertexRounding::Enabled>(DrawElement) : AddShapedTextElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Line:
			ElmementStat_Line++;
			DrawElement.IsPixelSnapped() ? AddLineElement<ESlateVertexRounding::Enabled>(DrawElement) : AddLineElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_DebugQuad:
			ElmementStat_Other++;
			DrawElement.IsPixelSnapped() ? AddQuadElement<ESlateVertexRounding::Enabled>(DrawElement) : AddQuadElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Spline:
			// Note that we ignore pixel snapping here; see implementation for more info.
			ElmementStat_Other++;
			AddSplineElement(DrawElement);
			break;
		case FSlateDrawElement::ET_Gradient:
			ElmementStat_Other++;
			DrawElement.IsPixelSnapped() ? AddGradientElement<ESlateVertexRounding::Enabled>(DrawElement) : AddGradientElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Viewport:
			ElmementStat_Other++;
			DrawElement.IsPixelSnapped() ? AddViewportElement<ESlateVertexRounding::Enabled>(DrawElement) : AddViewportElement<ESlateVertexRounding::Disabled>(DrawElement);
			break;
		case FSlateDrawElement::ET_Custom:
			ElmementStat_Other++;
			AddCustomElement(DrawElement);
			break;
		case FSlateDrawElement::ET_CustomVerts:
			ElmementStat_Other++;
			AddCustomVerts(DrawElement);
			break;
		case FSlateDrawElement::ET_Layer:
			ElmementStat_Other++;
			AddLayer(DrawElement);
			break;
		case FSlateDrawElement::ET_CachedBuffer:
			ElmementStat_CachedBuffer++;
			AddCachedBuffer(DrawElement);
			break;
		case FSlateDrawElement::ET_PostProcessPass:
			ElmementStat_Other++;
			AddPostProcessPass(DrawElement, ViewportSize);
			break;
		default:
			checkf(0, TEXT("Invalid element type"));
			break;
		}
	}
}

  AddCustomVerts里,判断如果是自定义Vert类型,会将customVert的vertdata和indexData赋给ElementBatcher:

BatchVertices = InPayload.CustomVertsData。
void FSlateElementBatcher::AddCustomVerts(const FSlateDrawElement& DrawElement)
{
	FElementBatchMap& LayerToElementBatches = DrawLayer->GetElementBatchMap();

	const FSlateDataPayload& InPayload = DrawElement.GetDataPayload();
	uint32 Layer = DrawElement.GetLayer();

	if (InPayload.CustomVertsData.Num() >0)
	{
		// See if the layer already exists.
		TUniqueObj<FElementBatchArray>* ElementBatches = LayerToElementBatches.Find(Layer);
		if (!ElementBatches)
		{
			// The layer doesn't exist so make it now
			ElementBatches = &LayerToElementBatches.Add( Layer );
		}
		check(ElementBatches);

		FSlateElementBatch NewBatch(
			InPayload.GetResourceProxy() != nullptr ? InPayload.GetResourceProxy()->Resource : nullptr,
			FShaderParams(),
			ESlateShader::Custom,
			ESlateDrawPrimitive::TriangleList,
			DrawElement.GetDrawEffects(),
			InPayload.BatchFlags,
			DrawElement.GetClippingIndex(),
			*ClippingStates,
			InPayload.NumInstances,
			InPayload.InstanceOffset,
			InPayload.InstanceData,
			DrawElement.GetSceneIndex()
		);

		int32 Index = (*ElementBatches)->Add(NewBatch);
		FSlateElementBatch* ElementBatch = &(**ElementBatches)[Index];

		BatchData->AssignVertexArrayToBatch(*ElementBatch);
		BatchData->AssignIndexArrayToBatch(*ElementBatch);

		FSlateVertexArray& BatchVertices = BatchData->GetBatchVertexList(*ElementBatch);
		FSlateIndexArray& BatchIndices = BatchData->GetBatchIndexList(*ElementBatch);

		// Vertex Buffer since  it is already in slate format it is a straight copy
		BatchVertices = InPayload.CustomVertsData;
		BatchIndices = InPayload.CustomVertsIndexData;
	}
}

  

 

整个ui这套东西学习可以从TestSuite一个ue4自带的例子进行学习:打开testSuite这个窗口通过:Window->Developer Tools -> Debug Tools -> Test Suite。对应的代码在ENgine->Source->Runtime->AppFramework下面。

打开窗口,有个TEstSuite2,里面的Element Tests就可以看到ue4自带的各种类型的element。代码是STestSuite.cpp文件。包含按钮,Box类型的,Text类型的,Gradient类型的,Spline类型,RotatedBox类型,CustomVert类型,各种类型都通过FSlateDrawElement的make相应类型的函数。

 想要做五星技能雷达图,gradient类型尝试了一下,发现不可以。改用customVert类型。将上面STestSuite.cpp文件中的相关代码进行修改:

 

	int32 TestCustomVerts(const FOnPaintHandlerParams& InParams)
	{
		const float Radius = FMath::Min(InParams.Geometry.GetLocalSize().X, InParams.Geometry.GetLocalSize().Y) * 0.5f;
		const FVector2D Center = InParams.Geometry.AbsolutePosition + InParams.Geometry.GetLocalSize() * 0.5f;

		//const FSlateBrush* MyBrush = FCoreStyle::Get().GetBrush("ColorWheel.HueValueCircle");
		const FSlateBrush* MyBrush = FTestStyle::Get().GetBrush("skillTga40px"); //skill40px skillTga40px
		// @todo this is not the correct way to do this
		FSlateShaderResourceProxy* ResourceProxy = FSlateDataPayload::ResourceManager->GetShaderResource(*MyBrush);
		FSlateResourceHandle Handle = FSlateApplication::Get().GetRenderer()->GetResourceHandle( *MyBrush );

		FVector2D UVCenter = FVector2D::ZeroVector;
		FVector2D UVRadius = FVector2D(1,1);
		if (ResourceProxy != nullptr)
		{
			UVRadius = 0.5f * ResourceProxy->SizeUV;
			UVCenter = ResourceProxy->StartUV + UVRadius;
		}

		// Make a triangle fan in the area allotted
		//const int NumTris = 12;
		const int NumTris = 5;
		TArray<FSlateVertex> Verts;
		Verts.Reserve(NumTris*3);

		// Center Vertex
		Verts.AddZeroed();
		{
			FSlateVertex& NewVert = Verts.Last();
			NewVert.Position[0] = Center.X;
			NewVert.Position[1] = Center.Y;
			NewVert.TexCoords[0] = UVCenter.X;
			NewVert.TexCoords[1] = UVCenter.Y;
			NewVert.TexCoords[2] = NewVert.TexCoords[3] = 1.0f;
			NewVert.Color = FColor::White;
		}

		for (int i = 0; i < NumTris; ++i)
		{
			Verts.AddZeroed();
			{
				const float Angle = (2*PI*i) / NumTris;
				const FVector2D EdgeDirection(FMath::Cos(Angle), FMath::Sin(Angle));
				FVector2D Edge = FVector2D(0, 0);
				switch (i) {
				case 1:
					Edge = Radius*EdgeDirection*0.8;
					break;
				case 2:
					Edge = Radius * EdgeDirection*0.6;
					break;
				case 3:
					Edge = Radius * EdgeDirection*0.9;
					break;
				case 4:
					Edge = Radius * EdgeDirection*0.8;
					break;
				case 5:
					Edge = Radius * EdgeDirection*0.7;
					break;
				default:
					Edge = Radius * EdgeDirection;
				}
				//const FVector2D Edge(Radius*EdgeDirection);
				FSlateVertex& NewVert = Verts.Last();
				NewVert.Position[0] = Center.X + Edge.X;
				NewVert.Position[1] = Center.Y + Edge.Y;
				NewVert.TexCoords[0] = UVCenter.X + UVRadius.X*EdgeDirection.X;
				NewVert.TexCoords[1] = UVCenter.Y + UVRadius.Y*EdgeDirection.Y;
				NewVert.TexCoords[2] = NewVert.TexCoords[3] = 1.0f;
				NewVert.Color = FColor::Red;
			}
		}

		TArray<SlateIndex> Indexes;
		for (int i = 1; i <= NumTris; ++i)
		{
			Indexes.Add(0);
			Indexes.Add(i);
			//Indexes.Add( (i+1 > 12) ? (1) : (i+1) );
			Indexes.Add((i + 1 > 5) ? (1) : (i + 1));
		}

		FSlateDrawElement::MakeCustomVerts(InParams.OutDrawElements, InParams.Layer, Handle, Verts, Indexes, nullptr, 0, 0);


		/*TArray<FSlateGradientStop> GradientStops;

		GradientStops.Add(FSlateGradientStop(Verts[1].Position, FColor::Magenta));
		GradientStops.Add(FSlateGradientStop(Verts[2].Position, FColor::Blue));
		GradientStops.Add(FSlateGradientStop(Verts[3].Position, FColor::Green));
		GradientStops.Add(FSlateGradientStop(Verts[4].Position, FColor::Red));
		GradientStops.Add(FSlateGradientStop(Verts[5].Position, FColor::Yellow));*/


		/*for (int i = 0; i < NumTris; ++i)
		{
			Verts.AddZeroed();
			{
				const float Angle = (2 * PI*i) / NumTris;
				const FVector2D EdgeDirection(FMath::Cos(Angle), FMath::Sin(Angle));
				const FVector2D Edge(Radius*EdgeDirection*0.5);
				FSlateVertex& NewVert = Verts.Last();
				NewVert.Position[0] = Center.X + Edge.X;
				NewVert.Position[1] = Center.Y + Edge.Y;
				NewVert.TexCoords[0] = UVCenter.X + UVRadius.X*EdgeDirection.X;
				NewVert.TexCoords[1] = UVCenter.Y + UVRadius.Y*EdgeDirection.Y;
				NewVert.TexCoords[2] = NewVert.TexCoords[3] = 1.0f;
				NewVert.Color = FColor::Green;
			}
		}*/

		/*FSlateDrawElement::MakeGradient(
			InParams.OutDrawElements,
			InParams.Layer,
			InParams.Geometry.ToPaintGeometry(),
			GradientStops,
			Orient_Vertical,
			InParams.bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect
		);*/
		return InParams.Layer;
	}
};

  改了vertex数据为5个,注意最后判断结束的地方也要改。

      另外,用了一张雷达底图改了brush,然后顶点颜色怎么都显示不出来。后来怀疑是图片的不透明导致的,又改为tga模式,然后顶点颜色渐变就能显示出来了。需要改如下代码:

在该AppFramework下的TestStyle.cpp文件中,增加了tga格式的,如下面的第三行:

#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ )
#define IMAGE_TGABRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".tga") ), __VA_ARGS__ )

  

同时,在该文件,FTestStyle文件的Create函数中,增加:

Style->Set("skill40px", new IMAGE_BRUSH("Testing/skill", Icon40x40));
	Style->Set("skillTga40px", new IMAGE_TGABRUSH("Testing/skill", Icon40x40));

  第一行是不透明的,显示不出顶点颜色,第二行可以。在该函数中,还能看到,这里的图片对应“UnrealEngine\Engine\Content\Slate\Testing”这个目录下的图片,把相应的图片放到这个文件夹下。

 

而这个控件在ui蓝图里还是不能看到,所以,怎么把这个控件加到控件蓝图?这里大概写了一下:类似Image包含SImage类和UImage类。

在G:\ClientNew\Engine\Source\Runtime\UMG\Private\Components下添加,CustomWidget.cpp文件:

#include "Components/CustomWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCustomWidget.h"

#define LOCTEXT_NAMESPACE "UMG"

UCustomWidget::UCustomWidget(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	//, ColorAndOpacity(FLinearColor::White)
{
	Visibility = ESlateVisibility::SelfHitTestInvisible;
}


void UCustomWidget::ReleaseSlateResources(bool bReleaseChildren)
{
	Super::ReleaseSlateResources(bReleaseChildren);

	MyCustomWidget.Reset();
}

TSharedRef<SWidget> UCustomWidget::RebuildWidget()
{
	MyCustomWidget = SNew(SCustomWidget);
		//.FlipForRightToLeftFlowDirection(bFlipForRightToLeftFlowDirection);

	return MyCustomWidget.ToSharedRef();
}

#if WITH_EDITOR

const FText UCustomWidget::GetPaletteCategory()
{
	return LOCTEXT("Common", "Common");
}

#endif

#undef LOCTEXT_NAMESPACE

  在对应的public相关目录下,添加CustomWidget.h:

#pragma once

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Misc/Attribute.h"
#include "Styling/SlateBrush.h"
#include "Widgets/SWidget.h"
#include "Components/Widget.h"
#include "UObject/ScriptInterface.h"
#include "CustomWidget.generated.h"

class SCustomWidget;


/**
 * The custom widget allows you to display a vertex constructed geometry in the UI.
 *
 * * No Children
 */
UCLASS()
class UMG_API UCustomWidget : public UWidget
{
    GENERATED_UCLASS_BODY()

public:
    //#if WITH_EDITORONLY_DATA
    //    /** Image to draw */
    //    UPROPERTY()
    //        USlateBrushAsset* Image_DEPRECATED;
    //#endif

        /** Image to draw */
        //UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Appearance)
        //    FSlateBrush Brush;

        ///** Color and opacity */
        //UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Appearance, meta = (sRGB = "true"))
        //    FLinearColor ColorAndOpacity;

        //~ Begin UWidget Interface
    virtual void SynchronizeProperties() override
    {
        Super::SynchronizeProperties();
    }
    //~ End UWidget Interface

    //~ Begin UVisual Interface
    virtual void ReleaseSlateResources(bool bReleaseChildren) override;
    //~ End UVisual Interface

#if WITH_EDITOR
    //~ Begin UWidget Interface
    virtual const FText GetPaletteCategory() override;
    //~ End UWidget Interface
#endif


protected:
    //~ Begin UWidget Interface
    virtual TSharedRef<SWidget> RebuildWidget() override;
    //~ End UWidget Interface
    TSharedPtr<SCustomWidget> MyCustomWidget;

    /*TSharedPtr<FStreamableHandle> StreamingHandle;
    FSoftObjectPath StreamingObjectPath;*/
};

这里控件里没有图片,颜色等属性。

在Engine\Source\Runtime\Slate\Public\Widgets路径下添加SCustomWidget.h文件:实现方式参考STestSuite.cpp文件中的TestCustomVerts函数

#pragma once

#include "CoreMinimal.h"
#include "Widgets/SWidget.h"
#include "Widgets/SLeafWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"

class FSlateWindowElementList;
class FPaintArgs;


/** Widget with a handler for OnPaint; convenient for testing various DrawPrimitives. */
class SLATE_API SCustomWidget : public SLeafWidget
{
public:
	SLATE_BEGIN_ARGS(SCustomWidget)
		//: _OnPaintHandler()
	{}

		SLATE_END_ARGS()

		/**
		 * Construct this widget
		 *
		 * @param	InArgs	The declaration data for this widget
		 */
		void Construct(const FArguments& InArgs)
	{
		//OnPaintHandler = InArgs._OnPaintHandler;
	}

	virtual FVector2D ComputeDesiredSize(float) const override
	{
		return FVector2D(128, 128);
	}

	virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& Geometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
	/*{
		if (OnPaintHandler.IsBound())
		{
			FOnPaintHandlerParams Params(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, bParentEnabled && IsEnabled());
			OnPaintHandler.Execute(Params);
		}
		else
		{
			FSlateDrawElement::MakeDebugQuad(
				OutDrawElements,
				LayerId,
				AllottedGeometry.ToPaintGeometry()
			);
		}

		return SWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled && IsEnabled());
	}*/

private:
	//FOnPaintHandler OnPaintHandler;
};

  cpp文件:

#include "Widgets/SCustomWidget.h"
#include "Rendering/DrawElements.h"
#include "Styling/CoreStyle.h"
#include "SlateGlobals.h"
#include "Framework/Application/SlateApplication.h"

int32 SCustomWidget::OnPaint(const FPaintArgs& Args, const FGeometry& Geometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
//{
//	if (OnPaintHandler.IsBound())
//	{
//		FOnPaintHandlerParams Params(AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, bParentEnabled && IsEnabled());
//		OnPaintHandler.Execute(Params);
//	}
//	else
//	{
//		FSlateDrawElement::MakeDebugQuad(
//			OutDrawElements,
//			LayerId,
//			AllottedGeometry.ToPaintGeometry()
//		);
//	}
//
//	//return SWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled && IsEnabled());
//	return LayerId;
//}

{
	const float Radius = FMath::Min(Geometry.GetLocalSize().X, Geometry.GetLocalSize().Y) * 0.5f;
	const FVector2D Center = Geometry.AbsolutePosition + Geometry.GetLocalSize() * 0.5f;

	const FSlateBrush* MyBrush = FCoreStyle::Get().GetBrush("ColorWheel.HueValueCircle");
	// @todo this is not the correct way to do this
	FSlateShaderResourceProxy* ResourceProxy = FSlateDataPayload::ResourceManager->GetShaderResource(*MyBrush);
	FSlateResourceHandle Handle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(*MyBrush);

	FVector2D UVCenter = FVector2D::ZeroVector;
	FVector2D UVRadius = FVector2D(1, 1);
	if (ResourceProxy != nullptr)
	{
		UVRadius = 0.5f * ResourceProxy->SizeUV;
		UVCenter = ResourceProxy->StartUV + UVRadius;
	}

	// Make a triangle fan in the area allotted
	const int NumTris = 12;
	TArray<FSlateVertex> Verts;
	Verts.Reserve(NumTris * 3);

	// Center Vertex
	Verts.AddZeroed();
	{
		FSlateVertex& NewVert = Verts.Last();
		NewVert.Position[0] = Center.X;
		NewVert.Position[1] = Center.Y;
		NewVert.TexCoords[0] = UVCenter.X;
		NewVert.TexCoords[1] = UVCenter.Y;
		NewVert.TexCoords[2] = NewVert.TexCoords[3] = 1.0f;
		NewVert.Color = FColor::White;
	}

	for (int i = 0; i < NumTris; ++i)
	{
		Verts.AddZeroed();
		{
			const float Angle = (2 * PI*i) / NumTris;
			const FVector2D EdgeDirection(FMath::Cos(Angle), FMath::Sin(Angle));
			const FVector2D Edge(Radius*EdgeDirection);
			FSlateVertex& NewVert = Verts.Last();
			NewVert.Position[0] = Center.X + Edge.X;
			NewVert.Position[1] = Center.Y + Edge.Y;
			NewVert.TexCoords[0] = UVCenter.X + UVRadius.X*EdgeDirection.X;
			NewVert.TexCoords[1] = UVCenter.Y + UVRadius.Y*EdgeDirection.Y;
			NewVert.TexCoords[2] = NewVert.TexCoords[3] = 1.0f;
			NewVert.Color = FColor::White;
		}
	}

	TArray<SlateIndex> Indexes;
	for (int i = 1; i <= NumTris; ++i)
	{
		Indexes.Add(0);
		Indexes.Add(i);
		Indexes.Add((i + 1 > 12) ? (1) : (i + 1));
	}

	FSlateDrawElement::MakeCustomVerts(OutDrawElements, LayerId, Handle, Verts, Indexes, nullptr, 0, 0);

	return LayerId;
}

  

 

 widget坐标系统:

 https://baemincheon.github.io/2020/02/09/unreal-widget-coordinate-system/

 

 

优化:

https://gameinstitute.qq.com/community/detail/113852