在DirectX 11之前,着色器是与具体的渲染步骤绑定的,例如像素着色器,顶点着色器等等。而从DirectX11开始,DirectX增加了一种计算着色器(Compute Shader),它是专门为与图形无关的通用计算设计的。因此DirectX就变成了一个通用GPU计算的平台。鉴于GPU拥有极其强大的并行运算能力,学习使用DirectCompute是很有意义的。
计算着色器的计算能力和处理器数量有关,比如如果你的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;
}
下图是线程和线程组,线程组和分发的总线程组之间的关系。
理解了上面的这段话我们依据上图对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)。
学习过程中的浅见,如有错误恳请指正。