前面說了對我這一年多的工做進行一個總結,因爲工做比較緊,加上本人比較懶,一直沒能抽出時間來寫,最近稍微閒下來了。先寫一篇GPU優化的,後續的文章但願能慢慢補齊。這些基本都是我我的優化的實際經驗,也參考了一些文章,我都放在後面引用 部分了,感興趣的能夠深刻研究。我的理解可能有問題,若有不正確的還請指正,下面進入正題。html
因爲圖形引擎的複雜性,瓶頸可能發生在CPU、GPU、,也可能發生在CPU與GPU的傳輸數據與交互之中。這裏咱們只假設瓶頸在GPU上,討論GPU的優化方法。算法
Premature optimization is the root of all evil. -- Donald Knuth 這告訴咱們過早優化程序是不可取的,我以爲有兩方面的意思,1、在沒有找到高效的算法前就開始優化。2、在沒有找到真正的瓶頸關就開始優化。正確的流程大概是這樣的編程
一、使功能能工做,程序能跑起來。ide
二、功能正確的工做。函數
三、讓整個程序能工做。工具
四、讓整個程序能正確工做。oop
五、使用這個程序並找到性能瓶頸。性能
六、使用性能分析工具找到瓶頸所在。優化
七、使程序高效正確的運行。[1]ui
還有一個原則就是80~20原則,即只有百分之二十的代碼是經常使用的,因此要集中優化這些代碼,而不是一些不多執行的代碼上花些時間。
既然直接談GPU優化,那咱們就假設上面流程中的前五條已經知足,咱們假定GPU有瓶頸了,這時咱們能夠藉助一些工具如Perfhud、Intel GPA、NSight(替代Perfhud的工具)或其它方法來找到程序的瓶頸所在,而後根據這些分析結果來有目的的優化程序。
GPU可能存在的瓶頸主要在如下幾個部分:
一、紋理傳輸帶寬限制 (顯存到高速緩衝區)
二、光柵操做完成後幀傳輸帶寬限制(高速緩衝區到顯存)
三、頂點着色處理能力限制(VS)
四、像素着色處理能力限制(PS)在新的硬件中使用統一着色處理單元時,能夠動態調整VS PS使用的數量。
五、光柵化限制。
六、顯卡顯存太小。
七、算法自己不夠高效。
八、Shader指令使用不合理。
具體查找瓶頸的方法以下:[2]
1、若是改變紋理尺寸,幀率有明顯變化,則瓶頸可能在紋理傳輸帶寬限制或紋理AGP傳輸能力限制。若改變紋理過濾方式幀率提升了,則多是紋理傳輸帶寬限制。此時可經過減少紋理分辨率或紋理過濾方式來解決。
2、若改變窗口大小,幀率有明顯變化,則多是因爲光柵化或像素着色Shader限制,或者幀緩衝區帶寬限制。此時減小PS指定數量,若FPS有明顯變化,則說明是PS是瓶頸。不然此時改變後臺緩衝區位寬,若幀率有明顯變化則說明是幀緩衝區帶寬限制,不然光柵化是瓶頸。
3、若改變顏色位寬幀率有明顯變化,則說明瓶頸在幀緩衝區,此時能夠經過改變幀緩衝區帶寬來提升幀率,這在幀緩衝區帶寬小的低端顯卡止效果很明顯。
4、減小VS指令數量,若是幀率有明顯變化,則說明瓶頸在VS上,這種狀況通常不會出現,若是在VS中訪問紋理會比較慢,瓶頸可能會出現(Shader Model 3.0)。
5、若是減小頂點數量,幀率有明顯提高,則說明瓶頸可能在頂點過多,或頂點AGP
傳輸限制,此時可能經過模型LOD來解決問題。
6、使用Perfhud等GPU分析工具來查找瓶頸,尤爲是GPA能夠實時修改查看效果,這樣就能夠比較高效的優化Shader。
7、若是自己算法有問題,則能夠找更高效的方法來實現一樣的效率,或者有時爲了效率也是能夠犧牲一些效果的,也能夠作Shader的LOD,不一樣配置下采用不一樣的Shader,這樣在低端和高端顯卡上都會有一個不錯的幀率。
優化方法:
一、紋理帶寬限制。
(1)減小沒必要要的大紋理。
(2)能夠動態修改紋理分辨率,去掉紋理前面幾級Mip map。
(3)儘可能使用DXT格式的紋理。
(4)避免使用非二次方的紋理。
(5)若是不須要寫顏色,那就把顏色寫關閉。(Pre-z 陰影貼圖等。)
二、幀緩衝區帶寬限制。
(1)減小顏色位寬,如使用16位顏色。
(2)減少後臺緩衝區和Render Target大小。
(3)對於特效比較多的狀況,能夠先渲染到一張小的紋理上去而後再Up-Sampling到主Render Target上。
三、AGP傳輸瓶頸
(1)頂點儘可能使用索引條帶或索引列表。
(2)對頂點進行排序,這樣能夠減小頂點重複計算的次數(對結果進行Cache),使用NVTriStrip這個工做。
(3)頂點大小應該是32(bit)的整數倍。
(4)使用模型LOD。
四、VS處理能力限制,通常不會成爲瓶頸
(1)使用模型LOD,減小頂點數量。
(2)儘可能減小VS指令的數量。
(3)控制頂點紋理的使用。
(4)能在CPU計算的在CPU計算完成後再傳給VS。
(5)方法3 中的優化也合適。
(6)儘可能使用低版本的Profile。
五、PS處理能力限制。
(1)儘可能減小指令的長度。
(2)儘可能使用低版本的Profile。(如PS_2_a)等。
(3)儘可能使用低精度half進行計算。
(4)利用硬件的特性來減小開銷,好比若是要對紋理進行降採樣,能夠利用GPU的雙線性來插值來實現,這樣能夠明顯減小紋理訪問的次數。
(5)能在CPU計算的在CPU計算完成後再傳給PS。
(6)作Pre-Z。
(7)作Shader LOD,能夠在不一樣配置下切換。
六、算法自己不夠高效
(1)儘可能尋找更高效的算法代替,這樣比一條條指令壓榨要提高太多效率。
7、Shader指令不合理,對GPU工做不是特別瞭解,致使寫出低效的代碼。[]
(1)使用代數化簡指令來減小沒必要要的計算。
(2)使用能工做的最小Profile版本。
(3)儘可能使用half。
(4)儘可能不要寫太通用的函數,這樣可能會產生一些無用的指令。
(5)不要亂用normalize,好比在PS中須要normalize,那麼在VS中就沒有必要使用。
(6)normalize過的向量不須要計算長度(就是1)。
(7)不要把向量放到多個Interpolator裏面。
(8)能在CPU計算的就放到CPU計算。
(9)若是一個參數永遠不變,則不必從CPU傳進來。
(10)若是是結果是線性的,則這些計算能夠放到VS計算,而不必在PS計算。
(11)使用下標小的interpolants。好比首先使用TEXCOORD0,再使用TEXCOORD1等。
(12)使用紋理查找來取代複雜的函數。(目前是至少是6條算數指令:1條取紋理的關係)
(13)Pre-z,儘可能避免Pre-Z失效。具體參見NVIDIA編程指南。
(14) 動態分支要謹慎使用,只有在大多數狀況都走同一個分支時使用纔有比較好的效果。
(15)產生陰影時可使用tex2DProj來利用硬件特性加速。
(16)瞭解GPU的彙編指令,寫出正確的代碼。
(17)紋理指令和算術指令交叉使用。
GPU的指令:[]
一、mad 是一條指令 (x + 1) * 0.5 = x * 0.5 + 0.5 前面是一條add+mul指定,後面是一條mad指令,編譯器並不會爲咱們優化。
二、括號不要亂加,好比 x + y * 0.5 + z * 0.2 是兩條指令 mad-->mad而x + (y* 0.5 + z * 0.2)是mul-->mad-->add三條指令。
三、代數化簡 (x + c) * (x-c) 三條指令 ---> x * x + (-c * c)兩條指令
四、減小沒必要要的mov指令。
1 float4 vPos = float4(0,0,0,0); 2 3 for (int i = 0; i < 4; ++i) 4 5 vPos += float4 (mul (vMat[i], vInputPos), 1.0); 6 7 ---> 8 9 float4 vPos = float4(0,0,0,1); 10 11 for (int i = 0; i < 4; ++i) 12 13 vPos .xyz+= mul (vMat[i], vInputPos);
這樣減小了3條mov指令。
五、a/b 是用a * rcp(b)實現。D3D也可能使用div指定,但顯式使用cp可能會產生更好的代碼。(x + a) / x --> 1.0 + a * rcp(x)
六、正確使用[branch] [flatten] [loop] [unroll]。[branch]在分支友好的狀況下效率纔會比較高。[unroll]在指令限制未到使用可提升效率。
七、線性的運算放到VS裏面去算,插值到PS便可。
八、更多優化方法請參考引用[4]。
總結:
遊戲引擎是一個複雜的系統,瓶頸在不一樣機器上出現可能不同,可能在CPU也可能在GPU,GPU上的瓶頸又可能出如今不一樣的環節。工欲善其事,必先利其器!咱們要藉助工具或上面提到的方法來找到程序的瓶頸,對症下藥,不要盲目去優化,找到那20%的代碼,首先是在算法上找優化的方法,若是肯定算法是好的了,那纔要開始指令級別的優化,不然是徒勞的。而後根據上面的方法基本上能夠解決大多數的性能問題,固然咱們還能夠把多個效果結合到一塊兒處理,由於可能有些中間結果是共用的,這樣又能夠省去一些額外的開銷。盡最大限度的優化,作到在不一樣配置機器上都能流暢運行,還要在高端機器上有次世代的畫面效果。
引用:
[1] http://c2.com/cgi/wiki?PrematureOptimization
[2] http://www.cnblogs.com/lancidie/archive/2011/03/29/1998830.html
[3] NVIDIA GPU Programming Guide version 2.5.0
[4] Low-level thinking in high-level shading languages.