FFT鏡頭效果已 經完成,並集成入KlayGE開發版中,命名為FFTLensEffectPostProcess。除此之外,還寫了一個命令行工具,用於產生鏡頭效果的 紋理。雖然FFT的方法能在一個pass內產生各種復雜的鏡頭效果,但目前性能低於默認的多重gaussian blur。其中最主要的開銷來自於FFT本身,下面就着重就討論一下GPU FFT的進展和未來。
前不久我在用Pixel Shader實現FFT的 時候,提到了用Compute Shader來實現FFT效率可以更高。之前Ocean的例子里就有用於實現波浪模擬的CS4 FFT(從NV的例子改進而成),它的輸入和輸出是1D Buffer的形式。為了更通用,在進入KlayGE核心的時候改成了2D Texture的形式。可惜的是,CS4不能寫入紋理,就需要增加一個把Buffer轉成紋理的PS。CS5的實現基本類似於CS4,但改為直接讀寫紋 理。現在CS4和CS5的FFT都已經實現完畢,終於可以比較一下性能。
算法的差異
由於輸出的限制,pixel shader的FFT只能實現最簡單的radix-2算法,每次輸入2個復數、生成2個復數。而compute shader支持任意寫入,所以可以實現更復雜的radix-4、radix-8甚至multi-radix。在這里我實現的是radix-8,每次處理 8個復數。所以512×512只需要6個pass,比PS的18個pass少得多。
另一個區別在於,因為CS4的限制,CS4 FFT的數據都以float2的Buffer來保存(實部和虛部),只有3通道,每個pixel的RGB是排列成R0/R1/R2…Rn G0/G1/G2…Gn B0/B1/B2…Bn的形式。而在PS和CS5的實現中,仍然保留R0G0B0A0/R1G1B1A1/R2G2B2A2…RnGnBnAn的排列,並 且是4通道的。所以單從計算來說,CS4少一個通道,標量的計算量和帶寬耗用量都會小於PS和CS5。
性能比較
這里的輸入是512×512,分別在高端、中端、低端顯卡上同時比較了正向FFT和逆向IFFT。
| NV GTX580 FFT/IFFT (ms) | AMD HD6670 FFT/IFFT (ms) | NV 9800GT FFT/IFFT (ms) | NV NVS4200M FFT/IFFT (ms) | |
|---|---|---|---|---|
| PS | 2.12/2.28 | 7.09/7.22 | 7.99/8.16 | 33.30/33.30 |
| CS4 | 0.70/0.70 | 2.44/2.47 | 3.33/3.45 | 10.36/9.80 |
| CS5 | 0.76/0.76 | 2.40/2.39 | 不支持 | 8.86/8.81 |
從結果可以看出,CS4的實現能比PS快2倍左右,一方面來自於radix-8帶來的計算量減小,另一個方面來自於pass數減少。有趣的是,除了 高端的GTX580,其他卡上CS5都比CS4略快。在NV的卡上,以64-bit為單位的IO效率高於其他格式,這一點也給CS4帶來了一定的優勢。
反過來,這個結果也說明了CS5還有優化的空間。CS5多做了1/3的計算,IO效率也不不是最高的,卻能於CS4相似甚至更快。
未來
這個版本里的FFT鏡頭效果暫時告一段落,除了繼續優化之外,以后還有很多算法級別的改進值得嘗試。
數據格式
現在所有的中間格式都是ABGR32F。如果用ABGR16F的話,在暗處將會出現一些artifact。如果這樣的artifact在可以接受的 范圍內,或者用其他補償方法來改進16F的精度,速度就能大大提高。輸入和輸出格式則不受限制。輸入格式可以用ABGR16F、或者更少的 B10G11R11F,輸出格式可以用ABGR16F。
純實數FFT
因為輸入數據是張只有實數部分的圖像,對純實數做FFT的結果是頭尾對稱的,可以利用這一點做算法上的改進。FFTW里面,這個變換稱為r2c。對 n長度的輸入它只需要輸出n/2+1個復數,而不用n個,IO和計算量都可以減小。另一個嘗試的方向是DCT。作為FFT的特例,經過特殊尋址安排的 DCT也符合卷積定理。所以用輸入輸出都是純實數的DCT也能減少IO。
sFFT
今年一月,MIT的一個團隊提出了Spare Fast Fourier Transform(sFFT)的算法。在輸入信號稀疏的情況下,sFFT能比傳統FFT快之百倍。由於用於鏡頭效果的輸入僅僅是亮度大於某個閾值的pixel,稀疏程度非常高,所以也可能可以利用sFFT來進行加速。
