phong expanded is the solution! thanks Noe!

#include "kernel_header.hcl"
#include "kernel_matrix.hcl"
#include "kernel_cam.hcl"
#include "kernel_color.hcl"
#include "kernel_cone.hcl"
#include "kernel_cylinder.hcl"
#include "kernel_data.hcl"
#include "kernel_debug.hcl"
#include "kernel_plane.hcl"
#include "kernel_sphere.hcl"

static t_hit			ray_hit(const __local t_scene *scene, const float3 origin, const float3 ray)
{
	unsigned int			i = 0;
	int						max = get_max_obj(scene);
	t_hit					hit = hit_init();
	float					dist = 0;

	while (i < max)
	{
		if (i < scene->n_cones)
			if (((dist = inter_cone(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < scene->n_cylinders)
			if (((dist = inter_cylinder(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < scene->n_planes)
			if (((dist = inter_plan(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < scene->n_spheres)
			if (((dist = inter_sphere(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			get_hit_normale(const __local t_scene *scene, float3 ray, t_hit hit)
{
	float3		res, save;

	if (hit.type == 1)
		res = get_cone_normale(scene, hit);
	else if (hit.type == 2)
		res = get_cylinder_normal(scene, hit);
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;

		if (scene->flag & OPTION_WAVE)
		{
			/*					VAGUELETTE						*/
			save = res;
			save.y = res.y + 0.8 * sin((hit.pos.x + scene->u_time));
			return (fast_normalize(save));
		}
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	save = res;
	if (scene->flag & OPTION_WAVE)
	{
		/*						VAGUELETTE							*/
		save.x = res.x + 0.8 * sin(res.y * 10 + scene->u_time);
		save.z = res.z + 0.8 * sin(res.x * 10 + scene->u_time);
		save.y = res.y + 0.8 * sin(res.x * 10 + scene->u_time);
	}

	return (fast_normalize(save));
}

unsigned int			phong2(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i;
	i = 0;
	unsigned int		res_color;
	res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	tmp = 0;
	float3				reflect;
	reflect = 0;
	float3 __private	diffuse;
	diffuse = 0;
	float __private	brightness;
	brightness = 0;
	int __private hue;
	hue = 0;
	int __private hue_light;
	hue_light = 0;
	unsigned int __private col_r, col_g, col_b, obj_r, obj_g, obj_b, l_r, l_b, l_g;
	col_r = 0;
	col_g = 0;
	col_b = 0;
	obj_r = 0;
	obj_g = 0;
	obj_b = 0;
	l_r = 0;
	l_g = 0;
	l_b = 0;
	t_light_ray			light_ray;
	t_hit				light_hit;
	float __private pow_of_spec;
	pow_of_spec = 0;
	int __private light_color;
	light_color = 0;
	float3 __private speculos;
	speculos = 0;

	while (i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
			;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0.f)
			{
				brightness = (float __private)LIGHT[light_hit.id].brightness;

				if (hit.type == 1)
					diffuse = (float3 __private)CONES[hit.id].diff;
				else if (hit.type == 2)
					diffuse = (float3 __private)CYLIND[hit.id].diff;
				else if (hit.type == 4)
					diffuse = (float3 __private)PLANE[hit.id].diff;
				else if (hit.type == 5)
					diffuse = (float3 __private)SPHERE[hit.id].diff;
				else
					diffuse = 0.f;

				if (hit.type == 1)
					hue = (int __private)CONES[hit.id].color;
				if (hit.type == 2)
					hue = (int __private)CYLIND[hit.id].color;
				if (hit.type == 3)
					hue = (int __private)LIGHT[hit.id].color;
				if (hit.type == 4)
					hue = (int __private)PLANE[hit.id].color;
				if (hit.type == 5)
					hue = (int __private)SPHERE[hit.id].color;

				if (hit.type == 1)
					hue_light = (int __private)CONES[light_hit.id].color;
				if (hit.type == 2)
					hue_light = (int __private)CYLIND[light_hit.id].color;
				if (hit.type == 3)
					hue_light = (int __private)LIGHT[light_hit.id].color;
				if (hit.type == 4)
					hue_light = (int __private)PLANE[light_hit.id].color;
				if (hit.type == 5)
					hue_light = (int __private)SPHERE[light_hit.id].color;
				col_r = (res_color & 0xFF0000) >> 16;
				col_g = (res_color & 0x00FF00) >> 8;
				col_b = (res_color & 0x0000FF);
				obj_r = (hue & 0xFF0000) >> 16;
				obj_g = (hue & 0x00FF00) >> 8;
				obj_b = (hue & 0x00000FF);
				l_r = (hue_light & 0xFF0000) >> 16;
				l_g = (hue_light & 0x00FF00) >> 8;
				l_b = (hue_light & 0x0000FF);

				col_r += ((l_r * brightness) + obj_r) * tmp * diffuse.x;
				col_g += ((l_g * brightness) + obj_g) * tmp * diffuse.y;
				col_b += ((l_b * brightness) + obj_b) * tmp * diffuse.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0.f)
			{
				speculos = 0.f;
				if (hit.type == 1)
					speculos = (float3 __private)CONES[hit.id].spec;
				else if (hit.type == 2)
					speculos = (float3 __private)CYLIND[hit.id].spec;
				else if (hit.type == 4)
					speculos = (float3 __private)PLANE[hit.id].spec;
				else if (hit.type == 5)
					speculos = (float3 __private)SPHERE[hit.id].spec;
				else
					speculos = 0.f;
				col_r = (res_color & 0x00FF0000) >> 16;
				col_g = (res_color & 0x0000FF00) >> 8;
				col_b = (res_color & 0x000000FF);

				pow_of_spec = native_powr(tmp, (LIGHT[light_hit.id].shrink));
				light_color = LIGHT[light_hit.id].color;
				col_r += (((light_color & 0xFF0000) >> 16) * pow_of_spec) * speculos.x;
				col_g += ((light_color & 0x00FF00) >> 8) * pow_of_spec * speculos.y;
				col_b += (light_color & 0x0000FF) * pow_of_spec * speculos.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
		}
		i++;
	}
	return (res_color);
}

//BACKUPD DE PHONH
/*unsigned int			phong3(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}*/

static unsigned int		bounce(const __local t_scene *scene, const float3 ray, const t_hit old_hit, const int depth)
{
	int				i = depth;
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (i > 0)
	{
		reflex = fast_normalize(ray - (2 * (float)dot(old_hit.normale, ray) * old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(scene, old_hit.pos, reflex);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = (new_hit.dist * reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, ray, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(scene, new_hit, reflex);
		}
		i--;
	}
	return (color);
}

static unsigned int	get_pixel_color(const __local t_scene *scene, float3 ray)
{
	t_hit			hit;
	int				depth = scene->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit(scene, (ACTIVECAM.pos), ray);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = (hit.dist * ray) + (ACTIVECAM.pos);
		hit.normale = get_hit_normale(scene, ray, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong2(scene, hit, ray);
		if (depth > 0)
			bounce_color = bounce(scene, ray, hit, depth);
		return (color + bounce_color);
//		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(scene, BACKCOLOR));
}

__kernel void	ray_trace(	__global	char		*output,
							__global	t_hit		*target_obj,
							__global	t_scene		*scene_data,
							__global	t_cam		*cameras_data,
							__global	t_cone		*cones_data,
							__global	t_cylinder	*cylinders_data,
							__global	t_light		*lights_data,
							__global	t_plane		*planes_data,
							__global	t_sphere	*spheres_data,
							__local		t_scene		*scene,
							__local		t_cam		*cameras,
							__local		t_cone		*cones,
							__local		t_cylinder	*cylinders,
							__local		t_light		*lights,
							__local		t_plane		*planes,
							__local		t_sphere	*spheres,
							__private	float		u_time,
							__private	int			flag)
{
 	event_t	ev;
	ev = async_work_group_copy((__local char *)scene, (__global char *)scene_data, sizeof(t_scene), 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cameras, (__global char *)cameras_data, sizeof(t_cam) * scene->n_cams, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cones, (__global char *)cones_data, sizeof(t_cone) * scene->n_cones, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cylinders, (__global char *)cylinders_data, sizeof(t_cylinder) * scene->n_cylinders, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)lights, (__global char *)lights_data, sizeof(t_light) * scene->n_lights, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)planes, (__global char *)planes_data, sizeof(t_plane) * scene->n_planes, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)spheres, (__global char *)spheres_data, sizeof(t_sphere) * scene->n_spheres, 0);
	wait_group_events(1, &ev);

	uint2	pix;
	pix.x = get_global_id(0);
	pix.y = get_global_id(1);

	scene->cameras = cameras;
	scene->cones = cones;
	scene->cylinders = cylinders;
	scene->lights = lights;
	scene->planes = planes;
	scene->spheres = spheres;
	scene->u_time = u_time;
	scene->flag = flag;

	int			id = pix.x + (scene->win_w * pix.y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE

	float3	prim_ray = get_ray_cam(scene, pix);
	if (pix.x == scene->mou_x && pix.y == scene->mou_y)
		*target_obj = ray_hit(scene, ACTIVECAM.pos, prim_ray);
	OUTPUTE = get_pixel_color(scene, prim_ray);
}

#include "kernel_header.hcl"
#include "kernel_matrix.hcl"
#include "kernel_cam.hcl"
#include "kernel_color.hcl"
#include "kernel_cone.hcl"
#include "kernel_cylinder.hcl"
#include "kernel_data.hcl"
#include "kernel_debug.hcl"
#include "kernel_plane.hcl"
#include "kernel_sphere.hcl"

static t_hit			ray_hit(const __local t_scene *scene, const float3 origin, const float3 ray)
{
	unsigned int			i = 0;
	int						max = get_max_obj(scene);
	t_hit					hit = hit_init();
	float					dist = 0;

	while (i < max)
	{
		if (i < scene->n_cones)
			if (((dist = inter_cone(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 1;
				hit.id = i;
			}
		if (i < scene->n_cylinders)
			if (((dist = inter_cylinder(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 2;
				hit.id = i;
			}
		if (i < scene->n_planes)
			if (((dist = inter_plan(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 4;
				hit.id = i;
			}
		if (i < scene->n_spheres)
			if (((dist = inter_sphere(scene, i, ray, origin)) < hit.dist || hit.dist == 0) && dist > 0)
			{
				hit.dist = dist;
				hit.type = 5;
				hit.id = i;
			}
		i++;
	}
	return (hit);
}

float3			get_hit_normale(const __local t_scene *scene, float3 ray, t_hit hit)
{
	float3		res, save;

	if (hit.type == 1)
		res = get_cone_normale(scene, hit);
	else if (hit.type == 2)
		res = get_cylinder_normal(scene, hit);
	else if (hit.type == 4)
	{
		if (dot(PLANE[hit.id].normale, -ray) < 0)
			res = -PLANE[hit.id].normale;
		else
			res = PLANE[hit.id].normale;

		if (scene->flag & OPTION_WAVE)
		{
			/*					VAGUELETTE						*/
			save = res;
			save.y = res.y + 0.8 * sin((hit.pos.x + scene->u_time));
			return (fast_normalize(save));
		}
	}
	else if (hit.type == 5)
		res = hit.pos - SPHERE[hit.id].pos;
	save = res;
	if (scene->flag & OPTION_WAVE)
	{
		/*						VAGUELETTE							*/
		save.x = res.x + 0.8 * sin(res.y * 10 + scene->u_time);
		save.z = save.z + 0.8 * sin(save.x * 10 + scene->u_time);
		save.y = res.y + 0.8 * sin(res.x * 10 + scene->u_time);
	}

	return (fast_normalize(save));
}

unsigned int			phong2(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i;
	i = 0;
	unsigned int		res_color;
	res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	tmp = 0;
	float3				reflect;
	reflect = 0;
	float3 __private	diffuse;
	diffuse = 0;
	float __private	brightness;
	brightness = 0;
	int __private hue;
	hue = 0;
	int __private hue_light;
	hue_light = 0;
	unsigned int __private col_r, col_g, col_b, obj_r, obj_g, obj_b, l_r, l_b, l_g;
	col_r = 0;
	col_g = 0;
	col_b = 0;
	obj_r = 0;
	obj_g = 0;
	obj_b = 0;
	l_r = 0;
	l_g = 0;
	l_b = 0;
	t_light_ray			light_ray;
	t_hit				light_hit;
	float __private pow_of_spec;
	pow_of_spec = 0;
	int __private light_color;
	light_color = 0;
	float3 __private speculos;
	speculos = 0;

	light_ray.dir = LIGHT[0].pos - hit.pos;
	light_ray.dist = fast_length(light_ray.dir);
	light_ray.dir = fast_normalize(light_ray.dir);
	light_hit = ray_hit(scene, hit.pos, light_ray.dir);
	light_hit.id = i;
	light_hit.type = 3;

		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
			res_color = 0;
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0.f)
			{
				brightness = (float __private)LIGHT[light_hit.id].brightness;

				if (hit.type == 1)
					diffuse = (float3 __private)CONES[hit.id].diff;
				else if (hit.type == 2)
					diffuse = (float3 __private)CYLIND[hit.id].diff;
				else if (hit.type == 4)
					diffuse = (float3 __private)PLANE[hit.id].diff;
				else if (hit.type == 5)
					diffuse = (float3 __private)SPHERE[hit.id].diff;
				else
					diffuse = 0.f;

				if (hit.type == 1)
					hue = (int __private)CONES[hit.id].color;
				if (hit.type == 2)
					hue = (int __private)CYLIND[hit.id].color;
				if (hit.type == 3)
					hue = (int __private)LIGHT[hit.id].color;
				if (hit.type == 4)
					hue = (int __private)PLANE[hit.id].color;
				if (hit.type == 5)
					hue = (int __private)SPHERE[hit.id].color;

				if (hit.type == 1)
					hue_light = (int __private)CONES[light_hit.id].color;
				if (hit.type == 2)
					hue_light = (int __private)CYLIND[light_hit.id].color;
				if (hit.type == 3)
					hue_light = (int __private)LIGHT[light_hit.id].color;
				if (hit.type == 4)
					hue_light = (int __private)PLANE[light_hit.id].color;
				if (hit.type == 5)
					hue_light = (int __private)SPHERE[light_hit.id].color;
				col_r = (res_color & 0xFF0000) >> 16;
				col_g = (res_color & 0x00FF00) >> 8;
				col_b = (res_color & 0x0000FF);
				obj_r = (hue & 0xFF0000) >> 16;
				obj_g = (hue & 0x00FF00) >> 8;
				obj_b = (hue & 0x00000FF);
				l_r = (hue_light & 0xFF0000) >> 16;
				l_g = (hue_light & 0x00FF00) >> 8;
				l_b = (hue_light & 0x0000FF);

				col_r += ((l_r * brightness) + obj_r) * tmp * diffuse.x;
				col_g += ((l_g * brightness) + obj_g) * tmp * diffuse.y;
				col_b += ((l_b * brightness) + obj_b) * tmp * diffuse.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0.f)
			{
				speculos = 0.f;
				if (hit.type == 1)
					speculos = (float3 __private)CONES[hit.id].spec;
				else if (hit.type == 2)
					speculos = (float3 __private)CYLIND[hit.id].spec;
				else if (hit.type == 4)
					speculos = (float3 __private)PLANE[hit.id].spec;
				else if (hit.type == 5)
					speculos = (float3 __private)SPHERE[hit.id].spec;
				else
					speculos = 0.f;
				col_r = (res_color & 0x00FF0000) >> 16;
				col_g = (res_color & 0x0000FF00) >> 8;
				col_b = (res_color & 0x000000FF);

				pow_of_spec = native_powr(tmp, (LIGHT[light_hit.id].shrink));
				light_color = LIGHT[light_hit.id].color;
				col_r += (((light_color & 0xFF0000) >> 16) * pow_of_spec) * speculos.x;
				col_g += ((light_color & 0x00FF00) >> 8) * pow_of_spec * speculos.y;
				col_b += (light_color & 0x0000FF) * pow_of_spec * speculos.z;
				(col_r > 255 ? col_r = 255 : 0);
				(col_g > 255 ? col_g = 255 : 0);
				(col_b > 255 ? col_b = 255 : 0);
				res_color = ((col_r << 16) + (col_g << 8) + col_b);
			}
		}
	return (res_color);
}

unsigned int			phong3(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

unsigned int			phong4(const __local t_scene *scene, const t_hit hit, const float3 ray)
{
	int					i = -1;
	unsigned int		res_color = get_ambient(scene, get_obj_hue(scene, hit));
	float				tmp;
	float3				reflect = 0;

	t_light_ray			light_ray;
	t_hit				light_hit = hit_init();

	while (++i < scene->n_lights)
	{
		tmp = 0;
		light_ray.dir = LIGHT[i].pos - hit.pos;
		light_ray.dist = fast_length(light_ray.dir);
		light_ray.dir = fast_normalize(light_ray.dir);
		light_hit = ray_hit(scene, hit.pos, light_ray.dir);
		light_hit.id = i;
		light_hit.type = 3;
		if (light_hit.dist < light_ray.dist && light_hit.dist > 0)
		{
		}
		else
		{
			tmp = dot(hit.normale, light_ray.dir);
			if (tmp > 0)
				res_color = color_diffuse(scene, hit, light_hit, res_color, tmp);
			reflect = fast_normalize(((float)(2.0 * dot(hit.normale, light_ray.dir)) * hit.normale) - light_ray.dir);
			tmp = dot(reflect, -ray);
			if (tmp > 0)
				res_color = color_specular(scene, hit, light_hit, res_color, tmp);
		}
	}
	return (res_color);
}

static unsigned int		bounce(const __local t_scene *scene, const float3 ray, const t_hit old_hit, const int depth)
{
	int				i = depth;
	unsigned int	color = 0;
	float3			reflex = 0;
	t_hit			new_hit;
	while (i > 0)
	{
		reflex = fast_normalize(ray - (2 * (float)dot(old_hit.normale, ray) * old_hit.normale));
		new_hit.dist = MAX_DIST;
		new_hit = ray_hit(scene, old_hit.pos, reflex);
		if (new_hit.dist > 0 && new_hit.dist < MAX_DIST)
		{
			new_hit.pos = (new_hit.dist * reflex) + old_hit.pos;
			new_hit.normale = get_hit_normale(scene, ray, new_hit);
			new_hit.pos = new_hit.pos + ((new_hit.dist / 100) * new_hit.normale);
			color += 0.1 * phong2(scene, new_hit, reflex);
		}
		i--;
	}
	return (color);
}

static unsigned int	get_pixel_color(const __local t_scene *scene, float3 ray)
{
	t_hit			hit;
	int				depth = scene->depth;
	unsigned int	color  = 0;
	unsigned int	bounce_color = 0;

	hit.dist = MAX_DIST;
	hit = ray_hit(scene, (ACTIVECAM.pos), ray);
	if (hit.dist > 0 && hit.dist < MAX_DIST) // ajout d'une distance max pour virer acnee mais pas fiable a 100%
	{
		hit.pos = (hit.dist * ray) + (ACTIVECAM.pos);
		hit.normale = get_hit_normale(scene, ray, hit);
		hit.pos = hit.pos + ((hit.dist / 100) * hit.normale);
		color = phong3(scene, hit, ray);
		if (depth > 0)
			bounce_color = bounce(scene, ray, hit, depth);
		return (color + bounce_color);
//		return (blend_add(color, 0.8*bounce_color));
	}
	return (get_ambient(scene, BACKCOLOR));
}

__kernel void	ray_trace(	__global	char		*output,
							__global	t_hit		*target_obj,
							__global	t_scene		*scene_data,
							__global	t_cam		*cameras_data,
							__global	t_cone		*cones_data,
							__global	t_cylinder	*cylinders_data,
							__global	t_light		*lights_data,
							__global	t_plane		*planes_data,
							__global	t_sphere	*spheres_data,
							__local		t_scene		*scene,
							__local		t_cam		*cameras,
							__local		t_cone		*cones,
							__local		t_cylinder	*cylinders,
							__local		t_light		*lights,
							__local		t_plane		*planes,
							__local		t_sphere	*spheres,
							__private	float		u_time,
							__private	int			flag)
{
 	event_t	ev;
	ev = async_work_group_copy((__local char *)scene, (__global char *)scene_data, sizeof(t_scene), 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cameras, (__global char *)cameras_data, sizeof(t_cam) * scene->n_cams, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cones, (__global char *)cones_data, sizeof(t_cone) * scene->n_cones, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)cylinders, (__global char *)cylinders_data, sizeof(t_cylinder) * scene->n_cylinders, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)lights, (__global char *)lights_data, sizeof(t_light) * scene->n_lights, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)planes, (__global char *)planes_data, sizeof(t_plane) * scene->n_planes, 0);
	wait_group_events(1, &ev);
	ev = async_work_group_copy((__local char *)spheres, (__global char *)spheres_data, sizeof(t_sphere) * scene->n_spheres, 0);
	wait_group_events(1, &ev);

	uint2	pix;
	pix.x = get_global_id(0);
	pix.y = get_global_id(1);

	scene->cameras = cameras;
	scene->cones = cones;
	scene->cylinders = cylinders;
	scene->lights = lights;
	scene->planes = planes;
	scene->spheres = spheres;
	scene->u_time = u_time;
	scene->flag = flag;

	int			id = pix.x + (scene->win_w * pix.y); // NE PAS VIRER ID CAR BESOIN DANS MACRO OUTPUTE

	float3	prim_ray = get_ray_cam(scene, pix);
	if (pix.x == scene->mou_x && pix.y == scene->mou_y)
		*target_obj = ray_hit(scene, ACTIVECAM.pos, prim_ray);
	OUTPUTE = get_pixel_color(scene, prim_ray);
}


test_safeptr_builtin.cl: implement all vloads and vstores

// RUN: $OCLANG $TEST_SRC -S -o $OUT_FILE.ll &&
// RUN: opt -load $CLAMP_PLUGIN -clamp-pointers -S $OUT_FILE.ll -o $OUT_FILE.clamped.ll &&
// RUN: echo "Checking that call is made to safe signature" &&
// RUN: grep "call.*_Z6vload4lPfS_S_"  $OUT_FILE.clamped.ll && grep "call.*_Z7vstore4Dv4_flPfS0_S0_"  $OUT_FILE.clamped.ll

#define IMPLEMENT_SAFE_VLOAD(TYPE, N, DEFAULT)                          \
  TYPE##N _cl_overloadable vload##N(long i, TYPE* cur, TYPE* begin, TYPE* end) { \
    TYPE *ptr = cur + i * N;                                            \
    if (ptr < begin) return (TYPE##N) DEFAULT;                          \
    if (ptr + N > end) return (TYPE##N) DEFAULT;                        \
    return vload##N(i, cur);                                            \
  }

#define IMPLEMENT_SAFE_VSTORE(TYPE, N)                                  \
  void _cl_overloadable vstore##N(TYPE##N x, long i, TYPE* cur, TYPE* begin, TYPE* end) { \
    TYPE *ptr = cur + i * N;                                            \
    if (ptr < begin) return;                                            \
    if (ptr + N > end) return;                                          \
    return vstore##N(x, i, cur);                                        \
  }

#define IMPLEMENT_SAFE_VLOAD_N(N, DEFAULT)                             \
  IMPLEMENT_SAFE_VLOAD (char, N, DEFAULT)                              \
  IMPLEMENT_SAFE_VLOAD (uchar, N, DEFAULT)                             \
  IMPLEMENT_SAFE_VLOAD (short, N, DEFAULT)                             \
  IMPLEMENT_SAFE_VLOAD (ushort, N, DEFAULT)                            \
  IMPLEMENT_SAFE_VLOAD (int, N, DEFAULT)                               \
  IMPLEMENT_SAFE_VLOAD (uint, N, DEFAULT)                              \
  IMPLEMENT_SAFE_VLOAD (long, N, DEFAULT)                              \
  IMPLEMENT_SAFE_VLOAD (ulong, N, DEFAULT)                             \
  IMPLEMENT_SAFE_VLOAD (float, N, DEFAULT)

#define IMPLEMENT_SAFE_VSTORE_N(N)                             \
  IMPLEMENT_SAFE_VSTORE (char, N)                              \
  IMPLEMENT_SAFE_VSTORE (uchar, N)                             \
  IMPLEMENT_SAFE_VSTORE (short, N)                             \
  IMPLEMENT_SAFE_VSTORE (ushort, N)                            \
  IMPLEMENT_SAFE_VSTORE (int, N)                               \
  IMPLEMENT_SAFE_VSTORE (uint, N)                              \
  IMPLEMENT_SAFE_VSTORE (long, N)                              \
  IMPLEMENT_SAFE_VSTORE (ulong, N)                             \
  IMPLEMENT_SAFE_VSTORE (float, N)

IMPLEMENT_SAFE_VLOAD_N (2, (0, 0))
IMPLEMENT_SAFE_VLOAD_N (3, (0, 0, 0))
IMPLEMENT_SAFE_VLOAD_N (4, (0, 0, 0, 0))
IMPLEMENT_SAFE_VLOAD_N (8, (0, 0, 0, 0, 0, 0, 0, 0))
IMPLEMENT_SAFE_VLOAD_N (16, (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))

IMPLEMENT_SAFE_VSTORE_N (2)
IMPLEMENT_SAFE_VSTORE_N (3)
IMPLEMENT_SAFE_VSTORE_N (4)
IMPLEMENT_SAFE_VSTORE_N (8)
IMPLEMENT_SAFE_VSTORE_N (16)

__kernel void test_kernel(__global float* in, __global float* out) {
  int i = get_global_id(0);
  float4 loaded = vload4(i, in);
  vstore4(loaded, i, out);
}

// RUN: $OCLANG $TEST_SRC -S -o $OUT_FILE.ll &&
// RUN: opt -load $CLAMP_PLUGIN -clamp-pointers -S $OUT_FILE.ll -o $OUT_FILE.clamped.ll &&
// RUN: echo "Checking that call is made to safe signature" &&
// RUN: grep "call.*_Z6vload4lPfS_S_1"  $OUT_FILE.clamped.ll && grep "call.*_Z7vstore4Dv4_flPfS0_S0_2"  $OUT_FILE.clamped.ll

float4 _cl_overloadable vload4(long i, float* cur, float* begin, float* end) {
  float *ptr = cur + i * 4;
  if (ptr < begin) return (float4)(0, 0, 0, 0);
  if (ptr + 4 > end) return (float4)(0, 0, 0, 0);
  return vload4(i, cur);
}

void _cl_overloadable vstore4(float4 x, long i, float* cur, float* begin, float* end) {
  float *ptr = cur + i * 4;
  if (ptr < begin) return;
  if (ptr + 4 > end) return;
  return vstore4(x, i, cur);
}

__kernel void test_kernel(__global float* in, __global float* out) {
  int i = get_global_id(0);
  float4 loaded = vload4(i, in);
  vstore4(loaded, i, out);
}


WIP #26 - relayout the code

WIP #26 - relayout the code

#ifndef __oclga_shuffler_chromosome__
#define __oclga_shuffler_chromosome__

#include "ga_utils.cl"

/**
 * __ShufflerChromosome is a struct for accessing n gene.
 */
typedef struct {
  int genes[SHUFFLER_CHROMOSOME_GENE_SIZE];
} __ShufflerChromosome;

/**
 * During the development, we may found some bugs in our chromosomes. One of
 * them is our codes generate duplicated gene while crossing over or mutating.
 * This is not acceptable in shuffler chromosome.
 * This function, shuffler_chromosome_check_dup, checks if there is any
 * duplicated gene in a chromosome.
 * @param *chromosome (global) the pointer of a chromosome which wants to be
 *                             checked.
 */
void shuffler_chromosome_check_dup(global __ShufflerChromosome* chromosome) {
  for (int i = 0; i < SHUFFLER_CHROMOSOME_GENE_SIZE; i++) {
    for (int j = i + 1; j < SHUFFLER_CHROMOSOME_GENE_SIZE; j++) {
      if (chromosome->genes[i] == chromosome->genes[j]) {
        printf("after chromosome element duplicated @%d, %d\n", i, j);
        return;
      }
    }
  }
}

/* ============== populating functions ============== */
/**
 * shuffler_chromosome_do_populate populates a chromosome randomly. Since this
 * is a shuffler chromosome, the elements index of a gene is the size of
 * chromosome. This function random shuffles the whole chromosome.
 * @param *chromosome (global) the chromosome we want to populate
 * @param *rand_holder the random value holder
 */
void shuffler_chromosome_do_populate(global __ShufflerChromosome* chromosome,
                                     uint* rand_holder) {
  int gene_elements[] = SIMPLE_GENE_ELEMENTS;
  int rndIdx;
  // The algorithm here is:
  // 1. random pick a index from 0 ~ size and moved to chromosome
  // 2. move the latest index to the picked index
  // 3. go back to 1 until end
  // 4. put the left element to the end of chromosome.
  for (int i = 0; i < SHUFFLER_CHROMOSOME_GENE_SIZE - 1; i++) {
    rndIdx = rand_range(rand_holder, (SHUFFLER_CHROMOSOME_GENE_SIZE - i - 1));
    chromosome->genes[i] = gene_elements[rndIdx];
    gene_elements[rndIdx] = gene_elements[
                                SHUFFLER_CHROMOSOME_GENE_SIZE - i - 1];
  }
  chromosome->genes[SHUFFLER_CHROMOSOME_GENE_SIZE - 1] = gene_elements[0];
}

/**
 * shuffler_chromosome_populate populate chromosomes with random. The current
 * design is to generate a chromosome in a thread.
 * Note: this is a kernel function and will be called by python.
 * @param *chromosomes (global) all memory storage for populating chromosomes.
 * @param *input_rand (global) random seeds for all threads.
 */
__kernel void shuffler_chromosome_populate(global int* chromosomes,
                                           global uint* input_rand) {
  int idx = get_global_id(0);
  // out of bound kernel task for padding
  if (idx >= POPULATION_SIZE) {
    return;
  }
  // create a private variable for each kernel to hold randome number.
  uint ra[1];
  init_rand(input_rand[idx], ra);
  shuffler_chromosome_do_populate(((global CHROMOSOME_TYPE*) chromosomes) + idx,
                                  ra);
  input_rand[idx] = ra[0];
}
/* ============== end of populating functions ============== */

/* ============== mutation functions ============== */
/**
 * shuffler_chromosome_swap swaps two genes of a chromosome.
 * @param *chromosome (global) the chromosome whose genes are swapped.
 * @param p1 the position of gene 1
 * @param p2 the position of gene 2
 */
void shuffler_chromosome_swap(global __ShufflerChromosome* chromosome, int p1,
                              int p2)
{
  int temp_p = chromosome->genes[p1];
  chromosome->genes[p1] = chromosome->genes[p2];
  chromosome->genes[p2] = temp_p;
}

/**
 * the dummy improving function. An improving function is a help function for
 * hinting the better result for swapping the selected gene. Since this is a
 * user provided function, we only need to implement a dummy function here.
 * @param *chromosome (global) the chromosome for improving. Please note this
 *                             chromosome is in int array mode.
 * @param idx the position of a gene which will be swapped.
 * @param chromosome_size the size of a chromosome.
 * @param FITNESS_ARGS the fitness arguments from ocl_ga options.
 * @return the index of suggested position for swapping.
 */
int shuffler_chromosome_dummy_improving_func(global int* chromosome,
                                             int idx,
                                             int chromosome_size FITNESS_ARGS)
{
  return 0;
}

/**
 * shuffler_chromosome_single_gene_mutate does the mutation of all chromosomes.
 *
 * Note: this is a kernel function and will be called by python.
 * @param *cs (global) all chromosomes.
 * @param *input_rand (global) random seeds array for all threads.
 * @param prob_mutate the threshold for mutation.
 * @param improve a flag to say if we need to call improving function.
 * @param FITNESS_ARGS the fitness arguments from ocl_ga options for improving
 *                     function.
 */
__kernel void shuffler_chromosome_single_gene_mutate(global int* cs,
                                                     global uint* input_rand,
                                                     float prob_mutate,
                                                     int improve FITNESS_ARGS)
{
  int idx = get_global_id(0);
  // out of bound kernel task for padding
  if (idx >= POPULATION_SIZE) {
    return;
  }
  // prepare random number for current thread
  uint ra[1];
  init_rand(input_rand[idx], ra);
  // generate a probability for mutation
  float prob_m =  rand_prob(ra);
  if (prob_m > prob_mutate) {
    // no need to mutate
    input_rand[idx] = ra[0];
    return;
  }
  global __ShufflerChromosome* chromosomes = (global __ShufflerChromosome*) cs;
  // choose a position for mutation randomly.
  uint i = rand_range(ra, SHUFFLER_CHROMOSOME_GENE_SIZE);
  uint j;
  if (improve == 1) {
    // we only gives global int* type to IMPROVED_FITNESS_FUNC instead of
    // __ShufflerChromosome
    j = IMPROVED_FITNESS_FUNC((global int*)(chromosomes + idx), i,
                              SHUFFLER_CHROMOSOME_GENE_SIZE FITNESS_ARGV);
    if (i != j) {
      shuffler_chromosome_swap(chromosomes + idx, i, j);
    }
  } else {
    j = rand_range_exclude(ra, SHUFFLER_CHROMOSOME_GENE_SIZE, i);
    shuffler_chromosome_swap(chromosomes + idx, i, j);
  }
  input_rand[idx] = ra[0];
  shuffler_chromosome_check_dup(chromosomes + idx);
}
/* ============== end of mutation functions ============== */

/* ============== crossover functions ============== */

/**
 * shuffler_chromosome_calc_ratio uses utils_calc_ratio to find the best, worst,
 * avg fitnesses among all chromosomes the probability for each chromosomes
 * based on their fitness value.
 * Note: this is a kernel function and will be called by python.
 * @param *fitness (global) the fitness value array of all chromosomes
 * @param *ratio (global, out) the probability array of each chromosomes
 * @param *best (global, out) the best fitness value
 * @param *worst (global, out) the worse fitness value
 * @param *avg (global, out) the average fitness value
 */__kernel void shuffler_chromosome_calc_ratio(global float* fitness,
                                                global float* ratio,
                                                global float* best,
                                                global float* worst,
                                                global float* avg)
{
  int idx = get_global_id(0);
  // we use the first kernel to calculate the ratio
  if (idx > 0) {
    return;
  }
  utils_calc_ratio(fitness, ratio, best, worst, avg, POPULATION_SIZE);
}

/**
 * shuffler_chromosome_pick_chromosomes picks a chromosome randomly based on the
 * ratio of each chromosome and copy all genes to p_other for crossover. The
 * reason copy to p_other is that OpenCLGA runs crossover at multi-thread mode.
 * The picked chromosomes may also be modified at the same time while crossing
 * over. If we don't copy them, we may have duplicated genes in a chromosome.
 * Note: this is a kernel function and will be called by python.
 * @param *cs (global) all chromosomes
 * @param *fitness (global) all fitness of chromosomes
 * @param *p_other (global) a spared space for storing another chromosome for
 *                          crossover.
 * @param *best (global) the best fitness of all chromosomes.
 * @param *worst (global) the worst fitness of all chromosomes.
 * @param *input_rand (global) all random seeds.
 */
__kernel void shuffler_chromosome_pick_chromosomes(global int* cs,
                                                   global float* fitness,
                                                   global int* p_other,
                                                   global float* ratio,
                                                   global float* best,
                                                   global float* worst,
                                                   global uint* input_rand)
{
  // We want to prevent the best one being changed.
  if (fabs(*worst - *best) < 0.00001) {
    return;
  }
  int idx = get_global_id(0);
  // out of bound kernel task for padding
  if (idx >= POPULATION_SIZE) {
    return;
  }
  uint ra[1];
  init_rand(input_rand[idx], ra);
  global __ShufflerChromosome* chromosomes = (global __ShufflerChromosome*) cs;
  global __ShufflerChromosome* parent_other = (global __ShufflerChromosome*) p_other;
  int i;
  // pick another chromosome randomly
  int cross_idx = random_choose_by_ratio(ratio, ra, POPULATION_SIZE);
  // copy the chromosome to local memory for crossover
  for (i = 0; i < SHUFFLER_CHROMOSOME_GENE_SIZE; i++) {
    parent_other[idx].genes[i] = chromosomes[cross_idx].genes[i];
  }
  input_rand[idx] = ra[0];
}

/**
 * shuffler_chromosome_do_crossover do the crossover algorithm.
 * Note: this is a kernel function and will be called by python.
 * @param *cs (global) all chromosomes
 * @param *fitness (global) all fitness of chromosomes
 * @param *p_other (global) a spared space for storing another chromosome for
 *                          crossover.
 * @param *c_map (global) a temp int array for marking if a gene is already in
 *                        the chromosome.
 * @param *best (global) the best fitness of all chromosomes.
 * @param *worst (global) the worst fitness of all chromosomes.
 * @param *avg (global) the average fitness of all chromosomes.
 * @param *input_rand (global) all random seeds.
 * @param prob_crossover the threshold of crossover.
 */
__kernel void shuffler_chromosome_do_crossover(global int* cs,
                                               global float* fitness,
                                               global int* p_other,
                                               global int* c_map,
                                               global float* best,
                                               global float* worst,
                                               global float* avg,
                                               global uint* input_rand,
                                               float prob_crossover)
{
  if (fabs(*worst - *best) < 0.00001) {
    return;
  }
  int idx = get_global_id(0);
  // out of bound kernel task for padding
  if (idx >= POPULATION_SIZE) {
    return;
  }
  uint ra[1];
  init_rand(input_rand[idx], ra);

  // keep the shortest path, we have to return here to prevent async barrier if someone is returned.
  if (fabs(fitness[idx] - *best) < 0.000001) {
    input_rand[idx] = ra[0];
    return;
  } else if (rand_prob(ra) >= prob_crossover) {
    input_rand[idx] = ra[0];
    return;
  }
  global __ShufflerChromosome* chromosomes = (global __ShufflerChromosome*) cs;
  global __ShufflerChromosome* parent_other = (global __ShufflerChromosome*) p_other;
  // we use chromosome as a map object for checking the existence of Nth item.
  global __ShufflerChromosome* cross_map = (global __ShufflerChromosome*) c_map;
  __ShufflerChromosome self;
  int i;
  int cross_point;

  for (i = 0; i < SHUFFLER_CHROMOSOME_GENE_SIZE; i++) {
    // copy self chromosome to local memory for cross over
    self.genes[i] = chromosomes[idx].genes[i];
    // reset the cross_map to 0
    cross_map[idx].genes[i] = 0;
  }

  // we must be cross over at least one element and must not cross over all of the element.
  cross_point = rand_range(ra, SHUFFLER_CHROMOSOME_GENE_SIZE - 1) + 1;

  // copy the first part from other chromosome
  for (i = 0; i < cross_point; i++) {
    chromosomes[idx].genes[i] = parent_other[idx].genes[i];
    cross_map[idx].genes[parent_other[idx].genes[i]] = 1;
  }
  // sort the second part at self chromosome
  for (i = 0; i < SHUFFLER_CHROMOSOME_GENE_SIZE; i++) {
    if (cross_map[idx].genes[self.genes[i]] == 0) {
        chromosomes[idx].genes[cross_point++] = self.genes[i];
    }
  }
  shuffler_chromosome_check_dup(chromosomes + idx);
  input_rand[idx] = ra[0];
}
/* ============== end of crossover functions ============== */

#endif


bugfix

bugfix

// simple kernel that copy in into out

#define radius 3
#define power_radius (radius * radius)
#define intensity_level 20

__kernel void video_image(
    __read_only image2d_t in,
    __write_only image2d_t out)
{
    const sampler_t format =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_FILTER_NEAREST |
        CLK_ADDRESS_CLAMP;
    const int2 d = (int2)(get_global_id(0), get_global_id(1));
    int intensity_count[intensity_level];
    float4 average_color[intensity_level];
    // cleanup
    for (int i = 0; i < intensity_level; ++i) {
        intensity_count[i] = 0;
        average_color[i] = (float4)(0.0f, 0.0f, 0.0f, 1.0f);
    }
    // step 1
    // for each pixel within radius of pixel
    for (int x = d.x - radius; x < d.x + radius; ++x) {
        for (int y = d.y - radius; y < d.y + radius; ++y) {
            float2 abs_pos = (float2)(d.x - x, d.y - y);
            if (power_radius < dot(abs_pos, abs_pos))
                continue;
            float4 color_element = read_imagef(in, format, (int2)(x, y));
            int current_intensity =
                (dot(color_element, (float4)(1.0f, 1.0f, 1.0f, 0.0f)) / 3.0f) *
                intensity_level;
            current_intensity = (current_intensity >= intensity_level) ?
                intensity_level - 1 :
                current_intensity;
            intensity_count[current_intensity] += 1;
            average_color[current_intensity] += color_element;
        }
    }
    // step 2
    // find the maximum level of intensity
    int max_level = 0;
    int max_index = 0;
    for (int level = 0; level < intensity_level; ++level) {
        if (intensity_count[level] > max_level) {
            max_level = intensity_count[level];
            max_index = level;
        }
    }
    // step 3
    // write the final color
    float4 out_col = average_color[max_index] / max_level;
    out_col.w = 1.0f;
    write_imagef(out, d, out_col);
}

// simple kernel that copy in into out

#define radius 3
#define power_radius (radius * radius)
#define intensity_level 20

__kernel void video_image(
    __read_only image2d_t in,
    __write_only image2d_t out)
{
    const sampler_t format =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_FILTER_NEAREST |
        CLK_ADDRESS_CLAMP;
    const int2 d = (int2)(get_global_id(0), get_global_id(1));
    int intensity_count[intensity_level];
    float4 average_color[intensity_level];
    // cleanup
    for (int i = 0; i < intensity_level; ++i) {
        intensity_count[i] = 0;
        average_color[i] = (float4)(0.0f, 0.0f, 0.0f, 1.0f);
    }
    // step 1
    // for each pixel within radius of pixel
    for (int x = d.x - radius; x < d.x + radius; ++x) {
        for (int y = d.y - radius; y < d.y + radius; ++y) {
            float2 abs_pos = (float2)(d.x - x, d.y - y);
            if (power_radius < dot(abs_pos, abs_pos))
                continue;
            float4 color_element = read_imagef(in, format, (int2)(x, y));
            int current_intensity =
                (dot(color_element, (float4)(1.0f, 1.0f, 1.0f, 0.0f)) / 3.0f) *
                intensity_level;
            current_intensity = (current_intensity >= intensity_level) ?
                current_intensity :
                current_intensity - 1;
            intensity_count[current_intensity] += 1;
            average_color[current_intensity] += color_element;
        }
    }
    // step 2
    // find the maximum level of intensity
    int max_level = 0;
    int max_index = 0;
    for (int level = 0; level < intensity_level; ++level) {
        if (intensity_count[level] > max_level) {
            max_level = intensity_count[level];
            max_index = level;
        }
    }
    // step 3
    // write the final color
    float4 out_col = average_color[max_index] / max_level;
    out_col.w = 1.0f;
    write_imagef(out, d, out_col);
}


another bug falls!

another bug falls!

#include "ocl_platform.cl"

//////////////////////////
// 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
Right (odd) boundary column

 **/

#define BOUNDARY_X 2

#define VERTICAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// VERTICAL_STRIDE * WIN_SIZE_Y

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define HORIZONTAL_ODD_TO_PREVIOUS_EVEN -513
#define HORIZONTAL_ODD_TO_NEXT_EVEN     -511

#define HORIZONTAL_EVEN_TO_PREVIOUS_ODD  511
#define HORIZONTAL_EVEN_TO_NEXT_ODD      513

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

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdX() {
      return getGlobalId(0) - 2 * BOUNDARY_X * getGroupId(0);
}

// read pixel from local buffer
int4 readPixel( LOCAL int*  restrict  src) {
	return (int4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

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

// write column to destination
void writeColumnToOutput(LOCAL int* restrict currentScratch, __write_only image2d_t odata, int firstY, int inputX, int height, int halfHeight){

	int2 posOut = {inputX, firstY>>1};
	for (int j = 0; j < WIN_SIZE_Y; j+=2) {

	    // even row

		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.y >= halfHeight)
			break;

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

		// odd row
		currentScratch += VERTICAL_STRIDE ;
		posOut.y+= halfHeight;

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

		currentScratch += VERTICAL_STRIDE;
		posOut.y -= (halfHeight - 1);
	}
}

// initial scratch offset when transforming vertically
inline int getScratchOffset(){
   return (getLocalId(0)>> 1) + (getLocalId(0)&1) * BUFFER_SIZE;
}

// 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) {

	int inputX = getCorrectedGlobalIdX();

	int outputX = inputX;
	outputX = (outputX >> 1) + (outputX & 1)*( width >> 1);

	const int halfWinSizeX = WIN_SIZE_X >> 1;
    const unsigned int halfHeight = height >> 1;
	LOCAL int scratch[PIXEL_BUFFER_SIZE];
	const float yDelta = 1.0/(height-1);
	int firstY = getGlobalId(1) * (steps * WIN_SIZE_Y);

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

	// read -1 point
	float2 posIn = (float2)(inputX, firstY - 1) /  (float2)(width-1, height-1);
	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 + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_Y; j+=2) {

			///////////////////////////////////////////////////////////////////////////////////////////
			// 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 + yDelta)
				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 += VERTICAL_STRIDE;
			writePixel(currentPlusOne, currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

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

		//4. transform horizontally
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		//odd columns (skip right odd boundary column)
		if ( (getLocalId(0)&1) && (getLocalId(0) != WIN_SIZE_X-1) ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				int4 currentOdd = readPixel(currentScratch);
				int4 prevEven = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN);
				int4 nextEven = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN);
				currentOdd -= ((prevEven + nextEven) >> 1);
				writePixel( currentOdd, currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}

		localMemoryFence();
		//even columns (skip left and right even boundary columns)
		if ( !(getLocalId(0)&1) && (getLocalId(0) != 0) && (getLocalId(0) != WIN_SIZE_X-2)  ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				int4 currentEven = readPixel(currentScratch);
				int4 prevOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_PREVIOUS_ODD);
				int4 nextOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_NEXT_ODD);
				currentEven += (prevOdd + nextOdd + (int4)(2,2,2,2)) >> 2;
				writePixel( currentEven, currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if ((getLocalId(0) >= BOUNDARY_X) && ( getLocalId(0) < WIN_SIZE_X - BOUNDARY_X) && (inputX < width) && inputX >= 0) {
			writeColumnToOutput(scratch + getScratchOffset(), odata, firstY, outputX, height, halfHeight);

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

#include "ocl_platform.cl"

//////////////////////////
// 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
Right (odd) boundary column

 **/

#define BOUNDARY_X 2

#define VERTICAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// VERTICAL_STRIDE * WIN_SIZE_Y

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define HORIZONTAL_ODD_TO_PREVIOUS_EVEN -513
#define HORIZONTAL_ODD_TO_NEXT_EVEN     -511

#define HORIZONTAL_EVEN_TO_PREVIOUS_ODD  511
#define HORIZONTAL_EVEN_TO_NEXT_ODD      513

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

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdX() {
      return getGlobalId(0) - 2 * BOUNDARY_X * getGroupId(0);
}

// read pixel from local buffer
int4 readPixel( LOCAL int*  restrict  src) {
	return (int4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

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

void writeColumnToOutput(LOCAL int* restrict currentScratch, __write_only image2d_t odata, int firstY, int inputX, int height, int halfHeight){

	// write points to destination
	int2 posOut = {inputX, 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 += VERTICAL_STRIDE ;
		posOut.y+= halfHeight + 1;
		if (posOut.y >= height)
			break;

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

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

// initial scratch offset when transforming vertically
inline int getScratchOffset(){
   return (getLocalId(0)>> 1) + (getLocalId(0)&1) * BUFFER_SIZE;
}

// 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) {

	int inputX = getCorrectedGlobalIdX();

	int outputX = inputX;
	outputX = (outputX >> 1) + (outputX & 1)*( width >> 1);

	const int halfWinSizeX = WIN_SIZE_X >> 1;
    const unsigned int halfHeight = height >> 1;
	LOCAL int scratch[PIXEL_BUFFER_SIZE];
	const 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
	float2 posIn = (float2)(inputX, 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 + getScratchOffset();
		for (int j = 0; j < WIN_SIZE_Y; j+=2) {

			///////////////////////////////////////////////////////////////////////////////////////////
			// 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 += VERTICAL_STRIDE;
			writePixel(currentPlusOne, currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

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

		//4. transform horizontally
		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//odd columns (skip right odd boundary column)
		if (getLocalId(0)&1 && (getLocalId(0) != WIN_SIZE_X-1) ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				int4 currentOdd = readPixel(currentScratch);
				int4 prevEven = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN);
				int4 nextEven = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN);
				currentOdd -= ((prevEven + nextEven) >> 1);
				writePixel( currentOdd, currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}
		localMemoryFence();

		//even columns (skip left and right even boundary columns)
		if ( !(getLocalId(0)&1) && (getLocalId(0) != 0) && (getLocalId(0) != WIN_SIZE_X-2)  ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				int4 currentEven = readPixel(currentScratch);
				int4 prevOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_PREVIOUS_ODD);
				int4 nextOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_NEXT_ODD);
				currentEven += (prevOdd + nextOdd + (int4)(2,2,2,2)) >> 2;
				writePixel( currentEven, currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if ((getLocalId(0) >= BOUNDARY_X) && ( getLocalId(0) < WIN_SIZE_X - BOUNDARY_X) && (inputX < width) && inputX >= 0) {
			writeColumnToOutput(scratch + getScratchOffset(), odata, firstY, outputX, height, halfHeight);

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


group even and odd columns together in local scratch buffer

#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 BOUNDARY_X_LEFT 1
 #define BOUNDARY_X_MIDDLE 2

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE 131			// BOUNDARY_X_LEFT + BOUNDARY_X_MIDDLE + WIN_SIZE_X

#define TOTAL_BUFFER_SIZE     1048          // VERTICAL_STRIDE * WIN_SIZE_Y
#define TOTAL_BUFFER_SIZE_X2  2096
#define TOTAL_BUFFER_SIZE_X3  3144

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

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

// write pixel to both designated column and mirrored boundary column
inline void writePixelAndBoundary(int4 pix, LOCAL int*  restrict  dest, int boundaryShift) {
	writePixel(pix, dest);
	writePixel(pix, dest+boundaryShift);
}

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

		currentScratch += VERTICAL_STRIDE ;
		posOut.y+= halfHeight + 1;

		// odd
		if (posOut.y >= height)
			break;

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

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

// offset when transforming columns
inline int getScratchColumnOffset(){
   return BOUNDARY_X_LEFT + (getLocalId(0) >> 1) + ( (getLocalId(0)&1) * (BOUNDARY_X_MIDDLE + (WIN_SIZE_X>>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;

    //check if this column needs to handle a boundary column
	int boundaryShift = 0;
	if (getGlobalId(0) == 1)
		boundaryShift =  halfWinSizeX + 2;
	else if (getGlobalId(0) == 2)
		boundaryShift = -3;
	else if (getGlobalId(0) == width - 2)
	    boundaryShift =  halfWinSizeX + 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 += VERTICAL_STRIDE;
				writePixel(currentPlusOne, currentScratch);

				//advance scratch pointer
				currentScratch += VERTICAL_STRIDE;

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

			//4. transform rows

			// transform odd (two rows at a time)
			currentScratch = scratch + getScratchColumnOffset() + (1+ getLocalId(0) % halfWinSizeX ) * VERTICAL_STRIDE;
			for (int j = 1; j < WIN_SIZE_Y; j+=2) {

				currentScratch += VERTICAL_STRIDE;
			}

			// transform even (two rows at a time
			for (int j = 0; j < WIN_SIZE_Y; j+=2) {

			}

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

/// 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) * 2) - (WIN_SIZE_X - 1) * (getLocalId(0) / (WIN_SIZE_X / 2));
}

/////////////////////////////////////////////////////////////////////////////////
//  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 (version 0)
2 left boundary columns
columns
1 right boundary column

 **/

 #define BOUNDARY_X_LEFT 2
 #define BOUNDARY_X_RIGHT 1

// two vertical neighbours: pointer diff:
#define VERTICAL_STRIDE 131			// BOUNDARY_X_LEFT + BOUNDARY_X_RIGHT + WIN_SIZE_X

#define TOTAL_BUFFER_SIZE     1048          // VERTICAL_STRIDE * WIN_SIZE_Y
#define TOTAL_BUFFER_SIZE_X2  2096
#define TOTAL_BUFFER_SIZE_X3  3144

/*

Layout for scratch buffer (version 1):

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

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

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

int4 readPixel( LOCAL int*  restrict  src) {
	return (int4)(*src, *(src+TOTAL_BUFFER_SIZE),  *(src+TOTAL_BUFFER_SIZE_X2),  *(src+TOTAL_BUFFER_SIZE_X3)) ;
}

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 writePixelAndBoundary(int4 pix, LOCAL int*  restrict  dest, int boundaryShift) {
	writePixel(pix, dest);
	writePixel(pix, dest+boundaryShift);
}

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

		currentScratch += VERTICAL_STRIDE ;
		posOut.y+= halfHeight + 1;

		// odd
		if (posOut.y >= height)
			break;

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

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

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

	int boundaryShift = 0;
	if (getGlobalId(0) == 1)
		boundaryShift = -2;
	else if (getGlobalId(0) == 2)
		boundaryShift = -4;
	if (getGlobalId(0) == width - 2);
	    boundaryShift = 2;

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

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

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

		   LOCAL int* currentScratch = scratch + getLocalId(0) + BOUNDARY_X_LEFT;

		   //read pixels two at a time, and store in local buffer
		   for (int j = 0; j < WIN_SIZE_Y>>1; ++j) {

	   			// jump two pixels
	   			posIn.y += yDelta;
				if (posIn.y >= 1)
				   break;

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

				// read current plus one (odd) point
				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 += VERTICAL_STRIDE;
				writePixel(currentPlusOne, currentScratch);

				//advance scratch pointer
				currentScratch += VERTICAL_STRIDE;

				//update registers
				minusOne = currentPlusOne;
				current = currentPlusTwo;
			}
			writeColumnToOutput(scratch +  getLocalId(0) + BOUNDARY_X_LEFT, odata, firstY, height, halfHeight);
			firstY += WIN_SIZE_Y;
		}

	} else {

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

		   LOCAL int* currentScratch = scratch + getLocalId(0) + BOUNDARY_X_LEFT;

		   //read pixels two at a time, and store in local buffer
		   for (int j = 0; j < WIN_SIZE_Y>>1; ++j) {

	   			// jump two pixels
	   			posIn.y += yDelta;
				if (posIn.y >= 1)
				   break;

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

				// read current plus one (odd) point
				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 += VERTICAL_STRIDE;
				writePixel(currentPlusOne, currentScratch);

				//advance scratch pointer
				currentScratch += VERTICAL_STRIDE;

				//update registers
				minusOne = currentPlusOne;
				current = currentPlusTwo;
			}
			writeColumnToOutput(scratch +  getLocalId(0) + BOUNDARY_X_LEFT, odata, firstY, height, halfHeight);
			firstY += WIN_SIZE_Y;
		}
	}
}


fixed another bug in the horizontal lifting

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

//////////////////////////
// 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
Right (odd) boundary column

 **/

#define BOUNDARY_X 4

#define VERTICAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// VERTICAL_STRIDE * WIN_SIZE_Y

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define HORIZONTAL_EVEN_TO_PREVIOUS_ODD  511
#define HORIZONTAL_EVEN_TO_NEXT_ODD      512

#define HORIZONTAL_ODD_TO_PREVIOUS_EVEN -512
#define HORIZONTAL_ODD_TO_NEXT_EVEN     -511

CONSTANT float P1 = -1.586134342;   ///< forward 9/7 predict 1
CONSTANT float U1 = -0.05298011854;  ///< forward 9/7 update 1
CONSTANT float P2 = 0.8829110762;   ///< forward 9/7 predict 2
CONSTANT float U2 = 0.4435068522;    ///< forward 9/7 update 2
CONSTANT float U1P1 = 0.08403358545952490068;

CONSTANT float scale97Mul = 1.23017410491400f;
CONSTANT float scale97Div = 1.0 / 1.23017410491400f;

/*

Lifting scheme consists of four steps: Predict1 Update1 Predict2 Update2
followed by scaling.

If the odd predict2 pixels are calculated first, then the even update2 pixels
can be easily calculated.

Predict Calculation:

For S odd, we have:

plusOne_P1 = plusOne + P1*(current + plusTwo)

plusOne_P2 = plusOne_P1 +
             P2*(current_U1 + plusTwo_U1)
           =   plusOne + P1*(current + plusTwo) +
		         P2*(current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo) +
				     plusTwo + U1*(plusOne + plusThree) + U1P1*(current + 2*plusTwo + plusFour)  )

Update Calculation

For S even, we have

current_U1 = current + U1*(minusOne_P1 + plusOne_P1)
           = current + U1*(minusOne + P1*(minusTwo + current) + plusOne + P1*(current + plusTwo))
		   = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo)

current_U2 = current_U1 + U2*(minusOne_P2 + plusOne_P2)

*/

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

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdX() {
      return getGlobalId(0) - 2 * BOUNDARY_X * getGroupId(0);
}

// read pixel from local buffer
float4 readPixel( LOCAL float*  restrict  src) {
	return (float4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

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

// write column to destination
void writeColumnToOutput(LOCAL float* restrict currentScratch, __write_only image2d_t odata, int firstY, int inputX, int height, int halfHeight){

	int2 posOut = {inputX, firstY>>1};
	for (int j = 0; j < WIN_SIZE_Y; j+=2) {

	    // even row

		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.y >= halfHeight)
			break;

		write_imagef(odata, posOut,scale97Div * readPixel(currentScratch));

		// odd row
		currentScratch += VERTICAL_STRIDE ;
		posOut.y+= halfHeight;

		write_imagef(odata, posOut,scale97Mul * readPixel(currentScratch));

		currentScratch += VERTICAL_STRIDE;
		posOut.y -= (halfHeight - 1);
	}
}

// initial scratch offset when transforming vertically
inline int getScratchOffset(){
   return (getLocalId(0)>> 1) + (getLocalId(0)&1) * BUFFER_SIZE;
}

// 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) {

	int inputX = getCorrectedGlobalIdX();

	int outputX = inputX;
	outputX = (outputX >> 1) + (outputX & 1)*( width >> 1);

	const int halfWinSizeX = WIN_SIZE_X >> 1;
    const unsigned int halfHeight = height >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float yDelta = 1.0/(height-1);
	int firstY = getGlobalId(1) * (steps * WIN_SIZE_Y);

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

	// read -4 point
	float2 posIn = (float2)(inputX, firstY - 4) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.y += yDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.y += yDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.y += yDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

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

	// +1 point
	posIn.y += yDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.y += yDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

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

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

	        //read next two points

			// +3 point
			posIn.y += yDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.y += yDelta;
	   		if (posIn.y > 1 + 3*yDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform horizontally
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - odd columns (skip left three boundary columns and all right boundary columns)
		if ( (getLocalId(0)&1) && (getLocalId(0) >= BOUNDARY_X-1) && (getLocalId(0) < WIN_SIZE_X-BOUNDARY_X) ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				float4 plusOne = readPixel(currentScratch);
				float4 current = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN);
				float4 minusOne = readPixel(currentScratch -1);
				float4 minusTwo = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN -1);
				float4 plusTwo = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN);
				float4 plusThree = readPixel(currentScratch + 1);
				float4 plusFour = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN+1);

				float4 current_U1 = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo);
				float4 plusOne_P2 =   plusOne + P1*(current + plusTwo) +
		           P2*(current_U1 + plusTwo + U1*(plusOne + plusThree) + U1P1*(current + 2*plusTwo + plusFour)  );

				writePixel(plusOne_P2, currentScratch);
				writePixel(current_U1, currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN);
				currentScratch += VERTICAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - even columns (skip left and right boundary columns)
		if ( !(getLocalId(0)&1) && (getLocalId(0) >= BOUNDARY_X) && (getLocalId(0) < WIN_SIZE_X-BOUNDARY_X)  ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {

				float4 current = readPixel(currentScratch);
				float4 prevOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_PREVIOUS_ODD);
				float4 nextOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_NEXT_ODD);
				writePixel(current + U2*(prevOdd + nextOdd), currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if ((getLocalId(0) >= BOUNDARY_X) && ( getLocalId(0) < WIN_SIZE_X - BOUNDARY_X) && (inputX < width) && inputX >= 0) {
			writeColumnToOutput(scratch + getScratchOffset(), odata, firstY, outputX, height, halfHeight);

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

/*  Copyright 2014 Aaron Boxer

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "ocl_platform.cl"

//////////////////////////
// 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
Right (odd) boundary column

 **/

#define BOUNDARY_X 4

#define VERTICAL_STRIDE 64  // WIN_SIZE_X/2

// two vertical neighbours: pointer diff:
#define BUFFER_SIZE            512	// VERTICAL_STRIDE * WIN_SIZE_Y

#define CHANNEL_BUFFER_SIZE     1024            // BUFFER_SIZE + BUFFER_SIZE

#define CHANNEL_BUFFER_SIZE_X2  2048
#define CHANNEL_BUFFER_SIZE_X3  3072

#define PIXEL_BUFFER_SIZE   4096

#define HORIZONTAL_EVEN_TO_PREVIOUS_ODD  511
#define HORIZONTAL_EVEN_TO_NEXT_ODD      512

#define HORIZONTAL_ODD_TO_PREVIOUS_EVEN -512
#define HORIZONTAL_ODD_TO_NEXT_EVEN     -511

CONSTANT float P1 = -1.586134342;   ///< forward 9/7 predict 1
CONSTANT float U1 = -0.05298011854;  ///< forward 9/7 update 1
CONSTANT float P2 = 0.8829110762;   ///< forward 9/7 predict 2
CONSTANT float U2 = 0.4435068522;    ///< forward 9/7 update 2
CONSTANT float U1P1 = 0.08403358545952490068;

CONSTANT float scale97Mul = 1.23017410491400f;
CONSTANT float scale97Div = 1.0 / 1.23017410491400f;

/*

Lifting scheme consists of four steps: Predict1 Update1 Predict2 Update2
followed by scaling.

If the odd predict2 pixels are calculated first, then the even update2 pixels
can be easily calculated.

Predict Calculation:

For S odd, we have:

plusOne_P1 = plusOne + P1*(current + plusTwo)

plusOne_P2 = plusOne_P1 +
             P2*(current_U1 + plusTwo_U1)
           =   plusOne + P1*(current + plusTwo) +
		         P2*(current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo) +
				     plusTwo + U1*(plusOne + plusThree) + U1P1*(current + 2*plusTwo + plusFour)  )

Update Calculation

For S even, we have

current_U1 = current + U1*(minusOne_P1 + plusOne_P1)
           = current + U1*(minusOne + P1*(minusTwo + current) + plusOne + P1*(current + plusTwo))
		   = current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo)

current_U2 = current_U1 + U2*(minusOne_P2 + plusOne_P2)

*/

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

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_MIRRORED_REPEAT  | CLK_FILTER_NEAREST;

inline int getCorrectedGlobalIdX() {
      return getGlobalId(0) - 2 * BOUNDARY_X * getGroupId(0);
}

// read pixel from local buffer
float4 readPixel( LOCAL float*  restrict  src) {
	return (float4)(*src, *(src+CHANNEL_BUFFER_SIZE),  *(src+CHANNEL_BUFFER_SIZE_X2),  *(src+CHANNEL_BUFFER_SIZE_X3)) ;
}

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

// write column to destination
void writeColumnToOutput(LOCAL float* restrict currentScratch, __write_only image2d_t odata, int firstY, int inputX, int height, int halfHeight){

	int2 posOut = {inputX, firstY>>1};
	for (int j = 0; j < WIN_SIZE_Y; j+=2) {

	    // even row

		//only need to check evens, since even point will be the first out of bound point
	    if (posOut.y >= halfHeight)
			break;

		write_imagef(odata, posOut,scale97Div * readPixel(currentScratch));

		// odd row
		currentScratch += VERTICAL_STRIDE ;
		posOut.y+= halfHeight;

		write_imagef(odata, posOut,scale97Mul * readPixel(currentScratch));

		currentScratch += VERTICAL_STRIDE;
		posOut.y -= (halfHeight - 1);
	}
}

// initial scratch offset when transforming vertically
inline int getScratchOffset(){
   return (getLocalId(0)>> 1) + (getLocalId(0)&1) * BUFFER_SIZE;
}

// 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) {

	int inputX = getCorrectedGlobalIdX();

	int outputX = inputX;
	outputX = (outputX >> 1) + (outputX & 1)*( width >> 1);

	const int halfWinSizeX = WIN_SIZE_X >> 1;
    const unsigned int halfHeight = height >> 1;
	LOCAL float scratch[PIXEL_BUFFER_SIZE];
	const float yDelta = 1.0/(height-1);
	int firstY = getGlobalId(1) * (steps * WIN_SIZE_Y);

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

	// read -4 point
	float2 posIn = (float2)(inputX, firstY - 4) /  (float2)(width-1, height-1);
	float4 minusFour = read_imagef(idata, sampler, posIn);

	posIn.y += yDelta;
	float4 minusThree = read_imagef(idata, sampler, posIn);

	// read -2 point
	posIn.y += yDelta;
	float4 minusTwo = read_imagef(idata, sampler, posIn);

	// read -1 point
	posIn.y += yDelta;
	float4 minusOne = read_imagef(idata, sampler, posIn);

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

	// +1 point
	posIn.y += yDelta;
	float4 plusOne = read_imagef(idata, sampler, posIn);

	// +2 point
	posIn.y += yDelta;
	float4 plusTwo = read_imagef(idata, sampler, posIn);

	float4 minusThree_P1 = minusThree + P1*(minusFour + minusTwo);
	float4 minusOne_P1   = minusOne   + P1*(minusTwo + current);
	float4 plusOne_P1    = plusOne    + P1*(current + plusTwo);

	float4 minusTwo_U1 = minusTwo + U1*(minusThree_P1 + minusOne_P1);
	float4 current_U1  = current + U1*(minusOne_P1 + plusOne_P1);
	float4 minusOne_P2 = minusOne_P1 + P2*(minusTwo_U1 + current_U1);

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

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

	        //read next two points

			// +3 point
			posIn.y += yDelta;
			float4 plusThree = read_imagef(idata, sampler, posIn);

	   		// +4 point
			posIn.y += yDelta;
	   		if (posIn.y > 1 + 3*yDelta)
				break;
			float4 plusFour = read_imagef(idata, sampler, posIn);

			float4 plusThree_P1    = plusThree  + P1*(plusTwo + plusFour);
			float4 plusTwo_U1      = plusTwo + U1*(plusOne_P1 + plusThree_P1);
			float4 plusOne_P2      = plusOne_P1 + P2*(current_U1 + plusTwo_U1);

			//write current U2 (even)
			writePixel(scale97Div * (current_U1 +  U2 * (minusOne_P2 + plusOne_P2)), currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

			//write current P2 (odd)
			writePixel(scale97Mul* plusOne_P2 , currentScratch);

			//advance scratch pointer
			currentScratch += VERTICAL_STRIDE;

			// shift registers up by two
			minusFour = minusTwo;
			minusThree = minusOne;
			minusTwo = current;
			minusOne = plusOne;
			current = plusTwo;
			plusOne = plusThree;
			plusTwo = plusFour;
			//update P1s
			minusThree_P1 = minusOne_P1;
			minusOne_P1 = plusOne_P1;
			plusOne_P1 = plusThree_P1;

			//update U1s
			minusTwo_U1 = current_U1;
			current_U1 = plusTwo_U1;

			//update P2s
			minusOne_P2 = plusOne_P2;
		}

		//4. transform horizontally
		currentScratch = scratch + getScratchOffset();

		localMemoryFence();
		// P2 - odd columns (skip left three boundary columns and all right boundary columns)
		if ( (getLocalId(0)&1) && (getLocalId(0) >= BOUNDARY_X-1) && (getLocalId(0) < WIN_SIZE_X-BOUNDARY_X) ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {
				float4 current = readPixel(currentScratch);
				float4 minusOne = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN);
				float4 minusTwo = readPixel(currentScratch -1);
				float4 minusThree = readPixel(currentScratch + HORIZONTAL_ODD_TO_PREVIOUS_EVEN -1);
				float4 plusOne = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN);
				float4 plusTwo = readPixel(currentScratch + 1);
				float4 plusThree = readPixel(currentScratch + HORIZONTAL_ODD_TO_NEXT_EVEN+1);

				float4 current_P2 =  current + P1*(minusOne + plusOne) +
		         P2*( minusOne + U1*(minusTwo + current) + U1P1*(minusThree + 2*minusOne + plusOne) +
				      plusOne + U1*(current + plusTwo) + U1P1*(minusOne + 2*plusOne + plusThree)  );
				writePixel(current_P2, currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}

		currentScratch = scratch + getScratchOffset();
		localMemoryFence();
		//U2 - even columns (skip left and right boundary columns)
		if ( !(getLocalId(0)&1) && (getLocalId(0) >= BOUNDARY_X) && (getLocalId(0) < WIN_SIZE_X-BOUNDARY_X)  ) {
			for (int j = 0; j < WIN_SIZE_Y; j++) {

				float4 current = readPixel(currentScratch);
				float4 minusOne = readPixel(currentScratch + HORIZONTAL_EVEN_TO_PREVIOUS_ODD);
				float4 minusTwo = readPixel(currentScratch -1);
				float4 plusOne = readPixel(currentScratch + HORIZONTAL_EVEN_TO_NEXT_ODD);
				float4 plusTwo = readPixel(currentScratch + 1);
				float4 prevOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_PREVIOUS_ODD);
				float4 nextOdd = readPixel(currentScratch + HORIZONTAL_EVEN_TO_NEXT_ODD);
				writePixel(current + U1*(minusOne + plusOne) + U1P1*(minusTwo + 2*current + plusTwo) +
				                                                   U2*(prevOdd + nextOdd), currentScratch);
				currentScratch += VERTICAL_STRIDE;
			}
		}
		localMemoryFence();

		//5. write local buffer column to destination image
		// (only write non-boundary columns that are within the image bounds)
		if ((getLocalId(0) >= BOUNDARY_X) && ( getLocalId(0) < WIN_SIZE_X - BOUNDARY_X) && (inputX < width) && inputX >= 0) {
			writeColumnToOutput(scratch + getScratchOffset(), odata, firstY, outputX, height, halfHeight);

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