input shit fixed

input shit fixed

/*
 * 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) + 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 == 0)
											{
												printf("G:c: %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,
													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 + 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) + 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 == 0)
											{
												printf("G:c: %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,
													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* MLO_FILTER_STRIDE1 + f_s;

						if (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* MLO_N_FILTER_SPLITS1 + 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) + 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  %f %f %f %f\n",
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s,
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i, // 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 + 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) + 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  %f %f %f %f\n",
													(out_y + ex_row)*MLO_FILTER_STRIDE1 + m*MLO_FILTER_STRIDE1 + f_s,
													(ex_pix + n)*MLO_FILTER_STRIDE0 + l*MLO_FILTER_STRIDE0 + i, // 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
}


Cleanup, indentation

Cleanup, indentation

/*
 * Copyright 2011-2017 Blender Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* OpenCL kernel entry points */

#include "kernel/kernel_compat_opencl.h"

#include "kernel/filter/filter_kernel.h"

/* kernels */

__kernel void kernel_ocl_filter_divide_shadow(int sample,
                                              ccl_global TilesInfo *tiles,
                                              ccl_global float *unfilteredA,
                                              ccl_global float *unfilteredB,
                                              ccl_global float *sampleVariance,
                                              ccl_global float *sampleVarianceV,
                                              ccl_global float *bufferVariance,
                                              int4 prefilter_rect,
                                              int buffer_pass_stride,
                                              int buffer_denoising_offset,
                                              char use_split_variance)
{
	int x = prefilter_rect.x + get_global_id(0);
	int y = prefilter_rect.y + get_global_id(1);
	if(x < prefilter_rect.z && y < prefilter_rect.w) {
		kernel_filter_divide_shadow(sample,
		                            tiles,
		                            x, y,
		                            unfilteredA,
		                            unfilteredB,
		                            sampleVariance,
		                            sampleVarianceV,
		                            bufferVariance,
		                            prefilter_rect,
		                            buffer_pass_stride,
		                            buffer_denoising_offset,
		                            use_split_variance);
	}
}

__kernel void kernel_ocl_filter_get_feature(int sample,
                                            ccl_global TilesInfo *tiles,
                                            int m_offset,
                                            int v_offset,
                                            ccl_global float *mean,
                                            ccl_global float *variance,
                                            int4 prefilter_rect,
                                            int buffer_pass_stride,
                                            int buffer_denoising_offset,
                                            char use_split_variance)
{
	int x = prefilter_rect.x + get_global_id(0);
	int y = prefilter_rect.y + get_global_id(1);
	if(x < prefilter_rect.z && y < prefilter_rect.w) {
		kernel_filter_get_feature(sample,
		                          tiles,
		                          m_offset, v_offset,
		                          x, y,
		                          mean, variance,
		                          prefilter_rect,
		                          buffer_pass_stride,
		                          buffer_denoising_offset,
		                          use_split_variance);
	}
}

__kernel void kernel_ocl_filter_detect_outliers(ccl_global float *image,
                                                ccl_global float *variance,
                                                ccl_global float *depth,
                                                ccl_global float *output,
                                                int4 prefilter_rect,
                                                int pass_stride)
{
	int x = prefilter_rect.x + get_global_id(0);
	int y = prefilter_rect.y + get_global_id(1);
	if(x < prefilter_rect.z && y < prefilter_rect.w) {
		kernel_filter_detect_outliers(x, y, image, variance, depth, output, prefilter_rect, pass_stride);
	}
}

__kernel void kernel_ocl_filter_combine_halves(ccl_global float *mean,
                                               ccl_global float *variance,
                                               ccl_global float *a,
                                               ccl_global float *b,
                                               int4 prefilter_rect,
                                               int r)
{
	int x = prefilter_rect.x + get_global_id(0);
	int y = prefilter_rect.y + get_global_id(1);
	if(x < prefilter_rect.z && y < prefilter_rect.w) {
		kernel_filter_combine_halves(x, y, mean, variance, a, b, prefilter_rect, r);
	}
}

__kernel void kernel_ocl_filter_construct_transform(const ccl_global float *ccl_restrict buffer,
                                                    ccl_global float *transform,
                                                    ccl_global int *rank,
                                                    int4 filter_area,
                                                    int4 rect,
                                                    int pass_stride,
                                                    int radius,
                                                    float pca_threshold)
{
	int x = get_global_id(0);
	int y = get_global_id(1);
	if(x < filter_area.z && y < filter_area.w) {
		ccl_global int *l_rank = rank + y*filter_area.z + x;
		ccl_global float *l_transform = transform + y*filter_area.z + x;
		kernel_filter_construct_transform(buffer,
		                                  x + filter_area.x, y + filter_area.y,
		                                  rect, pass_stride,
		                                  l_transform, l_rank,
		                                  radius, pca_threshold,
		                                  filter_area.z*filter_area.w,
		                                  get_local_id(1)*get_local_size(0) + get_local_id(0));
	}
}

__kernel void kernel_ocl_filter_nlm_calc_difference(int dx,
                                                    int dy,
                                                    const ccl_global float *ccl_restrict weight_image,
                                                    const ccl_global float *ccl_restrict variance_image,
                                                    ccl_global float *difference_image,
                                                    int4 rect,
                                                    int w,
                                                    int channel_offset,
                                                    float a,
                                                    float k_2)
{
	int x = get_global_id(0) + rect.x;
	int y = get_global_id(1) + rect.y;
	if(x < rect.z && y < rect.w) {
		kernel_filter_nlm_calc_difference(x, y, dx, dy, weight_image, variance_image, difference_image, rect, w, channel_offset, a, k_2);
	}
}

__kernel void kernel_ocl_filter_nlm_blur(const ccl_global float *ccl_restrict difference_image,
                                         ccl_global float *out_image,
                                         int4 rect,
                                         int w,
                                         int f)
{
	int x = get_global_id(0) + rect.x;
	int y = get_global_id(1) + rect.y;
	if(x < rect.z && y < rect.w) {
		kernel_filter_nlm_blur(x, y, difference_image, out_image, rect, w, f);
	}
}

__kernel void kernel_ocl_filter_nlm_calc_weight(const ccl_global float *ccl_restrict difference_image,
                                                ccl_global float *out_image,
                                                int4 rect,
                                                int w,
                                                int f)
{
	int x = get_global_id(0) + rect.x;
	int y = get_global_id(1) + rect.y;
	if(x < rect.z && y < rect.w) {
		kernel_filter_nlm_calc_weight(x, y, difference_image, out_image, rect, w, f);
	}
}

__kernel void kernel_ocl_filter_nlm_update_output(int dx,
                                                  int dy,
                                                  const ccl_global float *ccl_restrict difference_image,
                                                  const ccl_global float *ccl_restrict image,
                                                  ccl_global float *out_image,
                                                  ccl_global float *accum_image,
                                                  int4 rect,
                                                  int w,
                                                  int f)
{
	int x = get_global_id(0) + rect.x;
	int y = get_global_id(1) + rect.y;
	if(x < rect.z && y < rect.w) {
		kernel_filter_nlm_update_output(x, y, dx, dy, difference_image, image, out_image, accum_image, rect, w, f);
	}
}

__kernel void kernel_ocl_filter_nlm_normalize(ccl_global float *out_image,
                                              const ccl_global float *ccl_restrict accum_image,
                                              int4 rect,
                                              int w)
{
	int x = get_global_id(0) + rect.x;
	int y = get_global_id(1) + rect.y;
	if(x < rect.z && y < rect.w) {
		kernel_filter_nlm_normalize(x, y, out_image, accum_image, rect, w);
	}
}

__kernel void kernel_ocl_filter_nlm_construct_gramian(int dx,
                                                      int dy,
                                                      const ccl_global float *ccl_restrict difference_image,
                                                      const ccl_global float *ccl_restrict buffer,
                                                      const ccl_global float *ccl_restrict transform,
                                                      ccl_global int *rank,
                                                      ccl_global float *XtWX,
                                                      ccl_global float3 *XtWY,
                                                      int4 rect,
                                                      int4 filter_rect,
                                                      int w,
                                                      int h,
                                                      int f,
                                                      int pass_stride)
{
	int x = get_global_id(0) + max(0, rect.x-filter_rect.x);
	int y = get_global_id(1) + max(0, rect.y-filter_rect.y);
	if(x < min(filter_rect.z, rect.z-filter_rect.x) && y < min(filter_rect.w, rect.w-filter_rect.y)) {
		kernel_filter_nlm_construct_gramian(x, y,
		                                    dx, dy,
		                                    difference_image,
		                                    buffer,
		                                    transform, rank,
		                                    XtWX, XtWY,
		                                    rect, filter_rect,
		                                    w, h, f,
		                                    pass_stride,
		                                    get_local_id(1)*get_local_size(0) + get_local_id(0));
	}
}

__kernel void kernel_ocl_filter_finalize(int w,
                                         int h,
                                         ccl_global float *buffer,
                                         ccl_global int *rank,
                                         ccl_global float *XtWX,
                                         ccl_global float3 *XtWY,
                                         int4 filter_area,
                                         int4 buffer_params,
                                         int sample)
{
	int x = get_global_id(0);
	int y = get_global_id(1);
	if(x < filter_area.z && y < filter_area.w) {
		int storage_ofs = y*filter_area.z+x;
		rank += storage_ofs;
		XtWX += storage_ofs;
		XtWY += storage_ofs;
		kernel_filter_finalize(x, y, w, h, buffer, rank, filter_area.z*filter_area.w, XtWX, XtWY, buffer_params, sample);
	}
}

__kernel void kernel_ocl_filter_set_tiles(ccl_global TilesInfo* tiles,
                                          ccl_global float *buffer_1,
                                          ccl_global float *buffer_2,
                                          ccl_global float *buffer_3,
                                          ccl_global float *buffer_4,
                                          ccl_global float *buffer_5,
                                          ccl_global float *buffer_6,
                                          ccl_global float *buffer_7,
                                          ccl_global float *buffer_8,
                                          ccl_global float *buffer_9)
{
	if((get_global_id(0) == 0) && (get_global_id(1) == 0)) {
		tiles->buffers[0] = buffer_1;
		tiles->buffers[1] = buffer_2;
		tiles->buffers[2] = buffer_3;
		tiles->buffers[3] = buffer_4;
		tiles->buffers[4] = buffer_5;
		tiles->buffers[5] = buffer_6;
		tiles->buffers[6] = buffer_7;
		tiles->buffers[7] = buffer_8;
		tiles->buffers[8] = buffer_9;
	}
}


Add another case of allowing USE_VECTOR to influence dequant kernels.

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable

/*
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

__constant int cospi8sqrt2minus1 = 20091;
__constant int sinpi8sqrt2      = 35468;
__constant int rounding = 0;

void vp8_short_idct4x4llm(__global short*, short*, int);
void cl_memset_short(__global short*, int, size_t);

#define USE_VECTORS 1

__kernel void vp8_dequantize_b_kernel(
    __global short *dqcoeff_base,
    int dqcoeff_offset,
    __global short *qcoeff_base,
    int qcoeff_offset,
    __global short *dequant
)
{
    __global short *DQ  = dqcoeff_base + dqcoeff_offset;
    __global short *Q   = qcoeff_base  + qcoeff_offset;
    __global short *DQC = dequant;
#if USE_VECTORS
    short16 dqv = vload16(0,Q) * vload16(0,DQC);
    vstore16(vload16(0,Q) * vload16(0,DQC), 0, DQ);
#else
    int i;
    for (i = 0; i < 16; i++)
    {
        DQ[i] = Q[i] * DQC[i];
    }

#endif
}

__kernel void vp8_dequant_idct_add_kernel(
    __global short *input_base,
    int input_offset,
    __global short *dq,
    __global unsigned char *pred_base,
    int pred_offset,
    __global unsigned char *dest_base,
    int dest_offset,
    int pitch,
    int stride
)
{
    short output[16];
    short *diff_ptr = output;
    int r, c;
    int i;
    __global unsigned char *dest = dest_base + dest_offset;
    __global short *input = input_base + input_offset;
    __global unsigned char *pred = pred_base + pred_offset;

#if USE_VECTORS
    vstore16( (short16)vload16(0,dq) * (short16)vload16(0,input) , 0, input);
#else
    for (i = 0; i < 16; i++)
    {
        input[i] = dq[i] * input[i];
    }
#endif

    /* the idct halves ( >> 1) the pitch */
    vp8_short_idct4x4llm(input, output, 4 << 1);

    cl_memset_short(input, 0, 32);

    for (r = 0; r < 4; r++)
    {
        for (c = 0; c < 4; c++)
        {
            int a = diff_ptr[c] + pred[c];

            if (a < 0)
                a = 0;

            if (a > 255)
                a = 255;

            dest[c] = (unsigned char) a;
        }

        dest += stride;
        diff_ptr += 4;
        pred += pitch;
    }
}

__kernel void vp8_dequant_dc_idct_add_kernel(
    __global short *input,
    __global short *dq,
    __global unsigned char *pred,
    __global unsigned char *dest,
    int pitch,
    int stride,
    int Dc
)
{
    int i;
    short output[16];
    short *diff_ptr = output;
    int r, c;

    input[0] = (short)Dc;

#if USE_VECTORS
    vstore16( (short16)vload16(0,dq) * (short16)vload16(0,input) , 0, input);
#else
    for (i = 1; i < 16; i++)
    {
        input[i] = dq[i] * input[i];
    }
#endif

    /* the idct halves ( >> 1) the pitch */
    vp8_short_idct4x4llm(input, output, 4 << 1);

    cl_memset_short(input, 0, 32);

    for (r = 0; r < 4; r++)
    {
        for (c = 0; c < 4; c++)
        {
            int a = diff_ptr[c] + pred[c];

            if (a < 0)
                a = 0;

            if (a > 255)
                a = 255;

            dest[c] = (unsigned char) a;
        }

        dest += stride;
        diff_ptr += 4;
        pred += pitch;
    }
}

//Note that this kernel has been copied from common/opencl/idctllm_cl.cl
void vp8_short_idct4x4llm(
    __global short *input,
    short *output,
    int pitch
)
{
    int i;
    int a1, b1, c1, d1;

    __global short *ip = input;
    short *op = output;
    int temp1, temp2;
    int shortpitch = pitch >> 1;

    for (i = 0; i < 4; i++)
    {
        a1 = ip[0] + ip[8];
        b1 = ip[0] - ip[8];

        temp1 = (ip[4] * sinpi8sqrt2 + rounding) >> 16;
        temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1 + rounding) >> 16);
        c1 = temp1 - temp2;

        temp1 = ip[4] + ((ip[4] * cospi8sqrt2minus1 + rounding) >> 16);
        temp2 = (ip[12] * sinpi8sqrt2 + rounding) >> 16;
        d1 = temp1 + temp2;

        op[shortpitch*0] = a1 + d1;
        op[shortpitch*3] = a1 - d1;

        op[shortpitch*1] = b1 + c1;
        op[shortpitch*2] = b1 - c1;

        ip++;
        op++;
    }

    op = output;

    for (i = 0; i < 4; i++)
    {
        a1 = op[0] + op[2];
        b1 = op[0] - op[2];

        temp1 = (op[1] * sinpi8sqrt2 + rounding) >> 16;
        temp2 = op[3] + ((op[3] * cospi8sqrt2minus1 + rounding) >> 16);
        c1 = temp1 - temp2;

        temp1 = op[1] + ((op[1] * cospi8sqrt2minus1 + rounding) >> 16);
        temp2 = (op[3] * sinpi8sqrt2 + rounding) >> 16;
        d1 = temp1 + temp2;

        op[0] = (a1 + d1 + 4) >> 3;
        op[3] = (a1 - d1 + 4) >> 3;

        op[1] = (b1 + c1 + 4) >> 3;
        op[2] = (b1 - c1 + 4) >> 3;

        op += shortpitch;
    }

}

void cl_memset_short(__global short *s, int c, size_t n) {
    for (n /= sizeof(short); n > 0; --n)
        *s++ = c;
}

#pragma OPENCL EXTENSION cl_khr_byte_addressable_store : enable

/*
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

__constant int cospi8sqrt2minus1 = 20091;
__constant int sinpi8sqrt2      = 35468;
__constant int rounding = 0;

void vp8_short_idct4x4llm(__global short*, short*, int);
void cl_memset_short(__global short*, int, size_t);

#define USE_VECTORS 1

__kernel void vp8_dequantize_b_kernel(
    __global short *dqcoeff_base,
    int dqcoeff_offset,
    __global short *qcoeff_base,
    int qcoeff_offset,
    __global short *dequant
)
{
    __global short *DQ  = dqcoeff_base + dqcoeff_offset;
    __global short *Q   = qcoeff_base  + qcoeff_offset;
    __global short *DQC = dequant;
#if USE_VECTORS
    short16 dqv = vload16(0,Q) * vload16(0,DQC);
    vstore16(vload16(0,Q) * vload16(0,DQC), 0, DQ);
#else
    int i;
    for (i = 0; i < 16; i++)
    {
        DQ[i] = Q[i] * DQC[i];
    }

#endif
}

__kernel void vp8_dequant_idct_add_kernel(
    __global short *input_base,
    int input_offset,
    __global short *dq,
    __global unsigned char *pred_base,
    int pred_offset,
    __global unsigned char *dest_base,
    int dest_offset,
    int pitch,
    int stride
)
{
    short output[16];
    short *diff_ptr = output;
    int r, c;
    int i;
    __global unsigned char *dest = dest_base + dest_offset;
    __global short *input = input_base + input_offset;
    __global unsigned char *pred = pred_base + pred_offset;

#if USE_VECTORS
    vstore16( (short16)vload16(0,dq) * (short16)vload16(0,input) , 0, input);
#else
    for (i = 0; i < 16; i++)
    {
        input[i] = dq[i] * input[i];
    }
#endif

    /* the idct halves ( >> 1) the pitch */
    vp8_short_idct4x4llm(input, output, 4 << 1);

    cl_memset_short(input, 0, 32);

    for (r = 0; r < 4; r++)
    {
        for (c = 0; c < 4; c++)
        {
            int a = diff_ptr[c] + pred[c];

            if (a < 0)
                a = 0;

            if (a > 255)
                a = 255;

            dest[c] = (unsigned char) a;
        }

        dest += stride;
        diff_ptr += 4;
        pred += pitch;
    }
}

__kernel void vp8_dequant_dc_idct_add_kernel(
    __global short *input,
    __global short *dq,
    __global unsigned char *pred,
    __global unsigned char *dest,
    int pitch,
    int stride,
    int Dc
)
{
    int i;
    short output[16];
    short *diff_ptr = output;
    int r, c;

    input[0] = (short)Dc;

    //vstore16( (short16)vload16(0,dq) * (short16)vload16(0,input) , 0, input);
    for (i = 1; i < 16; i++)
    {
        input[i] = dq[i] * input[i];
    }

    /* the idct halves ( >> 1) the pitch */
    vp8_short_idct4x4llm(input, output, 4 << 1);

    cl_memset_short(input, 0, 32);

    for (r = 0; r < 4; r++)
    {
        for (c = 0; c < 4; c++)
        {
            int a = diff_ptr[c] + pred[c];

            if (a < 0)
                a = 0;

            if (a > 255)
                a = 255;

            dest[c] = (unsigned char) a;
        }

        dest += stride;
        diff_ptr += 4;
        pred += pitch;
    }
}

//Note that this kernel has been copied from common/opencl/idctllm_cl.cl
void vp8_short_idct4x4llm(
    __global short *input,
    short *output,
    int pitch
)
{
    int i;
    int a1, b1, c1, d1;

    __global short *ip = input;
    short *op = output;
    int temp1, temp2;
    int shortpitch = pitch >> 1;

    for (i = 0; i < 4; i++)
    {
        a1 = ip[0] + ip[8];
        b1 = ip[0] - ip[8];

        temp1 = (ip[4] * sinpi8sqrt2 + rounding) >> 16;
        temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1 + rounding) >> 16);
        c1 = temp1 - temp2;

        temp1 = ip[4] + ((ip[4] * cospi8sqrt2minus1 + rounding) >> 16);
        temp2 = (ip[12] * sinpi8sqrt2 + rounding) >> 16;
        d1 = temp1 + temp2;

        op[shortpitch*0] = a1 + d1;
        op[shortpitch*3] = a1 - d1;

        op[shortpitch*1] = b1 + c1;
        op[shortpitch*2] = b1 - c1;

        ip++;
        op++;
    }

    op = output;

    for (i = 0; i < 4; i++)
    {
        a1 = op[0] + op[2];
        b1 = op[0] - op[2];

        temp1 = (op[1] * sinpi8sqrt2 + rounding) >> 16;
        temp2 = op[3] + ((op[3] * cospi8sqrt2minus1 + rounding) >> 16);
        c1 = temp1 - temp2;

        temp1 = op[1] + ((op[1] * cospi8sqrt2minus1 + rounding) >> 16);
        temp2 = (op[3] * sinpi8sqrt2 + rounding) >> 16;
        d1 = temp1 + temp2;

        op[0] = (a1 + d1 + 4) >> 3;
        op[3] = (a1 - d1 + 4) >> 3;

        op[1] = (b1 + c1 + 4) >> 3;
        op[2] = (b1 - c1 + 4) >> 3;

        op += shortpitch;
    }

}

void cl_memset_short(__global short *s, int c, size_t n) {
    for (n /= sizeof(short); n > 0; --n)
        *s++ = c;
}


BUg in OCL code: duplicate fill_character

#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable

#define NUMBER_SEQUENCES ${N_SEQUENCES}
#define NUMBER_TARGETS ${N_TARGETS}
#define X ${X}
#define Y ${Y}

/** start of the alphabet, so scoringsmatrix index can be calculated */
#define characterOffset '${CHAR_OFFSET}'
/** character used to fill the sequence if length < X */
#define FILL_CHARACTER 'x'


minor notes

minor notes

(defpackage :com.orbbec.sdk
  (:use :common-lisp))

(defstruct param type name)
(defstruct code funcname params returntype)

(defun partial (func &rest args1)
   (lambda (&rest args2) (apply func (append args1 args2)))
)

(defun newline ()
	(write-string (format nil "~%"))
)

(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
            while pos)))

(defun formatmethod2 (lineformat funcdata)
	(format nil lineformat
		(code-returntype funcdata)
		(code-funcname funcdata)
		(code-params funcdata)
	)
)

(defun format-paramitem-type (p)
	(format nil "~A" (param-type p))
)

(defun format-paramlist-type (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-type params))
)

(defun format-paramitem-name (p)
	(format nil "~A" (param-name p))
)

(defun format-paramlist-name (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-name params))
)

(defun format-paramitem-full (p)
	(format nil "~A ~A" (param-type p) (param-name p))
)

(defun format-paramlist-full (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-full params))
)

(defun formatmethod (lineformat funcdata)
	(replace-all
		(replace-all
			(replace-all
				(replace-all
					(replace-all lineformat
						"^RETURN^" (code-returntype funcdata)
					)
					"^FUNC^" (code-funcname funcdata)
				)
				"^PARAMS^" (format-paramlist-full (code-params funcdata))
			)
			"^PARAM-NAMES^" (format-paramlist-name (code-params funcdata))
		)
		"^PARAM-TYPES^" (format-paramlist-type (code-params funcdata))
	)
)

(defun formatmethods (lineformat funclist)
	(map 'list
		(partial #'formatmethod lineformat) funclist
	)
)

(setq c1 (make-code :returntype "sk_status"
					:funcname "open_frame"
					:params (list (make-param :type "sk_stream*" :name "stream")
								  (make-param :type "sk_frame**" :name "frame")
							)
		)
)

(setq c2 (make-code :returntype "sk_status"
					:funcname "close_frame"
					:params (list (make-param :type "sk_frame**" :name "frame" ))
		)
)

(setq f1 "^RETURN^ ^FUNC^(^PARAMS^);
")
(setq f2 "^RETURN^ sensekit_^FUNC^(^PARAMS^) {
	return g_Context->^FUNC^(^PARAM-NAMES^);
}
")
(setq f3 "^RETURN^ (*^FUNC^)(void*, ^PARAM-TYPES^);
")

(setq funcs (list c1 c2))

(defun outputfuncs (f)
	(map nil #'write-string f)
)

(defun _ () (load "lpp.cl" :verbose nil))

(defun t ()
	;(write-string (formatmethod f1 c1))
	;(write-string (formatmethod f2 c1))
	(outputfuncs (formatmethods f1 funcs))
	(newline)
	(outputfuncs (formatmethods f2 funcs))
	(newline)
	(outputfuncs (formatmethods f3 funcs))
	(newline)
	nil
)
(setq beginmarker "^^^BEGINREPLACE^^^")
(setq endmarker "^^^ENDREPLACE^^^")

;(t)
(let ((infile (open "test.cpp.lpp" :if-does-not-exist nil))
	  (parts '(nil nil))
	  (currentpart nil)
	  (doneparts ())
	  )
  (when infile
    (loop
		for line = (read-line infile nil)
        while line
		;TODO check for beginmarker and endmarker
		do (setq currentpart (cons line currentpart))
	)
	(loop for line in (reverse currentpart)
		  do (format t "~a~%" line)
	)
    (close infile))
)

(defpackage :com.orbbec.sdk
  (:use :common-lisp))

(defstruct param type name)
(defstruct code funcname params returntype)

(defun partial (func &rest args1)
   (lambda (&rest args2) (apply func (append args1 args2)))
)

(defun newline ()
	(write-string (format nil "~%"))
)

(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
            while pos)))

(defun formatmethod2 (lineformat funcdata)
	(format nil lineformat
		(code-returntype funcdata)
		(code-funcname funcdata)
		(code-params funcdata)
	)
)

(defun format-paramitem-type (p)
	(format nil "~A" (param-type p))
)

(defun format-paramlist-type (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-type params))
)

(defun format-paramitem-name (p)
	(format nil "~A" (param-name p))
)

(defun format-paramlist-name (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-name params))
)

(defun format-paramitem-full (p)
	(format nil "~A ~A" (param-type p) (param-name p))
)

(defun format-paramlist-full (params)
	(format nil "~{~A~^, ~}" (mapcar #'format-paramitem-full params))
)

(defun formatmethod (lineformat funcdata)
	(replace-all
		(replace-all
			(replace-all
				(replace-all
					(replace-all lineformat
						"^RETURN^" (code-returntype funcdata)
					)
					"^FUNC^" (code-funcname funcdata)
				)
				"^PARAMS^" (format-paramlist-full (code-params funcdata))
			)
			"^PARAM-NAMES^" (format-paramlist-name (code-params funcdata))
		)
		"^PARAM-TYPES^" (format-paramlist-type (code-params funcdata))
	)
)

(defun formatmethods (lineformat funclist)
	(map 'list
		(partial #'formatmethod lineformat) funclist
	)
)

(setq c1 (make-code :returntype "sk_status"
					:funcname "open_frame"
					:params (list (make-param :type "sk_stream*" :name "stream")
								  (make-param :type "sk_frame**" :name "frame")
							)
		)
)

(setq c2 (make-code :returntype "sk_status"
					:funcname "close_frame"
					:params (list (make-param :type "sk_frame**" :name "frame" ))
		)
)

(setq f1 "^RETURN^ ^FUNC^(^PARAMS^);
")
(setq f2 "^RETURN^ sensekit_^FUNC^(^PARAMS^) {
	return g_Context->^FUNC^(^PARAM-NAMES^);
}
")
(setq f3 "^RETURN^ (*^FUNC^)(void*, ^PARAM-TYPES^);
")

(setq funcs (list c1 c2))

(defun outputfuncs (f)
	(map nil #'write-string f)
)

(defun _ () (load "lpp.cl" :verbose nil))

(defun t ()
	;(write-string (formatmethod f1 c1))
	;(write-string (formatmethod f2 c1))
	(outputfuncs (formatmethods f1 funcs))
	(newline)
	(outputfuncs (formatmethods f2 funcs))
	(newline)
	(outputfuncs (formatmethods f3 funcs))
	(newline)
	nil
)

;(t)
(let ((infile (open "test.cpp.lpp" :if-does-not-exist nil))
	  (parts '(nil nil))
	  (currentpart nil)
	  (doneparts ())
	  )
  (when infile
    (loop
		for line = (read-line infile nil)
        while line
		do (setq currentpart (cons line currentpart))
	)
	(loop for line in (reverse currentpart)
		  do (format t "~a~%" line)
	)
    (close infile))
)


Implement stratified multisampling using a tent filter

// Global parameters that will be set via a template during compilation time.
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define MAX_TRACE_STEPS 128
#define NO_SURFACE_HIT -1

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC3_ORIGIN VEC3(0,0,0)

// Supported surface types
#define SURFACE_DIFFUSE 0
#define SURFACE_SPECULAR 1
#define SURFACE_REFRACTIVE 2
#define SURFACE_LIGHT 65535

// DEBUGGING
#define DEBUG (get_global_id(0)==256 && get_global_id(1)==256)

// Distance estimator function generators
// --------------------------------------
#define PLANE_SD_FN(objIndex, origin, planeDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = normalize(planeDims); \
	return dot(point, dims); \
}
#define SPHERE_SD_FN(objIndex, origin, radius) float distToObj##objIndex( float4 point ){ \
	return length(point - origin) - radius; \
}
#define BOX_SD_FN(objIndex, origin, boxDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = boxDims; \
	float4 d = fabs(point) - (float4)(dims.xyz, point.w); \
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f)); \
}
#define TORUS_SD_FN(objIndex, origin, torusDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float2 dims = torusDims; \
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y; \
}

// Normal estimator generators
#define OBJECT_NORMAL_AT(objIndex, point) \
	normalize( \
		(float4)( \
			distToObj##objIndex(point + VEC3(NUDGE_EPSILON, 0,0)) - distToObj##objIndex(point - VEC3(NUDGE_EPSILON, 0,0)), \
			distToObj##objIndex(point + VEC3(0, NUDGE_EPSILON,0)) - distToObj##objIndex(point - VEC3(0, NUDGE_EPSILON,0)), \
			distToObj##objIndex(point + VEC3(0, 0, NUDGE_EPSILON)) - distToObj##objIndex(point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Random number generator
// -----------------------
float2 clRand(uint2 *state)
{
    const float2 invMaxInt = (float2) (1.0f/4294967296.0f, 1.0f/4294967296.0f);
    uint x = (*state).x * 17 + (*state).y * 13123;
    (*state).x = (x<<13) ^ x;
    (*state).y ^= (x<<7);

    uint2 tmp = (uint2)
    (
		(x * (x * x * 15731 + 74323) + 871483),
		(x * (x * x * 13734 + 37828) + 234234)
	);

    return convert_float2(tmp) * invMaxInt;
}

// Tracer implementation
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0);

__constant Material SceneMaterial[] = {
	{SURFACE_LIGHT, VEC3(0,0,0), VEC3(60,60,60)},
	{SURFACE_DIFFUSE, VEC3(0.75, 0.25, 0.25)},
	{SURFACE_DIFFUSE, VEC3(0.25, 0.25, 0.75)},
	{SURFACE_DIFFUSE, VEC3(0.75, 0.75, 0.75)},
	{SURFACE_SPECULAR, VEC3(0.999, 0.999, 0.999)},
	{SURFACE_REFRACTIVE, VEC3(0.999, 0.999, 0.999)}
};

// Object definitions
// The following code will be autogenerated by the scene processor
// ------------------

// Light
SPHERE_SD_FN(0, VEC3(0, 1.5, 0), 0.1f)
// Left wall
BOX_SD_FN(1, VEC3(-2,0,0), VEC3(0.1, 2, 2))
// Right wall
BOX_SD_FN(2, VEC3(2,0,0), VEC3(0.1, 2, 2))
// Top wall
BOX_SD_FN(3, VEC3(0,2,0), VEC3(2, 0.1, 2))
// Bottom wall
BOX_SD_FN(4, VEC3(0,-2,0), VEC3(2, 0.1, 2))
// Back wall
BOX_SD_FN(5, VEC3(0,0,-2), VEC3(2, 2, 0.1))
// Mirror ball
SPHERE_SD_FN(6, VEC3(-1.0, -1.4, -1.0), 0.5)
// Glass ball
SPHERE_SD_FN(7, VEC3(1.4, -1.4, -0.5), 0.5)

// Estimate normal at intersection point
void getNormal(Hit *hit){
	// The following code will be autogenerated by the scene processor
	if( hit->objIndex == 0 ){
		hit->normal = OBJECT_NORMAL_AT(0, hit->position);
	} else if( hit->objIndex == 1 ){
		hit->normal = OBJECT_NORMAL_AT(1, hit->position);
	} else if( hit->objIndex == 2 ){
		hit->normal = OBJECT_NORMAL_AT(2, hit->position);
	} else if( hit->objIndex == 3 ){
		hit->normal = OBJECT_NORMAL_AT(3, hit->position);
	} else if( hit->objIndex == 4 ){
		hit->normal = OBJECT_NORMAL_AT(4, hit->position);
	} else if( hit->objIndex == 5 ){
		hit->normal = OBJECT_NORMAL_AT(5, hit->position);
	} else if( hit->objIndex == 6 ){
		hit->normal = OBJECT_NORMAL_AT(6, hit->position);
	} else if( hit->objIndex == 7 ){
		hit->normal = OBJECT_NORMAL_AT(7, hit->position);
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_SURFACE_HIT;
	hit->matIndex = NO_SURFACE_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		// The following code will be autogenerated by the scene processor
		nearestDist = FLT_MAX;
		objDist = distToObj0(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 0;
			nearestMat = 0;
		}
		objDist = distToObj1(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 1;
			nearestMat = 1;
		}
		objDist = distToObj2(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 2;
			nearestMat = 2;
		}
		objDist = distToObj3(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 3;
			nearestMat = 3;
		}
		objDist = distToObj4(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 4;
			nearestMat = 3;
		}
		objDist = distToObj5(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 5;
			nearestMat = 3;
		}
		objDist = distToObj6(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 6;
			nearestMat = 4;
		}
		objDist = distToObj7(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 7;
			nearestMat = 5;
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir){
	Hit hit;
	float4 rCol;
	unsigned int bounce = 0;
	while( true ){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_SURFACE_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// For now just output the diffuse (or emissive for lights)
		rCol = max(SceneMaterial[hit.matIndex].diffuse, SceneMaterial[hit.matIndex].emissive);
		break;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Setup seed for random numbers
	uint2 rndSeed = (uint2)(x, y);

	// Calculate texel coordinates [0,1] range
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0,0,0,0);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Apply stratified sampling using a tent filter. This will wrap our
		// random numbers in the [-1, 1] range. X and Y point to the top corner
		// of the current texel so we need to add a bit of offset to get the coords
		// into the [-0.5, 1.5] range.
		float2 random = clRand(&rndSeed);
		float offX = random.x < 0.5f ? sqrt(2.0f * random.x) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.x);
		float offY = random.y < 0.5f ? sqrt(2.0f * random.y) - 0.5f : 1.5f - sqrt(2.0f - 2.0f * random.y);
		float tx = ((float)x + offX) * TEXEL_STEP_X;
		float ty = ((float)y + offY) * TEXEL_STEP_Y;

		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx));
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}

// Global parameters that will be set via a template during compilation time.
#define FRAME_W 512
#define FRAME_H 512
#define TEXEL_STEP_X 1.0f / 512.0f
#define TEXEL_STEP_Y 1.0f / 512.0f
#define MAX_TRACE_STEPS 128
#define NO_SURFACE_HIT -1

// Epsilon values
#define NUDGE_EPSILON 1.0e-2f
#define DIST_SNAP_EPSILON 1.0e-3f

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC3_ORIGIN VEC3(0,0,0)

// Supported surface types
#define SURFACE_DIFFUSE 0
#define SURFACE_SPECULAR 1
#define SURFACE_REFRACTIVE 2
#define SURFACE_LIGHT 65535

// DEBUGGING
#define DEBUG (get_global_id(0)==256 && get_global_id(1)==256)

// Distance estimator function generators
// --------------------------------------
#define PLANE_SD_FN(objIndex, origin, planeDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = normalize(planeDims); \
	return dot(point, dims); \
}
#define SPHERE_SD_FN(objIndex, origin, radius) float distToObj##objIndex( float4 point ){ \
	return length(point - origin) - radius; \
}
#define BOX_SD_FN(objIndex, origin, boxDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float4 dims = boxDims; \
	float4 d = fabs(point) - (float4)(dims.xyz, point.w); \
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f)); \
}
#define TORUS_SD_FN(objIndex, origin, torusDims) float distToObj##objIndex( float4 point ){ \
	point -= origin; \
	float2 dims = torusDims; \
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y; \
}

// Normal estimator generators
#define OBJECT_NORMAL_AT(objIndex, point) \
	normalize( \
		(float4)( \
			distToObj##objIndex(point + VEC3(NUDGE_EPSILON, 0,0)) - distToObj##objIndex(point - VEC3(NUDGE_EPSILON, 0,0)), \
			distToObj##objIndex(point + VEC3(0, NUDGE_EPSILON,0)) - distToObj##objIndex(point - VEC3(0, NUDGE_EPSILON,0)), \
			distToObj##objIndex(point + VEC3(0, 0, NUDGE_EPSILON)) - distToObj##objIndex(point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Tracer implementation
// ---------------------

typedef struct {
	float4 position;
	float4 normal;
	float distFromOrigin;
	int objIndex;
	int matIndex;
} Hit;

typedef struct {
	unsigned int type;
	float4 diffuse;
	float4 emissive;
} Material;

// Material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0);

__constant Material SceneMaterial[] = {
	{SURFACE_LIGHT, VEC3(0,0,0), VEC3(60,60,60)},
	{SURFACE_DIFFUSE, VEC3(0.75, 0.25, 0.25)},
	{SURFACE_DIFFUSE, VEC3(0.25, 0.25, 0.75)},
	{SURFACE_DIFFUSE, VEC3(0.75, 0.75, 0.75)},
	{SURFACE_SPECULAR, VEC3(0.999, 0.999, 0.999)},
	{SURFACE_REFRACTIVE, VEC3(0.999, 0.999, 0.999)}
};

// Object definitions
// The following code will be autogenerated by the scene processor
// ------------------

// Light
SPHERE_SD_FN(0, VEC3(0, 1.5, 0), 0.1f)
// Left wall
BOX_SD_FN(1, VEC3(-2,0,0), VEC3(0.1, 2, 2))
// Right wall
BOX_SD_FN(2, VEC3(2,0,0), VEC3(0.1, 2, 2))
// Top wall
BOX_SD_FN(3, VEC3(0,2,0), VEC3(2, 0.1, 2))
// Bottom wall
BOX_SD_FN(4, VEC3(0,-2,0), VEC3(2, 0.1, 2))
// Back wall
BOX_SD_FN(5, VEC3(0,0,-2), VEC3(2, 2, 0.1))
// Mirror ball
SPHERE_SD_FN(6, VEC3(-1.0, -1.4, -1.0), 0.5)
// Glass ball
SPHERE_SD_FN(7, VEC3(1.4, -1.4, -0.5), 0.5)

// Estimate normal at intersection point
void getNormal(Hit *hit){
	// The following code will be autogenerated by the scene processor
	if( hit->objIndex == 0 ){
		hit->normal = OBJECT_NORMAL_AT(0, hit->position);
	} else if( hit->objIndex == 1 ){
		hit->normal = OBJECT_NORMAL_AT(1, hit->position);
	} else if( hit->objIndex == 2 ){
		hit->normal = OBJECT_NORMAL_AT(2, hit->position);
	} else if( hit->objIndex == 3 ){
		hit->normal = OBJECT_NORMAL_AT(3, hit->position);
	} else if( hit->objIndex == 4 ){
		hit->normal = OBJECT_NORMAL_AT(4, hit->position);
	} else if( hit->objIndex == 5 ){
		hit->normal = OBJECT_NORMAL_AT(5, hit->position);
	} else if( hit->objIndex == 6 ){
		hit->normal = OBJECT_NORMAL_AT(6, hit->position);
	} else if( hit->objIndex == 7 ){
		hit->normal = OBJECT_NORMAL_AT(7, hit->position);
	}
}

// Estimate distance to nearest object
void intersectWorld(const float4 rayOrigin, const float4 rayDir, const float minDist, const float maxDist, Hit *hit){
	hit->objIndex = NO_SURFACE_HIT;
	hit->matIndex = NO_SURFACE_HIT;

	float4 point;
	float curDist = minDist;
	float objDist, nearestDist;
	int nearestObj = -1;
	int nearestMat = -1;
	for(unsigned int step=0;step<MAX_TRACE_STEPS;step++){
		point = rayOrigin + rayDir * curDist;

		// Find nearest object/dist to point
		// The following code will be autogenerated by the scene processor
		nearestDist = FLT_MAX;
		objDist = distToObj0(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 0;
			nearestMat = 0;
		}
		objDist = distToObj1(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 1;
			nearestMat = 1;
		}
		objDist = distToObj2(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 2;
			nearestMat = 2;
		}
		objDist = distToObj3(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 3;
			nearestMat = 3;
		}
		objDist = distToObj4(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 4;
			nearestMat = 3;
		}
		objDist = distToObj5(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 5;
			nearestMat = 3;
		}
		objDist = distToObj6(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 6;
			nearestMat = 4;
		}
		objDist = distToObj7(point);
		if(objDist < nearestDist){
			nearestDist = objDist;
			nearestObj = 7;
			nearestMat = 5;
		}

		// If we are inside an object nearestDist will be < 0
		nearestDist = fabs(nearestDist);

		curDist += nearestDist;
		if(curDist > maxDist){
			return;
		}

		// We are close enough to the object to register a hit
		if(nearestDist < DIST_SNAP_EPSILON){
			hit->position = point;
			hit->distFromOrigin = curDist;
			hit->objIndex = nearestObj;
			hit->matIndex = nearestMat;
			return;
		}
	}
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir){
	Hit hit;
	float4 rCol;
	unsigned int bounce = 0;
	while( true ){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

		// No hit
		if(hit.objIndex == NO_SURFACE_HIT){
			if(bounce == 0){
				rCol = sceneBgColor;
			}
			break;
		}

		// Get normal at hit point
		getNormal(&hit);

		// For now just output the diffuse (or emissive for lights)
		rCol = max(SceneMaterial[hit.matIndex].diffuse, SceneMaterial[hit.matIndex].emissive);
		break;
	}

	return rCol;
}

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		const float4 eyePos,
		const unsigned int blockY,
		const unsigned int samplesPerPixel,
		const float exposure
		){
	// Get pixel coordinates
	unsigned int x = get_global_id(0);
	unsigned int y = get_global_id(1);
	if ( x > FRAME_W || y > FRAME_H ) {
		return;
	}

	// Calculate texel coordinates [0,1] range
	float tx = (float)(x) * TEXEL_STEP_X;
	float ty = (float)(y) * TEXEL_STEP_Y;
	float accumScaler = 1.0f / (float)samplesPerPixel;
	float4 accum = (float4)(0,0,0,0);

	for( uint sample=0;sample<samplesPerPixel;sample++){
		// Get ray direction using trilinear interpolation; trace ray and
		// add result to accumulation buffer
		float4 lVec = frustrumCorners[0] * (1 - ty) + frustrumCorners[2] * ty;
		float4 rVec = frustrumCorners[1] * (1 - ty) + frustrumCorners[3] * ty;
		accum += traceRay(eyePos, normalize(lVec * (1 - tx) + rVec * tx));
	}

	// Average samples
	accum *= accumScaler;

	// Apply tone-mapping and gamma correction using:
	// 1 - exp(-hdrColor * exposure)) [tone mapping HDR -> LDR]
	// pow(ldr, 0.45)) [gamma correction]
	frameBuffer[y*FRAME_W + x] = pow(-expm1(-accum*exposure), 0.45f);
}