package com.nativelibs4java.opencl.demos.mandelbrot; import static com.nativelibs4java.opencl.JavaCL.createBestContext; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import org.bridj.Pointer; import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.UIManager; import com.nativelibs4java.opencl.CLBuildException; import com.nativelibs4java.opencl.CLContext; import com.nativelibs4java.opencl.CLKernel; import com.nativelibs4java.opencl.CLMem; import com.nativelibs4java.opencl.CLProgram; import com.nativelibs4java.opencl.CLQueue; import com.nativelibs4java.opencl.demos.SetupUtils; import com.nativelibs4java.util.IOUtils; /** * Copied and adapted from Bob Boothby's code : * http://bbboblog.blogspot.com/2009/10/gpgpu-mandelbrot-with-opencl-and-java.html */ public class MandelbrotDemo { //boundary of view on mandelbrot set public static void main(String[] args) throws IOException, CLBuildException { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch(Exception ex) {} SetupUtils.failWithDownloadProposalsIfOpenCLNotAvailable(); //Setup variables for parameters //Boundaries float realMin = -2.25f; //-0.19920f; // -2.25 float realMax = 0.75f; //-0.12954f; // 0.75 float imaginaryMin = -1.5f; //1.01480f; // -1.5 float imaginaryMax = 1.5f; //1.06707f; // 1.5 //Resolution int realResolution = 1640; // TODO validate against device capabilities int imaginaryResolution = 1640; //The maximum iterations to perform before returning and assigning 0 to a pixel (infinity) int maxIter = 64; //TODO describe what this number means... int magicNumber = 4; //Derive the distance in imaginary / real coordinates between adjacent pixels of the image. float deltaReal = (realMax - realMin) / (realResolution-1); float deltaImaginary = (imaginaryMax - imaginaryMin) / (imaginaryResolution-1); //Create a context and program using the devices discovered. CLContext context = createBestContext(); CLQueue queue = context.createDefaultQueue(); //Setup output buffer int size = realResolution * imaginaryResolution; Pointer<Integer> results = Pointer.allocateInts(size).order(context.getByteOrder()); //TODO use an image object directly. //CL.clCreateImage2D(context.get(), 0, OpenCLLibrary); //TODO set up a Float4 array in order to be able to provide a colour map. //This depends on whether we will be able to pass in a Float4 array as an argument in the future. //Read the source file. String src = IOUtils.readTextClose(MandelbrotDemo.class.getResourceAsStream("Mandelbrot.cl")); long time = buildAndExecuteKernel(queue, realMin, imaginaryMin, realResolution, imaginaryResolution, maxIter, magicNumber, deltaReal, deltaImaginary, results, src); int nPixels = imaginaryResolution * realResolution; long timePerPixel = time / nPixels; String label = "Computed in " + ((time / 1000)) + " microseconds\n(" + ((timePerPixel)) + " nanoseconds per pixel)"; BufferedImage image = getImage(realResolution, imaginaryResolution, results); outputImage(image); JFrame f = new JFrame("JavaCL Mandelbrot Demo"); f.getContentPane().add("Center", new JScrollPane(new JLabel(new ImageIcon(image)))); f.getContentPane().add("North", new JLabel(label)); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.pack(); f.setVisible(true); } private static BufferedImage getImage(int realResolution, int imaginaryResolution, Pointer<Integer> results) { int[] outputResults = results.getInts(realResolution * imaginaryResolution); int max = Integer.MIN_VALUE; for (int i = outputResults.length; i-- != 0;) { int v = outputResults[i]; if (v > max) max = v; } for (int i = outputResults.length; i-- != 0;) { int v = outputResults[i]; float f = v / (float)max; outputResults[i] = Color.HSBtoRGB(1 - f, 0.5f, 0.3f + f * 0.7f); } BufferedImage image = new BufferedImage(realResolution, imaginaryResolution, BufferedImage.TYPE_INT_RGB); image.setRGB(0, 0, realResolution, imaginaryResolution, outputResults, 0, realResolution); return image; } private static void outputImage(BufferedImage image) { try { ImageWriter writer = ImageIO.getImageWritersByFormatName("gif").next(); ImageOutputStream stream = ImageIO.createImageOutputStream(new File("test.gif")); writer.setOutput(stream); writer.write(image); } catch (IOException e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } } static boolean useAutoGenWrapper = true; private static long buildAndExecuteKernel(CLQueue queue, float realMin, float imaginaryMin, int realResolution, int imaginaryResolution, int maxIter, int magicNumber, float deltaReal, float deltaImaginary, Pointer<Integer> results, String src) throws CLBuildException, IOException { CLContext context = queue.getContext(); long startTime = System.nanoTime(); if (useAutoGenWrapper) { Mandelbrot mandelbrot = new Mandelbrot(context); mandelbrot.mandelbrot( queue, new float[] { deltaReal, deltaImaginary }, new float[] { realMin, imaginaryMin }, maxIter, magicNumber, realResolution, context.createBuffer(CLMem.Usage.Output, results, false), new int[]{realResolution, imaginaryResolution}, new int[]{1,1} ); } else { CLProgram program = context.createProgram(src).build(); //Create a kernel instance from the mandelbrot kernel, passing in parameters. CLKernel kernel = program.createKernel( "mandelbrot", new float[] { deltaReal, deltaImaginary }, new float[] { realMin, imaginaryMin }, maxIter, magicNumber, realResolution, context.createBuffer(CLMem.Usage.Output, results, false) ); //Enqueue and complete work using a 2D range of work groups corrsponding to individual pizels in the set. //The work groups are 1x1 in size and their range is defined by the desired resolution. This corresponds //to one device thread per pixel. kernel.enqueueNDRange(queue, new int[]{realResolution, imaginaryResolution}, new int[]{1,1}); } queue.finish(); long time = System.nanoTime() - startTime; return time; } }