OpenCL/CUDA中边界条件的处理

OpenCL/CUDA中边界条件的处理,cuda,opencl,gpgpu,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))。所以有很多潜在的分支 在O

给定一个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中是否有一种“优雅”的方法来实现这一点?另外,建议使用单独的内核处理边界吗?

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

  • CLK_地址_重复-超出范围的图像坐标被包装到有效范围。此地址模式只能与标准化坐标一起使用。如果未使用标准化坐标,此寻址模式可能会生成未定义的图像坐标
  • CLK_地址_钳制_至_边缘-超出范围的图像坐标被钳制到一定程度
  • CLK_地址_CLAMP32-超出范围的图像坐标将返回边框颜色。如果图像通道顺序为CL_A、CL_强度、CL_RA、CL_ARGB、CL_BGRA或CL_RGBA,则边框颜色为(0.0f、0.0f、0.0f、0.0f),如果图像通道顺序为CL_R、CL_RG、CL_RGB或CL_亮度,则边框颜色为(0.0f、0.0f、0.0f、1.0f)
  • CLK_ADDRESS_NONE-对于此地址模式,程序员保证用于对图像元素进行采样的图像坐标指的是图像内部的位置;否则,结果是未定义的
此外,还可以定义插值的过滤模式(最近邻或线性)


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

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

  • 如果“this element”超出范围,则返回(这在CUDA中非常有用,在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和某个最大值之间绑定,因此将源像素的out_of_边界钳制为边界像素

编辑:正如DarkZeros所评论的,使用clamp()函数更容易(也不容易出错)。它不仅检查最小值和最大值,还允许像float3这样的向量类型,并分别钳制每个维度。见:

下面是我作为练习做的一个例子,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;
} 
\u全局__
无效高斯模糊(常量无符号字符*常量输入通道,
无符号字符*常量输出通道,
int numRows,int numCols,
常量浮点*常量筛选器,常量整数筛选器宽度)
{
const int2 thread_2D_pos=make_int2(blockIdx.x*blockDim.x+threadIdx.x,
块IDX.y*块尺寸y+线程IDX.y);
const int thread_1D_pos=thread_2D_pos.y*numCols+thread_2D_pos.x;
如果(螺纹2D位置x>=numCols | |螺纹2D位置y>=numRows)
{
return;//“此输出像素”超出范围。请不要计算
}
int j,k,jn,kn,filterIndex=0;
浮动值=0.0;
int2像素2D位置;
int像素_1D_pos;
//现在我们将处理输入像素。
//注意使用最大值(0,最小值(螺纹位置x+j,numCols-1)),
//这是一种将坐标固定到边界的方法。

对于(k=-filterWidth/2;k)抱歉,我的问题不太具体(我更新了我的问题),不幸的是,我不认为我可以使用纹理。只是为了澄清一下:你想从最接近的内部值推断你的边界值?你的数据不能提供-比如说-“外壳”值,但仅限于内部值。在这种情况下,在整个数据集中使用线性插值/外推方案可能很有用。除了一些优化代码外,应用程序没有直接的OpenCL库支持。当然,您可以使用类似
float4 x=f*(float4)a+(1-f)*(float4)的代码b
对于a和b之间的线性插值,不必进行外推。基本上,给定一个坐标(全局id
id
),如果该坐标位于网格的边界(边界)(例如
id.x==0
表示坐标位于网格的左边界),然后我想知道哪一个是不是边界坐标的最近坐标,这样我就可以获取该位置的值,对其执行一些操作并将其存储在边界坐标的位置。我想写一个流体模拟,对于网格上的边界速度,我需要最接近“内部”的速度值单元格,将其与-1相乘并存储在边界处我想,您的问题本身不是3d问题。每个维度中最近的坐标与维度数量无关。因此,对于1D情况,c=0的最近坐标为1。对于c=max,最近的坐标为max-1。这在每个维度中都有效。您可以使用min/max查询类似于
float nextest=max(1,min(n-1,x))