/* * Copyright (c) 2002-2010 LWJGL Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'LWJGL' nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.lwjgl.test.opencl.gl; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.PointerBuffer; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opencl.*; import org.lwjgl.opencl.api.Filter; import org.lwjgl.opengl.*; import org.lwjgl.util.Color; import org.lwjgl.util.ReadableColor; import java.io.*; import java.net.URL; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.HashSet; import java.util.List; import java.util.Set; import static java.lang.Math.*; import static org.lwjgl.opencl.CL10.*; import static org.lwjgl.opencl.CL10GL.*; import static org.lwjgl.opencl.KHRGLEvent.*; import static org.lwjgl.opengl.AMDDebugOutput.*; import static org.lwjgl.opengl.ARBCLEvent.*; import static org.lwjgl.opengl.ARBDebugOutput.*; import static org.lwjgl.opengl.ARBSync.*; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL15.*; import static org.lwjgl.opengl.GL20.*; import static org.lwjgl.opengl.GL21.*; /* THIS DEMO USES CODE PORTED FROM JogAmp.org Original code: http://github.com/mbien/jocl-demos Original author: Michael Bien ___ ___ ___ / /\ / /\ ___ / /\ http://jocl.jogamp.org/ / /:/ / /::\ /__/\ / /::\ a http://jogamp.org/ project. /__/::\ / /:/\:\ \ \:\ / /:/\:\ \__\/\:\ / /:/~/::\ \ \:\ / /:/~/::\ \ \:\ /__/:/ /:/\:\ ___ \__\:\/__/:/ /:/\:\ \__\:\\ \:\/:/__\//__/\ | |:|\ \:\/:/__\/ / /:/ \ \::/ \ \:\| |:| \ \::/ /__/:/ \ \:\ \ \:\__|:| \ \:\ \__\/ \ \:\ \__\::::/ \ \:\ \__\/ ~~~~ \__\/ ___ ___ ___ ___ ___ / /\ / /\ / /\ /__/\ / /\ / /::\ / /::\ / /:/_ \ \:\ / /:/ / /:/\:\ / /:/\:\ / /:/ /\ \ \:\ / /:/ ___ ___ / /:/ \:\ / /:/~/:// /:/ /:/_ _____\__\:\ / /:/ ___ /__/\ / /\ /__/:/ \__\:\/__/:/ /://__/:/ /:/ /\/__/::::::::\/__/:/ / /\\ \:\ / /:/ \ \:\ / /:/\ \:\/:/ \ \:\/:/ /:/\ \:\~~\~~\/\ \:\ / /:/ \ \:\ /:/ \ \:\ /:/ \ \::/ \ \::/ /:/ \ \:\ ~~~ \ \:\ /:/ \ \:\/:/ \ \:\/:/ \ \:\ \ \:\/:/ \ \:\ \ \:\/:/ \ \::/ \ \::/ \ \:\ \ \::/ \ \:\ \ \::/ \__\/ \__\/ \__\/ \__\/ \__\/ \__\/ _____ ___ ___ ___ ___ / /::\ / /\ /__/\ / /\ / /\ / /:/\:\ / /:/_ | |::\ / /::\ / /:/_ / /:/ \:\ / /:/ /\ | |:|:\ / /:/\:\ / /:/ /\ /__/:/ \__\:| / /:/ /:/_ __|__|:|\:\ / /:/ \:\ / /:/ /::\ \ \:\ / /:/ /__/:/ /:/ /\ /__/::::| \:\ /__/:/ \__\:\ /__/:/ /:/\:\ \ \:\ /:/ \ \:\/:/ /:/ \ \:\~~\__\/ \ \:\ / /:/ \ \:\/:/~/:/ \ \:\/:/ \ \::/ /:/ \ \:\ \ \:\ /:/ \ \::/ /:/ \ \::/ \ \:\/:/ \ \:\ \ \:\/:/ \__\/ /:/ \__\/ \ \::/ \ \:\ \ \::/ /__/:/ \__\/ \__\/ \__\/ \__\/ */ /** * 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/> * 'Home' to reset the viewport<br/> * </p> * * @author Michael Bien, Spasi */ public class DemoFractal { // max number of used GPUs private static final int MAX_PARALLELISM_LEVEL = 8; private static final int COLOR_MAP_SIZE = 32 * 2 * 4; private Set<String> params; private CLContext clContext; private CLCommandQueue[] queues; private CLKernel[] kernels; private CLProgram[] programs; private CLMem[] glBuffers; private IntBuffer glIDs; private boolean useTextures; // Texture rendering private int dlist; private int vsh; private int fsh; private int program; private CLMem[] colorMap; private final PointerBuffer kernel2DGlobalWorkSize; // max per pixel iterations to compute the fractal private int maxIterations = 500; private int width = 512; private int height = 512; private double minX = -2f; private double minY = -1.2f; private double maxX = 0.6f; private double maxY = 1.3f; private boolean dragging; private double dragX; private double dragY; private double dragMinX; private double dragMinY; private double dragMaxX; private double dragMaxY; private int mouseX; private int mouseY; private int slices; private boolean drawSeparator; private boolean doublePrecision = true; private boolean buffersInitialized; private boolean rebuild; private boolean run = true; // EVENT SYNCING private final PointerBuffer syncBuffer = BufferUtils.createPointerBuffer(1); private boolean syncGLtoCL; // true if we can make GL wait on events generated from CL queues. private CLEvent[] clEvents; private GLSync[] clSyncs; private boolean syncCLtoGL; // true if we can make CL wait on sync objects generated from GL. private GLSync glSync; private CLEvent glEvent; public DemoFractal(final String[] args) { params = new HashSet<String>(); for ( int i = 0; i < args.length; i++ ) { final String arg = args[i]; if ( arg.charAt(0) != '-' && arg.charAt(0) != '/' ) throw new IllegalArgumentException("Invalid command-line argument: " + args[i]); final String param = arg.substring(1); if ( "forcePBO".equalsIgnoreCase(param) ) params.add("forcePBO"); else if ( "forceCPU".equalsIgnoreCase(param) ) params.add("forceCPU"); else if ( "debugGL".equalsIgnoreCase(param) ) params.add("debugGL"); else if ( "iterations".equalsIgnoreCase(param) ) { if ( args.length < i + 1 + 1 ) throw new IllegalArgumentException("Invalid iterations argument specified."); try { this.maxIterations = Integer.parseInt(args[++i]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid number of iterations specified."); } } else if ( "res".equalsIgnoreCase(param) ) { if ( args.length < i + 2 + 1 ) throw new IllegalArgumentException("Invalid res argument specified."); try { this.width = Integer.parseInt(args[++i]); this.height = Integer.parseInt(args[++i]); if ( width < 1 || height < 1 ) throw new IllegalArgumentException("Invalid res dimensions specified."); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid res dimensions specified."); } } } kernel2DGlobalWorkSize = BufferUtils.createPointerBuffer(2); } public static void main(String args[]) { DemoFractal demo = new DemoFractal(args); demo.init(); demo.run(); } public void init() { try { CL.create(); Display.setDisplayMode(new DisplayMode(width, height)); Display.setTitle("OpenCL Fractal Demo"); Display.setSwapInterval(0); Display.create(new PixelFormat(), new ContextAttribs().withDebug(params.contains("debugGL"))); } catch (LWJGLException e) { throw new RuntimeException(e); } try { initCL(Display.getDrawable()); } catch (Exception e) { if ( clContext != null ) clReleaseContext(clContext); Display.destroy(); throw new RuntimeException(e); } glDisable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); initView(Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight()); initGLObjects(); glFinish(); setKernelConstants(); } private void initCL(Drawable drawable) throws Exception { // Find a platform List<CLPlatform> platforms = CLPlatform.getPlatforms(); if ( platforms == null ) throw new RuntimeException("No OpenCL platforms found."); final CLPlatform platform = platforms.get(0); // just grab the first one // Find devices with GL sharing support final Filter<CLDevice> glSharingFilter = new Filter<CLDevice>() { public boolean accept(final CLDevice device) { final CLDeviceCapabilities caps = CLCapabilities.getDeviceCapabilities(device); return caps.CL_KHR_gl_sharing; } }; int device_type = params.contains("forceCPU") ? CL_DEVICE_TYPE_CPU : CL_DEVICE_TYPE_GPU; List<CLDevice> devices = platform.getDevices(device_type, glSharingFilter); if ( devices == null ) { device_type = CL_DEVICE_TYPE_CPU; devices = platform.getDevices(device_type, glSharingFilter); if ( devices == null ) throw new RuntimeException("No OpenCL devices found with KHR_gl_sharing support."); } // Create the context clContext = CLContext.create(platform, devices, new CLContextCallback() { protected void handleMessage(final String errinfo, final ByteBuffer private_info) { System.out.println("[CONTEXT MESSAGE] " + errinfo); } }, drawable, null); slices = min(devices.size(), MAX_PARALLELISM_LEVEL); // create command queues for every GPU, setup colormap and init kernels queues = new CLCommandQueue[slices]; kernels = new CLKernel[slices]; colorMap = new CLMem[slices]; for ( int i = 0; i < slices; i++ ) { colorMap[i] = clCreateBuffer(clContext, CL_MEM_READ_ONLY, COLOR_MAP_SIZE, null); colorMap[i].checkValid(); // create command queue and upload color map buffer on each used device queues[i] = clCreateCommandQueue(clContext, devices.get(i), CL_QUEUE_PROFILING_ENABLE, null); queues[i].checkValid(); final ByteBuffer colorMapBuffer = clEnqueueMapBuffer(queues[i], colorMap[i], CL_TRUE, CL_MAP_WRITE, 0, COLOR_MAP_SIZE, null, null, null); initColorMap(colorMapBuffer.asIntBuffer(), 32, Color.BLUE, Color.GREEN, Color.RED); clEnqueueUnmapMemObject(queues[i], colorMap[i], colorMapBuffer, null, null); } // check if we have 64bit FP support on all devices // if yes we can use only one program for all devices + one kernel per device. // if not we will have to create (at least) one program for 32 and one for 64bit devices. // since there are different vendor extensions for double FP we use one program per device. // (OpenCL spec is not very clear about this usecases) boolean all64bit = true; for ( CLDevice device : devices ) { if ( !isDoubleFPAvailable(device) ) { all64bit = false; break; } } // load program(s) programs = new CLProgram[all64bit ? 1 : slices]; final ContextCapabilities caps = GLContext.getCapabilities(); if ( !caps.OpenGL20 ) throw new RuntimeException("OpenGL 2.0 is required to run this demo."); else if ( device_type == CL_DEVICE_TYPE_CPU && !caps.OpenGL21 ) throw new RuntimeException("OpenGL 2.1 is required to run this demo."); if ( params.contains("debugGL") ) { if ( caps.GL_ARB_debug_output ) glDebugMessageCallbackARB(new ARBDebugOutputCallback()); else if ( caps.GL_AMD_debug_output ) glDebugMessageCallbackAMD(new AMDDebugOutputCallback()); } if ( device_type == CL_DEVICE_TYPE_GPU ) System.out.println("OpenCL Device Type: GPU (Use -forceCPU to use CPU)"); else System.out.println("OpenCL Device Type: CPU"); for ( int i = 0; i < devices.size(); i++ ) System.out.println("OpenCL Device #" + (i + 1) + " supports KHR_gl_event = " + CLCapabilities.getDeviceCapabilities(devices.get(i)).CL_KHR_gl_event); System.out.println("\nMax Iterations: " + maxIterations + " (Use -iterations <count> to change)"); System.out.println("Display resolution: " + width + "x" + height + " (Use -res <width> <height> to change)"); System.out.println("\nOpenGL caps.GL_ARB_sync = " + caps.GL_ARB_sync); System.out.println("OpenGL caps.GL_ARB_cl_event = " + caps.GL_ARB_cl_event); // Use PBO if we're on a CPU implementation useTextures = device_type == CL_DEVICE_TYPE_GPU && (!caps.OpenGL21 || !params.contains("forcePBO")); if ( useTextures ) { System.out.println("\nCL/GL Sharing method: TEXTURES (use -forcePBO to use PBO + DrawPixels)"); System.out.println("Rendering method: Shader on a fullscreen quad"); } else { System.out.println("\nCL/GL Sharing method: PIXEL BUFFER OBJECTS"); System.out.println("Rendering method: DrawPixels"); } buildPrograms(); // Detect GLtoCL synchronization method syncGLtoCL = caps.GL_ARB_cl_event; // GL3.2 or ARB_sync implied if ( syncGLtoCL ) { clEvents = new CLEvent[slices]; clSyncs = new GLSync[slices]; System.out.println("\nGL to CL sync: Using OpenCL events"); } else System.out.println("\nGL to CL sync: Using clFinish"); // Detect CLtoGL synchronization method syncCLtoGL = caps.OpenGL32 || caps.GL_ARB_sync; if ( syncCLtoGL ) { for ( CLDevice device : devices ) { if ( !CLCapabilities.getDeviceCapabilities(device).CL_KHR_gl_event ) { syncCLtoGL = false; break; } } } if ( syncCLtoGL ) { System.out.println("CL to GL sync: Using OpenGL sync objects"); } else System.out.println("CL to GL sync: Using glFinish"); if ( useTextures ) { dlist = glGenLists(1); glNewList(dlist, GL_COMPILE); glBegin(GL_QUADS); { glTexCoord2f(0.0f, 0.0f); glVertex2f(0, 0); glTexCoord2f(0.0f, 1.0f); glVertex2i(0, height); glTexCoord2f(1.0f, 1.0f); glVertex2f(width, height); glTexCoord2f(1.0f, 0.0f); glVertex2f(width, 0); } glEnd(); glEndList(); vsh = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vsh, "varying vec2 texCoord;\n" + "\n" + "void main(void) {\n" + "\tgl_Position = ftransform();\n" + "\ttexCoord = gl_MultiTexCoord0.xy;\n" + "}"); glCompileShader(vsh); fsh = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fsh, "uniform sampler2D mandelbrot;\n" + "\n" + "varying vec2 texCoord;\n" + "\n" + "void main(void) {\n" + "\tgl_FragColor = texture2D(mandelbrot, texCoord);" + "}"); glCompileShader(fsh); program = glCreateProgram(); glAttachShader(program, vsh); glAttachShader(program, fsh); glLinkProgram(program); glUseProgram(program); glUniform1i(glGetUniformLocation(program, "mandelbrot"), 0); } System.out.println(""); } private void buildPrograms() { /* * 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. * (again: the spec is not very clear about this kind of usages) */ if ( programs[0] != null ) { for ( CLProgram program : programs ) clReleaseProgram(program); } try { createPrograms(); } catch (IOException e) { throw new RuntimeException(e); } // disable 64bit floating point math if not available for ( int i = 0; i < programs.length; i++ ) { final CLDevice device = queues[i].getCLDevice(); final StringBuilder options = new StringBuilder(useTextures ? "-D USE_TEXTURE" : ""); final CLDeviceCapabilities caps = CLCapabilities.getDeviceCapabilities(device); if ( doublePrecision && isDoubleFPAvailable(device) ) { //cl_khr_fp64 options.append(" -D DOUBLE_FP"); //amd's verson of double precision floating point math if ( !caps.CL_KHR_fp64 && caps.CL_AMD_fp64 ) options.append(" -D AMD_FP"); } System.out.println("\nOpenCL COMPILER OPTIONS: " + options); try { clBuildProgram(programs[i], device, options, null); } finally { System.out.println("BUILD LOG: " + programs[i].getBuildInfoString(device, CL_PROGRAM_BUILD_LOG)); } } rebuild = false; // init kernel with constants for ( int i = 0; i < kernels.length; i++ ) kernels[i] = clCreateKernel(programs[min(i, programs.length)], "mandelbrot", null); } private void initGLObjects() { if ( glBuffers == null ) { glBuffers = new CLMem[slices]; glIDs = BufferUtils.createIntBuffer(slices); } else { for ( CLMem mem : glBuffers ) clReleaseMemObject(mem); if ( useTextures ) glDeleteTextures(glIDs); else glDeleteBuffers(glIDs); } if ( useTextures ) { glGenTextures(glIDs); // Init textures for ( int i = 0; i < slices; i++ ) { glBindTexture(GL_TEXTURE_2D, glIDs.get(i)); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width / slices, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (ByteBuffer)null); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBuffers[i] = clCreateFromGLTexture2D(clContext, CL_MEM_WRITE_ONLY, GL_TEXTURE_2D, 0, glIDs.get(i), null); } glBindTexture(GL_TEXTURE_2D, 0); } else { glGenBuffers(glIDs); // setup one empty PBO per slice for ( int i = 0; i < slices; i++ ) { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glIDs.get(i)); glBufferData(GL_PIXEL_UNPACK_BUFFER, width * height * 4 / slices, GL_STREAM_DRAW); glBuffers[i] = clCreateFromGLBuffer(clContext, CL_MEM_WRITE_ONLY, glIDs.get(i), null); } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } buffersInitialized = true; } // init kernels with constants private void setKernelConstants() { for ( int i = 0; i < slices; i++ ) { kernels[i] .setArg(6, glBuffers[i]) .setArg(7, colorMap[i]) .setArg(8, COLOR_MAP_SIZE) .setArg(9, maxIterations); } } // rendering cycle private void run() { long startTime = System.currentTimeMillis() + 5000; long fps = 0; while ( run ) { if ( !Display.isVisible() ) Thread.yield(); handleIO(); display(); Display.update(); if ( Display.isCloseRequested() ) break; if ( startTime > System.currentTimeMillis() ) { fps++; } else { long timeUsed = 5000 + (startTime - System.currentTimeMillis()); startTime = System.currentTimeMillis() + 5000; System.out.println(fps + " frames in 5 seconds = " + (fps / (timeUsed / 1000f))); fps = 0; } } clReleaseContext(clContext); if ( useTextures ) { glDeleteProgram(program); glDeleteShader(fsh); glDeleteShader(vsh); glDeleteLists(dlist, 1); } CL.destroy(); Display.destroy(); } public void display() { // TODO: Need to clean-up events, test when ARB_cl_events & KHR_gl_event are implemented. // make sure GL does not use our objects before we start computing if ( syncCLtoGL && glEvent != null ) { for ( final CLCommandQueue queue : queues ) clEnqueueWaitForEvents(queue, glEvent); } else glFinish(); if ( !buffersInitialized ) { initGLObjects(); setKernelConstants(); } if ( rebuild ) { buildPrograms(); setKernelConstants(); } compute(doublePrecision); render(); } // OpenCL private void compute(final boolean is64bit) { int sliceWidth = (int)(width / (float)slices); double rangeX = (maxX - minX) / slices; double rangeY = (maxY - minY); kernel2DGlobalWorkSize.put(0, sliceWidth).put(1, height); // start computation for ( int i = 0; i < slices; i++ ) { kernels[i].setArg(0, sliceWidth).setArg(1, height); if ( !is64bit || !isDoubleFPAvailable(queues[i].getCLDevice()) ) { kernels[i] .setArg(2, (float)(minX + rangeX * i)).setArg(3, (float)minY) .setArg(4, (float)rangeX).setArg(5, (float)rangeY); } else { kernels[i] .setArg(2, minX + rangeX * i).setArg(3, minY) .setArg(4, rangeX).setArg(5, rangeY); } // acquire GL objects, and enqueue a kernel with a probe from the list clEnqueueAcquireGLObjects(queues[i], glBuffers[i], null, null); clEnqueueNDRangeKernel(queues[i], kernels[i], 2, null, kernel2DGlobalWorkSize, null, null, null); clEnqueueReleaseGLObjects(queues[i], glBuffers[i], null, syncGLtoCL ? syncBuffer : null); if ( syncGLtoCL ) { clEvents[i] = queues[i].getCLEvent(syncBuffer.get(0)); clSyncs[i] = glCreateSyncFromCLeventARB(queues[i].getParent(), clEvents[i], 0); } } // block until done (important: finish before doing further gl work) if ( !syncGLtoCL ) { for ( int i = 0; i < slices; i++ ) clFinish(queues[i]); } } // OpenGL private void render() { glClear(GL_COLOR_BUFFER_BIT); if ( syncGLtoCL ) { for ( int i = 0; i < slices; i++ ) glWaitSync(clSyncs[i], 0, 0); } //draw slices int sliceWidth = width / slices; if ( useTextures ) { for ( int i = 0; i < slices; i++ ) { int seperatorOffset = drawSeparator ? i : 0; glBindTexture(GL_TEXTURE_2D, glIDs.get(i)); glCallList(dlist); } } else { for ( int i = 0; i < slices; i++ ) { int seperatorOffset = drawSeparator ? i : 0; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glIDs.get(i)); glRasterPos2i(sliceWidth * i + seperatorOffset, 0); glDrawPixels(sliceWidth, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } if ( syncCLtoGL ) { glSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); glEvent = clCreateEventFromGLsyncKHR(clContext, glSync, null); } //draw info text /* textRenderer.beginRendering(width, height, false); textRenderer.draw("device/time/precision", 10, height - 15); for ( int i = 0; i < slices; i++ ) { CLDevice device = queues[i].getDevice(); boolean doubleFP = doublePrecision && isDoubleFPAvailable(device); CLEvent event = probes.getEvent(i); long start = event.getProfilingInfo(START); long end = event.getProfilingInfo(END); textRenderer.draw(device.getType().toString() + i + " " + (int)((end - start) / 1000000.0f) + "ms @" + (doubleFP ? "64bit" : "32bit"), 10, height - (20 + 16 * (slices - i))); } textRenderer.endRendering(); */ } private void handleIO() { if ( Keyboard.getNumKeyboardEvents() != 0 ) { while ( Keyboard.next() ) { if ( Keyboard.getEventKeyState() ) continue; final int key = Keyboard.getEventKey(); if ( Keyboard.KEY_1 <= key && key <= Keyboard.KEY_8 ) { int number = key - Keyboard.KEY_1 + 1; slices = min(number, min(queues.length, MAX_PARALLELISM_LEVEL)); System.out.println("NEW PARALLELISM LEVEL: " + slices); buffersInitialized = false; } else { switch ( Keyboard.getEventKey() ) { case Keyboard.KEY_SPACE: drawSeparator = !drawSeparator; System.out.println("SEPARATOR DRAWING IS NOW: " + (drawSeparator ? "ON" : "OFF")); break; case Keyboard.KEY_D: doublePrecision = !doublePrecision; System.out.println("DOUBLE PRECISION IS NOW: " + (doublePrecision ? "ON" : "OFF")); rebuild = true; break; case Keyboard.KEY_HOME: minX = -2f; minY = -1.2f; maxX = 0.6f; maxY = 1.3f; break; case Keyboard.KEY_ESCAPE: run = false; break; } } } } while ( Mouse.next() ) { final int eventBtn = Mouse.getEventButton(); final int x = Mouse.getX(); final int y = Mouse.getY(); if ( Mouse.isButtonDown(0) && (x != mouseX || y != mouseY) ) { if ( !dragging ) { dragging = true; dragX = mouseX; dragY = mouseY; dragMinX = minX; dragMinY = minY; dragMaxX = maxX; dragMaxY = maxY; } double offsetX = (x - dragX) * (maxX - minX) / width; double offsetY = (y - dragY) * (maxY - minY) / height; minX = dragMinX - offsetX; minY = dragMinY - offsetY; maxX = dragMaxX - offsetX; maxY = dragMaxY - offsetY; } else { if ( dragging ) dragging = false; if ( eventBtn == -1 ) { final int dwheel = Mouse.getEventDWheel(); if ( dwheel != 0 ) { double scaleFactor = Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL) ? 0.25 : 0.05; double scale = dwheel > 0 ? scaleFactor : -scaleFactor; double deltaX = scale * (maxX - minX); double deltaY = scale * (maxY - minY); // offset for "zoom to cursor" double offsetX = (x / (double)width - 0.5) * deltaX * 2.0; double offsetY = (y / (double)height - 0.5) * deltaY * 2.0; minX += deltaX + offsetX; minY += deltaY - offsetY; maxX += -deltaX + offsetX; maxY += -deltaY - offsetY; } } } mouseX = x; mouseY = y; } } private static boolean isDoubleFPAvailable(CLDevice device) { final CLDeviceCapabilities caps = CLCapabilities.getDeviceCapabilities(device); return caps.CL_KHR_fp64 || caps.CL_AMD_fp64; } private void createPrograms() throws IOException { final String source = getProgramSource("org/lwjgl/test/opencl/gl/Mandelbrot.cl"); for ( int i = 0; i < programs.length; i++ ) programs[i] = clCreateProgramWithSource(clContext, source, null); } private String getProgramSource(final String file) throws IOException { InputStream source = null; URL sourceURL = Thread.currentThread().getContextClassLoader().getResource(file); if(sourceURL != null) { source = sourceURL.openStream(); } if ( source == null ) // dev-mode source = new FileInputStream("src/java/" + file); final BufferedReader reader = new BufferedReader(new InputStreamReader(source)); final StringBuilder sb = new StringBuilder(); String line; try { while ( (line = reader.readLine()) != null ) sb.append(line).append("\n"); } finally { source.close(); } return sb.toString(); } private static void initColorMap(IntBuffer colorMap, int stepSize, ReadableColor... colors) { for ( int n = 0; n < colors.length - 1; n++ ) { ReadableColor 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 << 0) | (g << 8) | (b << 16)); } } } private static void initView(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, width, 0.0, height, 0.0, 1.0); } }