slate相关
2022-06-11 21:43 kk20161206 阅读(246) 评论(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