浏览器帧渲染流程理解

浏览器帧渲染流程是指浏览器将网页内容显示到屏幕上的完整过程,原本是想对 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),避免主线程阻塞的问题。
  • 对于只涉及 transformopacity 的动画,可以在合成器线程中直接处理,无需参与布局和重绘阶段。

合成帧:是合成器线程生成的最终渲染结果,包含了所有图层的绘制信息,合成帧会被提交

合成器滚动(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 纹理。

帧渲染流程理解

  1. 输入事件阶段(Input Events)

    • 处理阻塞输入事件(Blocking input events):例如 touch、wheel 事件,这些会影响滚动阻塞后续任务执行,优先处理

    • 处理非阻塞输入事件(Non-blocking events):例如 click、keypress,不会阻塞滚动和合成器线程(Compositor Thread)调度

  2. 宏任务队列阶段(Macrotask ):例如 setTimeout、setInterval 执行。

  3. 微任务队列阶段(Microtasks):Promise.then / await、MutationObserver、queueMicrotask() ,微任务队列会被清空。

– 渲染管线阶段/渲染阶段(Render Pipeline) –

等待 VSync信号(用于垂直同步的 屏幕刷新信号,通常时间为60HZ = 16.6ms)到来,判断是否有内容更新(DOM/CSS变化/存在 RAF 未执行)然后进行下一帧的计算操作,从帧缓冲区中取出计算好的完整帧进行展示。

  1. 每帧事件阶段(per frame event):内部执行顺序取决于浏览器引擎本身的状态检测策略。

    • scroll 滚动事件:如果不是 合成器滚动(Compositor Scroll )的话,就会在主线程中处理滚动事件。
    • resize 事件:resize 通常会导致窗口变化,引起重新布局(回流)。
    • CSS 媒体查询阶段( Media Query):根据 media query 设定,更新 CSS 规则。
  2. RAF 阶段(requestAnimationFrame)

    • 执行所有 RAF 回调,是最后一次修改 DOM 的机会。
    • 执行 CSS Animation/Transition 的关键字步进。
  3. 布局阶段(Style & Layout)

    1. 计算样式(Recalculate Style): 根据 DOM 、CSSOM、样式规则,计算样式属性。
    2. 布局阶段( Update Layout ):即所指的 Reflow 回流,改变元素的位置和大小
    3. 更新渲染树:将结果更新会渲染树
    4. 观测执行(不属于该阶段,在此之后执行):IntersectionObserver 和 ResizeObserver 在元素布局后才能知道计算元素之间的关系(是否相交),保证结果和 UI 展示一致
  4. 重绘阶段(Paint):该过程只是构建指令,不会执行绘图,也是主线程任务的终点

    1. 标记重绘区域(Paint Invalidation):浏览器会检查哪些布局对象(Layout Object)发生变化,计算出需要重绘的区域,将屏幕划分出需要更新的矩形区域,标记为 脏区域(Dirty Rects/Invalid Rects),这个矩形区域就是无效的(Invalid),这样后续渲染时,未变更区域就可以复用上一帧缓存,做到增量更新
    2. 生成绘图指令(Paint):浏览器遍历布局树(Layout Tree),使用绘图指令构建行为,将行为存储 DisplayList 数据结构中,不涉及 GPU 和 像素计算流程。
    3. 录制/提交Record):把 DisplayList 中的指令封装并提交给 合成器线程(Compositor Thread)
  5. 光栅化(Raster)

    1. 图层划分(Layerization):合成器线程(Compositor Thread)根据上一步的 DisplayList和 transform 等合成器属性,判断哪些需要独立图层,构建或更新合成层树/图层树(Layer Tree),在这里重绘阶段生成 DisplayList 会被分组和切分,每个图层在后续光栅化只使用自己层的绘图指令集合。
    2. 光栅化(Raster):合成器线程(Compositor Thread)将图层的绘制指令拆分为栅格化任务,交给光栅线程调度,光栅线程生成并提交绘制命令;在启用 GPU Raster 时由 GPU 硬件执行光栅化直接生成纹理,回退情况下则由 CPU 执行软件光栅化。
  6. 合成(Composition)

    1. 生成绘制矩形的列表(Draw Quads):描述每个图层的最终位置、大小和来源的GPU纹理,将多个图层 Layer 组合成最终帧,确定每个图层的堆叠顺序
    2. 应用 transform / opacity 等合成属性:属于性能优化步骤,在 GPU 上执行,无需参与主线程中的样式计算、布局和重绘流程
    3. 提交显示:将 Draw Quads 列表打包成 合成帧(Compositor Frame),提交给浏览器进程 / GPU 进程进行展示
  7. 屏幕渲染(GPU Display)

    1. 数据提交:浏览器进程 / GPU 进程接收到合成帧(Compositor Frame),会把相关数据提交给 GPU 图形驱动
    2. 展示:GPU 驱动会把结果写入帧缓冲区( Frame Buffer ),屏幕在下一次接收到 VSync(垂直同步信号) ,会从帧缓冲区中取出数据,并展示到屏幕上,

— 空闲阶段—

11. 空闲时间(Idle Period)

  • 在这个时间端就会执行 requestIdleCallback 中的任务,如果时间不足,可能会被推迟到下一帧空闲时间执行

总结

浏览器以事件循环为驱动,每帧中先处理任务与微任务,如果到达 VSync 时机,则根据是否需要更新内容,执行 RAF 和 渲染管线(样式、布局、绘制、光栅化、合成),在下一次垂直同步时由 GPU 将结果显示到屏幕,后续根据是否有剩余时间进入空闲回调阶段。

  • Vsync 的周期决定了 RAF 的执行周期

  • 重绘阶段其实不会执行绘制操作,只是对构建了绘图指令的数据结构并提交给合成器线程,真正绘制是在 Vsync 信号发出后的合成阶段(第9步)。

  • 从渲染流程阶段可以看出,requestAnimation 对比 requestIdleTask,优先级更高,且 rquestIdleTask 不能保证每次事件循环都会执行。

思考

RAF 后台暂停是因为忽略了 VSync 信号吗?

不是的,RAF 被暂停实际是因为渲染管线被暂停,且因为内容不可见,无需计算几何和像素,会跳过样式计算/重绘/回流这些步骤。

浏览器还会继续捕获 VSync 信号,CSS 合成动画(transformopacity)由 GPU 完成,消耗极低,还是会继续执行。

参考内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值