zoukankan      html  css  js  c++  java
  • FFT与游戏开发(三)

    FFT与游戏开发(三)

    仅仅是将傅里叶变换的复杂度降到$$O(log(n))$$还不够,能不能再快一点呢?很容易地可以想到,可以将FFT搬到GPU上去实现,这里我实现了一个简单易懂的版本,代码附在最后,有兴趣的同学可以进一步进行优化,例如多尝试使用位运算等。

    蝶形结构

    FFT的蝶形结构很容易使其并行化,而且蝶形结构之间的计算不会互相影响。

    parallel fft

    Shared Memory

    使用shared memory可以保存计算的中间结果,而不用反复地将其存到system memory中,它有着最大32KB的空间。

    同步

    多个线程同时访问shared memory需要使用GroupMemoryBarrierWithGroupSync/GroupMemoryBarrier进行手动同步.两个同步函数不同的地方在于:

    1. 不带WithGroupSync后缀的同步函数,仅仅会保证warp中的线程访问不会出现data race
    2. 而带WithGroupSync后缀的同步函数,除了保证同一个warp不会出现data race之外,还会同步不同warp中的线程。

    除非知道group中所有的线程都会在同一个wrap中执行,否则使用第二种同步方式。

    GPUFFT的实现

    #pragma kernel FFT
    
    static const uint FFT_DIMENSION = 8;
    static const uint FFT_BUTTERFLYS = 4;
    static const uint FFT_STAGES = 3;
    static const float PI = 3.14159265;
    groupshared float2 pingPongArray[FFT_DIMENSION * 2];
    
    RWStructuredBuffer<float2> srcData;
    RWStructuredBuffer<float2> dstData;
    
    uint ReverseBits(uint index, uint count) {
    	return reversebits(index) >> (32 - count);
    }
    
    float2 ComplexMultiply(float2 a, float2 b) {
    	return float2(a.x * b.x - a.y * b.y, a.y * b.x + a.x * b.y);
    }
    
    void ButterFlyOnce(float2 input0, float2 input1, float2 twiddleFactor, out float2 output0, out float2 output1) {
    	float2 t = ComplexMultiply(twiddleFactor, input1);
    	output0 = input0 + t;
    	output1 = input0 - t;
    }
    
    float2 Euler(float theta) {
    	float2 ret;
    	sincos(theta, ret.y, ret.x);
    	return ret;
    }
    
    [numthreads(FFT_BUTTERFLYS, 1, 1)]
    void FFT(uint3 id : SV_DispatchThreadID)
    {
    	uint butterFlyID = id.x;
    	uint index0 = butterFlyID * 2;
    	uint index1 = butterFlyID * 2 + 1;
    	pingPongArray[index0] = srcData[ReverseBits(index0, FFT_STAGES)];
    	pingPongArray[index1] = srcData[ReverseBits(index1, FFT_STAGES)];
    
    	uint2 offset = uint2(0, FFT_BUTTERFLYS);
    	[unroll]
    	for (uint s = 1; s <= FFT_STAGES; s++) {
    		GroupMemoryBarrierWithGroupSync();
    		// 每个stage中独立的FFT的宽度
    		uint m = 1 << s;
    		uint halfWidth = m >> 1;
    		// 属于第几个FFT
    		uint nFFT = butterFlyID / halfWidth;
    		// 在FFT中属于第几个输入
    		uint k = butterFlyID % halfWidth;
    		index0 = k + nFFT * m;
    		index1 = index0 + halfWidth;
    		if (s != FFT_STAGES) {
    			ButterFlyOnce(
    				pingPongArray[offset.x + index0], pingPongArray[offset.x + index1],
    				Euler(-2 * PI * k / m),
    				pingPongArray[offset.y + index0], pingPongArray[offset.y + index1]);
    			offset.xy = offset.yx;
    		} else {
    			ButterFlyOnce(
    				pingPongArray[offset.x + index0], pingPongArray[offset.x + index1],
    				Euler(-2 * PI * k / m),
    				dstData[index0], dstData[index1]);
    		}
    	}
    }
    

    参考资料

    1. Introduction to Algorithms, 3rd
    2. Understanding Digital Signal Processing
    3. Practical Rendering and Computation with DirectX11
  • 相关阅读:
    蓝桥杯入门训练
    <泛> STL
    传递 hdu 5961 拓扑排序有无环~
    Alice and Bob hdu 4268
    Bipartite Graph hdu 5313 bitset 并查集 二分图
    Bomb HDU 3555 dp状态转移
    不要62 hdu 2089 dfs记忆化搜索
    Leaving Auction CF 749D
    Moo University
    算法(三)
  • 原文地址:https://www.cnblogs.com/hamwj1991/p/12503171.html
Copyright © 2011-2022 走看看