GPU架构演化史1: 3D渲染算法概述

前言

伴随着Intel重回GPU市场和国内一系列GPU创投企业流片完成, GPU似乎又回到了1997年前后那个群雄纷争的时代, 同时伴随着云端的工作负载也逐渐开始发生变化, VR/AR应用更多, 视频编解码需求更大, Transformer一类的模型及自己正在看的一些Topological Data Analysis和图模型使得现在的GPU架构也需要适应一系列变化.  因此体系结构上不可避免的NVidia会有自己的后向兼容的历史包袱。

美国《芯片和科学法案》的通过, 那么在体系结构上的自主变成了必然.  自主并不是简单的仿制,或者按照Elon Mask的说法:”Boil things down to their fundamental truth and reason up from there”,因此最近准备花几个月时间来好好学习一下计算机图形学和相应的架构,也就有了这个系列的笔记。目的是把那些未知的黑盒领域探清楚,再自顶而下的抽象获得更普适的处理方法。

3D渲染概览

谈到GPU,那么还是从最基本的3D渲染开始,  计算机图形学入门 3D渲染指南[1]

GPU架构演化史1: 3D渲染算法概述

这本书是比较好的入门教材,非常精炼的基于web canvas实现了光栅化(Rasterizer)和光追(RayTracer)两个完整的渲染模型,相较于其它基于OpenGL的图形学教程配置环境复杂更加容易上手。当然详细的图形学教程还需要参考虎书等更多的资料,会单独放到另一个系列的笔记中。

GPU架构演化史1: 3D渲染算法概述

Ray Tracing(光线追踪)

光线追踪算法可以追述到1525年丢勒的方法, 即把眼睛作为墙上的锚点,然后沿着这跟绳子追踪到与它相交的物体。

而本质上这根细绳按照现代的方式描述就是,以人视野内的一个平面(Viewing plane)为基础,构建光向量方程(Ray Equation),  然后再看在视野内是否和其它物体相交*Intersection)导致遮挡,根据交点来决定平面上的像素颜色,这样就构成了最基本的渲染模式。1969年,IBM将其引入到计算机图形学,用于计算可见度和阴影。然后接下来逐渐完成了反射捕捉、阴影和折射的处理。即考虑光照的影响,主要是像太阳那样的具有方向性的光源以及灯光这样的点光源,然后物体本身的材质导致的光照反射, 物体被光源遮挡产生的阴影,然后还要考虑物体反射到其它物体上,其它物体又反射回来的光线导致的递归,最终构成下图中第二步的光照方程。对于一些透明材质带来的光线折射等操作。具体算法总结如下图。

值得注意的是这样的算法计算量非常庞大,1984年卢卡斯影业将其用于电影特效的制作,但是在那个年代是没有足够的硬件去支撑实时渲染的,因此整个算法进入家用游戏也是在最近几年,nVidia Turing架构开始的,可以注意到上述算法中求Intersection在物体本身投射到Viewing Plane和光线射入及阴影等多个步骤中都有调用,因此Turing架构中包含了一个基于BVH算法的硬件逻辑(RayTracing Core, RT Core).具体的内容到介绍Turing和Ampere架构的时候再说。

Rasterizer(光栅算法)

光追固然效果好,但是否有效果相对差一点,但是计算更加快速的算法呢?而另一种算法就是光栅化的算法, 它的做法和我们对光线追踪所做的正好相反。光线追踪渲染器从画布中的一个点开始,和视角连成一条光线,然后一路追下去,并确定它可以通过该点看到什么;在这里,我们从物体本身透视投影到画布上的位置来计算。然后多个物体投射到画布上时,通过一系列的采样和插值算法来确定画布上像素点的取值,也就是所谓的光栅化(Rasterization)。最终再根据空间位置丢弃隐藏的片段.  具体处理步骤如下:

从应用程序的视角来看,本质是采用API描述渲染的几何图形,而这些几何图形本身可以由大量的点、线、线包围的三角形、多个三角形组成的多边形构成,然后这些三角面上还有一些颜色和纹理的信息。归根结底,3D API都将其抽象成一系列顶点在空间中的三维坐标、颜色、纹理,最终构成大量的顶点数据(VertexData)进行下一步的处理。

Vertex Processing

收到顶点数据后,顶点着色器(Vertex Shader)对每个顶点的数据会进行一系列的变换,主要包括几何变换、视图变换、投影变换等,主要计算方式就是线性代数中的矩阵乘法,考虑到齐次坐标(homogeneous coordinates),通常是一个四维矩阵乘法,和现如今的AI模型超大规模矩阵乘法是不同的。而这些顶点(Vertex)之间在这一步是相互独立的,因此也为后期进行大量的并行化处理带来了方便。例如一个齐次缩放变换:

而将它们最终转换到2D画布上,也同样是加载一个齐次投影矩阵乘法就好

    

Primitive Processing

前一步所有的Vertex是独立的,而在这一步它们将会组合在一起构成三角形,然后构成图元(Primitive),同时还有一些曲面细分(Tessellation),或者超出画布的图元裁剪成新的三角形,在构成图元的过程中会从纹理缓存中读取相应的颜色实现逐个图元的着色,所以这一步通常也被称为Geometry Shader。

Rasterization

光栅化也就是利用图元的信息和一系列插值算法将其映射到像素点的处理过程,映射后的这些内容我们将其称为片段(Fragments)。

Pixel Processing

有些人把这一步叫Fragment Shader,也有些厂商直接叫Pixel Shader,主要就是对光栅化后产生的Fragments进行像素级的颜色填充,例如将一张木箱表面的纹理填充到立方体上。中间还需要一系列抗锯齿和纹理双线性、三线性插值的操作。

而最终填充好纹理的Fragment将进行最后的输出合并,合并的过程中会使用到Z-buffer等操作,最终构成像素级的图片,输出到显示器上。整个流程汇总如下:

3D渲染流程的计算和访存

在整个渲染的过程中涉及了大量的浮点运算和访存操作,

正如文章开头所说,很多时候我们需要“Boil things down to their fundamental truth and reason up from there”,看GPU架构的本质并不在分析nVidia的CUDA SM是什么,Tensor Core做了啥,然后可以如何依葫芦画瓢去抄。而是从3D图形学计算渲染的本源去看,整个计算过程如何,如何从算法上简化,如何从硬件架构上优化,所以有必要去看过去四十年的计算机图形学历史上,诞生了一系列软硬件融合的优化方式,接下来的章节我们会从1982年SGI构建的第一块图形VLSI开始,逐渐展开,去回顾90年代初期的SGI Reality Engine,再到尝试商业化的IrisVersion,再到3Dfx Voodoo,然后到初代的nVidia TNT,再到逐步可编程的Geforce系列,最终到2007年CUDA的诞生和过去十几年CUDA架构的演进。

Reference

[1]

Computer Graphics from Scratch: https://www.gabrielgambetta.com/computer-graphics-from-scratch/00-introduction.html

GPU架构演化史1: 3D渲染算法概述》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/652.html