考虑一个简单的景深过滤器(我的实际用例类似)。它在图像上循环并将每个像素散布在其周围的圆形邻域上。邻域的半径取决于像素的深度 - 它越接近焦平面,半径越小。
请注意,我说“散布”而不是“收集”。在更简单的图像处理应用程序中,您通常使用“聚集”技术来执行均匀的高斯模糊。 IOW,你循环遍历每个像素的邻域,并将附近的值“聚集”成加权平均值。在这种情况下,这种方法很好,但是如果你使模糊内核在像素之间变化,同时仍然使用“聚集”,你会得到一些不切实际的效果。这种“空间变量过滤”场景是“散射”与“聚集”不同的地方。
要明确:分散算法是这样的:
init resultImage to black
loop over sourceImage
var c = fetch current pixel from sourceImage
var toAdd = c * weight // weight < 1
loop over circular neighbourhood of current sourcepixel
add toAdd to current neighbor from resultImage
我的问题是:如果我将此伪代码直接转换为OpenCL,是否会因为不同的工作项同时写入同一输出像素而出现同步问题?
答案的不同取决于我使用的是缓冲区还是图像?
The course I'm reading表示将成为同步问题。但OTOH我读了Mandelbulber 1.21-2的源代码,它就像我上面的伪代码一样做了一个简单的OpenCL DOF,它似乎运行正常。
(相关代码位于mandelbulber-opencl-1.21-2.orig/usr/share/cl/cl_DOF.cl
,如下所示)
//*********************************************************
// MANDELBULBER
// kernel for DOF effect
//
//
// author: Krzysztof Marczak
// contact: buddhi1980@gmail.com
// licence: GNU GPL v3.0
//
//*********************************************************
typedef struct
{
int width;
int height;
float focus;
float radius;
} sParamsDOF;
typedef struct
{
float z;
int i;
} sSortZ;
//------------------ MAIN RENDER FUNCTION --------------------
kernel void DOF(__global ushort4 *in_image, __global ushort4 *out_image, __global sSortZ *zBuffer, sParamsDOF p)
{
const unsigned int i = get_global_id(0);
uint index = p.height * p.width - i - 1;
int ii = zBuffer[index].i;
int2 scr = (int2){ii % p.width, ii / p.width};
float z = zBuffer[index].z;
float blur = fabs(z - p.focus) / z * p.radius;
blur = min(blur, 500.0f);
float4 center = convert_float4(in_image[scr.x + scr.y * p.width]);
float factor = blur * blur * sqrt(blur)* M_PI_F/3.0f;
int blurInt = (int)blur;
int2 scr2;
int2 start = (int2){scr.x - blurInt, scr.y - blurInt};
start = max(start, 0);
int2 end = (int2){scr.x + blurInt, scr.y + blurInt};
end = min(end, (int2){p.width - 1, p.height - 1});
for (scr2.y = start.y; scr2.y <= end.y; scr2.y++)
{
for(scr2.x = start.x; scr2.x <= end.x; scr2.x++)
{
float2 d = scr - scr2;
float r = length(d);
float op = (blur - r) / factor;
op = clamp(op, 0.0f, 1.0f);
float opN = 1.0f - op;
uint address = scr2.x + scr2.y * p.width;
float4 old = convert_float4(out_image[address]);
out_image[address] = convert_ushort4(opN * old + op * center);
}
}
}
答案 0 :(得分:0)
不,你不能不担心同步。如果两个工作项在没有同步的情况下分散到同一位置,则会出现竞争条件,并且无法获得正确的结果。缓冲区和图像都一样。使用缓冲区可以使用原子,但它们会降低代码速度,尤其是在存在争用时(但即使没有)。 AFAIK,读/写图像没有原子操作。