Skip to main content

Chapter 1: GPU Hardware Fundamentals

读完本章,你应能把一次渲染中的视觉结果或性能症状,定位到 GPU 硬件中的执行单元、内存路径、调度行为和平台能力边界上。这里的 GPU 指图形处理器,它把大量同构工作分摊给许多并行执行 lane,用吞吐量换取单位时间内处理更多顶点、像素、采样或通用计算任务。

本章用一个贯穿材料展开:一帧画面里有一个粒子 pass,CPU 每帧提交一个 draw call,GPU 使用 vertex shader 读取粒子位置,使用 fragment shader 输出带透明混合的发光圆点。这个 pass 的输入是粒子 buffer、纹理、uniform 参数和 pipeline state;输出是 render target 上的像素颜色,以及 profiler 里可观察到的帧耗时。

这个例子足够小,但可以覆盖 GPU 硬件基础。粒子数量增大时,性能可能受限于 shader 算术、buffer 读取、纹理采样、透明 overdraw、同步等待或 CPU 提交。硬件基础的价值在于把这些症状拆回具体资源路径:哪批线程在执行,哪类存储在供数,哪种调度在隐藏延迟,哪条 API 查询决定 fallback。

本章的核心结论是:GPU 硬件能力应通过执行粒度、计算单元、内存层级、平台差异、瓶颈证据和 API 查询共同判断。单看显存容量、核心数量或厂商名称,都无法稳定预测一个 frame 的表现。

1.1 GPU Execution Model and Hardware Role

GPU 的执行模型服务于吞吐型任务。吞吐型任务的特征是同一段程序要处理大量相似数据,例如一百万个顶点执行同一个 vertex shader,整张屏幕的 fragment 执行同一个 fragment shader,大量粒子执行同一个 compute kernel。GPU 把这些工作拆成许多线程,再把一组线程放到同一个执行批次中运行。

以粒子 pass 为例,CPU 侧的 draw call 描述了要绘制多少个粒子、使用哪个 pipeline、绑定哪些 buffer 和 texture。提交完成后,GPU 前端接收命令,把顶点工作、光栅化工作和像素工作分发到内部执行单元。每个粒子顶点执行相同 shader 代码,但读取的粒子位置、颜色、大小不同。大量线程的程序相同,数据索引不同,这就是 GPU 能批量执行的根源。

吞吐模型与低延迟模型的判断重点不同。CPU 通常追求单个任务尽快得到结果,GPU 追求同一时间段内处理尽可能多的独立工作。当一个 shader 线程等待内存返回时,调度器会切换到另一批已经就绪的线程。这个切换依赖 GPU 内部保留大量线程状态,包括寄存器内容、执行 mask 和待完成指令。延迟被更多在途工作覆盖,最终表现为更高吞吐。

这个过程可以用一条简化路径观察:

这张图的边界是单个 pass。它没有展开后续章节会讲的完整 render graph、barrier 和 frame pacing,但足以说明本章的主线:硬件角色从命令接收到 shader 执行,再到资源读取和输出写入。任何性能症状都应放回这条路径,而非孤立看某个术语。

GPU 执行模型中的“线程”也需要按 API 语境理解。CUDA 文档会使用 block、thread、warp、streaming multiprocessor 等术语;图形 API 的 shader 会使用 invocation、workgroup、subgroup、wave 或 simdgroup 等术语。它们的共同点是:程序员描述大量逻辑工作,硬件把这些工作打包到固定宽度或可配置宽度的执行批次里。NVIDIA 的 CUDA Programming Guide 把这一层作为理解 GPU 执行的基础入口;WebGPU、Vulkan、Metal、Direct3D 则把类似能力包装为各自的 shader 执行和设备能力模型。

在粒子 pass 中,最常见的第一层判断是“工作量在哪个阶段扩大”。如果 vertex shader 阶段耗时随粒子数近似线性增长,瓶颈可能在顶点读取、顶点变换或 instance 数据。fragment shader 阶段耗时随屏幕覆盖面积增长,瓶颈可能在像素填充、纹理采样、透明混合或 render target 带宽。这个判断先区分执行阶段,再把工作映射到硬件资源。

1.2 GPU 内部计算单元的组成与作用

GPU 内部计算单元负责执行 shader 指令。不同厂商命名不同,NVIDIA 常用 streaming multiprocessor,AMD 常用 compute unit 或 workgroup processor,Apple 文档通常在 Metal 层暴露 GPU family、feature set 和 device capability。名称差异背后有一个共同结构:一组执行 lane 批量处理线程,一组寄存器保存线程状态,一组本地或共享存储服务线程协作,调度器选择下一批可执行工作,专用管线处理纹理、插值、混合或矩阵类运算。

执行 lane 是真正完成算术和逻辑指令的地方。一个 lane 可以理解为一条处理同类指令的数据通道。shader 中的 position.xy += velocity.xy * deltaTime 会被编译成若干向量或标量指令,然后在多个 lane 上同时处理不同粒子的 position 和 velocity。lane 数量越多,同一周期可处理的数据项越多,但前提是有足够多的独立线程供调度器填满这些 lane。

寄存器文件保存每个在途线程的临时变量。粒子 shader 中的 position、velocity、color、clip position、临时光照结果都可能进入寄存器。寄存器使用量升高时,同一个计算单元可同时驻留的线程批次数下降,调度器可切换的候选工作减少,隐藏内存延迟的能力随之下降。AMD GPUOpen 的 register pressure 文章把寄存器占用与 occupancy 关联起来,这个关联在图形 shader 中同样有判断价值:Register pressure in AMD CDNA2 GPUs

shared memory、local memory、threadgroup memory 或 LDS 这类本地共享存储服务于同一工作组内的数据复用。它适合 compute shader 中的 tile 处理、前缀和、粒子分桶或图像滤波。普通 draw call 的 vertex shader 和 fragment shader 更常通过 buffer、texture、varying 和缓存系统传递数据;当章节进入 compute shader、tiling 和后处理时,本地共享存储会成为更直接的优化对象。

调度器负责选择可执行的线程批次。一个批次等待 texture fetch 或 buffer load 时,调度器倾向于切到另一批已经准备好操作数的线程。这个动作不需要程序员手写,但程序员会通过 shader 复杂度、寄存器数量、访存规律和分支结构间接影响它。粒子 pass 中,如果每个 fragment 都采样多张噪声纹理并执行透明混合,调度器虽然能切换批次,最终仍可能受限于 texture unit 或 render target 写入带宽。

专用单元处理固定功能或半固定功能任务。纹理单元负责采样、过滤、mipmap 选择和部分格式解码;ROP 或 blend 单元负责颜色写入、深度模板、混合和压缩相关工作;RT core、matrix core、media engine 等单元在特定平台上承担光线追踪、矩阵计算或视频编解码。图形工程里的关键判断是:shader ALU 很空闲时,帧耗时仍可能被 texture unit、blend、depth 或 bandwidth 约束。

下面的关系可以作为阅读 profiler 的硬件地图:

  • 执行 lane:对应 ALU 利用率、wave occupancy、subgroup 活跃度、shader 指令吞吐。
  • 寄存器文件:对应 register pressure、occupancy 下降、spill 风险和调度候选减少。
  • 本地共享存储:对应 workgroup 内复用、bank conflict、tile 计算和 barrier 等待。
  • 缓存与内存通路:对应 buffer load、texture fetch、cache miss、memory transaction 和带宽压力。
  • 专用单元:对应纹理过滤、depth/stencil、blend、ray tracing、matrix multiply 等专门路径。

粒子 pass 的优化顺序也应沿着这张地图走。先确认瓶颈阶段,再看该阶段主要使用哪类硬件资源;先减少无效工作,再改变数据布局;先用工具证据定位,再动 shader 或资源格式。这样做可以防止把所有卡顿都归因于“GPU 不够强”。

1.3 主流 GPU 架构对比分析(NVIDIA / AMD / Apple)

比较 GPU 架构时,应使用同一组维度:执行批次、计算单元组织、内存层级、专用单元、API 暴露方式、平台功耗和统一内存边界。这样比较能直接服务渲染判断。厂商品牌本身提供的信息有限,真正影响 pass 设计的是某个 shader 如何占用计算单元、某个资源如何进入内存系统、某个 feature 是否通过 API 暴露。

NVIDIA 桌面和数据中心 GPU 通常以 streaming multiprocessor 作为主要执行组织。CUDA 生态明确暴露 warp、block、shared memory、occupancy 等概念,图形 API 中虽然不会让普通 draw call 直接操作这些对象,但 shader 性能仍受同类硬件约束。NVIDIA 平台还常见独立显存、较强的专用计算单元和成熟 profiling 工具链,例如 Nsight Graphics 与 Nsight Compute。做实时渲染时,NVIDIA 设备通常适合用 profiler 精确拆分 SM 占用、纹理吞吐、RT 单元使用和显存带宽。

AMD 架构常使用 wavefront、compute unit、workgroup processor、LDS 等术语。RDNA 系列面向图形负载强化了前端、缓存和 wave32 支持;CDNA 系列更偏向数据中心计算。AMD GPUOpen 提供 RDNA 性能资料、Radeon GPU Profiler、Radeon Memory Visualizer 等工具入口。对渲染工程来说,AMD 平台的常见判断点是 wave 粒度、LDS 使用、memory coalescing、cache 行为和 draw/dispatch 的 GPU 时间拆分。

Apple GPU 运行在 Apple Silicon 体系内,常见特征是统一内存、tile-based deferred rendering 倾向、移动与桌面形态的能效约束,以及 Metal API 对功能集合的集中暴露。统一内存让 CPU 与 GPU 共享同一物理内存池,但资源访问仍需要关注 storage mode、cache 行为、同步和带宽预算。Apple 的 Metal Feature Set TablesMTLDevice 能力查询共同决定一个 Metal 程序可以使用哪些格式、纹理能力、argument buffer、ray tracing 或 shader 特性。

这三类平台在粒子 pass 上会给出不同工程信号。桌面 NVIDIA 或 AMD 独显上,粒子 buffer 可能位于显存,CPU 更新动态数据时要关注 upload buffer、staging、copy 和同步。Apple Silicon 上,统一内存降低了 CPU/GPU 数据交换的拷贝形态差异,但带宽仍与 CPU、GPU、Neural Engine、显示系统共享;频繁写大 buffer 仍会影响帧稳定性。WebGPU 或跨平台引擎还要受浏览器、驱动和安全模型限制,硬件支持的功能未必会被 WebGPU 直接暴露。

一组更稳的比较方式如下:

维度NVIDIA 常见判断点AMD 常见判断点Apple 常见判断点
执行批次warp、SM occupancy、CUDA/graphics profiler 证据wave32 / wave64、CU/WGP、LDS 与 occupancysimdgroup、threadgroup、Metal device family
内存形态独立显存、高带宽、显式上传路径独立显存或 APU 共享内存、cache 与 bandwidth 分析统一内存、storage mode、tile memory 倾向
专用能力RT、Tensor、纹理与压缩路径RT、matrix 指令、FidelityFX 生态与 RGP 工具Metal family、tile-based 优化、Apple 平台特性
工具证据Nsight Graphics / ComputeRadeon GPU Profiler / Memory VisualizerXcode GPU tools / Metal debugger
跨平台风险vendor extension 与驱动差异wave 粒度与资源绑定差异Metal 映射、统一内存同步和功耗约束

表中的内容不用于给厂商排名,而是用于决定工程入口。需要粒子数量上限时,看执行批次、寄存器、带宽和 overdraw;需要纹理压缩格式时,看 feature bits 和 format support;需要跨平台一致画面时,看坐标约定、精度、采样规则和颜色空间。平台差异的最终落点始终是 frame 内某个 pass 的资源路径。

1.4 GPU 性能瓶颈的根源与约束因素

GPU 性能瓶颈可以按约束资源分类。算力瓶颈来自 shader 指令执行量超过计算单元吞吐;带宽瓶颈来自 buffer、texture、render target 读写超过内存系统吞吐;延迟瓶颈来自大量随机访问或依赖链让调度器难以填满执行单元;分支发散让同一执行批次内的 lane 活跃度下降;同步等待让 CPU、queue 或 pass 在依赖上停顿;功耗和温度会降低移动或轻薄设备上的持续频率;API 提交开销会在小 draw、小 dispatch 过多时成为 CPU 或驱动侧限制。

粒子 pass 提供了一个完整症状图。粒子数量从一万增加到一百万,vertex shader 时间线性上升,说明顶点读取和顶点处理进入主路径;粒子屏幕半径增大后 fragment 时间快速上升,说明透明 overdraw 和颜色写入进入主路径;把噪声纹理从低分辨率换成高分辨率后时间上升,说明 texture fetch 和 cache miss 可能参与;把所有粒子拆成大量 draw call 后 CPU frame time 上升,说明提交开销变成限制。

瓶颈判断应先找“随什么变量增长”。随粒子数量增长的成本通常在 vertex、compute、buffer 读取或 draw count;随屏幕面积增长的成本通常在 fragment、depth、blend、render target bandwidth;随材质复杂度增长的成本通常在 texture、ALU、分支或 uniform 访问;随 pass 数量增长的成本通常在 render target 切换、barrier、load/store、resolve 或 command submission。

瓶颈还要区分执行利用率与等待。一个 shader 指令多、数据连续、lane 活跃度高的 pass 可能是算力受限;一个 shader 指令少但大量随机采样的 pass 可能是内存延迟受限;一个透明粒子 pass 可能 ALU 不高,却因为每个像素反复读取和混合 render target 而受带宽限制。工具里看到 GPU busy 高,只说明 GPU 正在花时间,不等于 ALU 是主因。

在没有 profiler 的场景中,可以用低成本实验建立初步判断。把粒子颜色改成常量,若耗时显著下降,纹理采样或材质计算可能是主因;把粒子半径减半,若耗时下降明显,fragment 覆盖面积和混合可能是主因;把粒子数量减半,若耗时近似减半,顶点或实例数据路径可能是主因;把 draw call 合批,若 CPU 时间下降而 GPU 时间变化小,提交开销可能是主因。

下面是一条可复用检查顺序:

  1. 固定场景和相机,让每次测试的视觉输入一致。
  2. 分离 CPU frame time 与 GPU frame time,判断限制发生在提交侧还是执行侧。
  3. 按 pass 查看耗时,把最长或增长最快的 pass 作为入口。
  4. 改变一个变量,例如粒子数量、屏幕半径、纹理采样数、draw call 数量或 render target 格式。
  5. 观察耗时随变量变化的方向,把症状归入算力、带宽、延迟、发散、同步、功耗或 API 提交。
  6. 再修改 shader、buffer layout、texture format、batching 或 pass 组织,验证推断。

这条顺序的意义在于先建立因果,再动优化。没有因果的优化经常把问题从一个资源通路转移到另一个资源通路,例如减少 ALU 后增加带宽,降低分辨率后引入 temporal artifact,合并 draw call 后增加无效 shader 分支。

1.5 如何在渲染引擎中识别目标 GPU 的能力

渲染引擎识别 GPU 能力,应从 adapter、device、feature bits、limits、format support 和 fallback path 六个对象开始。adapter 表示可用 GPU 或后端候选,device 表示应用实际创建出来的逻辑设备,feature bits 表示额外能力,limits 表示数量上限,format support 表示纹理和 render target 格式可用性,fallback path 表示能力不足时仍能产生正确画面的替代路径。

WebGPU 的能力查询可以作为跨平台例子。MDN 对 GPUAdapter.features 的说明明确指出:adapter 返回额外功能集合,某些底层硬件支持的功能在浏览器或系统约束下也可能不暴露;GPUAdapter.limits 则提供绑定数量、纹理尺寸、buffer 大小等上限信息。对应文档可见 GPUAdapter featuresGPUAdapter limits。引擎层面应把这些查询转成自己的 profile,而非在各处散落临时判断。

一个最小 WebGPU 查询示例如下。它展示的重点是查询顺序和 fallback 入口,代码省略了 canvas、pipeline 和错误 UI。

async function createParticleDevice() {
const adapter = await navigator.gpu?.requestAdapter({
powerPreference: "high-performance",
});

if (!adapter) {
return { kind: "fallback", reason: "no-webgpu-adapter" };
}

const requiredFeatures: GPUFeatureName[] = [];
if (adapter.features.has("texture-compression-bc")) {
requiredFeatures.push("texture-compression-bc");
}

const device = await adapter.requestDevice({ requiredFeatures });

return {
kind: "webgpu",
device,
limits: adapter.limits,
features: adapter.features,
};
}

这段代码回答三个工程问题。第一,当前运行环境是否能创建 WebGPU adapter;第二,纹理压缩这类可选能力是否可以进入高质量路径;第三,limits 是否允许粒子 buffer、bind group、texture size 和 storage buffer 使用当前配置。真实引擎会把这些结果写入 GpuProfileRenderDeviceCaps 或类似结构,再由资源系统、材质系统和 pass builder 统一读取。

在 Vulkan、Metal、Direct3D 和 OpenGL 中,查询对象名称不同,判断模型一致。Vulkan 使用 physical device properties、features、extensions 和 format properties;Metal 使用 MTLDevice、GPU family、feature set、pixel format 支持和 resource limits;Direct3D 使用 adapter、feature level、options 结构和 format support;OpenGL 使用版本、extension、limits 和 internal format 查询。跨 API 引擎应把这些差异收敛为几个稳定能力项,例如纹理压缩、采样数量、storage buffer、bindless/descriptor indexing、timestamp query、subgroup、ray tracing 和 mesh shader。

fallback path 是能力查询的终点。若设备没有某种压缩格式,材质系统应选择未压缩或另一种压缩格式;若 storage buffer 上限不足,粒子系统应降低批量或改用 texture buffer 类路径;若 timestamp query 不可用,性能面板应退回 CPU 侧粗粒度计时;若透明粒子在低功耗设备上超出预算,画质系统应降低粒子数量、半径或混合层数。fallback 的目标是保持画面正确、性能可控、错误可解释。

把硬件基础落到引擎实践,可以形成一条稳定路径:先查询目标 GPU 能力,再选择资源格式和 pipeline 变体;运行时用 pass 级计时和工具捕获验证瓶颈;最后把优化动作绑定到明确资源通路。这样,GPU 不再是一个抽象黑盒,而是由执行批次、计算单元、内存层级、专用管线、平台差异和 API 能力组成的可观察系统。

最小自检任务

给定一个粒子 pass:场景中有 200,000 个透明发光粒子,每个粒子使用一张小噪声纹理,粒子半径从 4 像素增大到 16 像素后,GPU frame time 从 4 ms 上升到 18 ms;把粒子数量减半后,GPU frame time 下降到 12 ms;把纹理采样改成常量颜色后,GPU frame time 下降到 9 ms。请判断最可能的瓶颈组合,并给出下一步检查顺序。

答案要点

最可能的瓶颈组合是 fragment 覆盖面积、透明混合带宽和纹理采样共同限制。半径从 4 像素增大到 16 像素时,屏幕覆盖面积按平方级增长,fragment 数量和混合写入压力快速上升;粒子数量减半后时间下降,说明顶点或实例读取也参与成本,但下降幅度小于半径变化带来的上升,主路径更偏向 fragment;纹理采样改成常量颜色后时间明显下降,说明 texture fetch、cache 或采样单元也参与。

下一步检查顺序应先固定相机和粒子分布,再分别记录 CPU frame time 与 GPU pass time。随后单独改变粒子半径、粒子数量、纹理采样数和 render target 格式,观察耗时随哪个变量增长最快。若工具可用,查看该 pass 的 fragment workload、texture throughput、blend/ROP 或 bandwidth 指标;若工具不可用,使用常量颜色、关闭透明混合、降低 render target 精度和减少 overdraw 的实验交叉验证。优化动作应优先减少屏幕覆盖、合并或裁剪不可见粒子、降低采样次数、选择合适压缩格式和控制透明层数。

本章知识点总结

  • 吞吐模型:GPU 通过大量并行线程和批量执行提高单位时间内处理的数据量。
  • 贯穿路径:一次 draw call 会经过命令队列、GPU 前端、计算单元、内存路径和输出写入。
  • 执行批次:warp、wave、subgroup 或 simdgroup 都表达一组线程被硬件批量调度的事实。
  • 执行 lane:lane 承担 shader 算术和逻辑指令,活跃线程不足会降低利用率。
  • 寄存器压力:寄存器使用量会影响同一计算单元可驻留的线程批次数。
  • 共享存储:threadgroup memory、LDS 或 shared memory 服务工作组内的数据复用。
  • 专用单元:纹理、混合、深度、矩阵和光追单元会让瓶颈出现在 ALU 之外。
  • 平台比较:NVIDIA、AMD 和 Apple 应按执行粒度、内存形态、工具证据和 API 暴露方式比较。
  • 统一内存:Apple Silicon 的统一内存改变数据交换形态,但带宽和同步仍要进入预算。
  • 瓶颈分类:算力、带宽、延迟、分支发散、同步、功耗和 API 提交都可能限制帧时间。
  • 变量实验:改变粒子数量、屏幕面积、采样数或 draw call 数,可以定位成本增长来源。
  • 能力查询:adapter、device、features、limits、format support 和 fallback path 共同决定渲染路径。
  • Fallback path:能力不足时,引擎应选择正确可运行的替代资源、格式、pass 或画质等级。
  • 工程判断:GPU 硬件基础最终服务于 pass 级定位、资源路径解释和可验证优化。