问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深入理解CPU和GPU渲染机制

创作时间:
作者:
@小白创作中心

深入理解CPU和GPU渲染机制

引用
1
来源
1.
https://juejin.cn/post/7440723906137341967

在现代网页开发中,了解CPU和GPU的渲染机制对于优化前端性能至关重要。本文将深入探讨CPU和GPU在图片渲染中的工作原理,以及如何通过GPU加速和合成层优化来提升网页性能。

在图片渲染的过程中,CPU和GPU扮演了截然不同但又紧密协作的角色,它们各自其实都可以实现图片的渲染,那你有没有想过它们之间的差异究竟有何不同?下面就让我们一起揭开图片渲染的工作原理,探索CPU和GPU渲染的奥秘,以及它们在性能优化中的实际应用。

一、图片渲染过程

首先,我们先来思考一下一张图片究竟是如何渲染在屏幕上的呢?

浏览器图片渲染是一个从文件加载到最终绘制的复杂过程,涉及网络传输、图片解码、位图生成以及GPU/CPU渲染等多个环节。其渲染过程如下:

二、CPU和GPU渲染

或许有人会提出疑问,解码后的图片需要绘制到屏幕上,浏览器会通过GPU和渲染管线进行处理,上图为什么没有体现GPU渲染的环节?

其实在浏览器处理图片渲染时,不一定都会用到GPU渲染,使用CPU也一样可以渲染,只不过使用CPU渲染容易出现性能瓶颈而已。

在计算机图形学中,图形渲染可以通过CPU(中央处理器)或GPU(图形处理单元)来完成。CPU和GPU在处理图像工作的过程中有不同的工作模式和性能特征,下面让我们一起来看下这两种渲染方式的过程。

CPU渲染图片

  • CPU首先会接收准备好的图形数据,包括顶点坐标、颜色、纹理等信息。
  • CPU逐个处理这些顶点的坐标,进行变换(比如世界坐标到屏幕坐标的变换),通常涉及到矩阵multiplication和其他数学运算。
  • CPU会将这些顶点转换为实际在屏幕上显示的像素。这个过程可能需要进行三角形的拆分、插值计算(确定颜色、深度、法线等属性),这个过程也叫光栅化,复杂的图片会涉及到很大的计算,这是瓶颈之一。
  • 在渲染过程中,CPU计算光照(如漫反射、镜面反射)和阴影,然后将这些信息应用到像素上。
  • 在图像渲染的最后一步,CPU将所有的像素信息合成一个图像。对于复杂场景,可能还需要通过多次取样(如抗锯齿)来提高图像质量,这个过程会涉及大量缓存区数据的读写操作,这是渲染瓶颈之二。
  • 最后,将渲染好的图像输出到显示设备。

接下来让我们看一看GPU的渲染过程。

GPU渲染图片

  • CPU将图形数据(顶点、纹理等)上传到GPU内存中。GPU预期这些数据在渲染过程中会被多次使用。
  • 渲染过程中使用着色器(GPU编程中独有的)来处理顶点和像素。着色器是运行在GPU上的程序,包括顶点着色器、片段着色器等。
  • GPU的强大之处在于它可以同时处理大量的顶点和像素。不同的块可以同时并行地执行相同的代码,因此可以在短时间内处理大量的数据。其并行计算处理能力,这是与CPU渲染最大的不同之处。
  • GPU会将处理后的顶点数据光栅化,将其转换为实际在屏幕上显示的像素,这一步GPU尤其高效,单位时间内处理海量图像数据,实现高吞吐量
  • GPU在光栅化后立即开始处理光照、纹理映射和其他特效(如反射、阴影等)。着色器允许高度的灵活性和复杂性。其充分利用计算机的硬件加速,优化了常见图像操作,如纹理映射、光栅化、抗锯齿等。
  • GPU将渲染好的图像合成并输出到缓冲区,最后将其显示在屏幕上~

上述是2D图像的渲染过程,而针对3D图像渲染的场景又会变得更复杂,因为它涉及模型、灯光、纹理等各环节,这里引用他人的一张图,原理其实大致都差不多,但会有更多的阶段细分,过程如下:

(图片来源:知乎-灵知子)

GPU和CPU渲染的区别

从芯片设计原理上来分析,中央处理单元(CPU)设计上是通用计算核心,适合复杂逻辑计算和任务调度,而图形处理单元(GPU)设计上专注于并行计算,非常适合处理大量相同类型的操作,如像素着色和矩阵计算等。

它们原本的职责功能就不一样,虽然都能用于处理图形的渲染工作,但两者还是有一定的差别,对比如下:

特性
GPU渲染
CPU渲染
并行计算
超强(支持数千个并行线程)
较弱(线程数量有限)
适合任务
大规模数据、重复任务
复杂逻辑、动态任务
速度
高速(尤其是大规模任务)
较慢(受限于核心数量)
实时性
优秀(支持高帧率)
较差(耗时较长)
计算精度
较低(精度适中)
较高(支持高精度)

CPU则胜在灵活性和精确性,适合逻辑复杂的离线渲染任务。
GPU优势在于速度和并行处理,适合需要实时渲染或大量重复计算的场景。

实际业务当中,CPU具有灵活性,GPU具有高效计算能力,两者是相辅相成的关系,配合利用得当,才能实现复杂图形的最佳渲染效果。

三、GPU加速

既然GPU原本就是因图形处理而生,那我们日常中该如何将它应用到我们的图形渲染优化当中呢?这其实也是很多面试官经常提到的问题,如何启用GPU加速?

其实一些现代图像格式(比如WebP、SVG等)可能会在浏览器内部使用GPU加速解析。尽管这些图像本身的加载仍由CPU负责,后续的绘制操作可能会通过GPU提升性能。

从点到面,从图片渲染到图层渲染,我们在实际性能优化场景中如何利用硬件加速(GPU加速)?

在前端开发中,启用GPU加速的方式其实有两种,一种是利用CSS,另一种则通过使用JavaScript,利用得当,两者都可以显著提高动画和视觉效果的性能。下面分别介绍如何通过CSS和JavaScript启用GPU加速。

CSS启用GPU加速

GPU加速的核心是将特定的渲染任务交给GPU处理,而不是依赖CPU。以下是通过CSS启用GPU加速的一些方法:

  1. 使用translateZ(0)translate3d(0, 0, 0)

CSS transform是启用GPU加速的最常用方式。通过定义2D或3D转换,浏览器会创建一个独立的合成层,并将该层交给GPU处理。

.div {
    transform: translateZ(0); /* 强制使用GPU */
}
  1. 使用will-change

will-change提前告诉浏览器某个属性即将发生变化,优化渲染路径。

.div { 
    will-change: transform, opacity; /* 预告浏览器优化 */ 
}
  1. 使用opacity

opacity改变元素透明度时通常会触发GPU加速。

.div { 
    opacity: 0.5; /* 渐变效果通过GPU渲染 */
}
  1. 过渡动画中使用opacity或transform

CSS动画或过渡中使用transformopacityfilter会触发GPU加速。

@keyframes slideIn { 
    from { 
        transform: translate3d(-100%, 0, 0); /* GPU渲染 */ 
    } 
    to { 
        transform: translate3d(0, 0, 0); /* GPU渲染 */ 
    } 
} 
.div { 
    animation: slideIn 1s ease-out; 
}

JavaScript启用GPU加速

其实JavaScript本身并不直接与GPU通信,但可以通过操作DOM元素触发GPU渲染路径。从上面我们可以知道,CSS是如何让GPU加速的,因此,在JavaScript中,通过操作CSS属性也可以来启用GPU加速。

  1. 操作CSS 3D相关的属性

改变CSS属性:如transformopacitywill-change等,其实本质上是css启动GPU的另一种形式,这里不再赘述。

const box = document.querySelector('.box');
// 设置transform,启用GPU加速
box.style.transform = 'translate3d(100px, 0, 0)';
  1. 动画使用requestAnimationFrame

当你进行动画时,使用requestAnimationFrame可以提供平滑的渲染并有效利用GPU。

const box = document.querySelector('.animationBox');
let start = null;
function animate(timestamp) {
  if (!start) start = timestamp;
  const progress = timestamp - start;
  box.style.transform = `translateY(${Math.min(progress / 2, 200)}px)`; // 使用transform
  if (progress < 400) {
    requestAnimationFrame(animate);
  }
}
// 触发动画
requestAnimationFrame(animate);
  1. 使用WebGL

如果需要更高级的GPU操作,可以通过WebGL与GPU直接交互。

const canvas = document.querySelector('canvas');
const wgl = canvas.getContext('webgl');
// 设置清屏颜色
wgl.clearColor(0.0, 0.0, 0.0, 1.0);
wgl.clear(gl.COLOR_BUFFER_BIT);

以上就是两种启动GPU加速的一些具体方法,通过合理使用CSS和JavaScript,开发者可以有效启用GPU加速,从而提升页面的动画性能和用户体验。

但在实际业务中,启动GPU加速与另外一个概念息息相关,那就是合成层。从上面图像在浏览器上的渲染原理,我们就可以看出,光栅化后会有一个合成图像的过程,因此合成层的提升也至关重要。

四、合成层的提升

1. 什么是合成层?

当浏览器渲染网页时,通常会将页面拆分成多个图层,这些图层分别负责不同的部分(例如背景、文本、图像等)。合成层允许浏览器在GPU上渲染这些层,并在最终一步将它们合成到屏幕上。

举个不太恰当但形象的例子,接触过Photoshop(PS)的都知道,图像的最终形态是由右侧小窗口一张张图层叠加而成,而合成层的概念就与PS合成最终图像形态的过程类似,而直接影响合成层的就是最上面那几张图层。

2. 合成层的作用

合成层(Compositing Layers),尤其是在处理动画和复杂布局时,合成层的提升有助于确保这些操作在GPU上进行,从而减少CPU的负担。这种方法相比传统的渲染方式可以减少重绘(Repaint)和重排(Reflow)的次数,提高渲染效率。

3. 如何提升合成层?

要提升合成层的使用,这个过程与启用GPU加速操作非常类似,因此,您可以采用以下几种策略:

1)使用will-change属性

使用CSS的will-change属性可以告知浏览器即将对某一元素进行变化,这样浏览器可以提前创建合成层。

.div {
  will-change: transform, opacity; /* 提升元素的合成层 */
}

2)使用CSS转换和过渡

  • 应用3D转换: 使用translateZ(0)translate3d(0, 0, 0),可以强制将元素推入合成层。
.div {
  transform: translateZ(0); /* 强制创建合成层 */
}
  • 过渡动画: 在进行动画时,使用transformopacity等属性,这些属性的操作会尽量在合成层中处理。
.box {
  transition: transform 0.3s, opacity 0.3s;
}

3)避免过大的合成层

虽然合成层能够提高性能,但过多或过大的合成层也会导致性能问题。不必要的合成层会增加GPU的内存使用,因此,在设计布局时,应合理安排需要提升的元素。

4)使用缓存的图像

图像常常在合成层中创建,确保图像的尺寸合适并尽量减少像素大小,以提升性能。例如,可以使用image-rendering属性来提高图像的渲染效果。

.img {
  image-rendering: crisp-edges; /* 优化图像渲染 */
}

5)简化图层结构

尽可能减少DOM层级和图层数量。复杂的布局可能会阻碍浏览器合成层的使用,简化DOM结构可以提高性能。

4. 隐式合成层

这个概念也许你听过,但并可能不清晰。隐式合成层,是相对显式合成层而言的,上面所描述的其实都是显式合成层,而所谓隐式合成层,就是指那些图层原本不是合成层,但浏览器基于性能优化自动创建的合成层。

什么意思呢?举个简单的场景来讲,那种z-index在合成层之上的图层。这种图层即使不是合成层,也会被转换成为合成层。

.div1 {
  transform: translateZ(0);
  z-index: 100;
}
.div2 {
  font-size: 12px;
  z-index: 1000;
}

上面例子当中,div1是合成层,但div2为普通图层,但由于它位于div1之上,出于性能优化的目的,浏览器最终会将它提升为合成层,这种就是隐式合成层。

因此,我们在写CSS的时候,一定要注意不要过度地滥用z-index属性,认为值越大越好,过多地使用这种图层有时候会适得其反。

当然这只是隐式合成层其中的一种方式,还有动画与过渡、滚动优化、混合模式、遮罩、3D变换等场景都会涉及到隐式合成层。

它的核心目标是减少重排和重绘的范围,提升渲染性能。

通过上面的分析,相信你对图形的渲染机制有了进一步的认识。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号