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