optimized histogram merging

optimized histogram merging

//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Niko Li, newlife20080214@gmail.com
//    Jia Haipeng, jiahaipeng95@gmail.com
//    Xu Pang, pangxu010@163.com
//    Wenju He, wenju@multicorewareinc.com
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//

#ifndef kercn
#define kercn 1
#endif

#ifndef T
#define T uchar
#endif

#define noconvert

__kernel void calculate_histogram(__global const uchar * src, int src_step, int src_offset, int src_rows, int src_cols,
                                  __global uchar * histptr, int total)
{
    int lid = get_local_id(0);
    int id = get_global_id(0) * kercn;
    int gid = get_group_id(0);

    __local int localhist[BINS];

    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        localhist[i] = 0;
    barrier(CLK_LOCAL_MEM_FENCE);

    int src_index;

    for (int grain = HISTS_COUNT * WGS * kercn; id < total; id += grain)
    {
#ifdef HAVE_SRC_CONT
        src_index = id;
#else
        src_index = mad24(id / src_cols, src_step, src_offset + id % src_cols);
#endif

#if kercn == 1
        atomic_inc(localhist + convert_int(src[src_index]));
#elif kercn == 4
        int value = *(__global const int *)(src + src_index);
        atomic_inc(localhist + (value & 0xff));
        atomic_inc(localhist + ((value >> 8) & 0xff));
        atomic_inc(localhist + ((value >> 16) & 0xff));
        atomic_inc(localhist + ((value >> 24) & 0xff));
#elif kercn >= 2
        T value = *(__global const T *)(src + src_index);
        atomic_inc(localhist + value.s0);
        atomic_inc(localhist + value.s1);
#if kercn >= 4
        atomic_inc(localhist + value.s2);
        atomic_inc(localhist + value.s3);
#if kercn >= 8
        atomic_inc(localhist + value.s4);
        atomic_inc(localhist + value.s5);
        atomic_inc(localhist + value.s6);
        atomic_inc(localhist + value.s7);
#if kercn == 16
        atomic_inc(localhist + value.s8);
        atomic_inc(localhist + value.s9);
        atomic_inc(localhist + value.sA);
        atomic_inc(localhist + value.sB);
        atomic_inc(localhist + value.sC);
        atomic_inc(localhist + value.sD);
        atomic_inc(localhist + value.sE);
        atomic_inc(localhist + value.sF);
#endif
#endif
#endif
#endif
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    __global int * hist = (__global int *)(histptr + gid * BINS * (int)sizeof(int));
    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        hist[i] = localhist[i];
}

#ifndef HT
#define HT int
#endif

#ifndef convertToHT
#define convertToHT noconvert
#endif

__kernel void merge_histogram(__global const int * ghist, __global uchar * histptr, int hist_step, int hist_offset)
{
    int lid = get_local_id(0);

    __global HT * hist = (__global HT *)(histptr + hist_offset);
#if WGS >= BINS
    HT res = (HT)(0);
#else
    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        hist[i] = (HT)(0);
#endif

    #pragma unroll
    for (int i = 0; i < HISTS_COUNT; ++i)
    {
        #pragma unroll
        for (int j = lid; j < BINS; j += WGS)
#if WGS >= BINS
            res += convertToHT(ghist[j]);
#else
            hist[j] += convertToHT(ghist[j]);
#endif
        ghist += BINS;
    }

#if WGS >= BINS
    if (lid < BINS)
        *(__global HT *)(histptr + mad24(lid, hist_step, hist_offset)) = res;
#endif
}

__kernel void calcLUT(__global uchar * dst, __constant int * hist, int total)
{
    int lid = get_local_id(0);
    __local int sumhist[BINS];
    __local float scale;

    sumhist[lid] = hist[lid];
    barrier(CLK_LOCAL_MEM_FENCE);

    if (lid == 0)
    {
        int sum = 0, i = 0;
        while (!sumhist[i])
            ++i;

        if (total == sumhist[i])
        {
            scale = 1;
            for (int j = 0; j < BINS; ++j)
                sumhist[i] = i;
        }
        else
        {
            scale = 255.f / (total - sumhist[i]);

            for (sumhist[i++] = 0; i < BINS; i++)
            {
                sum += sumhist[i];
                sumhist[i] = sum;
            }
        }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        dst[i]= convert_uchar_sat_rte(convert_float(sumhist[i]) * scale);
}

//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Niko Li, newlife20080214@gmail.com
//    Jia Haipeng, jiahaipeng95@gmail.com
//    Xu Pang, pangxu010@163.com
//    Wenju He, wenju@multicorewareinc.com
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//

#ifndef kercn
#define kercn 1
#endif

#ifndef T
#define T uchar
#endif

#define noconvert

__kernel void calculate_histogram(__global const uchar * src, int src_step, int src_offset, int src_rows, int src_cols,
                                  __global uchar * histptr, int total)
{
    int lid = get_local_id(0);
    int id = get_global_id(0) * kercn;
    int gid = get_group_id(0);

    __local int localhist[BINS];

    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        localhist[i] = 0;
    barrier(CLK_LOCAL_MEM_FENCE);

    int src_index;

    for (int grain = HISTS_COUNT * WGS * kercn; id < total; id += grain)
    {
#ifdef HAVE_SRC_CONT
        src_index = id;
#else
        src_index = mad24(id / src_cols, src_step, src_offset + id % src_cols);
#endif

#if kercn == 1
        atomic_inc(localhist + convert_int(src[src_index]));
#elif kercn == 4
        int value = *(__global const int *)(src + src_index);
        atomic_inc(localhist + (value & 0xff));
        atomic_inc(localhist + ((value >> 8) & 0xff));
        atomic_inc(localhist + ((value >> 16) & 0xff));
        atomic_inc(localhist + ((value >> 24) & 0xff));
#elif kercn >= 2
        T value = *(__global const T *)(src + src_index);
        atomic_inc(localhist + value.s0);
        atomic_inc(localhist + value.s1);
#if kercn >= 4
        atomic_inc(localhist + value.s2);
        atomic_inc(localhist + value.s3);
#if kercn >= 8
        atomic_inc(localhist + value.s4);
        atomic_inc(localhist + value.s5);
        atomic_inc(localhist + value.s6);
        atomic_inc(localhist + value.s7);
#if kercn == 16
        atomic_inc(localhist + value.s8);
        atomic_inc(localhist + value.s9);
        atomic_inc(localhist + value.sA);
        atomic_inc(localhist + value.sB);
        atomic_inc(localhist + value.sC);
        atomic_inc(localhist + value.sD);
        atomic_inc(localhist + value.sE);
        atomic_inc(localhist + value.sF);
#endif
#endif
#endif
#endif
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    __global int * hist = (__global int *)(histptr + gid * BINS * (int)sizeof(int));
    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        hist[i] = localhist[i];
}

#ifndef HT
#define HT int
#endif

#ifndef convertToHT
#define convertToHT noconvert
#endif

__kernel void merge_histogram(__global const int * ghist, __global uchar * histptr, int hist_step, int hist_offset)
{
    int lid = get_local_id(0);

    __global HT * hist = (__global HT *)(histptr + hist_offset);

    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        hist[i] = ghist[i];
    barrier(CLK_LOCAL_MEM_FENCE);

    #pragma unroll
    for (int i = 1; i < HISTS_COUNT; ++i)
    {
        ghist += BINS;
        #pragma unroll
        for (int j = lid; j < BINS; j += WGS)
            hist[j] += convertToHT(ghist[j]);
        barrier(CLK_LOCAL_MEM_FENCE);
    }
}

__kernel void calcLUT(__global uchar * dst, __constant int * hist, int total)
{
    int lid = get_local_id(0);
    __local int sumhist[BINS];
    __local float scale;

    sumhist[lid] = hist[lid];
    barrier(CLK_LOCAL_MEM_FENCE);

    if (lid == 0)
    {
        int sum = 0, i = 0;
        while (!sumhist[i])
            ++i;

        if (total == sumhist[i])
        {
            scale = 1;
            for (int j = 0; j < BINS; ++j)
                sumhist[i] = i;
        }
        else
        {
            scale = 255.f / (total - sumhist[i]);

            for (sumhist[i++] = 0; i < BINS; i++)
            {
                sum += sumhist[i];
                sumhist[i] = sum;
            }
        }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    #pragma unroll
    for (int i = lid; i < BINS; i += WGS)
        dst[i]= convert_uchar_sat_rte(convert_float(sumhist[i]) * scale);
}


libocl: refine length() implementation.

libocl: refine length() implementation.

Just try to reduce if-branching.
And the (m<1) condition check seems no use, so I remove it.

Signed-off-by: Ruiling Song <286a174c1339c6eff91abc41c3a1012c95030c95@intel.com>
Reviewed-by: Zhigang Gong <e04a7b9b70b1e4c6318cf117dcd1a9056e14b97a@linux.intel.com>

/*
 * Copyright © 2012 - 2014 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include "ocl_geometric.h"
#include "ocl_common.h"
#include "ocl_relational.h"
#include "ocl_math.h"
#include "ocl_float.h"

PURE CONST float __gen_ocl_fabs(float x);

OVERLOADABLE float dot(float p0, float p1) {
  return p0 * p1;
}
OVERLOADABLE float dot(float2 p0, float2 p1) {
  return mad(p0.x, p1.x, p0.y * p1.y);
}
OVERLOADABLE float dot(float3 p0, float3 p1) {
  return mad(p0.x, p1.x, mad(p0.y, p1.y, p0.z*p1.z));
}
OVERLOADABLE float dot(float4 p0, float4 p1) {
  return mad(p0.x, p1.x, mad(p0.y, p1.y, mad(p0.z, p1.z, p0.w * p1.w)));
}
OVERLOADABLE float length(float x) { return __gen_ocl_fabs(x); }

#define BODY \
  m = m==0.0f ? 1.0f : m; \
  m = isinf(m) ? 1.0f : m; \
  x = x/m; \
  return m * sqrt(dot(x,x));
OVERLOADABLE float length(float2 x) {
  float m = max(__gen_ocl_fabs(x.s0), __gen_ocl_fabs(x.s1));
  BODY;
}
OVERLOADABLE float length(float3 x) {
  float m = max(__gen_ocl_fabs(x.s0), max(__gen_ocl_fabs(x.s1), __gen_ocl_fabs(x.s2)));
  BODY;
}
OVERLOADABLE float length(float4 x) {
  float m = max(__gen_ocl_fabs(x.s0), max(__gen_ocl_fabs(x.s1), max(__gen_ocl_fabs(x.s2), __gen_ocl_fabs(x.s3))));
  BODY;
}
#undef BODY
OVERLOADABLE float distance(float x, float y) { return length(x-y); }
OVERLOADABLE float distance(float2 x, float2 y) { return length(x-y); }
OVERLOADABLE float distance(float3 x, float3 y) { return length(x-y); }
OVERLOADABLE float distance(float4 x, float4 y) { return length(x-y); }
OVERLOADABLE float normalize(float x) {
  union { float f; unsigned u; } u;
  u.f = x;
  if(u.u == 0)
    return 0.f;
  if(isnan(x))
    return NAN;
  return u.u < 0x7fffffff ? 1.f : -1.f;
}
OVERLOADABLE float2 normalize(float2 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}
OVERLOADABLE float3 normalize(float3 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}
OVERLOADABLE float4 normalize(float4 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}

OVERLOADABLE float fast_length(float x) { return __gen_ocl_fabs(x); }
OVERLOADABLE float fast_length(float2 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_length(float3 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_length(float4 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_distance(float x, float y) { return length(x-y); }
OVERLOADABLE float fast_distance(float2 x, float2 y) { return length(x-y); }
OVERLOADABLE float fast_distance(float3 x, float3 y) { return length(x-y); }
OVERLOADABLE float fast_distance(float4 x, float4 y) { return length(x-y); }
OVERLOADABLE float fast_normalize(float x) { return x > 0 ? 1.f : (x < 0 ? -1.f : 0.f); }
OVERLOADABLE float2 fast_normalize(float2 x) { return x * rsqrt(dot(x, x)); }
OVERLOADABLE float3 fast_normalize(float3 x) { return x * rsqrt(dot(x, x)); }
OVERLOADABLE float4 fast_normalize(float4 x) { return x * rsqrt(dot(x, x)); }

OVERLOADABLE float3 cross(float3 v0, float3 v1) {
   return v0.yzx*v1.zxy-v0.zxy*v1.yzx;
}
OVERLOADABLE float4 cross(float4 v0, float4 v1) {
   return (float4)(v0.yzx*v1.zxy-v0.zxy*v1.yzx, 0.f);
}

/*
 * Copyright © 2012 - 2014 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
#include "ocl_geometric.h"
#include "ocl_common.h"
#include "ocl_relational.h"
#include "ocl_math.h"
#include "ocl_float.h"

PURE CONST float __gen_ocl_fabs(float x);

OVERLOADABLE float dot(float p0, float p1) {
  return p0 * p1;
}
OVERLOADABLE float dot(float2 p0, float2 p1) {
  return mad(p0.x, p1.x, p0.y * p1.y);
}
OVERLOADABLE float dot(float3 p0, float3 p1) {
  return mad(p0.x, p1.x, mad(p0.y, p1.y, p0.z*p1.z));
}
OVERLOADABLE float dot(float4 p0, float4 p1) {
  return mad(p0.x, p1.x, mad(p0.y, p1.y, mad(p0.z, p1.z, p0.w * p1.w)));
}
OVERLOADABLE float length(float x) { return __gen_ocl_fabs(x); }

#define BODY \
  if(m == 0) \
    return 0; \
  if(isinf(m)) \
    return INFINITY; \
  if(m < 1) \
    m = 1; \
  x /= m; \
  return m * sqrt(dot(x,x));
OVERLOADABLE float length(float2 x) {
  float m = max(__gen_ocl_fabs(x.s0), __gen_ocl_fabs(x.s1));
  BODY;
}
OVERLOADABLE float length(float3 x) {
  float m = max(__gen_ocl_fabs(x.s0), max(__gen_ocl_fabs(x.s1), __gen_ocl_fabs(x.s2)));
  BODY;
}
OVERLOADABLE float length(float4 x) {
  float m = max(__gen_ocl_fabs(x.s0), max(__gen_ocl_fabs(x.s1), max(__gen_ocl_fabs(x.s2), __gen_ocl_fabs(x.s3))));
  BODY;
}
#undef BODY
OVERLOADABLE float distance(float x, float y) { return length(x-y); }
OVERLOADABLE float distance(float2 x, float2 y) { return length(x-y); }
OVERLOADABLE float distance(float3 x, float3 y) { return length(x-y); }
OVERLOADABLE float distance(float4 x, float4 y) { return length(x-y); }
OVERLOADABLE float normalize(float x) {
  union { float f; unsigned u; } u;
  u.f = x;
  if(u.u == 0)
    return 0.f;
  if(isnan(x))
    return NAN;
  return u.u < 0x7fffffff ? 1.f : -1.f;
}
OVERLOADABLE float2 normalize(float2 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}
OVERLOADABLE float3 normalize(float3 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}
OVERLOADABLE float4 normalize(float4 x) {
  float m = length(x);
  if(m == 0)
    return 0;
  return x / m;
}

OVERLOADABLE float fast_length(float x) { return __gen_ocl_fabs(x); }
OVERLOADABLE float fast_length(float2 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_length(float3 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_length(float4 x) { return sqrt(dot(x,x)); }
OVERLOADABLE float fast_distance(float x, float y) { return length(x-y); }
OVERLOADABLE float fast_distance(float2 x, float2 y) { return length(x-y); }
OVERLOADABLE float fast_distance(float3 x, float3 y) { return length(x-y); }
OVERLOADABLE float fast_distance(float4 x, float4 y) { return length(x-y); }
OVERLOADABLE float fast_normalize(float x) { return x > 0 ? 1.f : (x < 0 ? -1.f : 0.f); }
OVERLOADABLE float2 fast_normalize(float2 x) { return x * rsqrt(dot(x, x)); }
OVERLOADABLE float3 fast_normalize(float3 x) { return x * rsqrt(dot(x, x)); }
OVERLOADABLE float4 fast_normalize(float4 x) { return x * rsqrt(dot(x, x)); }

OVERLOADABLE float3 cross(float3 v0, float3 v1) {
   return v0.yzx*v1.zxy-v0.zxy*v1.yzx;
}
OVERLOADABLE float4 cross(float4 v0, float4 v1) {
   return (float4)(v0.yzx*v1.zxy-v0.zxy*v1.yzx, 0.f);
}


Change threads[] to private instead of constant... Seeing if it makes a difference in the Nv profiler.

#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);

#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];

    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;
}

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,
    int *threads
){
    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 == 0 || plane == 0) && filters[num_blocks*filter_type + block] > 0){
        if (cur_iter > 0){
            num_planes = 1;
        }

        int filter_level = filters[block];
        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_base, s_off - 2 * p, s_off - p, s_off,
                //        s_off + p);

                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];

            }
        }
    }
}

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 *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];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

    //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, threads);
    vp8_loop_filter_horizontal_edge_worker( s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data, threads);
    vp8_loop_filter_horizontal_edge_worker( s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data, threads);
}

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,
    int *threads
){
    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];

            }
        }
    }
}

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 *filters,
    int use_mbflim, //unused for normal filters
    int filter_type, //unused for normal filters
    int cur_iter, //unused
    int priority_offset
)
{
    local unsigned char s_data[16*8*3];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

    //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, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data, threads);
}

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,
    int *threads
){
    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_mbloop_filter_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
)
{
    local unsigned char s_data[16*8*3];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

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

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];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

    vp8_mbloop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi,
            filters,  priority_offset, s_data, threads);
    //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, threads);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data, threads);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data, threads);

}

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,
    int *threads
){
    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_mbloop_filter_vertical_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
)
{
    local unsigned char s_data[16*8*3];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

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

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];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

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

    //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, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data, threads);

    //barrier(CLK_GLOBAL_MEM_FENCE);

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

    //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, threads);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 3, priority_offset, s_data, threads);
    vp8_loop_filter_horizontal_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 4, priority_offset, s_data, threads);

}

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];
    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

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

    //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, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 6, priority_offset, s_data, threads);
    vp8_loop_filter_vertical_edge_worker(s_base, offsets, pitches, lfi, filters,
            DC_DIFFS_LOCATION, 7, priority_offset, s_data, threads);

}

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);

    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

    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);

    int threads[3];
    threads[0] = 16;
    threads[1] = 8;
    threads[2] = 8;

    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];

    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;
}

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);
    size_t num_planes;
    size_t num_blocks;
    num_planes = 3;
    num_blocks = get_global_size(2);

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

        int filter_level = filters[block];
        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_base, s_off - 2 * p, s_off - p, s_off,
                //        s_off + p);

                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];

            }
        }
    }
}

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 *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];

    //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_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];

            }
        }
    }
}

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 *filters,
    int use_mbflim, //unused for normal filters
    int filter_type, //unused for normal filters
    int cur_iter, //unused
    int priority_offset
)
{
    local unsigned char s_data[16*8*3];

    //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);
}

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_mbloop_filter_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
)
{
    local unsigned char s_data[16*8*3];

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

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_mbloop_filter_vertical_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
)
{
    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);
}

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];

    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);

    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;
}