1.go test 测试代码
2.element-plus源码学习日志-03
3.UGUI源码阅读之Mask
4.深入理解 Lustre 系列二:测试
5.UE4-Slate源码学习(六)slate渲染Part2-Paint控件绘制
6.vue/compiler-dom源码分析学习--day4: 字符串化hoist节点
go test 测试代码
在开发过程中,确保代码的测试测试稳定性和性能至关重要。Go语言提供了内置的源码testing包,用于执行单元测试和性能测试,什意思通过命令go test实现。节点节点这个命令会自动扫描源码目录下名为*_test.go的测试测试八字算命源码文件,生成测试可执行文件,源码并输出测试结果。什意思
无需额外参数时,节点节点go test会遍历整个包下的测试测试测试文件。但你也可以通过查阅go help testflag了解更多参数选项。源码例如,什意思编写测试用例时,节点节点如对NewTestFlightItem函数的测试测试测试,可在CIHFeedback.go同目录下创建CIHFeedback_test.go,源码并执行go test。
性能测试同样重要,可以通过在测试文件中添加BenchmarkNewTestflight()和BenchmarkNewTestflightTimeConsuming()函数来实现。执行压力测试时,使用go test -test.bench=".*"命令,如测试结果耗时,可能表明涉及数据库操作,需关注性能优化。
查看性能表现,可以使用go tool pprof命令,如cpu.profile,通过topN命令分析profile文件,查看函数调用时间和占比。同时,可以借助graphviz生成函数调用关系图,以图形化方式理解代码执行情况。
go test还提供了cover工具来检查测试覆盖率,通过-go test -coverprofile=cover.out运行测试并统计,使用go tool cover -func=cover.out分析未覆盖的代码部分。因此,在开发过程中,养成编写全面单元测试的习惯是必不可少的。
通过上述步骤,Go语言的wanlshop商城源码testing包为代码测试提供了全面的支持,有助于确保代码的健壮性和性能。
element-plus源码学习日志-
每日学习进阶,承上启下
昨日探讨了input组件的使用及编码准则,今日深入剖析element-plus源码,探索新知识。
文件定位至element-plus\packages\dialog\src\index.vue
先看模板代码片段,引入了teleport组件,这是新增的内置组件。
没有使用teleport时,元素作为app组件的子节点;而使用teleport后,元素变为app组件的同级节点,统一挂载于body下,to属性可指定具体id的DOM节点。
前端展示层级对最终显示结果影响重大。在Vue 2时代,使用Vue.extend创建新实例,挂载于app同级节点,解决全局弹层的层级问题。新自定义组件简化了开发流程,优化代码。
引入了Vue 3自定义指令,与之前版本有所调整,需进一步学习。
注意到Vue 3支持fragments,组件不再受限于单一节点,引入新问题,需深入研究官方文档,理解其用法。
JS代码段回顾了之前讨论过的基础知识,简要审视,复习要点。
今日总结:学习了Vue的新内置组件teleport,具备将包含的节点挂载至指定DOM节点的功能。并了解了新版本自定义指令的调整。
下一步规划:基于Jest为组件编写单元测试,学习Jest的基本用法、报告生成等操作,深入框架测试领域。自助打印 源码
UGUI源码阅读之Mask
Mask主要基于模版测试来进行裁剪,因此先来了解一下unity中的模版测试。
Unity Shader中的模版测试配置代码大致如上
模版测试的伪代码大概如上
传统的渲染管线中,模版测试和深度测试一般发生在片元着色器(Fragment Shader)之后,但是现在又出现了Early Fragment Test,可以在片元着色器之前进行。
Mask直接继承了UIBehaviour类,同时继承了ICanvasRaycastFilter和IMaterialModifier接口。
Mask主要通过GetModifiedMaterial修改graphic的Material。大致流程:
1.获取当前Mask的层stencilDepth
2.StencilMaterial.Add修改baseMaterial的模板测试相关配置,并将其缓存
3.StencilMaterial.Add设置一个unmaskMaterial,用于最后将模板值还原
MaskableGraphic通过MaskUtilities.GetStencilDepth计算父节点的Mask层数,然后StencilMaterial.Add修改模板测试的配置。
通过Frame Debugger看看具体每个batch都做了什么。先看第一个,是Mask1的m_MaskMaterial,关注Stencil相关的数值,白色圆内的stencil buffer的值设置为1
这个是Mask2的m_MaskMaterial,根据stencil的计算公式,Ref & ReadMask=1,Comp=Equal,只有stencil buffer & ReadMask=1的像素可以通过模板测试,即第一个白色圆内的像素,然后Pass=Replace,会将通过的像素写入模板值(Ref & WriteMask=3),即两圆相交部分模板值为3
这个是RawImage的Material,只有模板值等于3的像素可以通过模板测试,所以只有两个圆相交的部分可以写入buffer,其他部分舍弃,通过或者失败都不改变模板值
这是Mask2的unmaskMaterial,将两个圆相交部分的模板值设置为1,也就是还原Mask2之前的stencil buffer
这是Mask1的unmaskMaterial,将第一个圆内的模板值设置为0,还有成最初的stencil buffer
可以看到Mask会产生比较严重的overdraw。
2.drawcall和合批
每添加一个mask,一般会增加2个drawcall(加上mask会阻断mask外和mask内的合批造成的额外drawcall),一个用于设置遮罩用的stencil buffer,一个用于还原stencil buffer。esp touch 源码
如图,同一个Mask下放置两个使用相同的RawImage,通过Profiler可以看到两个RawImage可以进行合批
如图,两个RawImage使用相同的,它们处于不同的Mask之下,但是只要m_StencilValue相等,两个RawImage还是可以进行合批。同时可以看到Mask1和Mask1 (1),Mask2和Mask2 (1)也进行了合批,说明stencilDepth相等的Mask符合合批规则也可以进行合批。
StencilMaterial.Add会将修改后的材质球缓存在m_List中,因此调用StencilMaterial.Add在相同参数情况下将获得同一个材质球。
深入理解 Lustre 系列二:测试
Lustre 测试框架
Lustre 的功能和性能测试通过一套完整的测试集进行,即Lustre测试套件集(LTS)。LTS由超过个测试组成,涵盖了从bash脚本、C程序到外部应用的多种测试类型。LTS提供了自动化执行测试流程的工具,允许用户选择性地执行测试或分组执行,同时支持对特定配置、特性如ldiskfs、ZFS、DNE、HSM的验证。LTS测试代码位于源码树的/lustre/tests目录下,主要组件见下表。
Lustre测试术语
LTS包括被集成到lustre-tests-*.rpm和lustre-io-kit-*.rpm中的所有脚本和应用。在目录/usr/lib/lustre/tests下的一个集合称为测试套件,如sanity.sh。独立测试,如large-lun.sh中的test 4,属于特定的测试套件。测试套件可以组合执行,例如回归测试。LTS包括回归测试、特定特性的测试、配置验证、恢复测试和错误测试。同城discuz 源码下表列出了一些活跃的单元、特性和回归测试及其简要描述。
Lustre代码测试
在Lustre编码时,推荐在开发周期早期进行持续的测试。开发者在提交代码前,应确保通过小型验收测试(acceptance-small)套件。若引入新测试用例,先查找并重现bug,修复后验证代码。新测试用例若未覆盖新bug,则需专门添加,用于测试该bug。
错误规避
在测试时,若执行失败是由于与bug无关的问题,且问题已被修复,可通过配置规避选项参数。例如,规避sanity.sh中的“g”和“”子测试,或所有insanity.sh测试,设置环境变量即可。在运行acceptance-small测试时,也可使用命令规避测试。
Lustre测试框架选项
下面的例子展示了运行完整acceptance-small测试或其子测试的方法。Lustre测试脚本可方便灵活地添加新的测试用例。
Lustre小型验收测试
小型验收测试(acceptance small,acc-sm)是Lustre在开发早期阶段捕获bug的测试。acc-sm测试脚本位于目录/lustre/tests中,包括三个分支:b1_6( tests)、b1_8_gate( tests)和HEAD( tests)。下表列出了一些通用acc-sm测试。在acceptance-small.sh和每个测试脚本中定义了执行命令。
Lustre测试中的环境变量
本节介绍用于测试的环境变量,通常在/lustre/tests/cfg/$NAME.sh配置脚本中定义。通过NAME=name访问环境变量。默认单节点测试配置是NAME=local,在/lustre/tests/cfg/local.sh中访问。下表列出了一些重要环境变量及其用途。
UE4-Slate源码学习(六)slate渲染Part2-Paint控件绘制
上一篇文章介绍了绘制一个SWindow的初期步骤,即计算整个UI树的控件大小,为绘制做准备。文章随后深入探讨了绘制流程的第二步,即执行FSlateApplication::PrivateDrawWindows()后,开始调用SWidget::Paint()函数,每个控件随后实现其虚函数OnPaint()。
在这一过程中,绘制参数被封装在FPaintArgs中,作为Paint和OnPaint过程中的关键引用参数。FSlateRHIRenderer与FSlateDrawBuffer是继承自FSlateRenderer的类,作为FSlateApplicationBase的全局变量,在构造时创建。在绘制过程中,通过GetDrawBuffer()函数可获取到FSlateDrawBuffer对象。
FSlateDrawBuffer实现了Slate的绘制缓冲区,内部封装了FSlateWindowElementList数组,用于存储多个SWindow下的绘制元素列表。每个SWindow通过AddWindowElementList()返回一个元素列表。
FSlateWindowElementList负载了SWindow内的所有图元信息,内部封装了FSlateDrawElement的数组,包含Cached和Uncached元素,以及SWindow的指针和用于渲染的批处理数据FSlateBatchData。
FSlateDrawElement是构建Slate渲染界面的基本块,封装了UI树节点控件需要渲染的相关信息,如渲染变换、位置、大小、层级ID、绘制效果等,以及后续渲染阶段需要的相关数据。
在Paint流程中,处理当前传入的SWindow和ChildWindows,首先判断窗口是否可见和是否最小化,然后从参数封装的OutDrawBuffer中获取WindowElementList。调用SWindow的PaintWindow()函数开始绘制窗口,并最终返回所有子控件计算完的最大层级。接着,子窗口递归绘制。
PaintWindow()函数在绘制窗口时,首先调用SetHittestArea()设置点击区域,HittestGrid会判断窗口大小是否改变,若不变则仅更新窗口在屏幕中的位置。构造FPaintArgs参数后,将其封装到FSlateInvalidationContext中。
FSlateInvalidationRoot类的PaintInvalidationRoot()函数可以作为控件树的根节点或叶子节点(SInvalidationPanel),构建快速路径避免每次绘制都计算大小和Paint函数,有利于优化。本篇文章主要分析正常慢速路径调用流程,优化相关将另文分析。
PaintSlowPath()函数从SWindow开始调用Paint()函数,并定义LayerId从0开始作为参数,进行实际的绘制相关计算。
Paint()函数首先处理裁剪、透明度混合、坐标转换等代码。若SWidget包含NeedsTick掩码,则调用Tick函数,我们在日常开发中通过蓝图或lua使用Tick函数时即调用到这里,通过SObjectWidget::Tick调用到UUserWidget::NativeTick供实现Tick。构造FSlateWidgetPersistentState PersistentState作为SWidget的变量,表示Paint时的状态。
PersistentState.CachedElementHandle将当前SWidget存储到FSlateWindowElementList中的WidgetDrawStack数组中。
更新FPaintArgs中的父节点参数和继承可点击测试参数,判断点击测试状态,然后将当前SWidget添加到点击测试中。调用虚函数OnPaint,由控件自己实现。
OnPaint()函数参数包括绘制参数引用、几何体、裁剪矩形、缓冲元素列表、层级、控件风格、父节点状态等。最后处理重绘标签、延迟绘制相关内容、UpdateWidgetProxy()根据缓存句柄更新快速路径中需要处理标记设置为Volatile不稳定状态的SWidget。
虚函数OnPaint()由子类自己实现,本文列举了SImage、SButton、SCompoundWidget和SConstraintCanvas的OnPaint()示例代码学习。
在SImage中,简单判断Brush是否存在以及BrushDrawType的类型,然后调用FSlateDrawElement::MakeBox将控件添加到缓冲区元素列表中。
SButton继承自SCompoundWidget,GetBorder()根据当前按钮状态返回ui中设置的Enabled、Press、Hover、Disabled等状态的Brush。
SCompoundWidget作为合成节点,有且只能有一个子节点,且在Paint时强制将子节点的LayerId+1,同时SCompoundWidget可以单独设置混合颜色和透明度,影响子节点。
SConstraintCanvas作为SWidget的基类对应UMG中常用的UCanvasPanel,通过ArrangeLayeredChildren()对孩子进行层级排序,并根据孩子的层级是否相同存储bool值在ChildLayers中。遍历所有孩子,判断是否开启新层级,递归调用Paint函数,最后返回最大层级。
SConstraintCanvas::ArrangeLayeredChildren函数中,获取设置bExplicitChildZOrder,表示可以将同层一次渲染,有利于提高渲染器批处理。对所有孩子排序,排序规则为FSortSlotsByZOrder。遍历所有孩子,判断可见性掩码、计算偏移、锚点、位置、拉伸缩放等,封装成FArrangedWidget存储到ArrangedChildren中,用于OnPaint时有序遍历。判断每个孩子ZOrder是否相同,相同则bNewLayer为false,大于LastZOrder则将bNewLayer设置为true,最终存储到ArrangedChildLayers中,用于OnPaint函数判断是否将layerId+1。
FSlateDrawElement::MakeBox()函数在OnPaint之后调用,将绘制控件的相关信息通过创建FSlateDrawElement绘制元素对象,添加到SWindow管理的FSlateWindowElementList元素列表中。创建Payload用于存储贴图等相关信息,根据控件Paint过程中的参数调用Element.Init初始化绘制元素,得到为该控件绘制创建的FSlateDrawElement对象。
总结整个Slate绘制流程的第二步,我们没有分析快速处理和优化细节,而是按照正常绘制流程分析代码。通过从PaintWindow开始遍历整个控件树,处理每个空间节点的Paint、OnPaint函数,最终目的是给每个控件创建一个FSlateDrawElement对象,存储渲染线程绘制所需的相关信息,并添加到FSlateWindowElementList中。理解了整个调用流程,整个过程较为清晰,本文基于UE4版本4..2。
vue/compiler-dom源码分析学习--day4: 字符串化hoist节点
vue/compiler-dom源码解析继续:深入理解字符串化hoist节点
前言:在处理内置指令后,我们今日关注的是@vue/compiler-dom包中的字符串化hoist节点操作。这部分代码在baseCompile方法中找到调用入口,且hoistStatic选项默认为true,尽管没有直接传入参数。
在vue/compiler-sfc/__tests__/compileTemplate.spec.ts的测试用例中,我们发现参数来源。接着,我们追踪到hoistStatic.ts和`walk`函数,这是实现静态提升(static hoisting)的关键,用于优化性能,避免在render function中重复生成和比较不会变化的静态节点。
静态提升允许将不变的元素和文本节点抽离到render函数外,提高渲染效率。例如,一个只包含动态部分的,其静态部分会被提升,渲染时会直接使用字符串拼接,而不是每次都重新创建。
现在,我们来看下stringifyStatic方法。该方法在确定节点会被提升到哪个阶段后执行,确保只处理适合的普通元素和文本节点。在transforms/stringifyStatic.ts中,代码负责识别可stringify的子节点,比如v-slot组件是不支持的,但可以hoist。
在`analyzeNode`方法中,逐层递归检查节点,确保所有子节点满足stringify条件。文本节点则有特殊的处理方式,其他情况下,如遇到table元素,可能存在浏览器兼容性问题,导致不能使用innerHTML。
总结`stringifyCurrentChunk`方法,它将识别到的静态块转换为字符串调用节点,替换原始hoist元素。整个过程旨在优化性能,通过字符串化hoist节点,减少不必要的DOM创建和比较。
尽管代码逻辑相对直观,但众多小方法间的跳转可能影响阅读。核心是找到可stringify的最大静态块,并进行替换。关于内置指令和style的处理,也有相应的优化策略,如transformStyle处理静态style为bind类型。