图形学笔记(一) 底层知识背景

底层知识背景

显卡和GPU

GPU和CPU的区别?

主流CPU(Central Processing Unit,中央处理器)芯片上有四级缓存,消耗了大量晶体管,在运行时需要大量电力;主流GPU(Graphics Processing Unit,图形处理器)芯片最多有两层缓存,且GPU可以利用晶体管上的空间和能耗做成ALU单元,因此GPU比CPU效率高。

CPU重在实时响应,对单任务速度要求高,需要针对延迟优化,所以晶体管数量和能耗都需要用在分支预测、乱序执行、低延迟缓存等控制部分;GPU主要使用于具有极高可预测性和大量相似运算的批处理,以及高延迟、高吞吐的架构运算,对缓存的要求相对很低,顺序运算效率很高,同时相对的乱序处理效率很低。

CPU除了负责浮点和整型运算,还有很多其它的指令集的负载,如多媒体解码和硬件解码,CPU注重单线程性能,保证指令流不中断,需要消耗更多晶体管和能耗用在控制部分,于是CPU分配在浮点运算的功耗会减少;GPU基本只进行浮点运算,设计结构简单,效率更高,GPU注重吞吐率,单指令能驱动更多的计算,相比较GPU消耗在控制的能耗就少得多,因此可以将资源留给浮点运算使用。GPU的浮点运算能力比CPU高10~12倍。

什么是NVIDIA/AMD?

显卡品牌。

NVIDIA公司译为英伟达,其生产的显卡又被称为N卡。AMD译为超微半导体,其生产的显卡又被称为A卡。

N卡奉行大核心战略,GPU内部采用大量1D单元,在执行效率上理论可以达到100%,实际效率也可以维持在90%以上,因为架构执行效率高,灵活性强,所以在实际应用中易发挥应有性能。但是大核心的设计复杂,内部集成的SP数量也不会太多,成本和功耗也会比较高,控制单元在晶体管的消耗上占了更大比例,在相同晶体管数量的情况下,N卡能做的运算单元相对较少。N卡在软件上具有明显优势,包括微软在内的软件商都为N卡开发优化,使得大量工具软件和游戏在N卡环境下有更好的表现。

A卡奉行小核心战略,采用VLIW5或VLIW4的设LI计,分别采用4D+1D的设计和4D设计,可以在较小的晶体管代价和较小的核心面积下装入更多的SPU,以SP的数量取胜。其理论计算能力远超N卡,但实际执行效率并不高,一旦进入GPU的图形信息是1D或3D形式这一的非标准数据形式,A卡的执行效率最低可降至25%至20%。

显卡是个人计算机最基本组成部分之一,用途是将计算机系统所需要的显示信息进行转换以驱动显示器,并向显示器提供逐行或隔行扫描信号,控制显示器的正确显示,其高效的并行计算能力现阶段也用于深度学习等运算。

GPU和显卡有什么关系?

GPU是显卡上的核心处理芯片,显卡上除了GPU,还包括显存、电路板和BIOS固件等。由于GPU在显卡上十分重要,所以时常用GPU代指显卡。

显卡也叫显示适配器,分为独立显卡和集成显卡。独立显卡由GPU、显存和接口电路组成;集成显卡和CPU共用风扇和缓存,没有独立显存,而是使用主板上的内存。

显存是什么?

显卡储存体系的设计哲学是更大的内存带宽而非更低的访问延迟,这也是显存访问的特点:高带宽,高延迟。

显存既可以是物理上的,也可以是逻辑上的。

对集成显卡如Intel HD Graphics来说,GPU使用CPU专门划分出来的一份内存空间,即**UMA(Unified Memory Architecture,一致性储存架构)作为显存,GPU和CPU用不同的虚拟地址对UMA中的同一个物理地址寻址。使用集成显卡时,CPU和GPU共享总线。在渲染时,CPU将顶点等数据存入主存,然后GPU可以通过GART(Graphic Address Remapping Table,显存地址重定位表)**访问UMA。GART的作用是将UMA虚拟地址映射到GPU寻址空间。而由于UMA属于CPU内存范围,CPU可以直接访问它。

对独立显卡来说,GPU可以使用专门的显存条,并使用显存条的物理地址进行寻址,这是最常见的显卡类型。在独立显卡结构中,GPU可以直接从显存中读写信息。而CPU访问显存条中的储存空间时,需要映射一部分GPU储存空间到CPU地址空间,典型大小为256MB或512MB,CPU地址空间的获取一般由API完成。

无论使用哪种显卡,CPU和GPU交流必然要经过总线。独立显卡中CPU与显卡的沟通,是通过异步的**DMA(Direct Memory Access,内存直接访问)**实现的。主机将DMA命令块写入内存,DMA命令块由传输来源的源地址、传输目标地址和传输的字节数组成。CPU将这个命令块的地址写入DMA控制器,然后继续其它工作。随后DMA控制块会直接操纵内存总线,脱离主CPU的帮助下实现传输,将数据提供给显卡驱动。反过来,显卡驱动发送信息给DMA控制器请求线路,DMA控制器于是占用内存总线,并发送所需地址到内存地址总线,然后发送信号到DMA确认线路。当显存收到DMA确认信号时,他就传输数据到内存,并清楚DMA请求信号。每当一次沟通结束,DMA都会触发一次CPU中断。

什么是流处理器SP?

流处理器又称流处理单元,简称**SP单元(Streaming Processor)**或SPU,有些显卡生产商也会将其称作core(核心)。流处理器的数量能直接影响显卡性能。

之前的显卡具有两个重要的运算单元——顶点处理单元和像素处理单元。但自从DirectX10开始,微软引入了流处理器这个概念,顶点处理单元和像素处理单元很快被业界抛弃。流处理器是顶点处理单元和像素处理单元的统一,负责了渲染中的顶点和像素渲染。将顶点处理单元和像素处理单元合并的概念又被称作统一着色器架构(Unified Shader Architecture)

业界之所以抛弃之前的顶点+像素结构而使用SPU架构,是因为传统的顶点和像素分离渲染架构存在严重的资源分配不均的问题,两种单元渲染任务量不同,效率低下。而SP架构是统一结构,不再区分顶点和像素渲染,进行不同渲染任务时都能保证效率。

4D+1D/4D/1D都是什么?

D是维度Dimension的意思,在图形学中的nD指n维浮点向量运算,nD单元指由n个流处理单元整合成的n维浮点向量运算单元

像素坐标XYZW、色彩参数RGBA以及纹理坐标参数STPQ正好都是4维运算,这导致顶点处理单元和像素处理单元都是4D单元,在引入流处理器后,主流的流处理器也是4D单元。

而1D即一维向量,也就是标量。由于流处理器合并了顶点和像素处理单元,图形渲染中标量运算成分开始增多,GPU不再像早年那样只需要处理单纯的4D向量运算了。在这样的背景下,英伟达完全抛弃4D结构,设计了G80这样的1D标量处理器,将矢量运算分解为4次或更多次标量运算,这使N卡的灵活性大幅提升,在任意维度的运算环境下都可以得到满意的性能。

AMD没有放弃4D架构,而是进行了改良,增加了一个标量运算单元,这就是4D+1D矢量标量混合架构,也就是VLIW5(Very Long Instruction Word,超长指令口令)架构,它把需要计算的指令组合成适合4D+1D架构的长指令,比如将一个2D运算和一个3D运算合并为一个4D+1D运算,这样理论上每个统一处理器每个周期都可以进行一次4D运算加一次1D运算,是N卡1D单元运算效率的4~5倍,这种将指令组合的算法被称为co-issue算法。这五个ALU只需要一个发射端口,电路设计更加简单,功效与发热也更容易控制,但缺点就是依赖指令组合,一旦非最优指令组合,这些运算单元中部分维度就只能空转,运算效率将显著降低。

什么是流多处理器SM?

SM(Streaming Multiprocessor,流多重处理器)由多个SP加上共享内存特殊函数单元寄存器多边形引擎指令缓存L1缓存等的组合。

SM中的任务主要由SP承担,SM中SP的数量一般为32个,有时也有16或64个SP组成的SM。进行辅助计算的还有SFU(Special Function Units,特殊数学运算单元),它们用于进行三角函数和指对数等运算。

SM由Warp Scheduler(束管理器)驱动,束管理器会将指令移交给Dispatch Unit(指令分派单元),由于SM中每束处理的事务具有相似性,这个单元会从指令缓存新的读取指令,并一次性向整束的所有流处理器发送同一个指令。这种通过一条指令驱动若干线程的特性被称为SIMT(Single Instruction Multiple Thread,单指令多线程),在这个框架下,指令分派单元可以读取一条指令,然后向多个SP分派不同的参数,以让它们在不同的寄存器地址进行读写。

SM中一般配有一个多边形引擎(Polymorph Engine)。这个引擎的作用是实现属性装配、顶点拉取、曲面细分、裁剪和光栅化等渲染流水线中的固定步骤。

每个SM中具有一个足够大的寄存器,一般能达到128KB。所有SP共用这个寄存器中的空间,所以如果单个线程需要的寄存器空间过大,可能使每束的最多线程数减少,影响并行性。

L1缓存是开始时用于储存顶点数据的缓存,在顶点处理阶段结束后,SM会将处理结果送到SM外的L2缓存中。多边形将在SM外进行光栅化,然后将生成的片元重新分发到SM中,这时L1缓存中储存的就是片元数据了。

一个显卡上可能有10~20个SM,一般显卡厂商将若干SM的组合称为一个GPC(Graphic Processor Cluster,图形处理簇),每个簇可以处理一批(batch)顶点或片元。在有GPC结构的显卡上,L2缓存一般位于GPC中,而对于没有GPC结构的显卡,L2一般位于显存旁或显存中。

显卡有什么样的储存结构?

显卡中的物理储存器按存取速度的大小从快到慢依次有:寄存器、共享内存、L1缓存、L2缓存、纹理缓存、常量缓存、全局内存(显存)。

寄存器位于SM中,访问速度是1个时间周期,SP运行时可以随意的读和写寄存器。在指令分派单元的控制下,每个线程都会获得自己的寄存器空间,

共享内存和L1缓存都位于SM中,它们都可以被SM中的所有SP共用。L1缓存重点存放顶点和片元数据,而共享内存重点存放材质参数、光照、摄像机等常量数据。共享内存和L1缓存的访问速度都低于32个时间周期。

L1缓存和L2缓存重点用于顶点和片元数据的交换。L2缓存位于SM之外,它的访问速度相对L1较慢,大致需要32至64个时间周期,但由于大量SM会共用一个L2缓存,L2缓存的吞吐量是数个L1缓存的总和。在有些显卡设计中,L2缓存还可以作为纹理缓存和常量缓存的新一层缓存,供其它显卡硬件使用。

纹理缓存和常量缓存是显卡上重要的缓存类型,它们都是显存的直接缓存,访问延迟在400个时间周期以上,如果出现未命中,要等待访问显存的话,这个延迟甚至很可能超过1000个时间周期。

全局内存也就是显存,它的数据直接来自CPU总线。显卡驱动会在GPU流水线空闲时将任务数据从GPU缓冲区导入显存。显存的访问延迟在500个时间周期以上,显卡的访问一般在纹理缓存和常量缓存缺失时才会发生。但存在一种高吞吐GPU结构,使用更大量的GPU而减少了缓存的层数,这种结构中显存可能直接被共享内存访问,通过高吞吐量与高带宽来缓解缓存层级少带来的性能缺陷。

GPU是怎么应对阻塞的?

GPU不像CPU那样实现流水线,因为设计者认定GPU中的一批数据应当具有相对固定的处理过程,比如在一个屏幕后期处理的draw call中,GPU可能处理了1920x1080个片元,但对每个片元运行相同的一套算法。

GPU并不关心跳转,其性能瓶颈主要来自读取显存产生的阻塞而非跳转产生的阻塞。由于纹理数据储存在显存中,在每次遇到采样语句时,为了将显存中的数据调入核心,可能需要使处理器阻塞几百上千个时钟周期。

为了缓解这样的问题,GPU使用**换入换出操作(swapping)**来隐藏延迟。

SM中的寄存器可以备份若干份SP的执行状态。每当有一批片元在等待采样纹理数据时,可以将此时SP组的上下文备份保存在寄存器中,然后导入下一批片元进行处理,这个操作被称为换出(swap-out)。换出使得核心不需要空等采样结果,可以继续执行更多的片元。

由于GPU中的数据具有相对固定的处理过程,我们认为每一批片元都会在相同的周期后遇到一次换出操作。假设我们的GPU中只有一个核心(当然不存在这样的GPU),运行一批具有2000个片元的数据,这2000个片元都会在运行完前5个周期后遇到第一次采样语句,然后被执行换出。假设采样结果从显存中返回到核心需要1000个时间周期,那么在运行完第200个片元并将其换出后,第1个片元所需的采样数据就可以抵达核心,这时核心将第1个片元的上下文从寄存中拷贝回SP中,继续进行这个片元采样之后的运算,直到它再次被换出,这个操作被称为换入(swap-in)。如果这些片元在采样语句结束后还有5个时间周期用于算数运算,那么第1个片元执行完时第2个片元也恰好可以被执行换入。以此类推,执行完每200个片元所用的总时间是2000个时间周期,假如我们不使用换入换出操作,200个片元所用的时间将是200200个时间周期。

换入换出的延迟仅仅只有不到10个时间周期,在实际的应用中,不止是采样运算,延迟超过30个周期的很多运算都会使用换入换出操作。

当然,在上面的例子中,加入我们没有2000个片元数据,而仅有20个,那么我们不得不在运行完第20个片元的换出后等待1900个周期才能继续运算。实际的应用中,如果每个线程需要的寄存器数越多,就意味着每束处理的线程数越少,能被用于储存镜像的空间也越少,镜像短缺意味着换入换出策略的效果大幅下降,将严重影响GPU的并发执行。

GPU是怎么实现并行的?

执行一个着色器的最小单位是线程(thread)。多个线程被打包在一起称为线程束(英伟达称之为warp,AMD称之为wavefront,可以统一翻译为线程束)。多个线程束被打包为一个线程组(block),每个线程组中的所有线程可以通过共享内存来通信,不同线程组中的线程是无法通信的。

在实际运行中,处理器会将一个线程组分配给一个SM。每个SM获得线程组后,会通过束管理器将其分为若干线程束,每个线程束中线程的个数一般等于流多重处理器中流处理器的个数,如果一个SM被分为多个束,则线程束中线程的个数等于一束中流处理器的个数。如一个具有100个线程的线程组分配给一个具有32个流处理器的SM,SM会将其分为4个线程束,第031号线程分为第1束,第3263号线程分为第2束,第6495号线程分为第3束,第9699号线程分为第4束。在运行时,每个线程束中的32个线程会被分配到32个流处理器中进行并行运算。由于GPU任务的相似性,这32个线程很可能在同一时间遇到换出操作,那么SM就会安排下一个线程束进入流处理器。

动态分支语句如何影响GPU并行效率?

动态分支包括if语句和循环语句。它们在着色器语言中存在,但可能严重影响GPU性能。

在一个线程束中,如果线程中不存在动态分支语句,那么它们的所有行为都是可预测相同的。但一旦遇见一次动态分支语句,就可能产生分裂。如果线程束中所有线程执行相同的分支,那么运行结果不会有什么不同。但我们提到过,在SM中的一束线程共享同一份指令,但凡有一个线程执行另一个分支,那么整个线程束就不得不被执行两遍,将两个分支的结果都运行一次,并让每个线程扔掉它们各自不需要的结果。如果在着色器编写中出现连续的分支预测,甚至复杂的循环语句,每个线程束的执行次数可能呈指数级递增,这个效应被称为线程分歧(thread divergence)

根据此我们也可以发现,使用循环语句运行常数次来读取某数组中的信息并不会导致线程分歧,线程分歧的严重与否关键在于相邻的元素通过动态分支语句能否得到基本相似的分支。

什么是垂直同步?

早期CRT显示器中,电子枪从上到下进行扫描,扫描完成后显示器就显示一帧画面,然后电子枪回到初始位置进行下一次扫描。为了同步显示器的使用过程和系统的视频控制器,显示器会用硬件时钟产生一系列的定时信号:当电子枪换行扫描时,显示器会发送一个水平同步信号,简称HSync,当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号,简称VSync

技术升级带来液晶屏后,视频控制器(Vedio Controller)仍在根据同步信号逐帧读取帧缓冲区的数据。为了缓解单缓冲下帧缓冲区的读取和刷新效率低的问题,GPU中常用两个缓冲区,即双缓冲机制,在一帧渲染完后,视频控制器会将两个缓冲互换。由视频控制器直接读取的缓冲区称为前缓冲区,而在后台承担渲染任务的叫后缓冲区

双缓冲会引入一个新问题:在视频控制器读取前缓冲区只读取了一半时,GPU将新的一帧内容提交到帧缓冲区,并把两个缓冲区交换。视频控制器可能把新一帧数据的后半段渲染到了屏幕上,这使得屏幕得上半部分和下半部分分属不同得两帧,产生画面撕裂。

为了解决这个问题,GPU等待显示器发送得垂直同步信号来交换缓冲区,垂直同步信号沿用了的VSync这一称谓。垂直同步解决了画面撕裂现象,也增加了画面流畅度,但需要消费更多计算资源,也会带来部分延迟。

图形API和着色语言

什么是OpenGL/DirectX?

图像编程接口(图形API),是对GPU硬件的抽象,其地位类似C语言,属于GPU编程的中低层。几乎所有GPU都既可以和OpenGL合作也可以和DirectX合作。

OpenGL和DirectX的区别?

OpenGL是纯粹的图形API;DirectX是多种API的集合体,其中DirectX包含图形API——Direct3D和Direct2D。

DirectX支持Windows和Xbox;OpenGL支持Windows、MacOC、Linux等更多平台,在Android、IOS上允许使用OpenGL的简化版本OpenGL ES。

OpenGL相对来说易上手门槛低;DirectX难上手门槛高。OpenGL渲染效率相对低,特性少;DirectX相对效率高,特性多。如DirectX12提供了底层API,允许用户一定程度上绕过显卡驱动之间操纵底层硬件。

二者在图形学领域一样重要。OpenGL在各种领域都吃香,包括许多专业领域如特效和CG建模软件。DirectX在游戏中更通用。

什么是显卡驱动?

一个应用程序向显卡接口发送渲染命令,这些接口会依次向显卡驱动发送渲染命令。显卡驱动的地位类似于C语言编译器,可以将OpenGL或DirectX的函数调用翻译成GPU能读取的机器指令,即二进制文件。显卡驱动同时也负责把纹理等数据转换成GPU支持的格式。

什么是HLSL/GLSL/CG/CUDA?

HLSL、GLSL和CG是着色语言,专门用于编写着色器。其中HLSL(High Level Shading Language)属于DirectX,GLSL(OpenGL Shading Language)属于OpenGL,而CG(C for Graphic)是NVIDIA研发的,因为英伟达与微软合作密切,CG语法与HLSL极其相似。

CUDA(Compute Unified Device Architecture,统一计算架构)也是NVIDIA研发的,和前三者类似,但并不专注于图形领域,常用在机器学习等领域,不需要像着色语言那样使用图形计算的逻辑进行数字运算。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程解决的问题: 作为游戏行业或者图形从业者,你是否面临以下问题: 到底openGL底层如何实现的? 到底矩阵操作变换是怎么做到的? 到底光栅化的算法以及原理是什么? 到底如何才能从3D世界投射到2D屏幕呢? 图形有这么多的矩阵操作,到底如何推导如何应用呢? 完这门课程,你应该就可以从底层了解一个初级的openGL图形接口如何实现,图形底层的封装到底面临哪些挑战;跟随我们一行一行写完代码,你就会得到一个迷你版本的openGL图形库,你可以深度体会图形从模型变换,观察矩阵变换,投影矩阵变换一直到光栅化纹理操作的全套模拟流程。 课程介绍: 本课程将带领员不使用任何图形库,实现从0到1的图形接口封装以及算法讲解,并且带领大家手敲代码,一行一行进行实现。 涵盖了(环境搭建,绘制点,Bresenham算法绘制完美直线,三角形拆分绘制算法,颜色插值算法,图片操作,图片二次插值放缩算法,纹理系统接口搭建及封装,矩阵操作理论以及实践,openGL类似接口封装,3D世界的图形理论及接口封装等) 最终将带领大家通过C++实现一个3D世界的图形接口,方便所有人入门图形,进行接下来的openGL接口以及GPU编程的习   本课程为系列课程的第一步入门,且带领所有人进行实现,更加实用,可以让大家打牢图形的基础知识及编程技能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值