说实话,我接触GPU也有不少年头了。最早是打游戏追求高帧率,后来做实时渲染研究,再后来身边搞AI的朋友天天在服务器上把GPU Load拉到百分之百,可你问他:显卡里那一大坨芯片到底是怎么干活的?它凭什么一秒钟能算出几千万个像素的颜色?大部分人能答上来的也就是"并行嘛,很多核嘛"这六个字。
这也正常。GPU对绝大多数人来说就是个黑盒子,你喂它顶点数据、纹理参数,它吐给你一帧画面,过程快到你根本来不及想。但如果你真的钻进去,会发现这里面的设计思路和CPU完全是两个物种。这篇文章我打算用系列的形式,把GPU芯片内部的图形计算这件事掰开揉碎讲清楚。第一篇我们先解决两个最基础的问题:GPU里最小的计算单位到底是什么,以及一帧画面从CPU下发指令到最终显示到屏幕上,中间到底走了怎样一条流水线。
1. 渲染一帧画面有多"重":GPU为什么被设计成现在这样
1.1 一次画面渲染到底要算多少东西
很多人低估了实时图形渲染的计算量。咱们先算一笔账:一块普通的1080p屏幕,分辨率是1920乘1080,大约207万个像素。每个像素在屏幕上显示出来之前,要经过至少一次像素着色器计算,而像素着色器内部要做纹理采样、光照计算、颜色混合,有些复杂的PBR材质甚至要做多次采样和几十次浮点运算。
207万乘以几十次,这是单帧的像素阶段计算量。再加上场景里的模型顶点,一个中等规模的角色模型可能有10万到几十万个三角形,每个三角形都有顶点,顶点要经过变换、裁剪、光照计算。一个战斗大场景,几百个模型下来,上千万个三角形一点都不夸张。
这还只是一帧。显示器一秒要刷新60次,也就是一秒钟要重复上面这个流程60遍。你想想这个数字有多吓人:每秒需要完成几亿到几十亿次图形相关的浮点运算。CPU那颗核只有几个到几十个核心,主频再高也扛不住这种吞吐量。这决定了GPU必须走一条和CPU截然不同的路:用巨量的小核心并行堆算力,而不是用少数几个大核心硬卷单线程性能。
我用一个生活化的类比:CPU像是一个做饭特别快的厨师,一个人能同时炒三个菜还能兼顾摆盘;GPU则像是一整个快餐连锁后厨,几百个厨子分工明确,每人只负责自己那一小份,但单位时间内出餐的总量能吊打任何单兵。图形计算恰好就是这种可以被切成无数独立小任务的工作,天生适合后者。
1.2 图形专用架构的三块基石
聊到GPU芯片设计,绕不开三个基础模块:执行浮点运算的ALU阵列、海量显存带宽、以及一堆"固定功能单元"。
ALU阵列就是那一大排做加法和乘法的算术单元。CPU里有算术逻辑单元,但GPU里这种单元的密度要高得多。芯片面积就这么大,晶体管数量也就这么些,NVIDIA和AMD的设计哲学是明确偏科:把绝大部分晶体管花在ALU上,而不是花在分支预测、乱序执行这些让单个线程跑得更快的东西上。因为在图形渲染里,你不需要单个任务跑得飞快,你需要的是总吞吐量足够大。
并且图形渲染每个像素的计算彼此独立,像素A算成什么样完全不影响像素B,这就是传说中的"数据并行"。ALU阵列正好批量处理这种任务:每个ALU负责一个像素或一个顶点的计算,一颗GPU里几千上万个ALU同时开工,画面就"唰"地一下出来了。
第二个基石是显存带宽。GPU的算力非常恐怖,但算力再高,数据喂不进去也是白搭。像素着色器每算一个像素都要去采样纹理,纹理数据存在显存里,显存和计算单元之间的通路越宽,数据搬运就越快。所以你看现在游戏显卡的显存位宽动辄256bit、384bit,显存频率也越来越高,都是为了确保纹理数据能大量、快速地送到计算单元嘴边。用大白话说,ALU是"灶台",显存带宽是"送菜通道",通道窄了,再多的灶台也得空等。
第三个基石,固定功能单元。GPU芯片里不全是能执行任意指令的通用计算单元,还有一大块专用电路。比如光栅化阶段要判断一个三角形覆盖了哪些像素,这种操作几乎是每帧每帧重复的,没必要用通用ALU慢慢算,直接做成专用硬件电路,输入三角形顶点数据,输出覆盖的像素列表,速度和功耗都更优。纹理采样也有专门的纹理单元,深度测试有专门的深度测试硬件。这些固定功能单元加上可编程的着色器阵列,一起构成了我们常说的图形流水线。
这三个模块各司其职,就是一个GPU做图形计算的基本盘。接下来我们钻到芯片里面,看看那个"最小的计算单位"到底长什么样。
2. 芯片里的最小计算单位:从线程到SM的层层拆解
2.1 "线程"不是你想的那个线程,是硬件线程
你在GPU上写一个像素着色器,每个像素执行一次,一个1080p的画面就有207万个"像素任务"。这些任务在GPU眼里就是一个个线程——注意,这个线程和你在操作系统里听到的CPU线程完全是两码事。操作系统线程有栈、有上下文、切换起来要保存恢复一大堆状态,开销很大。GPU线程则极其轻量,基本上就是一组寄存器和一条指令指针,芯片里同时驻留着几万个这样的线程都毫无压力。
GPU并不是每来一个线程就分配给一个ALU去执行。真正的执行粒度是"线程束",NVIDIA管它叫warp,一个warp固定包含32个线程。AMD那边也有类似概念,叫wave32或wave64,意思是32或64个线程捆成一束。
这里有个关键设计:GPU执行指令时,一个warp里的32个线程是一起执行同一条机器指令的。什么意思呢?比如像素着色器里有一行"采样纹理",那么这32个线程会同时去执行"采样纹理"这条指令,只是每个线程操作自己那份数据。32个人拿着同一本菜谱做菜,只是每人手里的食材不同。这种模式叫SIMT,单指令多线程。
为什么偏偏是32而不是8或者64?这是NVIDIA在指令宽度、调度延迟、寄存器压力之间权衡很久的结果。32个线程执行一条指令,硬件正好把它做成一个SIMD宽度为32的指令来处理,效率高;同时32个线程需要的寄存器数量也在合理范围内。你不需要记住每个细节,但需要理解一点:GPU调度和派发工作的最小单位是warp,而warp里的单个线程,才是逻辑上最小的执行单位。
2.2 SM:GPU里的"小芯片",真正的计算工厂
如果把线程比作"零件",那么真正组装零件的车间就是流式多处理器SM(Streaming Multiprocessor)。NVIDIA的GPU核心,从图灵到Ada Lovelace架构,一颗芯片里集成着几十到上百个SM。每个SM内部都有自己的ALU阵列、寄存器文件、共享内存、纹理单元、光栅处理单元、warp调度器。
用一个具体的芯片举例,RTX 4090用的是AD102核心,里面有128个SM,每个SM包含128个FP32浮点计算单元,加起来就是16384个"核心"。市面上宣传的"多少多少CUDA核心"就是这么来的,本质上是SM内的浮点ALU总数。
SM的内部结构很有意思。每个SM分成几个子分区(最常见是4个),每个子分区都有自己独立的调度器。调度器负责给warp发指令:这个warp的指令准备好了,就给ALU阵列发出去执行;下一个warp再跟上。因为ALU数量远少于驻留的线程数,所以每个warp都是排队等着被调度执行的。SM内还配了不小的寄存器文件和共享内存,前者负责给线程暂存计算结果,后者是同一SM内线程协作时快速交换数据的地方。
你可能会问,这么多SM拼在一起就是一颗GPU,那它们的任务怎么分配?这就靠前面提到的那套"固定功能单元"和前端的任务分发硬件。图形渲染时,GPU前端(Graphics Frontend)接收到CPU下发的绘制命令,把顶点和像素的分块任务分配给各个SM去处理。所以,SM的个数、每个SM的ALU数量,直接决定了这颗GPU吞吐量的上限。
2.3 为什么要塞那么多线程在芯片里:隐藏延迟是核心
有个问题不知道你注意到没有:计算单元的数量是几千个,但芯片里驻留的线程通常有几万甚至几十万个,为什么非要多出来这么多"排队"的?
答案都在"延迟隐藏"这四个字上。显存的延迟是很高的,一次内存访问可能要几百纳秒。在这几百纳秒里,ALU如果干等着数据回来,那就纯属浪费。而GPU的调度器有一个绝活:一个warp在等显存数据时,调度器立刻切换到另一个指令已经准备好的warp去执行,切换开销接近于零,因为无非就是换一组寄存器读取。
你可以把这个过程想象成点菜。一个顾客(warp)点了菜,在等菜的时候,服务员(调度器)不会干站着,而是立刻去服务另一个已经想好吃什么的顾客。只要餐厅里同时坐着足够多的顾客,服务员和厨师就能一直保持忙碌。GPU中"同时坐在餐厅里的顾客"的数量,就是所谓的"occupancy",占用率。占用率越高,ALU越不容易因为等数据而空转。这也是为什么GPU的线程数一定要远超核心数,多出来的部分不是为了并发,而是为了"填坑"。
理解了SM、warp和延迟隐藏,你就知道一颗GPU的计算核心部分大概是怎么回事了。但是,光有这些计算单元还不足以画出画面,它还需要一整条配合默契的生产线来把顶点一步步变成像素。这就是下一节要说的完整图形流水线。
3. 完整图形流水线:一帧画面从指令到像素的旅程
3.1 第一步:CPU怎么把"画什么"交给GPU
图形渲染并不是GPU自己拍脑袋决定的,一切源于CPU上跑的游戏或应用。程序通过图形API——DirectX 12也好,Vulkan也好,往驱动里提交一组绘图命令,每条命令就是"我要用一个什么样的着色器、绑定哪些资源、画哪些顶点"。驱动会把这些命令翻译成GPU能懂的格式,放进一块命令缓冲区,最后通过一个提交指令推送给GPU前端的命令处理器。
命令处理器(Command Processor / Frontend)收到命令后,会把不同阶段的子任务"喂"给不同的硬件单元。这里有个点值得多说一句:CPU和GPU之间是异步的,CPU提交命令不等GPU执行完就继续往下准备下一帧了,这也是"帧延迟"的产生原因之一。你在游戏里调低画质,帧数变高,但操作手感总觉得有点"肉",就跟这条命令提交队列的流水长度有关系。
对GPU来说,接下来真正干活的起点,是"输入装配器"(Input Assembler)。它根据绘图命令里的描述,从显存中取出顶点缓冲、索引缓冲的数据,把它们装配成一个一个的三角形,准备交给下一个阶段。
3.2 顶点阶段:让每个三角形站到该站的位置
三角形装配完,顶点就要进可编程的顶点着色器(Vertex Shader)了。这是图形流水线里第一个可编程阶段,也是最近接几何层面的一步。
顶点着色器干的事情非常明确:把模型的本地坐标变换到世界坐标、相机坐标,最后投影到屏幕坐标。这个过程中还会做顶点颜色计算、法线变换,如果是骨骼动画,还会根据骨骼权重把顶点蒙皮到骨骼上。每个顶点都被当作一个独立线程来处理,所以大量顶点的变换完全是并行的。我曾经做过一个场景,里面有一万多棵树,每棵树几千个三角形,如果没有GPU把顶点变换并行掉,CPU算一帧能卡到怀疑人生。
顶点着色器输出的是经过变换的顶点坐标和一系列附加属性(颜色、法线、纹理坐标)。紧接着,固定功能的"图元装配"和"裁剪"阶段会接手:把顶点按拓扑结构连成三角形,再判断哪些三角形完全在屏幕可见范围之外,直接裁掉,省得后面白算。
这里有个值得一提的可选阶段:曲面细分和几何着色器。曲面细分用于把一个粗糙的低模网格细分成高密度网格,让轮廓更圆润;几何着色器则可以对整个三角形做增删改,比如把线段扩展成草地叶片。这两个阶段在现代引擎里用得越来越少了,主要原因在于它们和后续的光栅化耦合太紧,灵活性差,调度开销也不小。现在大家更愿意在计算着色器里自己搞一套,或者直接改用后面的Mesh Shader那一套任务网格方案。但理解基础流水线时,知道这些阶段存在即可。
3.3 光栅化:三角形是怎么"碎"成像素的
顶点阶段结束时,一个三角形的三个顶点已经都在屏幕坐标空间里了。但屏幕上显示的是一块块像素,不是一个漂亮的线框三角形。这中间最关键的一步,就是光栅化(Rasterization):把连续几何的三角形,离散成屏幕上一颗颗被它覆盖的像素。
很多初学者会以为光栅化就是"找到三角形内的像素,填个色",但工程实现上这步极其讲究效率。传统做法是扫描线算法,一行一行地计算三角形覆盖的边界;现代GPU则偏好用"边函数"(Edge Function)做覆盖测试。简单理解就是,对每一颗像素,用三角形三条边的方向判断它在三角形内部还是外部,三边内外判断都通过,这颗像素就算被覆盖了。
硬件里专门有一套三角形设置(Triangle Setup)和三角形遍历(Triangle Traversal)电路来做这件事,效率极高。光栅化阶段还会顺带做属性的重心坐标插值——顶点有着色需要的纹理坐标、法线、深度值,但三角形内部的像素并没有直接对应的数值,怎么办?用重心坐标把三个顶点的属性按比例插值出来。这就是为什么一个三角形的颜色可以是渐变过渡的。而这些插值出来的数值,会作为输入交给下一步的像素着色器。
3.4 像素阶段:每一颗像素的"上色时刻"
像素着色器(Pixel Shader / Fragment Shader)是整个流水线里工作量最大的阶段。每个被光栅化的像素,都要执行一遍像素着色器里的代码:从纹理里采样颜色,结合光照方向算漫反射、高光,再做阴影、环境遮挡、反射折射等各种效果。算到这一步,像素才真正拥有最终显示在屏幕上的颜色。
像素着色器的计算量和着色器里纹理采样、ALU指令的数量直接相关。我之前做过一个水面渲染效果,每个像素要做三次反射采样、一次折射采样、四次深度相关的噪声采样,再加上一堆PBR计算,同分辨率下帧率和简单光照场景能差出一倍还多。
但像素着色器有个很关键的优化你没注意到:它未必会在每一个像素上都执行一次。硬件在做像素着色前会先做一次"提前深度测试"(Early-Z)。如果当前像素的深度值比深度缓冲里已有的值更靠后,说明它已经被前面的物体挡住了,那后面的像素着色器根本不需要执行,直接丢弃。这一步能省下巨大的计算量,尤其是渲染复杂场景时,很多时候场景里大部分像素是被遮挡的。早期显卡没有这个机制,像素着色器哪怕算完也会被覆盖,白白浪费性能。你现在看一些渲染性能分析报告里讲"overdraw"(过绘制),指的就是这个阶段没能提前剔除掉的无用着色计算。
3.5 输出合并与帧缓冲:像素的"出厂质检"和"装车"
像素着色器算出的颜色并不是直接写到屏幕上的。GPU会把计算结果送到输出合并单元(ROP,Render Output Unit),在这里做最后一轮固定功能处理:深度/模板测试、透明度混合、多重采样抗锯齿的解析等。这些操作没有可编程的灵活性,但其专用电路的处理速度非常快。
在这个阶段,总免不了两个概念:缓冲和同步。画面渲染不会直接画到显示器正在扫描的帧缓冲上,而是画到一个后台缓冲,画完后再把前后台缓冲交换一下(双缓冲)。如果开了垂直同步,交换动作要等显示器刷新信号对齐,避免画面撕裂。如果帧率不够,垂直同步会把显示帧率钉在30FPS——这个很多人应该都遇到过,画面不撕裂了,但流畅度掉一半,就是这几块缓冲之间同步关系决定的。
到这一步,一帧画面的像素数据经过顶点变换、光栅化、像素着色、输出合并,终于写进了可以提交给显示器的帧缓冲里。显示器每隔一个刷新周期把这块缓冲读出来,就是你屏幕上看到的那一帧流畅画面。
4. 光栅化到底是怎么"扫"出像素的:一个简化示例
4.1 边函数判断三角形覆盖的数学原理
上面说了光栅化的概念,可能还有人觉得不过瘾。我就拿像素覆盖检测这一个细节展开讲讲,这也是很多引擎开发新手最容易忽视的数学细节。
想象屏幕上有一个三角形,三个顶点分别是v0、v1、v2。我们要判断某个像素点p是否落在三角形内部。数学上有个很优雅的办法:计算p相对于三角形三条边的"有向距离",也就是用叉积判断点在边的哪一侧。对边v0到v1来说,p的边函数值可以简化成:
edge(p) = (p.x - v0.x) * (v1.y - v0.y) - (p.y - v0.y) * (v1.x - v0.x)如果edge(p) >= 0,说明p在这条边的某一侧(具体哪一侧取决于三角形顶点是顺时针还是逆时针排列,以及约定)。对三条边都做一次判断,如果三次结果都符合约定,p就在三角形内部。
实际硬件做这事的时候会聪明得多。光栅化器会先计算出三角形外接矩形,只在这个小范围内遍历像素,再对每个像素做三边判断。而且因为边函数是线性函数,它还能用增量算法加速:检查完左边像素,再检查右边相邻像素时,只需要做一次加法而不是重新做一遍完整的叉积计算。这些细节听着琐碎,但GPU每秒钟要处理几千万个三角形,任何一步省下来的功耗和晶体管,在几十亿次重复下都是惊人的收益。
4.2 穷举判断看起来笨,为什么反而是最优解
你可能想问:为什么要用这种"遍历包围盒内每个像素做判断"的笨办法,而不是用扫描线那种几何计算更少的方式?早期显卡确实用扫描线,但后来硬件光栅化主流转到了基于edge function的遍历法。
原因很实际:edge function遍历法的硬件实现非常规整、方便并行。处理一个bbox里的所有像素,完全不需要考虑三角形的几何形状,只需要做一个又一个独立的、互不依赖的判断。这让光栅化器可以被拆成无数个小处理单元并行跑,32颗像素并行判断和1颗像素顺序判断的吞吐量天差地别。扫描线算法则需要大量串行的边界计算,并行度上不去。
所以在图形渲染这条路上,你会发现一个贯穿始终的思想:很多看似"浪费算力"的做法,恰恰因为简单、规整、可并行,而成为硬件的最优解。宁可多算几颗不在三角形里的像素,也要保证ALU始终在满负荷工作。这个思路从光栅化一路延伸到整颗GPU的设计哲学,理解了它,你再看渲染器为什么喜欢把任务切成一大块一大块均匀分配给所有SM,就会觉得很自然。
5. 实操者视角:怎么判断GPU在流水线哪一步卡住了
5.1 看远不止"GPU Load百分之多少"这么简单
写完了原理,说点实操层面的东西。很多人拿到一台机器,看性能第一反应是开任务管理器看GPU占用率,但那个数字很骗人。GPU Load高只说明某个引擎在忙,不代表它忙对了地方;GPU Load低也不代表"显卡不行",有可能是整个图形流水线在某个更早的环节被卡住了。
我自己做性能排查时,常用的工具组合是这些:
- GPU-Z:看传感器里的GPU Load、显存占用、显存带宽占用比率、温度功耗。重点是"显存带宽占用"这个指标,如果带宽占用很高而GPU Load没那么高,基本能断定是带宽受限而不是计算受限。
- nvidia-smi:被动看时间的工具,适合在AI推理训练场景里快速确认显存、算力占用,但对图形性能分析帮助有限,它看不到每个渲染阶段的具体时间。
- RenderDoc / Nsight Graphics:这个级别是真正做图形调试的工具。RenderDoc可以抓取一帧画面,逐Draw Call查看每个阶段的顶点输入、着色器执行、输出颜色,能直观看到哪些像素被Early-Z剔除了,哪个Draw Call的像素着色器耗时最长。Nsight Graphics则有更详细的硬件计数器,能看每一渲染阶段的cycle级时间分布。
5.2 "降低分辨率帧率没变"——这是经典瓶颈信号
性能分析里最经典的定位法,就是控制变量法。你怀疑像素着色器是瓶颈,那就把分辨率从4K降到1080p。如果帧率几乎不变,说明你的瓶颈根本不在像素阶段——大概率是顶点阶段或者CPU侧的Draw Call提交卡住了。如果帧率大幅上涨,那就是像素阶段或者填充率、带宽受限,因为分辨率变化直接影响的是像素数量和纹理采样量。
另一个我常用的手段是单独关掉某个后处理效果。比如关掉动态模糊或环境光遮蔽,帧率回升巨大,那就锁定了这个效果对应的shader是整个管线的"大头"。再去看这个shader对应的纹理采样次数和ALU指令数,就能判断是该优化算法,还是该减少采样量。很多人一碰到GPU性能问题就想着换显卡,其实很多时候你只要把某个shader里的半精度浮点换回全精度,或者把几个纹理采样合并一下,帧率就能救回来。
5.3 别被"GPU Load 99%"骗了,帧生成时间才靠谱
还有一个常见的坑,就是只看平均帧率,不看帧生成时间曲线。你可以在游戏里打开帧时间显示,或者用FrameView、PresentMon这类工具记录每一帧的实际生成时间。如果帧生成时间曲线是锯齿状,尖峰忽高忽低,说明周期性卡顿,往往是着色器编译、资源加载、或者GPU任务队列里某一步出现了瞬间过载。这种情况平均帧率甚至能到100帧,但玩起来一卡一卡的,非常劝退。
遇到这种问题,我一般先看一眼是不是shader编译卡顿,尤其是用DirectX 12和Vulkan的游戏,驱动会在首次遇到某类着色器时才去编译,导致场景切换瞬间掉帧。很多"新关卡一进去卡一下"的体验就是这个问题。解决方案要么是让驱动提前做管线缓存预编译,要么在加载界面里强制引导编译。这些细节,比盲目换硬件更值得优先排查。
6. 常见问题速查:图形流水线高频坑一览
下面这个表是我在实际项目里比较高频遇到的症状和排查方向,列出来给各位抄作业用:
| 症状 | 最可能的瓶颈阶段 | 优先排查的方向 |
|---|---|---|
| 分辨率调高后帧率明显掉 | 像素着色器/填充率/带宽 | 看显存带宽占用是否接近上限,降低抗锯齿等级 |
| 分辨率怎么改帧率都没太大变化 | 顶点阶段或CPU提交环节 | 监控CPU单核占用,减少Draw Call,考虑合批 |
| GPU Load显示99%但画面还是卡 | 流水线内某阶段长期饱和 | 用Nsight Graphics按阶段看耗时占比,定位具体环节 |
| 帧生成时间曲线锯齿严重 | 着色器编译/资源流加载 | 开启管线缓存预编译,检查是否有异步资源加载缺失 |
| 显存占用飙高但频率下降 | 显存溢出与数据换页 | 降低纹理压缩格式,检查是否有资源重复绑定在显存里 |
| 开了垂直同步依然画面撕裂 | 缓冲交换与刷新率未对齐 | 确认是否开启了混合帧率缓冲模式,尝试三缓冲配合G-Sync/FreeSync |
再补充一个我用得比较多的经验:如果你发现某个场景帧率奇低但GPU Load反而正常,多半是场景里某个后期特效触发了深度缓冲的额外读写路径。深度缓冲是整条流水线里被多个阶段反复读写的"公共资源",一旦某个特效需要把深度缓冲当纹理来用,它往往要先把整块深度复制一份,这个拷贝开销经常被低估。这类问题在RenderDoc里非常好观察,打开资源视图看看深度缓冲有没有被额外绑定到着色器就能确认。
还有一点,PC端各种奇葩环境带来的问题也很多。比如我遇到过一台机器,显卡驱动更新到最新版之后反而出现周期性掉帧,最后回退到两个版本之前的驱动才恢复正常。做图形相关的工作或调试,驱动版本的选择是个很玄学的事情,并不总是越新越好。我的习惯是锁定一个经过验证的稳定版本,除非新驱动有明确的功能性更新(针对特定游戏的优化、修复某个Bug),否则不轻易动它。
7. 结尾:这还只是第一层
把最小计算单位和图形流水线串起来看,你会发现GPU的图形计算其实是一套非常讲究的"分工流水线":前端接收命令,SM里的warp负责顶点和像素的可编程计算,固定功能单元负责光栅化、深度测试、输出合并,最后把像素数据写进帧缓冲。所有环节各司其职,环环相扣,哪一环慢了,整条流水线就会在那里等着,而整颗芯片的每一个SM又被设计了极高的占用率来尽量填满每一刻的等待空隙。
从我个人做性能分析的经验来看,理解这套流水线最大的价值,是它给了你一个排查问题的坐标系:当帧率出问题时,你能大致判断问题发生在哪个环节,再对症下药。这一篇把大框架铺开了,但GPU内部还有很多非常值得钻的细节,比如warp调度是如何影响执行效率的、显存带宽到底怎么计算和优化、现代GPU里的Mesh Shader又怎么改变了这条传统流水线。这些内容篇幅都不短,下一篇我会继续挑几个方向往里挖,如果你在实操中有遇到过跟这篇文章相关的问题,也欢迎一起交流。