Support multiple samples per pixel

Support multiple samples per pixel

The kernel will trace samplesPerPixel rays per pixel and average the
results. For now, traceRay() just returns a dummy result so we can test
that it works

// Global parameters that will be set via a template during compilation time.
__constant unsigned int frameW = 512;
__constant unsigned int frameH = 512;
__constant float texelStepX = 1.0 / 512.0;
__constant float texelStepY = 1.0 / 512.0;

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir){
	// For now, just return something so we can test the output
	return fabs(rayDir);
}

// 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 > frameW || y > frameH ) {
		return;
	}

	// Calculate texel coordinates [0,1] range
	float tx = (float)(x) * texelStepX;
	float ty = (float)(y) * texelStepY;
	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));
	}

	frameBuffer[y * frameW + x] = accum * accumScaler;
}

// Global parameters that will be set via a template during compilation time.
__constant unsigned int frameW = 512;
__constant unsigned int frameH = 512;
__constant float texelStepX = 1.0 / 512.0;
__constant float texelStepY = 1.0 / 512.0;

// Emit the color of a pixel by tracing a ray through the scene.
__kernel void tracePixel(
		__global float4 *frameBuffer,
		__global float4 *frustrumCorners,
		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 > frameW || y > frameH ) {
		return;
	}

	// Just interpolate the x,y coordinates
	float tx = (float)(x) * texelStepX;
	float ty = (float)(y) * texelStepY;
	frameBuffer[y * frameW + x] = (float4)(tx, ty, 0, 0.0);
}


interm

interm

/*
 * 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_ALIGNED_OUT_SCAN_LN)
#define MLO_OUT_HORIZ_PIX_SZ (MLO_N_OUT_HORIZ_READS * MLO_READ_UNIT)

// number of filter taps in the processing wk_item
#define MLO_WEI_WKITEM 4
// n of of filter blks
#define MLO_WEI_BLK_SZ0 ((MLO_FILTER_SIZE0 + MLO_WEI_WKITEM -1)/MLO_WEI_WKITEM)
#define MLO_WEI_BLK_SZ (MLO_FILTER_SIZE1*MLO_WEI_BLK_SZ0)
// n of filter tiles in the group grid
#define MLO_N_WEI_BLK (MLO_GRP_SZ/ MLO_WEI_BLK_SZ)
// n of steps per scan line to be made in the inner loop
#define MLO_OUT_WEI_SCAN_BLK ((MLO_OUT_WIDTH + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)

// max loop over virtual blocks for small images
#define MLO_MAX_WEI_BLK_LOOP_TMP ((MLO_OUT_WIDTH + MLO_OUT_WEI_SCAN_BLK - 1)/MLO_OUT_WEI_SCAN_BLK)
#if MLO_MAX_WEI_BLK_LOOP_TMP < MLO_N_WEI_BLK
#define MLO_MAX_WEI_BLK_LOOP MLO_MAX_WEI_BLK_LOOP_TMP
#else
#define MLO_MAX_WEI_BLK_LOOP MLO_N_WEI_BLK
#endif

#define MLO_OUT_BLK_GRP_PIX_SZ (MLO_OUT_HORIZ_PIX_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_BLK_GRP_WK_SZ (MLO_OUT_BLK_GRP_PIX_SZ / MLO_READ_UNIT)

// extended scan to deal with overshot in the inner loop
#define MLO_OUT_WEI_EXT_SCAN_BLK ((MLO_OUT_HORIZ_PIX_SZ + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)
#define MLO_OUT_HORIZ_PIX_EXT_SZ (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_N_WEI_BLK)
#define MLO_OUT_BLK_GRP_EXT_PIX_SZ (MLO_OUT_HORIZ_PIX_EXT_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_LCL_SZ (MLO_OUT_BLK_GRP_EXT_PIX_SZ)
// LDS OUT SIZE
#define MLO_TOTAL_OUT_LCL_SZ (MLO_N_LCL_OUT_MAPS*MLO_OUT_LCL_SZ)

// input size depends on output scan length and
// number of output scans
// this number is constrained by amount or LDS and size of register file.
// TO DO:: CHECK PADDING!!!
#define MLO_IN_LCL_HEIGHT ((MLO_N_ALIGNED_OUT_SCAN_BLK-1)*MLO_FILTER_STRIDE1 + MLO_FILTER_SIZE1)
#define MLO_N_IN_HORIZ_READS ((((MLO_OUT_HORIZ_PIX_SZ-1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT)
#define MLO_IN_BLK_GRP_PIX_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_IN_BLK_GRP_WK_SZ (MLO_IN_BLK_GRP_PIX_SZ/MLO_READ_UNIT)
#define MLO_IN_LCL_SZ (MLO_IN_BLK_GRP_PIX_SZ)
// LDS IN SIZE
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)
#define MLO_IN_VERT_READS (MLO_GRP_SZ/MLO_N_IN_HORIZ_READS)

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
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
inline int getWaveId()
{
	int wave_id = 0;

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

	return(wave_id);
}
#endif

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, _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];
			}

		}
	}
}

/*********************************************************************************************************
// wrw algorithm
// data layout.
// registers:
// per wk_item:
// MLO_READ_UNIT x MLO_N_LCL_OUT_MAPS of dy
// group:
// MLO_N_OUT_BLK_GRP blocks of MLO_N_OUT_BLK_GRP_PIX_SZ of dy
//
// LDS:
// data buffer
// MLO_N_LCL_IN_MAPS blocks of MLO_IN_LCL_SZ of x

// dw local buffer keeps 1 set of dweghts calculated at each iteration
	MLO_TEMP_WEI_BUFFER_SZ

//
// inputs:
// read into LDS
// MLO_N_IN_BLK_GRP groups
// output
// fetch
// MLO_N_OUT_BLK_GRP groups

// alg
// for each batch
//		until end of output map (MLO_N_OUT_BLK)
//			load input map block in LDS
//			load output maps in LDS
//          for k in filter size1
//				for l in filter size 0
//					at for each out/in pixel pair op,ip in wk_item
//                  calculate the contribution to a dweights by formula ip.x (input pixel.x) = (op.x * stride.x - pad .x + l) % (filter size 0), ip.y = (op.y * stride.y - pad.y + k) % filter size1,
//					wei[k,l] = in[ip.y, ip.x]*op[op.y, op.x]
//                  lcl_wei[MLO_OUT_BLK_GRP_PIX_SZ * out.grp + [op.y, op.x]] = we[k,l]
//                  accumulate the weights in the wk_item with wei_accum +=  lcl_wei[accum_id], accum_id = MLO_OUT_BLK_GRP*MLO_N_LCL_OUT_MAPS*map_id + k*(filter size 0) + l
//	write out accululate weights

// group layout
// 0 - input channel index
// 1 - output channel index
// 2 - 1

// loop over batches
**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW(
	const __global _FLOAT * top_df,
	const __global _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{
// input/output tiles

	__local _FLOAT lcl[(MLO_TOTAL_IN_LCL_SZ + MLO_TOTAL_OUT_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;
	__local _FLOAT * lcl_top = lcl + MLO_TOTAL_IN_LCL_SZ;

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();

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

// register memory
	int o_idx_base = get_group_id(2); // output map index base

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

	int o_idx = o_idx_base * (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS); // output map index

	int lcl_id = get_local_id(0);

// output global read
//	int o_blk_idx = iDiv(lcl_id, MLO_OUT_LCL_SZ);
//	int o_pix_X4 = iMod(lcl_id, o_blk_idx, MLO_OUT_LCL_SZ);
	int o_scan = iDiv(lcl_id, MLO_N_OUT_HORIZ_READS);
	int o_pix4 = iMod(lcl_id, o_scan, MLO_N_OUT_HORIZ_READS);

// input global read
	int i_blk_idx = 0; //iDiv(lcl_id, MLO_IN_LCL_SZ);
	//int c_pix_X4 = iMod(lcl_id, i_blk_idx, MLO_IN_LCL_SZ);
	int c_scan0 = iDiv(lcl_id, MLO_N_IN_HORIZ_READS);
	int c_pix4 = iMod(lcl_id,c_scan0, MLO_N_IN_HORIZ_READS);

	int w_blk_idx = iDiv(lcl_id, MLO_WEI_BLK_SZ);
	int w_idx = iMod(lcl_id, w_blk_idx, MLO_WEI_BLK_SZ);
	int w_y = iDiv(w_idx, MLO_WEI_BLK_SZ0);
	int w_x0 = iMod(w_idx, w_y, MLO_WEI_BLK_SZ0);

// FILTR SPECIFIC !!!!!
	int last_tap_to_remove = (w_x0 > 0)? 0 : 1;

// mask to shut down the overshot filter tap
	__constant int filter_mask0 = ~(1 << MLO_FILTER_SIZE0);
	int working_mask = 0;
// filled with 1 evry stride bit
	for(int i = 0; i < MLO_WEI_WKITEM; ++i)
	{
		working_mask |= (1 << (i*MLO_FILTER_STRIDE0));
	}
// add strting point
	working_mask <<= w_x0;
// clamp to the filter size
	working_mask &= filter_mask0;
// move back to the relative start

	__private _FLOAT pvt_accum[(MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM)];

	int gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE;
	int gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE;

	for (int i = 0; i < (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM); ++i)
	{
		pvt_accum[i] = 0;
	}

// zero out LDS
	for (int i = 0; i <  (MLO_TOTAL_IN_LCL_SZ + MLO_TOTAL_OUT_LCL_SZ) ; i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

	barrier(CLK_LOCAL_MEM_FENCE);
// zero out pvt accum
	// over all batches

#if 0
	if (c_idx == 0 && o_idx == 0)
	{
		printf("K:i: %d %d %d %d\n",
			lcl_id,
			c_scan0,
			MLO_N_IN_HORIZ_READS,
			MLO_IN_VERT_READS
		);
	}
#endif

#if 1
	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
		)
	{
		int in_y = 0;
		int out_y = 0;
// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1
// LDS UPDATE !!!!
// input scan
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		int gbl_in_scan_off = 0;
		int gbl_out_scan_off = 0;

		for(int c_scan = c_scan0;  in_y + c_scan < MLO_IN_HEIGHT && c_scan < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1 - 1; c_scan += MLO_IN_VERT_READS)
		{
// TODO :: multiply on right mask if needed
			*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
// TO DO:: CHECK HEIGHT
// TO DO:: CHECK PADDING
//			*(MLO_READ_TYPE*)&lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;
			for (int i = 0; i < MLO_READ_UNIT; ++i)
			{
				lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
			}

#if 0
			if (c_idx == 0 && o_idx == 0 && c_scan == 2 /*&& (c_pix4 == 0 || c_pix4 == 1)*/)
			{
				printf("K:i: %d %d %f %f\n",
					lcl_id,
					gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT,
					in_rd_data[0],
					in_rd_data[2]
				);
			}
#endif

		}

		in_y +=  MLO_FILTER_SIZE1 - MLO_FILTER_PAD1 - 1;

// TO DO: HANDLE PADDING
// over all out blocks
		for (int ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

// fetch input: (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1)
               // TODO:: HANDLE multiple INPUTS
			   // overshot i shadled by out put
			gbl_in_scan_off = gbl_in_off + mul24(in_y, MLO_IN_STRIDE);

			for (int c_scan = c_scan0;i_blk_idx < MLO_N_LCL_IN_MAPS && in_y + c_scan < MLO_IN_HEIGHT && c_scan <  (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1); c_scan += MLO_IN_VERT_READS)
			{

					*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
//					*(MLO_READ_TYPE*)&lcl_bot[(c_scan + MLO_FILTER_SIZE1 - 1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;
					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						lcl_bot[(c_scan + MLO_FILTER_SIZE1 - 1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
					}

#if 0
					if (c_idx == 0 && o_idx == 0 && in_y + c_scan == 2 && (c_pix4 == 0 || c_pix4 == 1))
					{
						printf("K:i: %d %d %f %f\n",
							lcl_id,
							gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT,
							in_rd_data[0],
							in_rd_data[2]
						);
					}
#endif

			}

			// ouput scan
			__private _FLOAT out_rd_data[MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT];

			gbl_out_scan_off = gbl_out_off + mul24(out_y, MLO_OUT_STRIDE);

// over all outputs groups

			int gbl_out_scan_off1 = gbl_out_scan_off;
			for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og, gbl_out_scan_off1 += MLO_OUT_LCL_SZ*MLO_OUT_CHANNEL_STRIDE )
			{

// fetch output

				for(int o = 0; o < MLO_N_LCL_OUT_MAPS
						&&  ( o_idx + o < MLO_N_OUTPUTS && out_y + o_scan < MLO_OUT_HEIGHT && o_scan < MLO_N_ALIGNED_OUT_SCAN_BLK)
						; ++o)
				{
						*(MLO_READ_TYPE*)&out_rd_data[o*MLO_READ_UNIT]
							= *(MLO_READ_TYPE*)&top_df[gbl_out_scan_off + o*MLO_OUT_CHANNEL_STRIDE + o_scan * MLO_OUT_STRIDE + o_pix4*MLO_READ_UNIT];
#if MLO_OUT_SCAN_NOT_DIVBY4
						if (o_pix4 == (MLO_N_OUT_HORIZ_READS-1))
						{
							for(int i = MLO_READ_UNIT-1; i >= MLO_READ_UNIT - MLO_OUT_N_PIXS_OFF; --i)
							{
	 							out_rd_data[o*MLO_READ_UNIT + i] = 0;
							}
						}
#endif
// write with MLO_OUT_HORIZ_PIX_EXT_SZ stride
//						*(MLO_READ_TYPE*)&lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)&out_rd_data[o*MLO_READ_UNIT];
						for (int i = 0; i < MLO_READ_UNIT; ++i)
						{
							lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + i] = out_rd_data[o*MLO_READ_UNIT + i];
						}

#if 0
						if (c_idx == 0 && o_idx == 0 && og == 0 && o == 1 && out_y + o_scan == 1)
						{
							printf("K:o: %d %d %d %f %f %f\n",
								lcl_id,
								gbl_out_scan_off + o*MLO_IN_CHANNEL_STRIDE + o_scan * MLO_IN_STRIDE + o_pix4*MLO_READ_UNIT,
								o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT,
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 0],
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 1],
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 2]
								);
						}
#endif

				}

// process
// algorithm

#if 1
				for(int j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)
				{

					_FLOAT i_vals[MLO_WEI_WKITEM];
					for(int w = 0; w < MLO_WEI_WKITEM - 1; ++w)
					{
							int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							int i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + 0) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[w] = i_val;

					}

// if we overshoot the scanline
// out data will be 0 by initial setting

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

//						for(int w = 0; w < MLO_WEI_WKITEM; ++w)

						{
							int w_x = w_x0 + (MLO_WEI_WKITEM-1)* MLO_WEI_BLK_SZ0;
							int i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[(MLO_WEI_WKITEM-1)] = i_val;

						}

						for(int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
						{
// read with MLO_OUT_HORIX_PIX_EXT_SZ stride
							_FLOAT o_val
								= lcl_top[o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i)];

							int w = 0;
							for(/*int w = 0*/; w < MLO_WEI_WKITEM - 1; ++w )
							{

								_FLOAT i_val = i_vals[w];
								pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

#if 0
								int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
								if (c_idx == 0 && o_idx == 0 && og ==0 && o == 1 && w_y ==0 && w_x == 0 && lcl_id <= 45/*&& (j==0 || j==1)*/)
								{
									printf("K:s: %d %d %d %d %d %f %f %f\n",
										lcl_id,
										j,
										i,
										w,
										o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i),
										pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w],
										i_val,
										o_val
									);
								}
#endif
							}

// set input data to zero if MLO_FILTER_SIZE0 is not multiple of MLO_WEI_WKITEM and it's over MLO_FILTER_SIZE0

							_FLOAT i_val = i_vals[w];
// not needed becuse it's handled when "virtual" tables are trasnformed into real ones
	//						int ii_val = as_uint(i_val) & last_tap_to_remove;
	//						i_val = as_float(ii_val);

							pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

#if 0
							int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							if (c_idx == 0 && o_idx == 0 && og == 0 && o == 1 && w_y == 0 && w_x == 0 && lcl_id <= 45/* && (j == 0 || j == 1) */)
							{
								printf("K:s: %d %d %d %d %d %f %f %f\n",
									lcl_id,
									j,
									i,
									w,
									o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i),
									pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w],
									i_val,
									o_val
								);
							}
#endif

						}

						for(int w = 0; w < MLO_WEI_WKITEM - 1; ++w)
						{
							i_vals[w] = i_vals[w + 1];

						}

					}
				}
#else
						for(int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
						{
							for(int w = 0; w < MLO_WEI_WKITEM; ++w)
							{
								pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] = lcl_top[(og * MLO_N_LCL_OUT_MAPS + o)] * lcl_bot[w];
							}
						}
#endif

			} // for(; og < (MLO_N_OUT_BLK_GRP; ++og )

			barrier(CLK_LOCAL_MEM_FENCE);

// move data up
			for(int c_scan = c_scan0; c_scan < MLO_FILTER_SIZE1 -1; c_scan += MLO_IN_VERT_READS)
			{
					*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&lcl_bot[(c_scan +  (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1))*(MLO_IN_LCL_WIDTH) + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT];
					*(MLO_READ_TYPE*)&lcl_bot[c_scan*(MLO_IN_LCL_WIDTH) + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;

			}

		} // for (int ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
	} // for (int b = 0;

#endif

	int oo = iDiv(lcl_id, (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0));
	int wei_i = iMod(lcl_id, oo, (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0));
	int wei_i_y = iDiv(wei_i, (MLO_FILTER_SIZE0));
	int wei_i_x = iMod(wei_i,wei_i_y, (MLO_FILTER_SIZE0));
	// send it out
	int wei_df_off = mul24(o_idx, (int)MLO_WEI_BATCH_STRIDE)
		// this input channel
		+ mul24(c_idx, (int)MLO_WEI_CHANNEL_STRIDE);

	int wei_lcl_off = 0;
	_FLOAT final_sum = 0;

// save in lcl and orgnize in a proper order
// outputs
//	filter size1
//	  filter size0

	for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
	{
		for(int o = 0;  /*w_blk_idx < MLO_N_WEI_BLK && */o < MLO_N_LCL_OUT_MAPS; ++o)
		{
			int w = 0;
			for(; w < MLO_WEI_WKITEM; ++w)
			{
// save "virtual" filter table
				int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
				wei_lcl_off = ((o * MLO_N_WEI_BLK  + w_blk_idx) * MLO_FILTER_SIZE1  + w_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + w_x;
				lcl_bot[wei_lcl_off] = pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];
#if 0
				if (c_idx == 0 && o_idx == 0 && og == 0 && o == 1 && w_y == 0 && w_x == 0 && lcl_id <= 45/* && (j == 0 || j == 1) */)
				{
					printf("K:u: %d %d %d %f %f\n",
						lcl_id,
						w_blk_idx,
						wei_lcl_off,
						lcl_bot[wei_lcl_off],
						pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w]
					);
				}
#endif

			}

		}

		barrier(CLK_LOCAL_MEM_FENCE);
#if 1
// read into real filter table
		if (lcl_id >= (og * MLO_N_LCL_OUT_MAPS *MLO_FILTER_SIZE1*MLO_FILTER_SIZE0) && lcl_id < ((og + 1) * MLO_N_LCL_OUT_MAPS *MLO_FILTER_SIZE1*MLO_FILTER_SIZE0))
		{
			for(int i = 0; i < MLO_MAX_WEI_BLK_LOOP; ++i)
			{
				final_sum += lcl_bot[(((oo - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK  + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x];
#if 0
				if (c_idx == 0 && o_idx == 0 && og == 0 && (oo - og * MLO_N_LCL_OUT_MAPS) ==1 && wei_i_y == 0 && wei_i_x == 0)
				{
					printf("K:fs: %d %d %d  %f %f\n",
						lcl_id,
						i,
						(((oo - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x,
						final_sum,
						lcl_bot[(((oo - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x]
					);
				}
#endif
			}

#if 1

			weights_df[wei_df_off + oo * MLO_WEI_BATCH_STRIDE + wei_i] = final_sum; //lcl_bot[lcl_id]; //
#else
			_FLOAT t_accum = 0;

			for (int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
			{
				for (int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
				{
					for (int w = 0; w < MLO_WEI_WKITEM; ++w)
					{
						t_accum += pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];
					}
				}
			}

			weights_df[lcl_id] = t_accum;
#endif

		}
#endif
		barrier(CLK_LOCAL_MEM_FENCE);

	} // for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)

}

/*
 * 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_ALIGNED_OUT_SCAN_LN)
#define MLO_OUT_HORIZ_PIX_SZ (MLO_N_OUT_HORIZ_READS * MLO_READ_UNIT)

// number of filter taps in the processing wk_item
#define MLO_WEI_WKITEM 4
// n of of filter blks
#define MLO_WEI_BLK_SZ0 ((MLO_FILTER_SIZE0 + MLO_WEI_WKITEM -1)/MLO_WEI_WKITEM)
#define MLO_WEI_BLK_SZ (MLO_FILTER_SIZE1*MLO_WEI_BLK_SZ0)
// n of filter tiles in the group grid
#define MLO_N_WEI_BLK (MLO_GRP_SZ/ MLO_WEI_BLK_SZ)
// n of steps per scan line to be mdae in the inner loop
#define MLO_OUT_WEI_SCAN_BLK ((MLO_IN_WIDTH + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)

// max loop over virtual blocks for small images
#define MLO_MAX_WEI_BLK_LOOP_TMP ((MLO_IN_WIDTH + MLO_OUT_WEI_SCAN_BLK - 1)/MLO_OUT_WEI_SCAN_BLK)
#if MLO_MAX_WEI_BLK_LOOP_TMP < MLO_N_WEI_BLK
#define MLO_MAX_WEI_BLK_LOOP MLO_MAX_WEI_BLK_LOOP_TMP
#else
#define MLO_MAX_WEI_BLK_LOOP MLO_N_WEI_BLK
#endif

#define MLO_OUT_BLK_GRP_PIX_SZ (MLO_OUT_HORIZ_PIX_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_BLK_GRP_WK_SZ (MLO_OUT_BLK_GRP_PIX_SZ / MLO_READ_UNIT)

// extended scan to deal with overshot in the inner loop
#define MLO_OUT_WEI_EXT_SCAN_BLK ((MLO_OUT_HORIZ_PIX_SZ + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)
#define MLO_OUT_HORIZ_PIX_EXT_SZ (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_N_WEI_BLK)
#define MLO_OUT_BLK_GRP_EXT_PIX_SZ (MLO_OUT_HORIZ_PIX_EXT_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_LCL_SZ (MLO_OUT_BLK_GRP_EXT_PIX_SZ)
// LDS OUT SIZE
#define MLO_TOTAL_OUT_LCL_SZ (MLO_N_LCL_OUT_MAPS*MLO_OUT_LCL_SZ)

// input size depends on output scan length and
// number of output scans
// this number is constrained by amount or LDS and size of register file.
// TO DO:: CHECK PADDING!!!
#define MLO_IN_LCL_HEIGHT ((MLO_N_ALIGNED_OUT_SCAN_BLK-1)*MLO_FILTER_STRIDE1 + MLO_FILTER_SIZE1)
#define MLO_N_IN_HORIZ_READS ((((MLO_OUT_HORIZ_PIX_SZ-1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0) + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT)
#define MLO_IN_BLK_GRP_PIX_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_IN_BLK_GRP_WK_SZ (MLO_IN_BLK_GRP_PIX_SZ/MLO_READ_UNIT)
#define MLO_IN_LCL_SZ (MLO_IN_BLK_GRP_PIX_SZ)
// LDS IN SIZE
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)
#define MLO_IN_VERT_READS (MLO_GRP_SZ/MLO_N_IN_HORIZ_READS)

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
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
inline int getWaveId()
{
	int wave_id = 0;

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

	return(wave_id);
}
#endif

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, _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];
			}

		}
	}
}

/*********************************************************************************************************
// wrw algorithm
// data layout.
// registers:
// per wk_item:
// MLO_READ_UNIT x MLO_N_LCL_OUT_MAPS of dy
// group:
// MLO_N_OUT_BLK_GRP blocks of MLO_N_OUT_BLK_GRP_PIX_SZ of dy
//
// LDS:
// data buffer
// MLO_N_LCL_IN_MAPS blocks of MLO_IN_LCL_SZ of x

// dw local buffer keeps 1 set of dweghts calculated at each iteration
	MLO_TEMP_WEI_BUFFER_SZ

//
// inputs:
// read into LDS
// MLO_N_IN_BLK_GRP groups
// output
// fetch
// MLO_N_OUT_BLK_GRP groups

// alg
// for each batch
//		until end of output map (MLO_N_OUT_BLK)
//			load input map block in LDS
//			load output maps in LDS
//          for k in filter size1
//				for l in filter size 0
//					at for each out/in pixel pair op,ip in wk_item
//                  calculate the contribution to a dweights by formula ip.x (input pixel.x) = (op.x * stride.x - pad .x + l) % (filter size 0), ip.y = (op.y * stride.y - pad.y + k) % filter size1,
//					wei[k,l] = in[ip.y, ip.x]*op[op.y, op.x]
//                  lcl_wei[MLO_OUT_BLK_GRP_PIX_SZ * out.grp + [op.y, op.x]] = we[k,l]
//                  accumulate the weights in the wk_item with wei_accum +=  lcl_wei[accum_id], accum_id = MLO_OUT_BLK_GRP*MLO_N_LCL_OUT_MAPS*map_id + k*(filter size 0) + l
//	write out accululate weights

// group layout
// 0 - input channel index
// 1 - output channel index
// 2 - 1

// loop over batches
**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW(
	const __global _FLOAT * top_df,
	const __global _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{
// input/output tiles

	__local _FLOAT lcl[(MLO_TOTAL_IN_LCL_SZ + MLO_TOTAL_OUT_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;
	__local _FLOAT * lcl_top = lcl + MLO_TOTAL_IN_LCL_SZ;

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();

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

// register memory
	int o_idx_base = get_group_id(2); // output map index base

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

	int o_idx = o_idx_base * (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS); // output map index

	int lcl_id = get_local_id(0);

// output global read
//	int o_blk_idx = iDiv(lcl_id, MLO_OUT_LCL_SZ);
//	int o_pix_X4 = iMod(lcl_id, o_blk_idx, MLO_OUT_LCL_SZ);
	int o_scan = iDiv(lcl_id, MLO_N_OUT_HORIZ_READS);
	int o_pix4 = iMod(lcl_id, o_scan, MLO_N_OUT_HORIZ_READS);

// input global read
	int i_blk_idx = 0; //iDiv(lcl_id, MLO_IN_LCL_SZ);
	//int c_pix_X4 = iMod(lcl_id, i_blk_idx, MLO_IN_LCL_SZ);
	int c_scan0 = iDiv(lcl_id, MLO_N_IN_HORIZ_READS);
	int c_pix4 = iMod(lcl_id,c_scan0, MLO_N_IN_HORIZ_READS);

	int w_blk_idx = iDiv(lcl_id, MLO_WEI_BLK_SZ);
	int w_idx = iMod(lcl_id, w_blk_idx, MLO_WEI_BLK_SZ);
	int w_y = iDiv(w_idx, MLO_WEI_BLK_SZ0);
	int w_x0 = iMod(w_idx, w_y, MLO_WEI_BLK_SZ0);

// FILTR SPECIFIC !!!!!
	int last_tap_to_remove = (w_x0 > 0)? 0 : 1;

// mask to shut down the overshot filter tap
	__constant int filter_mask0 = ~(1 << MLO_FILTER_SIZE0);
	int working_mask = 0;
// filled with 1 evry stride bit
	for(int i = 0; i < MLO_WEI_WKITEM; ++i)
	{
		working_mask |= (1 << (i*MLO_FILTER_STRIDE0));
	}
// add strting point
	working_mask <<= w_x0;
// clamp to the filter size
	working_mask &= filter_mask0;
// move back to the relative start

	__private _FLOAT pvt_accum[(MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM)];

	int gbl_in_off = c_idx * MLO_OUT_CHANNEL_STRIDE;
	int gbl_out_off = o_idx * MLO_IN_CHANNEL_STRIDE;

	for (int i = 0; i < (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM); ++i)
	{
		pvt_accum[i] = 0;
	}

// zero out LDS
	for (int i = 0; i <  (MLO_TOTAL_IN_LCL_SZ + MLO_TOTAL_OUT_LCL_SZ) ; i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

	barrier(CLK_LOCAL_MEM_FENCE);
// zero out pvt accum
	// over all batches

#if 0
	if (c_idx == 0 && o_idx == 0)
	{
		printf("K:i: %d %d %d %d\n",
			lcl_id,
			c_scan0,
			MLO_N_IN_HORIZ_READS,
			MLO_IN_VERT_READS
		);
	}
#endif

#if 1
	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS * MLO_OUT_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS * MLO_IN_BATCH_STRIDE
		)
	{
		int in_y = 0;
		int out_y = 0;
// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1
// LDS UPDATE !!!!
// input scan
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		int gbl_in_scan_off = 0;
		int gbl_out_scan_off = 0;

		for(int c_scan = c_scan0;  in_y + c_scan < MLO_OUT_HEIGHT && c_scan < MLO_FILTER_SIZE1 - MLO_FILTER_PAD1 - 1; c_scan += MLO_IN_VERT_READS)
		{
// TODO :: multiply on right mask if needed
			*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_OUT_STRIDE + c_pix4*MLO_READ_UNIT];
// TO DO:: CHECK HEIGHT
// TO DO:: CHECK PADDING
//			*(MLO_READ_TYPE*)&lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;
			for (int i = 0; i < MLO_READ_UNIT; ++i)
			{
				lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
			}

#if 0
			if (c_idx == 0 && o_idx == 0 && c_scan == 2 /*&& (c_pix4 == 0 || c_pix4 == 1)*/)
			{
				printf("K:i: %d %d %f %f\n",
					lcl_id,
					gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT,
					in_rd_data[0],
					in_rd_data[2]
				);
			}
#endif

		}

		in_y +=  MLO_FILTER_SIZE1 - MLO_FILTER_PAD1 - 1;

// TO DO: HANDLE PADDING
// over all out blocks
		for (int ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			gbl_in_scan_off = gbl_in_off + mul24(in_y, MLO_OUT_STRIDE);

// fetch input: (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1)
               // TODO:: HANDLE multiple INPUTS
			   // overshot i shadled by out put

			for (int c_scan = c_scan0;i_blk_idx < MLO_N_LCL_IN_MAPS && in_y + c_scan < MLO_OUT_HEIGHT && c_scan <  (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1); c_scan += MLO_IN_VERT_READS)
			{

					*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c_scan * MLO_OUT_STRIDE + c_pix4*MLO_READ_UNIT];
//					*(MLO_READ_TYPE*)&lcl_bot[(c_scan + MLO_FILTER_SIZE1 - 1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;
					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						lcl_bot[(c_scan + MLO_FILTER_SIZE1 - 1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
					}

#if 0
					if (c_idx == 0 && o_idx == 0 && in_y + c_scan == 2 && (c_pix4 == 0 || c_pix4 == 1))
					{
						printf("K:i: %d %d %f %f\n",
							lcl_id,
							gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT,
							in_rd_data[0],
							in_rd_data[2]
						);
					}
#endif

			}

			// ouput scan
			__private _FLOAT out_rd_data[MLO_N_LCL_OUT_MAPS * MLO_READ_UNIT];

			gbl_out_scan_off = gbl_out_off + mul24(out_y, MLO_IN_STRIDE);

// over all outputs groups

			int gbl_out_scan_off1 = gbl_out_scan_off;
			for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og, gbl_out_scan_off1 += MLO_OUT_LCL_SZ*MLO_IN_CHANNEL_STRIDE )
			{

// fetch output

				for(int o = 0; o < MLO_N_LCL_OUT_MAPS
						&&  ( o_idx + o < MLO_N_INPUTS && out_y + o_scan < MLO_IN_HEIGHT && o_scan < MLO_N_ALIGNED_OUT_SCAN_BLK)
						; ++o)
				{
						*(MLO_READ_TYPE*)&out_rd_data[o*MLO_READ_UNIT]
							= *(MLO_READ_TYPE*)&top_df[gbl_out_scan_off + o*MLO_IN_CHANNEL_STRIDE + o_scan * MLO_IN_STRIDE + o_pix4*MLO_READ_UNIT];
#if MLO_OUT_SCAN_NOT_DIVBY4
						if (o_pix4 == (MLO_N_OUT_HORIZ_READS-1))
						{
							for(int i = MLO_READ_UNIT-1; i >= MLO_READ_UNIT - MLO_OUT_N_PIXS_OFF; --i)
							{
	 							out_rd_data[o*MLO_READ_UNIT + i] = 0;
							}
						}
#endif
// write with MLO_OUT_HORIZ_PIX_EXT_SZ stride
//						*(MLO_READ_TYPE*)&lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)&out_rd_data[o*MLO_READ_UNIT];
						for (int i = 0; i < MLO_READ_UNIT; ++i)
						{
							lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + i] = out_rd_data[o*MLO_READ_UNIT + i];
						}

#if 0
						if (c_idx == 0 && o_idx == 0 && og == 0 && o == 0 && out_y + o_scan == 1)
						{
							printf("K:o: %d %d %d %f %f %f\n",
								lcl_id,
								gbl_out_scan_off + o*MLO_IN_CHANNEL_STRIDE + o_scan * MLO_IN_STRIDE + o_pix4*MLO_READ_UNIT,
								o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT,
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 0],
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 1],
								lcl_top[o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT + 2]
								);
						}
#endif

				}

// process
// algorithm

#if 1
				for(int j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)
				{

					_FLOAT i_vals[MLO_WEI_WKITEM];
					for(int w = 0; w < MLO_WEI_WKITEM - 1; ++w)
					{
							int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							int i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + 0) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[w] = i_val;

					}

// if we overshoot the scanline
// out data will be 0 by initial setting

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

//						for(int w = 0; w < MLO_WEI_WKITEM; ++w)

						{
							int w_x = w_x0 + (MLO_WEI_WKITEM-1)* MLO_WEI_BLK_SZ0;
							int i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[(MLO_WEI_WKITEM-1)] = i_val;

						}

						for(int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
						{
// read with MLO_OUT_HORIX_PIX_EXT_SZ stride
							_FLOAT o_val
								= lcl_top[o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i)];

							int w = 0;
							for(/*int w = 0*/; w < MLO_WEI_WKITEM - 1; ++w )
							{

								_FLOAT i_val = i_vals[w];
								pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

#if 0
								int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
								if (c_idx == 0 && o_idx == 0 && og ==0 && o == 0 && w_y ==0 && w_x == 9 && lcl_id <= 45/*&& (j==0 || j==1)*/)
								{
									printf("K:s: %d %d %d %d %d %f %f %f\n",
										lcl_id,
										j,
										i,
										w,
										o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i),
										pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w],
										i_val,
										o_val
									);
								}
#endif
							}

// set input data to zero if MLO_FILTER_SIZE0 is not multiple of MLO_WEI_WKITEM and it's over MLO_FILTER_SIZE0

							_FLOAT i_val = i_vals[w];
// not needed becuse it's handled when "virtual" tables are trasnformed into real ones
	//						int ii_val = as_uint(i_val) & last_tap_to_remove;
	//						i_val = as_float(ii_val);

							pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

#if 0
							int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							if (c_idx == 0 && o_idx == 0 && og == 0 && o == 0 && w_y == 0 && w_x == 9 && lcl_id <= 45/* && (j == 0 || j == 1) */)
							{
								printf("K:s: %d %d %d %d %d %f %f %f\n",
									lcl_id,
									j,
									i,
									w,
									o*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i),
									pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w],
									i_val,
									o_val
								);
							}
#endif

						}

						for(int w = 0; w < MLO_WEI_WKITEM - 1; ++w)
						{
							i_vals[w] = i_vals[w + 1];

						}

					}
				}
#else
						for(int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
						{
							for(int w = 0; w < MLO_WEI_WKITEM; ++w)
							{
								pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] = lcl_top[(og * MLO_N_LCL_OUT_MAPS + o)] * lcl_bot[w];
							}
						}
#endif

			} // for(; og < (MLO_N_OUT_BLK_GRP; ++og )

			barrier(CLK_LOCAL_MEM_FENCE);

// move data up
			for(int c_scan = c_scan0; c_scan < MLO_FILTER_SIZE1 -1; c_scan += MLO_IN_VERT_READS)
			{
					*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&lcl_bot[(c_scan +  (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1))*(MLO_IN_LCL_WIDTH) + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT];
					*(MLO_READ_TYPE*)&lcl_bot[c_scan*(MLO_IN_LCL_WIDTH) + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT] = *(MLO_READ_TYPE*)in_rd_data;

			}

		} // for (int ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
	} // for (int b = 0;

#endif

	int o = iDiv(lcl_id, (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0));
	int wei_i = iMod(lcl_id, o, (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0));
	int wei_i_y = iDiv(wei_i, (MLO_FILTER_SIZE0));
	int wei_i_x = iMod(wei_i,wei_i_y, (MLO_FILTER_SIZE0));

	int wei_lcl_off = 0;
	_FLOAT final_sum = 0;

#if 1
// save in lcl and orgnize in a proper order
// outputs
//	filter size1
//	  filter size0

	for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
	{
		for(int o = 0;  /*w_blk_idx < MLO_N_WEI_BLK && */o < MLO_N_LCL_OUT_MAPS; ++o)
		{
			int w = 0;
			for(; w < MLO_WEI_WKITEM; ++w)
			{
// save "virtual" filter table
				int w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
				wei_lcl_off = ((o * MLO_N_LCL_OUT_MAPS *  MLO_N_WEI_BLK  + w_blk_idx) * MLO_FILTER_SIZE1  + w_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + w_x;
				lcl_bot[wei_lcl_off] = pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];
#if 0
				if (c_idx == 0 && o_idx == 0 && og == 0 && o == 0 && w_y == 0 && w_x == 9 && lcl_id <= 45/* && (j == 0 || j == 1) */)
				{
					printf("K:u: %d %d %d %f %f\n",
						lcl_id,
						w_blk_idx,
						wei_lcl_off,
						lcl_bot[wei_lcl_off],
						pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w]
					);
				}
#endif

			}

		}

#if 1
// read into real filter table
		if (lcl_id >= (og * MLO_N_LCL_OUT_MAPS *MLO_FILTER_SIZE1*MLO_FILTER_SIZE0) && lcl_id < ((og + 1) * MLO_N_LCL_OUT_MAPS *MLO_FILTER_SIZE1*MLO_FILTER_SIZE0))
		{
			for(int i = 0; i < MLO_MAX_WEI_BLK_LOOP; ++i)
			{
				final_sum += lcl_bot[(((o - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK  + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x];
#if 0
				if (c_idx == 0 && o_idx == 0 && og == 0 && o == 0 && wei_i_y == 0 && wei_i_x == 9)
				{
					printf("K:fs: %d %d %d  %f %f\n",
						lcl_id,
						i,
						(((o - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x,
						final_sum,
						lcl_bot[(((o - og * MLO_N_LCL_OUT_MAPS) * MLO_N_WEI_BLK + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x]
					);
				}
#endif
			}
		}
#endif

	} // for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
#else

	for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
	{
		for(int o = 0; w_blk_idx < MLO_N_WEI_BLK && o < MLO_N_LCL_OUT_MAPS; ++o)
		{
			int w = 0;
			for(; w < MLO_WEI_WKITEM; ++w)
			{
				wei_lcl_off = og + (o * MLO_N_LCL_OUT_MAPS *  MLO_N_WEI_BLK  + w_blk_idx) * (MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0) + w_y * MLO_FILTER_SIZE0 + w_x0 + w * MLO_FILTER_STRIDE0;
				lcl_bot[wei_lcl_off] = pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];

			}
		}

	} // for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)

#endif
// send it out
	int wei_df_off = mul24(o_idx, (int)MLO_WEI_BATCH_STRIDE)
		// this input channel
						+ mul24(c_idx, (int)MLO_WEI_CHANNEL_STRIDE);
// write out from 0th wk_item
	if (lcl_id < (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS *MLO_FILTER_SIZE1*MLO_FILTER_SIZE0))
	{

#if 1

		weights_df[wei_df_off + o * MLO_WEI_BATCH_STRIDE + wei_i] = final_sum; //lcl_bot[lcl_id]; //
#else
		_FLOAT t_accum = 0;

		for(int og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
		{
			for(int o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
			{
				for(int w = 0; w < MLO_WEI_WKITEM; ++w)
				{
					t_accum += pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];
				}
			}
		}

		weights_df[lcl_id] = t_accum;
#endif

	}

}


Add direct light sampling for diffuse surfaces

Using direct light sampling helps the trace converge faster

// Global parameters that will be set via a template during compilation time.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#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 NO_MATERIAL_HIT -1

// Limits
#define MAX_TRACE_STEPS 32
#define MAX_BOUNCES 4 // iris pro compilation runs out of memory if set to > 5
#define MIN_BOUNCES_TO_USE_RR 5

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

// Vector helpers
#define VEC3_MAX_COMPONENT(v) max(v.x, max(v.y, v.z))
#define VEC1(x) (float4)(x, 0.0f, 0.0f, 0.0f)
#define VEC2(x,y) (float4)(x, y, 0.0f, 0.0f)
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC4(x,y,z,w) (float4)(x,y,z,w)
#define ORIGIN(v) VEC3(v,v,v)

// Normal estimator generators
#define OBJECT_NORMAL_AT(estimatorFn, objIndex, point) \
	normalize( \
		(float4)( \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(NUDGE_EPSILON, 0,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(NUDGE_EPSILON, 0,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, NUDGE_EPSILON,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, NUDGE_EPSILON,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, 0, NUDGE_EPSILON)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Tracer structs
// ---------------------

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

// Supported material types
#define MATERIAL_DIFFUSE 0
#define MATERIAL_SPECULAR 1
#define MATERIAL_REFRACTIVE 2
#define MATERIAL_LIGHT 65535

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

// Supported object types
#define OBJECT_PLANE 0
#define OBJECT_SPHERE 1
#define OBJECT_BOX 2
#define OBJECT_TORUS 3

typedef struct {
	unsigned int type;
	unsigned int material;
	float4 origin;
	float4 params;
} Object;

// -----------------------
// Object and material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{MATERIAL_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(50.0f,50.0f,50.0f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{MATERIAL_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{MATERIAL_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{MATERIAL_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

__constant Object SceneObject[] = {
	// Light
	{OBJECT_SPHERE, 0, VEC3(0.0f, 1.3f, 0.0f), VEC1(0.1f)},
	// Left wall
	{OBJECT_BOX, 1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Right wall
	{OBJECT_BOX, 2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Top wall
	{OBJECT_BOX, 3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Bottom wall
	{OBJECT_BOX, 3, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Back wall
	{OBJECT_BOX, 3, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f)},
	// Mirror ball
	{OBJECT_SPHERE, 4, VEC3(-1.0f, -1.4f, -1.0f), VEC1(0.5f)},
	// Glass ball
	{OBJECT_SPHERE, 5, VEC3(1.4f, -1.4f, -0.5f), VEC1(0.5f)}
};

// -----------------------
// Random number and direction generators
// -----------------------
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;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// Return random cosine weighted direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = cos(theta) /  pi * sin(theta_max)^2
float4 rndCosWeigthedConeDir(float4 normal, float sinThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rd = sinThetaMax * sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;
	float r2t = sqrt(1 - rnd.x*sinThetaMax*sinThetaMax);

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * r2t);
}

// Return random direction in a cone centered at the normal
// proportional to a solid angle. This function has:
// PDF = 1 / 2 * pi * (1 - cos(theta_max))
float4 rndConeDir(float4 normal, float cosThetaMax, uint2 *seed){
	float2 rnd  = clRand(seed);

	float rcd = 1.0f - rnd.x*(1.0f - cosThetaMax);
	float rd = sqrt(1.0f - rcd*rcd);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * rcd);
}

// -----------------------
// Distance estimators for supported primitives
// -----------------------
float planeSD(float4 origin, float4 dims, float4 point){
	dims = normalize(dims);
	return dot((point - origin).xyz, dims.xyz) + dims.w;
}
float sphereSD(float4 origin, float4 radius, float4 point){
	return length(point - origin) - radius.x;
}
float boxSD(float4 origin, float4 dims, float4 point){
	float3 d = fabs(point.xyz - origin.xyz) - dims.xyz;
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f));
}
float torusSD(float4 origin, float4 dims, float4 point){
	point -= origin;
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y;
}

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

// Estimate normal at intersection point
void getNormal(Hit *hit){
	switch(SceneObject[hit->objIndex].type) {
		case OBJECT_PLANE:
			hit->normal = OBJECT_NORMAL_AT(planeSD, hit->objIndex, hit->position);
		break;
		case OBJECT_SPHERE:
			hit->normal = OBJECT_NORMAL_AT(sphereSD, hit->objIndex, hit->position);
		break;
		case OBJECT_BOX:
			hit->normal = OBJECT_NORMAL_AT(boxSD, hit->objIndex, hit->position);
		break;
		case OBJECT_TORUS:
			hit->normal = OBJECT_NORMAL_AT(torusSD, hit->objIndex, hit->position);
		break;
	}
}

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

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

		// Find nearest object/dist to point
		nearestDist = FLT_MAX;
		for(objIndex=0;objIndex<numObjects; objIndex++){
			switch(SceneObject[objIndex].type) {
				case OBJECT_PLANE:
					objDist = planeSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_SPHERE:
					objDist = sphereSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_BOX:
					objDist = boxSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_TORUS:
					objDist = torusSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
			}

			if(objDist < nearestDist){
				nearestDist = objDist;
				nearestObj = objIndex;
				nearestMat = SceneObject[objIndex].material;
			}
		}

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

// Collect direct light at hit point.
float4 getDirectLight(Hit *hit, uint2 *rndSeed){
	float4 light = 0.0f;
	Hit shadowHit;
	__constant Material *material;
	unsigned int objIndex;
	unsigned int numObjects = (uint)(sizeof(SceneObject) / sizeof(Object));

	for(objIndex=0;objIndex<numObjects; objIndex++){
		material = &SceneMaterial[SceneObject[objIndex].material];
		if( material->type != MATERIAL_LIGHT) {
			continue;
		}

		float4 lightDir = SceneObject[objIndex].origin - hit->position;

		// Early rejection for back-facing surfaces
		if(dot(lightDir, hit->normal) < 0){
			continue;
		}

		// Treat the light as a sphere with radius the largest dimension; then
		// generate a lightDir vector to its center and a tangent vector so as to
		// form a right-angled triangle with base (light radius, light dir) and
		// hypotenuse the tangent vector. Then generate a random ray towards
		// the light proportional to the solid angle of the cone defined by
		// the triangle.
		float lightDirLen = length(lightDir);
		float radius = SceneObject[objIndex].params.x;
		float tLen = sqrt(lightDirLen*lightDirLen + radius*radius);
		float cosThetaMax = lightDirLen / tLen;
		lightDir = normalize(lightDir);
		float4 shadowDir = rndConeDir(lightDir, cosThetaMax, rndSeed);

		// Check line of sight; if we hit anything other than the target or nothing reject light
		intersectWorld(hit->position, shadowDir, NUDGE_EPSILON, lightDirLen, &shadowHit);
		if(shadowHit.objIndex != objIndex && shadowHit.objIndex != NO_MATERIAL_HIT){
			continue;
		}

		// Radiance = 1/pi * cos(theta) * emissive * PDF
		light += material->emissive * dot(hit->normal, shadowDir) * 2.0f * (1.0f - cosThetaMax);

	}
	return light;
}

// Trace a ray and return the gathered color.
float4 traceRay(float4 rayOrigin, float4 rayDir, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	bool hitDiffuseObject = false;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

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

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == MATERIAL_LIGHT ){
			if ( !hitDiffuseObject ){
				rCol += mask * material->emissive;
			}
			break;
		}

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

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > MIN_BOUNCES_TO_USE_RR ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == MATERIAL_DIFFUSE ) {
			hitDiffuseObject = true;
			rCol += mask * getDirectLight(&hit, rndSeed);

			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else if ( material->type == MATERIAL_SPECULAR){
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		} else {
			// todo
			break;
		}

		++bounce;
	}

	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,
		const int seed
		){
	// 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 * seed, y * seed);

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

	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), &rndSeed);
	}

	// 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.
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
#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 NO_MATERIAL_HIT -1

// Limits
#define MAX_TRACE_STEPS 32
#define MAX_BOUNCES 4 // iris pro compilation runs out of memory if set to > 5
#define MIN_BOUNCES_TO_USE_RR 2

// 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 VEC1(x) (float4)(x, 0.0f, 0.0f, 0.0f)
#define VEC2(x,y) (float4)(x, y, 0.0f, 0.0f)
#define VEC3(x,y,z) (float4)(x,y,z,0.0f)
#define VEC4(x,y,z,w) (float4)(x,y,z,w)
#define ORIGIN(v) VEC3(v,v,v)

// Normal estimator generators
#define OBJECT_NORMAL_AT(estimatorFn, objIndex, point) \
	normalize( \
		(float4)( \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(NUDGE_EPSILON, 0,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(NUDGE_EPSILON, 0,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, NUDGE_EPSILON,0)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, NUDGE_EPSILON,0)), \
			estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point + VEC3(0, 0, NUDGE_EPSILON)) - estimatorFn(SceneObject[objIndex].origin,SceneObject[objIndex].params, point - VEC3(0, 0, NUDGE_EPSILON)), \
			0.0f \
		) \
	)

// Tracer structs
// ---------------------

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

// Supported material types
#define MATERIAL_DIFFUSE 0
#define MATERIAL_SPECULAR 1
#define MATERIAL_REFRACTIVE 2
#define MATERIAL_LIGHT 65535

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

// Supported object types
#define OBJECT_PLANE 0
#define OBJECT_SPHERE 1
#define OBJECT_BOX 2
#define OBJECT_TORUS 3

typedef struct {
	unsigned int type;
	unsigned int material;
	float4 origin;
	float4 params;
} Object;

// -----------------------
// Object and material definitions
// The following code will be autogenerated by the scene processor
// ------------------
__constant float4 sceneBgColor = (float4)(0.0f);

__constant Material SceneMaterial[] = {
	{MATERIAL_LIGHT, VEC3(0.0f,0.0f,0.0f), VEC3(60.0f,60.0f,60.0f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.25f, 0.25f)},
	{MATERIAL_DIFFUSE, VEC3(0.25f, 0.25f, 0.75f)},
	{MATERIAL_DIFFUSE, VEC3(0.75f, 0.75f, 0.75f)},
	{MATERIAL_SPECULAR, VEC3(0.999f, 0.999f, 0.999f)},
	{MATERIAL_REFRACTIVE, VEC3(0.999f, 0.999f, 0.999f)}
};

__constant Object SceneObject[] = {
	// Light
	{OBJECT_SPHERE, 0, VEC3(0.0f, 1.5f, 0.0f), VEC1(0.1f)},
	// Left wall
	{OBJECT_BOX, 1, VEC3(-2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Right wall
	{OBJECT_BOX, 2, VEC3(2.0f,0.0f,0.0f), VEC3(0.1f, 2.0f, 2.0f)},
	// Top wall
	{OBJECT_BOX, 3, VEC3(0.0f,2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Bottom wall
	{OBJECT_BOX, 3, VEC3(0.0f,-2.0f,0.0f), VEC3(2.0f, 0.1f, 2.0f)},
	// Back wall
	{OBJECT_BOX, 3, VEC3(0,0,-2), VEC3(2.0f, 2.0f, 0.1f)},
	// Mirror ball
	{OBJECT_SPHERE, 4, VEC3(-1.0f, -1.4f, -1.0f), VEC1(0.5f)},
	// Glass ball
	{OBJECT_SPHERE, 5, VEC3(1.4f, -1.4f, -0.5f), VEC1(0.5f)}
};

// -----------------------
// Random number and direction generators
// -----------------------
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;
}

// Return random direction on hemiphere around normal.
// This function has: PDF = cos(theta) / pi
float4 rndCosWeightedHemisphereDir(float4 normal, uint2 *seed) {
	float2 rnd  = clRand(seed);

	// Generate point on disk
	float rd = sqrt(rnd.x);
	float phi = 2.0f*M_PI*rnd.y;

	// Generate tangent, bi-tangent vectors
	float4 u = normalize(cross((fabs(normal.x) > .1f ? (float4)(0.0f, 1.0f, 0.0f, 0.0f) : (float4)(1.0f, 0.0f, 0.0f, 0.0f)), normal));
	float4 v = cross(normal,u);

	// Project disk point to unit hemisphere and rotate so that the normal points up
	return normalize(u * rd * cos(phi) + v * rd * sin(phi) + normal * sqrt(1 - rnd.x));
}

// -----------------------
// Distance estimators for supported primitives
// -----------------------
float planeSD(float4 origin, float4 dims, float4 point){
	dims = normalize(dims);
	return dot((point - origin).xyz, dims.xyz) + dims.w;
}
float sphereSD(float4 origin, float4 radius, float4 point){
	return length(point - origin) - radius.x;
}
float boxSD(float4 origin, float4 dims, float4 point){
	float3 d = fabs(point.xyz - origin.xyz) - dims.xyz;
	return fmin(VEC3_MAX_COMPONENT(d), 0.0f) + length(fmax(d, 0.0f));
}
float torusSD(float4 origin, float4 dims, float4 point){
	point -= origin;
	return length((float2)(length(point.xz) - dims.x, point.y)) - dims.y;
}

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

// Estimate normal at intersection point
void getNormal(Hit *hit){
	switch(SceneObject[hit->objIndex].type) {
		case OBJECT_PLANE:
			hit->normal = OBJECT_NORMAL_AT(planeSD, hit->objIndex, hit->position);
		break;
		case OBJECT_SPHERE:
			hit->normal = OBJECT_NORMAL_AT(sphereSD, hit->objIndex, hit->position);
		break;
		case OBJECT_BOX:
			hit->normal = OBJECT_NORMAL_AT(boxSD, hit->objIndex, hit->position);
		break;
		case OBJECT_TORUS:
			hit->normal = OBJECT_NORMAL_AT(torusSD, hit->objIndex, hit->position);
		break;
	}
}

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

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

		// Find nearest object/dist to point
		nearestDist = FLT_MAX;
		for(objIndex=0;objIndex<numObjects; objIndex++){
			switch(SceneObject[objIndex].type) {
				case OBJECT_PLANE:
					objDist = planeSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_SPHERE:
					objDist = sphereSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_BOX:
					objDist = boxSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
				case OBJECT_TORUS:
					objDist = torusSD(SceneObject[objIndex].origin, SceneObject[objIndex].params, point);
				break;
			}

			if(objDist < nearestDist){
				nearestDist = objDist;
				nearestObj = objIndex;
				nearestMat = SceneObject[objIndex].material;
			}
		}

		// 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, uint2 *rndSeed){
	Hit hit;
	__constant Material *material;
	float4 rCol = (float4)(0.0f);
	float4 mask = (float4)(1.0f);
	unsigned int bounce = 0;
	for(bounce=0;bounce<MAX_BOUNCES;bounce++){
		intersectWorld(rayOrigin, rayDir, NUDGE_EPSILON, FLT_MAX, &hit);

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

		// Get material at hit point
		material = &SceneMaterial[hit.matIndex];

		// If we hit a light just add its emissive property and stop
		if( material->type == MATERIAL_LIGHT ){
			rCol += mask * material->emissive;
			break;
		}

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

		// After some bounces apply a russian roulette to terminate long paths
		if( bounce > MIN_BOUNCES_TO_USE_RR ) {
			float maxComponent = VEC3_MAX_COMPONENT(material->diffuse);
			if( maxComponent == 0 || clRand(rndSeed).x > maxComponent ){
				break;
			}

			// Boost surviving paths by 1/rr_prob
			mask /= maxComponent;
		}

		// Mask outgoing reflectance by material diffuse property
		mask *= material->diffuse;

		// Next bounce starts at hit point
		rayOrigin = hit.position;

		if( material->type == MATERIAL_DIFFUSE ) {
			// We do importance sampling for diffuse rays
			rayDir = rndCosWeightedHemisphereDir(hit.normal, rndSeed);
		} else{
			// Reflect ray around normal
			rayDir = normalize(-2.0f * dot(hit.normal, rayDir) * hit.normal + rayDir);
		}

		++bounce;
	}

	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,
		const int seed
		){
	// 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 * seed, y * seed);

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

	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), &rndSeed);
	}

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


Correct type error in sign()

Correct type error in sign()

/* OpenCL built-in library: sign()

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

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#include "templates.h"

DEFINE_EXPR_V_V(sign,
                ({
                  isnan(a) ? (stype)0 :
                    copysign(a == (stype)0 ? (vtype)0 : (vtype)1, a);
                }))

/* OpenCL built-in library: sign()

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

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#include "templates.h"

DEFINE_EXPR_V_V(sign,
                ({
                  isnan(a) ? (stype)0 :
                    copysign(a == (stype)0 ? (stype)0 : (stype)1, a);
                }))


Support both LPR_OPT_printer and LPR_OPT_Printer (uppercase P) in lpr-wrapper.cl

#!/bin/bash

# lpr-wrapper: calls the user's preferred lpr, building the
# argument string from environment variables

opts=$( printenv | grep '^LPR_OPT_' )
space=${IFS:- }
while read opt; do
	opt_key=$( echo "$opt" | sed 's/=.*//' )
	opt_val=$( echo "$opt" | sed 's/.*=//' )
	case "$opt_key" in
		LPR_OPT_p) argstring="${argstring}${space}-p ${opt_val}" ;;
		LPR_OPT_staple) argstring="${argstring}${space}-staple" ;;
		LPR_OPT_printer|LPR_OPT_Printer) argstring="${argstring}${space}-P ${opt_val}" ;;
		LPR_OPT_filename*) #filenames="$filenames"$'\n'"$opt_val" ;;
			;;
		LPR_OPT_fullcmd) ;; # ignore this
		*)	echo "Unrecognised option key: $opt_key" 1>&2 ;;
	esac
done <<<"$opts"

#echo "$filenames" | echo xargs lpr ${argstring}
lpr ${argstring} "$@"

#!/bin/bash

# lpr-wrapper: calls the user's preferred lpr, building the
# argument string from environment variables

opts=$( printenv | grep '^LPR_OPT_' )
space=${IFS:- }
while read opt; do
	opt_key=$( echo "$opt" | sed 's/=.*//' )
	opt_val=$( echo "$opt" | sed 's/.*=//' )
	case "$opt_key" in
		LPR_OPT_p) argstring="${argstring}${space}-p ${opt_val}" ;;
		LPR_OPT_staple) argstring="${argstring}${space}-staple" ;;
		LPR_OPT_printer) argstring="${argstring}${space}-P ${opt_val}" ;;
		LPR_OPT_filename*) #filenames="$filenames"$'\n'"$opt_val" ;;
			;;
		LPR_OPT_fullcmd) ;; # ignore this
		*)	echo "Unrecognised option key: $opt_key" 1>&2 ;;
	esac
done <<<"$opts"

#echo "$filenames" | echo xargs lpr ${argstring}
lpr ${argstring} "$@"


use fast_distance instead of distance

use fast_distance instead of distance

// OpenCL Kernel
// https://www.khronos.org/files/opencl-1-2-quick-reference-card.pdf

kernel void convolve_image(__read_only image2d_t image,
                           __read_only image2d_t pattern,
                           __global __write_only float* output,
                           sampler_t sampler,
                           const int2 top_left_rc,   // image (row, column)
                           const int image_start_r,  // where in the image row to start writing (for partitions)
                           const int image_end_r)    // end row writing (for partitions)
{
   // From a birds-eye-view what this kernel does is to compute a similarity
   // measure almost like a convolve operation, but with a distance measure
   // that uses the alpha channel of the pattern as a mask (to ignore or
   // down-weight parts of the pattern) and further uses a squared distance
   // measure. One call to this method computes a single pixel
   // in the `output` image (`output` is our region-of-interest (ROI)). The
   // output just contains float values (NOT in scale 0..1) for how similar
   // pattern and image are at that pixel. Lower value mean "more similar".
   // `top_left_rc` defines to which pixel coordinate the (0, 0) pixel in the
   // output corresponds.

    // Position (row, column) of the pixel in the output image.
    // Note, that OpenCL has uses column-major ("fortran"-style) storage, so
    // in the end we have to access the pixel data (through a sampler) in
    // (column, row) order. But we stick to naming like "C"-style row-major
    // ordering and just do the flip in the two nested for-loops in the loop
    // variables c and r, which are then used according to OpenCL's convention.
    const int2 pos = {get_global_id(0), get_global_id(1)};

    // The width of the image
    const int image_width = get_image_width(image);

    // The pattern should be at least 3x3 and have an odd width and height
    const int pattern_half_w = (get_image_width(pattern)-1) / 2;
    const int pattern_half_h = (get_image_height(pattern)-1) / 2;

    // 4-tuple of the RGBA values from the pattern image and RRB of the image.
    float4 pattern_color = (float4)(0.0, 0.0, 0.0, 0.0);
    float4 image_color = (float4)(0.0, 0.0, 0.0, 0.0);

    // The (column, row) of the center pixel in the pattern is computed
    // Note, that later one we use this as (column, row), therefore we switch
    // already here the .s0 and .s1 assignment
    // (s0 is the first entry, s1 second)
    int2 pattern_pos_cr = (int2)(0, 0);
    pattern_pos_cr.s1 = -top_left_rc.s0 - pos.s0 + pattern_half_h; // row
    pattern_pos_cr.s0 = -top_left_rc.s1 - pos.s1 + pattern_half_w; // column

    // value for the output pixel we are going to compute in this kernel
    float value = 0.0;
    // the distance in the color-space between the image and the pattern
    float dist = 0.0f;
    const float max_dist = distance(1+1+1);

    // Go though all pixel of the pattern image
    for (int r = image_start_r; r < image_end_r; r++) {

        for (int c = 0; c < image_width; c++) {

            pattern_color = read_imagef(pattern, sampler, pattern_pos_cr + (int2)(c, r));

            if(pattern_color.w > 0.0f) {
                // Use the alpha value `.w` from an RGBA image (pattern) as
                // weight and compute the `distance` which is the squared
                // absolute difference
                image_color = read_imagef(image, sampler, (int2)(c, r));
                dist = fast_distance(pattern_color.xyz, image_color.xyz);
                value += (1-pattern_color.w) * max_dist + pattern_color.w * dist;
            } else {
                // Just add a constant that represents the maximal possible
                // difference between image and pattern for a single pixel.
                // E.g. from white (1,1,1) to black (0,0,0).
                value += max_dist;
            }
        }
    }

    output[pos.s0 * get_global_size(1) + pos.s1] = value;
}

// OpenCL Kernel
// https://www.khronos.org/files/opencl-1-2-quick-reference-card.pdf

kernel void convolve_image(__read_only image2d_t image,
                           __read_only image2d_t pattern,
                           __global __write_only float* output,
                           sampler_t sampler,
                           const int2 top_left_rc,   // image (row, column)
                           const int image_start_r,  // where in the image row to start writing (for partitions)
                           const int image_end_r)    // end row writing (for partitions)
{
   // From a birds-eye-view what this kernel does is to compute a similarity
   // measure almost like a convolve operation, but with a distance measure
   // that uses the alpha channel of the pattern as a mask (to ignore or
   // down-weight parts of the pattern) and further uses a squared distance
   // measure. One call to this method computes a single pixel
   // in the `output` image (`output` is our region-of-interest (ROI)). The
   // output just contains float values (NOT in scale 0..1) for how similar
   // pattern and image are at that pixel. Lower value mean "more similar".
   // `top_left_rc` defines to which pixel coordinate the (0, 0) pixel in the
   // output corresponds.

    // Position (row, column) of the pixel in the output image.
    // Note, that OpenCL has uses column-major ("fortran"-style) storage, so
    // in the end we have to access the pixel data (through a sampler) in
    // (column, row) order. But we stick to naming like "C"-style row-major
    // ordering and just do the flip in the two nested for-loops in the loop
    // variables c and r, which are then used according to OpenCL's convention.
    const int2 pos = {get_global_id(0), get_global_id(1)};

    // The width of the image
    const int image_width = get_image_width(image);

    // The pattern should be at least 3x3 and have an odd width and height
    const int pattern_half_w = (get_image_width(pattern)-1) / 2;
    const int pattern_half_h = (get_image_height(pattern)-1) / 2;

    // 4-tuple of the RGBA values from the pattern image and RRB of the image.
    float4 pattern_color = (float4)(0.0, 0.0, 0.0, 0.0);
    float4 image_color = (float4)(0.0, 0.0, 0.0, 0.0);

    // The (column, row) of the center pixel in the pattern is computed
    // Note, that later one we use this as (column, row), therefore we switch
    // already here the .s0 and .s1 assignment
    // (s0 is the first entry, s1 second)
    int2 pattern_pos_cr = (int2)(0, 0);
    pattern_pos_cr.s1 = -top_left_rc.s0 - pos.s0 + pattern_half_h; // row
    pattern_pos_cr.s0 = -top_left_rc.s1 - pos.s1 + pattern_half_w; // column

    // value for the output pixel we are going to compute in this kernel
    float value = 0.0;
    // the distance in the color-space between the image and the pattern
    float dist = 0.0f;
    const float max_dist = distance(1+1+1);

    // Go though all pixel of the pattern image
    for (int r = image_start_r; r < image_end_r; r++) {

        for (int c = 0; c < image_width; c++) {

            pattern_color = read_imagef(pattern, sampler, pattern_pos_cr + (int2)(c, r));

            if(pattern_color.w > 0.0f) {
                // Use the alpha value `.w` from an RGBA image (pattern) as
                // weight and compute the `distance` which is the squared
                // absolute difference
                image_color = read_imagef(image, sampler, (int2)(c, r));
                dist = distance(pattern_color.xyz, image_color.xyz);
                value += (1-pattern_color.w) * max_dist + pattern_color.w * dist;
            } else {
                // Just add a constant that represents the maximal possible
                // difference between image and pattern for a single pixel.
                // E.g. from white (1,1,1) to black (0,0,0).
                value += max_dist;
            }
        }
    }

    output[pos.s0 * get_global_size(1) + pos.s1] = value;
}


Remove shebang from lpp.cl

Remove shebang from lpp.cl


Removing debug line

Removing debug line

#!/usr/local/bin/clisp

(defun encrypt (password) (string password))

(write-line (encrypt "hello"))


Code clean-up

Code clean-up


OpenCL: Fixed rayTriangleIntersection function order

/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#ifndef RAY_TRIANGLE_INTERSECTION_CL
#define RAY_TRIANGLE_INTERSECTION_CL

// Intersects a ray with a triangle defined by a vertex and two edges:
// float3 e1 = v1-v0;
// float3 e2 = v2-v0;
// If intersecting, t is the point of intersection along the ray
bool rayTriangleEdgeIntersection(float3 o, float3 dir, float3 v0, float3 e1, float3 e2, float* t) {
    float3 p = cross(dir, e2);
    float a = dot(e1, p);
    if (a > -0.00001f && a < 0.00001f) {
        return false;
    }
    float f = 1.f / a;
    // Distance from v0 to ray origin
    float3 s = o - v0;
    // u parameter
    float u = f * dot(s, p);
    if (u < 0.f || u > 1.f) {
        return false;
    }

    float3 q = cross(s, e1);
    float v = f * dot(dir, q);

    if (v < 0.f || u + v > 1.f) {
        return false;
    }
    // Compute the intersection point t

    *t = f * dot(e2, q);

    if (*t > 0.00001f) {
        return true;
    } else {
        return false;
    }
}
// Intersects a ray with a triangle defined by three vertices.
// If intersecting, t is the point of intersection along the ray
bool rayTriangleIntersection(float3 o, float3 dir, float3 v0, float3 v1, float3 v2, float* t) {
    float3 e1 = v1-v0;
    float3 e2 = v2-v0;
    return rayTriangleEdgeIntersection(o, dir, v0, e1, e2, t);
}

#endif

/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#ifndef RAY_TRIANGLE_INTERSECTION_CL
#define RAY_TRIANGLE_INTERSECTION_CL

// Intersects a ray with a triangle defined by three vertices.
// If intersecting, t is the point of intersection along the ray
bool rayTriangleIntersection(float3 o, float3 dir, float3 v0, float3 v1, float3 v2, float* t) {
    float3 e1 = v1-v0;
    float3 e2 = v2-v0;
    return rayTriangleEdgeIntersection(o, dir, v0, e1, e2, t);
}
// Intersects a ray with a triangle defined by a vertex and two edges:
// float3 e1 = v1-v0;
// float3 e2 = v2-v0;
// If intersecting, t is the point of intersection along the ray
bool rayTriangleEdgeIntersection(float3 o, float3 dir, float3 v0, float3 e1, float3 e2, float* t) {
    float3 p = cross(dir, e2);
    float a = dot(e1, p);
    if (a > -0.00001f && a < 0.00001f) {
        return false;
    }
    float f = 1.f/a;
    // Distance from v0 to ray origin
    float3 s = o-v0;
    // u parameter
    float u = f * dot(s, p);
    if (u < 0.f || u > 1.f) {
        return false;
    }

    float3 q = cross(s, e1);
    float v = f * dot(dir, q);

    if (v < 0.f || u+v > 1.f) {
        return false;
    }
    // Compute the intersection point t

    *t = f * dot(e2, q);

    if (*t > 0.00001f) {
        return true;
    } else {
        return false;
    }
}

#endif