DirectX计算着色器(Compute Shader)线程组相关参数详解(SV_GroupIndex,SV_DispatchThreadID,SV_GroupThreadID,SV_GroupID)

        在DirectX 11之前,着色器是与具体的渲染步骤绑定的,例如像素着色器,顶点着色器等等。而从DirectX11开始,DirectX增加了一种计算着色器(Compute Shader),它是专门为与图形无关的通用计算设计的。因此DirectX就变成了一个通用GPU计算的平台。鉴于GPU拥有极其强大的并行运算能力,学习使用DirectCompute是很有意义的。   DirectX计算着色器(Compute Shader)线程组相关参数详解(SV_GroupIndex,SV_DispatchThreadID,SV_GroupThreadID,SV_GroupID)_第1张图片

        计算着色器的计算能力和处理器数量有关,比如如果你的GPU有16个多处理器,那么就可以开启16个线程组。每个线程组,可以有n个线程。NVIDIA显卡基于SIMD32,n必须是一个warp(即32)的倍数。而ATI显卡是基于一个wavefront(即64)的倍数。一个线程组可以互相共享内存,而且等待每个线程同步,但是不同线程组是没有该能力的。

   下面是一个计算着色器案例

//numthreads(N, 1, 1),代表着线程的数量,是三维的

//groupThreadID,线程在线程组中的ID,用三维向量的形式表示如(3,1,1)

//dispatchThreadID,在一次dispatch线程中的ID,表示的是该线程在整个分发的线程组中的ID,用三维形式表示。

#define N 256
#define CacheSize (N + 2*gBlurRadius)
groupshared float4 gCache[CacheSize];


[numthreads(N, 1, 1)]
void HorzBlurCS(int3 groupThreadID : SV_GroupThreadID,
int3 dispatchThreadID : SV_DispatchThreadID)
{
//
// Fill local thread storage to reduce bandwidth.  To blur 
// N pixels, we will need to load N + 2*BlurRadius pixels
// due to the blur radius.
//

// This thread group runs N threads.  To get the extra 2*BlurRadius pixels, 
// have 2*BlurRadius threads sample an extra pixel.
if(groupThreadID.x < gBlurRadius)
{
// Clamp out of bound samples that occur at image borders.
int x = max(dispatchThreadID.x - gBlurRadius, 0);
gCache[groupThreadID.x] = gInput[int2(x, dispatchThreadID.y)];
}
if(groupThreadID.x >= N-gBlurRadius)
{
// Clamp out of bound samples that occur at image borders.
int x = min(dispatchThreadID.x + gBlurRadius, gInput.Length.x-1);
gCache[groupThreadID.x+2*gBlurRadius] = gInput[int2(x, dispatchThreadID.y)];
}


// Clamp out of bound samples that occur at image borders.
gCache[groupThreadID.x+gBlurRadius] = gInput[min(dispatchThreadID.xy, gInput.Length.xy-1)];


// Wait for all threads to finish.
GroupMemoryBarrierWithGroupSync();

//
// Now blur each pixel.
//


float4 blurColor = float4(0, 0, 0, 0);

[unroll]
for(int i = -gBlurRadius; i <= gBlurRadius; ++i)
{
int k = groupThreadID.x + gBlurRadius + i;

blurColor += gWeights[i+gBlurRadius]*gCache[k];
}

gOutput[dispatchThreadID.xy] = blurColor;

}

下图是线程和线程组,线程组和分发的总线程组之间的关系。

DirectX计算着色器(Compute Shader)线程组相关参数详解(SV_GroupIndex,SV_DispatchThreadID,SV_GroupThreadID,SV_GroupID)_第2张图片

          线程组里有若干线程,我们可以通过三维向量的形式指定一个线程组的规格数量,比如前面的计算着色器前需要指定的 [numthreads(N, 1, 1)],一个组分配了N个线程(y,z维度都是1),比如一个256x256的纹理做模糊处理,那么N可以是256,256个线程可以每个对应纹理第一行的256个像素,256个线程同时运行上面的计算着色器 void HorzBlurCS( ),对256个像素做模糊并行计算。如果把第一个线程组的256个线程覆盖处理的第一行256像素看成一个整体“1”,将256x256的纹理数组看成1x256的列数组,一个线程组可以覆盖这个列数组一行 的像素,那么我们需要分发256个线程组,就可以覆盖整个1x256数组的256行。即需要1(256)x256个线程组(ID3D11DeviceContext->Dispatch(1,256,1)),这样256x256个像素全部覆盖到了,每个线程处理一个像素,256x256个线程同时计算,大大提高了计算时间。

        理解了上面的这段话我们依据上图对SV_GroupIndex,SV_DispatchThreadID,SV_GroupThreadID,SV_GroupID解释。

SV_GroupThreadID:一个线程组中用来唯一表示这个线程的ID,这个唯一性只在本组内有效,表示形式为三维向量的形式,如上面的在一个线程组中的(7,5,0)

SV_GroupIndex:一个线程在本线程组中的索引,用整数表示,从0开始,从第一行从左往右累计,然后是第二,三,,,,行,如上面计算出来的57(其实是第58个线程,但对应索引为57,因为起始0占一个)。

SV_GroupID:一个线程组在整个分发的线程组中的ID,在所有线程组中具有唯一性,表现形式也是三维向量形式,如上面的(2,1,0)。

SV_DispatchThreadID:是用来表示一个线程在整个分发的全部线程组中的ID,在所有分发的线程中唯一,表示形式为三维向量形式,计算方式:SV_GroupID x 一个线程组的规格+SV_GroupThreadID。线程组规格为如上图的(10,8,3)或一致的代码中对应的numthreads(x,y,z)。

学习过程中的浅见,如有错误恳请指正。



你可能感兴趣的:(DirectX,游戏开发)