Commit f56ef0bd authored by Chris Coughlin's avatar Chris Coughlin

Initial support for OpenCL / Aparapi

parent 30dbff59
......@@ -35,6 +35,11 @@
<artifactId>dicomtoolkit</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.amd</groupId>
<artifactId>aparapi</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-tiff</artifactId>
......
/*
* Copyright (c) 2016 Emphysic LLC. All rights reserved.
*/
package com.emphysic.myriad.core.data.ops;
import com.amd.aparapi.Kernel;
/**
* ConvolutionKernel - Aparapi kernel for performing convolution operations.
* Created by ccoughlin on 8/31/16.
*/
public class ConvolutionKernel extends Kernel {
/**
* Input data
*/
private double[] idata;
/**
* Output data
*/
private double[] odata;
/**
* Convolution kernel
*/
private double[][] knl;
/**
* Width of data
*/
private int w;
/**
* Height of data
*/
private int h;
public ConvolutionKernel(double[][] kern, double[] input, double[] output, int width, int height) {
this.knl = kern;
this.idata = input;
this.odata = output;
this.w = width;
this.h = height;
}
@Override
public void run() {
int i = w - getGlobalId(0);
int j = h - getGlobalId(1);
double val = 0.0;
for (int kw = knl.length - 1; kw >= 0; kw--) {
for (int kh = knl[0].length - 1; kh >= 0; kh--) {
int x = safeIdx(i + kw - knl.length / 2, w);
int y = safeIdx(j + kh - knl[0].length / 2, h);
val += knl[kw][kh] * idata[y * w + x];
}
}
odata[j * w + i] = val;
}
/**
* Returns a safe index within bounds. Identical to {@link com.emphysic.myriad.core.data.util.DatasetUtils}
* implementation, required because Aparapi does not allow Java objects inside kernels.
* @param value proposed index
* @param endIndex end of safe values of indices
* @return proposed index if safe, 0 if proposed is less than 0, or endIndex-1 if value is greater than endIndex.
*/
private int safeIdx(int value, int endIndex) {
if (value < 0)
return 0;
if (value < endIndex)
return value;
return endIndex - 1;
}
/**
* Retrieves the input data array
* @return input array
*/
public double[] getIdata() {
return idata;
}
/**
* Retrieves the output data array
* @return output array
*/
public double[] getOdata() {
return odata;
}
/**
* Retrieves the convolution kernel
* @return convolution kernel
*/
public double[][] getKnl() {
return knl;
}
/**
* Retrieves the width of the data
* @return width of data
*/
public int getW() {
return w;
}
/**
* Retrieves the height of the dat
* @return height of data
*/
public int getH() {
return h;
}
}
......@@ -4,6 +4,9 @@
package com.emphysic.myriad.core.data.ops;
import com.amd.aparapi.Kernel;
import com.amd.aparapi.Range;
import com.amd.aparapi.device.Device;
import com.emphysic.myriad.core.data.io.Dataset;
import com.emphysic.myriad.core.data.util.DatasetUtils;
......@@ -82,15 +85,37 @@ public class ConvolutionOperation implements DatasetOperation {
return kernel[0].length;
}
/**
* Runs the convolution operator on the specified input file. The base ConvolutionOperator runs the
* identity kernel i.e. input and output data should be identical.
*
* @param input Dataset to convolve
* If a GPU that supports OpenCL is found, the convolution operation is
* performed on the "best" (as determined by number of compute units
* https://software.intel.com/sites/landingpage/opencl/optimization-guide/Basic_Concepts.htm ) device available.
*
* If no GPU is found, the convolution operation is performed on the CPU.
*
* @param input Dataset on which to operate
* @return convolved Dataset
*/
@Override
public Dataset run(Dataset input) {
Device best = Device.best();
// Disable for now - getting core dumps in Linux
/*if (best != null && best.getType() == Device.TYPE.GPU) {
// TODO: Add Device.TYPE.ACC to support Xeon Phi when new Aparapi API hits
return runOCLDevice(input, best);
}*/
return runCPU(input);
}
/**
* Convolves a Dataset on the CPU.
* @param input Dataset to convolve
* @return convolved Dataset
*/
public Dataset runCPU(Dataset input) {
int w = input.getWidth();
int h = input.getHeight();
Dataset output = new Dataset(w, h);
......@@ -109,4 +134,23 @@ public class ConvolutionOperation implements DatasetOperation {
}
return output;
}
/**
* Convolves a Dataset on an OpenCL device e.g. Device.best(), Device.firstGPU(), etc.
* @param input Dataset to convolve
* @param device OpenCL device to use
* @return convolved Dataset
*/
public Dataset runOCLDevice(Dataset input, Device device) {
int w = input.getWidth();
int h = input.getHeight();
Dataset output = new Dataset(w, h);
Range range = Range.create2D(device, w, h);
ConvolutionKernel kern = new ConvolutionKernel(kernel, input.getData(), output.getData(), w, h);
kern.setExecutionMode(Kernel.EXECUTION_MODE.GPU);
kern.execute(range);
output.setData(kern.getOdata(), w, h);
kern.dispose();
return output;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment