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

对学图形有益的一些GPU硬件知识(从硬件角度看图形渲染做 ...

[复制链接]
发表于 2022-3-7 14:25 | 显示全部楼层 |阅读模式
(欢迎大神来斧正)
GPU硬件架构图



1、显存:(高带宽高延迟的设计原则)对于集成显卡:cpu会专门划分一块内存空间给显卡用,注意:CPU个GPU对于该块物理空间具有不同的逻辑寻址。
对独立显卡来说,GPU可以使用专门的显存条,CPU对于这部分物理显存有单独的逻辑地址空间,逻辑地址的获取一般由API完成。(一般也就256MB或者512MB)
CPU和GPU的交流:必然要经过数据总线,另外对于独立显卡存在一个DMA异步中继两者的指令,每当一次沟通结束,DMA都会触发一次CPU中断。
2、SP:
流处理器,也叫核心,也叫SPU。顶点处理单元和像素处理单元的统一
3、4D+1D/4D/1D:
nD单元指的是n个流处理单元整合成的n维浮点向量运算单元。(这也是N 和A的区别:N是全1D,A是4D+1D)
4.SM:
就是流多重处理器,包含:SP单元(16/32/64)、
   共享内存、
   特殊函数单元(主要进行三角函数,对数运算等)、
   寄存器(空间一般设计的很大128kb,所有sp共享)、
   多边形引擎PolygenEngine(实现顶点拉取、曲面细分、裁剪、光栅化等渲染流水线中的固定步骤)、
   指令缓存、
   L1缓存(一开始用于储存顶点数据,顶点处理阶段结束之后。SM将数据发送到SM外的l2缓存,多边形将在SM外进行光栅化,然后将生成的片元重新分发到SM中存在l1中,这时l1中存储的就是片元数据了)。
   工作流程:由warpscheduler(束管理器)驱动,WS将指令移交给DispatchUnit(指令分派单元)。DU随后会从指令缓存中读取新的指令,一次性向一个束的所有SP发送同一个指令,所有SP一起开始执行这条指令(当然参数可以由DU分配不同的值)
5.显卡中关于数据存储单元的种类和储存结构(从存储速度快到慢)
    寄存器:访问速度:1个时间周期。SP运行时可以随意读和写。每个SP运行时获得自己的寄存器空间。
    共享内存:访问速度:<=32个时钟周期。被SM中的所有SP共用。主要存放材质参数,光照。摄像机等常量数据
    L1缓存:同上,但重点存放顶点和片元数据。
    L2缓存:32 ~ 64 个时钟周期。主要用于和L1配合实现顶点和片元数据的交换。一般大量SM共用同一个L2缓存,l2缓存吞吐量是数个l1的总和。
    纹理缓存:>= 400 时钟周期,显存的直接缓存,如果出现未命中,再次访问的延迟可能在1000时钟周期以上。
    常量缓存:同上
    全局内存(显存):>=500时钟周期。就是平常说的显存,数据来自CPU总线,放一些数据比如纹理数据。显卡驱动在GPU流水线空闲时将任务数据从GPU缓冲区导入显存。
6.GPU硬件设计上的阻塞与性能瓶颈:
    GPU的阻塞主要来源并非跳转而是来自显存的读取。由于纹理数据存在显存中,每次遇到采样语句,为了将显存中的数据调入核心,可能需要使处理器阻塞几百上千个时钟周期。
    GPU的换入换出策略:
SM中的寄存器可以备份若干份SP的执行状态。每当有一批片元在等待采样纹理数据时,可以将此时SP组的上下文备份保存在寄存器中,然后导入下一批片元进行处理,这个操作被称为换出(swap-out)。换出使得核心不需要空等采样结果,可以继续执行更多的片元。

7.GPU的并行策略:
   一个着色器的最小执行单位是线程。多个线程被打包为一个线程束(就是上面说的wrap)。多个线程束,被打包为一个线程组(block),每个线程组的所有线程通过共享内存通信,但不是同一个线程组的线程无法通行。一个SM负责一个线程组。举例:现在100个线程打包为一个线程组交给一个SM处理,一个SM里有32个SP。那么该线程组将分割为0 - 31号为第一束,32 ~ 63 是第二束, 64 ~ 95 是第三束,96 ~100 是第四束。运行时先把第一束的32个线程送入sm处理,这一束的32个线程被交给32个sp分别处理,他们是并行的。然后依据换入换出策略再加上GPU的任务执行大多是相似的,这一束的线程基本都会在同一时间完成处理,然后就把这一束和下一束来个换入换出,安排下一束进入SM。
8、软件设计里的动态分支语句(ifelse, while,for……)如何影响GPU并行效率:
   SM中的线程束用的是同一份指令,对于分支语句,只要有一个线程执行另一个分支,整个线程束就不得不执行两遍,将两个分支的结果都运行一次,并且让每个线程扔掉各自不需要的结果。如果存在连续的分支预测,复杂的循环,那么线程束的执行次数可能是指数级增长。
9、垂直同步与所谓画面撕裂:
   GPU往往采用双缓冲机制,由视频调度器(video controller)交换前后缓冲,以便画面显示和后端渲染的分离。这会有一个问题:在视频控制器读取前缓冲区只读取了一半时,GPU将新的一帧内容提交到帧缓冲区,发送信号让视频调度器把两个缓冲区交换。此时、视频控制器可能把新一帧数据的后半段渲染到了屏幕上,这使得屏幕得上半部分和下半部分分属不同得两帧,产生画面撕裂。
垂直同步就是:GPU等待显示器发送回垂直同步信号(该信号指示这一帧已经在屏幕上全部显示完),再让视频调度器交换前后缓冲。
10、从硬件上看DrawCall的流程:
第一步,CPU把一个网格的顶点数据从硬盘中加载到内存中。
第二步,CPU对这个网格设置渲染状态(每个网格不等于每个模型/图片,因为存在批处理)。所谓渲染状态包括纹理贴图、材质属性和被编译为二进制文件的着色器。随着渲染状态一起被传递到GPU的还有光照和摄像机相关的信息。图形API可以更深层次的定义渲染状态需要的数据。
第三步,CPU将网格顶点数据与渲染状态打包,将数据包按照指定格式交给DMA,由DMA将数据包传入显卡。
显存完成接受数据包后,CPU会收到一个中断信号,至此一次Draw Call正式结束。
11.GPU接受DrawCall的流程:
绘制指令(例如opengl中的glDrawElements在cpu被执行后),显卡驱动首先检查指令合法性,通过后将指令放入GPU缓冲,一段时间后当显卡存在空闲流水线或者CPU显式发送flush命令,驱动程序把缓冲区的一份指令发送给GPU,GPU接受指令开始处理命令。
根据命令,GPU将顶点缓冲区(Vertex Buffer)中的顶点取出【已改为评论第一条描述:一般vertex 和其他属性(比如normal和texcoord) 是提前放到buffer 中的,并不是drawCall 接收到后再放到vertexBuffer中。】,图元分配器(Primitive Distributer)开始通过顶点生成三角形,并分批次(batch),发送给一个或多个GPC(就是多个SM的集合,有的显卡有的没有),如果显卡不存在GPC,则直接分发给SM。SM获得数据后,束管理器安排多边形引擎(Polygon Engine)将三角形数据提取出来存入SM的L1缓存,随后开始顶点着色器阶段。
GPU一次处理缓存中的每一批网格,网格处理顺序与CPU的提交顺序有关。正因如此,透明物的绘制教程中,CPU最后提交透明物体,透明物就一定能最后渲染。
等一帧中所有DrawCall处理结束,显示器才会将图像打印在屏幕上。
12、批处理(一次Drawcall中直接提交多个绘制对象)的条件以及为什么进行批处理:
      对2D物体(UI)来说,如果两个或多个UI元素符合以下条件则可以被批处理:
           使用相同的材质与纹理,即使用相同渲染状态。
           这些元素的层级相同,或这些元素之间不夹杂使用其它渲染状态的元素的层级。
     对网格(模型)来说,如果两个或多个网格符合以下条件则可以被批处理:
           使用相同的材质与纹理,即使用相同渲染状态。
           顶点总数不超过一个阈值,该阈值大小与使用的处理引擎有关。
13.为何要进行批处理:
   Draw Call的性能瓶颈是CPU而非GPU。CPU每进行一次Draw Call,都要调用一次DMA(就是上面说的GPU CPU通信的“中继器”)将数据输入显存。在每次调用时,对显存的映射寻址、DMA控制块的注入、等待DMA响应等系统消耗都会浪费时间周期,多次进行Draw Call就会有多次消耗。同时,DMA擅长一次传输大量数据(显存的设计是高带宽高延迟),而不擅长多次传输少量数据。这使得降低Draw Call对于优化显示性能很有必要。

14、渲染流水线在GPU中的执行过程:
   渲染流水线是在显存中开启的。CPU将网格、材质、贴图、着色器等注入显存后,GPU开始渲染流水线。
   渲染流水线分为几何阶段和光栅化阶段(也被称为像素阶段),并最终将运算结果送到显示器的缓冲区中。
   几何阶段分为顶点着色器->曲面细分着色器(DirectX11和OpenGL4.x以上可编程)->几何着色器->裁剪->透视除法标准化设备坐标->屏幕映射五个步骤。
   光栅化阶段分为三角形设置->三角形遍历->片元着色器->逐片元操作四个步骤。

15、几何阶段:顶点着色的工作流程:
    束管理器将顶点数据存入寄存器,将顶点着色器代码指令存入指令缓存》》指令分配单元从指令缓存读取指令分配给SP进行顶点数据的运算》》顶点着色器的运算结果存入L1缓存》》GPU将多个SM的结果一起放入SM结构外的l2缓存。

16、几何阶段:曲面细分着色的工作流程:( 老旧的API版本中曲面细分着色器由硬件实现无法编程。在DirectX11以上或OpenGL4.x以上的版本中,可以通过API编辑这个着色器,可选
显卡上的视口变换器(viewport Transform)提供它的硬件实现。
视口变换器访问L2缓存与显存》》运行曲面细分着色代码指令》》曲面细分着色器可以递归的增加网格细度,并在细分后的顶点上生成插值色彩。由于它处理了邻接顶点的信息,它处理的结果会更加平滑,而不会产生跳跃间隙。
17、几何阶段:几何着色的工作流程(同上,可选):
视口变换器访问L2缓存与显存》》运行几何代码指令》》也可实现细分曲面。
几何着色器用于实现逐三角形的操作。由于相比顶点或片元数量,三角形数量并不多,所以这一步并没有分派到多SM中实现。几何着色器在投影的接受上十分重要,它还能实现扩展几何图形和绘制简单的粒子。
18、几何阶段:裁剪的工作流程:(完全不可编辑)
也由视口变换器实现,但它完全不可编辑。
GPU会将完全留在视野范围内的三角形保留,将完全在视野范围外的三角形抛弃,将部分留在视野范围内的三角形修正为新的几何体,即生成新的顶点,抛弃原来在视野外的顶点。
在裁剪结束后,会将齐次裁剪空间坐标转化为NDC坐标。
19、几何阶段:屏幕映射:
视口变换器负责,将NDC中的坐标转换到屏幕坐标系。 这个阶段将输出屏幕坐标系下的顶点坐标、顶点深度、顶点法线方向……等等。

20、光栅化阶段:三角形设置
视口变换器负责,将顶点坐标转为像素坐标,进一步获得顶点围成的三角形的像素坐标。将这些三角形以及其它关联信息一起打包》》移交给ROP(Raster Operations Units, 即图中ROP,光栅化引擎)。
三角形信息的处理策略:处理过某一批顶点的SM尽量处理同一批顶点生成的片元。
21、光栅化阶段:三角形遍历
ROP遍历三角形,生成片元》》将片元分配回SM
ROP中的元件ROPU并行地计算像素是否被三角形覆盖,如果是,则产生一个片元(fragment),其中片元是对3个顶点的信息进行插值得到。ROP不可编程。
在生成片元的同时,ROP还会同时进行裁剪、背面剔除和早期深度剔除。这几个操作是可以通过API进行配置的,但不可编程。
片元还不是一个像素(pixel),一个片元是用于生成一个像素的数据包,它包含了坐标、颜色、深度、法线、导数和纹理坐标等一系列计算像素所需要的数据。而像素则是片元经过整个光栅化阶段后,由片元所含的数据计算得出的,仅包含坐标和颜色信息。

22、光栅化阶段:片元着色器(此阶段有可能产生OverDraw)
束管理器将片元数据存入寄存器,片元着色器代码存储指令缓存》》指令分配单元从指令缓存读取指令分配给SP进行片元数据的运算》》片元着色的运算结果存入L1缓存》》GPU将多个SM的结果一起放入SM结构外的l2缓存。
注意:SM处理片元数据的过程中,片元最小的运算单位是2X2的片元块。如果有32个线程,那么一次将运算32个片元,8个2X2的片元块。如果此时缓存中只有一个片元,那么会有3个空片元占用3个SP运行。
22.1:OverDraw到底是怎么回事(一般解释为同一个像素绘制多遍):
21里说了:依赖像素是否被三角形覆盖就会产生一个片元。GPU在设计的时候为了精简和加速像素分派工作,降低功耗,会把四个相邻像素产生的片元作为不可分割的一组送到同1个SM中4个不同的sp中处理。如果当初三角形只覆盖了这四个像素中的一个,只产生了一个片元,那么在进入片元着色器的时候,会将它和相邻的三个空片元绑定到一起,导致有3个sp空转。 在极端环境下,整个网格可能全部都处于这样的状态,使得SM的效率低至25%。这种为了覆盖完整2x2片元而浪费资源的情况被称为过度渲染(Over Draw)
所以对重叠不可见元素的重复绘制会产生额外的开销,需要尽量减少Overdraw的发生。

22、光栅化阶段:逐片元操作:
显卡中的渲染输出器读取L2缓存中的片元》》处理可见性测试与混合(该过程可配置单不可自由编程)》》写数据到后置缓冲区,等待视频控制器交换前后缓冲区

以模板测试和深度测试为例:渲染输出器会首先将片元与模板缓冲(Stencil Buffer)中的模板值比对,舍弃没有通过模板测试的片元。片元通过模板测试后,渲染输出器就会将该片元与深度缓冲(Z-Buffer)中的深度信息比对进行深度测试,舍弃掉没有通过深度测试的片元。通过深度测试的片元就会与后置缓冲区(Back Buffer)中的像素进行混合。由于数据是高度可并行的,渲染输出器中的多个渲染输出单元会并行的执行这个过程。

23、流水线中有多少缓冲区?
顶点缓冲区(Vertex Buffer):由于GPU与CPU是异步的,顶点缓冲区被用于平衡两种速度不一致的硬件。通过顶点缓冲区,GPU可以访问CPU设定的顶点数组,通过图形APICPU可以手动定制顶点缓冲区的大小。
帧缓冲区(Frame Buffer):分为前置缓冲区和后置缓冲区,通过交换两个缓冲区可以分离渲染和显示,保证图像显示的连续性避免画面撕裂。帧缓冲区的大小主要由颜色缓冲区的大小决定。
颜色缓冲区(Color Buffer):颜色缓冲区是帧缓冲区的一部分,和帧缓冲区、显示器中的视频控制器相连。现在基本都是32位RGBA缓冲区了,拥有HDR的可能是64位RGBA。
深度缓冲区(Z-Buffer):如果场景中两个物体在同一个像素产生片元,GPU会比较二者的深度,保留离观察者较近的物体。如果两个片元的深度一致,由于GPU的并行性,无法确定某个片元始终处于另一个之上,进而使这两个片元出现闪烁,这个效应被称为深度冲突(Z-Fighting)。深度缓冲位数过低时,深度冲突发生的可能性就会增加,目前的深度缓冲一般使用24位或32位精度。
模板缓冲(Stencil Buffer):模板缓冲为每个像素保存一个无符号整数值,这个值的含义由开发者定义。模板缓冲是完全面向开发者的缓冲区设计,可以用于实现很多有趣的功能。模板测试发生在透明度测试之后,深度测试之前。一般的模板缓冲使用8位无符号整数。
几何缓冲(Geometry Buffer,或G-Buffer):延迟渲染技术中存储所有像素信息的一个缓冲,一般是64位精度。一种分配模式为: 用8位浮点数分别储存Normal.x、Normal.y、漫反射颜色、高光颜色,再使用24位储存RGB色彩 留下了一个空闲的8位通道用作机动。
23.1缓冲区内存该如何计算:
假设屏幕在真彩模式下显示一个2160×1080的图像,那么每个像素需要4个字节储存颜色,那么单个颜色缓冲需要的空间是:
2160*1080*4B=8.90MB
使用双缓冲区技术,则空间翻倍,每个像素使用8个字节储存颜色,再加上24位的深度缓冲,8位的模板缓冲,现在占用的空间是:
2160*1080*(2*4B+3B+1B)=26.70MB
如果使用抗锯齿处理,比如超级采样或多重采样,需要的储存空间会更多。

本帖子中包含更多资源

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

×
发表于 2022-3-7 14:27 | 显示全部楼层
知识面还是挺广的,
但还是有一些小错误,比如
11.GPU接受DrawCall的流程:一般vertex 和其他属性(比如normal和texcoord) 是提前放到buffer 中的,并不是drawCall 接收到后再放到vertexBuffer中。
  drawCall 的流程一般是(以openGL 为例),检查 3D API  设置的相关的状态,如果有新的改变,将state change 写入的CommandBuf,最后加一个triggle draw 命令到commandBuf。
GPU这边接受到CommandBuf,按commandBUf里的顺序先刷状态(就是设置一些GPU上的寄存器),然后triggle draw,
draw 就是把相关的数据送到图形的pipeline,经过整个pipeline 最终绘制到framebuffer上
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-5-9 18:03 , Processed in 0.234650 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

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