GPU 優化總結

  前面說了對我這一年多的工做進行一個總結,因爲工做比較緊,加上本人比較懶,一直沒能抽出時間來寫,最近稍微閒下來了。先寫一篇GPU優化的,後續的文章但願能慢慢補齊。這些基本都是我我的優化的實際經驗,也參考了一些文章,我都放在後面引用 部分了,感興趣的能夠深刻研究。我的理解可能有問題,若有不正確的還請指正,下面進入正題。html

因爲圖形引擎的複雜性,瓶頸可能發生在CPUGPU、,也可能發生在CPUGPU的傳輸數據與交互之中。這裏咱們只假設瓶頸在GPU上,討論GPU的優化方法。算法

Premature optimization is the root of all evil. -- Donald Knuth 這告訴咱們過早優化程序是不可取的,我以爲有兩方面的意思,1、在沒有找到高效的算法前就開始優化。2、在沒有找到真正的瓶頸關就開始優化。正確的流程大概是這樣的編程

  一、使功能能工做,程序能跑起來。ide

  二、功能正確的工做。函數

  三、讓整個程序能工做。工具

  四、讓整個程序能正確工做。oop

  五、使用這個程序並找到性能瓶頸。性能

  六、使用性能分析工具找到瓶頸所在。優化

  七、使程序高效正確的運行。[1]ui

  還有一個原則就是80~20原則,即只有百分之二十的代碼是經常使用的,因此要集中優化這些代碼,而不是一些不多執行的代碼上花些時間。

既然直接談GPU優化,那咱們就假設上面流程中的前五條已經知足,咱們假定GPU有瓶頸了,這時咱們能夠藉助一些工具如PerfhudIntel GPANSight(替代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、使用PerfhudGPU分析工具來查找瓶頸,尤爲是GPA能夠實時修改查看效果,這樣就能夠比較高效的優化Shader

 

  7、若是自己算法有問題,則能夠找更高效的方法來實現一樣的效率,或者有時爲了效率也是能夠犧牲一些效果的,也能夠作ShaderLOD,不一樣配置下采用不一樣的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)方法中的優化也合適。

 

    (6)儘可能使用低版本的Profile

 

  五、PS處理能力限制。

 

    (1)儘可能減小指令的長度。

 

    (2)儘可能使用低版本的Profile。(如PS_2_a)等。

 

    (3)儘可能使用低精度half進行計算。

 

    (4)利用硬件的特性來減小開銷,好比若是要對紋理進行降採樣,能夠利用GPU的雙線性來插值來實現,這樣能夠明顯減小紋理訪問的次數。

 

    (5)能在CPU計算的在CPU計算完成後再傳給PS

 

    (6)作Pre-Z

 

    (7)作Shader LOD,能夠在不一樣配置下切換。

 

  六、算法自己不夠高效

 

    (1)儘可能尋找更高效的算法代替,這樣比一條條指令壓榨要提高太多效率。

 

  7Shader指令不合理,對GPU工做不是特別瞭解,致使寫出低效的代碼。[]

    (1)使用代數化簡指令來減小沒必要要的計算。

    (2)使用能工做的最小Profile版本。

    (3)儘可能使用half

    (4)儘可能不要寫太通用的函數,這樣可能會產生一些無用的指令。

    (5)不要亂用normalize,好比在PS中須要normalize,那麼在VS中就沒有必要使用。

    (6normalize過的向量不須要計算長度(就是1)。

    (7)不要把向量放到多個Interpolator裏面。

      (8)能在CPU計算的就放到CPU計算。

    (9)若是一個參數永遠不變,則不必從CPU傳進來。

    (10)若是是結果是線性的,則這些計算能夠放到VS計算,而不必在PS計算。

    (11)使用下標小的interpolants。好比首先使用TEXCOORD0,再使用TEXCOORD1等。

    (12)使用紋理查找來取代複雜的函數。(目前是至少是6條算數指令:1條取紋理的關係)

    (13Pre-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-->madx + 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);

 

  這樣減小了3mov指令。

  五、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也可能在GPUGPU上的瓶頸又可能出如今不一樣的環節。工欲善其事,必先利其器!咱們要藉助工具或上面提到的方法來找到程序的瓶頸,對症下藥,不要盲目去優化,找到那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.

相關文章
相關標籤/搜索