- 图形学
- 图像处理
【免费下载链接】skia
Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.
本篇指南基于 Skia 官方开发者文档,系统讲解“如何让一个会改变 Blink 布局测试结果的 Skia 变更安全落地”。读完本文,你将掌握两条落地路径——小范围测试改动(少于约 20 个用例)的免协调重基线流程,以及大范围渲染改动所依赖的SK_IGNORE_xxx_FIX代码抑制(code suppression)机制,并理解 Skia 自动滚入(roll)Chromium 这一底层协作模型的来龙去脉。
背景:Skia 变更为什么会“炸”到 Blink 布局测试
Skia 是 Chromium 的 2D 渲染引擎,二者通过自动滚动(roll)机制保持同步:
- Skia 仓库的提交会由Auto Roll Bot(即
skia-deps-roller)每天多次自动滚入 Chromium 的DEPS,也可以手动触发。因此 Skia 侧的每一个像素级渲染差异,最终都会在 Chromium 侧的 Blink 布局测试(third_party/blink/web_tests/TestExpectations所管理的那套 WebKit 布局测试)上体现出来; - 布局测试的核心产物是截图比对:只要 Skia 的绘制行为变化(抗锯齿、抖动、光栅化策略等),大量布局测试的期望图像就会失配,
Chromium.WebKit树随之变红; - 文档给出的关键决策阈值是约 20 个测试用例:影响更少可以自行重基线,影响更多则必须引入代码抑制并通知 Blink gardener。
这一阈值划分是整个流程的骨架,下面分别展开。相关上游文档见 Skia 变更如何同步到 Chromium 与 Skia 在 Chrome 中的分支与滚动。
路径 A:影响少于约 20 个布局测试——免协调重基线
原文档明确指出:影响少于约 20 个布局测试结果的变更,无需与 Blink gardener 特别协调,按以下五步操作即可:
准备 Skia 变更,并记录哪些布局测试会转红。Skia 本地可通过自身的 GM 测试与 SkiaGold 比对预判,Chromium 侧的布局测试运行细节属于 Blink 仓库范畴,文档在此只给出“先预判红名单”这一要求。
把代码提交到 Skia 仓库。
在包含该变更的 Skia 自动 roll 之前,手动向 Blink 的
LayoutTests/TestExpectations文件推送一个变更,把因你的改动而预期失败的测试标记出来。标记语法为:foo/bar/test-name.html [ Failure Pass ] # Needs rebaseline这条期望行的含义是:该测试当前会失败(
Failure),在重基线完成后应当通过(Pass),尾部注释说明这是“待重基线”条目。等待 Skia roll 成功落地。此时 Blink 树不会因你的变更而变红,因为期望文件已经声明了这些失败是预期内的。
再提交一个 Blink
TestExpectations变更,移除你在第 3 步添加的所有跳过的测试期望,并运行:git cl rebaseline该命令会触发自动重基线机器人,由它自动把新的期望图像 check in,完成图像基线更新。
整条路径的精髓在于时序控制:TestExpectations 的声明必须先于(或恰好在)Skia roll 落地前生效,让 roll 通过 CQ 时布局测试不会大面积变红;roll 落地后再撤销期望并交给 rebase bot 自动更新图像。
路径 B:影响超过约 20 个测试——代码抑制 + 重基线 + 清理三段式
当“大范围”指超过约 20 个测试时,直接改图会导致Chromium.WebKit树在较长时间内大面积变红,必须走更严谨的三段式流程:Setup(设置代码抑制)→ Rebaseline(重基线)→ Cleanup(清理)。
基本概念
- 代码抑制(code suppression):即一个编译开关(build flag,又称 define),用来在 Chromium 侧暂时屏蔽新渲染路径、保留旧行为;
- 命名规范:抑制开关必须命名为
SK_IGNORE_xxx_FIX形式(FIX后缀提示该开关是临时性的,重基线后必须移除); - roll 的含义:更新 Chromium 中 Skia 版本的动作称为 roll,由 Auto Roll Bot 每天自动执行多次,也可手动执行(对应
skia-deps-roller的 DEPS 变更)。
代码抑制在 Skia 源码中的真实形态
文档要求“把变更放在代码抑制后面”,即新代码路径用#ifdef / #ifndef包裹,Chromium 侧不定义该宏时行为与旧版本完全一致。当前 Skia 仓库中就有多个这样的存量开关,可作为写代码时的直接参照:
src/core/SkBitmapDevice.cpp 中
SK_IGNORE_BLURRED_RRECT_OPT包裹了drawRRect的优化路径——定义该宏则回退到通用的drawPath分支:void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { #ifdef SK_IGNORE_BLURRED_RRECT_OPT // call the VIRTUAL version, so any subclasses who do handle drawPath aren't // required to override drawRRect. this->drawPath(SkPath::RRect(rrect), paint, true); #else LOOP_TILER( drawRRect(rrect, paint), Bounder(rrect.getBounds(), paint)) #endif }src/gpu/DitherUtils.h 用
#ifndef SK_IGNORE_GPU_DITHER把 GPU 抖动相关的整组声明(DitherRangeForConfig、MakeDitherLUT)包裹起来,对应的实现侧 src/gpu/DitherUtils.cpp、src/gpu/ganesh/SkGr.cpp 以及 Graphite 侧的 src/gpu/graphite/PaintParams.cpp、src/gpu/graphite/precompile/PaintOption.cpp 也以同样的#ifndef对称包裹。这种“头文件与实现同时用相同宏包裹”的写法保证了:Chromium 定义
SK_IGNORE_GPU_DITHER后,新路径的声明与实现一起消失,旧版 Chromium 代码不会因链接缺失或符号冲突而出错。编写自己的SK_IGNORE_xxx_FIX时,应对齐这一模式。
Setup:三种情形下的初始化步骤
情形一:抑制尚不存在——直接法(Direct method)
- 在 Skia 中做出会改变大量 Blink 布局测试的变更;
- 将该变更放在代码抑制(
SK_IGNORE_xxx_FIX)之后; - 把变更 check in 到 Skia 仓库;
- 手动 roll Skia,或给 autoroll 追加代码抑制——即把它写进 Chromium 的
skia/chromium_skia_defines.gypi。
情形二:抑制尚不存在——替代法(Alternate method)
- 先在 Chromium 的
skia/chromium_skia_defines.gypi中加上代码抑制,再动 Skia 代码; - 在 Skia 中做出会改变大量布局测试的变更;
- 把变更放在代码抑制后面;
- 把变更 check in 到 Skia 仓库;
- 等待 Skia roll 进 Chromium。
两种写法的差异只在时序:直接法让 Skia 先落地、roll 时带上 define;替代法让 define 先落地,roll 到 Chromium 后新代码自然被抑制。两者终态相同。
情形三:抑制已存在于头文件中
- 从 Chromium 的头文件中移除该代码抑制,同时把它加入
skia/chromium_skia_defines.gypi; - 原文档特别警告:代码抑制不能同时存在于头文件和 gyp 文件的 define 中,否则会产生“多重定义”警告,而在 Chromium 构建中这类警告会被当作错误,直接打断整个 Chromium 构建。
这一约束与 Skia 与 Chromium 的 API 同步策略 中关于“code suppression cannot exist in both the header file and the gyp file, it should only reside in one location”的描述完全一致,说明 define 的存放位置是全局唯一归属问题,而非简单便利问题。
Rebaseline:重基线窗口期的操作
- 选择 Blink 树安静的时段操作,尤其避开 PST 下午;改动越大,这一点越重要。无论如何,都要确认当值 Blink gardener 是谁并事先通知——你会让
Chromium.WebKit树变红一段时间,gardener 需要知道这不是他要修的故障。 - 提交一个同时做两件事的 CL:从 Chromium 的
skia/chromium_skia_defines.gypi中移除代码抑制,同时向 Blink 的LayoutTests/TestExpectations添加[ NeedsRebaseline ]期望行。之后自动重基线机器人会负责把新图像 check in。原文档给出的规模指引是:大约600 张以内需要重基线的图像走这套自动化流程是普遍可接受的;超过 600 张时仍可用[ NeedsRebaseline ],但最好与 gardener 协调。该 CL 应当能干净地通过 CQ。 - 小心本来就失败或不稳定(flaky)的测试:它们是否需要重基线是不确定的;而 flaky 测试无论如何都不应从 TestExpectations 中移除。遇到这类情况,在提交前先回退(revert)你对 TestExpectations 的改动。
- 如果清理步骤不由你负责,请按以下模板开一个 Skia Issue 交给负责人:
- 标题:
Remove code suppression SK_IGNORE_xxx_FIX. - 描述:
Code suppression SK_IGNORE_xxx_FIX rebaselined with Blink revision 123456. - 并 assign 给负责清理的那个人。
- 标题:
Cleanup:清理
- 从 Skia 中删除已经不再使用的旧代码,以及当初为抑制新代码而引入的所有 define;
- 把清理变更 check in 到 Skia 仓库;
- 等待 Skia roll 进 Chromium。
至此SK_IGNORE_xxx_FIX从“临时脚手架”完成它的生命周期。可以推断,正是这个“Issue 跟踪 + 最终移除”的闭环,使得仓库里存活的SK_IGNORE_*宏数量有限且都可追溯到具体变更(如上文SK_IGNORE_BLURRED_RRECT_OPT、SK_IGNORE_GPU_DITHER即为其产物)。
配套能力:在 trybot 上联调 Skia + Chromium/Blink 改动
上述流程的前提是“能在提交前验证 Skia 变更在 Chromium 中的表现”。多仓库 Chromium trybot 指南 提供了配套的验证手段:
- 只有 Skia 改动:Skia 补丁已在 Gerrit 上时,直接跑 Chromium trybot 即可,机器人会应用该 Skia 补丁;
- Skia + Chromium 改动:在 Chromium CL 的
<chromium>/src/DEPS的hooks数组中加fetch_custom_patch+apply_custom_patch两个钩子,从 Gerrit 的refs/changes/XX/YYYY/ZZfetch 并 cherry-pick Skia 补丁,让 trybot 在 Skia 补丁之上跑测试; - 本地验证时运行
gclient runhooks拉取 Skia 源码;若third_party/skia工作区不干净(已打过补丁),需先在该目录执行git reset --hard再运行gclient runhooks; - 上传 Chromium CL 时用常规
git cl upload,但要在 issue 描述中加COMMIT=false,避免误提交。
对于无法走 DEPS 钩子的任意文件改动,文档还给出了把文件拷入<chromium>/src/patch/并按 Chromium 目录结构覆盖的兜底方案。
出问题时:roll 失败、回滚与树管理
重基线流程发生在 Skia roll 与 Chromium 树的交汇处,失败时的处置方式在 Skia 文档体系中有明确对应:
Skia 在 Chrome 的分支与滚动 说明:roll 出问题时应到 autoroll 页面暂停新 roll、revert 有问题的 DEPS roll,找不到 owner 时指派给 Skia Gardener(列于 status.skia.org 的 gardeners 组件中);
Skia 日常维护(gardening)文档 明确指出:DEPS roll 落地失败的常见原因就是布局测试——检查 DEPS roll 的 commit 哈希区间找到肇事的 Skia CL 并 revert(或联系作者);如果 Skia CL 改变了布局测试但新图像看起来正确,则测试需要重基线,并给出了两条操作路径:编辑 Chromium 侧的
skia/skia_test_expectations.txt(较快、但文档标注为不推荐),或提交单独的 Blink 补丁编辑LayoutTests/TestExpectations(推荐但更慢)。此外还给出了创建 “Skia image rebaseline” Chromium bug 的完整模板(标签需包含OS-All与Cr-Blink-LayoutTests,滤镜相关改动需 cc 特定同学)。注意这两条路径与本文主线文档的关系:gardening 文档面向“roll 已经红了、需要灭火”的场景,而
blink.md面向“我要主动发起一个已知会改变渲染的变更”的场景——后者通过代码抑制和[ NeedsRebaseline ]预期行,本质上就是为了避免进入前者的救火状态。
决策速查
| 变更规模 | 是否需要通知 gardener | 核心机制 | 关键动作 |
|---|---|---|---|
| 少于约 20 个布局测试 | 否 | TestExpectations期望行 | roll 前加foo/bar/test.html [ Failure Pass ] # Needs rebaseline,roll 后移除并git cl rebaseline |
| 超过约 20 个布局测试 | 是 | SK_IGNORE_xxx_FIX代码抑制 | Setup(define 写入skia/chromium_skia_defines.gypi,且不得与头文件重复)→ Rebaseline(移除 define + 加[ NeedsRebaseline ],约 600 张以内自动化可接受)→ Cleanup(删旧码与 define,开 Skia Issue 跟踪) |
无论哪条路径,时序纪律都是成败关键:期望声明要跑在 roll 变红之前,define 的增删要成对出现,清理必须闭环。按这套流程操作,一个像素级的 Skia 变更就能在 Chromium 庞大的布局测试体系下平稳落地。
- 图形学
- 图像处理
【免费下载链接】skia
Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.
相关推荐
miniblink49 中 Skia 变更与 Blink 布局测试(Layout Tests)的提交协作指南
miniblink49 中 Skia 变更与 Blink 布局测试(Layout Tests)的提交协作指南 导读 本文以 third_party/skia/s
前端桌面应用Skia 与 Chromium 代码同步指南:API 变更的构建宏抑制策略与 Blink 测试协作流程
Skia 与 Chromium 代码同步指南:API 变更的构建宏抑制策略与 Blink 测试协作流程 导读 当你的 Skia 改动修改了公共 API 时,往往
图形学Skia 变更如何优雅落地 Blink Layout Tests:从 Rebaseline 到 Staging Define 全流程指南
Skia 变更如何优雅落地 Blink Layout Tests:从 Rebaseline 到 Staging Define 全流程指南 导读 Skia 是 C
图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考