我可以在不担心同步问题的情况下从内核进行随机写入吗?

时间:2017-08-26 20:43:54

标签: multithreading opencl gpgpu thread-synchronization

考虑一个简单的景深过滤器(我的实际用例类似)。它在图像上循环并将每个像素散布在其周围的圆形邻域上。邻域的半径取决于像素的深度 - 它越接近焦平面,半径越小。

请注意,我说“散布”而不是“收集”。在更简单的图像处理应用程序中,您通常使用“聚集”技术来执行均匀的高斯模糊。 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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

不,你不能不担心同步。如果两个工作项在没有同步的情况下分散到同一位置,则会出现竞争条件,并且无法获得正确的结果。缓冲区和图像都一样。使用缓冲区可以使用原子,但它们会降低代码速度,尤其是在存在争用时(但即使没有)。 AFAIK,读/写图像没有原子操作。

相关问题