slate相关
2022-06-11 21:43 kk20161206 阅读(341) 评论(0) 收藏 举报
这张图片真特别好。注意中间部分的几个函数的参数是windowElementList.今天尝试看最左边的paintInvalidationRoot这部分,半天没有看明白。有空继续看吧。
Layer的传递部分往后还要继续看。
合批
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;
- STextBlock,整体能合批。 slate合批机制一文中说的不对,而且并不需要开启simpleMode。
- SRichTextBlock,每一行每一块都会另开一层绘制。错误,两行三行都是同一个dc,第二行用不同的字体也还是能合批;
- SProgressBar自身逻辑会分别另开一层Layer绘制BackgroundImage和FillImage,LayerId+ 2。但return LayerId + 1
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函数
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
浙公网安备 33010602011771号