前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >更新一些GPU相关知识

更新一些GPU相关知识

作者头像
重归混沌
发布2021-03-24 20:52:04
8780
发布2021-03-24 20:52:04
举报
文章被收录于专栏:重归混沌重归混沌

在学完并实现路径追踪之后,即使增加了多线程渲染,在SPP=1024的情况下,依然需要30+分钟才能渲染一帧。

为了更快的渲染速度 ,我试图通过使用GPU的CUDA SDK来加速渲染。然而测下来竟然还没我的CPU跑的快,一方面我没有更好的显卡,另一方面我也不太确定是不是我CUDA使用错误所致。再加上就算使用GPU也不可能达到每帧秒级渲染。于是GPU的学习就搁置了。

然而在最近研究Splat地形渲染方案时, 我无意间发现了一个现象。测试地形为4层混合,在所有Texture都不开Mipmapping的情况下, FPS只有30左右,而开了Mipmapping之后,FPS可以稳定在60. 这激起了我强烈的好奇心,终于将了解GPU的运行架构提上日程(又产生了一次PageFault, 本来我在学习《mysql是怎么运行的》这本书,都已经快把B+ Tree看完了) 。

对照《GPU 精粹1》中的【28.2节 定位瓶颈】得出一个结论,如果Texture Filtering会影响FPS, 那么就说明瓶颈在Texture Bandwidth。

这引出我的第一个问题,Texture Bandwidth到底是什么,为什么Mipmapping会影响Texture Bandwidth?

我最开始以为是从CPU到GPU之间传输图片的带宽,越查资料越确定不是这样。

在找到影响FPS的因素之前,其实我大约花了一天试验了各种设置(这就是基本功不够扎实的现象,没有头绪各种试),甚至在FPS达到60时,我都搞不清到底是改了哪个设置变好的。

当然在这期间我也查了很多资料,其中最重要的两个点是说,对于Splat地形方案,减少Sampler的个数和使用 TextureArray可以改善性能。使用TextureArray可以改善性能的原因是因为它减少了bindtexture的次数,而减少Sampler可以提高性能的原因我当时并没有找到。

这就引出了第二和第三个问题,Sampler的数量为什么会影响性能?bindtexture为什么会很“贵”?

直到昨天我发现了一篇NVIDIA讲解GPU架构的文章, 这篇文章虽然不长,但是指出了各种我们在写shader时需要知道的要点。


我先简要概述这篇文章,然后试图来解释这三个问题。

在GPU中有Warp Scheduler, thread, register file, TMU, TextureCache等概念。

Warp Scheduler是最基本的调度单元,也就是说整个Warp Scheduler中的thread一直在执行“齐步走”逻辑. 如果有一个Thread需要换出(switch out)比如等待内存加载), 整个Warp Scheduler的所有Thread都会换出(switch out)。

只要有一个Thread 的if () 判断为真,那所有的Thread都需要执行if为真的逻辑,即使有的Thread的if判断为假,也需要等待if为真的Thread都执行完才执行else, 而之前那部分if为真的Thread同样需要等待else的语句执行完再继续“齐步走”。

每个Warp Scheduler会有32个Thread。这么理解下来其实每个Warp Scheduler就相当于一个具有32通道的SIMD指令。

每个Thread都有自己的寄存器, 这些寄存器都从register file进行分配,如果shader使用过多的寄存器,就会导致更少的Warp Scheduler和更少的Threads, 而更少的Warp Scheduler则意味着GPU的Core可能跑不满(类比操作系统,如果所有Thread都Sleep, 那CPU就在空转是一样的), GPU的性能就得不到发挥。

而根据Wiki的解释 和 另一篇文章, TMU( texture mapping units)和Shader中的sampler是一致的,当我们调用Sampler去采样纹理时,本质上就是调用某一个TMU去采样纹理。

TMU是一种硬件资源,当shader使用过多的Sampler时,会造成同时进行纹理采样的Thread变少,会变相阻碍Thread/Warp的并行度。

纹理采样时,会首先向Texture Cache中去读取,如果读到不到就会从L2加载到Textuer Cache, 如果L2也没有就会从DRAM(显存)中读取纹理,然后依次填充L2和Texture Cache.

根据英伟达说明的GF100内存架构从Thread读到Texture Cache只需要几十个周期,而从L2向DRAM加载则需要几百个周期。在这些周期内,需要采样纹理的Warp Scheduler都需要被换出(swap out)。

至目前为止,其实已经能解释前两个问题了:

1. Texture Bandwidth到底是什么,为什么Mipmapping会影响Texture Bandwidth?

Texture Bandwidth其实就是指Texture 从DRAM到L2和L2到Texture Cache的加载带宽

没有使用Mipmapping之前,我们地形的每一层图片尺寸都是1024*1024的图片,并且被渲染出的像素尺寸只有256*256大小, 这样在渲染相邻的pixel时被采样的texel在内存中是不连续的(会跳4个像素), 因此在纹理采样过程中会频发触发Texture Cache Miss, 每次Cache Miss都需要额外的周期从L2或DRAM中重新加载。

使用了Mipmapping之后,GPU可以根据当前的渲染情况来判断采用哪一个Mip Level。

当选择合适的Mip Level之后, 相邻的pixel对应的texel也会尽可能的相邻,可以极大的缓解Texture Cache Miss的状况。

2. Sampler的数量为什么会影响性能?

根据上文分析,如果使用Sampler过多,就会导致每一个Thread占用过多的TMU资源,会影响Thread的并行度

3. 为什么bindtexture开销比较大?

暂时未找到合理的解释


在查资料过程中,有两个额外的收获:

bindtexture并不是用来从CPU向GPU上传图片,在opengl中上传图片是使用glTexImage2D来实现的,这时图片只在显存中。

在fragment阶段,并不是每一个像素都被任意分配到一个Thread然后并行执行的。

一个Warp Scheduler被分成8*4个线程组,每2x2的像素块,被分配给一个数量为4的Thread组, 也是就说每2x2的像素块一定被分配给在同一个Warp Scheduler中的4个Thread。具体原因英伟达的文章上并没有细说。但是大概意思是,比如在决定mip level时,除非这4个像素uv跳跃太大,不然可以只用计算一次mip level就可以了。

https://blog.gotocoding.com/archives/1453

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 重归混沌 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档