the same

the same

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

// filter size for all filters with small n of input maps (first layer)
// split a long filter by stride

#define MLO_N_FILTER_SPLITS1 ((MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1 - 1)/ MLO_FILTER_STRIDE1)
#ifndef MLO_OUT_PIX_TILE0
#define MLO_N_FILTER_SPLITS0 ((MLO_FILTER_SIZE0 + MLO_FILTER_STRIDE0 - 1)/ MLO_FILTER_STRIDE0)
#define MLO_OUT_PIX_TILE0 MLO_N_FILTER_SPLITS0
#endif
// processing arrangement
// generate full output width
// extent1 == MLO_GRP_SZ / MLO_PROCESING_WIDTH
#ifndef MLO_OUT_EXTENT1
#define MLO_PROCESSING_WIDTH  ((MLO_OUT_WIDTH + MLO_OUT_PIX_TILE0 - 1) / MLO_OUT_PIX_TILE0)
#define MLO_OUT_EXTENT1 (MLO_GRP_SZ / MLO_PROCESSING_WIDTH)
#endif

#define MLO_WEI_LCL_WIDTH MLO_FILTER_SIZE0 //(MLO_N_FILTER_SPLITS0*MLO_FILTER_STRIDE0)
#define MLO_WEI_EXTENT1 MLO_N_FILTER_SPLITS1
#define MLO_WEI_SZ (MLO_WEI_EXTENT1*MLO_WEI_LCL_WIDTH)
// LDS size
#define MLO_WEI_LCL_SZ (MLO_WEI_SZ * MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS)

#define MLO_IN_LCL_HEIGHT (MLO_OUT_EXTENT1 + MLO_N_FILTER_SPLITS1 - 1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS  / MLO_READ_UNIT)*MLO_READ_UNIT)

#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2 * MLO_FILTER_PAD0)
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH*MLO_IN_LCL_HEIGHT)
// LDS size
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_IN_LCL_SZ* MLO_N_LCL_IN_MAPS)

//#if (MLO_WEI_LCL_SZ + MLO_TOTAL_IN_LCL_SZ) > (MLO_N_PARTIAL_SUMS *MLO_OUT_WIDTH*MLO_OUT_EXTENT1)
#define MLO_LCL_MEM_SZ (MLO_WEI_LCL_SZ + MLO_TOTAL_IN_LCL_SZ)
//#else
//#define MLO_LCL_MEM_SZ (MLO_N_PARTIAL_SUMS *MLO_OUT_WIDTH*MLO_OUT_EXTENT1)
#//endif

// number of loops to flush put full output map
#define MLO_N_OUT_BLKS 1   //((MLO_OUT_HEIGHT + (MLO_OUT_PIX_TILE1*MLO_N_OUT_FOLDS1) -1) / (MLO_OUT_PIX_TILE1*MLO_N_OUT_FOLDS1))

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
static inline int getWaveId()
{
	int wave_id = 0;

	wave_id = __hsail_get_dynwave_id();
	wave_id = wave_id & MLO_N_PHYS_WAVES_MASK;
	return(wave_id);
}
#else
static inline int getWaveId()
{
	int wave_id = 0;

	wave_id = (get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ);

	return(wave_id);
}
#endif

static inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ) << MLO_LG2_PHYS_WAVE_SZ);
	return(lcl_wave_id);
}

static inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

static inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

static inline void ReduceKernel(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{

				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

static inline void  Kahan_summation(_FLOAT *sum, _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

static inline void  Kahan_summation_tricked(_FLOAT *sum, _FLOAT * c, _FLOAT v, _FLOAT mod)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) * mod - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

static inline void Kahan_summation2(_FLOAT *sum, _FLOAT *c, _FLOAT *v, int n)
{
	for (int i = 0; i < n; ++i)
	{
		_FLOAT y = v[i] - c[i];    //So far, so good: c is zero.
		_FLOAT t = sum[i] + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
		c[i] = (t - sum[i]) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
		sum[i] = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
	}
}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// process 3 output pixel per wk-item, 19 wk-items per output scan,
// 13 output sacn-line per group of 256
// read (13+2) input scan-lines 4 scan-lines apart from 2 batches
// convolve with 3 filters rows 4 rowes apart from 4(8) filter banks.

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvFwd(
	const __global _FLOAT * bot,
	const __global _FLOAT * weights,
#if MLO_CONV_BIAS == 1
	const __global _FLOAT * bias,
#endif
	__global _FLOAT *top,
	_FLOAT padding_val
)
{

	__local _FLOAT lcl_mem[MLO_LCL_MEM_SZ];
	__local _FLOAT * bot_mem = lcl_mem;
	__local _FLOAT * wei_mem = lcl_mem + MLO_TOTAL_IN_LCL_SZ;

	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int ob = get_group_id(0);  // output map extent id

	int k_idx = get_group_id(1) * (MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS); // input map index based

	int c_idx = 0;

	int ib_idx = get_group_id(2)*MLO_N_LCL_BATCHS; // batch idx

	int ib = ib_idx;

	int gbl_in_off = /*c_idx * MLO_IN_CHANNEL_STRIDE + */ib * MLO_IN_BATCH_STRIDE;
	int gbl_wei_off = k_idx * MLO_WEI_BATCH_STRIDE;

#define MLO_ACCUM_SZ (MLO_OUT_PIX_TILE1 * MLO_OUT_PIX_TILE0 * MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS*MLO_N_LCL_BATCHS)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	// zero out LDS
	for (int i = lcl_id; i < (MLO_LCL_MEM_SZ); i += MLO_GRP_SZ)
	{
		lcl_mem[i] = 0;
	}

// processing arrangement
	int ex_row = iDiv(lcl_id, MLO_PROCESSING_WIDTH);
//
	int ex_col = iMod(lcl_id, ex_row, MLO_PROCESSING_WIDTH);
	int ex_pix = ex_col * MLO_OUT_PIX_TILE0;

	int out_y = ob*MLO_OUT_EXTENT1;

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		b += MLO_N_LCL_BATCHS,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE
		)
	{

		barrier(CLK_LOCAL_MEM_FENCE);

		// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1 input scans
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		int gbl_in_scan_off0 = gbl_in_off;

		// generate pixels all MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS output maps
//		for (int ob = 0; ob < MLO_N_OUT_BLKS; ++ob, in_y0 += (MLO_OUT_PIX_TILE1 *MLO_FILTER_STRIDE1*MLO_N_OUT_FOLDS1), gbl_in_scan_off0 += (MLO_OUT_PIX_TILE1 *MLO_FILTER_STRIDE1*MLO_N_OUT_FOLDS1) * MLO_IN_CHANNEL_STRIDE, out_y += MLO_OUT_PIX_TILE1 *MLO_N_OUT_FOLDS1)
		{

			for (int i = 0; i < MLO_ACCUM_SZ; ++i)
			{
				pvt_accum[i] = 0;
			}

			// all input maps
			for (int c = 0, gbl_in_scan_off = gbl_in_scan_off0; c < MLO_N_INPUTS; ++c, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
			{
				for (int f_s = 0; f_s < MLO_FILTER_STRIDE1; ++f_s)
				{

					barrier(CLK_LOCAL_MEM_FENCE);

					// read weights by stride
					for (int w = lcl_id; w < MLO_WEI_LCL_SZ; w += MLO_GRP_SZ)
					{
						int k = iDiv(w, MLO_WEI_SZ);
						int t0 = iMod(w, k, MLO_WEI_SZ);
						int j = iDiv(t0, MLO_FILTER_SIZE0);
						int i = iMod(t0, j, MLO_FILTER_SIZE0);
						int wei_off = gbl_wei_off + k*MLO_WEI_BATCH_STRIDE + c*MLO_WEI_CHANNEL_STRIDE;

						if ((j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i < MLO_WEI_CHANNEL_STRIDE)
						{
							wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i] = weights[wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i];
#if 0
								if (ob==0)
								{
									printf("G:w: %d %d %d   %f %f\n",
//										lcl_id,
//										w,
//										f_s,
//										j,
//										i,
//										k_idx,
//										k,
										k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i,
										gbl_wei_off,
										wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i,
										weights[wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i],
										wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i]
									);
								}

#endif

						}
						else
						{
							wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i] = 0;
						}
					}

					int n_reads = (ob == 0 && (f_s < MLO_FILTER_PAD1)) ? MLO_IN_LCL_HEIGHT - 1 : MLO_IN_LCL_HEIGHT;
					int lcl_scan = (ob == 0 && (f_s < MLO_FILTER_PAD1)) ? 1 : 0;

					// fetch input by stride
					for (int p4 = lcl_id, c_scan = 0;  p4 < MLO_N_IN_HORIZ_READS * n_reads * MLO_N_LCL_BATCHS;
						p4 += MLO_GRP_SZ)
					{
						int b = iDiv(p4, MLO_N_IN_HORIZ_READS * n_reads);
						int t0 = iMod(p4, b, MLO_N_IN_HORIZ_READS * n_reads);
						c_scan = iDiv(t0, MLO_N_IN_HORIZ_READS);
						int c_pix4 = iMod(t0, c_scan, MLO_N_IN_HORIZ_READS);
						int in_scan = (c_scan + lcl_scan) * MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1;

						if (0 <= out_y*MLO_FILTER_STRIDE1 + in_scan && out_y*MLO_FILTER_STRIDE1 + in_scan < MLO_IN_HEIGHT)
						{

							int gbl_off = gbl_in_scan_off + b*MLO_IN_BATCH_STRIDE + (out_y*MLO_FILTER_STRIDE1 + in_scan) * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
							// still problems with unaligned LDS access
#if MLO_IN_N_PIXS_OFF > 0
							if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
							{
								int i = 0;
								for (; i < MLO_IN_N_PIXS_OFF; ++i)
								{
									in_rd_data[i] = bot[gbl_off + i];
								}
								for (; i < MLO_READ_UNIT; ++i)
								{
									in_rd_data[i] = 0;
								}

							}
							else
#endif
							{
								//					*(MLO_READ_TYPE*)in_rd_data = *(__global MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];

								for (int i = 0; i < MLO_READ_UNIT; ++i)
								{
									in_rd_data[i] = bot[gbl_off + i];
								}
							}
							int lcl_off = (lcl_scan + c_scan)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT;
							for (int i = 0; i < MLO_READ_UNIT; ++i)
							{
								bot_mem[lcl_off + i] = in_rd_data[i];
							}

						}
					}

					barrier(CLK_LOCAL_MEM_FENCE);

					// convolution
					// along vertical filter
					for (int m = 0; m < MLO_N_FILTER_SPLITS1; ++m)
					{
						// select all vertical scans that matches the vertical filter tap
						__private _FLOAT in_vals[MLO_N_LCL_BATCHS * ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0)];
						// read input values for this filter phase
						for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
						{
							for (int i = 0; i < ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0); ++i)
							{
								in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + i]
									= bot_mem[bb*MLO_IN_LCL_SZ + (ex_row + m) * MLO_IN_LCL_WIDTH + ex_pix*MLO_FILTER_STRIDE0 + i];
							}

						}
// only for 11
						__private _FLOAT wei_vals[MLO_N_LCL_OUT_MAPS*MLO_FILTER_STRIDE0];
						// first 2 splits
						int l;
						for (l = 0; l < MLO_N_FILTER_SPLITS0 - 1; ++l)
						{
// read all weights
							for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
							{
								for (int i = 0; i < MLO_FILTER_STRIDE0; ++i)
								{
									wei_vals[k*MLO_FILTER_STRIDE0 + i]
										= wei_mem[k*MLO_WEI_SZ + m*MLO_WEI_LCL_WIDTH + l*MLO_FILTER_STRIDE0 + i];
								}
							}

							// convolve
							for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
							{

								for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
								{
									for (int n = 0; n < MLO_OUT_PIX_TILE0; ++n)
									{

										for (int i = 0; i < MLO_FILTER_STRIDE0; ++i)
										{
											_FLOAT in_val = in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + (n+l)*MLO_FILTER_STRIDE0 + i];
											_FLOAT wei_val = wei_vals[k*MLO_FILTER_STRIDE0 + i];
											pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n]
												+= wei_val * in_val;
#if 0
											if (wei_val * in_val != 0 && ib+b+bb == 0 && k_idx+k == 0 && out_y + ex_row == 0 && ex_pix + n == 2)
											{
												printf("G:c: %d %d %d %d %d %d %d %d %d %d %d %d  %f %f %f %f\n",
													f_s,
													out_y,
													ex_row,
													ex_pix,
													m,
													n,
													l,
													i,
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1, // actual input vertical position
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i - MLO_FILTER_PAD0, // actual input horiz pos (assuming full scan is inside LDS)
													m*MLO_FILTER_STRIDE1 + f_s, // actual filter vet pos
													l*MLO_FILTER_STRIDE0 + i, // actual filter horiz pos
													pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n],
													wei_val * in_val,
													wei_val,
													in_val
												);
											}

#endif

										}
									}
								}
							} // b
						} // l
// 3d
						{

							for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
							{
								for (int i = 0; i < MLO_FILTER_STRIDE0 - 1; ++i)
								{
									wei_vals[k*MLO_FILTER_STRIDE0 + i]
										= wei_mem[k*MLO_WEI_SZ + m*MLO_WEI_LCL_WIDTH + l*MLO_FILTER_STRIDE0 + i];
								}
							}

							// convolve
							for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
							{

								for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
								{
									for (int n = 0; n < MLO_OUT_PIX_TILE0; ++n)
									{

										for (int i = 0; i < MLO_FILTER_STRIDE0 - 1; ++i)
										{
											_FLOAT in_val = in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + (n+l)*MLO_FILTER_STRIDE0 + i];
											_FLOAT wei_val = wei_vals[k*MLO_FILTER_STRIDE0 + i];
											pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n]
												+= wei_val * in_val;
#if 0
											if (wei_val * in_val != 0 && ib + b + bb == 0 && k_idx + k == 0 && out_y + ex_row == 0 && ex_pix + n == 2)
											{
												printf("G:c: %d %d %d %d %d %d %d %d %d %d %d %d  %f %f %f %f\n",
													f_s,
													out_y,
													ex_row,
													ex_pix,
													m,
													n,
													l,
													i,
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1,
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i - MLO_FILTER_PAD0, // actual input horiz pos (assuming full scan is inside LDS)
													m*MLO_FILTER_STRIDE1 + f_s, // actual filter vet pos
													l*MLO_FILTER_STRIDE0 + i, // actual filter horiz pos
													pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n],
													wei_val * in_val,
													wei_val,
													in_val
												);
											}

#endif
										}
									}
								}
							} // b
						} // l

					} // m

				} // f_s

			} // c

			barrier(CLK_LOCAL_MEM_FENCE);

			for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
			{
				for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
				{
					// write out
					// inputs are outputs
					int out_off = (ib + b + bb) * MLO_OUT_BATCH_STRIDE + (k_idx + k) * MLO_OUT_CHANNEL_STRIDE + (out_y + ex_row) *MLO_OUT_STRIDE + ex_pix;
					for (int i = 0; i < MLO_OUT_PIX_TILE0 && (out_y + ex_row) < MLO_OUT_HEIGHT && ex_pix + i < MLO_OUT_WIDTH; ++i)
					{
						top[out_off + i] = pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + i];
					}
				}
			}

		} // ob

	} // b
}

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

// filter size for all filters with small n of input maps (first layer)
// split a long filter by stride

#define MLO_N_FILTER_SPLITS1 ((MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1 - 1)/ MLO_FILTER_STRIDE1)
#ifndef MLO_OUT_PIX_TILE0
#define MLO_N_FILTER_SPLITS0 ((MLO_FILTER_SIZE0 + MLO_FILTER_STRIDE0 - 1)/ MLO_FILTER_STRIDE0)
#define MLO_OUT_PIX_TILE0 MLO_N_FILTER_SPLITS0
#endif
// processing arrangement
// generate full output width
// extent1 == MLO_GRP_SZ / MLO_PROCESING_WIDTH
#ifndef MLO_OUT_EXTENT1
#define MLO_PROCESSING_WIDTH  ((MLO_OUT_WIDTH + MLO_OUT_PIX_TILE0 - 1) / MLO_OUT_PIX_TILE0)
#define MLO_OUT_EXTENT1 (MLO_GRP_SZ / MLO_PROCESSING_WIDTH)
#endif

#define MLO_WEI_LCL_WIDTH MLO_FILTER_SIZE0 //(MLO_N_FILTER_SPLITS0*MLO_FILTER_STRIDE0)
#define MLO_WEI_EXTENT1 MLO_N_FILTER_SPLITS1
#define MLO_WEI_SZ (MLO_WEI_EXTENT1*MLO_WEI_LCL_WIDTH)
// LDS size
#define MLO_WEI_LCL_SZ (MLO_WEI_SZ * MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS)

#define MLO_IN_LCL_HEIGHT (MLO_OUT_EXTENT1 + MLO_N_FILTER_SPLITS1 - 1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS  / MLO_READ_UNIT)*MLO_READ_UNIT)

#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2 * MLO_FILTER_PAD0)
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH*MLO_IN_LCL_HEIGHT)
// LDS size
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_IN_LCL_SZ* MLO_N_LCL_IN_MAPS)

//#if (MLO_WEI_LCL_SZ + MLO_TOTAL_IN_LCL_SZ) > (MLO_N_PARTIAL_SUMS *MLO_OUT_WIDTH*MLO_OUT_EXTENT1)
#define MLO_LCL_MEM_SZ (MLO_WEI_LCL_SZ + MLO_TOTAL_IN_LCL_SZ)
//#else
//#define MLO_LCL_MEM_SZ (MLO_N_PARTIAL_SUMS *MLO_OUT_WIDTH*MLO_OUT_EXTENT1)
#//endif

// number of loops to flush put full output map
#define MLO_N_OUT_BLKS 1   //((MLO_OUT_HEIGHT + (MLO_OUT_PIX_TILE1*MLO_N_OUT_FOLDS1) -1) / (MLO_OUT_PIX_TILE1*MLO_N_OUT_FOLDS1))

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
static inline int getWaveId()
{
	int wave_id = 0;

	wave_id = __hsail_get_dynwave_id();
	wave_id = wave_id & MLO_N_PHYS_WAVES_MASK;
	return(wave_id);
}
#else
static inline int getWaveId()
{
	int wave_id = 0;

	wave_id = (get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ);

	return(wave_id);
}
#endif

static inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ) << MLO_LG2_PHYS_WAVE_SZ);
	return(lcl_wave_id);
}

static inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

static inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

static inline void ReduceKernel(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{

				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

static inline void  Kahan_summation(_FLOAT *sum, _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

static inline void  Kahan_summation_tricked(_FLOAT *sum, _FLOAT * c, _FLOAT v, _FLOAT mod)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) * mod - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

static inline void Kahan_summation2(_FLOAT *sum, _FLOAT *c, _FLOAT *v, int n)
{
	for (int i = 0; i < n; ++i)
	{
		_FLOAT y = v[i] - c[i];    //So far, so good: c is zero.
		_FLOAT t = sum[i] + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
		c[i] = (t - sum[i]) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
		sum[i] = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
	}
}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// process 3 output pixel per wk-item, 19 wk-items per output scan,
// 13 output sacn-line per group of 256
// read (13+2) input scan-lines 4 scan-lines apart from 2 batches
// convolve with 3 filters rows 4 rowes apart from 4(8) filter banks.

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvFwd(
	const __global _FLOAT * bot,
	const __global _FLOAT * weights,
#if MLO_CONV_BIAS == 1
	const __global _FLOAT * bias,
#endif
	__global _FLOAT *top,
	_FLOAT padding_val
)
{

	__local _FLOAT lcl_mem[MLO_LCL_MEM_SZ];
	__local _FLOAT * bot_mem = lcl_mem;
	__local _FLOAT * wei_mem = lcl_mem + MLO_TOTAL_IN_LCL_SZ;

	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int ob = get_group_id(0);  // output map extent id

	int k_idx = get_group_id(1) * (MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS); // input map index based

	int c_idx = 0;

	int ib_idx = get_group_id(2)*MLO_N_LCL_BATCHS; // batch idx

	int ib = ib_idx;

	int gbl_in_off = /*c_idx * MLO_IN_CHANNEL_STRIDE + */ib * MLO_IN_BATCH_STRIDE;
	int gbl_wei_off = k_idx * MLO_WEI_BATCH_STRIDE;

#define MLO_ACCUM_SZ (MLO_OUT_PIX_TILE1 * MLO_OUT_PIX_TILE0 * MLO_N_LCL_OUT_MAPS* MLO_N_LCL_IN_MAPS*MLO_N_LCL_BATCHS)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	// zero out LDS
	for (int i = lcl_id; i < (MLO_LCL_MEM_SZ); i += MLO_GRP_SZ)
	{
		lcl_mem[i] = 0;
	}

// processing arrangement
	int ex_row = iDiv(lcl_id, MLO_PROCESSING_WIDTH);
//
	int ex_col = iMod(lcl_id, ex_row, MLO_PROCESSING_WIDTH);
	int ex_pix = ex_col * MLO_OUT_PIX_TILE0;

	int out_y = ob*MLO_OUT_EXTENT1;

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		b += MLO_N_LCL_BATCHS,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE
		)
	{

		barrier(CLK_LOCAL_MEM_FENCE);

		// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1 input scans
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		int gbl_in_scan_off0 = gbl_in_off;

		// generate pixels all MLO_OUT_STACKS * MLO_N_LCL_OUT_MAPS output maps
//		for (int ob = 0; ob < MLO_N_OUT_BLKS; ++ob, in_y0 += (MLO_OUT_PIX_TILE1 *MLO_FILTER_STRIDE1*MLO_N_OUT_FOLDS1), gbl_in_scan_off0 += (MLO_OUT_PIX_TILE1 *MLO_FILTER_STRIDE1*MLO_N_OUT_FOLDS1) * MLO_IN_CHANNEL_STRIDE, out_y += MLO_OUT_PIX_TILE1 *MLO_N_OUT_FOLDS1)
		{

			for (int i = 0; i < MLO_ACCUM_SZ; ++i)
			{
				pvt_accum[i] = 0;
			}

			// all input maps
			for (int c = 0, gbl_in_scan_off = gbl_in_scan_off0; c < MLO_N_INPUTS; ++c, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
			{
				for (int f_s = 0; f_s < MLO_FILTER_STRIDE1; ++f_s)
				{

					barrier(CLK_LOCAL_MEM_FENCE);

					// read weights by stride
					for (int w = lcl_id; w < MLO_WEI_LCL_SZ; w += MLO_GRP_SZ)
					{
						int k = iDiv(w, MLO_WEI_SZ);
						int t0 = iMod(w, k, MLO_WEI_SZ);
						int j = iDiv(t0, MLO_FILTER_SIZE0);
						int i = iMod(t0, j, MLO_FILTER_SIZE0);
						int wei_off = gbl_wei_off + k*MLO_WEI_BATCH_STRIDE + c*MLO_WEI_CHANNEL_STRIDE;

						if ((j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i < MLO_WEI_CHANNEL_STRIDE)
						{
							wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i] = weights[wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i];
#if 0
								if (ob==0)
								{
									printf("G:w: %d %d %d   %f %f\n",
//										lcl_id,
//										w,
//										f_s,
//										j,
//										i,
//										k_idx,
//										k,
										k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i,
										gbl_wei_off,
										wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i,
										weights[wei_off + (j*MLO_FILTER_STRIDE1 + f_s)*MLO_FILTER_SIZE0 + i],
										wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i]
									);
								}

#endif

						}
						else
						{
							wei_mem[k*MLO_WEI_SZ + j*MLO_WEI_LCL_WIDTH + i] = 0;
						}
					}

					int n_reads = (ob == 0 && (f_s < MLO_FILTER_PAD1)) ? MLO_IN_LCL_HEIGHT - 1 : MLO_IN_LCL_HEIGHT;
					int lcl_scan = (ob == 0 && (f_s < MLO_FILTER_PAD1)) ? 1 : 0;

					// fetch input by stride
					for (int p4 = lcl_id, c_scan = 0;  p4 < MLO_N_IN_HORIZ_READS * n_reads * MLO_N_LCL_BATCHS;
						p4 += MLO_GRP_SZ)
					{
						int b = iDiv(p4, MLO_N_IN_HORIZ_READS * n_reads);
						int t0 = iMod(p4, b, MLO_N_IN_HORIZ_READS * n_reads);
						c_scan = iDiv(t0, MLO_N_IN_HORIZ_READS);
						int c_pix4 = iMod(t0, c_scan, MLO_N_IN_HORIZ_READS);
						int in_scan = (c_scan + lcl_scan) * MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1;

						if (0 <= out_y*MLO_FILTER_STRIDE1 + in_scan && out_y*MLO_FILTER_STRIDE1 + in_scan < MLO_IN_HEIGHT)
						{

							int gbl_off = gbl_in_scan_off + b*MLO_IN_BATCH_STRIDE + (out_y*MLO_FILTER_STRIDE1 + in_scan) * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
							// still problems with unaligned LDS access
#if MLO_IN_N_PIXS_OFF > 0
							if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
							{
								int i = 0;
								for (; i < MLO_IN_N_PIXS_OFF; ++i)
								{
									in_rd_data[i] = bot[gbl_off + i];
								}
								for (; i < MLO_READ_UNIT; ++i)
								{
									in_rd_data[i] = 0;
								}

							}
							else
#endif
							{
								//					*(MLO_READ_TYPE*)in_rd_data = *(__global MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];

								for (int i = 0; i < MLO_READ_UNIT; ++i)
								{
									in_rd_data[i] = bot[gbl_off + i];
								}
							}
							int lcl_off = (lcl_scan + c_scan)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT;
							for (int i = 0; i < MLO_READ_UNIT; ++i)
							{
								bot_mem[lcl_off + i] = in_rd_data[i];
							}

						}
					}

					barrier(CLK_LOCAL_MEM_FENCE);

					// convolution
					// along vertical filter
					for (int m = 0; m < MLO_N_FILTER_SPLITS1; ++m)
					{
						// select all vertical scans that matches the vertical filter tap
						__private _FLOAT in_vals[MLO_N_LCL_BATCHS * ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0)];
						// read input values for this filter phase
						for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
						{
							for (int i = 0; i < ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0); ++i)
							{
								in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + i]
									= bot_mem[bb*MLO_IN_LCL_SZ + (ex_row + m) * MLO_IN_LCL_WIDTH + ex_pix*MLO_FILTER_STRIDE0 + i];
							}

						}
// only for 11
						__private _FLOAT wei_vals[MLO_N_LCL_OUT_MAPS*MLO_FILTER_STRIDE0];
						// first 2 splits
						int l;
						for (l = 0; l < MLO_N_FILTER_SPLITS0 - 1; ++l)
						{
// read all weights
							for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
							{
								for (int i = 0; i < MLO_FILTER_STRIDE0; ++i)
								{
									wei_vals[k*MLO_FILTER_STRIDE0 + i]
										= wei_mem[k*MLO_WEI_SZ + m*MLO_WEI_LCL_WIDTH + l*MLO_FILTER_STRIDE0 + i];
								}
							}

							// convolve
							for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
							{

								for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
								{
									for (int n = 0; n < MLO_OUT_PIX_TILE0; ++n)
									{

										for (int i = 0; i < MLO_FILTER_STRIDE0; ++i)
										{
											_FLOAT in_val = in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + (n+l)*MLO_FILTER_STRIDE0 + i];
											_FLOAT wei_val = wei_vals[k*MLO_FILTER_STRIDE0 + i];
											pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n]
												+= wei_val * in_val;
#if 1
											if (wei_val * in_val != 0 && ib+b+bb == 0 && k_idx+k == 0 && out_y + ex_row == 0 && ex_pix + n == 2)
											{
												printf("G:c: %d %d %d %d %d %d %d %d %d %d %d %d  %f %f %f %f\n",
													f_s,
													out_y,
													ex_row,
													ex_pix,
													m,
													n,
													l,
													i,
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1, // actual input vertical position
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i - MLO_FILTER_PAD0, // actual input horiz pos (assuming full scan is inside LDS)
													m*MLO_FILTER_STRIDE1 + f_s, // actual filter vet pos
													l*MLO_FILTER_STRIDE0 + i, // actual filter horiz pos
													pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n],
													wei_val * in_val,
													wei_val,
													in_val
												);
											}

#endif

										}
									}
								}
							} // b
						} // l
// 3d
						{

							for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
							{
								for (int i = 0; i < MLO_FILTER_STRIDE0 - 1; ++i)
								{
									wei_vals[k*MLO_FILTER_STRIDE0 + i]
										= wei_mem[k*MLO_WEI_SZ + m*MLO_WEI_LCL_WIDTH + l*MLO_FILTER_STRIDE0 + i];
								}
							}

							// convolve
							for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
							{

								for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
								{
									for (int n = 0; n < MLO_OUT_PIX_TILE0; ++n)
									{

										for (int i = 0; i < MLO_FILTER_STRIDE0 - 1; ++i)
										{
											_FLOAT in_val = in_vals[bb* ((MLO_OUT_PIX_TILE0 - 1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + (n+l)*MLO_FILTER_STRIDE0 + i];
											_FLOAT wei_val = wei_vals[k*MLO_FILTER_STRIDE0 + i];
											pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n]
												+= wei_val * in_val;
#if 1
											if (wei_val * in_val != 0 && ib + b + bb == 0 && k_idx + k == 0 && out_y + ex_row == 0 && ex_pix + n == 2)
											{
												printf("G:c: %d %d %d %d %d %d %d %d %d %d %d %d  %f %f %f %f\n",
													f_s,
													out_y,
													ex_row,
													ex_pix,
													m,
													n,
													l,
													i,
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s - MLO_FILTER_PAD1,
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i - MLO_FILTER_PAD0, // actual input horiz pos (assuming full scan is inside LDS)
													m*MLO_FILTER_STRIDE1 + f_s, // actual filter vet pos
													l*MLO_FILTER_STRIDE0 + i, // actual filter horiz pos
													pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + n],
													wei_val * in_val,
													wei_val,
													in_val
												);
											}

#endif
										}
									}
								}
							} // b
						} // l

					} // m

				} // f_s

			} // c

			barrier(CLK_LOCAL_MEM_FENCE);

			for (int bb = 0; bb < MLO_N_LCL_BATCHS; ++bb)
			{
				for (int k = 0; k < MLO_N_LCL_OUT_MAPS; ++k)
				{
					// write out
					// inputs are outputs
					int out_off = (ib + b + bb) * MLO_OUT_BATCH_STRIDE + (k_idx + k) * MLO_OUT_CHANNEL_STRIDE + (out_y + ex_row) *MLO_OUT_STRIDE + ex_pix;
					for (int i = 0; i < MLO_OUT_PIX_TILE0 && (out_y + ex_row) < MLO_OUT_HEIGHT && ex_pix + i < MLO_OUT_WIDTH; ++i)
					{
						top[out_off + i] = pvt_accum[(bb*MLO_N_LCL_OUT_MAPS + k) * MLO_OUT_PIX_TILE0 + i];
					}
				}
			}

		} // ob

	} // b
}


new 3x3

new 3x3

/*
 * Copyright (c) 2016 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define MLO_N_OUT_HORIZ_READS ((MLO_OUT_WIDTH + MLO_IN_TILE0 - 1) / MLO_IN_TILE0)
#define MLO_N_SPANS_PER_SCAN (MLO_N_OUT_HORIZ_READS)
#define MLO_N_OUT_HORIZ_PIX_READS (MLO_N_OUT_HORIZ_READS * MLO_IN_TILE0)
#define MLO_OUT_N_PIXS_OFF (MLO_OUT_WIDTH - ((MLO_OUT_WIDTH / MLO_IN_TILE0)*MLO_IN_TILE0))
#define MLO_N_OUT_VERTICAL_READS (MLO_FILTER_SIZE1)

#define MLO_IN_VERT_READS (MLO_IN_HEIGHT)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS/MLO_READ_UNIT)*MLO_READ_UNIT)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2* MLO_FILTER_PAD0)
#define MLO_IN_LCL_HEIGHT MLO_IN_VERT_READS
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)

#define MLO_WEI_LCL_SZ (MLO_GRP_SZ * MLO_FILTER_SIZE0)
#if MLO_TOTAL_IN_LCL_SZ > MLO_WEI_LCL_SZ
#define MLO_LCL_SZ (MLO_TOTAL_IN_LCL_SZ)
#else
#define MLO_LCL_SZ (MLO_WEI_LCL_SZ)
#endif

#define MLO_HW_WAVE_ID_SETTING 0
#if MLO_HW_WAVE_ID_SETTING //&& MLO_COMPILER_AMD_OPENCL_HSAIL==1

// FIXME Conduct enabling from the host code.
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
#endif
static inline int getWaveId()
{
	int wave_id = 0;

#if MLO_HW_WAVE_ID_SETTING //&& MLO_COMPILER_AMD_OPENCL_HSAIL==1

	wave_id = __hsail_get_dynwave_id();
	wave_id &= MLO_N_WAVES_MASK;
#if 0
#elif MLO_HW_WAVE_ID_SETTING && MLO_COMPILER_AMD_OPENCL_LC==1 && MLO_GRP_SZ1==1 && MLO_GRP_SZ2==1 && (MLO_GRP_SZ % (1 << MLO_LG2_WAVE_SZ))==0
	// (local_id/wavesize) has the same value in all workitems.
	// Make it scalar to enable scalarization optimizations.
	wave_id = __llvm_amdgcn_readfirstlane((uint)(get_local_id(0) >> MLO_LG2_WAVE_SZ));
	// Alternate implementation:
	//__asm__ ("v_readfirstlane_b32 %0, %1" : "=s" (wave_id) : "v" ((int)(get_local_id(0) >> MLO_LG2_WAVE_SZ)) );
#endif
#else
	wave_id = (get_local_id(0) >> MLO_LG2_WAVE_SZ);
#endif
	return(wave_id);
}

inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_WAVE_SZ) << MLO_LG2_WAVE_SZ);
	return(lcl_wave_id);
}

inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, __private _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
// add second half
// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

static inline void  Kahan_summation(__private _FLOAT *sum, __private _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

/*********************************************************************************************************
// wrw algorithm for 7x7 and smallee
// idea:
// layout
// each wave keeps WAVE_SZ output maps
// 1 input map
// alg
// for  all batches

// read output maps in VGPRS 1 map per wk_item
// for number of waves
// read full input map in SGPRs
// convolve internal pixels
// convolve border pixels
// exchange input maps
// rof
// rof

// write out

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW_7x7(
	const __global _FLOAT * top_df,
	__constant _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int c_idx_base = get_group_id(0); // input map index base

	int o_idx_base = get_group_id(1); // output map index base

	int ib_base = get_group_id(2); //batch index

	int ib = ib_base*MLO_N_LCL_BATCHS;

	int c_idx = c_idx_base * MLO_N_WAVES* MLO_N_LCL_IN_MAPS; // input map index

	int o_idx = o_idx_base * MLO_N_WAVES * MLO_HW_WAVE_SZ* MLO_N_LCL_OUT_MAPS; // output map index

	int gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	int gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

// out address based on wave and batch
	int out_map = lcl_wv_id;

	gbl_out_off +=  (wave_id * MLO_HW_WAVE_SZ  + out_map) * MLO_OUT_CHANNEL_STRIDE;

#define MLO_DAT_SZ (MLO_IN_HEIGHT * MLO_IN_WIDTH)

	__private _FLOAT top_dat[MLO_DAT_SZ];

/*
	for (int i = 0; i < MLO_DAT_SZ; ++i)
	{
		top_dat[i] = 0;
	}

*/
	__private _FLOAT bot_dat[MLO_DAT_SZ];

/*
	for (int i = 0; i < MLO_DAT_SZ; ++i)
	{
		bot_dat[i] = 0;
	}
*/

#define MLO_ACCUM_SZ (MLO_N_WAVES * MLO_N_LCL_OUT_MAPS * MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	for (int i = 0; i < MLO_ACCUM_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		// read output map

		for (int j = 0; j < MLO_DAT_SZ; ++j)
		{
			top_dat[j] = top_df[gbl_out_off + j];
		}
		for (int w = 0; w < MLO_N_WAVES; ++w)
		{
			// in address based on wave_id

			int wave_id_r = w; //((wave_id + w) < MLO_N_WAVES) ?  (wave_id + w) : (wave_id + w) - MLO_N_WAVES;
			int gbl_in_off_r = gbl_in_off + wave_id_r * MLO_IN_CHANNEL_STRIDE;

			// read input map presumably to SGPRs

			for (int j = 0; j < MLO_DAT_SZ; ++j)
			{
				bot_dat[j] = bot[gbl_in_off_r + j];
			}

// convolve
// internals
			for (int m = 0; m < MLO_FILTER_SIZE1 - 1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];

#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:si: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
 				} // l

			} // m

// top
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = 0;
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];

#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:st: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif

						}  // i
					} // j
				} // l

			} // m

// bot
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = MLO_IN_HEIGHT - 1;
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:sb: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

// left
			for (int m = 0; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						int i = 0;
						{
							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:sl: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

// right
			for (int m = 0; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						int i = MLO_IN_WIDTH -1;
						{
							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:sr: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

// top - left
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = 0;
					{
						int i = 0;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:stl: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

#if 1
// top - right
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					int j = 0;
					{
						int i = MLO_IN_WIDTH - 1;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:str: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

// bot - left
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = MLO_IN_WIDTH - 1;
					{
						int i = 0;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:sbl: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

// bot - right
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					int j = MLO_IN_HEIGHT - 1;
					{
						int i = MLO_IN_WIDTH - 1;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)* MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
#if 0
							_FLOAT bot_val = bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
							_FLOAT top_val = top_dat[j*MLO_IN_WIDTH + i];

							if (bot_val*top_val != 0 && lcl_id == 0 && m == 0 && l == 0)
							{
								printf("K:sbl: %d %d %f %f %f %f\n",
									j,
									i,
									pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l],
									bot_val*top_val,
									bot_val,
									top_val
								);
							}
#endif
						}  // i
					} // j
				} // l

			} // m

#endif

		} // w

	} // 	for (int b = 0;

// output
// inputs are outputs
// TODO : for more than 1 input
	int c = 0;

	int wei_df_off = (wave_id * MLO_HW_WAVE_SZ + out_map) * MLO_WEI_BATCH_STRIDE + c_idx * MLO_WEI_CHANNEL_STRIDE;

	for (int w = 0; w < MLO_N_WAVES && (wave_id * MLO_HW_WAVE_SZ + out_map) < MLO_N_OUTPUTS && (c_idx + w) < MLO_N_INPUTS; ++w)
	{
		for (int i = 0; i < (MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0); ++i)
		{
			weights_df[wei_df_off + w * MLO_WEI_CHANNEL_STRIDE + i] = pvt_accum[w*(MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0) + i];
		}

	}

}

#if 0
// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MLOpenCvBwdWrW_rdc(
	const __global _FLOAT * weight_df_tmp,
	__global _FLOAT * weights_df
)
{
	int gbl_id = get_global_id(0);
	int wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

	int wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	int wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (int i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	int batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (int i = 0; i < batch_loop; ++i)
	{
		*(MLO_UT_READ_TYPE*)pvt_accum_wei
			+= *(__global MLO_UT_READ_TYPE*)&weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE)  + wei_idx];
	}

	*(__global MLO_UT_READ_TYPE*)&weights_df[wei_idx0] = *(MLO_UT_READ_TYPE*)pvt_accum_wei;

}

#endif

/*
 * Copyright (c) 2016 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define MLO_N_OUT_HORIZ_READS ((MLO_OUT_WIDTH + MLO_IN_TILE0 - 1) / MLO_IN_TILE0)
#define MLO_N_SPANS_PER_SCAN (MLO_N_OUT_HORIZ_READS)
#define MLO_N_OUT_HORIZ_PIX_READS (MLO_N_OUT_HORIZ_READS * MLO_IN_TILE0)
#define MLO_OUT_N_PIXS_OFF (MLO_OUT_WIDTH - ((MLO_OUT_WIDTH / MLO_IN_TILE0)*MLO_IN_TILE0))
#define MLO_N_OUT_VERTICAL_READS (MLO_FILTER_SIZE1)

#define MLO_IN_VERT_READS (MLO_IN_HEIGHT)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS/MLO_READ_UNIT)*MLO_READ_UNIT)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2* MLO_FILTER_PAD0)
#define MLO_IN_LCL_HEIGHT MLO_IN_VERT_READS
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)

#define MLO_WEI_LCL_SZ (MLO_GRP_SZ * MLO_FILTER_SIZE0)
#if MLO_TOTAL_IN_LCL_SZ > MLO_WEI_LCL_SZ
#define MLO_LCL_SZ (MLO_TOTAL_IN_LCL_SZ)
#else
#define MLO_LCL_SZ (MLO_WEI_LCL_SZ)
#endif

#define MLO_HW_WAVE_ID_SETTING 1
#if MLO_HW_WAVE_ID_SETTING //&& MLO_COMPILER_AMD_OPENCL_HSAIL==1

// FIXME Conduct enabling from the host code.
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
#endif
static inline int getWaveId()
{
	int wave_id = 0;

#if MLO_HW_WAVE_ID_SETTING //&& MLO_COMPILER_AMD_OPENCL_HSAIL==1

	wave_id = __hsail_get_dynwave_id();
	wave_id &= MLO_N_WAVES_MASK;
#if 0
#elif MLO_HW_WAVE_ID_SETTING && MLO_COMPILER_AMD_OPENCL_LC==1 && MLO_GRP_SZ1==1 && MLO_GRP_SZ2==1 && (MLO_GRP_SZ % (1 << MLO_LG2_WAVE_SZ))==0
	// (local_id/wavesize) has the same value in all workitems.
	// Make it scalar to enable scalarization optimizations.
	wave_id = __llvm_amdgcn_readfirstlane((uint)(get_local_id(0) >> MLO_LG2_WAVE_SZ));
	// Alternate implementation:
	//__asm__ ("v_readfirstlane_b32 %0, %1" : "=s" (wave_id) : "v" ((int)(get_local_id(0) >> MLO_LG2_WAVE_SZ)) );
#endif
#else
	wave_id = (get_local_id(0) >> MLO_LG2_WAVE_SZ);
#endif
	return(wave_id);
}

inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_WAVE_SZ) << MLO_LG2_WAVE_SZ);
	return(lcl_wave_id);
}

inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, __private _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
// add second half
// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

static inline void  Kahan_summation(__private _FLOAT *sum, __private _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

/*********************************************************************************************************
// wrw algorithm for 7x7 and smallee
// idea:
// layout
// each wave keeps WAVE_SZ output maps
// 1 input map
// alg
// for  all batches

// read output maps in VGPRS 1 map per wk_item
// for number of waves
// read full input map in SGPRs
// convolve internal pixels
// convolve border pixels
// exchange input maps
// rof
// rof

// write out

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW_7x7(
	const __global _FLOAT * top_df,
	__constant _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int c_idx_base = get_group_id(0); // input map index base

	int o_idx_base = get_group_id(1); // output map index base

	int ib_base = get_group_id(2); //batch index

	int ib = ib_base*MLO_N_LCL_BATCHS;

	int c_idx = c_idx_base * MLO_N_WAVES* MLO_N_LCL_IN_MAPS; // input map index

	int o_idx = o_idx_base * MLO_N_WAVES * MLO_HW_WAVE_SZ* MLO_N_LCL_OUT_MAPS; // output map index

	int gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	int gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

// out address based on wave and batch
	int out_map = lcl_wv_id;

	gbl_out_off +=  (wave_id * MLO_HW_WAVE_SZ  + out_map) * MLO_OUT_CHANNEL_STRIDE;

#define MLO_DAT_SZ (MLO_IN_HEIGHT * MLO_IN_WIDTH)

	__private _FLOAT top_dat[MLO_DAT_SZ];

/*
	for (int i = 0; i < MLO_DAT_SZ; ++i)
	{
		top_dat[i] = 0;
	}

*/
	__private _FLOAT bot_dat[MLO_DAT_SZ];

/*
	for (int i = 0; i < MLO_DAT_SZ; ++i)
	{
		bot_dat[i] = 0;
	}
*/

#define MLO_ACCUM_SZ (MLO_N_WAVES * MLO_N_LCL_OUT_MAPS * MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)

	__private _FLOAT pvt_accum[MLO_ACCUM_SZ];

	for (int i = 0; i < MLO_ACCUM_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		// read output map

		for (int j = 0; j < MLO_DAT_SZ; ++j)
		{
			top_dat[j] = top_df[gbl_out_off + j];
		}
		for (int w = 0; w < MLO_N_WAVES; ++w)
		{
			// in address based on wave_id

			int wave_id_r = w; //((wave_id + w) < MLO_N_WAVES) ?  (wave_id + w) : (wave_id + w) - MLO_N_WAVES;
			int gbl_in_off_r = gbl_in_off + wave_id_r * MLO_IN_CHANNEL_STRIDE;

			// read input map presumably to SGPRs

			for (int j = 0; j < MLO_DAT_SZ; ++j)
			{
				bot_dat[j] = bot[gbl_in_off_r + j];
			}

// convolve
// internals
			for (int m = 0; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
 				} // l

			} // m

#if 1

// top
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = 0;
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
				} // l

			} // m

// bot
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = MLO_IN_HEIGHT - 1;
					{
						for (int i = MLO_FILTER_PAD0; i < MLO_IN_WIDTH - MLO_FILTER_PAD0; ++i)
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
				} // l

			} // m

// left
			for (int m = 0; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						int i = 0;
						{
							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
				} // l

			} // m

// right
			for (int m = 0; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					for (int j = MLO_FILTER_PAD1; j < MLO_IN_HEIGHT - MLO_FILTER_PAD1; ++j)
					{
						int i = MLO_IN_WIDTH -1;
						{
							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1) *MLO_IN_WIDTH + i + l];
						}  // i
					} // j
				} // l

			} // m

// top - left
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = 0;
					{
						int i = 0;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
				} // l

			} // m

// top - right
			for (int m = MLO_FILTER_PAD1; m < MLO_FILTER_SIZE1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					int j = 0;
					{
						int i = MLO_IN_WIDTH - 1;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m - MLO_FILTER_PAD1)*MLO_IN_WIDTH + i + l];
						}  // i
					} // j
				} // l

			} // m

// bot - left
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = MLO_FILTER_PAD0; l < MLO_FILTER_SIZE0; ++l)
				{
					int j = MLO_IN_WIDTH - 1;
					{
						int i = 0;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m)*MLO_IN_WIDTH + i + l - MLO_FILTER_PAD0];
						}  // i
					} // j
				} // l

			} // m

// bot - right
			for (int m = 0; m < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1; ++m)
			{
				for (int l = 0; l < MLO_FILTER_SIZE0 - MLO_FILTER_PAD0; ++l)
				{
					int j = MLO_IN_HEIGHT - 1;
					{
						int i = MLO_IN_WIDTH - 1;
						{

							pvt_accum[(wave_id_r * MLO_FILTER_SIZE1 + m) * MLO_FILTER_SIZE0 + l]
								+= top_dat[j*MLO_IN_WIDTH + i] * bot_dat[(j + m)* MLO_IN_WIDTH + i + l];
						}  // i
					} // j
				} // l

			} // m

#endif

		} // w

	} // 	for (int b = 0;

// output
// inputs are outputs
// TODO : for more than 1 input
	int c = 0;

	int wei_df_off = (wave_id * MLO_HW_WAVE_SZ + out_map) * MLO_WEI_BATCH_STRIDE + c_idx * MLO_WEI_CHANNEL_STRIDE;

	for (int w = 0; w < MLO_N_WAVES && (wave_id * MLO_HW_WAVE_SZ + out_map) < MLO_N_OUTPUTS && (c_idx + w) < MLO_N_INPUTS; ++w)
	{
		for (int i = 0; i < (MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0); ++i)
		{
			weights_df[wei_df_off + w * MLO_WEI_CHANNEL_STRIDE + i] = pvt_accum[w*(MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0) + i];
		}

	}

}

#if 0
// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MLOpenCvBwdWrW_rdc(
	const __global _FLOAT * weight_df_tmp,
	__global _FLOAT * weights_df
)
{
	int gbl_id = get_global_id(0);
	int wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

	int wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	int wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (int i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	int batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (int i = 0; i < batch_loop; ++i)
	{
		*(MLO_UT_READ_TYPE*)pvt_accum_wei
			+= *(__global MLO_UT_READ_TYPE*)&weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE)  + wei_idx];
	}

	*(__global MLO_UT_READ_TYPE*)&weights_df[wei_idx0] = *(MLO_UT_READ_TYPE*)pvt_accum_wei;

}

#endif


refactor

refactor

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

/*

Bit Plane Coder
===============

A. Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define CODEBLOCKY_QUARTER 8

#define BOUNDARY 1
#define BOUNDARY_X2 2

#define CODEBLOCKY_X4 128

#define STATE_BUFFER_SIZE 1156
#define STATE_BOTTOM_BOUNDARY_OFFSET 1124

#define STATE_BUFFER_STRIDE 34
#define STATE_BUFFER_STRIDE_X2 68
#define STATE_BUFFER_STRIDE_X3 102
#define STATE_BUFFER_STRIDE_X4 136

#define LEFT_TOP -35
#define TOP  -34
#define RIGHT_TOP -33

#define LEFT   -1
#define RIGHT   1

#define LEFT_BOTTOM  33
#define BOTTOM  34
#define RIGHT_BOTTOM  35

// bit positions (0 based indices)
#define INPUT_SIGN_BITPOS  15

#define SIGMA_NEW_BITPOS   0x1
#define SIGMA_OLD_BITPOS   0x1
#define NBH_BITPOS         0x2
#define SIGMA_OLD_TO_NBH_SHIFT 0x1
#define RLC_BITPOS         0x4
#define PIXEL_START_BITPOS 0xA
#define PIXEL_END_BITPOS   0x18
#define SIGN_BITPOS        0x19

// bit flags
#define SIGMA_NEW_F			 0x1		  //position 0
#define NOT_SIGMA_NEW_F      0xFFFFFFFE   // ~SIGMA_NEW
#define SIGMA_OLD_F			 0x10		  //position  1
#define NBH_F				 0x20		  //position  2
#define RLC_F				 0x80		  //position  4
#define RLC_D_POSITION_F	 0x380        //positions 6-9
#define PIXEL_F				 0x7FFF0000   //positions 10-24
#define SIGN_F				 0x2000000    //position  25

#define INPUT_TO_SIGN_SHIFT 10

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

#define BIT(pix) (((pix)>>(bp))&1)
#define NBH(pix) ((pix) & NBH_F)
#define SIGMA_OLD(pix) ((pix) & SIGMA_OLD_F)
#define SIGMA_NEW(pix) ((pix) & SIGMA_NEW_F)
#define SIGN(pix) ((pix) & SIGN_F)

#define SET_SIGMA_NEW(pix) ( (pix) |= SIGMA_NEW_F )
#define CLEAR_SIGMA_NEW(pix) ( (pix) &= NOT_SIGMA_NEW_F )

#define AMD

/**

**/

void KERNEL run(read_only image2d_t channel) {

	// state buffer
	LOCAL int state[STATE_BUFFER_SIZE];
	LOCAL int cxd[STATE_BUFFER_SIZE];

	///////////////////////////////////////////////////////////////////////////////////
	//1. Calculate MSB

	LOCAL int msbScratch[CODEBLOCKX]; // between one and 32 - zero value indicates that this code block is identically zero

	int maxSigBit;
	if (getLocalId(1) == 0) {

		int maxVal = 0;
		state[getLocalId(0)] = 0;   //top boundary
		LOCAL int* statePtr = state + (BOUNDARY + getLocalId(0));
		int2 posIn = (int2)(getGlobalId(0),  (getGlobalId(1) >> 3)*CODEBLOCKY);

		for (int i = 0; i < CODEBLOCKY; ++i) {
			int pixel = read_imagei(channel, sampler, posIn).x;
			int absPixel = abs(pixel);
			maxVal = max(maxVal, absPixel);
			pixel = (absPixel << PIXEL_START_BITPOS) | SIGN((pixel << INPUT_TO_SIGN_SHIFT));
			statePtr[0] = pixel;

			posIn.y++;
			statePtr += STATE_BUFFER_STRIDE;
		}
		state[ getLocalId(0) + STATE_BOTTOM_BOUNDARY_OFFSET] = 0;		//bottom boundary

		//initialize full left and right boundary columns
		if (getLocalId(0) == 0 || getLocalId(0) == CODEBLOCKX-1) {
			int delta = -1 + (getLocalId(0)/(CODEBLOCKX-1))*2; // -1 or +1
			statePtr = state + BOUNDARY + getLocalId(0) + delta;
			for (int i = 0; i < CODEBLOCKY+ BOUNDARY_X2; ++i) {
				 *statePtr = 0;
				 statePtr += STATE_BUFFER_STRIDE;
			}
		}

		// calculate column-msb
		maxSigBit = 31 - clz(maxVal);
		msbScratch[getLocalId(0)] =maxSigBit;
	}
	localMemoryFence();

	//calculate global msb
	if (!getLocalId(0) && !getLocalId(1) ) {
		int4 mx = (int4)(maxSigBit);

		LOCAL int* scratchPtr = msbScratch;
		for(int i=0; i < CODEBLOCKX; i+=4) {
			mx = max(mx,(int4)(msbScratch[i],msbScratch[i+1],msbScratch[i+2],msbScratch[i+3]));
		}
		maxSigBit = mx.x;
		maxSigBit = max(maxSigBit, mx.y);
		maxSigBit = max(maxSigBit, mx.z);
		maxSigBit = max(maxSigBit, mx.w);
		msbScratch[0] = maxSigBit;
	}
	localMemoryFence();

	if (msbScratch[0] == -1)
		return;

	int startIndex = (BOUNDARY + getLocalId(0)) + (BOUNDARY + getLocalId(1))*STATE_BUFFER_STRIDE_X4;

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 2. CUP on MSB

	//Algorithm 13 Clean-up pass on the MSB
	//1: set sigmanew := bit value
	//2: set rlcNbh :=  SUM(sigmanew) of preceding column and upper position
	//3: if rlcNbh = 0 and y-dimension of current position is a multiple of 4
	//then
	//4: execute RLC operation
	//5: end if
	//6: if rlcNbh = 1 then
	//7: execute ZC operation
	//8: end if
	//9: if sigmanew = 1 then
	//10: execute SC operation
	//11: end if

	int bp = msbScratch[0] + PIXEL_START_BITPOS;

	// i) set sigma_new for strip column (sigma_old is zero)
	LOCAL int* statePtr = state + startIndex;
	for (int i = 0; i < 4; ++i) {
		int current = statePtr[0];
		current |= BIT(current);
		*statePtr = current;
		statePtr += STATE_BUFFER_STRIDE;

	}
	localMemoryFence();

	// ii) set rlc nbh for strip column
	statePtr = state + startIndex;
	int current			= statePtr[0];
	int top				= statePtr[TOP];
	int leftTop			= statePtr[LEFT_TOP];
	int left			= statePtr[LEFT];
	int leftBottom		= statePtr[LEFT_BOTTOM];

	int rlcCount = 0;

	current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
	statePtr[0] = current;

	// set doRLC flag
	bool doRLC = !NBH(current) && !BIT(current);

	if (doRLC) {
		rlcCount++;
	} else {
		if ( NBH(current) ) {
			//ZC
		}
		if (BIT(current)) {
		    //SC
		}
	}

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

		statePtr += STATE_BUFFER_STRIDE;
		top = current;
		current = statePtr[0];
		leftTop = left;
		left = leftBottom;
		leftBottom = statePtr[LEFT_BOTTOM];
		current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
		statePtr[0] = current;

		// toggle doRLC flag
		doRLC = doRLC && !NBH(current) && !BIT(current);
		if (doRLC) {

			rlcCount++;
		} else {
		   if (NBH(current)) {
				//ZC
		   }
		   if (BIT(current)) {
		       //SC
		   }
		}
	}

	// last pixel in strip column -
	// ignore leftBottom pixel, because it is in the next
	// stripe and it hasn't been processed yet
	statePtr += STATE_BUFFER_STRIDE;
	top = current;
	current = statePtr[0];
	leftTop = left;
	left = leftBottom;
	leftBottom = statePtr[LEFT_BOTTOM];
	current |=  (BIT(top) | BIT(leftTop) | BIT(left)) << NBH_BITPOS;
	statePtr[0] = current;

	// toggle doRLC flag
	doRLC = doRLC && !NBH(current) && !BIT(current);
	if (doRLC) {

		rlcCount++;
	} else {
		if (NBH(current)) {
			//ZC
		}
		if (BIT(current)) {
		    //SC
		}
	}

	localMemoryFence();

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// 3. Processs rest of bit plans
	bp--;
	LOCAL char blockVote;

	while (bp >= PIXEL_START_BITPOS) {
		blockVote = 0;

		/////////////////////////////
		// 4. pre-process bit plane

		// i) migrate sigma_new to sigma_old, and clear sigma new bit
		statePtr = state + startIndex;
		for (int i = 0; i < 4; ++i) {
		    int current = *statePtr;
			current |=  SIGMA_NEW(current) << SIGMA_OLD_BITPOS;
			CLEAR_SIGMA_NEW(current);

			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;

		}
		localMemoryFence();

		// ii) update nbh
		statePtr = state + startIndex;

		int top = statePtr[TOP];
		int leftTop = statePtr[LEFT_TOP];
		int rightTop = statePtr[RIGHT_TOP];
		int left = statePtr[LEFT];
		int current = statePtr[0];
		int right = statePtr[RIGHT];
		int leftBottom = statePtr[LEFT_BOTTOM];
		int bottom = statePtr[BOTTOM];
		int rightBottom = statePtr[RIGHT_BOTTOM];

		current |=  (   SIGMA_OLD(leftTop) |
						SIGMA_OLD( top) |
						SIGMA_OLD( rightTop) |
						SIGMA_OLD( left) |
						SIGMA_OLD( right) |
						SIGMA_OLD( leftBottom) |
						SIGMA_OLD( bottom) |
						SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

		if ( BIT(current) &&
				NBH(current) &&
					!SIGMA_OLD(current) ) {
			SET_SIGMA_NEW(current);
			blockVote = 1;

		}
		*statePtr = current;

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			 = current;
			leftTop		 = left;
			rightTop	 = right;
			left		 = leftBottom;
			current		 = statePtr[0];
			right		 = rightBottom;
			leftBottom   = statePtr[LEFT_BOTTOM];
			bottom		 = statePtr[BOTTOM];
			rightBottom  = statePtr[RIGHT_BOTTOM];

			current |=	   ( SIGMA_OLD(leftTop) |
							 SIGMA_OLD( top) |
							 SIGMA_OLD( rightTop) |
							 SIGMA_OLD( left) |
							 SIGMA_OLD( right) |
							 SIGMA_OLD( leftBottom) |
							 SIGMA_OLD( bottom) |
							 SIGMA_OLD( rightBottom)  ) << SIGMA_OLD_TO_NBH_SHIFT;

			if ( BIT(current) &&
					 NBH(current) &&
						 !SIGMA_OLD(current) ) {
			    SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			*statePtr = current;

		}
		localMemoryFence();

		// iii) block vote on sigma new
#ifdef AMD
		while (blockVote)
#endif
		{
		    blockVote = 0;
			localMemoryFence();

			statePtr = state + startIndex;

			int top			= statePtr[TOP];
			int leftTop		= statePtr[LEFT_TOP];
			int left		= statePtr[LEFT];
			int current		= statePtr[0];
			int leftBottom  = statePtr[LEFT_BOTTOM];

			// first three pixels in strip column
			for (int i = 0; i < 3; ++i) {

				current  |= ( SIGMA_NEW(leftTop) |
							  SIGMA_NEW( top) |
							  SIGMA_NEW( left) |
							  SIGMA_NEW( leftBottom) ) << NBH_BITPOS;

				if (  BIT(current) &&
				          !SIGMA_OLD(current) &&
						      NBH(current)  &&
							     !SIGMA_NEW(current)) {
					SET_SIGMA_NEW(current);
					blockVote = 1;

				}
				statePtr[0] = current;
				statePtr += STATE_BUFFER_STRIDE;

				top			= current;
				current		= statePtr[0];
				leftTop		= left;
				left		= leftBottom;
				leftBottom  = statePtr[LEFT_BOTTOM];
			}

			// last pixel in strip column -
			// ignore leftBottom pixel, because it is in the next
			// stripe and it hasn't been processed yet
			current |= ( SIGMA_NEW(leftTop) |
						 SIGMA_NEW( top) |
						 SIGMA_NEW( left) ) << NBH_BITPOS;
			if (  BIT(current) &&
			        !SIGMA_OLD(current) &&
					    NBH(current)  &&
						    !SIGMA_NEW(current)) {
				SET_SIGMA_NEW(current);
				blockVote = 1;
			}
			statePtr[0] = current;

			localMemoryFence();

		}

		/////////////////////////
		// 5. bit plane processing
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop			= statePtr[LEFT_TOP];
		rightTop		= statePtr[RIGHT_TOP];
		left			= statePtr[LEFT];
		current			= statePtr[0];
		right			= statePtr[RIGHT];
		leftBottom		= statePtr[LEFT_BOTTOM];
		bottom			= statePtr[BOTTOM];
		rightBottom		= statePtr[RIGHT_BOTTOM];

		if (SIGMA_OLD(current)) {
			// MRC
		} else {
			int nbh = ( NBH(leftTop) |
						NBH( top) |
						NBH( rightTop) |
						NBH( left) |
						NBH( right) |
						NBH( leftBottom) |
						NBH( bottom) |
						NBH( rightBottom)  ) ;

			if (!nbh) {
				if ( !SIGMA_NEW(current) ) {

					//RLC
				}
			}
			else  {

				 // ZC

				 if (BIT(current)) {
				     //SC
				  }
			}
		}

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;

			top			= current;
			leftTop		= left;
			rightTop	= right;
			left		= leftBottom;
			current		= statePtr[0];
			right		= rightBottom;
			leftBottom  = statePtr[LEFT_BOTTOM];
			bottom		= statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];

			if (SIGMA_OLD(current)) {
				// MRC
			} else {
				int nbh = ( NBH(leftTop) |
							NBH( top) |
							NBH( rightTop) |
							NBH( left) |
							NBH( right) |
							NBH( leftBottom) |
							NBH( bottom) |
							NBH( rightBottom)  ) ;

				if (!nbh) {
					if ( !SIGMA_NEW(current) ) {
						//RLC
					}
				}
				else  {
					 // ZC
					 if (BIT(current)) {
						 //SC
					  }
				}
			}
		}

		localMemoryFence();
		bp--;
	}
}

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

/*

Bit Plane Coder
===============

A. Scan Pattern

Xx4 stripes

The stripes are scanned from top to bottom, and the columns within a
stripe are scanned from left to right.

*/

#define CODEBLOCKY_QUARTER 8

#define BOUNDARY 1
#define BOUNDARY_X2 2

#define CODEBLOCKY_X4 128

#define STATE_BUFFER_SIZE 1156
#define STATE_BOTTOM_BOUNDARY_OFFSET 1124

#define STATE_BUFFER_STRIDE 34
#define STATE_BUFFER_STRIDE_X2 68
#define STATE_BUFFER_STRIDE_X3 102
#define STATE_BUFFER_STRIDE_X4 136

#define LEFT_TOP -35
#define TOP  -34
#define RIGHT_TOP -33

#define LEFT   -1
#define RIGHT   1

#define LEFT_BOTTOM  33
#define BOTTOM  34
#define RIGHT_BOTTOM  35

// bit positions (0 based indices)
#define INPUT_SIGN_BITPOS  15

#define SIGMA_NEW_BITPOS   0x1
#define SIGMA_OLD_BITPOS   0x1
#define NBH_BITPOS         0x2
#define SIGMA_OLD_TO_NBH_SHIFT 0x1
#define RLC_BITPOS         0x4
#define PIXEL_START_BITPOS 0xA
#define PIXEL_END_BITPOS   0x18
#define SIGN_BITPOS        0x19

#define SIGMA_NEW			 0x1		  //position 0
#define NOT_SIGMA_NEW        0xFFFFFFFE   // ~SIGMA_NEW
#define SIGMA_OLD			 0x10		  //position  1
#define NBH					 0x20		  //position  2
#define RLC					 0x80		  //position  4
#define RLC_D_POSITION		 0x380        //positions 6-9
#define PIXEL				 0x7FFF0000   //positions 10-24
#define SIGN				 0x2000000    //position  25

#define INPUT_TO_SIGN_SHIFT 10

CONSTANT sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE  | CLK_FILTER_NEAREST;

#define BIT(pix) (((pix)>>(bp))&1)

/**

**/

void KERNEL run(read_only image2d_t channel) {

	// state buffer
	LOCAL int state[STATE_BUFFER_SIZE];
	LOCAL int cxd[STATE_BUFFER_SIZE];

	///////////////////////////////////////////////////////////////////////////////////
	//1. Calculate MSB

	LOCAL int msbScratch[CODEBLOCKX]; // between one and 32 - zero value indicates that this code block is identically zero

	int maxSigBit;
	if (getLocalId(1) == 0) {
		int maxVal = 0;
		state[getLocalId(0)] = 0;   //top boundary
		LOCAL int* statePtr = state + (BOUNDARY + getLocalId(0));
		int2 posIn = (int2)(getGlobalId(0),  (getGlobalId(1) >> 3)*CODEBLOCKY);
		for (int i = 0; i < CODEBLOCKY; ++i) {
			int pixel = read_imagei(channel, sampler, posIn).x;
			pixel = (abs(pixel) << PIXEL_START_BITPOS) | ((pixel << INPUT_TO_SIGN_SHIFT) & SIGN);
			posIn.y++;
			maxVal = max(maxVal, (pixel>> PIXEL_START_BITPOS)&0x7FFF);
			statePtr[0] = pixel;
			statePtr += STATE_BUFFER_STRIDE;
		}
		state[ getLocalId(0) + STATE_BOTTOM_BOUNDARY_OFFSET] = 0;		//bottom boundary

		//initialize full left and right boundary columns
		if (getLocalId(0) == 0 || getLocalId(0) == CODEBLOCKX-1) {
			int delta = -1 + (getLocalId(0)/(CODEBLOCKX-1))*2; // -1 or +1
			statePtr = state + BOUNDARY + getLocalId(0) + delta;
			 for (int i = 0; i < CODEBLOCKY+ BOUNDARY_X2; ++i) {
				 *statePtr = 0;
				 statePtr += STATE_BUFFER_STRIDE;
			 }
		}

		// calculate column-msb
		maxSigBit = 31 - clz(maxVal);
		msbScratch[getLocalId(0)] =maxSigBit;
	}
	localMemoryFence();

	//calculate global msb
	if (!getLocalId(0) && !getLocalId(1) ) {
		int4 mx = (int4)(maxSigBit);

		LOCAL int* scratchPtr = msbScratch;
		for(int i=0; i < CODEBLOCKX; i+=4) {
			mx = max(mx,(int4)(msbScratch[i],msbScratch[i+1],msbScratch[i+2],msbScratch[i+3]));
		}
		maxSigBit = mx.x;
		maxSigBit = max(maxSigBit, mx.y);
		maxSigBit = max(maxSigBit, mx.z);
		maxSigBit = max(maxSigBit, mx.w);
		msbScratch[0] = maxSigBit;
	}
	localMemoryFence();

	if (msbScratch[0] == -1)
		return;

	int startIndex = (BOUNDARY + getLocalId(0)) + (BOUNDARY + getLocalId(1))*STATE_BUFFER_STRIDE_X4;

	/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// 2. CUP on MSB

	//Algorithm 13 Clean-up pass on the MSB
	//1: set sigmanew := bit value
	//2: set rlcNbh :=  SUM(sigmanew) of preceding column and upper position
	//3: if rlcNbh = 0 and y-dimension of current position is a multiple of 4
	//then
	//4: execute RLC operation
	//5: end if
	//6: if rlcNbh = 1 then
	//7: execute ZC operation
	//8: end if
	//9: if sigmanew = 1 then
	//10: execute SC operation
	//11: end if

	int bp = msbScratch[0] + PIXEL_START_BITPOS;

	// i) set sigma_new for strip column (sigma_old is zero)
	LOCAL int* statePtr = state + startIndex;
	for (int i = 0; i < 4; ++i) {
		int current = statePtr[0];
		current |= BIT(current);
		*statePtr = current;
		statePtr += STATE_BUFFER_STRIDE;

	}
	localMemoryFence();

	// ii) set rlc nbh for strip column
	statePtr = state + startIndex;
	int current = statePtr[0];
	int top = statePtr[TOP];
	int leftTop = statePtr[LEFT_TOP];
	int left = statePtr[LEFT];
	int leftBottom = statePtr[LEFT_BOTTOM];

	int rlcCount = 0;

	current |=  (BIT(top) | BIT(leftTop) | BIT(left) | BIT(leftBottom)) << NBH_BITPOS;
	statePtr[0] = current;

	// set doRLC flag
	bool doRLC = !(current & NBH) && !BIT(current);

	if (doRLC) {
		rlcCount++;
	} else {
		if (current & NBH) {
			//ZC
		}
		if (BIT(current)) {
		    //SC
		}
	}

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

		statePtr += STATE_BUFFER_STRIDE;
		top = current;
		current = statePtr[0];
		leftTop = left;
		left = leftBottom;
		leftBottom = statePtr[LEFT_BOTTOM];

		// toggle doRLC flag
		doRLC = doRLC && !(current & NBH) && !BIT(current);
		if (doRLC) {

			rlcCount++;
		} else {
		   if (current & NBH) {
				//ZC
		   }
		   if (BIT(current)) {
		       //SC
		   }
		}

	}
	localMemoryFence();

	/////////////////////////////////////////////////////////////////////////////////////////////////
	// 3. Processs rest of bit plans
	bp--;
	LOCAL char blockVote;

	while (bp >= PIXEL_START_BITPOS) {
		blockVote = 0;

		/////////////////////////////
		// 4. pre-process bit plane

		//Algorithm 15 Bit-plane Preprocessing
		//1: //First step
		//2: set sigma_old |= sigma_new
		//3: set nbh |=  old_sigma from neighbouring positions
		//4: if sigma_old = 0 AND nbh != 0 AND bit value = 1 then
		//5: set sigma_new := 1
		//6: end if
		//7: if there is some sigma_new = 1 then
		//8: blockVote := true
		//9: end if
		//10: //Second step
		//11: while blockVote = true do
		//12: set blockVote := false //reset the voting variable
		//13: set nbh :=  sigma_new from the four previous positions
		//14: if sigma_old = 0 AND nbh != 0 AND bit value = 1 then
		//15: set sigma_new = 1
		//16: end if
		//17: if there is some sigma_new = 1 found in this iteration then
		//18: set blockVote := true
		//19: end if
		//20: end while

		// i) migrate sigma_new to sigma_old, and clear sigma new bit
		statePtr = state + startIndex;
		for (int i = 0; i < 4; ++i) {
		    int current = *statePtr;
			current |= ((current) & SIGMA_NEW) << SIGMA_OLD_BITPOS;
			current &= NOT_SIGMA_NEW;  // clear sigma new
			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;

		}
		localMemoryFence();

		// ii) update nbh
		statePtr = state + startIndex;

		int top = statePtr[TOP];
		int leftTop = statePtr[LEFT_TOP];
		int rightTop = statePtr[RIGHT_TOP];
		int left = statePtr[LEFT];
		int current = statePtr[0];
		int right = statePtr[RIGHT];
		int leftBottom = statePtr[LEFT_BOTTOM];
		int bottom = statePtr[BOTTOM];
		int rightBottom = statePtr[RIGHT_BOTTOM];

		current |= ((leftTop & SIGMA_OLD) |
							( top & SIGMA_OLD) |
							( rightTop &  SIGMA_OLD) |
							( left & SIGMA_OLD) |
							( right & SIGMA_OLD) |
							( leftBottom & SIGMA_OLD) |
							( bottom & SIGMA_OLD) |
							( rightBottom & SIGMA_OLD)  ) << SIGMA_OLD_TO_NBH_SHIFT;

		if ( BIT(current) && (current & NBH) && !(current & SIGMA_OLD) ) {
			current |= SIGMA_NEW;
			blockVote = 1;

		}
		*statePtr = current;

		for (int i = 0; i < 3; ++i) {
			statePtr += STATE_BUFFER_STRIDE;
			top = current;
			leftTop = left;
			rightTop = right;
			left = leftBottom;
			current = statePtr[0];
			right = rightBottom;
			leftBottom = statePtr[LEFT_BOTTOM];
			bottom = statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];

			current |= ((leftTop & SIGMA_OLD) |
							 ( top & SIGMA_OLD) |
							 ( rightTop &  SIGMA_OLD) |
							 ( left & SIGMA_OLD) |
							 ( right & SIGMA_OLD) |
							 ( leftBottom & SIGMA_OLD) |
							 ( bottom & SIGMA_OLD) |
							 ( rightBottom & SIGMA_OLD)  ) << SIGMA_OLD_TO_NBH_SHIFT;

			if ( BIT(current) && (current & NBH) && !(current & SIGMA_OLD) ) {
			    current |= SIGMA_NEW;
				blockVote = 1;
			}
			*statePtr = current;

		}
		localMemoryFence();

		// iii) block vote on sigma new
#ifdef AMD
		while (blockVote)
#endif
		{
		    blockVote = 0;
			localMemoryFence();

			statePtr = state + startIndex;

			int top = statePtr[TOP];
			int leftTop = statePtr[LEFT_TOP];
			int left = statePtr[LEFT];
			int current = statePtr[0];
			int leftBottom = statePtr[LEFT_BOTTOM];

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

				current  |= ((leftTop & SIGMA_NEW) |
							( top & SIGMA_NEW) |
							( left & SIGMA_NEW) |
							( leftBottom & SIGMA_NEW) ) << NBH_BITPOS;

				if (  BIT(current) && !(current & SIGMA_OLD) &&  (current & NBH)  && !(current & SIGMA_NEW)) {
					current |= SIGMA_NEW;
					blockVote = 1;

				}
				statePtr[0] = current;
				statePtr += STATE_BUFFER_STRIDE;
				top = current;
				current = statePtr[0];
				leftTop = left;
				left = leftBottom;
				leftBottom = statePtr[LEFT_BOTTOM];
			}
			current |= ((leftTop & SIGMA_NEW) |
						( top & SIGMA_NEW) |
						( left & SIGMA_NEW) ) << NBH_BITPOS; // ignore leftBottom pixel, because it is in the next
						                                     // stripe and it hasn't been processed yet

			if (  BIT(current) && !(current & SIGMA_OLD) &&  (current & NBH)  && !(current & SIGMA_NEW)) {
				current |= SIGMA_NEW;
				blockVote = 1;

			}
			statePtr[0] = current;
			localMemoryFence();

		}

		/////////////////////////
		// 5. Significance
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop = statePtr[LEFT_TOP];
		rightTop = statePtr[RIGHT_TOP];
		left = statePtr[LEFT];
		current = statePtr[0];
		right = statePtr[RIGHT];
		leftBottom = statePtr[LEFT_BOTTOM];
		bottom = statePtr[BOTTOM];
		rightBottom = statePtr[RIGHT_BOTTOM];
		for (int i = 0; i < 4; ++i) {

			int nbh = ((leftTop & SIGMA_OLD) |
							 ( top & SIGMA_OLD) |
							 ( rightTop &  SIGMA_OLD) |
							 ( left & SIGMA_OLD) |
							 ( right & SIGMA_OLD) |
							 ( leftBottom & SIGMA_OLD) |
							 ( bottom & SIGMA_OLD) |
							 ( rightBottom & SIGMA_OLD)  ) << NBH_BITPOS;

			current |= nbh;
			if ( !(current & NBH) && nbh && BIT(current) ) {
			    current |= SIGMA_NEW;

			}
			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;
			top = current;
			leftTop = left;
			rightTop = right;
			left = leftBottom;
			current = statePtr[0];
			right = rightBottom;
			leftBottom = statePtr[LEFT_BOTTOM];
			bottom = statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];
		}
		localMemoryFence();

		//////////////////////////
		// 6. Magnitude Refinement
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop = statePtr[LEFT_TOP];
		rightTop = statePtr[RIGHT_TOP];
		left = statePtr[LEFT];
		current = statePtr[0];
		right = statePtr[RIGHT];
		leftBottom = statePtr[LEFT_BOTTOM];
		bottom = statePtr[BOTTOM];
		rightBottom = statePtr[RIGHT_BOTTOM];
		for (int i = 0; i < 4; ++i) {

			int nbh = ((leftTop & SIGMA_OLD) |
							 ( top & SIGMA_OLD) |
							 ( rightTop &  SIGMA_OLD) |
							 ( left & SIGMA_OLD) |
							 ( right & SIGMA_OLD) |
							 ( leftBottom & SIGMA_OLD) |
							 ( bottom & SIGMA_OLD) |
							 ( rightBottom & SIGMA_OLD)  ) << NBH_BITPOS;

			current |= nbh;
			if ( !(current & NBH) && nbh && BIT(current) ) {
			    current |= SIGMA_NEW;

			}
			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;
			top = current;
			leftTop = left;
			rightTop = right;
			left = leftBottom;
			current = statePtr[0];
			right = rightBottom;
			leftBottom = statePtr[LEFT_BOTTOM];
			bottom = statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];
		}
		localMemoryFence();

		///////////////////////
		// 7. cleanup
		statePtr = state + startIndex;

		top = statePtr[TOP];
		leftTop = statePtr[LEFT_TOP];
		rightTop = statePtr[RIGHT_TOP];
		left = statePtr[LEFT];
		current = statePtr[0];
		right = statePtr[RIGHT];
		leftBottom = statePtr[LEFT_BOTTOM];
		bottom = statePtr[BOTTOM];
		rightBottom = statePtr[RIGHT_BOTTOM];
		for (int i = 0; i < 4; ++i) {

			int nbh = ((leftTop & SIGMA_OLD) |
							 ( top & SIGMA_OLD) |
							 ( rightTop &  SIGMA_OLD) |
							 ( left & SIGMA_OLD) |
							 ( right & SIGMA_OLD) |
							 ( leftBottom & SIGMA_OLD) |
							 ( bottom & SIGMA_OLD) |
							 ( rightBottom & SIGMA_OLD)  ) << NBH_BITPOS;

			current |= nbh;
			if ( !(current & NBH) && nbh && BIT(current) ) {
			    current |= SIGMA_NEW;

			}
			*statePtr = current;
			statePtr += STATE_BUFFER_STRIDE;
			top = current;
			leftTop = left;
			rightTop = right;
			left = leftBottom;
			current = statePtr[0];
			right = rightBottom;
			leftBottom = statePtr[LEFT_BOTTOM];
			bottom = statePtr[BOTTOM];
			rightBottom = statePtr[RIGHT_BOTTOM];
		}
		localMemoryFence();

		bp--;
	}
}