我为一个图像的Box Filtering写了一个简单的CUDA内核。
texture<unsigned char,2> tex8u;
#define FILTER_SIZE 7
#define FILTER_OFFSET (FILTER_SIZE/2)
__global__ void box_filter_8u_c1(unsigned char* out, int width, int height, int pitch)
{
unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x>=width || y>=height) return;
float val = 0.0f;
for(int i = -FILTER_OFFSET; i<= FILTER_OFFSET; i++)
for(int j= -FILTER_OFFSET; j<= FILTER_OFFSET; j++)
val += tex2D(tex8u,x + i, y + j);
out[y * pitch + x] = static_cast<unsigned char>(val/(FILTER_SIZE * FILTER_SIZE));
}
上述代码的问题是图像的上边框和左边框未正确过滤。它们分别包含来自底部和右边界的值。不正确边框的宽度等于FILTER_OFFSET
。
但当我将x
和y
索引更改为int
而不是unsigned int
时,输出就完美了。
问题:为什么会这样?
P.S:对于x和y方向,纹理寻址模式都设置为cudaAddressModeClamp
。
答案 0 :(得分:2)
这个的根本原因与CUDA无关,它是导致你看到的结果的基本C类型转换规则。 C99标准说明了如何执行转换:
6.3.1.8通常的算术转换
- 如果两个操作数具有相同的类型,则不需要进一步转换。
- 否则,如果两个操作数都有有符号整数类型或两者都有无符号整数类型,则操作数的类型为小整数 转换等级转换为具有更大的操作数的类型 秩。
- 否则,如果具有无符号整数类型的操作数的等级大于或等于另一个操作数的类型的等级,则 带有符号整数类型的操作数转换为 带无符号整数类型的操作数。
- 否则,如果带有符号整数类型的操作数的类型可以表示带有无符号的操作数类型的所有值 整数类型,然后转换具有无符号整数类型的操作数 到带有符号整数类型的操作数的类型。
- 否则,两个操作数都将转换为无符号整数类型,对应于带有符号整数类型的操作数类型。
醇>
第三点意味着有符号整数(在这种情况下为i
和j
)首先转换为无符号整数并添加到无符号整数( x
和y
)。将负有符号整数转换为无符号整数的结果是特定于实现的,但是在这里,直接的二进制补码表示将一个小的负整数转换为非常大的无符号整数。纹理的读取模式将此超出范围的坐标限制在纹理中允许的最大值,并且内核会从纹理的错误一侧读取。
如果使用有符号整数,则不会发生转换,整个问题就会消失。这个故事的寓意可能是“了解你的编程语言”。