content: /*
   * Copyright (C) 2010-2014 Andreas Maier
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.conrad.opencl;
  
  import java.io.IOException;
  import java.nio.FloatBuffer;
  import java.util.Arrays;
  
  import com.jogamp.opencl.CLBuffer;
  import com.jogamp.opencl.CLCommandQueue;
  import com.jogamp.opencl.CLCommandQueue.Mode;
  import com.jogamp.opencl.CLContext;
  import com.jogamp.opencl.CLDevice;
  import com.jogamp.opencl.CLKernel;
  import com.jogamp.opencl.CLPlatform;
  import com.jogamp.opencl.CLProgram;
  import com.jogamp.opencl.CLMemory.Mem;
  
  
  
  
  
  
  
  
  
  
  
  
  import edu.stanford.rsl.conrad.geometry.AbstractShape;
  import edu.stanford.rsl.conrad.geometry.shapes.compound.CompoundShape;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Box;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Cone;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Cylinder;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Ellipsoid;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Pyramid;
  import edu.stanford.rsl.conrad.geometry.shapes.simple.Sphere;
  import edu.stanford.rsl.conrad.geometry.splines.BSpline;
  import edu.stanford.rsl.conrad.geometry.splines.SurfaceBSpline;
  import edu.stanford.rsl.conrad.geometry.splines.TimeVariantSurfaceBSpline;
  import edu.stanford.rsl.conrad.numerics.SimpleMatrix;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLBox;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLCompoundShape;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLCone;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLCylinder;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLEllipsoid;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLPyramid;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLSphere;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLTextureTimeVariantSurfaceBSpline;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLUniformBSpline;
  import edu.stanford.rsl.conrad.opencl.shapes.OpenCLUniformTextureSurfaceBSpline;
  import edu.stanford.rsl.conrad.phantom.forbild.shapes.ForbildBox;
  import edu.stanford.rsl.conrad.phantom.forbild.shapes.ForbildCone;
  import edu.stanford.rsl.conrad.phantom.forbild.shapes.ForbildCylinder;
  import edu.stanford.rsl.conrad.phantom.forbild.shapes.ForbildEllipsoid;
  import edu.stanford.rsl.conrad.phantom.forbild.shapes.ForbildSphere;
  import edu.stanford.rsl.conrad.utils.Configuration;
  import edu.stanford.rsl.conrad.utils.RegKeys;
  import edu.stanford.rsl.jpop.utils.UserUtil;
  
  public abstract class OpenCLUtil {
  
  	public static CLProgram program;
  	public static CLProgram render;
  	public static CLProgram simpleObjects;
  	public static CLProgram yxdraw;
  	public static CLProgram appendBuffer;
  	public static CLContext staticContext;
  	public static CLCommandQueue staticCommandQueue;
  	public static CLProgram filter;
  	
  	public static boolean isOpenCLConfigured(){
  		return Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.OPENCL_DEVICE_SELECTION) != null;
  	}
  	
  	public static void releaseContext (CLContext context){
  		program = null;
  		render = null;
  		simpleObjects = null;
  		yxdraw = null;
  		appendBuffer = null;
  		staticContext = null;
  		filter = null;
  		context.release();
  	}
  	
  	public synchronized static CLContext getStaticContext (){
  		if (staticContext == null) {
  			staticContext = createContext();
  		}
  		return staticContext;
  	}
  	
  	public synchronized static CLCommandQueue getStaticCommandQueue (){
  		if(staticContext == null)
  			staticContext = createContext();
  		if(staticCommandQueue == null || staticCommandQueue.isReleased())
  			staticCommandQueue = staticContext.getDevices()[0].createCommandQueue();
  		return staticCommandQueue;
  	}
  	
  	public synchronized static void releaseStaticContext (){
  		if (staticCommandQueue != null && !staticCommandQueue.isReleased()) {
  			staticCommandQueue.release();
  		}
  		if (staticContext != null && !staticContext.isReleased()) {
  			staticContext.release();
  		}
  		staticContext = null;
  		staticCommandQueue = null;
  	}
  	
  	public static CLBuffer<FloatBuffer> generateSamplingPoints(int elementCountU, int elementCountV, CLContext context, CLDevice device){
  		CLBuffer<FloatBuffer> samplingPoints = context.createFloatBuffer(elementCountU * elementCountV*2, Mem.READ_ONLY);
  		for (int j = 0; j < elementCountV; j++){
  			for (int i = 0; i < elementCountU; i++){
  				samplingPoints.getBuffer().put(i*(1.0f / elementCountU));
  				samplingPoints.getBuffer().put(j*(1.0f / elementCountV));
  			}
  		}
  		samplingPoints.getBuffer().rewind();
  		CLCommandQueue clc = device.createCommandQueue();
  		clc.putWriteBuffer(samplingPoints, true).finish();
  		clc.release();
  		return samplingPoints;
  	}
  	
  	//public static CLContext context;
  
  
  	//public static CLContext getContext(){
  	//	return context;
  	//}
  	/**
  	 * Creates the CLContext for the device that is preconfigured in CONRAD.
  	 * @return the CLContext
  	 * @see edu.stanford.rsl.conrad.utils.RegKeys#OPENCL_DEVICE_SELECTION
  	 */
  	public synchronized static CLContext createContext(){
  		CLContext context = null;
  		if( Configuration.getGlobalConfiguration() == null){
  			Configuration.loadConfiguration();
  		}
  
  		String devString = Configuration.getGlobalConfiguration().getRegistry().get(RegKeys.OPENCL_DEVICE_SELECTION);
  		if (devString != null){
  			CLPlatform[] platforms = CLPlatform.listCLPlatforms();
  			CLDevice[] deviceList = platforms[0].listCLDevices();
  			for (int i = 1; i < platforms.length; i++) {
  				CLDevice[] temp = platforms[i].listCLDevices();
  				int N = deviceList.length; 
  				deviceList = Arrays.copyOf(deviceList, N + temp.length);
  				for (int j = 0; j < temp.length; j++)
  					deviceList[N+j] = temp[j];
  			}
  			CLDevice device = null;
  			for (CLDevice selection: deviceList){
  				String test = selection.getName()+" "+selection.getVendor();
  				if (test.equals(devString)){
  					device = selection;
  					break;
  				}
  			}
  			context = CLContext.create(device);				
  		} else {
  
  			/*try {
  					context = CLContext.create();
  				} catch (CLException e) {*/
  			CLPlatform[] platforms = CLPlatform.listCLPlatforms();
  			CLDevice[] deviceList = platforms[0].listCLDevices();
  			for (int i = 1; i < platforms.length; i++) {
  				CLDevice[] temp = platforms[i].listCLDevices();
  				int N = deviceList.length; 
  				deviceList = Arrays.copyOf(deviceList, N + temp.length);
  				for (int j = 0; j < temp.length; j++)
  					deviceList[N+j] = temp[j];
  			}
  
  			CLDevice device = deviceList[0];
  			try {
  				device = (CLDevice) UserUtil.chooseObject("Choose OpenCL device", "OpenCL device selection", deviceList, deviceList[0]);
  				Configuration.getGlobalConfiguration().getRegistry().put(RegKeys.OPENCL_DEVICE_SELECTION, device.getName()+" "+device.getVendor());
  				Configuration.saveConfiguration();
  			} catch (Exception e1) {
  				// TODO Auto-generated catch block
  				e1.printStackTrace();
  			}
  			context = CLContext.create(device);
  			//}
  		}
  		return context;
  	}
  
  	public synchronized static void initYXDraw(CLContext context){
  		if (yxdraw==null){
  			try {
  				yxdraw = context.createProgram(TestOpenCL.class.getResourceAsStream("yxdraw.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!yxdraw.getContext().equals(context)){
  				try {
  					yxdraw = context.createProgram(TestOpenCL.class.getResourceAsStream("yxdraw.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  	
  	public synchronized static void initFilter(CLContext context){
  		if (filter==null){
  			try {
  				filter = context.createProgram(TestOpenCL.class.getResourceAsStream("filter.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!filter.getContext().equals(context)){
  				try {
  					filter = context.createProgram(TestOpenCL.class.getResourceAsStream("filter.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  
  	public static CLProgram getYXDrawInstance(){
  		return yxdraw;
  	}
  	
  	public static OpenCLEvaluatable getOpenCLEvaluatableSubclass(AbstractShape s, CLDevice device){
  		if (s instanceof Cylinder || s instanceof ForbildCylinder)
  			return new OpenCLCylinder((Cylinder) s, device);
  		else if (s instanceof Box  || s instanceof ForbildBox)
  			return new OpenCLBox((Box)s, device);
  		else if (s instanceof Sphere || s instanceof ForbildSphere)
  			return new OpenCLSphere((Sphere) s, device);
  		else if (s instanceof Cone  || s instanceof ForbildCone)
  			return new OpenCLCone((Cone)s, device);
  		else if (s instanceof Ellipsoid  || s instanceof ForbildEllipsoid)
  			return new OpenCLEllipsoid((Ellipsoid)s, device);
  		else if (s instanceof Pyramid)
  			return new OpenCLPyramid((Pyramid)s, device);
  		else if (s instanceof BSpline)
  			return new OpenCLUniformBSpline((BSpline) s, device);
  		else if (s instanceof SurfaceBSpline)
  			return new OpenCLUniformTextureSurfaceBSpline((SurfaceBSpline) s, device);
  		else if (s instanceof TimeVariantSurfaceBSpline)
  			return new OpenCLTextureTimeVariantSurfaceBSpline((TimeVariantSurfaceBSpline) s, device);
  		else if (s instanceof CompoundShape)
  			return new OpenCLCompoundShape((CompoundShape) s, device);
  		else
  			throw new RuntimeException(s.getName() + " --> This shape is not yet implemented.");
  	}
  
  	public synchronized static void initRender(CLContext context){
  		if (render==null){
  			try {
  				render = context.createProgram(TestOpenCL.class.getResourceAsStream("projection.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!render.getContext().equals(context)){
  				try {
  					render = context.createProgram(TestOpenCL.class.getResourceAsStream("projection.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  	
  	public synchronized static void initSimpleObjectEvaluator(CLContext context){
  		if (simpleObjects==null){
  			try {
  				simpleObjects = context.createProgram(TestOpenCL.class.getResourceAsStream("simpleObjects.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!simpleObjects.getContext().equals(context)){
  				try {
  					simpleObjects = context.createProgram(TestOpenCL.class.getResourceAsStream("simpleObjects.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  
  	public static CLProgram getRenderInstance(){
  		return render;
  	}
  
  	public synchronized static void initAppendBufferRender(CLContext context){
  		if (appendBuffer==null){
  			try {
  				appendBuffer = context.createProgram(TestOpenCL.class.getResourceAsStream("appendBuffer.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!appendBuffer.getContext().equals(context)){
  				try {
  					appendBuffer = context.createProgram(TestOpenCL.class.getResourceAsStream("appendBuffer.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  
  	public synchronized static void initTriangleAppendBufferRender(CLContext context){
  		if (appendBuffer==null){
  			try {
  				appendBuffer = context.createProgram(TestOpenCL.class.getResourceAsStream("triangleAppendBuffer.cl")).build(); // CLProgram.CompilerOptions.DISABLE_OPT
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!appendBuffer.getContext().equals(context)){
  				try {
  					appendBuffer = context.createProgram(TestOpenCL.class.getResourceAsStream("triangleAppendBuffer.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  	
  //	public synchronized static void initSimpleObjectEvaluator(CLContext context){
  //		if (simpleObjects==null){
  //			try {
  //				simpleObjects = context.createProgram(TestOpenCL.class.getResourceAsStream("simpleObjects.cl")).build();
  //			} catch (IOException e) {
  //				// TODO Auto-generated catch block
  //				e.printStackTrace();
  //			}
  //		} else {
  //			if (!simpleObjects.getContext().equals(context)){
  //				try {
  //					simpleObjects = context.createProgram(TestOpenCL.class.getResourceAsStream("simpleObjects.cl")).build();
  //				} catch (IOException e) {
  //					// TODO Auto-generated catch block
  //					e.printStackTrace();
  //				}
  //			}
  //		}
  //	}
  
  
  	public static CLProgram getAppendBufferRenderInstance(){
  		return appendBuffer;
  	}
  	
  	
  	public static CLBuffer<FloatBuffer> copyMatrixToDevice(SimpleMatrix m, CLContext context, CLDevice device){
  		CLBuffer<FloatBuffer> pMatrix = context.createFloatBuffer((m.getCols()*m.getRows()), Mem.READ_ONLY);
  		pMatrix.getBuffer().clear();
  		for (int i = 0; i < m.getRows(); i++){
  			for (int j=0; j<m.getCols(); j++){
  				pMatrix.getBuffer().put((float)m.getElement(i,j));
  			}
  		}
  		pMatrix.getBuffer().rewind();
  		CLCommandQueue clc = device.createCommandQueue();
  		clc.putWriteBuffer(pMatrix, false).finish();
  		clc.release();
  		return pMatrix;
  	}
  		
  	public static void transformPoints(CLBuffer<FloatBuffer> outputBuffer, SimpleMatrix matrix, CLContext context, CLDevice device){
  		int elementCount = outputBuffer.getBuffer().capacity()/3;                                  // Length of arrays to process
  		int localWorkSize = Math.min(device.getMaxWorkGroupSize(), 256);  // Local work size dimensions
  		int globalWorkSize = OpenCLUtil.roundUp(localWorkSize, elementCount);   // rounded up to the nearest multiple of the localWorkSize
  		
  		CLBuffer<FloatBuffer> clmatrix = copyMatrixToDevice(matrix, context, device);
  		
  		CLKernel kernel = OpenCLUtil.simpleObjects.createCLKernel("applyTransform");
  		
  		kernel.putArgs(outputBuffer)
  		.putArg(clmatrix).putArg(elementCount);
  
  		CLCommandQueue clc = device.createCommandQueue();
  		clc.put1DRangeKernel(kernel, 0, globalWorkSize, localWorkSize).finish();
  		clmatrix.release();
  		kernel.release();
  		clc.release();
  	}
  
  	public synchronized static void initProgram(CLContext context){
  		if (program == null){
  			try {
  				program = context.createProgram(TestOpenCL.class.getResourceAsStream("bspline.cl")).build();
  			} catch (IOException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		} else {
  			if (!program.getContext().equals(context)){
  				try {
  					program = context.createProgram(TestOpenCL.class.getResourceAsStream("bspline.cl")).build();
  				} catch (IOException e) {
  					// TODO Auto-generated catch block
  					e.printStackTrace();
  				}
  			}
  		}
  	}
  
  	public static CLProgram getProgramInstance(){
  		return program;
  	}
  
  	/**
  	 * rounded up to the nearest multiple of the groupSize
  	 * @param groupSize
  	 * @param globalSize
  	 * @return the rounded value
  	 */
  	public static int roundUp(int groupSize, int globalSize) {
  		int r = globalSize % groupSize;
  		if (r == 0) {
  			return globalSize;
  		} else {
  			return globalSize + groupSize - r;
  		}
  	}
  
  
  	/**
  	 * Integral division, rounding the result to the next highest integer.
  	 * 
  	 * @param a Dividend
  	 * @param b Divisor
  	 * @return a/b rounded to the next highest integer.
  	 */
  	public static long iDivUp(long a, long b)
  	{
  		return (a % b != 0) ? (a / b + 1) : (a / b);
  	}
  
  
  	/**
  	 * Integral division, rounding the result to the next highest integer.
  	 * 
  	 * @param a Dividend
  	 * @param b Divisor
  	 * @return a/b rounded to the next highest integer.
  	 */
  	public static int iDivUp(int a, int b)
  	{
  		return (a % b != 0) ? (a / b + 1) : (a / b);
  	}
  
  }
  