Some mild cleanups and increased vectorization in loopfilter.cl

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable
#pragma OPENCL EXTENSION cl_amd_printf : enable

typedef unsigned char uc;
typedef signed char sc;

__inline signed char vp8_filter_mask(sc, sc, uc, uc, uc, uc, uc, uc, uc, uc);
__inline signed char vp8_simple_filter_mask(signed char, signed char, uc, uc, uc, uc);
__inline signed char vp8_hevmask(signed char, uc, uc, uc, uc);

__inline void vp8_mbfilter(signed char mask,signed char hev,global uc *op2,
    global uc *op1,global uc *op0,global uc *oq0,global uc *oq1,global uc *oq2);

void vp8_simple_filter(signed char mask,global uc *base, int op1_off,int op0_off,int oq0_off,int oq1_off);

typedef struct
{
    signed char lim[16];
    signed char flim[16];
    signed char thr[16];
    signed char mbflim[16];
} loop_filter_info;

void vp8_filter(
    signed char mask,
    signed char hev,
    global uc *base,
    int op1_off,
    int op0_off,
    int oq0_off,
    int oq1_off
)
{

    global uc *op1 = &base[op1_off];
    global uc *op0 = &base[op0_off];
    global uc *oq0 = &base[oq0_off];
    global uc *oq1 = &base[oq1_off];

    signed char ps0, qs0;
    signed char ps1, qs1;
    signed char vp8_filter, Filter1, Filter2;
    signed char u;

    ps1 = (signed char) * op1 ^ 0x80;
    ps0 = (signed char) * op0 ^ 0x80;
    qs0 = (signed char) * oq0 ^ 0x80;
    qs1 = (signed char) * oq1 ^ 0x80;

    /* add outer taps if we have high edge variance */
    vp8_filter = clamp(ps1 - qs1, -128, 127);

    vp8_filter &= hev;

    /* inner taps */
    vp8_filter = clamp(vp8_filter + 3 * (qs0 - ps0), -128, 127);
    vp8_filter &= mask;

    /* save bottom 3 bits so that we round one side +4 and the other +3
     * if it equals 4 we'll set to adjust by -1 to account for the fact
     * we'd round 3 the other way
     */
    Filter1 = clamp(vp8_filter + 4, -128, 127);
    Filter2 = clamp(vp8_filter + 3, -128, 127);
    Filter1 >>= 3;
    Filter2 >>= 3;
    u = clamp(qs0 - Filter1, -128, 127);
    *oq0 = u ^ 0x80;
    u = clamp(ps0 + Filter2, -128, 127);
    *op0 = u ^ 0x80;
    vp8_filter = Filter1;

    /* outer tap adjustments */
    vp8_filter += 1;
    vp8_filter >>= 1;
    vp8_filter &= ~hev;

    u = clamp(qs1 - vp8_filter, -128, 127);
    *oq1 = u ^ 0x80;
    u = clamp(ps1 + vp8_filter, -128, 127);
    *op1 = u ^ 0x80;
}

kernel void vp8_loop_filter_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];
                    int  hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i = get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s_off += i;

                        mask = vp8_filter_mask(limit[i], flimit[i], s_base[s_off - 4*p],
                                s_base[s_off - 3*p], s_base[s_off - 2*p], s_base[s_off - p],
                                s_base[s_off], s_base[s_off + p], s_base[s_off + 2*p],
                                s_base[s_off + 3*p]);

                        hev = vp8_hevmask(thresh[i], s_base[s_off - 2*p], s_base[s_off - p],
                                s_base[s_off], s_base[s_off+p]);

                        vp8_filter(mask, hev, s_base, s_off - 2 * p, s_off - p, s_off,
                                s_off + p);
                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];
                    int  hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s_off += p * i;
                        mask = vp8_filter_mask(limit[i], flimit[i],
                                s_base[s_off-4], s_base[s_off-3], s_base[s_off-2],
                                s_base[s_off-1], s_base[s_off], s_base[s_off+1],
                                s_base[s_off+2], s_base[s_off+3]);

                        hev = vp8_hevmask(thresh[i], s_base[s_off-2], s_base[s_off-1],
                                s_base[s_off], s_base[s_off+1]);

                        vp8_filter(mask, hev, s_base, s_off - 2, s_off - 1, s_off, s_off + 1);

                    }
                }
            }
        }
    }
}

kernel void vp8_mbloop_filter_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    global uc *s = s_base+s_off;

                    signed char hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s += i;

                        mask = vp8_filter_mask(limit[i], flimit[i],
                                               s[-4*p], s[-3*p], s[-2*p], s[-1*p],
                                               s[0*p], s[1*p], s[2*p], s[3*p]);

                        hev = vp8_hevmask(thresh[i], s[-2*p], s[-1*p], s[0*p], s[1*p]);

                        vp8_mbfilter(mask, hev, s - 3 * p, s - 2 * p, s - 1 * p, s, s + 1 * p, s + 2 * p);

                    }
                }
            }
        }
    }
}

kernel void vp8_mbloop_filter_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    global uc *s = s_base + s_off;

                    signed char hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s += p * i;

                        mask = vp8_filter_mask(limit[i], flimit[i],
                                               s[-4], s[-3], s[-2], s[-1], s[0], s[1], s[2], s[3]);

                        hev = vp8_hevmask(thresh[i], s[-2], s[-1], s[0], s[1]);

                        vp8_mbfilter(mask, hev, s - 3, s - 2, s - 1, s, s + 1, s + 2);

                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_simple_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;

                        s_off += i;
                        mask = vp8_simple_filter_mask(limit[i], flimit[i], s_base[s_off-2*p], s_base[s_off-p], s_base[s_off], s_base[s_off+p]);
                        vp8_simple_filter(mask, s_base, s_off - 2 * p, s_off - 1 * p, s_off, s_off + 1 * p);
                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_simple_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets, /* Y or YUV offsets for EACH block being processed*/
    global int *pitches, /* 1 or 3 values for Y or YUV pitches*/
    global loop_filter_info *lfi, /* Single struct for the frame */
    global int *filter_levels, /* Filter level for each block being processed */
    int use_mbflim, /* Use lfi->flim or lfi->mbflim, need once per kernel call */
    global int *threads, /* Thread counts per plane */
    global int *apply_filters /* Should the filter be applied (per block) */
)
{
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);

    if (plane < get_global_size(1)){
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;

                        s_off += p * i;
                        mask = vp8_simple_filter_mask(limit[i], flimit[i], s_base[s_off-2], s_base[s_off-1], s_base[s_off], s_base[s_off+1]);
                        vp8_simple_filter(mask, s_base, s_off - 2, s_off - 1, s_off, s_off + 1);
                    }
                }
            }
        }
    }
}

//Inline and non-kernel functions follow.

__inline void vp8_mbfilter(
    signed char mask,
    signed char hev,
    global uc *op2,
    global uc *op1,
    global uc *op0,
    global uc *oq0,
    global uc *oq1,
    global uc *oq2
)
{
    signed char s, u;
    int3 s3, u3;
    signed char vp8_filter;

    char2 filter;

    char3 ps = { *op0, *op1, *op2 };
    ps ^= (char3){0x80, 0x80, 0x80};

    char3 qs = { *oq0, *oq1, *oq2 };
    qs ^= (char3){0x80, 0x80, 0x80};

    /* add outer taps if we have high edge variance */
    vp8_filter = clamp(ps.s1 - qs.s1, -128, 127);
    vp8_filter = clamp(vp8_filter + 3 * (qs.s0 - ps.s0), -128, 127);
    vp8_filter &= mask;

    filter.s1 = vp8_filter;
    filter.s1 &= hev;
    filter.s0 = filter.s1;

    /* save bottom 3 bits so that we round one side +4 and the other +3 */
    filter += (char2){4,3};
    filter = clamp(filter, -128, 127);
    filter.s0 >>= 3;
    filter.s1 >>= 3;

    qs.s0 = clamp(qs.s0 - filter.s0, -128, 127);
    ps.s0 = clamp(ps.s0 + filter.s1, -128, 127);

    /* only apply wider filter if not high edge variance */
    vp8_filter &= ~hev;
    filter.s1 = vp8_filter;

    u3 = (int3){filter.s1, filter.s1, filter.s1};
    u3 *= (int3){27, 18, 9};
    u3 += 63;
    u3 >>= 7;
    u3 = clamp(u3, -128, 127);

    /* roughly 3/7th difference across boundary */
    s = clamp(qs.s0 - u3.s0, -128, 127);
    *oq0 = s ^ 0x80;
    s = clamp(ps.s0 + u3.s0, -128, 127);
    *op0 = s ^ 0x80;

    /* roughly 2/7th difference across boundary */
    s = clamp(qs.s1 - u3.s1, -128, 127);
    *oq1 = s ^ 0x80;
    s = clamp(ps.s1 + u3.s1, -128, 127);
    *op1 = s ^ 0x80;

    /* roughly 1/7th difference across boundary */
    s = clamp(qs.s2 - u3.s2, -128, 127);
    *oq2 = s ^ 0x80;
    s = clamp(ps.s2 + u3.s2, -128, 127);
    *op2 = s ^ 0x80;
}

/* is there high variance internal edge ( 11111111 yes, 00000000 no) */
__inline signed char vp8_hevmask(signed char thresh, uc p1, uc p0, uc q0, uc q1)
{
    signed char hev = 0;
    hev  |= (abs(p1 - p0) > thresh) * -1;
    hev  |= (abs(q1 - q0) > thresh) * -1;
    return hev;
}

/* should we apply any filter at all ( 11111111 yes, 00000000 no) */
__inline signed char vp8_filter_mask(
    signed char limit,
    signed char flimit,
     uc p3, uc p2, uc p1, uc p0, uc q0, uc q1, uc q2, uc q3)
{
    signed char mask = 0;
    mask |= (abs(p3 - p2) > limit) * -1;
    mask |= (abs(p2 - p1) > limit) * -1;
    mask |= (abs(p1 - p0) > limit) * -1;
    mask |= (abs(q1 - q0) > limit) * -1;
    mask |= (abs(q2 - q1) > limit) * -1;
    mask |= (abs(q3 - q2) > limit) * -1;
    mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2  > flimit * 2 + limit) * -1;
    mask = ~mask;
    return mask;
}

/* should we apply any filter at all ( 11111111 yes, 00000000 no) */
__inline signed char vp8_simple_filter_mask(
    signed char limit,
    signed char flimit,
    uc p1,
    uc p0,
    uc q0,
    uc q1
)
{
    signed char mask = (abs(p0 - q0) * 2 + abs(p1 - q1) / 2  <= flimit * 2 + limit) * -1;
    return mask;
}

void vp8_simple_filter(
    signed char mask,
    global uc *base,
    int op1_off,
    int op0_off,
    int oq0_off,
    int oq1_off
)
{

    global uc *op1 = base + op1_off;
    global uc *op0 = base + op0_off;
    global uc *oq0 = base + oq0_off;
    global uc *oq1 = base + oq1_off;

    signed char vp8_filter, Filter1, Filter2;
    signed char p1 = (signed char) * op1 ^ 0x80;
    signed char p0 = (signed char) * op0 ^ 0x80;
    signed char q0 = (signed char) * oq0 ^ 0x80;
    signed char q1 = (signed char) * oq1 ^ 0x80;
    signed char u;

    vp8_filter = clamp(p1 - q1, -128, 127);
    vp8_filter = clamp(vp8_filter + 3 * (q0 - p0), -128, 127);
    vp8_filter &= mask;

    /* save bottom 3 bits so that we round one side +4 and the other +3 */
    Filter1 = clamp(vp8_filter + 4, -128, 127);
    Filter1 >>= 3;
    u = clamp(q0 - Filter1, -128, 127);
    *oq0  = u ^ 0x80;

    Filter2 = clamp(vp8_filter + 3, -128, 127);
    Filter2 >>= 3;
    u = clamp(p0 + Filter2, -128, 127);
    *op0 = u ^ 0x80;
}

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable
#pragma OPENCL EXTENSION cl_amd_printf : enable

typedef unsigned char uc;
typedef signed char sc;

__inline signed char vp8_filter_mask(sc, sc, uc, uc, uc, uc, uc, uc, uc, uc);
__inline signed char vp8_simple_filter_mask(signed char, signed char, uc, uc, uc, uc);
__inline signed char vp8_hevmask(signed char, uc, uc, uc, uc);

__inline void vp8_mbfilter(signed char mask,signed char hev,global uc *op2,
    global uc *op1,global uc *op0,global uc *oq0,global uc *oq1,global uc *oq2);

void vp8_simple_filter(signed char mask,global uc *base, int op1_off,int op0_off,int oq0_off,int oq1_off);

typedef struct
{
    signed char lim[16];
    signed char flim[16];
    signed char thr[16];
    signed char mbflim[16];
} loop_filter_info;

void vp8_filter(
    signed char mask,
    signed char hev,
    global uc *base,
    int op1_off,
    int op0_off,
    int oq0_off,
    int oq1_off
)
{

    global uc *op1 = &base[op1_off];
    global uc *op0 = &base[op0_off];
    global uc *oq0 = &base[oq0_off];
    global uc *oq1 = &base[oq1_off];

    signed char ps0, qs0;
    signed char ps1, qs1;
    signed char vp8_filter, Filter1, Filter2;
    signed char u;

    ps1 = (signed char) * op1 ^ 0x80;
    ps0 = (signed char) * op0 ^ 0x80;
    qs0 = (signed char) * oq0 ^ 0x80;
    qs1 = (signed char) * oq1 ^ 0x80;

    /* add outer taps if we have high edge variance */
    vp8_filter = clamp(ps1 - qs1, -128, 127);

    vp8_filter &= hev;

    /* inner taps */
    vp8_filter = clamp(vp8_filter + 3 * (qs0 - ps0), -128, 127);
    vp8_filter &= mask;

    /* save bottom 3 bits so that we round one side +4 and the other +3
     * if it equals 4 we'll set to adjust by -1 to account for the fact
     * we'd round 3 the other way
     */
    Filter1 = clamp(vp8_filter + 4, -128, 127);
    Filter2 = clamp(vp8_filter + 3, -128, 127);
    Filter1 >>= 3;
    Filter2 >>= 3;
    u = clamp(qs0 - Filter1, -128, 127);
    *oq0 = u ^ 0x80;
    u = clamp(ps0 + Filter2, -128, 127);
    *op0 = u ^ 0x80;
    vp8_filter = Filter1;

    /* outer tap adjustments */
    vp8_filter += 1;
    vp8_filter >>= 1;
    vp8_filter &= ~hev;

    u = clamp(qs1 - vp8_filter, -128, 127);
    *oq1 = u ^ 0x80;
    u = clamp(ps1 + vp8_filter, -128, 127);
    *op1 = u ^ 0x80;
}

kernel void vp8_loop_filter_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);

        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];
                    int  hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i = get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s_off += i;

                        mask = vp8_filter_mask(limit[i], flimit[i], s_base[s_off - 4*p],
                                s_base[s_off - 3*p], s_base[s_off - 2*p], s_base[s_off - p],
                                s_base[s_off], s_base[s_off + p], s_base[s_off + 2*p],
                                s_base[s_off + 3*p]);

                        hev = vp8_hevmask(thresh[i], s_base[s_off - 2*p], s_base[s_off - p],
                                s_base[s_off], s_base[s_off+p]);

                        vp8_filter(mask, hev, s_base, s_off - 2 * p, s_off - p, s_off,
                                s_off + p);
                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];
                    int  hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s_off += p * i;
                        mask = vp8_filter_mask(limit[i], flimit[i],
                                s_base[s_off-4], s_base[s_off-3], s_base[s_off-2],
                                s_base[s_off-1], s_base[s_off], s_base[s_off+1],
                                s_base[s_off+2], s_base[s_off+3]);

                        hev = vp8_hevmask(thresh[i], s_base[s_off-2], s_base[s_off-1],
                                s_base[s_off], s_base[s_off+1]);

                        vp8_filter(mask, hev, s_base, s_off - 2, s_off - 1, s_off, s_off + 1);

                    }
                }
            }
        }
    }
}

kernel void vp8_mbloop_filter_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    global uc *s = s_base+s_off;

                    signed char hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s += i;

                        mask = vp8_filter_mask(limit[i], flimit[i],
                                               s[-4*p], s[-3*p], s[-2*p], s[-1*p],
                                               s[0*p], s[1*p], s[2*p], s[3*p]);

                        hev = vp8_hevmask(thresh[i], s[-2*p], s[-1*p], s[0*p], s[1*p]);

                        vp8_mbfilter(mask, hev, s - 3 * p, s - 2 * p, s - 1 * p, s, s + 1 * p, s + 2 * p);

                    }
                }
            }
        }
    }
}

kernel void vp8_mbloop_filter_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    global uc *s = s_base + s_off;

                    signed char hev = 0; /* high edge variance */
                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit, *thresh;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;
                        thresh = lf_info->thr;

                        s += p * i;

                        mask = vp8_filter_mask(limit[i], flimit[i],
                                               s[-4], s[-3], s[-2], s[-1], s[0], s[1], s[2], s[3]);

                        hev = vp8_hevmask(thresh[i], s[-2], s[-1], s[0], s[1]);

                        vp8_mbfilter(mask, hev, s - 3, s - 2, s - 1, s, s + 1, s + 2);

                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_simple_horizontal_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filter_levels,
    int use_mbflim,
    global int *threads,
    global int *apply_filters
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;

                        s_off += i;
                        mask = vp8_simple_filter_mask(limit[i], flimit[i], s_base[s_off-2*p], s_base[s_off-p], s_base[s_off], s_base[s_off+p]);
                        vp8_simple_filter(mask, s_base, s_off - 2 * p, s_off - 1 * p, s_off, s_off + 1 * p);
                    }
                }
            }
        }
    }
}

kernel void vp8_loop_filter_simple_vertical_edge_kernel
(
    global unsigned char *s_base,
    global int *offsets, /* Y or YUV offsets for EACH block being processed*/
    global int *pitches, /* 1 or 3 values for Y or YUV pitches*/
    global loop_filter_info *lfi, /* Single struct for the frame */
    global int *filter_levels, /* Filter level for each block being processed */
    int use_mbflim, /* Use lfi->flim or lfi->mbflim, need once per kernel call */
    global int *threads, /* Thread counts per plane */
    global int *apply_filters /* Should the filter be applied (per block) */
)
{
    size_t plane = get_global_id(1);

    if (plane < get_global_size(1)){
        size_t block = get_global_id(2);
        if (block < get_global_size(2)){
            if (apply_filters[block] > 0){
                int filter_level = filter_levels[block];
                if (filter_level){
                    int p = pitches[plane];
                    int s_off = offsets[block*get_global_size(1)+plane];

                    signed char mask = 0;
                    size_t i= get_global_id(0);

                    global signed char *limit, *flimit;
                    global loop_filter_info *lf_info;

                    if (i < threads[plane]){
                        lf_info = &lfi[filter_level];
                        if (use_mbflim == 0){
                            flimit = lf_info->flim;
                        } else {
                            flimit = lf_info->mbflim;
                        }

                        limit = lf_info->lim;

                        s_off += p * i;
                        mask = vp8_simple_filter_mask(limit[i], flimit[i], s_base[s_off-2], s_base[s_off-1], s_base[s_off], s_base[s_off+1]);
                        vp8_simple_filter(mask, s_base, s_off - 2, s_off - 1, s_off, s_off + 1);
                    }
                }
            }
        }
    }
}

//Inline and non-kernel functions follow.

__inline void vp8_mbfilter(
    signed char mask,
    signed char hev,
    global uc *op2,
    global uc *op1,
    global uc *op0,
    global uc *oq0,
    global uc *oq1,
    global uc *oq2
)
{
    signed char s, u;
    signed char vp8_filter;

    char2 filter;

    char3 ps = { *op0, *op1, *op2 };
    ps ^= (char3){0x80, 0x80, 0x80};

    char3 qs = { *oq0, *oq1, *oq2 };
    qs ^= (char3){0x80, 0x80, 0x80};

    /* add outer taps if we have high edge variance */
    vp8_filter = clamp(ps.s1 - qs.s1, -128, 127);
    vp8_filter = clamp(vp8_filter + 3 * (qs.s0 - ps.s0), -128, 127);
    vp8_filter &= mask;

    filter.s1 = vp8_filter;
    filter.s1 &= hev;
    filter.s0 = filter.s1;

    //char2 rounding = { 4, 3 };
    //filter += rounding;
    //filter = clamp(filter, -128, 127);
    //filter >>= 3;

    /* save bottom 3 bits so that we round one side +4 and the other +3 */
    filter.s0 = clamp(filter.s0 + 4, -128, 127);
    filter.s1 = clamp(filter.s1 + 3, -128, 127);
    filter.s0 >>= 3;
    filter.s1 >>= 3;

    qs.s0 = clamp(qs.s0 - filter.s0, -128, 127);
    ps.s0 = clamp(ps.s0 + filter.s1, -128, 127);

    /* only apply wider filter if not high edge variance */
    vp8_filter &= ~hev;
    filter.s1 = vp8_filter;

    /* roughly 3/7th difference across boundary */
    u = clamp((63 + filter.s1 * 27) >> 7, -128, 127);
    s = clamp(qs.s0 - u, -128, 127);
    *oq0 = s ^ 0x80;
    s = clamp(ps.s0 + u, -128, 127);
    *op0 = s ^ 0x80;

    /* roughly 2/7th difference across boundary */
    u = clamp((63 + filter.s1 * 18) >> 7, -128, 127);
    s = clamp(qs.s1 - u, -128, 127);
    *oq1 = s ^ 0x80;
    s = clamp(ps.s1 + u, -128, 127);
    *op1 = s ^ 0x80;

    /* roughly 1/7th difference across boundary */
    u = clamp((63 + filter.s1 * 9) >> 7, -128, 127);
    s = clamp(qs.s2 - u, -128, 127);
    *oq2 = s ^ 0x80;
    s = clamp(ps.s2 + u, -128, 127);
    *op2 = s ^ 0x80;
}

/* is there high variance internal edge ( 11111111 yes, 00000000 no) */
__inline signed char vp8_hevmask(signed char thresh, uc p1, uc p0, uc q0, uc q1)
{
    signed char hev = 0;
    hev  |= (abs(p1 - p0) > thresh) * -1;
    hev  |= (abs(q1 - q0) > thresh) * -1;
    return hev;
}

/* should we apply any filter at all ( 11111111 yes, 00000000 no) */
__inline signed char vp8_filter_mask(
    signed char limit,
    signed char flimit,
     uc p3, uc p2, uc p1, uc p0, uc q0, uc q1, uc q2, uc q3)
{
    signed char mask = 0;
    mask |= (abs(p3 - p2) > limit) * -1;
    mask |= (abs(p2 - p1) > limit) * -1;
    mask |= (abs(p1 - p0) > limit) * -1;
    mask |= (abs(q1 - q0) > limit) * -1;
    mask |= (abs(q2 - q1) > limit) * -1;
    mask |= (abs(q3 - q2) > limit) * -1;
    mask |= (abs(p0 - q0) * 2 + abs(p1 - q1) / 2  > flimit * 2 + limit) * -1;
    mask = ~mask;
    return mask;
}

/* should we apply any filter at all ( 11111111 yes, 00000000 no) */
__inline signed char vp8_simple_filter_mask(
    signed char limit,
    signed char flimit,
    uc p1,
    uc p0,
    uc q0,
    uc q1
)
{
    signed char mask = (abs(p0 - q0) * 2 + abs(p1 - q1) / 2  <= flimit * 2 + limit) * -1;
    return mask;
}

void vp8_simple_filter(
    signed char mask,
    global uc *base,
    int op1_off,
    int op0_off,
    int oq0_off,
    int oq1_off
)
{

    global uc *op1 = base + op1_off;
    global uc *op0 = base + op0_off;
    global uc *oq0 = base + oq0_off;
    global uc *oq1 = base + oq1_off;

    signed char vp8_filter, Filter1, Filter2;
    signed char p1 = (signed char) * op1 ^ 0x80;
    signed char p0 = (signed char) * op0 ^ 0x80;
    signed char q0 = (signed char) * oq0 ^ 0x80;
    signed char q1 = (signed char) * oq1 ^ 0x80;
    signed char u;

    vp8_filter = clamp(p1 - q1, -128, 127);
    vp8_filter = clamp(vp8_filter + 3 * (q0 - p0), -128, 127);
    vp8_filter &= mask;

    /* save bottom 3 bits so that we round one side +4 and the other +3 */
    Filter1 = clamp(vp8_filter + 4, -128, 127);
    Filter1 >>= 3;
    u = clamp(q0 - Filter1, -128, 127);
    *oq0  = u ^ 0x80;

    Filter2 = clamp(vp8_filter + 3, -128, 127);
    Filter2 >>= 3;
    u = clamp(p0 + Filter2, -128, 127);
    *op0 = u ^ 0x80;
}


Fixing kernel

Fixing kernel

__kernel void getSuperK_M(
   __global uint* R2M_G, // Matrix of reads
   __global uint* counters, // Matrix of temporal output
   __global uint* TMP, // Matrix of temporal output
   const unsigned int nr, // Number of reads
   const unsigned int r, // Read size
   const unsigned int k, // Kmer size
   const unsigned int m // m-mer size
   )
{
   uint x, y, yl, xl, p, start, offset, nmk, nm, min, a, b, c, idt, ts, nt, lsd, nkr, tmp, ls, d = 0;
   bool flag;

   x = get_global_id(0);
   y = get_global_id(1);
   xl = get_local_id(0);
   yl = get_local_id(1);
   ls = get_local_size(0);

  __local uint R2M_L[180]; // Vector of a read and super k-mers (32 bits) , len = lenght of reads
  __local uint mR_10[180-4+1]; // Vector containing mmers, len = lenght of reads - minimizer size + 1
  __local uint counter; // Size of mmers
  __local uint MT[9]; // Position of minimizer in each tile
  __local uint PMT[9]; // Current minimizer in each tile

  if (x==0) {
    counter = 0;
  }

   // Global to local
   ts = ls;
   nt = (r-1)/ts + 1; // nt: Number of tiles or number of sub-reads per read
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt; i++) { // coalesced
        p=ts*i+x;
        if (p > r-1) {
          break;
        }
        R2M_L[p]=R2M_G[y*r+p];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // FUNCTION: GetM-mers
    // Read to decimal representation of m-mers
    lsd = ls / m;
    nm = r - m + 1; // nm: Number of m-mers per read
    ts = ((nm-1)/lsd) + 1; // ts: Tile size
    nt = ((nm-1)/ts) + 1 ; // nt: Number of tiles or number of sub-reads per read
    a = 63; // a:Mask , same as: 2^(2m-2)-1  m=4

    // rewrited from here
    if ((x < m*nt) && (y<nr)) {
      // Cómputo en pllo del primer m-mer de cada tile
      idt = x/m;
      start = ts*idt;
      offset = x % m;
      p = (start) + (offset);
      mR_10[start] = 0;
      b = R2M_L [p];
      atomic_or(&mR_10[start], (b<<(m-(x%m)-1)*2));
    }
    barrier(CLK_LOCAL_MEM_FENCE);

      // Cómputo en serie del resto de m-mers de cada tile

      if ((x%m == 0) && (x < m*nt) && (y<nr)) {
        c = mR_10[start];
        for (int z=0; z<(ts-1); z++) {
          if (start+m+z > r-1) {
            break;
          }
          c = (c & a) << 2;
          b = R2M_L[start+m+z];
          c = c | b;
          mR_10[start+1+z]=c;
        }
      }
    barrier(CLK_LOCAL_MEM_FENCE);

   // Clear R2M_L
   ts = ls;  // ts: Tile size, work group size
   nt = (r-1)/ts + 1; // nt: Number of tiles
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt; i++)  // coalesced
        {
          p=ts*i+x;
          if (p > r-1)
            { break; }
          R2M_L[p] = 0;
        }
    }

   nt = ((nm-1)/ts) + 1; // nt: Number of tiles or number of sub-reads per read

   // Extract MR_10 debuggin
   if ((x<ts) && (y<nr)){
     for (int i=0; i<nt; i++) { // coalesced
       p=ts*i+x;
       if (p > nm-1) {
         break;
       }
       TMP[y*nm+p]=mR_10[p];
     }
   }

    // FUNCTION: GetSK_M
    // m-mers to minimizers
    nmk = k-m+1; // nm: Number of m-mers per k-mers
    nkr = r - k + 1;
    lsd = ls/nmk;
    ts = (nkr-1)/lsd + 1; // ts: Tile size
    nt = (nkr-1)/ts + 1; // nt: Number of tiles

    if (x < nt){
      MT[x] = 0xFFFFFFFF;
      PMT[x] = 0;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

  if ((x<(nt*nmk )) && (y<nr)){
  //if ((x>=(5*nmk)) && (y<nr) && (x<(6*nmk))){
      idt = x/nmk;
      start = ts*idt;
      offset = x%nmk;
      p = start + offset;
      // Cómputo en pllo del min del primer k-mer de cada tile
      b = mR_10[p];
      atomic_min(&MT[idt],b); // Atomic min - se obtiene el min del primer k-mer de cada tile
}

barrier(CLK_LOCAL_MEM_FENCE);

 if ((x < (nt*nmk)) && (y<nr)) {
   if (b == MT[idt]) {
     if (idt == 0){
        PMT[idt] = atomic_max(&PMT[idt],p);
      } else {
        PMT[idt] = atomic_min(&PMT[idt],p);
      };
   }
 }
barrier(CLK_LOCAL_MEM_FENCE);

for (int z=0; z < ts ; z++){ // Cómputo en serie del min del resto de k-mers de cada tile

    if ((start + nmk + z) > (r -1)){
      break;
    }

    if ((x%nmk - z%nmk) == 0)
    {
            b = mR_10[start + nmk + z]; // Se lee el último m-mer del k-mer a evaluar
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x < (nt*nmk)) && (y<nr))
    {
      flag = false;
      if (PMT[idt] != (start + z))
      {
        flag = true;
      }
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ( (x < (nt*nmk)) && (y<nr) ) {
        if (flag){
          if ((x%nmk - z%nmk) == 0){
            if (b < MT[idt]){
              // Atomic OR - end of super k-mers
              atomic_or(&R2M_L[PMT[idt]], (start + z + k - 1));

              PMT[idt] = start+nmk+z;

              MT[idt] = b;
              // Atomic OR - start of super k-mers
              atomic_or(&R2M_L[PMT[idt]], ((start + z + 1) << 12));
            }
          }
        }
      }
      if ((x < (nt*nmk)) && (y<nr))  {
        if (!flag) {
          if (x%nmk == 0){
            // Atomic OR - end of super k-mer
            atomic_or(&R2M_L[PMT[idt]], (start + z + k - 1));

          }
          MT[idt] = 0xFFFFFFFF;
        }
      }
        barrier(CLK_LOCAL_MEM_FENCE);

        if ((x < (nt*nmk)) && (y<nr))
      	{
          if (!flag)
          {
              atomic_min(&MT[idt], b);
          }
        }
          barrier(CLK_LOCAL_MEM_FENCE);

          if ((x < (nt*nmk)) && (y<nr))
          {
    			     if (!flag)
               {
    	            if (b == MT[idt])
                    {
                      atomic_min(&PMT[idt],p);
                    }
               }
    		  }

        barrier(CLK_LOCAL_MEM_FENCE);
      if ((x < (nt*nmk)) && (y<nr) )
        {
  			if (!flag)
          {
          if (p == PMT[idt]) {
            if (x%nmk <= z%nmk){
              PMT[idt] = start + ((z/nmk)+1)*nmk + (x%nmk);
            } else {
              PMT[idt] = start + (z/nmk)*nmk +(x%nmk);
            }
            // Atomic OR - start of super k-mers
            atomic_or(&R2M_L[PMT[idt]], ((start + z + 1) << 12));
          }

         }
       }
     }
    barrier(CLK_LOCAL_MEM_FENCE);

      if (x == (nt*nmk - 1)){
        // Atomic OR - end of super k-mer
        atomic_or(&R2M_L[PMT[idt]], (r-1));
      }
      barrier(CLK_LOCAL_MEM_FENCE);

    // Compact output
    lsd = ls/m; // lsd: Local space divisions
    nm=r-m+1; // nm: Number of m-mers per read
    ts=(nm-1)/lsd + 1; // ts: Tile size
    nt=(nm-1)/ts + 1; // // nt: Number of tiles
    if ((x<ts) && (x<nm) && (y<nr)) {
   		for (int i=0; i<nt; i++) {
        p=ts*i+x;
        if (p > nm-1)
          { break; }
        b = R2M_L[p];
        if (b != 0)
        {
    	     c = atom_inc(&counter); // Atomic increment
           a = ((b & 0x00000FFF) - ((b >> 12) & 0x00000FFF)) & 0x000000FF; // a is the size
           R2M_L[c] =  ((mR_10[p] << 20) & 0xFFF00000) | ((b >> 4) & 0x000FFF00) | a;
        }
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    //   //FUNCTION: Local2Global
    //   //Reads in local memory to Read in global memory

    ts = ls;  // ts: Tile size, work group size
    nt = (r-1)/ts + 1; // nt: Number of tiles

    if ((x<ts) && (x<r) && (y<nr)) {
      for (int i=0; i<nt; i++){  // coalesced
        p=ts*i+x;
        if (p > nm-1)
          {
            R2M_G[y*r+p] = 0;
            break;
          }
        R2M_G[y*r+p] = R2M_L[p];
      }
    }
   // Extract counters
    if ((x == 0) && (y<nr))
     {
             counters[y] = counter;
      }
}

__kernel void getSuperK_M(
   __global uint* R2M_G, // Matrix of reads
   __global uint* counters, // Matrix of temporal output
   __global uint* TMP, // Matrix of temporal output
   const unsigned int nr, // Number of reads
   const unsigned int r, // Read size
   const unsigned int k, // Kmer size
   const unsigned int m // m-mer size
   )
{
   uint x, y, yl, xl, p, start, offset, nmk, nm, min, a, b, c, idt, ts, nt, lsd, nkr, tmp, ls, d = 0;

   x = get_global_id(0);
   y = get_global_id(1);
   xl = get_local_id(0);
   yl = get_local_id(1);
   ls = get_local_size(0);

  __local uint R2M_L[180]; // Vector of a read and super k-mers (32 bits) , len = lenght of reads
  __local uint mR_10[180-4+1]; // Vector containing mmers, len = lenght of reads - minimizer size + 1
  __local uint counter; // Size of mmers
  __local uint counter2;
  __local uint MT[9]; // Position of minimizer in each tile
  __local uint PMT[9]; // Current minimizer in each tile

  if (x==0) {
    counter = 0;
    counter2 = 0;
  }

   // Global to local
   ts = ls;
   nt = (r-1)/ts + 1; // nt: Number of tiles or number of sub-reads per read
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt; i++) { // coalesced
        p=ts*i+x;
        if (p > r-1) {
          break;
        }
        R2M_L[p]=R2M_G[y*r+p];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // FUNCTION: GetM-mers
    // Read to decimal representation of m-mers
    lsd = ls / m;
    nm = r - m + 1; // nm: Number of m-mers per read
    ts = ((nm-1)/lsd) + 1; // ts: Tile size
    nt = ((nm-1)/ts) + 1 ; // nt: Number of tiles or number of sub-reads per read
    a = 63; // a:Mask , same as: 2^(2m-2)-1  m=4

    // rewrited from here
    if ((x < m*nt) && (y<nr)) {
      // Cómputo en pllo del primer m-mer de cada tile
      idt = x/m;
      start = ts*idt;
      offset = x % m;
      p = (start) + (offset);
      mR_10[start] = 0;
      b = R2M_L [p];
      atomic_or(&mR_10[start], (b<<(m-(x%m)-1)*2));
    }
    barrier(CLK_LOCAL_MEM_FENCE);

      // Cómputo en serie del resto de m-mers de cada tile

      if ((x%m == 0) && (x < m*nt) && (y<nr)) {
        c = mR_10[start];
        for (int z=0; z<(ts-1); z++) {
          if (start+m+z > r-1) {
            break;
          }
          c = (c & a) << 2;
          b = R2M_L[start+m+z];
          c = c | b;
          mR_10[start+1+z]=c;
        }
      }
    barrier(CLK_LOCAL_MEM_FENCE);

   // Clear R2M_L
   ts = ls;  // ts: Tile size, work group size
   nt = (r-1)/ts + 1; // nt: Number of tiles
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt; i++)  // coalesced
        {
          p=ts*i+x;
          if (p > r-1)
            { break; }
          R2M_L[p] = 0;
        }
    }

   nt = ((nm-1)/ts) + 1; // nt: Number of tiles or number of sub-reads per read

   // Extract MR_10 debuggin
   if ((x<ts) && (y<nr)){
     for (int i=0; i<nt; i++) { // coalesced
       p=ts*i+x;
       if (p > nm-1) {
         break;
       }
       TMP[y*nm+p]=mR_10[p];
     }
   }

    // FUNCTION: GetSK_M
    // m-mers to minimizers
    nmk = k-m+1; // nm: Number of m-mers per k-mers
    nkr = r - k + 1;
    lsd = ls/nmk;
    ts = (nkr-1)/lsd + 1; // ts: Tile size
    nt = (nkr-1)/ts + 1; // nt: Number of tiles

    if (x < nt){
      MT[x] = 0xFFFFFFFF;
      PMT[x] = 0;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

  if ((x<(nt*nmk )) && (y<nr)){
  //if ((x>=(5*nmk)) && (y<nr) && (x<(6*nmk))){
      idt = x/nmk;
      start = ts*idt;
      offset = x%nmk;
      p = start + offset;
      // Cómputo en pllo del min del primer k-mer de cada tile
      b = mR_10[p];
      atomic_min(&MT[idt],b); // Atomic min - se obtiene el min del primer k-mer de cada tile
}

barrier(CLK_LOCAL_MEM_FENCE);

 if ((x < (nt*nmk)) && (y<nr)) {
   if (b == MT[idt]) {
     atomic_max(&PMT[idt],p);
   }
 }
barrier(CLK_LOCAL_MEM_FENCE);

for (int z=0; z < ts ; z++){ // Cómputo en serie del min del resto de k-mers de cada tile

    if ((start + nmk + z) > (r -1)){
      break;
    }

    if ((x%nmk - z%nmk) == 0)
    {
            b = mR_10[start + nmk + z]; // Se lee el último m-mer del k-mer a evaluar
    }

    barrier(CLK_LOCAL_MEM_FENCE);
    if ( (x < (nt*nmk)) && (y<nr) ) {
        if (PMT[idt] != (start + z)){
          if ((x%nmk - z%nmk) == 0){
            if (b < MT[idt]){
              // Atomic OR - end of super k-mers
              atomic_or(&R2M_L[PMT[idt]], (start + z + k - 1));

              PMT[idt] = start+nmk+z;

              MT[idt] = b;
              // Atomic OR - start of super k-mers
              atomic_or(&R2M_L[PMT[idt]], ((start + z + 1) << 12));
            }
          }
        }
      }
      if ((x < (nt*nmk)) && (y<nr))  {
        if (PMT[idt] == (start + z)) {
          if (x%nmk == 0){
            // Atomic OR - end of super k-mer
            atomic_or(&R2M_L[PMT[idt]], (start + z + k - 1));

          }
          MT[idt] = 0xFFFFFFFF;
        }
      }
        barrier(CLK_LOCAL_MEM_FENCE);

        if ((x < (nt*nmk)) && (y<nr))
      	{
          if (PMT[idt] == (start + z))
          {
              atomic_min(&MT[idt], b);
          }
        }
          barrier(CLK_LOCAL_MEM_FENCE);
      if ((x < (nt*nmk)) && (y<nr) )
        {
  			if (PMT[idt] == (start + z))
          {
          if (b == MT[idt]) {
            if (x%nmk <= z%nmk){
              PMT[idt] = start + ((z/nmk)+1)*nmk + (x%nmk);
            } else {
              PMT[idt] = start + (z/nmk)*nmk +(x%nmk);
            }
            // Atomic OR - start of super k-mers
            atomic_or(&R2M_L[PMT[idt]], ((start + z + 1) << 12));
          }

         }
       }
     }
    barrier(CLK_LOCAL_MEM_FENCE);

      if (x == (nt*nmk - 1)){
        // Atomic OR - end of super k-mer
        atomic_or(&R2M_L[PMT[idt]], (r-1));
      }
      barrier(CLK_LOCAL_MEM_FENCE);

    // Compact output
    lsd = ls/m; // lsd: Local space divisions
    nm=r-m+1; // nm: Number of m-mers per read
    ts=(nm-1)/lsd + 1; // ts: Tile size
    nt=(nm-1)/ts + 1; // // nt: Number of tiles
    if ((x<ts) && (x<nm) && (y<nr)) {
   		for (int i=0; i<nt; i++) {
        p=ts*i+x;
        if (p > nm-1)
          { break; }
        b = R2M_L[p];
        if (b != 0)
        {
    	     c = atom_inc(&counter); // Atomic increment
           a = ((b & 0x00000FFF) - ((b >> 12) & 0x00000FFF)) & 0x000000FF;
           R2M_L[c] = a | ((b & 0x00FFF000)>>4) | ((mR_10[p] << 20) & 0xFFF00000);
        }
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    //   //FUNCTION: Local2Global
    //   //Reads in local memory to Read in global memory

    ts = ls;  // ts: Tile size, work group size
    nt = (r-1)/ts + 1; // nt: Number of tiles

    if ((x<ts) && (x<r) && (y<nr)) {
      for (int i=0; i<nt; i++){  // coalesced
        p=ts*i+x;
        if (p > nm-1)
          {
            R2M_G[y*r+p] = 0;
            break;
          }
        R2M_G[y*r+p] = R2M_L[p];
      }
    }
   // Extract counters
    if ((x == 0) && (y<nr))
     {
             counters[y] = counter;
      }
}


cl file fix

cl file fix

/*******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2011, 2017 OpenWorm.
 * http://openworm.org
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the MIT License
 * which accompanies this distribution, and is available at
 * http://opensource.org/licenses/MIT
 *
 * Contributors:
 *     	OpenWorm - http://openworm.org/people.html
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/

#ifdef cl_amd_printf
	#pragma OPENCL EXTENSION cl_amd_printf : enable
	#define PRINTF_ON // this comment because using printf leads to very slow work on Radeon r9 290x on my machine
    	              // don't know why
#elif defined(cl_intel_printf)
	#pragma OPENCL EXTENSION cl_intel_printf : enable
	#define PRINTF_ON
#endif

#ifdef _DOUBLE_PRECISION
	#ifdef cl_khr_fp64
		#pragma OPENCL EXTENSION cl_khr_fp64 : enable

	#elif defined(cl_amd_fp64)
		#pragma OPENCL EXTENSION cl_amd_fp64 : enable
		#define _DOUBLE_PRECISION
	#else
		#error "Double precision floating point not supported by OpenCL implementation."
	#endif
#endif

#include "inc/ocl_struct.h"

#define NO_PARTICLE_ID -1

typedef struct particle_f{
	float4 pos;
	float4 vel;
	size_t cell_id;
	size_t type_;
	float density;
	float pressure;
} particle_f;
#ifdef _DOUBLE_PRECISION
typedef struct particle_d{
	double4 pos;
	double4 vel;
	size_t cell_id;
	size_t type_;
	double density;
	double pressure;
} particle_d;
#endif

/** Just for test
*/
__kernel void work_with_struct(__global struct extendet_particle * ext_particles,
							   __global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){
	int id = get_global_id(0);
#ifdef PRINTF_ON
	if(id == 0){
		printf("sizeof() of particles_f is %d\n", sizeof(particle_f) );
		#ifdef _DOUBLE_PRECISION
		printf("sizeof() of particles_d is %d\n", sizeof(particle_d) );
		#endif
	}
#endif
#ifdef _DOUBLE_PRECISION

	particles[id].pos = (double4)(id, id, id, id);
	particles[id].vel = (double4)(id, id, id, id);
#else
	particles[id].pos = (float4)(id, id, id, id);
	particles[id].vel = (float4)(id, id, id, id);
#endif
	particles[id].type_ = id + 1;
}

/**Initialization of neighbour list by -1
* what means that it's no neighbours.
*/
__kernel void _ker_init_ext_particles(__global struct extendet_particle * ext_particles){
	int id = get_global_id(0);
	ext_particles[id].p_id = id;
	for(int i=0;i<NEIGHBOUR_COUNT;++i){
		ext_particles[id].neigbour_list[i] = -1;
	}
}

__kernel void _ker_grid_cell_indexing(__global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles,
									uint PARTICLE_COUNT,
									__global uint* grid_cell_index,
									uint grid_cell_count){
	int cell_id_to_find = get_global_id(0);

	//check for trivial cases
	if (cell_id_to_find > grid_cell_count){
		return;
	}
    if (PARTICLE_COUNT == 0) {
        /* array is empty */
        return;
    } else if (particles[0].cell_id > cell_id_to_find) {
        /* cell_id_to_find is lower than everyone else */
        return;
    } else if (particles[PARTICLE_COUNT - 1].cell_id < cell_id_to_find) {
        /* cell_id_to_find is greater than everyone else */
        return;
    }

	if (cell_id_to_find == 0){
		grid_cell_index[cell_id_to_find] = 0;
		return;
	}
	if (cell_id_to_find == grid_cell_count){
		grid_cell_index[cell_id_to_find] = PARTICLE_COUNT;
		return;
	}
	//end check

	//binary search
	int low = 0;
	int high = PARTICLE_COUNT - 1;
	bool converged = false;
	int particle_id = NO_PARTICLE_ID;
	int cur_part_cell_id = -1;

	while (!converged){
		if (low > high){
			converged = true;
			particle_id = NO_PARTICLE_ID;
			continue;
		}

		int middle = low + (high - low) / 2;
		cur_part_cell_id = particles[middle].cell_id;
		particle_id = middle;
		if (cell_id_to_find <= cur_part_cell_id){
			high = middle;
		} else {
			low = middle + 1;
		}

	}
	//end binary search

	//find lowest particle_id that gives cell_id_to_find and init grid_cell_index value
	if (!converged && particles[last].cell_id == cell_id_to_find){
		particle_id = last;
		if (low == high){
			particle_id = low;
		}
		//[QUEST] Is there no need to check if last == 0 ? I think so because if last == 0 it means that we wanted to find cell_id_to_find == 0. That case was handled in trivial cases

		//find lowest particle_id that gives cell_id_to_find
		// [QUEST] is the while condition below correct?
		while (particles[particle_id - 1] && particles[particle_id - 1].cell_id == cell_id_to_find){
			particle_id--;
		}
	} else {
		particle_id = NO_PARTICLE_ID;
	}
 	grid_cell_index[ cell_id_to_find ] = particle_id;
}

/** Calc current cell id for each particles
*/
__kernel void _ker_calc_cell_id(__global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){

}

/** Searchin for neigbours foe each particles
*/
__kernel void _ker_neighbour_search(__global struct extendet_particle * ext_particles,
							   __global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){

}

int cellId(
		   int4 cellFactors_,
		   uint gridCellsX,
		   uint gridCellsY,
		   uint gridCellsZ//don't use
		   )
{
	int cellId_ = cellFactors_.x + cellFactors_.y * gridCellsX
		+ cellFactors_.z * gridCellsX * gridCellsY;
	return cellId_;
}
/** Caculation spatial hash cellId for every particle
 *  Kernel fill up particleIndex buffer.
 */
 int4 cellFactors(
				 __global struct
				#ifdef _DOUBLE_PRECISION
					particle_d
				#else
					particle_f
				#endif
					* particle,
				 float xmin,
				 float ymin,
				 float zmin,
				 float hashGridCellSizeInv
				 )
{
	//xmin, ymin, zmin
	int4 result;
	result.x = (int)( particle.pos.x *  hashGridCellSizeInv );
	result.y = (int)( particle.pos.y *  hashGridCellSizeInv );
	result.z = (int)( particle.pos.z *  hashGridCellSizeInv );
	return result;
}
__kernel void hashParticles(
							__global struct
							#ifdef _DOUBLE_PRECISION
								particle_d
							#else
								particle_f
							#endif
								* particle,
							uint gridCellsX,
							uint gridCellsY,
							uint gridCellsZ,
							float hashGridCellSizeInv,
							float xmin,
							float ymin,
							float zmin,
							__global uint2 * particleIndex,
							uint   PARTICLE_COUNT
							)
{
	int id = get_global_id( 0 );
	if( id >= PARTICLE_COUNT ) return;
	float4 _position = particle[ id ];
	int4 cellFactors_ = cellFactors( _position, xmin, ymin, zmin, hashGridCellSizeInv );
	int cellId_ = cellId( cellFactors_, gridCellsX, gridCellsY, gridCellsZ ) & 0xffffff; // truncate to low 16 bits
	uint2 result;
	PI_CELL_ID( result ) = cellId_;
	PI_SERIAL_ID( result ) = id;
	particleIndex[ id ] = result;
}

/*******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2011, 2017 OpenWorm.
 * http://openworm.org
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the MIT License
 * which accompanies this distribution, and is available at
 * http://opensource.org/licenses/MIT
 *
 * Contributors:
 *     	OpenWorm - http://openworm.org/people.html
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/

#ifdef cl_amd_printf
	#pragma OPENCL EXTENSION cl_amd_printf : enable
	#define PRINTF_ON // this comment because using printf leads to very slow work on Radeon r9 290x on my machine
    	              // don't know why
#elif defined(cl_intel_printf)
	#pragma OPENCL EXTENSION cl_intel_printf : enable
	#define PRINTF_ON
#endif

#ifdef _DOUBLE_PRECISION
	#ifdef cl_khr_fp64
		#pragma OPENCL EXTENSION cl_khr_fp64 : enable

	#elif defined(cl_amd_fp64)
		#pragma OPENCL EXTENSION cl_amd_fp64 : enable
		#define _DOUBLE_PRECISION
	#else
		#error "Double precision floating point not supported by OpenCL implementation."
	#endif
#endif

#include "inc/ocl_struct.h"

#define NO_PARTICLE_ID -1

typedef struct particle_f{
	float4 pos;
	float4 vel;
	size_t cell_id;
	size_t type_;
	float density;
	float pressure;
} particle_f;
#ifdef _DOUBLE_PRECISION
typedef struct particle_d{
	double4 pos;
	double4 vel;
	size_t cell_id;
	size_t type_;
	double density;
	double pressure;
} particle_d;
#endif

/** Just for test
*/
__kernel void work_with_struct(__global struct extendet_particle * ext_particles,
							   __global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){
	int cell_id_to_find = get_global_id(0);
#ifdef PRINTF_ON
	if(cell_id_to_find == 0){
		printf("sizeof() of particles_f is %d\n", sizeof(particle_f) );
		#ifdef _DOUBLE_PRECISION
		printf("sizeof() of particles_d is %d\n", sizeof(particle_d) );
		#endif
	}
#endif
#ifdef _DOUBLE_PRECISION

	particles[cell_id_to_find].pos = (double4)(cell_id_to_find, cell_id_to_find, cell_id_to_find, cell_id_to_find);
	particles[cell_id_to_find].vel = (double4)(cell_id_to_find, cell_id_to_find, cell_id_to_find, cell_id_to_find);
#else
	particles[cell_id_to_find].pos = (float4)(cell_id_to_find, cell_id_to_find, cell_id_to_find, cell_id_to_find);
	particles[cell_id_to_find].vel = (float4)(cell_id_to_find, cell_id_to_find, cell_id_to_find, cell_id_to_find);
#endif
	particles[cell_id_to_find].type_ = cell_id_to_find + 1;
}

/**Initialization of neighbour list by -1
* what means that it's no neighbours.
*/
__kernel void _ker_init_ext_particles(__global struct extendet_particle * ext_particles){
	int cell_id_to_find = get_global_id(0);
	ext_particles[cell_id_to_find].p_id = cell_id_to_find;
	for(int i=0;i<NEIGHBOUR_COUNT;++i){
		ext_particles[cell_id_to_find].neigbour_list[i] = -1;
	}
}

__kernel void _ker_grid_cell_indexing(__global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles,
									uint PARTICLE_COUNT,
									__global uint* grid_cell_index,
									uint grid_cell_count){
	int cell_id_to_find = get_global_id(0);

	//check for trivial cases
	if (cell_id_to_find > grid_cell_count){
		return;
	}
    if (PARTICLE_COUNT == 0) {
        /* array is empty */
        return;
    } else if (particles[0].cell_id > cell_id_to_find) {
        /* cell_id_to_find is lower than everyone else */
        return;
    } else if (particles[PARTICLE_COUNT - 1].cell_id < cell_id_to_find) {
        /* cell_id_to_find is greater than everyone else */
        return;
    }

	if (cell_id_to_find == 0){
		grid_cell_index[cell_id_to_find] = 0;
		return;
	}
	if (cell_id_to_find == grid_cell_count){
		grid_cell_index[cell_id_to_find] = PARTICLE_COUNT;
		return;
	}
	//end check

	//binary search
	int low = 0;
	int high = PARTICLE_COUNT - 1;
	bool converged = false;
	int particle_id = NO_PARTICLE_ID;
	int cur_part_cell_id = -1;

	while (!converged){
		if (low > high){
			converged = true;
			particle_id = NO_PARTICLE_ID;
			continue;
		}

		int middle = low + (high - low) / 2;
		cur_part_cell_id = particles[middle].cell_id;
		particle_id = middle;
		if (cell_id_to_find <= cur_part_cell_id){
			high = middle;
		} else {
			low = middle + 1;
		}

	}
	//end binary search

	//find lowest particle_id that gives cell_id_to_find and init grid_cell_index value
	if (!converged && particles[last].cell_id == cell_id_to_find){
		particle_id = last;
		if (low == high){
			particle_id = low;
		}
		//[QUEST] Is there no need to check if last == 0 ? I think so because if last == 0 it means that we wanted to find cell_id_to_find == 0. That case was handled in trivial cases

		//find lowest particle_id that gives cell_id_to_find
		// [QUEST] is the while condition below correct?
		while (particles[particle_id - 1] && particles[particle_id - 1].cell_id == cell_id_to_find){
			particle_id--;
		}
	} else {
		particle_id = NO_PARTICLE_ID;
	}
 	grid_cell_index[ cell_id_to_find ] = particle_id;
}

/** Calc current cell cell_id_to_find for each particles
*/
__kernel void _ker_calc_cell_id(__global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){

}

/** Searchin for neigbours foe each particles
*/
__kernel void _ker_neighbour_search(__global struct extendet_particle * ext_particles,
							   __global struct
							   #ifdef _DOUBLE_PRECISION
									particle_d
							   #else
									particle_f
							   #endif
									* particles){

}

int cellId(
		   int4 cellFactors_,
		   uint gridCellsX,
		   uint gridCellsY,
		   uint gridCellsZ//don't use
		   )
{
	int cellId_ = cellFactors_.x + cellFactors_.y * gridCellsX
		+ cellFactors_.z * gridCellsX * gridCellsY;
	return cellId_;
}
/** Caculation spatial hash cellId for every particle
 *  Kernel fill up particleIndex buffer.
 */
 int4 cellFactors(
				 __global struct
				#ifdef _DOUBLE_PRECISION
					particle_d
				#else
					particle_f
				#endif
					* particle,
				 float xmin,
				 float ymin,
				 float zmin,
				 float hashGridCellSizeInv
				 )
{
	//xmin, ymin, zmin
	int4 result;
	result.x = (int)( particle.pos.x *  hashGridCellSizeInv );
	result.y = (int)( particle.pos.y *  hashGridCellSizeInv );
	result.z = (int)( particle.pos.z *  hashGridCellSizeInv );
	return result;
}
__kernel void hashParticles(
							__global struct
							#ifdef _DOUBLE_PRECISION
								particle_d
							#else
								particle_f
							#endif
								* particle,
							uint gridCellsX,
							uint gridCellsY,
							uint gridCellsZ,
							float hashGridCellSizeInv,
							float xmin,
							float ymin,
							float zmin,
							__global uint2 * particleIndex,
							uint   PARTICLE_COUNT
							)
{
	int cell_id_to_find = get_global_id( 0 );
	if( cell_id_to_find >= PARTICLE_COUNT ) return;
	float4 _position = particle[ cell_id_to_find ];
	int4 cellFactors_ = cellFactors( _position, xmin, ymin, zmin, hashGridCellSizeInv );
	int cellId_ = cellId( cellFactors_, gridCellsX, gridCellsY, gridCellsZ ) & 0xffffff; // truncate to low 16 bits
	uint2 result;
	PI_CELL_ID( result ) = cellId_;
	PI_SERIAL_ID( result ) = cell_id_to_find;
	particleIndex[ cell_id_to_find ] = result;
}