From ac98280c015b8d8235fbf48ac0a559a60f4eea02 Mon Sep 17 00:00:00 2001 From: Eric Caspole <eric.caspole@amd.com> Date: Thu, 20 Dec 2012 18:33:30 +0000 Subject: [PATCH] oop nbody example --- examples/oopnbody/.project | 17 + examples/oopnbody/README.txt | 20 + examples/oopnbody/build.xml | 204 ++++++++++ examples/oopnbody/nbody.sh | 12 + .../amd/aparapi/examples/oopnbody/Body.java | 37 ++ .../amd/aparapi/examples/oopnbody/Main.java | 372 ++++++++++++++++++ .../amd/aparapi/examples/oopnbody/duke.jpg | Bin 0 -> 6927 bytes .../amd/aparapi/examples/oopnbody/moon.jpg | Bin 0 -> 7745 bytes .../aparapi/examples/oopnbody/particle.jpg | Bin 0 -> 1056 bytes 9 files changed, 662 insertions(+) create mode 100644 examples/oopnbody/.project create mode 100644 examples/oopnbody/README.txt create mode 100644 examples/oopnbody/build.xml create mode 100755 examples/oopnbody/nbody.sh create mode 100644 examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Body.java create mode 100644 examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Main.java create mode 100644 examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/duke.jpg create mode 100644 examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/moon.jpg create mode 100644 examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/particle.jpg diff --git a/examples/oopnbody/.project b/examples/oopnbody/.project new file mode 100644 index 00000000..87e9909a --- /dev/null +++ b/examples/oopnbody/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>oopnbody</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/examples/oopnbody/README.txt b/examples/oopnbody/README.txt new file mode 100644 index 00000000..d5fa2430 --- /dev/null +++ b/examples/oopnbody/README.txt @@ -0,0 +1,20 @@ +oopnbody readme - December 2012 + +This demo is an example of an Aparapi experimental feature to allow some +limited use of java objects in kernels, based on the regular nbody demo. + +There is limited support in Aparapi for using arrays of java objects in kernels, +where a kernel accesses the objects by indexing into the object array and the +only member functions on the objects that can be called +are bean-style getters and setters, where if the object +has a data member "foo" the getter will be named "getFoo" and so on. + +The types of the object fields accessed in a kernel in this way must be basic +types otherwise in line with Aparapi usage. + +This may allow a more natural java programming style. This is implemented by +copying the referenced object fields into C-style arrays of structs to pass to +OpenCL. Then when the kernel completes the data is replaced into the original +java object. The extra copying may result in lower performance than explicit +use of basic type arrays in the original java source. + diff --git a/examples/oopnbody/build.xml b/examples/oopnbody/build.xml new file mode 100644 index 00000000..52ed41d1 --- /dev/null +++ b/examples/oopnbody/build.xml @@ -0,0 +1,204 @@ +<?xml version="1.0"?> + +<project name="oopnbody" default="build" basedir="."> + <!--<property name="jogamp-jar-url" value="http://jogamp.org/deployment/archive/rc/gluegen_28-joal_17-jogl_41-jocl_25/jar"/>--> + <property name="jogamp-jar-url" value="http://jogamp.org/deployment/archive/rc/gluegen_52-joal_32-jogl_66-jocl_41/jar"/> + <path id="compiler.class.path"> + <pathelement path="../../com.amd.aparapi/dist/aparapi.jar"/> + <pathelement path="jogamp/jogl-all.jar"/> + <pathelement path="jogamp/gluegen-rt.jar"/> + </path> + + <path id="runtime.class.path" cache="true"> + <path refid="compiler.class.path"/> + <pathelement path="${ant.project.name}.jar"/> + </path> + + <target name="getjogl-windows-i586" if="use.win32"> + <delete dir="jogamp"/> + <get dest="jogamp"> + <url url="${jogamp-jar-url}/jogl-all-natives-windows-i586.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt-natives-windows-i586.jar"/> + <url url="${jogamp-jar-url}/jogl-all.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt.jar"/> + </get> + <unzip src="jogamp/jogl-all-natives-windows-i586.jar" dest="jogamp"/> + <unzip src="jogamp/gluegen-rt-natives-windows-i586.jar" dest="jogamp"/> + <delete dir="jogamp/META-INF"/> + </target> + + <target name="getjogl-windows-amd64" if="use.win64"> + <delete dir="jogamp"/> + <get dest="jogamp"> + <url url="${jogamp-jar-url}/jogl-all-natives-windows-amd64.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt-natives-windows-amd64.jar"/> + <url url="${jogamp-jar-url}/jogl-all.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt.jar"/> + </get> + <unzip src="jogamp/jogl-all-natives-windows-amd64.jar" dest="jogamp"/> + <unzip src="jogamp/gluegen-rt-natives-windows-amd64.jar" dest="jogamp"/> + <delete dir="jogamp/META-INF"/> + </target> + + <target name="getjogl-linux-i586" if="use.linux32"> + <delete dir="jogamp"/> + <get dest="jogamp"> + <url url="${jogamp-jar-url}/jogl-all-natives-linux-i586.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt-natives-linux-i586.jar"/> + <url url="${jogamp-jar-url}/jogl-all.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt.jar"/> + </get> + <unzip src="jogamp/jogl-all-natives-linux-i586.jar" dest="jogamp"/> + <unzip src="jogamp/gluegen-rt-natives-linux-i586.jar" dest="jogamp"/> + <delete dir="jogamp/META-INF"/> + </target> + + <target name="getjogl-linux-amd64" if="use.linux64"> + <delete dir="jogamp"/> + <get dest="jogamp"> + <url url="${jogamp-jar-url}/jogl-all-natives-linux-amd64.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt-natives-linux-amd64.jar"/> + <url url="${jogamp-jar-url}/jogl-all.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt.jar"/> + </get> + <unzip src="jogamp/jogl-all-natives-linux-amd64.jar" dest="jogamp"/> + <unzip src="jogamp/gluegen-rt-natives-linux-amd64.jar" dest="jogamp"/> + <delete dir="jogamp/META-INF"/> + </target> + + <target name="getjogl-mac-universal" if="use.mac.universal"> + <delete dir="jogamp"/> + <get dest="jogamp"> + <url url="${jogamp-jar-url}/jogl-all-natives-macosx-universal.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt-natives-macosx-universal.jar"/> + <url url="${jogamp-jar-url}/jogl-all.jar"/> + <url url="${jogamp-jar-url}/gluegen-rt.jar"/> + </get> + <unzip src="jogamp/jogl-all-natives-macosx-universal.jar" dest="jogamp"/> + <unzip src="jogamp/gluegen-rt-natives-macosx-universal.jar" dest="jogamp"/> + <delete dir="jogamp/META-INF"/> + </target> + + <target name="checkos"> + <condition property="use.win32"> + <and> + <os family="windows" /> + <or> + <os arch="x86" /> + <os arch="i386" /> + </or> + <not><available file="jogamp/jogl-all-natives-windows-i586.jar"/> </not> + <not><available file="jogamp/gluegen-rt-natives-windows-i586.jar"/> </not> + <not><available file="jogamp/jogl-all.jar"/> </not> + <not><available file="jogamp/gluegen-rt.jar"/> </not> + </and> + </condition> + <condition property="use.win64"> + <and> + <os family="windows" /> + <not> + <or> + <os arch="x86" /> + <os arch="i386" /> + </or> + </not> + <not><available file="jogamp/jogl-all-natives-windows-amd64.jar"/> </not> + <not><available file="jogamp/gluegen-rt-natives-windows-amd64.jar"/> </not> + <not><available file="jogamp/jogl-all.jar"/> </not> + <not><available file="jogamp/gluegen-rt.jar"/> </not> + </and> + </condition> + <condition property="use.linux32"> + <and> + <os family="unix" /> + <not> + <os family="mac" /> + </not> + <or> + <os arch="x86" /> + <os arch="i386" /> + </or> + <not><available file="jogamp/jogl-all-natives-linux-i586.jar"/> </not> + <not><available file="jogamp/gluegen-rt-natives-linux-i586.jar"/> </not> + <not><available file="jogamp/jogl-all.jar"/> </not> + <not><available file="jogamp/gluegen-rt.jar"/> </not> + </and> + </condition> + <condition property="use.linux64"> + <and> + <os family="unix" /> + <not> + <os family="mac" /> + </not> + <not> + <or> + <os arch="x86" /> + <os arch="i386" /> + </or> + </not> + <not><available file="jogamp/jogl-all-natives-linux-amd64.jar"/> </not> + <not><available file="jogamp/gluegen-rt-natives-linux-amd64.jar"/> </not> + <not><available file="jogamp/jogl-all.jar"/> </not> + <not><available file="jogamp/gluegen-rt.jar"/> </not> + </and> + </condition> + <condition property="use.mac.universal"> + <and> + <os family="mac" /> + <not> + <or> + <os arch="x86" /> + <os arch="i386" /> + </or> + </not> + <not><available file="jogamp/jogl-all-natives-macosx-universal.jar"/> </not> + <not><available file="jogamp/gluegen-rt-natives-macosx-universal.jar"/> </not> + <not><available file="jogamp/jogl-all.jar"/> </not> + <not><available file="jogamp/gluegen-rt.jar"/> </not> + </and> + </condition> + </target> + + <target name="getjogl" depends="checkos, getjogl-windows-i586, getjogl-windows-amd64, + getjogl-linux-i586, getjogl-linux-amd64, getjogl-mac-universal"/> + + <target name="build" depends="clean, getjogl"> + <mkdir dir="classes"/> + <javac srcdir="src" destdir="classes" debug="on" includeantruntime="false"> + <classpath refid="compiler.class.path"/> + </javac> + <copy todir="classes/com/amd/aparapi/examples/oopnbody" file="src/com/amd/aparapi/examples/oopnbody/particle.jpg"/> + <jar jarfile="${ant.project.name}.jar" basedir="classes"/> + </target> + + <target name="clean"> + <delete dir="classes"/> + <delete file="${ant.project.name}.jar"/> + </target> + + <target name="run-jtp"> + <java classname="com.amd.aparapi.examples.oopnbody.Main" fork="true"> + <classpath refid="runtime.class.path"/> + <sysproperty key="java.library.path" path="..\..\com.amd.aparapi.jni\dist;jogamp"/> + <sysproperty key="com.amd.aparapi.executionMode" value="JTP"/> + <sysproperty key="bodies" value="16384"/> + <sysproperty key="height" value="800"/> + <sysproperty key="width" value="800"/> + </java> + </target> + + <target name="run-gpu"> + <java classname="com.amd.aparapi.examples.oopnbody.Main" fork="true"> + <classpath refid="runtime.class.path"/> + <sysproperty key="java.library.path" path="..\..\com.amd.aparapi.jni\dist;jogamp"/> + <sysproperty key="com.amd.aparapi.executionMode" value="GPU"/> + <sysproperty key="bodies" value="16384"/> + <sysproperty key="height" value="800"/> + <sysproperty key="width" value="800"/> + </java> + </target> + + <target name="run" depends="run-gpu"/> + + +</project> diff --git a/examples/oopnbody/nbody.sh b/examples/oopnbody/nbody.sh new file mode 100755 index 00000000..d2903d64 --- /dev/null +++ b/examples/oopnbody/nbody.sh @@ -0,0 +1,12 @@ + +java \ + -Djava.library.path=../../com.amd.aparapi.jni/dist:jogamp \ + -Dcom.amd.aparapi.executionMode=$1 \ + -Dcom.amd.aparapi.logLevel=INFO \ + -Dcom.amd.aparapi.enableShowGeneratedOpenCL=true \ + -Dbodies=$2 \ + -Dheight=800 \ + -Dwidth=1200 \ + -classpath jogamp/jogl-all.jar:jogamp/gluegen-rt.jar:../../com.amd.aparapi/dist/aparapi.jar:oopnbody.jar \ + com.amd.aparapi.examples.oopnbody.Main + diff --git a/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Body.java b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Body.java new file mode 100644 index 00000000..ba02d159 --- /dev/null +++ b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Body.java @@ -0,0 +1,37 @@ +package com.amd.aparapi.examples.oopnbody; + +import java.util.List; + +public final class Body{ + protected final float delT = .005f; + protected final float espSqr = 1.0f; + public static Body[] allBodies; + + public Body(float _x, float _y, float _z, float _m) { + x = _x; + y = _y; + z = _z; + m = _m; + } + + float x, y, z, m, vx, vy, vz; + + public float getX() { return x; } + public float getY() { return y; } + public float getZ() { return z; } + + public float getVx() { return vx; } + public float getVy() { return vy; } + public float getVz() { return vz; } + + public float getM() { return m; } + public void setM(float _m) { m = _m; } + + public void setX(float _x) { x = _x; } + public void setY(float _y) { y = _y; } + public void setZ(float _z) { z = _z; } + + public void setVx(float _vx) { vx = _vx; } + public void setVy(float _vy) { vy = _vy; } + public void setVz(float _vz) { vz = _vz; } +} diff --git a/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Main.java b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Main.java new file mode 100644 index 00000000..307701ca --- /dev/null +++ b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/Main.java @@ -0,0 +1,372 @@ +/* +Copyright (c) 2010-2011, Advanced Micro Devices, Inc. +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 the copyright holder 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 HOLDER 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. + +If you use the software (in whole or in part), you shall adhere to all applicable U.S., European, and other export +laws, including but not limited to the U.S. Export Administration Regulations ("EAR"), (15 C.F.R. Sections 730 through +774), and E.U. Council Regulation (EC) No 1334/2000 of 22 June 2000. Further, pursuant to Section 740.6 of the EAR, +you hereby certify that, except pursuant to a license granted by the United States Department of Commerce Bureau of +Industry and Security or as otherwise permitted pursuant to a License Exception under the U.S. Export Administration +Regulations ("EAR"), you will not (1) export, re-export or release to a national of a country in Country Groups D:1, +E:1 or E:2 any restricted technology, software, or source code you receive hereunder, or (2) export to Country Groups +D:1, E:1 or E:2 the direct product of such technology or software, if such foreign produced direct product is subject +to national security controls as identified on the Commerce Control List (currently found in Supplement 1 to Part 774 +of EAR). For the most current Country Group listings, or for additional information about the EAR or your obligations +under those regulations, please refer to the U.S. Bureau of Industry and Security's website at http://www.bis.doc.gov/. + + */ +package com.amd.aparapi.examples.oopnbody; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.glu.GLU; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.WindowConstants; + +import com.amd.aparapi.Kernel; +import com.amd.aparapi.ProfileInfo; +import com.amd.aparapi.Range; + +import com.jogamp.opengl.util.FPSAnimator; +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureIO; + +/** + * NBody implementing demonstrating Aparapi kernels. + * + * For a description of the NBody problem. + * + * @see http://en.wikipedia.org/wiki/N-body_problem + * + * We use JOGL to render the bodies. + * @see http://jogamp.org/jogl/www/ + * + * @author gfrost + * + */ +public class Main { + + public static class NBodyKernel extends Kernel { + + protected final float delT = .005f; + protected final float espSqr = 1.0f; + protected final float mass = 5f; + + private final Range range; + public Body[] bodies; + + /** + * Constructor initializes xyz and vxyz arrays. + * + * @param _bodies + */ + public NBodyKernel(Range _range) { + range = _range; + bodies = new Body[range.getGlobalSize(0)]; + + final float maxDist = 20f; + for (int body = 0; body < range.getGlobalSize(0); body ++) { + final float theta = (float) (Math.random() * Math.PI * 2); + final float phi = (float) (Math.random() * Math.PI * 2); + final float radius = (float) (Math.random() * maxDist); + + // get the 3D dimensional coordinates + float x = (float) (radius * Math.cos(theta) * Math.sin(phi)); + float y = (float) (radius * Math.sin(theta) * Math.sin(phi)); + float z = (float) (radius * Math.cos(phi)); + + // divide into two 'spheres of bodies' by adjusting x + if ((body % 2) == 0) { + x += maxDist * 1.5; + } else { + x -= maxDist * 1.5; + } + bodies[body] = new Body(x,y,z, 5f); + } + + Body.allBodies = bodies; + } + + /** + * Here is the kernel entrypoint. Here is where we calculate the position of each body + */ + @Override + public void run() { + final int body = getGlobalId(); + + float accx = 0.f; + float accy = 0.f; + float accz = 0.f; + + float myPosx = bodies[body].getX(); + float myPosy = bodies[body].getY(); + float myPosz = bodies[body].getZ(); + + for(int i=0; i<getGlobalSize(0); i++) { + + final float dx = bodies[i].getX() - myPosx; + final float dy = bodies[i].getY() - myPosy; + final float dz = bodies[i].getZ() - myPosz; + final float invDist = rsqrt((dx * dx) + (dy * dy) + (dz * dz) + espSqr); + final float s = bodies[i].getM() * invDist * invDist * invDist; + accx = accx + (s * dx); + accy = accy + (s * dy); + accz = accz + (s * dz); + } + + accx = accx * delT; + accy = accy * delT; + accz = accz * delT; + bodies[body].setX(myPosx + (bodies[body].getVx() * delT) + (accx * .5f * delT)); + bodies[body].setY(myPosy + (bodies[body].getVy() * delT) + (accy * .5f * delT)); + bodies[body].setZ(myPosz + (bodies[body].getVz() * delT) + (accz * .5f * delT)); + + bodies[body].setVx(bodies[body].getVx() + accx); + bodies[body].setVy(bodies[body].getVy() + accy); + bodies[body].setVz(bodies[body].getVz() + accz); + } + + + + /** + * Render all particles to the OpenGL context + * + * @param gl + */ + + protected void render(GL2 gl) { + gl.glBegin(GL2.GL_QUADS); + int sz = range.getGlobalSize(0); + for (int i = 0; i < sz; i++) { + + if (i < (sz / 2)) { + gl.glColor3f(1f, 0f, 0f); + } else if (i < (sz * 0.666)) { + gl.glColor3f(0f, 1f, 0f); + } else { + gl.glColor3f(0f, 0f, 1f); + } + + Body currBody = bodies[i]; + + gl.glTexCoord2f(0, 1); + gl.glVertex3f(currBody.getX(), currBody.getY() + 1, currBody.getZ()); + gl.glTexCoord2f(0, 0); + gl.glVertex3f(currBody.getX(), currBody.getY(), currBody.getZ()); + gl.glTexCoord2f(1, 0); + gl.glVertex3f(currBody.getX() + 1, currBody.getY(), currBody.getZ()); + gl.glTexCoord2f(1, 1); + gl.glVertex3f(currBody.getX() + 1, currBody.getY() + 1, currBody.getZ()); + + } + gl.glEnd(); + } + } + + public static int width; + + public static int height; + + public static boolean running; + + public static void main(String _args[]) { + int bodyCount = Integer.getInteger("bodies", 8192); + + //final Main kernel = new Main(bodyCount); + final NBodyKernel kernel = new NBodyKernel(Range.create(Integer.getInteger("bodies", 8192))); + + final JFrame frame = new JFrame("NBody"); + + final JPanel panel = new JPanel(new BorderLayout()); + final JPanel controlPanel = new JPanel(new FlowLayout()); + panel.add(controlPanel, BorderLayout.SOUTH); + + final JButton startButton = new JButton("Start"); + + startButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + running = true; + startButton.setEnabled(false); + } + }); + controlPanel.add(startButton); + //controlPanel.add(new JLabel(kernel.getExecutionMode().toString())); + + controlPanel.add(new JLabel(" Particles")); + controlPanel.add(new JTextField("" + bodyCount , 5)); + + controlPanel.add(new JLabel("FPS")); + final JTextField framesPerSecondTextField = new JTextField("0", 5); + + controlPanel.add(framesPerSecondTextField); + controlPanel.add(new JLabel("Score(")); + final JLabel miniLabel = new JLabel("<html><small>calcs</small><hr/><small>µsec</small></html>"); + + controlPanel.add(miniLabel); + controlPanel.add(new JLabel(")")); + + final JTextField positionUpdatesPerMicroSecondTextField = new JTextField("0", 5); + + controlPanel.add(positionUpdatesPerMicroSecondTextField); + final GLCapabilities caps = new GLCapabilities(null); + final GLProfile profile = caps.getGLProfile(); + caps.setDoubleBuffered(true); + caps.setHardwareAccelerated(true); + final GLCanvas canvas = new GLCanvas(caps); + + final Dimension dimension = new Dimension(Integer.getInteger("width", 742 - 64), Integer.getInteger("height", 742 - 64)); + canvas.setPreferredSize(dimension); + + canvas.addGLEventListener(new GLEventListener() { + private double ratio; + + private final float xeye = 0f; + + private final float yeye = 0f; + + private final float zeye = 100f; + + private final float xat = 0f; + + private final float yat = 0f; + + private final float zat = 0f; + + public final float zoomFactor = 1.0f; + + private int frames; + + private long last = System.currentTimeMillis(); + + @Override + public void dispose(GLAutoDrawable drawable) { + + } + + @Override + public void display(GLAutoDrawable drawable) { + + final GL2 gl = drawable.getGL().getGL2(); + + gl.glLoadIdentity(); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + gl.glColor3f(1f, 1f, 1f); + + final GLU glu = new GLU(); + glu.gluPerspective(45f, ratio, 0f, 1000f); + + glu.gluLookAt(xeye, yeye, zeye * zoomFactor, xat, yat, zat, 0f, 1f, 0f); + if (running) { + //Arrays.parallel(bodies.toArray(new Body[1])).forEach(b -> {b.nextMove();}); + kernel.execute(kernel.range); + + } + kernel.render(gl); + + final long now = System.currentTimeMillis(); + final long time = now - last; + frames++; + + if (time > 1000) { // We update the frames/sec every second + if (running) { + final float framesPerSecond = (frames * 1000.0f) / time; + final int updatesPerMicroSecond = (int) ((framesPerSecond * bodyCount * bodyCount) / 1000000); + framesPerSecondTextField.setText(String.format("%5.2f", framesPerSecond)); + positionUpdatesPerMicroSecondTextField.setText(String.format("%4d", updatesPerMicroSecond)); + } + frames = 0; + last = now; + } + gl.glFlush(); + + } + + @Override + public void init(GLAutoDrawable drawable) { + final GL2 gl = drawable.getGL().getGL2(); + + gl.glShadeModel(GLLightingFunc.GL_SMOOTH); + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE); + try { + final InputStream textureStream = Main.class.getResourceAsStream("particle.jpg"); + final Texture texture = TextureIO.newTexture(textureStream, false, null); + texture.enable(gl); + } catch (final IOException e) { + e.printStackTrace(); + } catch (final GLException e) { + e.printStackTrace(); + } + + } + + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int _width, int _height) { + width = _width; + height = _height; + + final GL2 gl = drawable.getGL().getGL2(); + gl.glViewport(0, 0, width, height); + + ratio = (double) width / (double) height; + + } + + }); + + panel.add(canvas, BorderLayout.CENTER); + frame.getContentPane().add(panel, BorderLayout.CENTER); + final FPSAnimator animator = new FPSAnimator(canvas, 100); + + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + + animator.start(); + + } + +} diff --git a/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/duke.jpg b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/duke.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2729fd2fc9cef1a95cb291aa633fb037fd6cf9ab GIT binary patch literal 6927 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 literal 0 HcmV?d00001 diff --git a/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/moon.jpg b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/moon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..40a6668a778f30a5898620873eb89ebbe9e8741e GIT binary patch literal 7745 zcmb7pcUTk5vu;A@(!ZeemM9(Ry_Xme>4c6z0HqT`ktV%L4N}C=q_@zkRFTj@As_+* zQl$x~2vU#V@7#0l{rArPH9ODF%$8?o_kC~XZ&m?xIxuY*00;yCfPWj{W)YwPpdck9 zCnKdGCnKk%q_{<Wo0gi2ikcP7NOzlyori~uos$#FCn*HIC&tUkDXb(cCM6>;FAot^ z(NvMukd%{``PT`Ml9G~|ikgL%mPO_+=UtiqZ@K9M(31m}0qY<j4}gds2%-nxya%uW z07OKff7AV6AqJ5EiT-B%*Q!7VAOeCwM8w1-AP|Tc^mkH3AOJBv1GgB7+CMyo_N3yd z2r{sGdOxJ7!RXCLhp$JB5{>UPd?No2W&-|4{@3rnD?$V!{%c{U|JU+Y`M+U-^hDfZ zAO<zVn*{*X-+m%`5IsNzaJ0bpriAZJV%nQT0rf<Ip<-X<(oZ5`_or(%+rtPZfyQ56 zhby<-_0uM1Sb{?$c`+LAc(Qo+1wZw$T=KLg39Zn5ksA2j)*`E4gzmUN?%8I4JinvA zLl-Y^`ckxR*F2l~yp8v}TizeXUxdw7_WMWcYgR|hH8CZQj#4w<ZUCMKSJgE;H-M;P zKr-QcfbDX`gGR*}d1+SH9aUzDFnD0+Uuu3oVM`%pt79dj>WSEU^XGR$?t`|T)rmAM zSS3TQ3d3?+IOA3*_^wkm;tx_4r)6#cu5UAE&Fy_7L*9oazCTT@KSK>~w^R3BAfA5- z)1RFhJ5m`C+mKOjVcMqI=KNkF(nEcwh7`K`$Mb;teR%4r>6eLtvo|mS04qSc%C_|G zMk7N*gQ_$a5<C`G^%FcD0z8UY6m9F=^-oz5DK|5`s8Ct1l283AHO=SwYWBdB?A%#{ z`3hfT_HKtj-B0{of$(vKE&#cYw!H9TKJRL(x)l5*55FHcvqhhJ0}w)5KHtNJ?!G2Q z+kXAima&ofAthj|>ag>U{_ZsAlqGj&Q$is(r^?iFo5J-A4lOPJ?#D-mm8z+z>8WjQ zn@dHPm!WU3dR^C@lGprGOdS1Wo$MoN+iS<}7fvVgl~Vq{8_ZYvuPP`|nj;XYEei-J z3-Z$M+HGsF@ZAd(5pj}*7h7lC0P<T7ZvaHs?DHY~M7Dm6DTlS0Bc4V}=e)hu&u8;H zu@!5|<d?kdJz};Y57#Ug^9J7>2-2RX?r<z!34U^S?|h=&c$t^k>}^#(2G-EoCY<sv z9qMX8?9NZ29uL2$7JKXA{+!gQ*x2Sl@jKo~yM7<onI||@;Av+>ykyFHa%+hxoha3Y z4AZuxwK@nO7M0|9V2;n2A}nvFt3u9dk*aKO<)Ccg0$oT1V`dHq8KX!g3+#atu}7-0 zo>W5l%vJzwB@$k;d{>Uld7t*)>)cTj1Dhcbho=h}EP07Na^I@u0r|=%fK)C0a1h;Q z<^!9S3}B-2ryL4f&fh&XEGgF|QWpoVU(AYIJa=j#Gi4eX`TYE3T7Qp`70yXj`l8^q z${-Tz?uvFE;UfQr9sspC_-BIO59jt0WJ<FVt<u~c*s;o8Bu2eY_X8yv)UciaT<~Uj zGF79|4<s-pf6Q+Hi8E`q$;eM@6N8=CcC<nxO;-{t-&}D#^IUnA?S~frKJRH(e;14I z>x=J&lJ1ps2w%Y^A_P+RA0SPo_CmEWtFGKS!=j@Da_5O(gSJ(JeP%vaKJ9pl-+Z3` zrt|0A#cjD=zBkPzU_&FYp%&awi;?@0@7OI|Wf)0vw6N6pcLv!{n6RJSB2^ZV70`@u zvY>Opqo!5AvnS=Z771iE*5xNkb{gkoz31J(4|;<3_ULZ_;mWs~&t%H&ef4KO@7$5G z3n{*$_*VR>S2^78&eH}MARSga8E`K__vx8~$*JO6_2xYb7?$xXoN=plMFa!Q{F-`; ze(wExXoKNO(`&zUnbXtCUg$g(Q<@4Q@2W~m%iJ*qVJ40YaW8M7=6~bjSNU@FiQF&7 zmI3^9vOE^lP|Y0V(MdHTs9=X`(Nx!9hR>QjNnb82;b+fCuPEl@m6M!p!Ic}78z+xU zJe|olV6lN*Pzrwm-?6xZWuH9ZggHeEmB2~W{-Dy~et?p|MP}aN?#gn<4Y>?dkC>6Q zRNY37HuG_Z&2Ii6iXArgKEi|xh>pc?gh^RIX{1ys;NH^PhPa9G=d27li*|vy;A#sJ zeHQkVXOkCmn{)SLg8QlH_+Lf->2X@h#dW1^@N_m?S0jTkPWkSTB$r7taD+2;#IW3; zC(O-Sx;9qJ)w?$7$yu2fj>3SP64hb*E2U|Et$AcNtr4IyAqOTke*wgzBib2FS^Xzn z56d1eyc_r_rucE&7co=NO*Bmt&QHvsq*}oYE}w})h<7<Q#IrNcvk!wK(jcNtJSFSb zT;DqVzAT6=jW8X27kpi(>&<r1dSS+Xd`IW9q->4kIx4U4itJj@r}Qq^&{93|-|L~8 zGp<u>$Y$?$xA=!JqczpehQ^eKoOa^RXL*aSDMHxpd#1kc*5UH-V7(o`*NjP>{eI{2 zN^p@bgWF%$X1*3NZ)G*_yJ!2#feMk;5KjQk2PkiB6x3>S&tjXrR#<_yw&xd!KS>k! z6VKbY@<XeF!92Q+MBr`C(^_F<BFvQF1Mh=4@PF1sO?-v*-RHC%D}z26qYGcXKv|a7 zQ-01S*YoBN$gyfLvGWcXbYA{iGW9YRlG;rsO8Bs-sAazSHpA46ym+Jp*(wm6dRxwF zA{|eNlD49Pvv=>nJtd>3EAH@yAX=rp1SL^Px7`WJ;1mtMVUF%WCnvFc<OWu~rP{AT z!ej09KPASlnUaG+X)?$j0e%jc(+6;jGBSTQorzGl<`ghbBS58#w{jA`=S$@MfYBz9 zzCfT*>@Zx*LbFz~({~lsUiLtbZ}8Gjy}BNSy(aX#h39^pTP?{gHVO!;XpbI*5Odd~ zs=7gyzTY;|;Jbb)`mUutJ0D|bD}o~;a(4tpR0~H4)SBM_O!KC325o+s^Hdee%?V4W z376btYZ0_j^(qmyrg4~SA4nf+M4gc1=@wvWhkK@&XhCk@fmaHiL45c96AR-bFV+g6 z)b3Xx+(E3FZzS3=!>0!b#uE2lB#AJw(Wf38DT<44hxk`?%~XC?idj^>NHOdslt<~9 zxgAS|zik$WmA{V@>M04}oIp8uK77-l1vZKaCR@#hW9F8(abQEC)qc-$8FE@|YGvOA z%MXR8jveiZ<lwHxc1^=C34NU1p$Z@6bwhbe7tPgSFU%35fIqKKI6FozKB-_f@Ey60 zQ4F#$F1GKkD59pcAwHS;9||V^S<#UbKaniJ!#(5Du6$PUq_)z)FJ<b(6G__}2YZIE zN5sK6<tWr)?$XnATaL30EWphiNBtJ?l_@>RA?neBkWZY@X9tDccSTldh1et4CcF#p zU2TqP1fmgyAw4_n<`!x_Gv1zj_G`k8Bx-W0LUMG;MX**)_Nw~vWHa&3kirFFD(loA zHb=sSmxG{9Pl0T)R4`JRZ7|{V$HPs9<AxGNo{h$QpxnMo$648RvFN+{%k9CbQ8HHx z3naI8ztUVzyOCO7zk<6UDO6R8L4Q|pf|}cc9!l~me48vc>&IC$a{?$%UX?mhX@XZG zveX>OR$tm>oL0;Q!scql9vym0Nw|{v;{8hzUpQ2Ud)$5W!(3hJ?4L0}3Pp!Gyu=g< zZs2^*c=MnLtaDLHK<%&j@F%y5Mt%lSZr;-;No{jCbNhq?L`2wG)Qjp%;g@z<Wo12S zD{$B*gE84`Cez0^07FYrGG4~pG^iabZaJP-ylCGBGGrzTl{GD^)TE6e%J(zAZA{Mv z!2Yf(&7AFt7Ai(<8EZwyKBWpukZ)8wDT^7MczPc3Bgtc-+CH-=LlD!k2+AFe{66C_ z*1y4l?^>+2xPDln&BUa!rC(HM8~aZnxTZD>!WHz1?6|i6DJ0Kqg4HuL_3GGi>yOmX z-#Ya%+y2aWhRpD6dc3pxZiNi7Ox0!YcfrT1edi*ktWE&O^K@I|GiLjopk6~K4r4aH zmA8Lu6TTZJ408%#JvXDp9C&5sHJ&vhL)||w<Yq~*(F~LGLO7!4GQ;s2O@Yj?_iSE| zzUasMVcCFbBtE`~(k83)%wxY~JiljQ!z)#_uNWVYk5W~jh-NscGck3{UGzQ2yY;k` zN&@knkReF0SE4^_oJh?AYRB}j(#pUKM;nwy=g3SpflWedTXoO9xnrN-XK-AvYR=|= zdIM+?CiZdtu+AIu&vJgUwTlP^)1m4EudfM`owtdDjx8suVQByZJ*7utt67AB{Aaba z(W6!d5Lu?B=O<9&a4UI><Fqf=gyxkC`?yJc((>SVFwf3x#n)G?@}UwBzx0_XJbN8g zue1yG<gs9@7GptSV8HXB>1iCZ!D_@suG=63Ey=u`?O4)Q6LTuB5nII7j!wa_`X~5i z$X>g9!KGA6!`ynKQ;KY#Pfu6yOa^=~CA><a+vEiIvirdR8Uh<@MNLSkXlc2iya*mo zwCnF8h@~Yqt2|4e_JBI$MB4Ee$Q>IM)#kPxZ@s!VPtsA9abs}5_!FX!W#PeyQm4VD zf3QS?^wnZNB2kY{$P<TweM5x@IuNj(xaySax4|UoiS{DsM*$f!sjli*G@@ed%Wq66 z7gBT32FlrhAwJz$uh1B0xe!tEr4-lR#|21T2;&HV@%tlf7si2i)leZIbCpOZ$pp4? zsn!y<?gvFO8Wa4u)9Cw_Om9UDGAeC2D^!|Lw>TQwFbCJI4n_?fiRHm8*N*3d!-;MH zCsLp7HEV!)vP0{(ufbiMB);(x_+I+or~bIIkY~k_=<dMIQyV$=>Q_J80gi`8Enn%o zT$hsQp7EpylFKr?TKT97=UD-B@zS0apE>2#l*@`=mVAa7fHtqu+GpRxE3L!5(ME%^ z40OP0n5aJZrDu}$H~G*ey+vqxRfpn{iwb!MRAlu}{j+i-G>=eFkJdD)KvA&Mw$RUe zwHod;)E-c@w%CV~l+TjUcl78P3uNc1j&*B~wCP$k=&KJ@2jUw99GoVDsdoimS3j%S z<bj&T_<#@ET1@=%o3rR&r83V4eNqmpyDoiWyve5xQJlGJn<qHMc!J4bsq;2YKg=yp z+?U#%XpYt3;fWEK_NzmmR2;~U4uN~D%hbQIW$allD@Mv5W^k~1WCKGs0;<$aYO?eW znchjz-V+T_yb36{-j|5fFWhR9fb|W>jrNHuca;8c)Oywwwb%{U`Uk$OCA&V?OqF!N zq3XoE<X>~P!Ed9Aoaw&rJ3cbcw$1f~C(~*-nWm7OEGakfp)u45JQgm|Yj5!0u{TN2 z;RCRLl2n;6F4JTwLE=Q&7qypY;bY@)CwZth#5Axu5PAIE6of_FNUc%yGuS66ntFV^ zY%u2moEi*yh0JX_X&O8oS^RK8aCBT}^RG5`Hnm`fN#g}%YYa>5o*PL|&Y#Z|lS|*C zExVo-vVXzOJ6evQ4NCY~N9J*y>p$&X36aid{loDxvJN8X0%B#a;wZ}gNk4jMHN+kG zkWOJ0;Ti5j=cA>Ke_SZz^JMYT(AbZQWWQq()wYnhcWa@Sru9s)r1M8Z+YLY(m#=9z zKG;<ra;4?760_fU33N2^Oyv>dD4IIb@^$guxrGn-=@U?Q-*!qlcO(xAfGbrgaWVEO zoabVdUs|vMDEg#sS@Vc6g?tuF!8h*QnjElPWwc-9T#hCg=4G0E<aq;_wHqB!+J)Pw zS4rA=Md;Mv)rdWMevR@|wx9f1%S^YG^ZwKS$K`W*0>nCG{-;AC=8ce#oIja9aX=Z= zz~hAR3L1AUfa8y_1_YKjMpIj+$PpEm?<t9$N2`hYyv1D}SYu-j3Na97YHY}oL-%cS zWP5mwKMm>i4M3-`=iA>`o6wKWPR`dL<<GTf)5OQIiPrkuLLgxJ4M1wa%{<6$i`|Do z6u!v*bLDr{^W=|qJA<F04v**Rio@mYDJZ++w_b!))V4G!Gdy6ES7^j}Q%HfgH5d&* zj@!$5m?yWQo%^<`J)!pnR*_^1r;@j8((jBBm4BMc$zR~+{l(Oo{*#fp6r>=NX~k*j zIR;Pnla45`{tF3cX&ya&A;@GPcI{&It>Nv4-G;}c8}2qr8;C(z`!g-glB{V#T2&E- z!>lqb>NaQ}AX`ieReB50?`w!e@2iAil|kz-3#4P3W}q!_j&-uGclGF8`aiDUBw`6e zTmeKaeMzlqTuG5+K|`jy-|3OC=@C5X4WQeQqtZKZC#^5Z@uwvwad5i&!8)!#Ct2?B zp`bGlY2XrNF11fh5Cri7opDLX`jDBqpiBLOe4)9YPABj8a!Gs6b7w^=Y6Zk6kyOD} zX;u;bhjG-hH-J)9E+(#0{yNCIeljePU$Au+4PhUVn_&FVjXrYT*FI`FsSIg0)2zzC zmvSDt;TSLrHtoz@$=!k@y%crJG(};~(I818RsAaV<KM^&vogJi&HK!}9;d%?>{Egn zq(&cK4g;|3l5cmJhIzgCN)Ph&IpN%|Kg`CIL~MNIpD7SI!Fn00kCU%HL8j`|BQ(js ziE+u*47l5jYL%>SDz|{EOr@l65&5VsM~Sqxzjdb;{?V#-%Rq84=eu4o;ciQjQe3Yj z*DwLNPV~lq*+aM$o$rcYRzGv@v(>M!lFUXmEap0iT;?cydAR}?|8!}hJTeKQOf4o- zEmvI$2}vKL#`2DJZQxExRe#i29g5;7e?%X9w=+-NMR7#ih`TsEqx83=?we#Kc~A;H zmdBDZ4UN;xz8oVeWH8G}A=o`qd{IpTi>hA%ziisMYXUsnu^K*Za0dX^Fj<C?a;K76 zhunBYU5`kTryK@5tDOY~p{{Zoov9*96oqM>s$7mwf?x%y72_Yo&+@&p^<5EeS+p1v zx@Y*+Tw{x*%EvGBuCBge;K}`zrp@~U2$>2tIMc2L6O~9)>seJtiS#GSY+BZ$ANlxN zq2cSg*st0^nrA~l;miHyq^cAK8jP!ncBr<bdjTQHP`qoWM#l`+wDy<w*Dd;zri@;w zk^0grb?hpZy&CoUpLsZ>O*kn_k%qwFWSY#hfn^B(xEhbl7B1JLOkaNagn}VG?QaRX zFF``4+|wNW15l&ZPli4)UJ{j^q**fcLZl73n)(Zg4;>D=l0Z{zVeUVk*7DWH#0`8p zn312TotGs)^ElpA;M{lnOgtgn&YdHWe6;A?CD5(%)way@TvKSDdFI@c;=95XUa$cD zYw)sZzshGYboW#F#5$+ww8=)m^{mbmHmY+QxnsQ8I2c(M%ZuExUi3|nOxPmZl<M+W zuTB8FvaZWkjaiMicKBvb<%CrP2hWTNxo~^$tF3at3*R+X3C$bzqo+#Uo8(5b7IWtx z-g@{I7k1rRBPZ*?h}uzu&A5W}{5LpGGBfHIJ@$kOso^lO{AY~JGm-D}Ear`j5=7yX zW(1V6=J`W7=~f1zoYnuNhC&V)&z9<n=`VHWKyI=gEVQKfkSZG}?KnDxY$P>#;@jx= zS>qz_l6*TZ9=Di8j>h;rdZIn@eK_hXy`Ven~_j>lt{j<RkMhaZ<ak2<>sj`_gZG zWO-i68mDLcT8JF}y`NH=u)5tM`j}dn$E8q0$5_?6?)8NUH@CJ$ebM4fE>)!m{fCXv z)<G^O3$|u=z`n~9z0^Ao6*WnW&wCT~ZU7bV@oH%FNP%?Npj}CX&YlCsr1lK}ocE<8 zfJuSIb)e=;k)7VqH?)DixMgd(jbl<lt@wvVfP*!|gAYal{$ICYLgTd!w)~p$m}lcD zd%bot9E5BGqqqTUtQ|{hbBnUFb=BbzQs;zkY<N)pdjRBOh8ew4K!-%{nB>`_@Ahjs z7=}5x9#+Y#v9g=ruOBK8+PdrEBY|XA3_`5BXn_&o)djIZsT*}vK5SZ|)A~jGuDOjg zGGL3!J-VgpcVKM&>Tjv$rU_Op9+3*{pExMXX3!l|8AE}L1SG!hyt*V()ugdN&_{{d zr=b$@P<#`v>=o3~^p-?Y3_s7jY9_%rJ-Yc%NYG5fqte*LE0k99{p}<Q@e+of=&;-u z_&LhP)zoZvBe6$UrsUpgE6-iB2}6eJ<cmtl#P2<G1QzYw$-<Bpd}hpK!~qrB)*`BM z8^4wDWm<DF8d0gOe8O<KB@Z>&2k<KYmFC=<3Wbzic(8Lr<g$@Y<%;YiKO?0C>^;&5 z_Xm8q#r?FOT;rd;`Vwc17quBnyezSQ<;!FeJ>a0atHWwpUSse!$|41pN<~4Co)FRl zaCsSN?$m|Q8#2U_jR&agNOlez?5rra2#@b3Nm92jZ3k&%V`W!D>s=XN{5n=kwsHuV z=C_hvi$&;WY>o_h)r$U<$1!`;){$o9hmevN1-Et75+|d1kPlszVqLWaRwd)ucZg9x z*>irE@%V3c&EEhXX$uVq44Nlj7AxfH$c#z)w2cOWa<jB)<Hx;mU%E*iR3z(~2=Hyb zCFI7koE)Z!rRvo7rvK9;(UUQ6EEAxUNM!H7kwoj`hS}@3urktI7^5Qn#nDp9j5u){ z%r)ZSZteG4HFa`IDb5}ACHfC+^rj?>Vk1)ZSPa4V`B%yt5pJ3~3@KD&p*_eMe{jHS zK_Aa1%0778HHZ=pVOj5+l2Qy{S@dyDoct~KLB2=rdKSr+(@*vr=Py<57;;(8fyX8u zkABM*-pRn4GQCzt@RGjLCfji!KNw{4^Udb9$JT{u=)JN}q9xxOUQwzpuwaMtLW7Od zt=&b{IMrI1{9IV;g|sJwXXwYrSa|?aNpz}m=Ru}k@o+NM)LG-wcl99o_-(ciElsVo ztpes<Z*ep-{k6bHT#a<-9zip)9jJ8D@t%<vH*vULf!=Hcrr#udHagV3GzTL~?2(xE zh=p{zFk=KH#nbddBQS2R$l#89@LEpV6F=qoitt;Qc@BKKf%RxVIytTuLBU}`l-#zm zT#HM0G<sXt%1^;fZx;t@MGbO?x2x+ddu}sE@!g8y<*cuLxa$BU+=+a~NbjV(5zE`c z2TgjPndCAAJv8|8MId5Zd7gNEC20EhB~*8^Dqcm0ChX99#dO!Fgv^RhB&4G727uO$ z=r!LZH|J-8u=4R5x(D3=lrPNgE7nTZLa<qKHQEq`+enO?&QiFc7Pv?YY!dU|7_qig z%loXus(bjrQ(WRe<pXIfn+&m7AI`S*fun~GT+%#mde}!SegBbcMqZ=L)db4F+c2#H zHH0#24H)6$K<%T4=9=t%4W>5nHNaij%3^zLB<jGFe#xua;DW^kim*A`pj$$qgfSr% z0PNp?=?E){{dN9Ym3;`6D{%n9zcRYrPNQ<j?sM_&noC_AsDV9>r;LMsdLbiuwzaA` z+i)qh1zwu}NOy``sUb~ZpxnBeol*^i5is8nDB)dl&LD*k{W|WI)C#5Z7TdP<#je#L z@{d`y-2r4FMrdm>j}kzN%u+$A7n%@a{wo>~hCUfI84HP@y0Ve`3?r<@-R*=R@3n3w zfL}Joj!DEERJMa(e~A=V1AUTg&Qm8zdzP3oDh?!$Ips2qdt|MjEcWg_*0G63X@@mo zJyC)`fLwY`e>@aWwYc2*Z5)}*l=qWsQMnx#^!wVad{(Gt`!^s6xX>cU^85x+17Fls zl~dSYGnK?B;%s3;LzqXdZj8-;skrd}P;vjw)cg-KSCWEAiY&PjkQaCem+wjlUupkK zAhfjYswV66ah`A!hZ4REX4PyZ>8)`TRXB<_hZ6qc4v8o);XK$un}?!Vfe@}sY^cno zWS7B_x#^xX%j*c3P=Jd+I(jC5As#%GEGWWz56o6S%-M7DuH3}LCcR@_4s>7@wmV}d zcGp<(p3K!>e_{$w7}?oeW{vZm(8pUvm@O&8moqEGboz7cr@Os5ne-kX!xh}1R9Dr( zZ%2r}A5h`|i44Bm9ZRzb)7pew*ioi;7$OQwGf!01-YZ?jV({@chpMUz>p@0pJI>Ox zQjUe&-+TxZ8T$KB`|QWA^PJC8u6Nyc`Nkas%os<2$X-Q(o$SS|-xMM*!Q3%JRgPqG q;~eW@IzE`94ZWCaUh{$`L$dwvCXC!`sWF8A5AX4Rj(FJ3=l=p&jaKgf literal 0 HcmV?d00001 diff --git a/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/particle.jpg b/examples/oopnbody/src/com/amd/aparapi/examples/oopnbody/particle.jpg new file mode 100644 index 0000000000000000000000000000000000000000..70e28ac5ea0a5ae58736f61f46ef5a326fbfbde0 GIT binary patch literal 1056 zcmex=<NpH&0WUXCHwH#VMg|WcWcYuZ!5PG(2!ITs$N-S${|6WZIT#ce6qp&67?=bZ znFSgDA7PMZU|?j0n9TqMj7-cdtZeKYoLt;M1zQCem>8LvnOK-vSy@<ss%wGr3@n1I zLW+itY{G$w>`H|qMvW5}awt1(JSZA;@q>zSQc)8pmzcPOq?D?fx`w8fiK&^ng{76V zi>sTvho@I?NN8AiL}XNQN@`kqMrKxVNoiSmMP*fUOKV$uM`zch$y26In?7UatVN5L zEM2yI#mZHiHgDOwZTpU$yAB;ba`f2o6DLnyx_ss8wd*%--g@}x@sp>|p1*kc>f@)+ zU%r0({^RE_kiQrinBhSN@fe!F1cClyVqsxsVF&q(k*OSrnFU!`6%E;h90S=C3x$=8 z8aYIqCNA7~kW<+>=!0ld(M2vX6_bamA3<IN`;0h`HId~rxW^Fwy2Zf5%m|D;W<dsf zhJRuv{Cu8e50jnT-W{0txbbJtuj<nEbr<XeUr9~T`1Y;XcQ?!1-aa;~0>cBZY;CjZ zTk7Py1Fqye%xyn2%T)1zLR;dG@9%2;FaP@apP?zP{-B=8`dh{BnJR7fZYpi{d^RuQ zU@v3La}}P+FaJp@$R9u6VJCb)bE2opw@I$s)t~iFdA{LE)$b=s`|3XOHL9*YF-b9g zc0<(!`x7U>{mK5V1~hZUe};n=U*6wrd-vMUWm}?*ua5Deb=P8qROEKPvEuuw&ivEu z$Kpjt6DuoLaT|F(5bitUX8-H{`W5V+A0{8UR^|J5>1nOqt|qx#``khs&o5stQ~Kmn z_OBg4lZ)yPhOetnTWe&Yo0qHHx}Z-o_kKb+V`00@bB5;^%3jAGJ=Xe2d`qH=U*VqQ zIq9;W?B?3b{jj^c=uhC@jXg@SUM<NC)=U*o<`uuay#KL-{N?q3M6Uni@-%;IT3K0N zT+Y*LlPi<e!=;qWWWadn@%GQ>MgQ#o<9j`M|L$EjOrJKM$b0hqdSl;L{SG_zd#?&N roh;Ou-R@SVA~-)j=JVvf=gjNdvUk0{D=Fu$^6p)&bzSTW=KnVVV0)Pr literal 0 HcmV?d00001 -- GitLab