diff options
author | Michael Bien <[email protected]> | 2010-04-12 22:27:03 +0200 |
---|---|---|
committer | Michael Bien <[email protected]> | 2010-04-12 22:27:03 +0200 |
commit | 2c85c416d85205ab98b33e1a0b0daab32d4d81ff (patch) | |
tree | 4187ba06dec81da46495a300bb4f914e931f0c58 /src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java | |
parent | b51f2e1c254cdd74c9e43904c62694f64e6ae7e6 (diff) |
changes due to package renaming in jocl.
Diffstat (limited to 'src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java')
-rw-r--r-- | src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java b/src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java new file mode 100644 index 0000000..26770b6 --- /dev/null +++ b/src/com/jogamp/opencl/demos/fractal/MultiDeviceFractal.java @@ -0,0 +1,485 @@ +package com.jogamp.opencl.demos.fractal; + +import com.jogamp.opencl.CLBuffer; +import com.jogamp.opencl.CLCommandQueue; +import com.jogamp.opencl.CLDevice; +import com.jogamp.opencl.CLEvent; +import com.jogamp.opencl.CLEventList; +import com.jogamp.opencl.CLException; +import com.jogamp.opencl.gl.CLGLBuffer; +import com.jogamp.opencl.gl.CLGLContext; +import com.jogamp.opencl.CLKernel; +import com.jogamp.opencl.CLPlatform; +import com.jogamp.opencl.CLProgram; +import com.jogamp.opencl.CLProgram.CompilerOptions; +import com.jogamp.opengl.util.awt.TextRenderer; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Point; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.io.IOException; +import java.nio.IntBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.media.opengl.DebugGL2; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLContext; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import static com.jogamp.common.nio.Buffers.*; +import static javax.media.opengl.GL2.*; +import static com.jogamp.opencl.CLMemory.Mem.*; +import static com.jogamp.opencl.CLEvent.ProfilingCommand.*; +import static com.jogamp.opencl.CLCommandQueue.Mode.*; +import static java.lang.Math.*; + +/** + * Computes the Mandelbrot set with OpenCL using multiple GPUs and renders the result with OpenGL. + * A shared PBO is used as storage for the fractal image.<br/> + * http://en.wikipedia.org/wiki/Mandelbrot_set + * <p> + * controls:<br/> + * keys 1-9 control parallelism level<br/> + * space enables/disables slice seperator<br/> + * 'd' toggles between 32/64bit floatingpoint precision<br/> + * mouse/mousewheel to drag and zoom<br/> + * </p> + * @author Michael Bien + */ +public class MultiDeviceFractal implements GLEventListener { + + // max number of used GPUs + private static final int MAX_PARRALLELISM_LEVEL = 8; + + // max per pixel iterations to compute the fractal + private static final int MAX_ITERATIONS = 500; + + private GLCanvas canvas; + + private CLGLContext clContext; + private CLCommandQueue[] queues; + private CLKernel[] kernels; + private CLProgram program; + private CLEventList probes; + private CLGLBuffer<?>[] pboBuffers; + private CLBuffer<IntBuffer>[] colorMap; + + private int width = 0; + private int height = 0; + + private double minX = -2f; + private double minY = -1.2f; + private double maxX = 0.6f; + private double maxY = 1.3f; + + private int slices; + + private boolean drawSeperator; + private boolean doublePrecision; + private boolean buffersInitialized; + private boolean rebuild; + + private final TextRenderer textRenderer; + + public MultiDeviceFractal(int width, int height) { + + this.width = width; + this.height = height; + + canvas = new GLCanvas(new GLCapabilities(GLProfile.get(GLProfile.GL2))); + canvas.addGLEventListener(this); + initSceneInteraction(); + + JFrame frame = new JFrame("JOCL Multi GPU Mandelbrot Set"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + canvas.setPreferredSize(new Dimension(width, height)); + frame.add(canvas); + frame.pack(); + + frame.setVisible(true); + + textRenderer = new TextRenderer(frame.getFont().deriveFont(Font.BOLD, 14), true, true, null, false); + } + + public void init(GLAutoDrawable drawable) { + + if(clContext == null) { + // enable GL error checking using the composable pipeline + drawable.setGL(new DebugGL2(drawable.getGL().getGL2())); + + drawable.getGL().glFinish(); + initCL(drawable.getContext()); + + GL2 gl = drawable.getGL().getGL2(); + + gl.setSwapInterval(0); + gl.glDisable(GL_DEPTH_TEST); + gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + initView(gl, drawable.getWidth(), drawable.getHeight()); + + initPBO(gl); + drawable.getGL().glFinish(); + + setKernelConstants(); + } + } + + private void initCL(GLContext glCtx){ + try { + // create context managing all available GPUs +// clContext = CLGLContext.create(glCtx, GPU); + clContext = CLGLContext.create(glCtx, CLPlatform.getDefault().listCLDevices()[0]); + + + CLDevice[] devices = clContext.getDevices(); + + slices = min(devices.length, MAX_PARRALLELISM_LEVEL); + + // create command queues for every GPU, setup colormap and init kernels + queues = new CLCommandQueue[slices]; + kernels = new CLKernel[slices]; + probes = new CLEventList(slices); + colorMap = new CLBuffer[slices]; + + for (int i = 0; i < slices; i++) { + + colorMap[i] = clContext.createIntBuffer(32*2, READ_ONLY); + initColorMap(colorMap[i].getBuffer(), 32, Color.BLUE, Color.GREEN, Color.RED); + + // create command queue and upload color map buffer on each used device + queues[i] = devices[i].createCommandQueue(PROFILING_MODE).putWriteBuffer(colorMap[i], true); // blocking upload + + } + + // load and build program + program = clContext.createProgram(getClass().getResourceAsStream("Mandelbrot.cl")); + buildProgram(); + + } catch (IOException ex) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "can not find 'Mandelbrot.cl' in classpath.", ex); + } catch (CLException ex) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "something went wrong, hopefully no one got hurt", ex); + } + + } + + private void initColorMap(IntBuffer colorMap, int stepSize, Color... colors) { + + for (int n = 0; n < colors.length - 1; n++) { + + Color color = colors[n]; + int r0 = color.getRed(); + int g0 = color.getGreen(); + int b0 = color.getBlue(); + + color = colors[n + 1]; + int r1 = color.getRed(); + int g1 = color.getGreen(); + int b1 = color.getBlue(); + + int deltaR = r1 - r0; + int deltaG = g1 - g0; + int deltaB = b1 - b0; + + for (int step = 0; step < stepSize; step++) { + float alpha = (float) step / (stepSize - 1); + int r = (int) (r0 + alpha * deltaR); + int g = (int) (g0 + alpha * deltaG); + int b = (int) (b0 + alpha * deltaB); + colorMap.put((r << 16) | (g << 8) | (b << 0)); + } + } + colorMap.rewind(); + + } + + private void initView(GL2 gl, int width, int height) { + + gl.glViewport(0, 0, width, height); + + gl.glMatrixMode(GL_MODELVIEW); + gl.glLoadIdentity(); + + gl.glMatrixMode(GL_PROJECTION); + gl.glLoadIdentity(); + gl.glOrtho(0.0, width, 0.0, height, 0.0, 1.0); + } + + @SuppressWarnings("unchecked") + private void initPBO(GL gl) { + + if(pboBuffers != null) { + int[] oldPbos = new int[pboBuffers.length]; + for (int i = 0; i < pboBuffers.length; i++) { + CLGLBuffer<?> buffer = pboBuffers[i]; + oldPbos[i] = buffer.GLID; + buffer.release(); + } + gl.glDeleteBuffers(oldPbos.length, oldPbos, 0); + } + + pboBuffers = new CLGLBuffer[slices]; + + int[] pbo = new int[slices]; + gl.glGenBuffers(slices, pbo, 0); + + // setup one empty PBO per slice + for (int i = 0; i < slices; i++) { + + gl.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo[i]); + gl.glBufferData(GL_PIXEL_UNPACK_BUFFER, width*height * SIZEOF_INT / slices, null, GL_STREAM_DRAW); + gl.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + pboBuffers[i] = clContext.createFromGLBuffer(pbo[i], WRITE_ONLY); + + } + + buffersInitialized = true; + } + + private void buildProgram() { + + /* + * workaround: The driver keeps using the old binaries for some reason. + * to solve this we simple create a new program and release the old. + * however rebuilding programs should be possible -> remove when drivers are fixed. + */ + if(program != null && rebuild) { + String source = program.getSource(); + program.release(); + program = clContext.createProgram(source); + } + + // disable 64bit floating point math if not available + if(doublePrecision) { + for (CLDevice device : program.getCLDevices()) { + if(!device.isDoubleFPAvailable()) { + doublePrecision = false; + break; + } + } + } + + if(doublePrecision) { + program.build(CompilerOptions.FAST_RELAXED_MATH, "-D DOUBLE_FP"); + }else{ + program.build(CompilerOptions.FAST_RELAXED_MATH); + } + rebuild = false; + + for (int i = 0; i < kernels.length; i++) { + // init kernel with constants + kernels[i] = program.createCLKernel("mandelbrot"); + } + + } + + // init kernels with constants + private void setKernelConstants() { + for (int i = 0; i < slices; i++) { + kernels[i].setForce32BitArgs(!doublePrecision) + .setArg(6, pboBuffers[i]) + .setArg(7, colorMap[i]) + .setArg(8, colorMap[i].getBuffer().capacity()) + .setArg(9, MAX_ITERATIONS); + } + } + + // rendering cycle + public void display(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + + // make sure GL does not use our objects before we start computeing + gl.glFinish(); + if(!buffersInitialized) { + initPBO(gl); + setKernelConstants(); + } + if(rebuild) { + buildProgram(); + setKernelConstants(); + } + compute(); + + render(gl.getGL2()); + } + + // OpenCL + private void compute() { + + int sliceWidth = width / slices; + double rangeX = (maxX - minX) / slices; + double rangeY = (maxY - minY); + + // release all old events, you can't reuse events in OpenCL + probes.release(); + + // start computation + for (int i = 0; i < slices; i++) { + + kernels[i].putArg( sliceWidth).putArg(height) + .putArg(minX + rangeX*i).putArg( minY) + .putArg( rangeX ).putArg(rangeY) + .rewind(); + + // aquire GL objects, and enqueue a kernel with a probe from the list + queues[i].putAcquireGLObject(pboBuffers[i].ID) + .put2DRangeKernel(kernels[i], 0, 0, sliceWidth, height, 0, 0, probes) + .putReleaseGLObject(pboBuffers[i].ID); + + } + + // block until done (important: finish before doing further gl work) + for (int i = 0; i < slices; i++) { + queues[i].finish(); + } + + } + + // OpenGL + private void render(GL2 gl) { + + gl.glClear(GL_COLOR_BUFFER_BIT); + + //draw slices + int sliceWidth = width / slices; + + for (int i = 0; i < slices; i++) { + + int seperatorOffset = drawSeperator?i:0; + + gl.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboBuffers[i].GLID); + gl.glRasterPos2i(sliceWidth*i + seperatorOffset, 0); + + gl.glDrawPixels(sliceWidth, height, GL_BGRA, GL_UNSIGNED_BYTE, 0); + + } + gl.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + //draw info text + textRenderer.beginRendering(width, height, false); + + textRenderer.draw("precision: "+ (doublePrecision?"64bit":"32bit"), 10, height-15); + + for (int i = 0; i < slices; i++) { + CLEvent event = probes.getEvent(i); + long start = event.getProfilingInfo(START); + long end = event.getProfilingInfo(END); + textRenderer.draw("GPU"+i +" "+(int)((end-start)/1000000.0f)+"ms", 10, height-(20+16*(slices-i))); + } + + textRenderer.endRendering(); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + + if(this.width == width && this.height == height) + return; + + this.width = width; + this.height = height; + + initPBO(drawable.getGL()); + + initView(drawable.getGL().getGL2(), drawable.getWidth(), drawable.getHeight()); + } + + private void initSceneInteraction() { + + MouseAdapter mouseAdapter = new MouseAdapter() { + + Point lastpos = new Point(); + + @Override + public void mouseDragged(MouseEvent e) { + + double offsetX = (lastpos.x - e.getX()) * (maxX - minX) / width; + double offsetY = (lastpos.y - e.getY()) * (maxY - minY) / height; + + minX += offsetX; + minY -= offsetY; + + maxX += offsetX; + maxY -= offsetY; + + lastpos = e.getPoint(); + + canvas.display(); + + } + + @Override + public void mouseMoved(MouseEvent e) { + lastpos = e.getPoint(); + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + float rotation = e.getWheelRotation() / 25.0f; + + double deltaX = rotation * (maxX - minX); + double deltaY = rotation * (maxY - minY); + + // offset for "zoom to cursor" + double offsetX = (e.getX() / (float)width - 0.5f) * deltaX * 2; + double offsetY = (e.getY() / (float)height- 0.5f) * deltaY * 2; + + minX += deltaX+offsetX; + minY += deltaY-offsetY; + + maxX +=-deltaX+offsetX; + maxY +=-deltaY-offsetY; + + canvas.display(); + } + }; + + KeyAdapter keyAdapter = new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_SPACE) { + drawSeperator = !drawSeperator; + }else if(e.getKeyChar() > '0' && e.getKeyChar() < '9') { + int number = e.getKeyChar()-'0'; + slices = min(number, min(queues.length, MAX_PARRALLELISM_LEVEL)); + buffersInitialized = false; + }else if(e.getKeyCode() == KeyEvent.VK_D) { + doublePrecision = !doublePrecision; + rebuild = true; + } + canvas.display(); + } + + }; + + canvas.addMouseMotionListener(mouseAdapter); + canvas.addMouseWheelListener(mouseAdapter); + canvas.addKeyListener(keyAdapter); + } + + public void dispose(GLAutoDrawable drawable) { + } + + public static void main(String args[]) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + new MultiDeviceFractal(512, 512); + } + }); + } + +} |