有许多关于 3D 编程(OpenGL 或 DirectX)和相应图形管道的资源可用,但我想知道它们在现代 GPU 上是在哪个级别实现的。
到目前为止,我发现已经从实现图形管道各个阶段的非常专业的电路转向更通用的方法。这种转变已部分反映在可编程着色器形式的 3D API 上。大多数晶体管似乎专用于执行实际着色器指令的大规模并行 SIMD 单元。
但是图形管道的其余部分呢?它们仍然在硬件中实现吗?
现代 GPU(例如 Nvidia Fermi)基本上是一组“愚蠢的”SIMD 阵列,由来自 CPU 和各种缓存的指令和数据驱动,而将图形管道映射到这些指令的所有实际逻辑都发生在图形驱动程序中?
或者 GPU 中是否存在一些控制单元,将传入的高级指令和数据流(编译的着色器程序、顶点数据和属性以及纹理)转换为实际的 SIMD 指令并负责同步、内存分配等?
我怀疑现实情况介于这两个极端之间,而且答案可能相当冗长,并且基于大量的猜测(某些 GPU 供应商拒绝发布其产品的任何文档,更不用说驱动程序源代码了……),但是任何正确的方向和有用的资源的提示都将不胜感激。
到目前为止,我已经找到了系列博客文章这对于更好地了解现代 GPU 非常有用,但我缺少关于整体架构的某种更高级别的概述 —— 我可以理解大多数提到的概念,但不太明白它们是如何组合在一起的。
答案1
到目前为止,我发现已经从实现图形管道各个阶段的非常专业的电路转向更通用的方法。这种转变已部分反映在可编程着色器形式的 3D API 上。大多数晶体管似乎专用于执行实际着色器指令的大规模并行 SIMD 单元。
正确。基本上,由于旧 GPU 上的特征尺寸相对较大,高效实现基本照明、抗锯齿、纹理映射、几何等功能的唯一方法是使用“固定功能”管道。他们为了性能而牺牲了灵活性,因为他们没有足够的芯片密度来使用更通用的大规模并行 SIMD 架构(如当前 GPU)来实现它。
现代 GPU(例如 Nvidia Fermi)基本上是一组“愚蠢的”SIMD 阵列,由来自 CPU 和各种缓存的指令和数据驱动,而将图形管道映射到这些指令的所有实际逻辑都发生在图形驱动程序中?
有些事情仍然在硬件中完成,其他事情则不是。例如,防滚翻装置仍然用于将像素数据推送到 VGA 芯片组的最后阶段。请注意,我在这里使用“VGA 芯片组”作为通用术语,指的是将视频信号传输到显示器的机制,无论它是否真正是“VGA”。
总体而言,当前的 GPU 架构(例如 Nvidia Fermi 和 AMD Southern Islands)大部分都是大规模并行 CPU,它们具有自定义指令集,并且每个单独的“核心”都非常弱,但所有的很多核心(有时几千个)。但其中仍然有图形专用硬件:
硬件视频解码通常在很大程度上使用固定功能芯片完成。当涉及 DRM(数字限制管理)时尤其如此。有时“硬件”视频解码实际上意味着一组固件引导的指令,这些指令只是作为 SIMD 核心的常规旧任务提供。这确实取决于具体情况。
除极少数专用于计算的 Nvidia 主板(Tesla)外,几乎所有“通用 SIMD”显卡拥有专门用于视频输出的完整硬件阵列。视频输出与渲染不同;固定功能输出元素包括 LVDS/TMDS/HDMI/DisplayPort 编解码器、HDCP,甚至声音的处理(基本上是一点 DSP),因为 HDMI 支持音频。
“图形内存”仍存储在 GPU 上,因此它们不必穿越繁琐且延迟相对较高的 PCIe 总线来访问系统 RAM,而系统 RAM 本身比更昂贵、更高质量、更快的图形内存(例如 GDDR5)更慢,响应时间更长,后者的容量更小,但速度比系统内存更快。将内容存储在图形内存中并从那里检索到 GPU 或 CPU 的过程仍然基本上是固定功能操作。一些 GPU 有自己的“IOMMU”,但这种内存管理单元与 CPU 不同(分开)。然而,对于最近集成到其处理器(Sandy 和 Ivy Bridge)中的英特尔 GPU 来说,情况并非如此,其中的内存架构几乎完全是“连贯的”(图形内存是系统内存)和图形内存的读取对于 CPU 和 GPU 来说一样便宜。
或者 GPU 中是否存在一些控制单元,将传入的高级指令和数据流(编译的着色器程序、顶点数据和属性以及纹理)转换为实际的 SIMD 指令并负责同步、内存分配等?
SIMD 的“本机”语言几乎总是由软件中的驱动程序生成,而不是由 GPU 自己的固件生成。对于 DirectX 9 / OpenGL 2.x 级别功能尤其如此。使用 HLSL、GLSL 或 OpenGL ARB 着色器汇编器等高级语言编写的着色器最终由驱动程序通过敲击某些寄存器并执行所需的 PCIe 操作转换为 GPU 指令,以便发送计算和/或渲染命令的批处理缓冲区。
有些功能,例如硬件曲面细分(DirectX 11 / OpenGL 4.0),再次以固定功能的方式被推入硬件,类似于过去几乎所有功能。这是因为,性能限制要求进行这些计算的最有效方法是为其配备专用电路,而不是让固件或驱动程序“编程”SIMD 来执行这些计算。
我怀疑现实情况介于这两个极端之间,而且答案可能相当冗长,并且基于大量的猜测(某些 GPU 供应商拒绝发布其产品的任何文档,更不用说驱动程序源代码了……),但是任何正确的方向和有用的资源的提示都将不胜感激。
AMD 和英特尔公开了关于其最新 GPU 的非常详尽的文档,以及适用于 Linux 的完全可用的开源图形驱动程序(参见 Mesa 和 Direct Rendering Manager 项目)。如果您查看这些驱动程序中的一些代码,您会大笑,因为图形驱动程序编写者实际上必须实施在“软件”中绘制各种形状或图案等几何图形(但使用硬件命令将实际工作提交给硬件进行处理),因为 GPU 固件和固定功能都不再存在,无法在硬件中完全处理它:)他们必须做的事情才可以在新硬件上支持 OpenGL 1.x / 2.x,这有点可笑。
演变过程大致如下:
- 很久以前(在实时 3D 渲染被认为可能之前):在 CPU 上进行光线追踪对于非实时渲染来说是很正常的。对于像您在早期版本的 Windows 中看到的简单图形,CPU 的速度足够快,可以在没有固定功能硬件的情况下绘制简单的形状(矩形、字体字符、阴影图案等),但它无法绘制太复杂的东西。
- 很久以前(OpenGL 1.x):几乎所有东西都是由固态硬件实现的;即使是基本操作,“电气”固定功能也是常态
- 不久前(OpenGL 2.x):GPU 开始向可编程方向转变。5 年前的硬件上的“片段着色器”(又称像素着色器)可以几乎像 CPU 一样执行任意计算,但受到架构的限制,该架构仍然非常面向图形。因此,OpenCL / DirectCompute 不适用于此硬件。
- 最近(OpenGL 3.x):向通用 GPU 的过渡基本完成,但它们当然针对涉及批量提交的大型数据矩阵(想想线性代数)的工作负载进行了优化,而不是针对可以有效操作长序列非常小的数据(1 + 1、2 * 4、5 * 6 序列等)的 CPU。通用计算可通过 OpenCL、CUDA 等获得,但硬件仍然不是完整的“SIMD 协处理器”,因为(a)您仍然必须敲击硬件特定的寄存器才能获得 GPU 功能;(b)由于 PCIe 总线开销,从 GPU VRAM 读取非常慢(在当前架构上从 GPU 读取不是很优化);(c)内存和缓存架构与 CPU 不一致;许多传统的固定功能硬件仍然存在。
- 目前(OpenGL 4.x):摆脱了许多旧式固定功能硬件。略微改善了 GPU 读取延迟。管理单元允许 VRAM 和系统内存之间进行(翻译的)硬件辅助映射。还引入了硬件镶嵌,恢复了固定功能元素。
- 未来 (人血清白蛋白): GPU 基本上是一个协处理器。它几乎完全与 CPU 集成,GPU 和 CPU 之间的阻抗(对于读取/写入)非常小,即使是 PCIe 总线上的专用 GPU 也是如此。完全一致的内存架构 - “mi memoria es su memoria”(我的内存就是你的内存)。用户空间程序可以从“VRAM”读取,就像它们从系统内存读取一样,无需驱动程序垫片,硬件会处理它。您可以使用 CPU 对适量数据进行“串行”处理(执行此操作,然后执行此操作,然后执行此操作),使用 GPU 进行“并行”处理(对这个庞大的数据集执行此操作并按您认为合适的方式进行划分)。GPU 所在的主板可能仍然有 ROP、HDMI 编解码器等,但这些东西对于显示输出是必需的,因此您可以将其移动到主板中并让所有 GPU 都像 Tesla 卡一样,或者如果可以减少延迟,则将其留在 GPU 上。