处理OpenCL / CUDA中的边界条件

时间:2015-01-20 05:01:28

标签: cuda opencl gpgpu

给定3D均匀网格,我想设置边界单元格的值相对于网格内最近邻居的值。例如,给定10x10x10网格,对于坐标(0,8,8)处的体素,我想设置如下值: val(0,8,8)= a * val(1 ,8,8)

因为, a 可能是任何实数,我不认为在这种情况下可以使用纹理+采样器。此外,该方法也适用于普通缓冲区。

此外,由于边界体素坐标可以是网格的角落,边缘或面部的一部分,因此存在26(= 8 + 12 + 6)个查找最近邻居的不同选择(例如,如果坐标位于(0,0,0)其最近的邻居,网格将是(1,1,1))。所以有很多潜在的分支。

是否有优雅的"在OpenCL / CUDA中实现这一目标的方法?另外,建议使用单独的内核来处理边界吗?

2 个答案:

答案 0 :(得分:2)

在CUDA中处理边界的最常用方法是检查所有可能的边界条件并采取相应措施,即:

  • 如果"这个元素"超出范围,然后return(这在CUDA中非常有用,你可能会在那里启动比严格必要的线程更多的线程,因此额外的线程必须提前退出以避免写入超出范围的内存)
  • 如果"这个元素"位于左边界/近边界(最小x),然后对左边界进行特殊操作。
  • 右,上,下(以及正面和背面,3D)边框相同。

幸运的是,在大多数情况下,您可以使用max / min来简化这些操作,因此您可以避免过多的ifs。我喜欢使用这种形式的表达式:

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;
} 

答案 1 :(得分:1)

在OpenCL中,您可以使用Image3d来处理3d网格。边界处理可以通过采样器和特定的地址模式实现:

  
      
  • CLK_ADDRESS_REPEAT - 超出范围的图像坐标被包装到有效范围。此地址模式只能与标准化坐标一起使用。如果未使用标准化坐标,则此寻址模式可能会生成未定义的图像坐标。
  •   
  • CLK_ADDRESS_CLAMP_TO_EDGE - 超出范围的图像坐标被限制。
  •   
  • CLK_ADDRESS_CLAMP32 - 超出范围的图像坐标将返回边框颜色。如果图像通道顺序为CL_A,CL_INTENSITY,CL_RA,CL_ARGB,CL_BGRA或CL_RGBA,则边框颜色为(0.0f,0.0f,0.0f,0.0f),如果图像为(0.0f,0.0f,0.0f,1.0f)渠道订单是CL_R,CL_RG,CL_RGB或CL_LUMINANCE。
  •   
  • CLK_ADDRESS_NONE - 对于这种地址模式,程序员保证用于采样图像元素的图像坐标指的是图像内的位置;否则结果是不确定的。
  •   

此外,您可以为插值定义滤波器模式(最近邻或线性)。

这是否符合您的需求?否则,请向我们提供有关您的数据及其边界要求的更多详细信息。