在 CUDA 中处理边界最常用的方法是检查所有可能的边界条件并采取相应措施,即:
- 如果“此元素”超出范围,则
return(这在 CUDA 中非常有用,您可能会启动比严格必要的线程更多的线程,因此必须提前退出额外的线程以避免写入输出-of-bounds 内存)。
- 如果“此元素”位于/靠近左边框(最小 x),则对左边框执行特殊操作。
- 右、上、下(以及 3D 中的前后)边框相同。
幸运的是,在大多数情况下,您可以使用 max/min 来简化这些操作,从而避免使用过多的 if。我喜欢用这种形式的表达方式:
source_pixel_x = max(0, min(thread_2D_pos.x + j, MAX_X));
source_pixel_y = ... // you get the idea
这些表达式的结果总是限制在 0 和某个 MAX 之间,因此将 out_of_bounds 源像素钳制到边界像素。
编辑:正如 DarkZeros 所评论的,使用clamp() 函数更容易(并且更不容易出错)。它不仅检查最小值和最大值,还允许像 float3 这样的向量类型,并分别钳制每个维度。见:clamp
这是我作为练习做的一个例子,一个 2D 高斯模糊:
__global__
void gaussian_blur(const unsigned char* const inputChannel,
unsigned char* const outputChannel,
int numRows, int numCols,
const float* const filter, const int filterWidth)
{
const int2 thread_2D_pos = make_int2( blockIdx.x * blockDim.x + threadIdx.x,
blockIdx.y * blockDim.y + threadIdx.y);
const int thread_1D_pos = thread_2D_pos.y * numCols + thread_2D_pos.x;
if (thread_2D_pos.x >= numCols || thread_2D_pos.y >= numRows)
{
return; // "this output pixel" is out-of-bounds. Do not compute
}
int j, k, jn, kn, filterIndex = 0;
float value = 0.0;
int2 pixel_2D_pos;
int pixel_1D_pos;
// Now we'll process input pixels.
// Note the use of max(0, min(thread_2D_pos.x + j, numCols-1)),
// which is a way to clamp the coordinates to the borders.
for(k = -filterWidth/2; k <= filterWidth/2; ++k)
{
pixel_2D_pos.y = max(0, min(thread_2D_pos.y + k, numRows-1));
for(j = -filterWidth/2; j <= filterWidth/2; ++j,++filterIndex)
{
pixel_2D_pos.x = max(0, min(thread_2D_pos.x + j, numCols-1));
pixel_1D_pos = pixel_2D_pos.y * numCols + pixel_2D_pos.x;
value += ((float)(inputChannel[pixel_1D_pos])) * filter[filterIndex];
}
}
outputChannel[thread_1D_pos] = (unsigned char)value;
}