[cl] Fix bug and reduce divergence from intersection code

#ifndef INTERSECT_KERNEL_CL
#define INTERSECT_KERNEL_CL

#define MAX_ITERATIONS 10000

#define BVH_MAX_STACK_SIZE 32

#define BVH_IS_LEAF(node) (node.leftChild.w <= 0)
#define BVH_LEFT_CHILD(node) (node.leftChild.w)
#define BVH_RIGHT_CHILD(node) (node.rightChild.w)
#define BVH_TRIANGLE_INDEX(node) (-node.firstTriIndex.w)
#define BVH_TRIANGLE_COUNT(node) (node.numTriangles.w)
#define BVH_MESH_INSTANCE_ID(node) (-node.meshInstance.w)

#define RAY_PACKET_SIZE 64
#define HALF_RAY_PACKET_SIZE RAY_PACKET_SIZE / 2

#define RAY_VISIT_NO_NODE 0
#define RAY_VISIT_LEFT_NODE 1
#define RAY_VISIT_RIGHT_NODE 2
#define RAY_VISIT_BOTH_NODES 3

void printIntersection(Intersection *intersection);

// Test for ray intersections with scene geometry and set an ouput flag to indicate
// intersections. This method does not calculate any intersection details so its
// cheaper to use for general intersection queries (e.g light occlusion)
__kernel void rayIntersectionTest(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag
		){

	int globalId = get_global_id(0);
	if(globalId >= *numRays){
		return;
	}

	int stackIndex;
	int meshBvhStackStartIndex;
	uint nodeStack[BVH_MAX_STACK_SIZE];
	BvhNode curNode;
	BvhNode childNodes[2];
	int meshInstanceId;
	MeshInstance meshInstance;

	// triangle intersection vars
	float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Setup stack
	stackIndex = 0;
	meshBvhStackStartIndex = -1;
	curNode = bvhNodes[0];

	int wantLeft;
	int wantRight;
	int gotHit = 0;

	while(stackIndex > -1){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
				meshInstance = meshInstances[meshInstanceId];

				// Push bottom BVH root to the stack and keep a record
				// of the current stack so that we know when we exit the
				// bottom BVH
				meshBvhStackStartIndex = stackIndex;
				nodeStack[stackIndex++] = meshInstance.bvhRoot;

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					v0 = vertexList[vIndex].xyz;
					edge01 = vertexList[vIndex+1].xyz - v0;
					edge02 = vertexList[vIndex+2].xyz - v0;

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) < INTERSECTION_EPSILON){
						continue;
					}

					float invDet = native_recip(det);

					// Calculate barycentric coords
					float3 tVec = ray.origin.xyz - v0;
					float u = dot(tVec, pVec) * invDet;
					if( u < 0.0f || u > 1.0f ){
						continue;
					}

					float3 qVec = cross(tVec, edge01);
					float v = dot(ray.dir.xyz, qVec) * invDet;
					if( v < 0.0f || u+v > 1.0f ){
						continue;
					}

					float t = dot(edge02, qVec) * invDet;
					if (t > INTERSECTION_EPSILON){
						gotHit = 1;
						stackIndex = -1;
						break;
					}
				}
			}

			wantLeft = 0;
			wantRight = 0;
		} else {
			// Read children
			childNodes[0] = bvhNodes[BVH_LEFT_CHILD(curNode)];
			childNodes[1] = bvhNodes[BVH_RIGHT_CHILD(curNode)];

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			wantRight = rHitDist < FLT_MAX ? 1 : 0;
		}

		if( wantLeft && wantRight ){
			nodeStack[stackIndex++] = wantLeft ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
			curNode = wantLeft ? childNodes[0] : childNodes[1];
		} else if(wantLeft || wantRight){
			curNode = wantLeft ? childNodes[0] : childNodes[1];
		} else if(stackIndex <= 0) {
			stackIndex = -1;
		} else {
			if(stackIndex == meshBvhStackStartIndex){
				// If we exited from a bottom bvh tree we need to restore our ray
				ray.origin.xyz = origRayOrigin;
				ray.dir.xyz = origRayDir;
				meshBvhStackStartIndex = -1;
			}

			// Pop the next node off the stack
			curNode = bvhNodes[nodeStack[--stackIndex]];
		}
	}

	// Update hit flag
	hitFlag[globalId] = gotHit;
}

// Test for ray intersections with scene geometry. Sets an ouput flag to indicate
// intersections and also emits intersection data for any found intersections.
__kernel void rayIntersectionQuery(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag,
		__global Intersection* intersections
		){

	Intersection intersection;
	intersection.wuvt.w = FLT_MAX;

	int globalId = get_global_id(0);
	if(globalId >= *numRays){
		return;
	}

	int stackIndex;
	int meshBvhStackStartIndex;
	uint nodeStack[BVH_MAX_STACK_SIZE];
	BvhNode curNode;
	BvhNode childNodes[2];
	int meshInstanceId;
	MeshInstance meshInstance;

	// triangle intersection vars
	float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Setup stack
	stackIndex = 0;
	meshBvhStackStartIndex = -1;
	curNode = bvhNodes[0];

	int wantLeft;
	int wantRight;
	while(stackIndex > -1){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
				meshInstance = meshInstances[meshInstanceId];

				// Push bottom BVH root to the stack and keep a record
				// of the current stack so that we know when we exit the
				// bottom BVH
				meshBvhStackStartIndex = stackIndex;
				nodeStack[stackIndex++] = meshInstance.bvhRoot;

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					v0 = vertexList[vIndex].xyz;
					edge01 = vertexList[vIndex+1].xyz - v0;
					edge02 = vertexList[vIndex+2].xyz - v0;

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) < INTERSECTION_EPSILON){
						continue;
					}

					float invDet = native_recip(det);

					// Calculate barycentric coords
					float3 tVec = ray.origin.xyz - v0;
					float u = dot(tVec, pVec) * invDet;
					if( u < 0.0f || u > 1.0f ){
						continue;
					}

					float3 qVec = cross(tVec, edge01);
					float v = dot(ray.dir.xyz, qVec) * invDet;
					if( v < 0.0f || u+v > 1.0f ){
						continue;
					}

					float t = dot(edge02, qVec) * invDet;
					if (t > INTERSECTION_EPSILON && t < intersection.wuvt.w){
						intersection.wuvt = (float4)(
								1.0f - (u+v),
								u,
								v,
								t
								);
						intersection.triIndex = vIndex / 3;
						intersection.meshInstance = meshInstanceId;
					}
				}
			}

			wantLeft = 0;
			wantRight = 0;
		} else {
			// Read children
			childNodes[0] = bvhNodes[BVH_LEFT_CHILD(curNode)];
			childNodes[1] = bvhNodes[BVH_RIGHT_CHILD(curNode)];

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			wantRight = rHitDist < FLT_MAX ? 1 : 0;
		}

		if( wantLeft && wantRight ){
			nodeStack[stackIndex++] = wantLeft ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
			curNode = wantLeft ? childNodes[0] : childNodes[1];
		} else if(wantLeft || wantRight){
			curNode = wantLeft ? childNodes[0] : childNodes[1];
		}
		else if(stackIndex <= 0){
			stackIndex = -1;
		} else {
			if(stackIndex == meshBvhStackStartIndex){
				// If we exited from a bottom bvh tree we need to restore our ray
				ray.origin.xyz = origRayOrigin;
				ray.dir.xyz = origRayDir;
				meshBvhStackStartIndex = -1;
			}

			// Pop the next node off the stack
			curNode = bvhNodes[nodeStack[--stackIndex]];
		}
	}

	// Update hit flag
	hitFlag[globalId] = intersection.wuvt.w < FLT_MAX ? 1 : 0;
	intersections[globalId] = intersection;
}

// Test for ray packet intersections with scene geometry. Sets an ouput flag to
// indicate intersections and also emits intersection data for any found intersections.
// This kernel operates on a bundle of RAY_PACKET_SIZE rays in parallel. Stack
// operations are handled by the first thread in the local thread group.
__kernel void packetIntersectionQuery(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag,
		__global Intersection* intersections
		){

	Intersection intersection;
	intersection.wuvt.w = FLT_MAX;

	int globalId = get_global_id(0);
	if (globalId >= *numRays){
		return;
	}
	int localId = get_local_id(0);

	// Shared data for all threads
	__local int stackIndex;
	__local int meshBvhStackStartIndex;
	__local uint nodeStack[BVH_MAX_STACK_SIZE];
	__local BvhNode curNode;
	__local  BvhNode childNodes[2];
	__local int scratchMemory[RAY_PACKET_SIZE];
	__local int meshInstanceId;
	__local MeshInstance meshInstance;

	// Shared triangle intersection vars
	__local float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Thread 0 manages the stack; set initial values
	if(localId == 0){
		stackIndex = 0;
		meshBvhStackStartIndex = -1;
		curNode = bvhNodes[0];
	}

	for(int iteration=0;iteration<MAX_ITERATIONS;iteration++){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				if( localId == 0 ){
					meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
					meshInstance = meshInstances[meshInstanceId];

					// Push bottom BVH root to the stack and keep a record
					// of the current stack so that we know when we exit the
					// bottom BVH
					meshBvhStackStartIndex = stackIndex;
					nodeStack[stackIndex++] = meshInstance.bvhRoot;
				}
				barrier(CLK_LOCAL_MEM_FENCE);

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					// Thread 0 fetches triangle data into local memory
					if(localId == 0 ){
						v0 = vertexList[vIndex].xyz;
						edge01 = vertexList[vIndex+1].xyz - v0;
						edge02 = vertexList[vIndex+2].xyz - v0;
					}
					barrier(CLK_LOCAL_MEM_FENCE);

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) >= INTERSECTION_EPSILON){
						float invDet = native_recip(det);

						// Calculate barycentric coords
						float3 tVec = ray.origin.xyz - v0;
						float u = dot(tVec, pVec) * invDet;
						float3 qVec = cross(tVec, edge01);
						float v = dot(ray.dir.xyz, qVec) * invDet;
						float t = dot(edge02, qVec) * invDet;

						if (u >= 0.0f &&
								u <= 1.0f &&
								v >= 0.0f &&
								u+v <= 1.0f &&
								t > INTERSECTION_EPSILON &&
								t < intersection.wuvt.w){
							intersection.wuvt = (float4)(
									1.0f - (u+v),
									u,
									v,
									t
									);
							intersection.triIndex = vIndex / 3;
							intersection.meshInstance = meshInstanceId;
						}
					}
					barrier(CLK_LOCAL_MEM_FENCE);
				}
			}
		} else {
			// Read children in parallel
			if( localId < 2 ){
				childNodes[localId] = bvhNodes[localId == 0 ? BVH_LEFT_CHILD(curNode) : BVH_RIGHT_CHILD(curNode)];

				// Clear the first 4 scratchMemory locations
				scratchMemory[localId] = 0;
				scratchMemory[localId+2] = 0;
			}

			barrier(CLK_LOCAL_MEM_FENCE);

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// If scratchMemory[i] is TRUE then at least one ray wants to:
			// [0] visit none of the nodes
			// [1] visit only left node
			// [2] visit only right node
			// [3] visit both nodes
			int wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			int wantRight = rHitDist < FLT_MAX ? 1 : 0;
			//printf("[%d] wantLeft: %d, wantRight: %d\n", localId, wantLeft, wantRight);
			scratchMemory[2*wantRight + wantLeft] = 1;

			// Wait for all threads to encode their traversal preference
			barrier(CLK_LOCAL_MEM_FENCE);

			if( scratchMemory[RAY_VISIT_BOTH_NODES] || (scratchMemory[RAY_VISIT_LEFT_NODE] && scratchMemory[RAY_VISIT_RIGHT_NODE])){
				// For each thread, set its scratchMemory location to -1 if
				// ray prefers the left node and to 1 if the ray prefers the right node.
				scratchMemory[localId] = wantLeft || lHitDist < rHitDist ? -1 : 1;
				//printf("[%2d] scratchMemory -> %d\n", localId, scratchMemory[localId]);
				barrier(CLK_LOCAL_MEM_FENCE);

				// run a parallel reduction on scratchMemory. We are using
				// sequential addressing to avoid bank conflicts
				// see: https://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf
				for (int s=HALF_RAY_PACKET_SIZE; s>0; s>>=1) {
					if (localId < s) {
						scratchMemory[localId] += scratchMemory[localId + s];
					}
					barrier(CLK_LOCAL_MEM_FENCE);
				}

				// scratchMemory[0] sign indicates which node should we visit first
				if( localId == 0 ){
					nodeStack[stackIndex++] = scratchMemory[0] < 1 ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
					curNode = scratchMemory[0] < 1 ? childNodes[0] : childNodes[1];
				}

				barrier(CLK_LOCAL_MEM_FENCE);
				continue;
			} else if(scratchMemory[RAY_VISIT_LEFT_NODE] || scratchMemory[RAY_VISIT_RIGHT_NODE]) {
				if( localId == 0 ){
					curNode = scratchMemory[1] ? childNodes[0] : childNodes[1];
				}

				barrier(CLK_LOCAL_MEM_FENCE);
				continue;
			}
		}

		if(stackIndex == 0){
			// We are done
			hitFlag[globalId] = intersection.wuvt.w < FLT_MAX ? 1 : 0;
			intersections[globalId] = intersection;
			return;
		} else if(stackIndex == meshBvhStackStartIndex){
			// If we exited from a bottom bvh tree we need to restore our ray
			ray.origin.xyz = origRayOrigin;
			ray.dir.xyz = origRayDir;

			if(localId == 0 ){
				meshBvhStackStartIndex = -1;
			}
		}

		barrier(CLK_LOCAL_MEM_FENCE);

		// Pop the next node pointer off the stack and read it into local memory
		if( localId == 0 ){
			curNode = bvhNodes[nodeStack[--stackIndex]];
		}

		barrier(CLK_LOCAL_MEM_FENCE);
	}
}

void printIntersection(Intersection *inter){
	printf("[tid: %03d] intersection (barycentric: %2.2v3hlf, t: %f, meshInstance: %d, triIndex: %d)\n",
			get_global_id(0),
			inter->wuvt.xyz,
			inter->wuvt.w,
			inter->meshInstance,
			inter->triIndex
		  );
}

#endif

#ifndef INTERSECT_KERNEL_CL
#define INTERSECT_KERNEL_CL

#define MAX_ITERATIONS 10000

#define BVH_MAX_STACK_SIZE 32

#define BVH_IS_LEAF(node) (node.leftChild.w <= 0)
#define BVH_LEFT_CHILD(node) (node.leftChild.w)
#define BVH_RIGHT_CHILD(node) (node.rightChild.w)
#define BVH_TRIANGLE_INDEX(node) (-node.firstTriIndex.w)
#define BVH_TRIANGLE_COUNT(node) (node.numTriangles.w)
#define BVH_MESH_INSTANCE_ID(node) (-node.meshInstance.w)

#define RAY_PACKET_SIZE 64
#define HALF_RAY_PACKET_SIZE RAY_PACKET_SIZE / 2

#define RAY_VISIT_NO_NODE 0
#define RAY_VISIT_LEFT_NODE 1
#define RAY_VISIT_RIGHT_NODE 2
#define RAY_VISIT_BOTH_NODES 3

void printIntersection(Intersection *intersection);

// Test for ray intersections with scene geometry and set an ouput flag to indicate
// intersections. This method does not calculate any intersection details so its
// cheaper to use for general intersection queries (e.g light occlusion)
__kernel void rayIntersectionTest(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag
		){

	Intersection intersection;
	intersection.wuvt.w = FLT_MAX;

	int globalId = get_global_id(0);
	if(globalId >= *numRays){
		return;
	}

	int stackIndex;
	int meshBvhStackStartIndex;
	uint nodeStack[BVH_MAX_STACK_SIZE];
	BvhNode curNode;
	BvhNode childNodes[2];
	int meshInstanceId;
	MeshInstance meshInstance;

	// triangle intersection vars
	float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Setup stack
	stackIndex = 0;
	meshBvhStackStartIndex = -1;
	curNode = bvhNodes[0];

	for(int iteration=0;iteration<MAX_ITERATIONS;iteration++){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
				meshInstance = meshInstances[meshInstanceId];

				// Push bottom BVH root to the stack and keep a record
				// of the current stack so that we know when we exit the
				// bottom BVH
				meshBvhStackStartIndex = stackIndex;
				nodeStack[stackIndex++] = meshInstance.bvhRoot;

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					v0 = vertexList[vIndex].xyz;
					edge01 = vertexList[vIndex+1].xyz - v0;
					edge02 = vertexList[vIndex+2].xyz - v0;

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) < INTERSECTION_EPSILON){
						continue;
					}

					float invDet = native_recip(det);

					// Calculate barycentric coords
					float3 tVec = ray.origin.xyz - v0;
					float u = dot(tVec, pVec) * invDet;
					if( u < 0.0f || u > 1.0f ){
						continue;
					}

					float3 qVec = cross(tVec, edge01);
					float v = dot(ray.dir.xyz, qVec) * invDet;
					if( v < 0.0f || u+v > 1.0f ){
						continue;
					}

					float t = dot(edge02, qVec) * invDet;
					if (t > INTERSECTION_EPSILON && t < intersection.wuvt.w){
						hitFlag[globalId] = 1;
						return;
					}
				}
			}
		} else {
			// Read children
			childNodes[0] = bvhNodes[BVH_LEFT_CHILD(curNode)];
			childNodes[1] = bvhNodes[BVH_RIGHT_CHILD(curNode)];

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			int wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			int wantRight = rHitDist < FLT_MAX ? 1 : 0;

			if( wantLeft && wantRight ){
				nodeStack[stackIndex++] = wantLeft ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
				curNode = wantLeft ? childNodes[0] : childNodes[1];
				continue;
			} else if(wantLeft || wantRight){
				curNode = wantLeft ? childNodes[0] : childNodes[1];
				continue;
			}
		}

		if(stackIndex == 0){
			// We are done
			hitFlag[globalId] = 0;
			return;
		} else if(stackIndex == meshBvhStackStartIndex){
			// If we exited from a bottom bvh tree we need to restore our ray
			ray.origin.xyz = origRayOrigin;
			ray.dir.xyz = origRayDir;
			meshBvhStackStartIndex = -1;
		}

		// Pop the next node off the stack
		curNode = bvhNodes[nodeStack[--stackIndex]];
	}
}

// Test for ray intersections with scene geometry. Sets an ouput flag to indicate
// intersections and also emits intersection data for any found intersections.
__kernel void rayIntersectionQuery(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag,
		__global Intersection* intersections
		){

	Intersection intersection;
	intersection.wuvt.w = FLT_MAX;

	int globalId = get_global_id(0);
	if(globalId >= *numRays){
		return;
	}

	int stackIndex;
	int meshBvhStackStartIndex;
	uint nodeStack[BVH_MAX_STACK_SIZE];
	BvhNode curNode;
	BvhNode childNodes[2];
	int meshInstanceId;
	MeshInstance meshInstance;

	// triangle intersection vars
	float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Setup stack
	stackIndex = 0;
	meshBvhStackStartIndex = -1;
	curNode = bvhNodes[0];

	for(int iteration=0;iteration<MAX_ITERATIONS;iteration++){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
				meshInstance = meshInstances[meshInstanceId];

				// Push bottom BVH root to the stack and keep a record
				// of the current stack so that we know when we exit the
				// bottom BVH
				meshBvhStackStartIndex = stackIndex;
				nodeStack[stackIndex++] = meshInstance.bvhRoot;

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					v0 = vertexList[vIndex].xyz;
					edge01 = vertexList[vIndex+1].xyz - v0;
					edge02 = vertexList[vIndex+2].xyz - v0;

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) < INTERSECTION_EPSILON){
						continue;
					}

					float invDet = native_recip(det);

					// Calculate barycentric coords
					float3 tVec = ray.origin.xyz - v0;
					float u = dot(tVec, pVec) * invDet;
					if( u < 0.0f || u > 1.0f ){
						continue;
					}

					float3 qVec = cross(tVec, edge01);
					float v = dot(ray.dir.xyz, qVec) * invDet;
					if( v < 0.0f || u+v > 1.0f ){
						continue;
					}

					float t = dot(edge02, qVec) * invDet;
					if (t > INTERSECTION_EPSILON && t < intersection.wuvt.w){
						intersection.wuvt = (float4)(
								1.0f - (u+v),
								u,
								v,
								t
								);
						intersection.triIndex = vIndex / 3;
						intersection.meshInstance = meshInstanceId;
					}
				}
			}
		} else {
			// Read children
			childNodes[0] = bvhNodes[BVH_LEFT_CHILD(curNode)];
			childNodes[1] = bvhNodes[BVH_RIGHT_CHILD(curNode)];

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			int wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			int wantRight = rHitDist < FLT_MAX ? 1 : 0;

			if( wantLeft && wantRight ){
				nodeStack[stackIndex++] = wantLeft ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
				curNode = wantLeft ? childNodes[0] : childNodes[1];

				continue;
			} else if(wantLeft || wantRight){
				curNode = wantLeft ? childNodes[0] : childNodes[1];
				continue;
			}
		}

		if(stackIndex == 0){
			// We are done
			hitFlag[globalId] = intersection.wuvt.w < FLT_MAX ? 1 : 0;
			intersections[globalId] = intersection;
			return;
		} else if(stackIndex == meshBvhStackStartIndex){
			// If we exited from a bottom bvh tree we need to restore our ray
			ray.origin.xyz = origRayOrigin;
			ray.dir.xyz = origRayDir;
			meshBvhStackStartIndex = -1;
		}

		// Pop the next node off the stack
		curNode = bvhNodes[nodeStack[--stackIndex]];
	}
}

// Test for ray packet intersections with scene geometry. Sets an ouput flag to
// indicate intersections and also emits intersection data for any found intersections.
// This kernel operates on a bundle of RAY_PACKET_SIZE rays in parallel. Stack
// operations are handled by the first thread in the local thread group.
__kernel void packetIntersectionQuery(
		__global Ray* rays,
		__global const int *numRays,
		__global BvhNode* bvhNodes,
		__global MeshInstance* meshInstances,
		__global float4* vertexList,
		__global int* hitFlag,
		__global Intersection* intersections
		){

	Intersection intersection;
	intersection.wuvt.w = FLT_MAX;

	int globalId = get_global_id(0);
	if (globalId >= *numRays){
		return;
	}
	int localId = get_local_id(0);

	// Shared data for all threads
	__local int stackIndex;
	__local int meshBvhStackStartIndex;
	__local uint nodeStack[BVH_MAX_STACK_SIZE];
	__local BvhNode curNode;
	__local  BvhNode childNodes[2];
	__local int scratchMemory[RAY_PACKET_SIZE];
	__local int meshInstanceId;
	__local MeshInstance meshInstance;

	// Shared triangle intersection vars
	__local float3 v0, edge01, edge02;

	// Node bbox and leaf primitive intersection vars
	float3 invDir, tmin, tmax, rmin, rmax;
	float minmax, maxmin;
	int triStartIndex, numTriangles;

	// Fetch ray
	Ray	ray = rays[globalId];
	float3 origRayOrigin = ray.origin.xyz;
	float3 origRayDir = ray.dir.xyz;

	// Thread 0 manages the stack; set initial values
	if(localId == 0){
		stackIndex = 0;
		meshBvhStackStartIndex = -1;
		curNode = bvhNodes[0];
	}

	for(int iteration=0;iteration<MAX_ITERATIONS;iteration++){
		if(BVH_IS_LEAF(curNode)){
			numTriangles = BVH_TRIANGLE_COUNT(curNode);

			// If this is a top BVH leaf we need to load the mesh instance
			// and transform all rays using its matrix.
			if( numTriangles == 0 ){
				if( localId == 0 ){
					meshInstanceId = BVH_MESH_INSTANCE_ID(curNode);
					meshInstance = meshInstances[meshInstanceId];

					// Push bottom BVH root to the stack and keep a record
					// of the current stack so that we know when we exit the
					// bottom BVH
					meshBvhStackStartIndex = stackIndex;
					nodeStack[stackIndex++] = meshInstance.bvhRoot;
				}
				barrier(CLK_LOCAL_MEM_FENCE);

				// Transform rays without translating ray direction vector
				ray.origin.xyz = mul4x1(ray.origin.xyz, meshInstance.transformMat0, meshInstance.transformMat1, meshInstance.transformMat2, meshInstance.transformMat3);
				ray.dir.xyz = mul3x1(ray.dir.xyz, meshInstance.transformMat0.xyz, meshInstance.transformMat1.xyz, meshInstance.transformMat2.xyz);
			} else {
				// Intersect with all triangles using the Moller-Trumbore algorithm
				triStartIndex = BVH_TRIANGLE_INDEX(curNode);
				for(int vIndex = triStartIndex * 3; vIndex < (triStartIndex + numTriangles)*3;vIndex+=3){
					// Thread 0 fetches triangle data into local memory
					if(localId == 0 ){
						v0 = vertexList[vIndex].xyz;
						edge01 = vertexList[vIndex+1].xyz - v0;
						edge02 = vertexList[vIndex+2].xyz - v0;
					}
					barrier(CLK_LOCAL_MEM_FENCE);

					float3 pVec = cross(ray.dir.xyz, edge02);
					float det = dot(edge01, pVec);

					if (fabs(det) >= INTERSECTION_EPSILON){
						float invDet = native_recip(det);

						// Calculate barycentric coords
						float3 tVec = ray.origin.xyz - v0;
						float u = dot(tVec, pVec) * invDet;
						float3 qVec = cross(tVec, edge01);
						float v = dot(ray.dir.xyz, qVec) * invDet;
						float t = dot(edge02, qVec) * invDet;

						if (u >= 0.0f &&
								u <= 1.0f &&
								v >= 0.0f &&
								u+v <= 1.0f &&
								t > INTERSECTION_EPSILON &&
								t < intersection.wuvt.w){
							intersection.wuvt = (float4)(
									1.0f - (u+v),
									u,
									v,
									t
									);
							intersection.triIndex = vIndex / 3;
							intersection.meshInstance = meshInstanceId;
						}
					}
					barrier(CLK_LOCAL_MEM_FENCE);
				}
			}
		} else {
			// Read children in parallel
			if( localId < 2 ){
				childNodes[localId] = bvhNodes[localId == 0 ? BVH_LEFT_CHILD(curNode) : BVH_RIGHT_CHILD(curNode)];

				// Clear the first 4 scratchMemory locations
				scratchMemory[localId] = 0;
				scratchMemory[localId+2] = 0;
			}

			barrier(CLK_LOCAL_MEM_FENCE);

			// Check for intersection with first child
			invDir = native_recip(ray.dir.xyz);
			tmin = (childNodes[0].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[0].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float lHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// Check for intersection with second child
			tmin = (childNodes[1].minExtent.xyz - ray.origin.xyz) * invDir;
			tmax = (childNodes[1].maxExtent.xyz - ray.origin.xyz) * invDir;
			rmin = fmin(tmin, tmax);
			rmax = fmax(tmin, tmax);
			minmax = fmin( fmin(rmax.x, rmax.y), rmax.z);
			maxmin = fmax( fmax(rmin.x, rmin.y), rmin.z);
			float rHitDist = minmax < 0 || maxmin > minmax ? FLT_MAX : (maxmin >= ray.origin.w ? FLT_MAX : maxmin);

			// If scratchMemory[i] is TRUE then at least one ray wants to:
			// [0] visit none of the nodes
			// [1] visit only left node
			// [2] visit only right node
			// [3] visit both nodes
			int wantLeft = lHitDist < FLT_MAX ? 1 : 0;
			int wantRight = rHitDist < FLT_MAX ? 1 : 0;
			//printf("[%d] wantLeft: %d, wantRight: %d\n", localId, wantLeft, wantRight);
			scratchMemory[2*wantRight + wantLeft] = 1;

			// Wait for all threads to encode their traversal preference
			barrier(CLK_LOCAL_MEM_FENCE);

			if( scratchMemory[RAY_VISIT_BOTH_NODES] || (scratchMemory[RAY_VISIT_LEFT_NODE] && scratchMemory[RAY_VISIT_RIGHT_NODE])){
				// For each thread, set its scratchMemory location to -1 if
				// ray prefers the left node and to 1 if the ray prefers the right node.
				scratchMemory[localId] = wantLeft || lHitDist < rHitDist ? -1 : 1;
				//printf("[%2d] scratchMemory -> %d\n", localId, scratchMemory[localId]);
				barrier(CLK_LOCAL_MEM_FENCE);

				// run a parallel reduction on scratchMemory. We are using
				// sequential addressing to avoid bank conflicts
				// see: https://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf
				for (int s=HALF_RAY_PACKET_SIZE; s>0; s>>=1) {
					if (localId < s) {
						scratchMemory[localId] += scratchMemory[localId + s];
					}
					barrier(CLK_LOCAL_MEM_FENCE);
				}

				// scratchMemory[0] sign indicates which node should we visit first
				if( localId == 0 ){
					nodeStack[stackIndex++] = scratchMemory[0] < 1 ? BVH_RIGHT_CHILD(curNode) : BVH_LEFT_CHILD(curNode);
					curNode = scratchMemory[0] < 1 ? childNodes[0] : childNodes[1];
				}

				barrier(CLK_LOCAL_MEM_FENCE);
				continue;
			} else if(scratchMemory[RAY_VISIT_LEFT_NODE] || scratchMemory[RAY_VISIT_RIGHT_NODE]) {
				if( localId == 0 ){
					curNode = scratchMemory[1] ? childNodes[0] : childNodes[1];
				}

				barrier(CLK_LOCAL_MEM_FENCE);
				continue;
			}
		}

		if(stackIndex == 0){
			// We are done
			hitFlag[globalId] = intersection.wuvt.w < FLT_MAX ? 1 : 0;
			intersections[globalId] = intersection;
			return;
		} else if(stackIndex == meshBvhStackStartIndex){
			// If we exited from a bottom bvh tree we need to restore our ray
			ray.origin.xyz = origRayOrigin;
			ray.dir.xyz = origRayDir;

			if(localId == 0 ){
				meshBvhStackStartIndex = -1;
			}
		}

		barrier(CLK_LOCAL_MEM_FENCE);

		// Pop the next node pointer off the stack and read it into local memory
		if( localId == 0 ){
			curNode = bvhNodes[nodeStack[--stackIndex]];
		}

		barrier(CLK_LOCAL_MEM_FENCE);
	}
}

void printIntersection(Intersection *inter){
	printf("[tid: %03d] intersection (barycentric: %2.2v3hlf, t: %f, meshInstance: %d, triIndex: %d)\n",
			get_global_id(0),
			inter->wuvt.xyz,
			inter->wuvt.w,
			inter->meshInstance,
			inter->triIndex
		  );
}

#endif


fixed extrapolation in ocl::adaptiveBilateralFilter

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Harris Gasparakis, harris.gasparakis@amd.com
//    Xiaopeng Fu, fuxiaopeng2222@163.com
//    Yao Wang, bitwangyaoyao@gmail.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other oclMaterials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#ifdef BORDER_CONSTANT
#define ELEM(i,l_edge,r_edge,elem1,elem2) (i)<(l_edge) | (i) >= (r_edge) ? (elem1) : (elem2)
#elif defined BORDER_REPLICATE
#define EXTRAPOLATE(x, maxV) \
    { \
        x = max(min(x, maxV - 1), 0); \
    }
#elif defined BORDER_WRAP
#define EXTRAPOLATE(x, maxV) \
    { \
        if (x < 0) \
            x -= ((x - maxV + 1) / maxV) * maxV; \
        if (x >= maxV) \
            x %= maxV; \
    }
#elif defined(BORDER_REFLECT) || defined(BORDER_REFLECT_101)
#define EXTRAPOLATE_(x, maxV, delta) \
    { \
        if (maxV == 1) \
            x = 0; \
        else \
            do \
            { \
                if ( x < 0 ) \
                    x = -x - 1 + delta; \
                else \
                    x = maxV - 1 - (x - maxV) - delta; \
            } \
            while (x >= maxV || x < 0); \
    }
#ifdef BORDER_REFLECT
#define EXTRAPOLATE(x, maxV) EXTRAPOLATE_(x, maxV, 0)
#else
#define EXTRAPOLATE(x, maxV) EXTRAPOLATE_(x, maxV, 1)
#endif
#else
#error No extrapolation method
#endif

__kernel void
edgeEnhancingFilter_C4_D0(
    __global const uchar4 * restrict src,
    __global uchar4 *dst,
    float alpha,
    int src_offset,
    int src_whole_rows,
    int src_whole_cols,
    int src_step,
    int dst_offset,
    int dst_rows,
    int dst_cols,
    int dst_step,
    __global const float* lut,
    int lut_step)
{
    int col = get_local_id(0);
    const int gX = get_group_id(0);
    const int gY = get_group_id(1);

    int src_x_off = (src_offset % src_step) >> 2;
    int src_y_off = src_offset / src_step;
    int dst_x_off = (dst_offset % dst_step) >> 2;
    int dst_y_off = dst_offset / dst_step;

    int startX = gX * (THREADS-ksX+1) - anX + src_x_off;
    int startY = (gY * (1+EXTRA)) - anY + src_y_off;

    int dst_startX = gX * (THREADS-ksX+1) + dst_x_off;
    int dst_startY = (gY * (1+EXTRA)) + dst_y_off;

    int posX = dst_startX - dst_x_off + col;
    int posY = (gY * (1+EXTRA))	;

    __local uchar4 data[ksY+EXTRA][THREADS];

    float4 tmp_sum[1+EXTRA];
    for(int tmpint = 0; tmpint < 1+EXTRA; tmpint++)
        tmp_sum[tmpint] = (float4)(0,0,0,0);

#ifdef BORDER_CONSTANT
    bool con;
    uchar4 ss;
    for(int j = 0;	j < ksY+EXTRA; j++)
    {
        con = (startX+col >= 0 && startX+col < src_whole_cols && startY+j >= 0 && startY+j < src_whole_rows);
        int cur_col = clamp(startX + col, 0, src_whole_cols);
        if (con)
            ss = src[(startY+j)*(src_step>>2) + cur_col];

        data[j][col] = con ? ss : (uchar4)0;
    }
#else
    for(int j= 0; j < ksY+EXTRA; j++)
    {
        int selected_row = startY+j, selected_col = startX+col;
        EXTRAPOLATE(selected_row, src_whole_rows)
        EXTRAPOLATE(selected_col, src_whole_cols)

        data[j][col] = src[selected_row * (src_step>>2) + selected_col];
    }
#endif

    barrier(CLK_LOCAL_MEM_FENCE);

    float4 var[1+EXTRA];

#if VAR_PER_CHANNEL
    float4 weight;
    float4 totalWeight = (float4)(0,0,0,0);
#else
    float weight;
    float totalWeight = 0;
#endif

    int4 currValCenter;
    int4 currWRTCenter;

    int4 sumVal = 0;
    int4 sumValSqr = 0;

    if(col < (THREADS-(ksX-1)))
    {
        int4 currVal;
        int howManyAll = (2*anX+1)*(ksY);

        //find variance of all data
        int startLMj;
        int endLMj ;
#if CALCVAR
        // Top row: don't sum the very last element
        for(int extraCnt = 0; extraCnt <=EXTRA; extraCnt++)
        {
            startLMj = extraCnt;
            endLMj =  ksY+extraCnt-1;
            sumVal =0;
            sumValSqr=0;
            for(int j = startLMj; j < endLMj; j++)
                for(int i=-anX; i<=anX; i++)
                {
                    currVal = convert_int4(data[j][col+anX+i]);

                    sumVal += currVal;
                    sumValSqr += mul24(currVal, currVal);
                }

            var[extraCnt] = convert_float4( ( (sumValSqr * howManyAll)- mul24(sumVal , sumVal) ) ) /  ( (float)(howManyAll*howManyAll) ) ;
#else
        var[extraCnt] = (float4)(900.0, 900.0, 900.0, 0.0);
#endif
        }

        for(int extraCnt = 0; extraCnt <= EXTRA; extraCnt++)
        {

            // top row: include the very first element, even on first time
            startLMj = extraCnt;
            // go all the way, unless this is the last local mem chunk,
            // then stay within limits - 1
            endLMj =  extraCnt + ksY;

            // Top row: don't sum the very last element
            currValCenter = convert_int4( data[ (startLMj + endLMj)/2][col+anX] );

            for(int j = startLMj, lut_j = 0; j < endLMj; j++, lut_j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
#if FIXED_WEIGHT
#if VAR_PER_CHANNEL
                    weight.x = 1.0f;
                    weight.y = 1.0f;
                    weight.z = 1.0f;
                    weight.w = 1.0f;
#else
                    weight = 1.0f;
#endif
#else
                    currVal = convert_int4(data[j][col+anX+i]);
                    currWRTCenter = currVal-currValCenter;

#if VAR_PER_CHANNEL
                    weight = var[extraCnt] / (var[extraCnt] + convert_float4(currWRTCenter * currWRTCenter)) *
                        (float4)(lut[lut_j*lut_step+anX+i]);
#else
                    weight = 1.0f/(1.0f+( mul24(currWRTCenter.x, currWRTCenter.x) + mul24(currWRTCenter.y, currWRTCenter.y) +
                        mul24(currWRTCenter.z, currWRTCenter.z))/(var.x+var.y+var.z));
#endif
#endif
                    tmp_sum[extraCnt] += convert_float4(data[j][col+anX+i]) * weight;
                    totalWeight += weight;
                }
            }

            tmp_sum[extraCnt] /= totalWeight;

            if(posX >= 0 && posX < dst_cols && (posY+extraCnt) >= 0 && (posY+extraCnt) < dst_rows)
                dst[(dst_startY+extraCnt) * (dst_step>>2)+ dst_startX + col] = convert_uchar4(tmp_sum[extraCnt]);

#if VAR_PER_CHANNEL
            totalWeight = (float4)(0,0,0,0);
#else
            totalWeight = 0;
#endif
        }
    }
}

__kernel void
edgeEnhancingFilter_C1_D0(
    __global const uchar * restrict src,
    __global uchar *dst,
    float alpha,
    int src_offset,
    int src_whole_rows,
    int src_whole_cols,
    int src_step,
    int dst_offset,
    int dst_rows,
    int dst_cols,
    int dst_step,
    __global const float * lut,
    int lut_step)
{
    int col = get_local_id(0);
    const int gX = get_group_id(0);
    const int gY = get_group_id(1);

    int src_x_off = (src_offset % src_step);
    int src_y_off = src_offset / src_step;
    int dst_x_off = (dst_offset % dst_step);
    int dst_y_off = dst_offset / dst_step;

    int startX = gX * (THREADS-ksX+1) - anX + src_x_off;
    int startY = (gY * (1+EXTRA)) - anY + src_y_off;

    int dst_startX = gX * (THREADS-ksX+1) + dst_x_off;
    int dst_startY = (gY * (1+EXTRA)) + dst_y_off;

    int posX = dst_startX - dst_x_off + col;
    int posY = (gY * (1+EXTRA))	;

    __local uchar data[ksY+EXTRA][THREADS];

    float tmp_sum[1+EXTRA];
    for(int tmpint = 0; tmpint < 1+EXTRA; tmpint++)
    {
        tmp_sum[tmpint] = (float)(0);
    }

#ifdef BORDER_CONSTANT
    bool con;
    uchar ss;
    for(int j = 0;	j < ksY+EXTRA; j++)
    {
        con = (startX+col >= 0 && startX+col < src_whole_cols && startY+j >= 0 && startY+j < src_whole_rows);

        int cur_col = clamp(startX + col, 0, src_whole_cols);
        if(con)
        {
            ss = src[(startY+j)*(src_step) + cur_col];
        }

        data[j][col] = con ? ss : 0;
    }
#else
    for(int j= 0; j < ksY+EXTRA; j++)
    {
        int selected_row = startY+j, selected_col = startX+col;
        EXTRAPOLATE(selected_row, src_whole_rows)
        EXTRAPOLATE(selected_col, src_whole_cols)

        data[j][col] = src[selected_row * (src_step) + selected_col];
    }
#endif

    barrier(CLK_LOCAL_MEM_FENCE);

    float var[1+EXTRA];

    float weight;
    float totalWeight = 0;

    int currValCenter;
    int currWRTCenter;

    int sumVal = 0;
    int sumValSqr = 0;

    if(col < (THREADS-(ksX-1)))
    {
        int currVal;

        int howManyAll = (2*anX+1)*(ksY);

        //find variance of all data
        int startLMj;
        int endLMj;
#if CALCVAR
        // Top row: don't sum the very last element
        for(int extraCnt=0; extraCnt<=EXTRA; extraCnt++)
        {
            startLMj = extraCnt;
            endLMj =  ksY+extraCnt-1;
            sumVal = 0;
            sumValSqr =0;
            for(int j = startLMj; j < endLMj; j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
                    currVal	= (uint)(data[j][col+anX+i])	;

                    sumVal += currVal;
                    sumValSqr += mul24(currVal, currVal);
                }
            }
            var[extraCnt] = (float)( ( (sumValSqr * howManyAll)- mul24(sumVal , sumVal) ) ) /  ( (float)(howManyAll*howManyAll) ) ;
#else
        var[extraCnt] = (float)(900.0);
#endif
        }

        for(int extraCnt = 0; extraCnt <= EXTRA; extraCnt++)
        {

            // top row: include the very first element, even on first time
            startLMj = extraCnt;
            // go all the way, unless this is the last local mem chunk,
            // then stay within limits - 1
            endLMj =  extraCnt + ksY;

            // Top row: don't sum the very last element
            currValCenter = (int)( data[ (startLMj + endLMj)/2][col+anX] );

            for(int j = startLMj, lut_j = 0; j < endLMj; j++, lut_j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
#if FIXED_WEIGHT
                    weight = 1.0f;
#else
                    currVal	= (int)(data[j][col+anX+i])	;
                    currWRTCenter = currVal-currValCenter;

                    weight = var[extraCnt] / (var[extraCnt] + (float)mul24(currWRTCenter,currWRTCenter)) * lut[lut_j*lut_step+anX+i] ;
#endif
                    tmp_sum[extraCnt] += (float)(data[j][col+anX+i] * weight);
                    totalWeight += weight;
                }
            }

            tmp_sum[extraCnt] /= totalWeight;

            if(posX >= 0 && posX < dst_cols && (posY+extraCnt) >= 0 && (posY+extraCnt) < dst_rows)
            {
                dst[(dst_startY+extraCnt) * (dst_step)+ dst_startX + col] = (uchar)(tmp_sum[extraCnt]);
            }

            totalWeight = 0;
        }
    }
}

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                           License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2010-2013, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
//    Harris Gasparakis, harris.gasparakis@amd.com
//    Xiaopeng Fu, fuxiaopeng2222@163.com
//    Yao Wang, bitwangyaoyao@gmail.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other oclMaterials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#ifdef BORDER_REPLICATE
//BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh
#define ADDR_L(i, l_edge, r_edge)  ((i) <  (l_edge) ? (l_edge)   : (i))
#define ADDR_R(i, r_edge, addr)    ((i) >= (r_edge) ? (r_edge)-1 : (addr))
#define ADDR_H(i, t_edge, b_edge)  ((i) <  (t_edge) ? (t_edge)   :(i))
#define ADDR_B(i, b_edge, addr)    ((i) >= (b_edge) ? (b_edge)-1 :(addr))
#endif

#ifdef BORDER_REFLECT
//BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb
#define ADDR_L(i, l_edge, r_edge)  ((i) <  (l_edge) ? -(i)-1               : (i))
#define ADDR_R(i, r_edge, addr)    ((i) >= (r_edge) ? -(i)-1+((r_edge)<<1) : (addr))
#define ADDR_H(i, t_edge, b_edge)  ((i) <  (t_edge) ? -(i)-1 : (i))
#define ADDR_B(i, b_edge, addr)    ((i) >= (b_edge) ? -(i)-1+((b_edge)<<1) : (addr))
#endif

#ifdef BORDER_REFLECT_101
//BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba
#define ADDR_L(i, l_edge, r_edge)  ((i) <  (l_edge) ? -(i)                 : (i))
#define ADDR_R(i, r_edge, addr)    ((i) >= (r_edge) ? -(i)-2+((r_edge)<<1) : (addr))
#define ADDR_H(i, t_edge, b_edge)  ((i) <  (t_edge) ? -(i)                 : (i))
#define ADDR_B(i, b_edge, addr)    ((i) >= (b_edge) ? -(i)-2+((b_edge)<<1) : (addr))
#endif

//blur function does not support BORDER_WRAP
#ifdef BORDER_WRAP
//BORDER_WRAP:          cdefgh|abcdefgh|abcdefg
#define ADDR_L(i, l_edge, r_edge)  ((i) <  (l_edge) ? (i)+(r_edge) : (i))
#define ADDR_R(i, r_edge, addr)    ((i) >= (r_edge) ? (i)-(r_edge) : (addr))
#define ADDR_H(i, t_edge, b_edge)  ((i) <  (t_edge) ? (i)+(b_edge) : (i))
#define ADDR_B(i, b_edge, addr)    ((i) >= (b_edge) ? (i)-(b_edge) : (addr))
#endif

__kernel void
edgeEnhancingFilter_C4_D0(
    __global const uchar4 * restrict src,
    __global uchar4 *dst,
    float alpha,
    int src_offset,
    int src_whole_rows,
    int src_whole_cols,
    int src_step,
    int dst_offset,
    int dst_rows,
    int dst_cols,
    int dst_step,
    __global const float* lut,
    int lut_step)
{
    int col = get_local_id(0);
    const int gX = get_group_id(0);
    const int gY = get_group_id(1);

    int src_x_off = (src_offset % src_step) >> 2;
    int src_y_off = src_offset / src_step;
    int dst_x_off = (dst_offset % dst_step) >> 2;
    int dst_y_off = dst_offset / dst_step;

    int startX = gX * (THREADS-ksX+1) - anX + src_x_off;
    int startY = (gY * (1+EXTRA)) - anY + src_y_off;

    int dst_startX = gX * (THREADS-ksX+1) + dst_x_off;
    int dst_startY = (gY * (1+EXTRA)) + dst_y_off;

    int posX = dst_startX - dst_x_off + col;
    int posY = (gY * (1+EXTRA))	;

    __local uchar4 data[ksY+EXTRA][THREADS];

    float4 tmp_sum[1+EXTRA];
    for(int tmpint = 0; tmpint < 1+EXTRA; tmpint++)
    {
        tmp_sum[tmpint] = (float4)(0,0,0,0);
    }

#ifdef BORDER_CONSTANT
    bool con;
    uchar4 ss;
    for(int j = 0;	j < ksY+EXTRA; j++)
    {
        con = (startX+col >= 0 && startX+col < src_whole_cols && startY+j >= 0 && startY+j < src_whole_rows);

        int cur_col = clamp(startX + col, 0, src_whole_cols);
        if(con)
        {
            ss = src[(startY+j)*(src_step>>2) + cur_col];
        }

        data[j][col] = con ? ss : (uchar4)0;
    }
#else
    for(int j= 0; j < ksY+EXTRA; j++)
    {
        int selected_row;
        int selected_col;
        selected_row = ADDR_H(startY+j, 0, src_whole_rows);
        selected_row = ADDR_B(startY+j, src_whole_rows, selected_row);

        selected_col = ADDR_L(startX+col, 0, src_whole_cols);
        selected_col = ADDR_R(startX+col, src_whole_cols, selected_col);

        data[j][col] = src[selected_row * (src_step>>2) + selected_col];
    }
#endif

    barrier(CLK_LOCAL_MEM_FENCE);

    float4 var[1+EXTRA];

#if VAR_PER_CHANNEL
    float4 weight;
    float4 totalWeight = (float4)(0,0,0,0);
#else
    float weight;
    float totalWeight = 0;
#endif

    int4 currValCenter;
    int4 currWRTCenter;

    int4 sumVal = 0;
    int4 sumValSqr = 0;

    if(col < (THREADS-(ksX-1)))
    {
        int4 currVal;

        int howManyAll = (2*anX+1)*(ksY);

        //find variance of all data
        int startLMj;
        int endLMj ;
#if CALCVAR
        // Top row: don't sum the very last element
        for(int extraCnt = 0; extraCnt <=EXTRA; extraCnt++)
        {
            startLMj = extraCnt;
            endLMj =  ksY+extraCnt-1;
            sumVal =0;
            sumValSqr=0;
            for(int j = startLMj; j < endLMj; j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
                    currVal	= convert_int4(data[j][col+anX+i])	;

                    sumVal += currVal;
                    sumValSqr += mul24(currVal, currVal);
                }
            }
            var[extraCnt] = convert_float4( ( (sumValSqr * howManyAll)- mul24(sumVal , sumVal) ) ) /  ( (float)(howManyAll*howManyAll) ) ;
#else
        var[extraCnt] = (float4)(900.0, 900.0, 900.0, 0.0);
#endif
        }

        for(int extraCnt = 0; extraCnt <= EXTRA; extraCnt++)
        {

            // top row: include the very first element, even on first time
            startLMj = extraCnt;
            // go all the way, unless this is the last local mem chunk,
            // then stay within limits - 1
            endLMj =  extraCnt + ksY;

            // Top row: don't sum the very last element
            currValCenter = convert_int4( data[ (startLMj + endLMj)/2][col+anX] );

            for(int j = startLMj, lut_j = 0; j < endLMj; j++, lut_j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
#if FIXED_WEIGHT
#if VAR_PER_CHANNEL
                    weight.x = 1.0f;
                    weight.y = 1.0f;
                    weight.z = 1.0f;
                    weight.w = 1.0f;
#else
                    weight = 1.0f;
#endif
#else
                    currVal	= convert_int4(data[j][col+anX+i])	;
                    currWRTCenter = currVal-currValCenter;

#if VAR_PER_CHANNEL
                    weight = var[extraCnt] / (var[extraCnt] + convert_float4(currWRTCenter * currWRTCenter)) * (float4)(lut[lut_j*lut_step+anX+i]);
                    //weight.x = var[extraCnt].x / ( var[extraCnt].x + (float) mul24(currWRTCenter.x , currWRTCenter.x) ) ;
                    //weight.y = var[extraCnt].y / ( var[extraCnt].y + (float) mul24(currWRTCenter.y , currWRTCenter.y) ) ;
                    //weight.z = var[extraCnt].z / ( var[extraCnt].z + (float) mul24(currWRTCenter.z , currWRTCenter.z) ) ;
                    //weight.w = 0;
#else
                    weight = 1.0f/(1.0f+( mul24(currWRTCenter.x, currWRTCenter.x) + mul24(currWRTCenter.y, currWRTCenter.y) +  mul24(currWRTCenter.z, currWRTCenter.z))/(var.x+var.y+var.z));
#endif
#endif
                    tmp_sum[extraCnt] += convert_float4(data[j][col+anX+i]) * weight;
                    totalWeight += weight;
                }
            }

            tmp_sum[extraCnt] /= totalWeight;

            if(posX >= 0 && posX < dst_cols && (posY+extraCnt) >= 0 && (posY+extraCnt) < dst_rows)
            {
                dst[(dst_startY+extraCnt) * (dst_step>>2)+ dst_startX + col] = convert_uchar4(tmp_sum[extraCnt]);
            }

#if VAR_PER_CHANNEL
            totalWeight = (float4)(0,0,0,0);
#else
            totalWeight = 0;
#endif
        }
    }
}

__kernel void
edgeEnhancingFilter_C1_D0(
    __global const uchar * restrict src,
    __global uchar *dst,
    float alpha,
    int src_offset,
    int src_whole_rows,
    int src_whole_cols,
    int src_step,
    int dst_offset,
    int dst_rows,
    int dst_cols,
    int dst_step,
    __global const float * lut,
    int lut_step)
{
    int col = get_local_id(0);
    const int gX = get_group_id(0);
    const int gY = get_group_id(1);

    int src_x_off = (src_offset % src_step);
    int src_y_off = src_offset / src_step;
    int dst_x_off = (dst_offset % dst_step);
    int dst_y_off = dst_offset / dst_step;

    int startX = gX * (THREADS-ksX+1) - anX + src_x_off;
    int startY = (gY * (1+EXTRA)) - anY + src_y_off;

    int dst_startX = gX * (THREADS-ksX+1) + dst_x_off;
    int dst_startY = (gY * (1+EXTRA)) + dst_y_off;

    int posX = dst_startX - dst_x_off + col;
    int posY = (gY * (1+EXTRA))	;

    __local uchar data[ksY+EXTRA][THREADS];

    float tmp_sum[1+EXTRA];
    for(int tmpint = 0; tmpint < 1+EXTRA; tmpint++)
    {
        tmp_sum[tmpint] = (float)(0);
    }

#ifdef BORDER_CONSTANT
    bool con;
    uchar ss;
    for(int j = 0;	j < ksY+EXTRA; j++)
    {
        con = (startX+col >= 0 && startX+col < src_whole_cols && startY+j >= 0 && startY+j < src_whole_rows);

        int cur_col = clamp(startX + col, 0, src_whole_cols);
        if(con)
        {
            ss = src[(startY+j)*(src_step) + cur_col];
        }

        data[j][col] = con ? ss : 0;
    }
#else
    for(int j= 0; j < ksY+EXTRA; j++)
    {
        int selected_row;
        int selected_col;
        selected_row = ADDR_H(startY+j, 0, src_whole_rows);
        selected_row = ADDR_B(startY+j, src_whole_rows, selected_row);

        selected_col = ADDR_L(startX+col, 0, src_whole_cols);
        selected_col = ADDR_R(startX+col, src_whole_cols, selected_col);

        data[j][col] = src[selected_row * (src_step) + selected_col];
    }
#endif

    barrier(CLK_LOCAL_MEM_FENCE);

    float var[1+EXTRA];

    float weight;
    float totalWeight = 0;

    int currValCenter;
    int currWRTCenter;

    int sumVal = 0;
    int sumValSqr = 0;

    if(col < (THREADS-(ksX-1)))
    {
        int currVal;

        int howManyAll = (2*anX+1)*(ksY);

        //find variance of all data
        int startLMj;
        int endLMj;
#if CALCVAR
        // Top row: don't sum the very last element
        for(int extraCnt=0; extraCnt<=EXTRA; extraCnt++)
        {
            startLMj = extraCnt;
            endLMj =  ksY+extraCnt-1;
            sumVal = 0;
            sumValSqr =0;
            for(int j = startLMj; j < endLMj; j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
                    currVal	= (uint)(data[j][col+anX+i])	;

                    sumVal += currVal;
                    sumValSqr += mul24(currVal, currVal);
                }
            }
            var[extraCnt] = (float)( ( (sumValSqr * howManyAll)- mul24(sumVal , sumVal) ) ) /  ( (float)(howManyAll*howManyAll) ) ;
#else
        var[extraCnt] = (float)(900.0);
#endif
        }

        for(int extraCnt = 0; extraCnt <= EXTRA; extraCnt++)
        {

            // top row: include the very first element, even on first time
            startLMj = extraCnt;
            // go all the way, unless this is the last local mem chunk,
            // then stay within limits - 1
            endLMj =  extraCnt + ksY;

            // Top row: don't sum the very last element
            currValCenter = (int)( data[ (startLMj + endLMj)/2][col+anX] );

            for(int j = startLMj, lut_j = 0; j < endLMj; j++, lut_j++)
            {
                for(int i=-anX; i<=anX; i++)
                {
#if FIXED_WEIGHT
                    weight = 1.0f;
#else
                    currVal	= (int)(data[j][col+anX+i])	;
                    currWRTCenter = currVal-currValCenter;

                    weight = var[extraCnt] / (var[extraCnt] + (float)mul24(currWRTCenter,currWRTCenter)) * lut[lut_j*lut_step+anX+i] ;
#endif
                    tmp_sum[extraCnt] += (float)(data[j][col+anX+i] * weight);
                    totalWeight += weight;
                }
            }

            tmp_sum[extraCnt] /= totalWeight;

            if(posX >= 0 && posX < dst_cols && (posY+extraCnt) >= 0 && (posY+extraCnt) < dst_rows)
            {
                dst[(dst_startY+extraCnt) * (dst_step)+ dst_startX + col] = (uchar)(tmp_sum[extraCnt]);
            }

            totalWeight = 0;
        }
    }
}


WGSm and WGSn now have to be explicitly set by the caller.

#ifndef ACCUMULATOR
    #define ACCUMULATOR double
#endif

// ================= Sum reduction =============================================

inline ACCUMULATOR work_group_reduction_sum (const ACCUMULATOR value) {

    uint local_id = get_local_id(0);

    __local ACCUMULATOR lacc[WGS];
    lacc[local_id] = value;

    work_group_barrier(CLK_LOCAL_MEM_FENCE);

    ACCUMULATOR pacc = value;
    uint i = get_local_size(0);
    while (i > 0) {
        bool include_odd = (i > ((i >> 1) << 1)) && (local_id == ((i >> 1) - 1));
        i >>= 1;
        if (include_odd) {
            pacc += lacc[local_id + i + 1];
        }
        if (local_id < i) {
            pacc += lacc[local_id + i];
            lacc[local_id] = pacc;
        }
        work_group_barrier(CLK_LOCAL_MEM_FENCE);
    }

    return lacc[0];
}

inline REAL work_group_reduction_sum_2 (const REAL value) {

    uint local_row = get_local_id(0);
    uint local_col = get_local_id(1);
    uint local_m = get_local_size(0);

    __local REAL lacc[WGSm * WGSn];
    lacc[local_row + local_col * local_m] = value;

    work_group_barrier(CLK_LOCAL_MEM_FENCE);

    REAL pacc = value;
    uint i = get_local_size(1);
    while (i > 0) {
        bool include_odd = (i > ((i >> 1) << 1)) && (local_col == ((i >> 1) - 1));
        i >>= 1;
        if (include_odd) {
            pacc += lacc[local_row + (local_col + i + 1) * local_m];
        }
        if (local_col < i) {
            pacc += lacc[local_row + (local_col + i) * local_m];
            lacc[local_row + local_col * local_m] = pacc;
        }
        work_group_barrier(CLK_LOCAL_MEM_FENCE);
    }

    return lacc[local_row];

}

#ifndef ACCUMULATOR
    #define ACCUMULATOR double
#endif

#ifndef WGS
    #define WGS 256
#endif

#ifndef WGSm
#define WGSm 16
#endif

#ifndef WGSn
#define WGSn 16
#endif

// ================= Sum reduction =============================================

inline ACCUMULATOR work_group_reduction_sum (const ACCUMULATOR value) {

    uint local_id = get_local_id(0);

    __local ACCUMULATOR lacc[WGS];
    lacc[local_id] = value;

    work_group_barrier(CLK_LOCAL_MEM_FENCE);

    ACCUMULATOR pacc = value;
    uint i = get_local_size(0);
    while (i > 0) {
        bool include_odd = (i > ((i >> 1) << 1)) && (local_id == ((i >> 1) - 1));
        i >>= 1;
        if (include_odd) {
            pacc += lacc[local_id + i + 1];
        }
        if (local_id < i) {
            pacc += lacc[local_id + i];
            lacc[local_id] = pacc;
        }
        work_group_barrier(CLK_LOCAL_MEM_FENCE);
    }

    return lacc[0];
}

inline REAL work_group_reduction_sum_2 (const REAL value) {

    uint local_row = get_local_id(0);
    uint local_col = get_local_id(1);
    uint local_m = get_local_size(0);

    __local REAL lacc[WGSm * WGSn];
    lacc[local_row + local_col * local_m] = value;

    work_group_barrier(CLK_LOCAL_MEM_FENCE);

    REAL pacc = value;
    uint i = get_local_size(1);
    while (i > 0) {
        bool include_odd = (i > ((i >> 1) << 1)) && (local_col == ((i >> 1) - 1));
        i >>= 1;
        if (include_odd) {
            pacc += lacc[local_row + (local_col + i + 1) * local_m];
        }
        if (local_col < i) {
            pacc += lacc[local_row + (local_col + i) * local_m];
            lacc[local_row + local_col * local_m] = pacc;
        }
        work_group_barrier(CLK_LOCAL_MEM_FENCE);
    }

    return lacc[local_row];

}


ajout déplacement sphere

ajout déplacement sphere

#define WINX 1920
#define WINY 1080
#define BACKCOLOR 0x00000000
#define SCOLOR 0x000000FF
#define PCOLOR 0x00FF0000

float3			sub_vect(float3 v1, float3 v2)
{
	float3		res;

	res.x = v1.x - v2.x;
	res.y = v1.y - v2.y;
	res.z = v1.z - v2.z;
	return (res);
}

float3			add_vect(float3 v1, float3 v2)
{
	float3		res;

	res.x = v1.x + v2.x;
	res.y = v1.y + v2.y;
	res.z = v1.z + v2.z;
	return (res);
}

float			norme_vect(float3 v)
{
	return (sqrt(v.x * v.x + v.y * v.y + v.z * v.z));
}

float3			normalize_vect(float3 v)
{
	float3		res;
	int			id = 1 / sqrt(v.x * v.x + v.y * v.y + v.z * v.z);

	res.x = v.x * id;
	res.y = v.y * id;
	res.z = v.z * id;
	return (res);
}

float			dot_vect(float3 v1, float3 v2)
{
	return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}

float3			mult_fvect(float m, float3 v)
{
	float3		res;

	res.x = m * v.x;
	res.y = m * v.y;
	res.z = m * v.z;
	return (res);
}

float3			get_abc(float radius, float3 ray, float3 origin)
{
	float3		abc;

	abc.x = dot_vect(ray, ray);
	abc.y = 2 * dot_vect(ray, origin);
	abc.z = dot_vect(origin, origin) - (radius * radius);
	return (abc);
}

float			inter_sphere(float radius, float3 ray, float3 cam_origin, float3 sphere_origin)
{
	float3		abc;
	float		d;
	float		res1;
	float		res2;

	cam_origin.x -= sphere_origin.x;
	cam_origin.y -= sphere_origin.y;
	cam_origin.z -= sphere_origin.z;
	abc = get_abc(radius, ray, cam_origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (-1.0);
	res1 = -(abc[1] + sqrt(d));
	res2 = -(abc[1] - sqrt(d));
	if (res1 < res2)
		return(res1);
	return(res2);
}

float			inter_plan(float3 plan_origin, float3 plan_normale, float3 ray, float3 cam_origin)
{
	float		t;

	t = dot_vect(ray, plan_normale);
	if (t < 0.0000001)
		return (-1.0);
	t = dot_vect(sub_vect(plan_origin, cam_origin), plan_normale) / t;
	if (t < 0)
		return (-1.0);
	return (t);
}

float			modh_vect(int x)
{
	return ((float)((float)x - (WINX / 2.0)) / (WINX / 2.0));
}

float			modv_vect(int y)
{
	return ((float)(((float)y - (WINY / 2.0)) / (WINY / 2.0) * WINY / WINX));
}

float3			get_ray(float3 n, float3 v, float3 h, int x, int y)
{
	float3		res;

	h = mult_fvect(modh_vect(x), h);
	v = mult_fvect(modv_vect(y), v);
	res = add_vect(n, v);
	res = add_vect(res, h);
	return (res);
}

__kernel void	ray_trace(__global char *output)
{
	int		id = get_global_id(0);
	int		x = id % WINX;
	int		y = id / WINX;

	// CAM
	float3	cam_origin;
	cam_origin.x = 0;
	cam_origin.y = 0;
	cam_origin.z = -10;
	float3	hor;
	hor.x = 0.6;
	hor.y = 0;
	hor.z = 0;
	float3	vert;
	vert.x = 0;
	vert.y = 0.6;
	vert.z = 0;
	float3	cam_dir;
	cam_dir.x = 0;
	cam_dir.y = 0;
	cam_dir.z = 1;

	// SPHERE
	float3	boule_origin;
	boule_origin.x = 1;
	boule_origin.y = -1;
	boule_origin.z = 3;
	float	radius = 2;

	// PLANE
	float3	plan_origin;
	plan_origin.x = -100;
	plan_origin.y = 0;
	plan_origin.z = 0;
	float3	plan_normale;
	plan_normale.x = 0;
	plan_normale.y = 0;
	plan_normale.z = 1;

	float3 ray = get_ray(cam_dir, vert, hor, x ,y);
	float plan = inter_plan(plan_origin, plan_normale, ray, cam_origin);
	float sphere = inter_sphere(radius, ray, cam_origin, boule_origin);
	if (plan < 0 && sphere < 0)
		((__global unsigned int *)output)[id] = BACKCOLOR;
	else if (plan < sphere)
		((__global unsigned int *)output)[id] = PCOLOR;
	else
		((__global unsigned int *)output)[id] = SCOLOR;
}

#define WINX 1920
#define WINY 1080
#define BACKCOLOR 0x00000000
#define SCOLOR 0x000000FF
#define PCOLOR 0x00FF0000

float3			sub_vect(float3 v1, float3 v2)
{
	float3		res;

	res.x = v1.x - v2.x;
	res.y = v1.y - v2.y;
	res.z = v1.z - v2.z;
	return (res);
}

float3			add_vect(float3 v1, float3 v2)
{
	float3		res;

	res.x = v1.x + v2.x;
	res.y = v1.y + v2.y;
	res.z = v1.z + v2.z;
	return (res);
}

float			norme_vect(float3 v)
{
	return (sqrt(v.x * v.x + v.y * v.y + v.z * v.z));
}

float3			normalize_vect(float3 v)
{
	float3		res;
	int			id = 1 / sqrt(v.x * v.x + v.y * v.y + v.z * v.z);

	res.x = v.x * id;
	res.y = v.y * id;
	res.z = v.z * id;
	return (res);
}

float			dot_vect(float3 v1, float3 v2)
{
	return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}

float3			mult_fvect(float m, float3 v)
{
	float3		res;

	res.x = m * v.x;
	res.y = m * v.y;
	res.z = m * v.z;
	return (res);
}

float3			get_abc(float radius, float3 ray, float3 origin)
{
	float3		abc;

	abc.x = dot_vect(ray, ray);
	abc.y = 2 * dot_vect(ray, origin);
	abc.z = dot_vect(origin, origin) - (radius * radius);
	return (abc);
}

float			inter_sphere(float radius, float3 ray, float3 origin)
{
	float3		abc;
	float		d;
	float		res1;
	float		res2;

	abc = get_abc(radius, ray, origin);
	d = (abc.y * abc.y) - (4 * (abc.x * abc.z));
	if (d < 0)
		return (-1.0);
	res1 = -(abc[1] + sqrt(d));
	res2 = -(abc[1] - sqrt(d));
	if (res1 < res2)
		return(res1);
	return(res2);
}

float			inter_plan(float3 plan_origin, float3 plan_normale, float3 ray, float3 cam_origin)
{
	float		t;

	t = dot_vect(ray, plan_normale);
	if (t < 0.0000001)
		return (-1.0);
	t = dot_vect(sub_vect(plan_origin, cam_origin), plan_normale) / t;
	if (t < 0)
		return (-1.0);
	return (t);
}

float			modh_vect(int x)
{
	return ((float)((float)x - (WINX / 2.0)) / (WINX / 2.0));
}

float			modv_vect(int y)
{
	return ((float)(((float)y - (WINY / 2.0)) / (WINY / 2.0) * WINY / WINX));
}

float3			get_ray(float3 n, float3 v, float3 h, int x, int y)
{
	float3		res;

	h = mult_fvect(modh_vect(x), h);
	v = mult_fvect(modv_vect(y), v);
	res = add_vect(n, v);
	res = add_vect(res, h);
	return (res);
}

__kernel void	ray_trace(__global char *output)
{
	float3	cam_origin;
	float3	boule_origin;
	float3	direction_base;
	float	radius = 2;
	float3	ray;
	float3	hor;
	float3	vert;
	float3	cam_dir;

//plan
	float3	plan_origin;
	float3	plan_normale;

	int		id = get_global_id(0);
	int		x = id % WINX;
	int		y = id / WINX;

	hor.x = 0.6;
	hor.y = 0;
	hor.z = 0;
	vert.x = 0;
	vert.y = 0.6;
	vert.z = 0;
	cam_dir.x = 0;
	cam_dir.y = 0;
	cam_dir.z = 1;

	boule_origin.x = 0;
	boule_origin.y = 0;
	boule_origin.z = 0;
	plan_origin.x = -100;
	plan_origin.y = 0;
	plan_origin.z = 0;
	plan_normale.x = 0;
	plan_normale.y = 0;
	plan_normale.z = 1;
	cam_origin.x = 0;
	cam_origin.y = 0;
	cam_origin.z = -10;
	ray = get_ray(cam_dir, vert, hor, x ,y);
	float plan = inter_plan(plan_origin, plan_normale, ray, cam_origin);
	float sphere = inter_sphere(radius, ray, cam_origin);
	if (plan < 0 && sphere < 0)
		((__global unsigned int *)output)[id] = BACKCOLOR;
	else if (plan < sphere)
		((__global unsigned int *)output)[id] = PCOLOR;
	else
		((__global unsigned int *)output)[id] = SCOLOR;
}