浏览器帧渲染流程是指浏览器将网页内容显示到屏幕上的完整过程,原本是想对 requestAnimiationFrame(RAF)的执行机制做了解,后面就拓展成了帧渲染流程的理解
概念理解
VSync(垂直同步信号)
VSync (Vertical Synchronization,垂直同步)是显示器硬件发出的周期性同步信号,协调 GPU 渲染和显示器刷新的时序。
浏览器会主动捕获 Vsync信号执行渲染流程,我们常说的 60hz 屏幕刷新率,RAF 执行周期为 16.6ms ,其实就是指 Vsync 的周期时间。
画面撕裂问题:
Vsync 保证 GPU 在显示器完成当前帧刷新显示后才开始计算渲染下一帧。浏览器的渲染流程,是通过帧计算,然后将数据存入缓冲器,然后浏览器从缓冲区中取出帧数据,把画面渲染到界面上
由于 GPU 的帧计算速度过快,而屏幕刷新率过低,如果没有 VSync 存在,浏览器可能在取上一帧的缓冲区时,下一帧的数据就会进到缓冲区内,导致一个画面出现两帧的内容,就会出现画面撕裂的问题
所以 VSync 采用了双缓冲机制和同步信号机制,来避免画面撕裂问题。
-
同步信号:浏览器会捕获 Vsync信号,只有在 VSync 信号到达时,才会去刷新画面,然后再开始计算下一帧的画面,如果 GPU 渲染速度过快,会被强制等待,直到下一个 VSync 信号,同时利用双缓冲机制,来避免画面撕裂现象
-
双缓冲机制:使用前缓冲区(Front Buffer)和后缓冲区(Back Buffer),显示器从前缓冲区读取数据并显示,GPU 在后缓冲区渲染新帧
VSync 1 → GPU 渲染帧1到后缓冲区 → VSync 2 → 交换缓冲区 → 显示器显示帧1 → GPU 渲染帧2到后缓冲区 → VSync 3 → 交换缓冲区 → 显示器显示帧2
合成器线程(Compositor Thread)
浏览器中独立于主线程的专用线程,主要负责图层的合成和渲染优化工作,可以并行处理多个图层的合成。
主要完成以下工作:
- 接收主线程提交的 DisplayList 绘图指令
- 构建和管理图层树(Layer Tree)
- 将图层分配给光栅线程或 GPU 线程进行光栅化
- 执行图层的合成操作,生成最终的合成帧,合成帧是合成器线程生成的最终渲染结果,包含了所有图层的绘制信息,合成帧会被提交给浏览器进程或 GPU 进程,最终写入帧缓冲区等待显示。
- 处理合成器滚动(Compositor Scroll),避免主线程阻塞的问题。
- 对于只涉及
transform和opacity的动画,可以在合成器线程中直接处理,无需参与布局和重绘阶段。
合成帧:是合成器线程生成的最终渲染结果,包含了所有图层的绘制信息,合成帧会被提交
合成器滚动(Compositor Scroll):通常滚动事件是在主线程中处理,如果是合成器滚动且滚动内容不会引起 回流/重绘的话,就会在合成器线程(Compositor Thread)中处理滚动事件,提高滚动性能。
图层树(Layer Tree):即由页面图层构成的一棵树形结构,在 Chrome DevTool 中,可以通过 3D 动画看到对应的图层结构,通常使用 transform 3D、绝对定位、will-change、特殊元素
<video>、<canvas>等,就会生成一个图层,由此构成的图层结构即为图层树显示列表(DisplayList):浏览器渲染引擎中的数据结构,用于存储如何绘制页面元素的绘图指令序列,可以理解成 canvas 中的绘图指令,类似
drawText()、drawImage()光栅化(Raster):在浏览器渲染流程,光栅化是将 DisplayList 中的绘图指令转换为 GPU Texture(GPU 纹理 - 可以理解为像素数据)的关键步骤。
GPU Raster:一种光栅化模式,绘制指令在 GPU 执行,将图层内容直接光栅化为 GPU 纹理。
帧渲染流程理解
-
输入事件阶段(Input Events)
-
处理阻塞输入事件(Blocking input events):例如 touch、wheel 事件,这些会影响滚动阻塞后续任务执行,优先处理
-
处理非阻塞输入事件(Non-blocking events):例如 click、keypress,不会阻塞滚动和合成器线程(Compositor Thread)调度
-
-
宏任务队列阶段(Macrotask ):例如 setTimeout、setInterval 执行。
-
微任务队列阶段(Microtasks):Promise.then / await、MutationObserver、queueMicrotask() ,微任务队列会被清空。
– 渲染管线阶段/渲染阶段(Render Pipeline) –
→ 等待 VSync信号(用于垂直同步的 屏幕刷新信号,通常时间为60HZ = 16.6ms)到来,判断是否有内容更新(DOM/CSS变化/存在 RAF 未执行)然后进行下一帧的计算操作,从帧缓冲区中取出计算好的完整帧进行展示。
-
每帧事件阶段(per frame event):内部执行顺序取决于浏览器引擎本身的状态检测策略。
- scroll 滚动事件:如果不是 合成器滚动(Compositor Scroll )的话,就会在主线程中处理滚动事件。
- resize 事件:resize 通常会导致窗口变化,引起重新布局(回流)。
- CSS 媒体查询阶段( Media Query):根据 media query 设定,更新 CSS 规则。
-
RAF 阶段(requestAnimationFrame)
- 执行所有 RAF 回调,是最后一次修改 DOM 的机会。
- 执行 CSS Animation/Transition 的关键字步进。
-
布局阶段(Style & Layout)
- 计算样式(Recalculate Style): 根据 DOM 、CSSOM、样式规则,计算样式属性。
- 布局阶段( Update Layout ):即所指的 Reflow 回流,改变元素的位置和大小
- 更新渲染树:将结果更新会渲染树
- 观测执行(不属于该阶段,在此之后执行):IntersectionObserver 和 ResizeObserver 在元素布局后才能知道计算元素之间的关系(是否相交),保证结果和 UI 展示一致
-
重绘阶段(Paint):该过程只是构建指令,不会执行绘图,也是主线程任务的终点
- 标记重绘区域(Paint Invalidation):浏览器会检查哪些布局对象(Layout Object)发生变化,计算出需要重绘的区域,将屏幕划分出需要更新的矩形区域,标记为 脏区域(Dirty Rects/Invalid Rects),这个矩形区域就是无效的(Invalid),这样后续渲染时,未变更区域就可以复用上一帧缓存,做到增量更新
- 生成绘图指令(Paint):浏览器遍历布局树(Layout Tree),使用绘图指令构建行为,将行为存储 DisplayList 数据结构中,不涉及 GPU 和 像素计算流程。
- 录制/提交(Record):把 DisplayList 中的指令封装并提交给 合成器线程(Compositor Thread)
-
光栅化(Raster)
- 图层划分(Layerization):合成器线程(Compositor Thread)根据上一步的 DisplayList和
transform等合成器属性,判断哪些需要独立图层,构建或更新合成层树/图层树(Layer Tree),在这里重绘阶段生成 DisplayList 会被分组和切分,每个图层在后续光栅化只使用自己层的绘图指令集合。 - 光栅化(Raster):合成器线程(Compositor Thread)将图层的绘制指令拆分为栅格化任务,交给光栅线程调度,光栅线程生成并提交绘制命令;在启用 GPU Raster 时由 GPU 硬件执行光栅化直接生成纹理,回退情况下则由 CPU 执行软件光栅化。
- 图层划分(Layerization):合成器线程(Compositor Thread)根据上一步的 DisplayList和
-
合成(Composition)
- 生成绘制矩形的列表(Draw Quads):描述每个图层的最终位置、大小和来源的GPU纹理,将多个图层 Layer 组合成最终帧,确定每个图层的堆叠顺序
- 应用 transform / opacity 等合成属性:属于性能优化步骤,在 GPU 上执行,无需参与主线程中的样式计算、布局和重绘流程
- 提交显示:将 Draw Quads 列表打包成 合成帧(Compositor Frame),提交给浏览器进程 / GPU 进程进行展示
-
屏幕渲染(GPU Display)
- 数据提交:浏览器进程 / GPU 进程接收到合成帧(Compositor Frame),会把相关数据提交给 GPU 图形驱动
- 展示:GPU 驱动会把结果写入帧缓冲区( Frame Buffer ),屏幕在下一次接收到 VSync(垂直同步信号) ,会从帧缓冲区中取出数据,并展示到屏幕上,
— 空闲阶段—
11. 空闲时间(Idle Period)
- 在这个时间端就会执行 requestIdleCallback 中的任务,如果时间不足,可能会被推迟到下一帧空闲时间执行
总结
浏览器以事件循环为驱动,每帧中先处理任务与微任务,如果到达 VSync 时机,则根据是否需要更新内容,执行 RAF 和 渲染管线(样式、布局、绘制、光栅化、合成),在下一次垂直同步时由 GPU 将结果显示到屏幕,后续根据是否有剩余时间进入空闲回调阶段。
-
Vsync 的周期决定了 RAF 的执行周期
-
重绘阶段其实不会执行绘制操作,只是对构建了绘图指令的数据结构并提交给合成器线程,真正绘制是在 Vsync 信号发出后的合成阶段(第9步)。
-
从渲染流程阶段可以看出,requestAnimation 对比 requestIdleTask,优先级更高,且 rquestIdleTask 不能保证每次事件循环都会执行。
思考
RAF 后台暂停是因为忽略了 VSync 信号吗?
不是的,RAF 被暂停实际是因为渲染管线被暂停,且因为内容不可见,无需计算几何和像素,会跳过样式计算/重绘/回流这些步骤。
浏览器还会继续捕获 VSync 信号,CSS 合成动画(transform、opacity)由 GPU 完成,消耗极低,还是会继续执行。
1038

被折叠的 条评论
为什么被折叠?



