找回密码
 立即注册
查看: 184|回复: 1

图形学硬件拾遗(三)

[复制链接]
发表于 2023-2-26 10:53 | 显示全部楼层 |阅读模式
一、内存架构和总线/Memory Architecture and Buses

在两个设备间,发送数据的通道叫做 端口/port总线/bus 是超过两个设备间共享的通道。带宽/bandwidth 是用来描述在端口/总线间传输数据的速度。在计算机图形结构中,端口/总线是把各个部分连接起来的胶水,可以说是非常重要。因为带宽是一种稀缺资源,因此在图形系统构造中,精心的设计和分析是必须的。
很多 GPU 都有独占的 GPU 内存 ,也叫做显存/video memory。访问显存会比通过CPU总线访问系统内存要快很多。比如PC上的 16位 PCIe v3 总线,速度是 15.75GB/s,PCIe v4 总线,速度是31.51 GB/s。Pascal 架构的显卡(GTX 1080)显存,速度可以达到320 GB/s。
一般来说,texture 和 render target 都是存储在显存中,对于场景中不怎么改变的物体,数据也是存在显存中。即使是带动画的人物角色,mesh 也是不变的,每帧变化的是在关节处混合的顶点。在这种情况下,动画是由模型的矩阵和 vertex shader program来驱动的。因此静态的 vertex buffer 和 index buffer是放在显存中的,供快速访问。而每帧需要更新的数据,是放置在动态的 vertext buffer和index buffer中,位于可通过总线访问的系统内存中。
大部分游戏主机,比如所有版本的 XBox和 PS4,会使用通用内存架构/unified memory architecture/UMA,这时 CPU 和 GPU 会共享内存,并使用相同的总线,这点和 PC 上显卡的显存很不一样。Intel 的 GEN9 图形架构也是使用了 UMA,来实现 CPU 和 GPU的内存共享,如下图所示。不过可以看出,GPU 还是拥有自己的 L1、L2、L3 缓存,而 LLC/last -level cache 才是共享的,这样可以尽量保证 GPU 访问内存的速度。



Intel的Gen9 SoC结构,图形处理器和CPU时共享内存的,LLC也是共享的。

二、缓存和压缩/Caching and Compression

在不同的GPU 架构中,缓存的位置不尽相同。总体来说,增加级联式的缓存的目的,一是降低内存访问的延迟,二是利用内存访问的局部性/locality 来提高带宽的利用率。局部性指的是,当GPU访问某个位置的内存后,很可能会在不久后再次访问该处的内存,或者相邻位置的内存。大部分 texture 和 buffer 都是按照 tiled 模式存储的,这点也对增加局部性有帮助。
为了降低带宽的占用,大部分 GPU 的硬件单元会将 render target 实时压缩和解压。注意,这些压缩算法都是无损的,这样总是能精准复现原始的数据。压缩算法的核心是 tile table,会为每个 tile 存储额外的信息,tile table 可以存储在 chip 上或者级联内存中。如图所示,是两种压缩系统的设计方式,压缩单元在两种系统中的位置不同。



GPU中 render taget的压缩和缓存技术示意图。左:压缩解压单元在缓存之后;右:压缩解压单元在缓存之前

一般来说,depth、color、stencil 的压缩设置方式是一样的。在 tile table 中,会记录对应 tile 中像素的状态,可能是 compressed、uncompressed 或者 cleared。压缩的模式也可能是不同的,例如某个压缩模式可以压缩至 25%,另外一个可能压缩至 50%。GPU 处理内存转换的大小也会影响压缩的级别。例如某种架构下 GPU 能处理的最小内存大小为 32B,如果 tile 的大小为 64B,那就只能压缩至 50%。如果tile 大小为 128B,那么可能的压缩比例是 75%(96B)、50%(64B)、25%(32B)。
tile table 也会用来做快速 clear render target。当某个render target 需要 clear 时,只需要在 tile table 中将对应的状态设置为 cleared,而不用修改 render target 中的内容。当硬件需要访问 render target 中的内容时,解压缩单元会先检查 tile table 中设置的 tile 状态,如果状态是 cleared,那么就将 clear 的值直接写到缓存,而不需要进行解压缩处理,这样就节省了带宽。如果 render target的状态不是cleared,那么解压缩单元会从内存中读取数据,如果是压缩数据,则进行解压。因此 render target 的 clear 是一个非常快速的操作,通过简单地设置状态就能实现。
当硬件单元访问 render target 且写入了新的值后,当 tile 从cache 中被置换出来时,它就会被发送到压缩单元,压缩单元会尝试对其进行压缩。如果支持两种以上的压缩方式,那么几种压缩格式都会被尝试,然后选择占用内存最小的那个。因为 render target的压缩要求必须是无损的,如果没有合适的压缩格式,那就会使用未压缩格式进行存储。如果压缩成功,tile 的状态被设置成 compressed,压缩失败时,则设置成 uncompressed。
压缩/解压单元可以在缓存之后(post-cache),也可以在缓存之前(pre-cache)。Pre-cache 的压缩方式可以显著增加缓存的利用效率,但是也会使得系统变得更加复杂。通常对于颜色的压缩方式是这样的:在 tile 中得到一个颜色的锚点值,然后算出其他颜色和锚点值得差,并进行记录。而对于深度值,因为深度值在屏幕空间中是线性得,所以通常是通过保存平面方程来实现。
三、颜色缓冲区/Color Buffering

GPU的渲染需要访问很多的 buffer,比如颜色、深度、模板等。注意这里的“颜色”,并不一定是可以看到的颜色,可以是任意的数据。
根据可以表示的颜色数量的不同,Color buffer 有几种不同的颜色模式,这些模式包括:

  • High color,每个像素两个字节,其中15位或者16位表示颜色,共可表示 32768/65536种颜色;
  • True color/ RGB color,每像素3或4个字节,24位用来表示颜色,可表示 16777216/1700万种颜色;
  • Deep color,每像素30/36/48位,可表示超过十亿种颜色。
High color 使用16位表示颜色时,红绿蓝三个通道,每个至少可以分到5位,表示32个级别的值。剩余1位,通常会用来加在绿色通道中,因为人眼对绿色是最敏感的,对亮度的贡献度最高。High color 相对 true color 和 deep color 有很大的速度优势,因为每像素2字节对于计算机是非常友好的,访问速度会非常快。但是现在 high color 已经非常少见,因为如果每通道只有32或64个级别,那么相邻的颜色级别很容易被区分。这个问题也叫做 bandingposterization。由于 Mach banding 的心理效应,人类的视觉系统会放大这种差异。通过将相邻的颜色级别进行混合,进行抖动,可以降低这种效应的影响。即使是在24位的显示器上,banding 还是会被注意到,往 framebuffer 中添加噪声可以掩盖这个问题。



长方形从左从左往右,从白色到黑色,共32个级别,形成了 banding。

True color 使用24位表示RGB颜色,每个通道一个字节。在PC系统上,有时候顺序会被翻转成BGR。在计算机内部,true color常常会存储成每像素32位,因为计算机的内存系统通常对4字节的元素访问进行优化。当 true color 使用24位进行保存时,也叫做 packed pixel format。有些时候我们会为RGB通道添加一个A通道,表示 alpha 值,形成RGBA。对于实时渲染来说,通常用24位来表示颜色是足够的。虽然也能看到颜色的 banding,但是相对16位颜色来说要好的多。
Deep color每个通道使用10/12/16位,共30/36/48位表示RGB颜色。如果加上 alpha 通道,就变成 40/48/64 位。HDMI 3.1 支持30/36/48这三种模式,DisplayPort每通道最多可以支持16位。
Color buffer 通常会使用上面我们提到的方式进行压缩和缓存,除此之外,将 color buffer 中的颜色和即待写入的颜色进行混合的过程 blending,将在后面讲到。Blending 通过 raster operation/ROP 单元实现,每个 ROP 通常会连接到一个内存分区上。
四、视频输出控制器/Video Display Controller

每个GPU,都有视频输出控制器/Video Display Controller/VDC,或者叫做 display engine 或 display interface,负责将 color buffer 显示到显示器上。GPU 上有这样的硬件单元,来提供不同的接口,比如说 high-definition multimedia interface (HDMI)、DisplayPort、digital visual interface(DVI) 和 video graphics array (VGA)。即将进行显示的 color buffer 可能是位于可供 CPU 访问的内存中,也可能是位于 GPU 的显存中。每个接口都会使用相应的标准协议传输 color buffer,时间信息,甚至音频信息。VDC 也可能进行一些图形缩放、降噪和多图形源组合等功能。
显示器的图像刷新频率通常在 60Hz~144Hz之间,叫做垂直刷新率/vertical refresh rate。当屏幕刷新率低于 72Hz 时,人眼就能感受到闪烁。
近些年来,显示器技术在刷新率、每通道位数、色域和同步方面都有进步。显示器刷新率一般都是60Hz,现在120Hz的屏幕也越来越常见,甚至最高能达到600Hz。对于高刷新率,一张图形通常会显示多次,有些时候会插入一些纯黑色的帧,来最小化帧间移动时导致的模糊。相对于每通道8位的表示,现代的HDR显示器每通道可以使用10位或者更多。Dolby 的HDR显示器技术,会使用LED组来增强LCD显示器。这样可以是显示器获得 10倍的亮度和100倍的对比度。带有更大色域的显示器也越来越多,可以显示更多的颜色,更加生动的图像。
为了降低撕裂效果,厂商也开发出一些自适应的同步技术,像AMD的FreeSync和NVIDIA的G-sync。
五、单缓冲,双缓冲和三缓冲/Single, Double, and Triple Buffering

假设现在我们只有一个buffer,表示当前需要显示到显示器上的内容。当一帧中的三角形被绘制时,会随着显示器的刷新,逐渐一点点出现,这种效果是很奇怪的。即使我们的帧率和显示器刷新率相等,single buffer 还是会有问题。如果我们决定清除 buffer 然后绘制一个较大的三角形,因为VDC 会将正在绘制的color buffer区域输出,我们就会短暂地看到 color buffer的变化。这种现象叫做撕裂/tearing,画面显示看起来被分割成了两部分。在一些像Amiga 的古老系统中,你可以通过检测 Beam来防止画面撕裂,这样single buffering就是可行的。现在只有在一些 VR系统中才会用到这种 single bugger渲染架构,会使用 beam 方式来尽可能降低延迟。
目前最常用的消除撕裂的方式是使用 double buffering。一个渲染完成的图像在 front buffer 中显示,同时不可见的的 back buffer 在被绘制。当back buffer 中的图像被传输到显示器后,图形驱动会 swap front buffer 和 back buffer,来避免撕裂。Swap的过程通常都是简单地交换两个 color buffer的指针。对于 CRT 显示器,这个事件叫做 vertical retrace,整个过程叫做 vertical synchronization / Vsync / 垂直同步。虽然LCD 显示器没有 beam 的 retace,但是我们使用相同的术语来表示显示器中的交换过程。
如果渲染过程完成后,立即交换 back buffer 和 front buffer,可以最大化帧率,这样可以用来测试渲染系统的性能。但是这样其实是没有跟随 vsync 来进行更新的,同样会造成撕裂。不过因为两个buffer 都是渲染好的完整图像,撕裂效果不会像 single buffer 中那样糟糕。


对 double buffering 进行改进,添加上 pending buffer, 就形成了 triple buffering。pending buffer 和 back buffer 类似,都是不可见的,不同的是 pending buffer 是可以被修改的。pending buffer 在交换之后,会变成 back buffer。再次 swap 后,成为 front buffer。这样,三种buffer 构成了循环,如上图所示。
使用 doule buffering 时,等待垂直刷新及swap时,front buffer 需要被显示,而 back buffer 因为是已经渲染好的图像,所以需要保持不变,等待被显示。相对 double buffering,triple buffering的优势在于,当等待垂直刷新的时候,系统可以访问 pending buffer。这样 triple buffering 避免了等待的时间,从而增加帧率。不过相应的缺点就是,会增加整整一帧的延迟,会给用户从手柄鼠标键盘的输入增加延迟,甚至产生卡顿。
理论上来说,超过三个的buffer 也是可行的。如果每帧渲染的时间波动很大,使用更多的buffer,就能使结果更平滑,帧率更高,当然代价就是更大的延迟。
一种加速渲染技术就是 SLI/scalable link interface,可以让两张或者更多显卡同时工作。AMD 系列显卡中这种技术叫做 CorssFire X。并行工作的原理,可以是将整个屏幕分成两个或更多部分,每个显卡负责一部分区域的渲染。也可以是每个显卡负责一个单独的帧,最后将所有帧汇总。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2023-2-26 10:57 | 显示全部楼层
[调皮]
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 06:47 , Processed in 1.437784 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表