Apple doesn't actually define __CL_VERSION_1_0__ when version=1.0.

This might be a shared trait with others, so if the value isn't defined,
it is initialized.

#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, local uc*);
__inline signed char vp8_simple_filter_mask(signed char, signed char, uc, uc, uc, uc);
__inline signed char vp8_hevmask(signed char, local uc*);

__inline void vp8_mbfilter(signed char mask,signed char hev,local uc*);

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

constant int threads[3] = {16, 8, 8};

#ifndef __CL_VERSION_1_0__
#define __CL_VERSION_1_0__ 100
#endif

#if __OPENCL_VERSION__ == __CL_VERSION_1_0__
#define clamp(x,y,z) vp8_char_clamp(x)
#endif

char vp8_char_clamp(int in){
    if (in > 127)
        return 127;
    if (in < -128)
        return -128;

    return in;
}

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,
    local uc *base
)
{

    local uc *op1 = &base[0];
    local uc *op0 = &base[1];
    local uc *oq0 = &base[2];
    local uc *oq1 = &base[3];

    char vp8_filter, Filter1, Filter2;
    char u;

    char ps1 = (char) * op1 ^ 0x80;
    char ps0 = (char) * op0 ^ 0x80;
    char qs0 = (char) * oq0 ^ 0x80;
    char qs1 = (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;
}

// Filters horizontal edges of inner blocks in a Macroblock
void vp8_loop_filter_horizontal_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int cur_iter,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    int filter_level = filters[block];
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if (filters[num_blocks*filter_type + block] > 0){
        if (cur_iter == 0 || plane == 0){
            if (cur_iter > 0){
                num_planes = 1;
            }

            if (filter_level){
                int p = pitches[plane];
                int block_offset = num_blocks*11 + cur_iter*num_blocks*num_planes + block*num_planes+plane;
                int s_off = offsets[block_offset+priority_offset];

                size_t i = get_global_id(0);

                int  hev = 0; /* high edge variance */
                signed char mask = 0;

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

                if (i < threads[plane]){
                    lf_info = &lfi[filter_level];
                    flimit = lf_info->flim;
                    limit = lf_info->lim;
                    thresh = lf_info->thr;

                    s_off += i;
                    s_data += plane*128+i*8;

                    s_data[0] = s_base[s_off - 4*p];
                    s_data[1] = s_base[s_off - 3*p];
                    s_data[2] = s_base[s_off - 2*p];
                    s_data[3] = s_base[s_off - 1*p];
                    s_data[4] = s_base[s_off];
                    s_data[5] = s_base[s_off + p];
                    s_data[6] = s_base[s_off + 2*p];
                    s_data[7] = s_base[s_off + 3*p];

                    mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                    hev = vp8_hevmask(thresh[i], &s_data[2]);

                    vp8_filter(mask, hev, &s_data[2]);

                    s_base[s_off - 2*p] = s_data[2];
                    s_base[s_off - p  ] = s_data[3];
                    s_base[s_off      ] = s_data[4];
                    s_base[s_off + p  ] = s_data[5];

                }
            }
        }
    }
}

void vp8_loop_filter_vertical_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int cur_iter,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if ((cur_iter ==1 || plane == 0) && filters[num_blocks*filter_type + block] > 0){
        if (cur_iter > 1){
            num_planes = 1;
        }

        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->flim;

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

                s_off += p * i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4];
                s_data[1] = s_base[s_off - 3];
                s_data[2] = s_base[s_off - 2];
                s_data[3] = s_base[s_off - 1];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + 1];
                s_data[6] = s_base[s_off + 2];
                s_data[7] = s_base[s_off + 3];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_filter(mask, hev, s_data+2);

                s_base[s_off - 2] = s_data[2];
                s_base[s_off - 1] = s_data[3];
                s_base[s_off    ] = s_data[4];
                s_base[s_off + 1] = s_data[5];

            }
        }
    }
}

void vp8_mbloop_filter_horizontal_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if (filters[num_blocks*ROWS_LOCATION + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = 8*num_blocks + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->mbflim;

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

                s_off += i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4*p];
                s_data[1] = s_base[s_off - 3*p];
                s_data[2] = s_base[s_off - 2*p];
                s_data[3] = s_base[s_off - 1*p];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + p];
                s_data[6] = s_base[s_off + 2*p];
                s_data[7] = s_base[s_off + 3*p];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_mbfilter(mask, hev, s_data + 1);

                s_base[s_off - 3*p] = s_data[1];
                s_base[s_off - 2*p] = s_data[2];
                s_base[s_off - 1*p] = s_data[3];
                s_base[s_off      ] = s_data[4];
                s_base[s_off + p  ] = s_data[5];
                s_base[s_off + 2*p] = s_data[6];
            }
        }
    }
}

kernel void vp8_loop_filter_horizontal_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim, //unused for normal filters
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    vp8_mbloop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi,
            filters,  priority_offset, s_data);
    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 0, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data);
}

void vp8_mbloop_filter_vertical_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    if (filters[num_blocks*filter_type + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->mbflim;

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

                s_off += p * i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4];
                s_data[1] = s_base[s_off - 3];
                s_data[2] = s_base[s_off - 2];
                s_data[3] = s_base[s_off - 1];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + 1];
                s_data[6] = s_base[s_off + 2];
                s_data[7] = s_base[s_off + 3];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_mbfilter(mask, hev, s_data + 1);

                s_base[s_off - 3] = s_data[1];
                s_base[s_off - 2] = s_data[2];
                s_base[s_off - 1] = s_data[3];
                s_base[s_off    ] = s_data[4];
                s_base[s_off + 1] = s_data[5];
                s_base[s_off + 2] = s_data[6];
            }
        }
    }
}

kernel void vp8_loop_filter_all_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    //Prefetch vertical edge source pixels into global cache
    for(int plane = 0; plane < 3; plane++){
        int p = pitches[plane];
        int block_offset = get_global_id(2)*3+plane;
        int s_off = offsets[block_offset+priority_offset];
        for (int thread = 0; thread < 16; thread++){
            prefetch(&s_base[s_off+p*thread-4], 8);
        }
    }

    vp8_mbloop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            COLS_LOCATION, priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 1, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data);

    barrier(CLK_GLOBAL_MEM_FENCE);

    //Prefetch horizontal source pixels
#if 0
    for(int plane = 0; plane < 3; plane++){
        int p = pitches[plane];
        int block_offset = get_global_id(2)*3+plane;
        int s_off = offsets[block_offset+priority_offset];
        for (int thread = 0; thread < 16; thread++){
            prefetch(&s_base[s_off+thread-4*p], 8*p);
        }
    }
#endif

    vp8_mbloop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi,
            filters,  priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 0, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data);

}

kernel void vp8_loop_filter_vertical_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    vp8_mbloop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            COLS_LOCATION, priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 1, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data);

}

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 *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
)
{
    private size_t plane = get_global_id(1);
    private size_t block = get_global_id(2);
    local size_t num_planes;
    local size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    local unsigned char s_data[16*8*3];

    if (filters[num_blocks*filter_type + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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 *filters, /* Filters for each block being processed */
    int use_mbflim, /* Use lfi->flim or lfi->mbflim, need once per kernel call */
    int filter_type, /* Should dc_diffs, rows, or cols be used?*/
    int cur_iter,
    int priority_offset
)
{
    private size_t plane = get_global_id(1);
    private size_t block = get_global_id(2);
    local size_t num_planes;
    local size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    local unsigned char s_data[16*8*3];

    if (filters[filter_type * num_blocks + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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,
    local uc *base
)
{
    signed char s, u;
    signed char vp8_filter;

    local uc *op2 = base;
    local uc *op1 = base + 1;
    local uc *op0 = base + 2;
    local uc *oq0 = base + 3;
    local uc *oq1 = base + 4;
    local uc *oq2 = base + 5;

    char2 filter;

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

    char4 qs = { *oq0, *oq1, *oq2, 0 };
    qs ^= (char4){0x80, 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.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, local uc pq[])
{
    signed char hev = 0;
    hev  |= (abs(pq[0] - pq[1]) > thresh) * -1;
    hev  |= (abs(pq[3] - pq[2]) > 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,
        local uc *pq)
{
    signed char mask = 0;
    mask |= (abs(pq[0] - pq[1]) > limit) * -1;
    mask |= (abs(pq[1] - pq[2]) > limit) * -1;
    mask |= (abs(pq[2] - pq[3]) > limit) * -1;
    mask |= (abs(pq[5] - pq[4]) > limit) * -1;
    mask |= (abs(pq[6] - pq[5]) > limit) * -1;
    mask |= (abs(pq[7] - pq[6]) > limit) * -1;
    mask |= (abs(pq[3] - pq[4]) * 2 + abs(pq[2] - pq[5]) / 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, local uc*);
__inline signed char vp8_simple_filter_mask(signed char, signed char, uc, uc, uc, uc);
__inline signed char vp8_hevmask(signed char, local uc*);

__inline void vp8_mbfilter(signed char mask,signed char hev,local uc*);

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

constant int threads[3] = {16, 8, 8};

#if __OPENCL_VERSION__ == __CL_VERSION_1_0__
#define clamp(x,y,z) vp8_char_clamp(x)
#endif

char vp8_char_clamp(int in){
    if (in > 127)
        return 127;
    if (in < -128)
        return -128;

    return in;
}

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,
    local uc *base
)
{

    local uc *op1 = &base[0];
    local uc *op0 = &base[1];
    local uc *oq0 = &base[2];
    local uc *oq1 = &base[3];

    char vp8_filter, Filter1, Filter2;
    char u;

    char ps1 = (char) * op1 ^ 0x80;
    char ps0 = (char) * op0 ^ 0x80;
    char qs0 = (char) * oq0 ^ 0x80;
    char qs1 = (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;
}

// Filters horizontal edges of inner blocks in a Macroblock
void vp8_loop_filter_horizontal_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int cur_iter,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    int filter_level = filters[block];
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if (filters[num_blocks*filter_type + block] > 0){
        if (cur_iter == 0 || plane == 0){
            if (cur_iter > 0){
                num_planes = 1;
            }

            if (filter_level){
                int p = pitches[plane];
                int block_offset = num_blocks*11 + cur_iter*num_blocks*num_planes + block*num_planes+plane;
                int s_off = offsets[block_offset+priority_offset];

                size_t i = get_global_id(0);

                int  hev = 0; /* high edge variance */
                signed char mask = 0;

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

                if (i < threads[plane]){
                    lf_info = &lfi[filter_level];
                    flimit = lf_info->flim;
                    limit = lf_info->lim;
                    thresh = lf_info->thr;

                    s_off += i;
                    s_data += plane*128+i*8;

                    s_data[0] = s_base[s_off - 4*p];
                    s_data[1] = s_base[s_off - 3*p];
                    s_data[2] = s_base[s_off - 2*p];
                    s_data[3] = s_base[s_off - 1*p];
                    s_data[4] = s_base[s_off];
                    s_data[5] = s_base[s_off + p];
                    s_data[6] = s_base[s_off + 2*p];
                    s_data[7] = s_base[s_off + 3*p];

                    mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                    hev = vp8_hevmask(thresh[i], &s_data[2]);

                    vp8_filter(mask, hev, &s_data[2]);

                    s_base[s_off - 2*p] = s_data[2];
                    s_base[s_off - p  ] = s_data[3];
                    s_base[s_off      ] = s_data[4];
                    s_base[s_off + p  ] = s_data[5];

                }
            }
        }
    }
}

void vp8_loop_filter_vertical_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int cur_iter,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if ((cur_iter ==1 || plane == 0) && filters[num_blocks*filter_type + block] > 0){
        if (cur_iter > 1){
            num_planes = 1;
        }

        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->flim;

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

                s_off += p * i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4];
                s_data[1] = s_base[s_off - 3];
                s_data[2] = s_base[s_off - 2];
                s_data[3] = s_base[s_off - 1];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + 1];
                s_data[6] = s_base[s_off + 2];
                s_data[7] = s_base[s_off + 3];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_filter(mask, hev, s_data+2);

                s_base[s_off - 2] = s_data[2];
                s_base[s_off - 1] = s_data[3];
                s_base[s_off    ] = s_data[4];
                s_base[s_off + 1] = s_data[5];

            }
        }
    }
}

void vp8_mbloop_filter_horizontal_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

    if (filters[num_blocks*ROWS_LOCATION + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = 8*num_blocks + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->mbflim;

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

                s_off += i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4*p];
                s_data[1] = s_base[s_off - 3*p];
                s_data[2] = s_base[s_off - 2*p];
                s_data[3] = s_base[s_off - 1*p];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + p];
                s_data[6] = s_base[s_off + 2*p];
                s_data[7] = s_base[s_off + 3*p];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_mbfilter(mask, hev, s_data + 1);

                s_base[s_off - 3*p] = s_data[1];
                s_base[s_off - 2*p] = s_data[2];
                s_base[s_off - 1*p] = s_data[3];
                s_base[s_off      ] = s_data[4];
                s_base[s_off + p  ] = s_data[5];
                s_base[s_off + 2*p] = s_data[6];
            }
        }
    }
}

kernel void vp8_loop_filter_horizontal_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches, /* pitch */
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim, //unused for normal filters
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    vp8_mbloop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi,
            filters,  priority_offset, s_data);
    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 0, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data);
}

void vp8_mbloop_filter_vertical_edge_worker(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int filter_type,
    int priority_offset,
    local unsigned char *s_data
){
    size_t plane = get_global_id(1);
    size_t block = get_global_id(2);
    size_t num_planes;
    size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    if (filters[num_blocks*filter_type + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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];
                flimit = lf_info->mbflim;

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

                s_off += p * i;
                s_data += plane*128+i*8;

                s_data[0] = s_base[s_off - 4];
                s_data[1] = s_base[s_off - 3];
                s_data[2] = s_base[s_off - 2];
                s_data[3] = s_base[s_off - 1];
                s_data[4] = s_base[s_off];
                s_data[5] = s_base[s_off + 1];
                s_data[6] = s_base[s_off + 2];
                s_data[7] = s_base[s_off + 3];

                mask = vp8_filter_mask(limit[i], flimit[i], s_data);

                hev = vp8_hevmask(thresh[i], &s_data[2]);

                vp8_mbfilter(mask, hev, s_data + 1);

                s_base[s_off - 3] = s_data[1];
                s_base[s_off - 2] = s_data[2];
                s_base[s_off - 1] = s_data[3];
                s_base[s_off    ] = s_data[4];
                s_base[s_off + 1] = s_data[5];
                s_base[s_off + 2] = s_data[6];
            }
        }
    }
}

kernel void vp8_loop_filter_all_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    //Prefetch vertical edge source pixels into global cache
    for(int plane = 0; plane < 3; plane++){
        int p = pitches[plane];
        int block_offset = get_global_id(2)*3+plane;
        int s_off = offsets[block_offset+priority_offset];
        for (int thread = 0; thread < 16; thread++){
            prefetch(&s_base[s_off+p*thread-4], 8);
        }
    }

    vp8_mbloop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            COLS_LOCATION, priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 1, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data);

    barrier(CLK_GLOBAL_MEM_FENCE);

    //Prefetch horizontal source pixels
#if 0
    for(int plane = 0; plane < 3; plane++){
        int p = pitches[plane];
        int block_offset = get_global_id(2)*3+plane;
        int s_off = offsets[block_offset+priority_offset];
        for (int thread = 0; thread < 16; thread++){
            prefetch(&s_base[s_off+thread-4*p], 8*p);
        }
    }
#endif

    vp8_mbloop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi,
            filters,  priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 0, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data);

}

kernel void vp8_loop_filter_vertical_edges_kernel(
    global unsigned char *s_base,
    global int *offsets,
    global int *pitches,
    global loop_filter_info *lfi,
    global int *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
){
    local unsigned char s_data[16*8*3];

    vp8_mbloop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            COLS_LOCATION, priority_offset, s_data);

    //YUV planes, then 2 more passes of Y plane
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 1, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data);

}

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 *filters,
    int use_mbflim,
    int filter_type,
    int cur_iter,
    int priority_offset
)
{
    private size_t plane = get_global_id(1);
    private size_t block = get_global_id(2);
    local size_t num_planes;
    local size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    local unsigned char s_data[16*8*3];

    if (filters[num_blocks*filter_type + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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 *filters, /* Filters for each block being processed */
    int use_mbflim, /* Use lfi->flim or lfi->mbflim, need once per kernel call */
    int filter_type, /* Should dc_diffs, rows, or cols be used?*/
    int cur_iter,
    int priority_offset
)
{
    private size_t plane = get_global_id(1);
    private size_t block = get_global_id(2);
    local size_t num_planes;
    local size_t num_blocks;
    num_planes = get_global_size(1);
    num_blocks = get_global_size(2);

    local unsigned char s_data[16*8*3];

    if (filters[filter_type * num_blocks + block] > 0){
        int filter_level = filters[block];
        if (filter_level){
            int p = pitches[plane];
            int block_offset = cur_iter*num_blocks*num_planes + block*num_planes+plane;
            int s_off = offsets[block_offset+priority_offset];

            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,
    local uc *base
)
{
    signed char s, u;
    signed char vp8_filter;

    local uc *op2 = base;
    local uc *op1 = base + 1;
    local uc *op0 = base + 2;
    local uc *oq0 = base + 3;
    local uc *oq1 = base + 4;
    local uc *oq2 = base + 5;

    char2 filter;

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

    char4 qs = { *oq0, *oq1, *oq2, 0 };
    qs ^= (char4){0x80, 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.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, local uc pq[])
{
    signed char hev = 0;
    hev  |= (abs(pq[0] - pq[1]) > thresh) * -1;
    hev  |= (abs(pq[3] - pq[2]) > 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,
        local uc *pq)
{
    signed char mask = 0;
    mask |= (abs(pq[0] - pq[1]) > limit) * -1;
    mask |= (abs(pq[1] - pq[2]) > limit) * -1;
    mask |= (abs(pq[2] - pq[3]) > limit) * -1;
    mask |= (abs(pq[5] - pq[4]) > limit) * -1;
    mask |= (abs(pq[6] - pq[5]) > limit) * -1;
    mask |= (abs(pq[7] - pq[6]) > limit) * -1;
    mask |= (abs(pq[3] - pq[4]) * 2 + abs(pq[2] - pq[5]) / 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;
}


Added exppart to opencl routines.

Added exppart to opencl routines.

/*
  finite_sum_opencl.cl

  OpenCL code for computing the finite sum in the computation of the Riemann
  theta function.
*/

/*
 */
#define PI 3.141592653589793

float exppart(int, __global float*, __global float*, __global float*, int,
	      __global float*, int);

float normpart(int, __global float*, __global float*, int,
	       __global float* int);

/*
  Given integer vector S[i], compte the exppart of the sum.
*/
float exppart(int i,
	      __global float* X,
	      __global float* intshift,
	      __global float* x,
	      int N,
	      __global float* S,
	      int L)
{
  // perform dot product of matrix mult
  float sum = 0;
  float v_j, Xw_j;
  for (int j=0; j<N; j++) {
    // jth component of a-intshift
    vj = S[i*N+j]-intshift[j];

    // jth component of (1/2) X*(a-intshift) + x
    Xw_j = 0;
    for (int k=0; k<N; k++) {
      Xw_j += 0.5 * X[j + N*k] * (S[i*N+k]-intshift[k]) + x[j];
    }

    // add the jth component to the dot product container
    sum += v_j*Xw_j
  }

  // scale and return
  sum *= 2*PI;
  return sum
}

/*
  Given integer vector S[i], compte the exppart of the sum.
*/
float normpart(float* a,
	       __global float* T,
	       __global float* fracshift,
	       int N,
	       __global float* S,
	       int L);
{

}

__kernel void finite_sum(__global float* X,
			 __global float* Yinv,
			 __global float* T,
			 __global float* x,
			 __global float* y,
			 int N
			 __global float* S,
			 int L)
{
  int i = get_global_id(0);   // index of the integer point
}

/*
  finite_sum_opencl.cl

  OpenCL code for computing the finite sum in the computation of the Riemann
  theta function.
*/

/*
 */
float vdot(float* x, float* y, int n){

}


Correct ilogb implementation

Correct ilogb implementation

/* OpenCL built-in library: ilogb()

   Copyright (c) 2011 Universidad Rey Juan Carlos

   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.
*/

#include "templates.h"

DEFINE_BUILTIN_K_V(ilogb)

/* OpenCL built-in library: ilogb()

   Copyright (c) 2011 Universidad Rey Juan Carlos

   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.
*/

#include "templates.h"

DEFINE_BUILTIN_J_V(ilogb)


lib/kernel: fix exp10()

lib/kernel: fix exp10()

/* OpenCL built-in library: exp10()

   Copyright (c) 2011 Erik Schnetter <eschnetter@perimeterinstitute.ca>
                      Perimeter Institute for Theoretical Physics

   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.
*/

#include "templates.h"

DEFINE_EXPR_V_V(exp10, exp(a*TYPED_CONST(stype, M_LN10_H, M_LN10_F, M_LN10)))

DEFINE_EXPR_F_F(half_exp10, exp10(a))

/* OpenCL built-in library: exp10()

   Copyright (c) 2011 Erik Schnetter <eschnetter@perimeterinstitute.ca>
                      Perimeter Institute for Theoretical Physics

   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.
*/

#include "templates.h"

DEFINE_EXPR_V_V(exp10, exp10(a*TYPED_CONST(stype, M_LN10_H, M_LN10_F, M_LN10)))

DEFINE_EXPR_F_F(half_exp10, exp10(a))


removed vestigial kernels

removed vestigial kernels

__kernel void Sum_A_ID(const int ySize, __global int* a, __global int* c)
{

    unsigned int i = get_global_id(0);
	unsigned int j = get_global_id(1);

	unsigned int id = i + ySize*j;

    c[id] = a[id] + id;
}

__kernel void getID(const int ySize, __global int* c)
{

    unsigned int i = get_global_id(0);
	unsigned int j = get_global_id(1);

	unsigned int id = i + ySize*j;

    c[id] = id;
}

__kernel void ToggleCell(const int ySize, __global int* a, __global int* c)
{

    unsigned int i = get_global_id(0);
	unsigned int j = get_global_id(1);

	unsigned int id = i + ySize*j;

	if(a[id]==0){c[id]=1;}else{c[id]=0;}

}

__kernel void Conway(const int ySize, __global int* a, __global int* c)
{

    unsigned int i = get_global_id(0);
	unsigned int j = get_global_id(1);
	unsigned int my_id = i + ySize*j;
	unsigned int count = 0;

	if(a[my_id - 1] == 1) {count = count + 1;}
	if(a[my_id + 1] == 1) {count = count + 1;}
	if(a[my_id - 1 - ySize] == 1) {count = count + 1;}
	if(a[my_id - 1 + ySize] == 1) {count = count + 1;}
	if(a[my_id + 1 - ySize] == 1) {count = count + 1;}
	if(a[my_id + 1 + ySize] == 1) {count = count + 1;}
	if(a[my_id - ySize] == 1) {count = count + 1;}
	if(a[my_id + ySize] == 1) {count = count + 1;}

	if(count != 2){
		if(count==3){
			c[my_id]=1;
		}else{
			c[my_id]=0;
		}
	}

}


add boundary columns for even and odd buffers

#include "ocl_platform.cl"

// number of banks in local memory
// LDS_BANKS

//////////////////////////
// dimensions of window
// WIN_SIZE_X	assume this equals number of work items in work group
// WIN_SIZE_Y
///////////////////////////

//  scratch buffer (in local memory of GPU) where block of input image is stored.
//  All operations expect WIN_SIZE_X threads.

 /**

Layout for scratch buffer

Odd and even columns are separated. (Generates less bank conflicts when using lifting scheme.)
All even columns are stored first, then all odd columns.

Left (even) boundary column
Even Columns
Right (even) boundary column
Left (odd) boundary column
Odd Columns

 **/

#define VERTICAL_STRIDE_EVEN 66  // WIN_SIZE_X/2 + 2 boundary columns
#define VERTICAL_STRIDE_ODD 65   // WIN_SIZE_X/2 + 1 boundary column

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE_EVEN            528	// VERTICAL_STRIDE_EVEN * WIN_SIZE_Y
#define BUFFER_SIZE_ODD             520	// VERTICAL_STRIDE_ODD * WIN_SIZE_Y

#define TOTAL_BUFFER_SIZE     1048  // BUFFER_SIZE_EVEN + BUFFER_SIZE_ODD
#define TOTAL_BUFFER_SIZE_X2  2096
#define TOTAL_BUFFER_SIZE_X3  3144

#define STRIDE_HIGH_TO_LOW  -462   // VERTICAL_STRIDE_EVEN - BUFFER_SIZE_SIZE_EVEN

///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

// read pixel from local buffer
int4 readPixel( LOCAL int*  restrict  src) {
	return (int4)(*src, *(src+TOTAL_BUFFER_SIZE),  *(src+TOTAL_BUFFER_SIZE_X2),  *(src+TOTAL_BUFFER_SIZE_X3)) ;
}

//write pixel to column
inline void writePixel(int4 pix, LOCAL int*  restrict  dest) {
	*dest = pix.x;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.y;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.z;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.w;
}

void writeColumnToOutput(LOCAL int* restrict currentScratch, __write_only image2d_t odata, int firstY, int height, int halfHeight){
	// write points to destination
	int2 posOut = {getGlobalId(0), firstY>>1};
	for (int j = 0; j < WIN_SIZE_Y; j+=2) {

	    // even
	    if (posOut.y >= halfHeight)
			break;

		write_imagei(odata, posOut,readPixel(currentScratch));

		// odd
		currentScratch += BUFFER_SIZE_EVEN ;
		posOut.y+= halfHeight + 1;
		if (posOut.y >= height)
			break;

		write_imagei(odata, posOut,readPixel(currentScratch));

		currentScratch += STRIDE_HIGH_TO_LOW;
		posOut.y -= halfHeight;
	}
}

// offset when transforming columns
inline int getScratchColumnOffset(){
   return 1 + (getLocalId(0) >> 1);
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL run(__read_only image2d_t idata, __write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {

	if (getGlobalId(0) >= width)
	    return;

	const int halfWinSizeX = WIN_SIZE_X >> 1;

	int boundaryShift = 0;
	if (getGlobalId(0) == 1)				// left odd boundary column
		boundaryShift =  -1;
	else if (getGlobalId(0) == 2)           // left even boundary column
		boundaryShift = -2;
	else if (getGlobalId(0) == width - 2)   // right even boundary column
	    boundaryShift =  1;

    const unsigned int halfHeight = height >> 1;
	LOCAL int scratch[TOTAL_BUFFER_SIZE << 2];
	float yDelta = 1.0/height;
	int firstY = getGlobalId(1) * (steps * WIN_SIZE_Y);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -1 point
	const float2 posIn = (float2)(getGlobalId(0), firstY - 1) /  (float2)(width, height);
	int4 minusOne = read_imagei(idata, sampler, posIn);

	// read 0 point
	posIn.y += yDelta;
	int4 current = read_imagei(idata, sampler, posIn);

	// transform -1 point (no need to write to local memory)
	minusOne -= ( read_imagei(idata, sampler, (float2)(posIn.x, (firstY - 2)*yDelta)) + current) >> 1;

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform columns, and store in local scratch
		LOCAL int* currentScratch = scratch + getScratchColumnOffset();
		for (int j = 0; j < WIN_SIZE_Y>>1; ++j) {

			///////////////////////////////////////////////////////////////////////////////////////////
			// fetch next two pixels, then transform and write current (odd) and last (even)

			// read current plus one (odd) point
			posIn.y += yDelta;
			if (posIn.y >= 1)
				break;
			int4 currentPlusOne = read_imagei(idata, sampler, posIn);

			// read current plus two (even) point
			posIn.y += yDelta;
			int4 currentPlusTwo = read_imagei(idata, sampler, posIn);

			// transform current plus one (odd) point
			currentPlusOne -= (current + currentPlusTwo) >> 1;

			// transform current (even) point
			current += (minusOne + currentPlusOne + (int4)(2,2,2,2)) >> 2;

			//write current (even)
			writePixel(current, currentScratch);

			//write odd
			currentScratch += BUFFER_SIZE_EVEN;
			writePixel(currentPlusOne, currentScratch);

			//advance scratch pointer
			currentScratch += STRIDE_HIGH_TO_LOW;

			//update registers
			minusOne = currentPlusOne;
			current = currentPlusTwo;
		}

		//4. transform rows

		//5. write local buffer to destination image
		writeColumnToOutput(scratch + getScratchColumnOffset(), odata, firstY, height, halfHeight);

		// move to next step
		firstY += WIN_SIZE_Y;
	}
}

////////////////////////////////////////////
/*
/// Returns index ranging from 0 to num work items, such that first half
/// of the work items get even indices and others get odd indices. Each work item
/// gets a different index.
/// Example: (for WIN_SiZE_X == 8)   getLocalId(0):   0  1  2  3  4  5  6  7
///									 parityIdx:		  0  2  4  6  1  3  5  7

/// @return parity-separated index of work item
inline int parityIdx() {
	return (getLocalId(0) << 1) - (WIN_SIZE_X - 1) * (getLocalId(0) / (WIN_SIZE_X >> 1));
}

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE_EVEN 66			// 2*BOUNDARY_X + (WIN_SIZE_X / 2)

#define VERTICAL_STRIDE_ODD  65          //  BOUNDARY_X  + (WIN_SIZE_X / 2)

// size of one of two buffers (odd or even)
#define BUFFER_SIZE_EVEN     528          // VERTICAL_STRIDE_EVEN * WIN_SIZE_Y
#define BUFFER_SIZE_ODD     528          // VERTICAL_STRIDE_ODD * WIN_SIZE_Y

/// padding between even and odd buffers: used to reduce bank conflicts
#define PADDING			32           // LDS_BANKS - ((BUFFER_SIZE + LDS_BANKS / 2) % LDS_BANKS);

// offset of the odd columns buffer from the beginning of data buffer
#define ODD_OFFSET 560              // BUFFER_SIZE + PADDING

// size of buffer for both even and odd columns
#define TOTAL_BUFFER_SIZE  1088     // 2 * BUFFER_SIZE + PADDING;
#define TOTAL_BUFFER_SIZE_X2  2176
#define TOTAL_BUFFER_SIZE_X3  3264
*/
////////////////////////////////////////////

#include "ocl_platform.cl"

// number of banks in local memory
// LDS_BANKS

//////////////////////////
// dimensions of window
// WIN_SIZE_X	assume this equals number of work items in work group
// WIN_SIZE_Y
///////////////////////////

//  scratch buffer (in local memory of GPU) where block of input image is stored.
//  All operations expect WIN_SIZE_X threads.

 /**

Layout for scratch buffer

Odd and even columns are separated. (Generates less bank conflicts when using lifting scheme.)
All even columns are stored first, then all odd columns.

Left (even) boundary column
Even Columns
Right (even) boundary column
Left (odd) boundary column
Odd Columns

 **/

#define VERTICAL_STRIDE 64
#define STRIDE_HIGH_TO_LOW  -448   // VERTICAL_STRIDE - BUFFER_SIZE

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// VERTICAL_STRIDE * WIN_SIZE_Y
#define TOTAL_BUFFER_SIZE     1024  //2 * BUFFER_SIZE
#define TOTAL_BUFFER_SIZE_X2  2048
#define TOTAL_BUFFER_SIZE_X3  3072

///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

// read pixel from local buffer
int4 readPixel( LOCAL int*  restrict  src) {
	return (int4)(*src, *(src+TOTAL_BUFFER_SIZE),  *(src+TOTAL_BUFFER_SIZE_X2),  *(src+TOTAL_BUFFER_SIZE_X3)) ;
}

//write pixel to column
inline void writePixel(int4 pix, LOCAL int*  restrict  dest) {
	*dest = pix.x;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.y;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.z;
	dest += TOTAL_BUFFER_SIZE;
	*dest = pix.w;
}

void writeColumnToOutput(LOCAL int* restrict currentScratch, __write_only image2d_t odata, int firstY, int height, int halfHeight){
	// write points to destination
	int2 posOut = {getGlobalId(0), firstY>>1};
	for (int j = 0; j < WIN_SIZE_Y; j+=2) {

	    // even
	    if (posOut.y >= halfHeight)
			break;

		write_imagei(odata, posOut,readPixel(currentScratch));

		// odd
		currentScratch += BUFFER_SIZE ;
		posOut.y+= halfHeight + 1;
		if (posOut.y >= height)
			break;

		write_imagei(odata, posOut,readPixel(currentScratch));

		currentScratch += STRIDE_HIGH_TO_LOW;
		posOut.y -= halfHeight;
	}
}

// offset when transforming columns
inline int getScratchColumnOffset(){
   return getLocalId(0) >> 1;
}

// assumptions: width and height are both even
// (we will probably have to relax these assumptions in the future)
void KERNEL run(__read_only image2d_t idata, __write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {

	if (getGlobalId(0) >= width)
	    return;

	const int halfWinSizeX = WIN_SIZE_X >> 1;

	int boundaryShift = 0;
	if (getGlobalId(0) == 1)				// left odd boundary column
		boundaryShift =  -1;
	else if (getGlobalId(0) == 2)           // left even boundary column
		boundaryShift = -2;
	else if (getGlobalId(0) == width - 2)   // right even boundary column
	    boundaryShift =  1;

    const unsigned int halfHeight = height >> 1;
	LOCAL int scratch[TOTAL_BUFFER_SIZE << 2];
	float yDelta = 1.0/height;
	int firstY = getGlobalId(1) * (steps * WIN_SIZE_Y);

	//0. Initialize: fetch first pixel (and 2 top boundary pixels)

	// read -1 point
	const float2 posIn = (float2)(getGlobalId(0), firstY - 1) /  (float2)(width, height);
	int4 minusOne = read_imagei(idata, sampler, posIn);

	// read 0 point
	posIn.y += yDelta;
	int4 current = read_imagei(idata, sampler, posIn);

	// transform -1 point (no need to write to local memory)
	minusOne -= ( read_imagei(idata, sampler, (float2)(posIn.x, (firstY - 2)*yDelta)) + current) >> 1;

	for (int i = 0; i < steps; ++i) {

		// 1. read from source image, transform columns, and store in local scratch
		LOCAL int* currentScratch = scratch + getScratchColumnOffset();
		for (int j = 0; j < WIN_SIZE_Y>>1; ++j) {

			///////////////////////////////////////////////////////////////////////////////////////////
			// fetch next two pixels, then transform and write current (odd) and last (even)

			// read current plus one (odd) point
			posIn.y += yDelta;
			if (posIn.y >= 1)
				break;
			int4 currentPlusOne = read_imagei(idata, sampler, posIn);

			// read current plus two (even) point
			posIn.y += yDelta;
			int4 currentPlusTwo = read_imagei(idata, sampler, posIn);

			// transform current plus one (odd) point
			currentPlusOne -= (current + currentPlusTwo) >> 1;

			// transform current (even) point
			current += (minusOne + currentPlusOne + (int4)(2,2,2,2)) >> 2;

			//write current (even)
			writePixel(current, currentScratch);

			//write odd
			currentScratch += BUFFER_SIZE;
			writePixel(currentPlusOne, currentScratch);

			//advance scratch pointer
			currentScratch += STRIDE_HIGH_TO_LOW;

			//update registers
			minusOne = currentPlusOne;
			current = currentPlusTwo;
		}

		//4. transform rows

		//5. write local buffer to destination image
		writeColumnToOutput(scratch + getScratchColumnOffset(), odata, firstY, height, halfHeight);

		// move to next step
		firstY += WIN_SIZE_Y;
	}
}

////////////////////////////////////////////
/*
/// Returns index ranging from 0 to num work items, such that first half
/// of the work items get even indices and others get odd indices. Each work item
/// gets a different index.
/// Example: (for WIN_SiZE_X == 8)   getLocalId(0):   0  1  2  3  4  5  6  7
///									 parityIdx:		  0  2  4  6  1  3  5  7

/// @return parity-separated index of work item
inline int parityIdx() {
	return (getLocalId(0) << 1) - (WIN_SIZE_X - 1) * (getLocalId(0) / (WIN_SIZE_X >> 1));
}

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE_EVEN 66			// 2*BOUNDARY_X + (WIN_SIZE_X / 2)

#define VERTICAL_STRIDE_ODD  65          //  BOUNDARY_X  + (WIN_SIZE_X / 2)

// size of one of two buffers (odd or even)
#define BUFFER_SIZE_EVEN     528          // VERTICAL_STRIDE_EVEN * WIN_SIZE_Y
#define BUFFER_SIZE_ODD     528          // VERTICAL_STRIDE_ODD * WIN_SIZE_Y

/// padding between even and odd buffers: used to reduce bank conflicts
#define PADDING			32           // LDS_BANKS - ((BUFFER_SIZE + LDS_BANKS / 2) % LDS_BANKS);

// offset of the odd columns buffer from the beginning of data buffer
#define ODD_OFFSET 560              // BUFFER_SIZE + PADDING

// size of buffer for both even and odd columns
#define TOTAL_BUFFER_SIZE  1088     // 2 * BUFFER_SIZE + PADDING;
#define TOTAL_BUFFER_SIZE_X2  2176
#define TOTAL_BUFFER_SIZE_X3  3264
*/
////////////////////////////////////////////


simple average working now

simple average working now

#include "ocl_platform.cl"

/// Returns index ranging from 0 to num threads, such that first half
/// of threads get even indices and others get odd indices. Each thread
/// gets different index.
/// Example: (for 8 threads)   threadIdx.x:   0  1  2  3  4  5  6  7
///                              parityIdx:   0  2  4  6  1  3  5  7
/// @param THREADS  total count of participating threads
/// @return parity-separated index of thread
inline int parityIdx(int THREADS) {
	return (getLocalId(0) * 2) - (THREADS - 1) * (getLocalId(0) / (THREADS / 2));
}

// number of banks in local memory
#define LDS_BANKS 32

//////////////////////////
// dimensions of window
#define WIN_SIZE_X		128
#define WIN_SIZE_Y		8
///////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////
// Transform Buffer (in local memory of GPU) where block of input image is stored.

/// Odd and even columns are separated. (Generates less bank conflicts when using lifting scheme.)
/// All even columns are stored first, then all odd columns.
/// All operations expect BUF_SIZE_X threads.

/// BUF_SIZE_X       width of the buffer excluding two boundaries
///					(Also equal to the number of threads participating on all operations)
///					Must be divisible by 4.
/// BUF_SIZE_Y      height of buffer (total number of lines)
/// BOUNDARY_X		number of extra pixels at the left and right side
///					boundary is expected to be smaller than half BUF_SIZE_X
///					Must be divisible by 2, because half of the boundary lies on the left of the image,
///                 half lies on the right

#define BUF_SIZE_X      WIN_SIZE_X

#define BUF_SIZE_Y		11			 // WIN_SIZE_Y + 3 (two boundary columns on left, and one on right)

// x boundary
#define BOUNDARY_X		2

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE 65			// BOUNDARY_X + (BUF_SIZE_X / 2)

// size of one of two buffers (odd or even)
#define BUFFER_SIZE     715          // VERTICAL_STRIDE * BUF_SIZE_Y

/// padding between even and odd buffers: used to reduce bank conflicts
#define PADDING			5           // LDS_BANKS - ((BUFFER_SIZE + LDS_BANKS / 2) % LDS_BANKS);

// offset of the odd columns buffer from the beginning of data buffer
#define ODD_OFFSET 720              // BUFFER_SIZE + PADDING

// size of buffer for both even and odd columns
#define TOTAL_BUFFER_SIZE  1445      // 2 * BUFFER_SIZE + PADDING;
///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

void KERNEL run(__read_only image2d_t idata, __write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {
	LOCAL int scratch[TOTAL_BUFFER_SIZE*4];

	//cache last three pixels
	int pixCache[3];

	int firstY = get_global_id(1) *steps*WIN_SIZE_Y;

	//1. initialize column (and perhaps boundary column)

	//2. read column into scratch

	//3. vertically transform column

	//4. horizontally transform all rows corresponding to this column

	//5. write into destination

	int yIndex = getGlobalId(1) * steps * WIN_SIZE_Y;
	for (int i = 0; i < steps; ++i) {

	   //store a line in local memory
	   for (int j = 0; j < WIN_SIZE_Y; ++j) {

			const int2 pos = {getGlobalId(0), yIndex + j };

			const float2 posNormal = {pos.x/(float)width, pos.y/(float)height};
			int4 pix = read_imagei(idata, sampler, posNormal);

			int channelIndex = getLocalId(0) + j * WIN_SIZE_X;
			scratch[channelIndex] = pix.x;
			channelIndex += TOTAL_BUFFER_SIZE;
			scratch[channelIndex] = pix.y;
			channelIndex += TOTAL_BUFFER_SIZE;
			scratch[channelIndex] = pix.z;
			channelIndex += TOTAL_BUFFER_SIZE;
			scratch[channelIndex] = pix.w;

		}

	    localMemoryFence();
		// read from local memory and store in destination
		for (int j = 0; j < WIN_SIZE_Y; ++j) {
			const int2 pos = {getGlobalId(0), yIndex + j };
			if (pos.x < width && pos.y < height) {
				int4 pix1, pix2;
				//get pixel 1
				int channelIndex = getLocalId(0) + j * WIN_SIZE_X;
				pix1.x = scratch[channelIndex] ;
				channelIndex += TOTAL_BUFFER_SIZE;
				pix1.y = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix1.z = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix1.w = scratch[channelIndex];

				//get pixel 2
				channelIndex = getLocalId(0) + 1 + j * WIN_SIZE_X;
				pix2.x = scratch[channelIndex] ;
				channelIndex += TOTAL_BUFFER_SIZE;
				pix2.y = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix2.z = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix2.w = scratch[channelIndex];

				//write average ( there are boundary errors at the moment)
				write_imagei(odata, pos,(pix1+pix2)/2);

			}
		}
		yIndex += WIN_SIZE_Y;
		localMemoryFence();
	}

}

#include "ocl_platform.cl"

/// Returns index ranging from 0 to num threads, such that first half
/// of threads get even indices and others get odd indices. Each thread
/// gets different index.
/// Example: (for 8 threads)   threadIdx.x:   0  1  2  3  4  5  6  7
///                              parityIdx:   0  2  4  6  1  3  5  7
/// @param THREADS  total count of participating threads
/// @return parity-separated index of thread
inline int parityIdx(int THREADS) {
	return (getLocalId(0) * 2) - (THREADS - 1) * (getLocalId(0) / (THREADS / 2));
}

// number of banks in local memory
#define LDS_BANKS 32

//////////////////////////
// dimensions of window
#define WIN_SIZE_X		128
#define WIN_SIZE_Y		8
///////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////
// Transform Buffer (in local memory of GPU) where block of input image is stored.

/// Odd and even columns are separated. (Generates less bank conflicts when using lifting scheme.)
/// All even columns are stored first, then all odd columns.
/// All operations expect BUF_SIZE_X threads.

/// BUF_SIZE_X       width of the buffer excluding two boundaries
///					(Also equal to the number of threads participating on all operations)
///					Must be divisible by 4.
/// BUF_SIZE_Y      height of buffer (total number of lines)
/// BOUNDARY_X		number of extra pixels at the left and right side
///					boundary is expected to be smaller than half BUF_SIZE_X
///					Must be divisible by 2, because half of the boundary lies on the left of the image,
///                 half lies on the right

#define BUF_SIZE_X      WIN_SIZE_X

#define BUF_SIZE_Y		11			 // WIN_SIZE_Y + 3 (two boundary columns on left, and one on right)

// x boundary
#define BOUNDARY_X		2

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE 65			// BOUNDARY_X + (BUF_SIZE_X / 2)

// size of one of two buffers (odd or even)
#define BUFFER_SIZE     715          // VERTICAL_STRIDE * BUF_SIZE_Y

/// padding between even and odd buffers: used to reduce bank conflicts
#define PADDING			5           // LDS_BANKS - ((BUFFER_SIZE + LDS_BANKS / 2) % LDS_BANKS);

// offset of the odd columns buffer from the beginning of data buffer
#define ODD_OFFSET 720              // BUFFER_SIZE + PADDING

// size of buffer for both even and odd columns
#define TOTAL_BUFFER_SIZE  1445      // 2 * BUFFER_SIZE + PADDING;
///////////////////////////////////////////////////////////////////////

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

void KERNEL run(__read_only image2d_t idata, __write_only image2d_t odata,
                       const unsigned int  width, const unsigned int  height, const unsigned int steps) {
	LOCAL int scratch[TOTAL_BUFFER_SIZE*4];

	//cache last three pixels
	int pixCache[3];

	int firstY = get_global_id(1) *steps*WIN_SIZE_Y;

	//1. initialize column (and perhaps boundary column)

	//2. read column into scratch

	//3. vertically transform column

	//4. horizontally transform all rows corresponding to this column

	//5. write into destination

	int yIndex = getGlobalId(1) * steps * WIN_SIZE_Y;
	for (int i = 0; i < steps; ++i) {

	   //store a line in local memory
	   for (int j = 0; j < WIN_SIZE_Y; ++j) {

			const int2 pos = {getGlobalId(0), yIndex + j };

			if (pos.x < width && pos.y < height) {

				const float2 posNormal = {pos.x/(float)width, pos.y/(float)height};
				int4 pix = read_imagei(idata, sampler, posNormal);

				int channelIndex = getLocalId(0) + j * WIN_SIZE_X;
				scratch[channelIndex] = pix.x;
				channelIndex += TOTAL_BUFFER_SIZE;
				scratch[channelIndex] = pix.y;
				channelIndex += TOTAL_BUFFER_SIZE;
				scratch[channelIndex] = pix.z;
				channelIndex += TOTAL_BUFFER_SIZE;
				scratch[channelIndex] = pix.w;

			}
		}

		// read from local memory and store in destination
		for (int j = 0; j < WIN_SIZE_Y; ++j) {
			const int2 pos = {getGlobalId(0), yIndex + j };
			if (pos.x < width && pos.y < height) {
				int channelIndex = getLocalId(0) + j * WIN_SIZE_X;
				int4 pix;
				pix.x = scratch[channelIndex] ;
				channelIndex += TOTAL_BUFFER_SIZE;
				pix.y = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix.z = scratch[channelIndex];
				channelIndex += TOTAL_BUFFER_SIZE;
				pix.w = scratch[channelIndex];

				//write
				write_imagei(odata, pos,pix);

			}
		}
		yIndex += WIN_SIZE_Y;
	}

}