slate相关
2022-06-11 21:43 kk20161206 阅读(260) 评论(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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | 在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); } //…… } 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类了:<br><br> 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); } |
TSharedRef<FSlateRenderDataHandle, ESPMode::ThreadSafe> FSlateRHIRenderer::CacheElementRenderData这个函数里也会把ElementList加入到ELementBatcher里。
Slate3DRenderer函数也会把ElementList加入到ELementBatcher里:
这个windowElementLists是怎么构造的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } |
另外,
1 | ElementBatcher->AddElements(ElementList) |
又调用了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 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:
1 | BatchVertices = InPayload.CustomVertsData。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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文件中的相关代码进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 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格式的,如下面的第三行:
1 2 3 | #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函数中,增加:
1 2 | 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文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #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函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #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文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | #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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!