From 7a4f9fe683cabff00e941fd3cd8a257822e21923 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 5 Mar 2015 06:22:38 +0100 Subject: Disable Demo 'VertexArrayRange', this extension is no more supported --- src/demos/vertexArrayRange/VertexArrayRange.java | 737 --------------------- .../VertexArrayRange.java-disabled | 737 +++++++++++++++++++++ 2 files changed, 737 insertions(+), 737 deletions(-) delete mode 100644 src/demos/vertexArrayRange/VertexArrayRange.java create mode 100644 src/demos/vertexArrayRange/VertexArrayRange.java-disabled (limited to 'src') diff --git a/src/demos/vertexArrayRange/VertexArrayRange.java b/src/demos/vertexArrayRange/VertexArrayRange.java deleted file mode 100644 index 37268af..0000000 --- a/src/demos/vertexArrayRange/VertexArrayRange.java +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Portions Copyright (C) 2003 Sun Microsystems, Inc. - * All rights reserved. - */ - -/* - * - * COPYRIGHT NVIDIA CORPORATION 2003. ALL RIGHTS RESERVED. - * BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO: - * - * 1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS - * IN AND TO THE SOFTWARE; - * - * 2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT - * INCLUDING THIS NOTICE AND AGREEMENT; - * - * 3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY - * APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND - * THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, - * EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED - * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY - * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES - * WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS - * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS - * INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS' - * FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE, - * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - * - */ - -package demos.vertexArrayRange; - -import demos.common.Demo; -import demos.common.DemoListener; -import java.awt.BorderLayout; -import java.awt.Frame; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import com.jogamp.opengl.GL; -import com.jogamp.opengl.GL2ES1; -import com.jogamp.opengl.GL2; -import com.jogamp.opengl.GLAutoDrawable; -import com.jogamp.opengl.awt.GLCanvas; -import com.jogamp.opengl.glu.GLU; -import com.jogamp.opengl.util.Animator; -import javax.swing.JOptionPane; - - - -/**

A port of NVidia's [tm] Vertex Array Range demonstration to - OpenGL[tm] for Java[tm] and the Java programming language. The - current web site for the demo (which does not appear to contain - the original C++ source code for this demo) is here.

- -

This demonstration requires the following: - -

- -

- -

This demonstration illustrates the effective use of the - java.nio direct buffer classes in JDK 1.4 to access memory outside - of the Java garbage-collected heap, in particular that returned - from the NVidia-specific routine wglAllocateMemoryNV. This memory - region is used in conjunction with glVertexArrayRangeNV.

- -

On a 750 MHz PIII with an SDRAM memory bus and a GeForce 256 - running the Java HotSpot[tm] Client VM and OpenGL for Java 2.8, - this demonstration attains 90% of the speed of the compiled C++ - code, with a frame rate of 27 FPS, compared to 30 FPS for the C++ - version. On higher-end hardware (a dual 667 MHz PIII with RDRAM - and a GeForce 2) the demo currently attains between 65% and 75% of - C++ speed with the HotSpot Client and Server compilers, - respectively.

*/ - -public class VertexArrayRange extends Demo { - public static void main(String[] args) { - boolean startSlow = false; - - if (args.length > 1) { - usage(); - } - - if (args.length == 1) { - if (args[0].equals("-slow")) { - startSlow = true; - } else { - usage(); - } - } - - GLCanvas canvas = new GLCanvas(); - VertexArrayRange demo = new VertexArrayRange(); - if (startSlow) { - demo.setFlag('v', false); // VAR off - } - canvas.addGLEventListener(demo); - - final VertexArrayRange f_demo = demo; - canvas.addKeyListener(new KeyAdapter() { - public void keyTyped(KeyEvent e) { - f_demo.dispatchKey(e.getKeyChar()); - } - }); - - - final Animator animator = new Animator(canvas); - animator.setRunAsFastAsPossible(true); - demo.setDemoListener(new DemoListener() { - public void shutdownDemo() { - runExit(animator); - } - public void repaint() {} - }); - - Frame frame = new Frame("Very Simple NV_vertex_array_range demo"); - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - runExit(animator); - } - }); - frame.setLayout(new BorderLayout()); - canvas.setSize(800, 800); - frame.add(canvas, BorderLayout.CENTER); - frame.pack(); - frame.setVisible(true); - canvas.requestFocus(); - - animator.start(); - } - - private static void usage() { - System.out.println("usage: java VertexArrayRange [-slow]"); - System.out.println("-slow flag starts up using data in the Java heap"); - System.exit(0); - } - - public VertexArrayRange() { - setFlag(' ', true); // animation on - setFlag('i', true); // infinite viewer and light - setFlag('v', true); // VAR on - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private GLU glu = new GLU(); - private boolean[] b = new boolean[256]; - private static final int SIZEOF_FLOAT = 4; - private static final int STRIP_SIZE = 48; - private int tileSize = 9 * STRIP_SIZE; - private int numBuffers = 4; - private int bufferLength = 1000000; - private int bufferSize = bufferLength * SIZEOF_FLOAT; - private static final int SIN_ARRAY_SIZE = 1024; - - private FloatBuffer bigArrayVar; - private FloatBuffer bigArraySystem; - private FloatBuffer bigArray; - private IntBuffer[] elements; - private float[] xyArray; - - static class VarBuffer { - public FloatBuffer vertices; - public FloatBuffer normals; - public int fence; - } - private VarBuffer[] buffers; - - private float[] sinArray; - private float[] cosArray; - - // Primitive: GL_QUAD_STRIP, GL_LINE_STRIP, or GL_POINTS - private int primitive = GL2.GL_QUAD_STRIP; - - // Animation parameters - private float hicoef = .06f; - private float locoef = .10f; - private float hifreq = 6.1f; - private float lofreq = 2.5f; - private float phaseRate = .02f; - private float phase2Rate = -0.12f; - private float phase = 0; - private float phase2 = 0; - - // Temporaries for computation - float[] ysinlo = new float[STRIP_SIZE]; - float[] ycoslo = new float[STRIP_SIZE]; - float[] ysinhi = new float[STRIP_SIZE]; - float[] ycoshi = new float[STRIP_SIZE]; - - // For thread-safety when dealing with keypresses - private volatile boolean toggleVAR = false; - private volatile boolean toggleLighting = false; - private volatile boolean toggleLightingModel = false; - private volatile boolean recomputeElements = false; - - // Frames-per-second computation - private boolean firstProfiledFrame; - private int profiledFrameCount; - private int numDrawElementsCalls; - private long startTimeMillis; - - static class PeriodicIterator { - public PeriodicIterator(int arraySize, - float period, - float initialOffset, - float delta) { - float arrayDelta = arraySize * (delta / period); // floating-point steps-per-increment - increment = (int)(arrayDelta * (1<<16)); // fixed-point steps-per-increment - - float offset = arraySize * (initialOffset / period); // floating-point initial index - initOffset = (int)(offset * (1<<16)); // fixed-point initial index - - arraySizeMask = 0; - int i = 20; // array should be reasonably sized... - while((arraySize & (1<> 16) & arraySizeMask; - } - - public void incr() { - index += increment; - } - - public void decr() { - index -= increment; - } - - public void reset() { - index = initOffset; - } - - private int arraySizeMask; - // fraction bits == 16 - private int increment; - private int initOffset; - private int index; - } - - private void setFlag(char key, boolean val) { - b[((int) key) & 0xFF] = val; - } - - private boolean getFlag(char key) { - return b[((int) key) & 0xFF]; - } - - private void ensurePresent(GL gl, String function) { - if (!gl.isFunctionAvailable(function)) { - final String message = "OpenGL routine \"" + function + "\" not available"; - new Thread(new Runnable() { - public void run() { - JOptionPane.showMessageDialog(null, message, "Unavailable extension", JOptionPane.ERROR_MESSAGE); - shutdownDemo(); - } - }).start(); - throw new RuntimeException(message); - } - } - - public void init(GLAutoDrawable drawable) { - // drawable.setGL(new TraceGL(drawable.getGL(), System.err)); - // drawable.setGL(new DebugGL(drawable.getGL())); - - GL2 gl = drawable.getGL().getGL2(); - - // Try and disable synch-to-retrace for fastest framerate - gl.setSwapInterval(0); - - try { - ensurePresent(gl, "glVertexArrayRangeNV"); - ensurePresent(gl, "glGenFencesNV"); - ensurePresent(gl, "glSetFenceNV"); - ensurePresent(gl, "glTestFenceNV"); - ensurePresent(gl, "glFinishFenceNV"); - ensurePresent(gl, "glAllocateMemoryNV"); - } catch (RuntimeException e) { - shutdownDemo(); - throw (e); - } - - gl.glEnable(GL.GL_DEPTH_TEST); - - gl.glClearColor(0, 0, 0, 0); - - gl.glEnable(GL2ES1.GL_LIGHT0); - gl.glEnable(GL2ES1.GL_LIGHTING); - gl.glEnable(GL2ES1.GL_NORMALIZE); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_AMBIENT, new float[] {.1f, .1f, 0, 1}, 0); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_DIFFUSE, new float[] {.6f, .6f, .1f, 1}, 0); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_SPECULAR, new float[] { 1, 1, .75f, 1}, 0); - gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL2ES1.GL_SHININESS, 128.f); - - gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, .5f, 0}, 0); - gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 0); - - // NOTE: it looks like GLUT (or something else) sets up the - // projection matrix in the C version of this demo. - gl.glMatrixMode(GL2ES1.GL_PROJECTION); - gl.glLoadIdentity(); - glu.gluPerspective(60, 1.0, 0.1, 100); - gl.glMatrixMode(GL2ES1.GL_MODELVIEW); - - allocateBigArray(gl, true); - allocateBuffersAndFences(gl); - - sinArray = new float[SIN_ARRAY_SIZE]; - cosArray = new float[SIN_ARRAY_SIZE]; - - for (int i = 0; i < SIN_ARRAY_SIZE; i++) { - double step = i * 2 * Math.PI / SIN_ARRAY_SIZE; - sinArray[i] = (float) Math.sin(step); - cosArray[i] = (float) Math.cos(step); - } - - if (getFlag('v')) { - gl.glEnableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); - gl.glVertexArrayRangeNV(bufferSize, bigArrayVar); - bigArray = bigArrayVar; - } else { - bigArray = bigArraySystem; - } - setupBuffers(); - gl.glEnableClientState(GL2ES1.GL_VERTEX_ARRAY); - gl.glEnableClientState(GL2ES1.GL_NORMAL_ARRAY); - - computeElements(); - } - - private void allocateBuffersAndFences(GL2 gl) { - buffers = new VarBuffer[numBuffers]; - int[] fences = new int[1]; - for (int i = 0; i < numBuffers; i++) { - buffers[i] = new VarBuffer(); - gl.glGenFencesNV(1, fences, 0); - buffers[i].fence = fences[0]; - } - } - - private void setupBuffers() { - int sliceSize = bufferLength / numBuffers; - for (int i = 0; i < numBuffers; i++) { - int startIndex = i * sliceSize; - buffers[i].vertices = sliceBuffer(bigArray, startIndex, sliceSize); - buffers[i].normals = sliceBuffer(buffers[i].vertices, 3, - buffers[i].vertices.limit() - 3); - } - } - - private void dispatchKey(char k) { - setFlag(k, !getFlag(k)); - // Quit on escape or 'q' - if ((k == (char) 27) || (k == 'q')) { - shutdownDemo(); - return; - } - - if (k == 'r') { - if (getFlag(k)) { - profiledFrameCount = 0; - numDrawElementsCalls = 0; - firstProfiledFrame = true; - } - } - - if (k == 'w') { - if (getFlag(k)) { - primitive = GL2.GL_LINE_STRIP; - } else { - primitive = GL2.GL_QUAD_STRIP; - } - } - - if (k == 'p') { - if (getFlag(k)) { - primitive = GL2.GL_POINTS; - } else { - primitive = GL2.GL_QUAD_STRIP; - } - } - - if (k == 'v') { - toggleVAR = true; - } - - if (k == 'd') { - toggleLighting = true; - } - - if (k == 'i') { - toggleLightingModel = true; - } - - if('h'==k) - hicoef += .005; - if('H'==k) - hicoef -= .005; - if('l'==k) - locoef += .005; - if('L'==k) - locoef -= .005; - if('1'==k) - lofreq += .1f; - if('2'==k) - lofreq -= .1f; - if('3'==k) - hifreq += .1f; - if('4'==k) - hifreq -= .1f; - if('5'==k) - phaseRate += .01f; - if('6'==k) - phaseRate -= .01f; - if('7'==k) - phase2Rate += .01f; - if('8'==k) - phase2Rate -= .01f; - - if('t'==k) { - if(tileSize < 864) { - tileSize += STRIP_SIZE; - recomputeElements = true; - System.err.println("tileSize = " + tileSize); - } - } - - if('T'==k) { - if(tileSize > STRIP_SIZE) { - tileSize -= STRIP_SIZE; - recomputeElements = true; - System.err.println("tileSize = " + tileSize); - } - } - } - - public void dispose(GLAutoDrawable drawable) { - } - - public void display(GLAutoDrawable drawable) { - GL2 gl = drawable.getGL().getGL2(); - - // Check to see whether to animate - if (getFlag(' ')) { - phase += phaseRate; - phase2 += phase2Rate; - - if (phase > (float) (20 * Math.PI)) { - phase = 0; - } - - if (phase2 < (float) (-20 * Math.PI)) { - phase2 = 0; - } - } - - PeriodicIterator loX = - new PeriodicIterator(SIN_ARRAY_SIZE, (float) (2 * Math.PI), phase, (float) ((1.f/tileSize)*lofreq*Math.PI)); - PeriodicIterator loY = new PeriodicIterator(loX); - PeriodicIterator hiX = - new PeriodicIterator(SIN_ARRAY_SIZE, (float) (2 * Math.PI), phase2, (float) ((1.f/tileSize)*hifreq*Math.PI)); - PeriodicIterator hiY = new PeriodicIterator(hiX); - - if (toggleVAR) { - if (getFlag('v')) { - gl.glEnableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); - gl.glVertexArrayRangeNV(bufferSize, bigArrayVar); - bigArray = bigArrayVar; - } else { - gl.glDisableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); - bigArray = bigArraySystem; - } - toggleVAR = false; - setupBuffers(); - } - - if (toggleLighting) { - if (getFlag('d')) { - gl.glDisable(GL2ES1.GL_LIGHTING); - } else { - gl.glEnable(GL2ES1.GL_LIGHTING); - } - toggleLighting = false; - } - - if (toggleLightingModel) { - if(getFlag('i')) { - // infinite light - gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, .5f, 0 }, 0); - gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 0); - } else { - gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, -.5f, 1 }, 0); - gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 1); - } - toggleLightingModel = false; - } - - if (recomputeElements) { - computeElements(); - recomputeElements = false; - } - - gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); - - gl.glPushMatrix(); - - final float[] modelViewMatrix = new float[] { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, -1, 1 - }; - gl.glLoadMatrixf(modelViewMatrix, 0); - - // FIXME: add mouse interaction - // camera.apply_inverse_transform(); - // object.apply_transform(); - - int cur = 0; - int numSlabs = tileSize / STRIP_SIZE; - - for(int slab = numSlabs; --slab>=0; ) { - cur = slab % numBuffers; - if (slab >= numBuffers) { - if (!gl.glTestFenceNV(buffers[cur].fence)) { - gl.glFinishFenceNV(buffers[cur].fence); - } - } - - FloatBuffer v = buffers[cur].vertices; - int vertexIndex = 0; - - gl.glVertexPointer(3, GL.GL_FLOAT, 6 * SIZEOF_FLOAT, v); - gl.glNormalPointer(GL.GL_FLOAT, 6 * SIZEOF_FLOAT, buffers[cur].normals); - - for(int jj=STRIP_SIZE; --jj>=0; ) { - ysinlo[jj] = sinArray[loY.getIndex()]; - ycoslo[jj] = cosArray[loY.getIndex()]; loY.incr(); - ysinhi[jj] = sinArray[hiY.getIndex()]; - ycoshi[jj] = cosArray[hiY.getIndex()]; hiY.incr(); - } - loY.decr(); - hiY.decr(); - - for(int i = tileSize; --i>=0; ) { - float x = xyArray[i]; - int loXIndex = loX.getIndex(); - int hiXIndex = hiX.getIndex(); - - int jOffset = (STRIP_SIZE-1)*slab; - float nx = locoef * -cosArray[loXIndex] + hicoef * -cosArray[hiXIndex]; - - // Help the HotSpot Client Compiler by hoisting loop - // invariant variables into locals. Note that this may be - // good practice for innermost loops anyway since under - // the new memory model operations like accidental - // synchronization may force any compiler to reload these - // fields from memory, destroying their ability to - // optimize. - float locoef_tmp = locoef; - float hicoef_tmp = hicoef; - float[] ysinlo_tmp = ysinlo; - float[] ysinhi_tmp = ysinhi; - float[] ycoslo_tmp = ycoslo; - float[] ycoshi_tmp = ycoshi; - float[] sinArray_tmp = sinArray; - float[] xyArray_tmp = xyArray; - - for(int j = STRIP_SIZE; --j>=0; ) { - float y; - - y = xyArray_tmp[j + jOffset]; - - float ny; - - v.put(vertexIndex, x); - v.put(vertexIndex + 1, y); - v.put(vertexIndex + 2, (locoef_tmp * (sinArray_tmp[loXIndex] + ysinlo_tmp[j]) + - hicoef_tmp * (sinArray_tmp[hiXIndex] + ysinhi_tmp[j]))); - v.put(vertexIndex + 3, nx); - ny = locoef_tmp * -ycoslo_tmp[j] + hicoef_tmp * -ycoshi_tmp[j]; - v.put(vertexIndex + 4, ny); - v.put(vertexIndex + 5, .15f); //.15f * (1.f - sqrt(nx * nx + ny * ny)); - vertexIndex += 6; - } - loX.incr(); - hiX.incr(); - } - loX.reset(); - hiX.reset(); - - for (int i = 0; i < elements.length; i++) { - ++numDrawElementsCalls; - gl.glDrawElements(primitive, elements[i].capacity(), GL2.GL_UNSIGNED_INT, elements[i]); - if(getFlag('f')) { - gl.glFlush(); - } - } - - gl.glSetFenceNV(buffers[cur].fence, GL2.GL_ALL_COMPLETED_NV); - } - - gl.glPopMatrix(); - - gl.glFinishFenceNV(buffers[cur].fence); - - if (getFlag('r')) { - if (!firstProfiledFrame) { - if (++profiledFrameCount == 30) { - long endTimeMillis = System.currentTimeMillis(); - double secs = (endTimeMillis - startTimeMillis) / 1000.0; - double fps = 30.0 / secs; - double ppf = tileSize * tileSize * 2; - double mpps = ppf * fps / 1000000.0; - System.err.println("fps: " + fps + " polys/frame: " + ppf + " million polys/sec: " + mpps + - " DrawElements calls/frame: " + (numDrawElementsCalls / 30)); - profiledFrameCount = 0; - numDrawElementsCalls = 0; - startTimeMillis = System.currentTimeMillis(); - } - } else { - startTimeMillis = System.currentTimeMillis(); - firstProfiledFrame = false; - - } - } - } - - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} - - // Unused routines - public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} - - private void allocateBigArray(GL2 gl, boolean tryAgain) { - float priority = .5f; - - bigArraySystem = setupBuffer(ByteBuffer.allocateDirect(bufferSize)); - - float megabytes = (bufferSize / 1000000.f); - try { - bigArrayVar = setupBuffer(gl.glAllocateMemoryNV(bufferSize, 0, 0, priority)); - } - catch (OutOfMemoryError e1) { - // Try a higher priority - try { - bigArrayVar = setupBuffer(gl.glAllocateMemoryNV(bufferSize, 0, 0, 1.f)); - } - catch (OutOfMemoryError e2) { - if (!tryAgain) { - throw new RuntimeException("Unable to allocate " + megabytes + - " megabytes of fast memory. Giving up."); - } - - System.err.println("Unable to allocate " + megabytes + - " megabytes of fast memory. Trying less."); - bufferSize /= 2; - numBuffers /= 2; - allocateBigArray(gl, false); - return; - } - } - - System.err.println("Allocated " + megabytes + " megabytes of fast memory"); - } - - private FloatBuffer setupBuffer(ByteBuffer buf) { - buf.order(ByteOrder.nativeOrder()); - return buf.asFloatBuffer(); - } - - private FloatBuffer sliceBuffer(FloatBuffer array, - int sliceStartIndex, int sliceLength) { - array.position(sliceStartIndex); - FloatBuffer ret = array.slice(); - array.position(0); - ret.limit(sliceLength); - return ret; - } - - private void computeElements() { - xyArray = new float[tileSize]; - for (int i = 0; i < tileSize; i++) { - xyArray[i] = i / (tileSize - 1.0f) - 0.5f; - } - - elements = new IntBuffer[tileSize - 1]; - for (int i = 0; i < tileSize - 1; i++) { - elements[i] = IntBuffer.allocate(2 * STRIP_SIZE); - for (int j = 0; j < 2 * STRIP_SIZE; j += 2) { - elements[i].put(j, i * STRIP_SIZE + (j / 2)); - elements[i].put(j+1, (i + 1) * STRIP_SIZE + (j / 2)); - } - } - } - - private static void runExit(final Animator animator) { - // Note: calling System.exit() synchronously inside the draw, - // reshape or init callbacks can lead to deadlocks on certain - // platforms (in particular, X11) because the JAWT's locking - // routines cause a global AWT lock to be grabbed. Run the - // exit routine in another thread. - new Thread(new Runnable() { - public void run() { - animator.stop(); - System.exit(0); - } - }).start(); - } -} diff --git a/src/demos/vertexArrayRange/VertexArrayRange.java-disabled b/src/demos/vertexArrayRange/VertexArrayRange.java-disabled new file mode 100644 index 0000000..37268af --- /dev/null +++ b/src/demos/vertexArrayRange/VertexArrayRange.java-disabled @@ -0,0 +1,737 @@ +/* + * Portions Copyright (C) 2003 Sun Microsystems, Inc. + * All rights reserved. + */ + +/* + * + * COPYRIGHT NVIDIA CORPORATION 2003. ALL RIGHTS RESERVED. + * BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO: + * + * 1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS + * IN AND TO THE SOFTWARE; + * + * 2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT + * INCLUDING THIS NOTICE AND AGREEMENT; + * + * 3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY + * APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND + * THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, + * EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED + * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY + * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES + * WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS + * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS + * INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS' + * FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +package demos.vertexArrayRange; + +import demos.common.Demo; +import demos.common.DemoListener; +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import com.jogamp.opengl.GL; +import com.jogamp.opengl.GL2ES1; +import com.jogamp.opengl.GL2; +import com.jogamp.opengl.GLAutoDrawable; +import com.jogamp.opengl.awt.GLCanvas; +import com.jogamp.opengl.glu.GLU; +import com.jogamp.opengl.util.Animator; +import javax.swing.JOptionPane; + + + +/**

A port of NVidia's [tm] Vertex Array Range demonstration to + OpenGL[tm] for Java[tm] and the Java programming language. The + current web site for the demo (which does not appear to contain + the original C++ source code for this demo) is here.

+ +

This demonstration requires the following: + +

+ +

+ +

This demonstration illustrates the effective use of the + java.nio direct buffer classes in JDK 1.4 to access memory outside + of the Java garbage-collected heap, in particular that returned + from the NVidia-specific routine wglAllocateMemoryNV. This memory + region is used in conjunction with glVertexArrayRangeNV.

+ +

On a 750 MHz PIII with an SDRAM memory bus and a GeForce 256 + running the Java HotSpot[tm] Client VM and OpenGL for Java 2.8, + this demonstration attains 90% of the speed of the compiled C++ + code, with a frame rate of 27 FPS, compared to 30 FPS for the C++ + version. On higher-end hardware (a dual 667 MHz PIII with RDRAM + and a GeForce 2) the demo currently attains between 65% and 75% of + C++ speed with the HotSpot Client and Server compilers, + respectively.

*/ + +public class VertexArrayRange extends Demo { + public static void main(String[] args) { + boolean startSlow = false; + + if (args.length > 1) { + usage(); + } + + if (args.length == 1) { + if (args[0].equals("-slow")) { + startSlow = true; + } else { + usage(); + } + } + + GLCanvas canvas = new GLCanvas(); + VertexArrayRange demo = new VertexArrayRange(); + if (startSlow) { + demo.setFlag('v', false); // VAR off + } + canvas.addGLEventListener(demo); + + final VertexArrayRange f_demo = demo; + canvas.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + f_demo.dispatchKey(e.getKeyChar()); + } + }); + + + final Animator animator = new Animator(canvas); + animator.setRunAsFastAsPossible(true); + demo.setDemoListener(new DemoListener() { + public void shutdownDemo() { + runExit(animator); + } + public void repaint() {} + }); + + Frame frame = new Frame("Very Simple NV_vertex_array_range demo"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + runExit(animator); + } + }); + frame.setLayout(new BorderLayout()); + canvas.setSize(800, 800); + frame.add(canvas, BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + canvas.requestFocus(); + + animator.start(); + } + + private static void usage() { + System.out.println("usage: java VertexArrayRange [-slow]"); + System.out.println("-slow flag starts up using data in the Java heap"); + System.exit(0); + } + + public VertexArrayRange() { + setFlag(' ', true); // animation on + setFlag('i', true); // infinite viewer and light + setFlag('v', true); // VAR on + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private GLU glu = new GLU(); + private boolean[] b = new boolean[256]; + private static final int SIZEOF_FLOAT = 4; + private static final int STRIP_SIZE = 48; + private int tileSize = 9 * STRIP_SIZE; + private int numBuffers = 4; + private int bufferLength = 1000000; + private int bufferSize = bufferLength * SIZEOF_FLOAT; + private static final int SIN_ARRAY_SIZE = 1024; + + private FloatBuffer bigArrayVar; + private FloatBuffer bigArraySystem; + private FloatBuffer bigArray; + private IntBuffer[] elements; + private float[] xyArray; + + static class VarBuffer { + public FloatBuffer vertices; + public FloatBuffer normals; + public int fence; + } + private VarBuffer[] buffers; + + private float[] sinArray; + private float[] cosArray; + + // Primitive: GL_QUAD_STRIP, GL_LINE_STRIP, or GL_POINTS + private int primitive = GL2.GL_QUAD_STRIP; + + // Animation parameters + private float hicoef = .06f; + private float locoef = .10f; + private float hifreq = 6.1f; + private float lofreq = 2.5f; + private float phaseRate = .02f; + private float phase2Rate = -0.12f; + private float phase = 0; + private float phase2 = 0; + + // Temporaries for computation + float[] ysinlo = new float[STRIP_SIZE]; + float[] ycoslo = new float[STRIP_SIZE]; + float[] ysinhi = new float[STRIP_SIZE]; + float[] ycoshi = new float[STRIP_SIZE]; + + // For thread-safety when dealing with keypresses + private volatile boolean toggleVAR = false; + private volatile boolean toggleLighting = false; + private volatile boolean toggleLightingModel = false; + private volatile boolean recomputeElements = false; + + // Frames-per-second computation + private boolean firstProfiledFrame; + private int profiledFrameCount; + private int numDrawElementsCalls; + private long startTimeMillis; + + static class PeriodicIterator { + public PeriodicIterator(int arraySize, + float period, + float initialOffset, + float delta) { + float arrayDelta = arraySize * (delta / period); // floating-point steps-per-increment + increment = (int)(arrayDelta * (1<<16)); // fixed-point steps-per-increment + + float offset = arraySize * (initialOffset / period); // floating-point initial index + initOffset = (int)(offset * (1<<16)); // fixed-point initial index + + arraySizeMask = 0; + int i = 20; // array should be reasonably sized... + while((arraySize & (1<> 16) & arraySizeMask; + } + + public void incr() { + index += increment; + } + + public void decr() { + index -= increment; + } + + public void reset() { + index = initOffset; + } + + private int arraySizeMask; + // fraction bits == 16 + private int increment; + private int initOffset; + private int index; + } + + private void setFlag(char key, boolean val) { + b[((int) key) & 0xFF] = val; + } + + private boolean getFlag(char key) { + return b[((int) key) & 0xFF]; + } + + private void ensurePresent(GL gl, String function) { + if (!gl.isFunctionAvailable(function)) { + final String message = "OpenGL routine \"" + function + "\" not available"; + new Thread(new Runnable() { + public void run() { + JOptionPane.showMessageDialog(null, message, "Unavailable extension", JOptionPane.ERROR_MESSAGE); + shutdownDemo(); + } + }).start(); + throw new RuntimeException(message); + } + } + + public void init(GLAutoDrawable drawable) { + // drawable.setGL(new TraceGL(drawable.getGL(), System.err)); + // drawable.setGL(new DebugGL(drawable.getGL())); + + GL2 gl = drawable.getGL().getGL2(); + + // Try and disable synch-to-retrace for fastest framerate + gl.setSwapInterval(0); + + try { + ensurePresent(gl, "glVertexArrayRangeNV"); + ensurePresent(gl, "glGenFencesNV"); + ensurePresent(gl, "glSetFenceNV"); + ensurePresent(gl, "glTestFenceNV"); + ensurePresent(gl, "glFinishFenceNV"); + ensurePresent(gl, "glAllocateMemoryNV"); + } catch (RuntimeException e) { + shutdownDemo(); + throw (e); + } + + gl.glEnable(GL.GL_DEPTH_TEST); + + gl.glClearColor(0, 0, 0, 0); + + gl.glEnable(GL2ES1.GL_LIGHT0); + gl.glEnable(GL2ES1.GL_LIGHTING); + gl.glEnable(GL2ES1.GL_NORMALIZE); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_AMBIENT, new float[] {.1f, .1f, 0, 1}, 0); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_DIFFUSE, new float[] {.6f, .6f, .1f, 1}, 0); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL2ES1.GL_SPECULAR, new float[] { 1, 1, .75f, 1}, 0); + gl.glMaterialf(GL.GL_FRONT_AND_BACK, GL2ES1.GL_SHININESS, 128.f); + + gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, .5f, 0}, 0); + gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 0); + + // NOTE: it looks like GLUT (or something else) sets up the + // projection matrix in the C version of this demo. + gl.glMatrixMode(GL2ES1.GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluPerspective(60, 1.0, 0.1, 100); + gl.glMatrixMode(GL2ES1.GL_MODELVIEW); + + allocateBigArray(gl, true); + allocateBuffersAndFences(gl); + + sinArray = new float[SIN_ARRAY_SIZE]; + cosArray = new float[SIN_ARRAY_SIZE]; + + for (int i = 0; i < SIN_ARRAY_SIZE; i++) { + double step = i * 2 * Math.PI / SIN_ARRAY_SIZE; + sinArray[i] = (float) Math.sin(step); + cosArray[i] = (float) Math.cos(step); + } + + if (getFlag('v')) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); + gl.glVertexArrayRangeNV(bufferSize, bigArrayVar); + bigArray = bigArrayVar; + } else { + bigArray = bigArraySystem; + } + setupBuffers(); + gl.glEnableClientState(GL2ES1.GL_VERTEX_ARRAY); + gl.glEnableClientState(GL2ES1.GL_NORMAL_ARRAY); + + computeElements(); + } + + private void allocateBuffersAndFences(GL2 gl) { + buffers = new VarBuffer[numBuffers]; + int[] fences = new int[1]; + for (int i = 0; i < numBuffers; i++) { + buffers[i] = new VarBuffer(); + gl.glGenFencesNV(1, fences, 0); + buffers[i].fence = fences[0]; + } + } + + private void setupBuffers() { + int sliceSize = bufferLength / numBuffers; + for (int i = 0; i < numBuffers; i++) { + int startIndex = i * sliceSize; + buffers[i].vertices = sliceBuffer(bigArray, startIndex, sliceSize); + buffers[i].normals = sliceBuffer(buffers[i].vertices, 3, + buffers[i].vertices.limit() - 3); + } + } + + private void dispatchKey(char k) { + setFlag(k, !getFlag(k)); + // Quit on escape or 'q' + if ((k == (char) 27) || (k == 'q')) { + shutdownDemo(); + return; + } + + if (k == 'r') { + if (getFlag(k)) { + profiledFrameCount = 0; + numDrawElementsCalls = 0; + firstProfiledFrame = true; + } + } + + if (k == 'w') { + if (getFlag(k)) { + primitive = GL2.GL_LINE_STRIP; + } else { + primitive = GL2.GL_QUAD_STRIP; + } + } + + if (k == 'p') { + if (getFlag(k)) { + primitive = GL2.GL_POINTS; + } else { + primitive = GL2.GL_QUAD_STRIP; + } + } + + if (k == 'v') { + toggleVAR = true; + } + + if (k == 'd') { + toggleLighting = true; + } + + if (k == 'i') { + toggleLightingModel = true; + } + + if('h'==k) + hicoef += .005; + if('H'==k) + hicoef -= .005; + if('l'==k) + locoef += .005; + if('L'==k) + locoef -= .005; + if('1'==k) + lofreq += .1f; + if('2'==k) + lofreq -= .1f; + if('3'==k) + hifreq += .1f; + if('4'==k) + hifreq -= .1f; + if('5'==k) + phaseRate += .01f; + if('6'==k) + phaseRate -= .01f; + if('7'==k) + phase2Rate += .01f; + if('8'==k) + phase2Rate -= .01f; + + if('t'==k) { + if(tileSize < 864) { + tileSize += STRIP_SIZE; + recomputeElements = true; + System.err.println("tileSize = " + tileSize); + } + } + + if('T'==k) { + if(tileSize > STRIP_SIZE) { + tileSize -= STRIP_SIZE; + recomputeElements = true; + System.err.println("tileSize = " + tileSize); + } + } + } + + public void dispose(GLAutoDrawable drawable) { + } + + public void display(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); + + // Check to see whether to animate + if (getFlag(' ')) { + phase += phaseRate; + phase2 += phase2Rate; + + if (phase > (float) (20 * Math.PI)) { + phase = 0; + } + + if (phase2 < (float) (-20 * Math.PI)) { + phase2 = 0; + } + } + + PeriodicIterator loX = + new PeriodicIterator(SIN_ARRAY_SIZE, (float) (2 * Math.PI), phase, (float) ((1.f/tileSize)*lofreq*Math.PI)); + PeriodicIterator loY = new PeriodicIterator(loX); + PeriodicIterator hiX = + new PeriodicIterator(SIN_ARRAY_SIZE, (float) (2 * Math.PI), phase2, (float) ((1.f/tileSize)*hifreq*Math.PI)); + PeriodicIterator hiY = new PeriodicIterator(hiX); + + if (toggleVAR) { + if (getFlag('v')) { + gl.glEnableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); + gl.glVertexArrayRangeNV(bufferSize, bigArrayVar); + bigArray = bigArrayVar; + } else { + gl.glDisableClientState(GL2.GL_VERTEX_ARRAY_RANGE_NV); + bigArray = bigArraySystem; + } + toggleVAR = false; + setupBuffers(); + } + + if (toggleLighting) { + if (getFlag('d')) { + gl.glDisable(GL2ES1.GL_LIGHTING); + } else { + gl.glEnable(GL2ES1.GL_LIGHTING); + } + toggleLighting = false; + } + + if (toggleLightingModel) { + if(getFlag('i')) { + // infinite light + gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, .5f, 0 }, 0); + gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 0); + } else { + gl.glLightfv(GL2ES1.GL_LIGHT0, GL2ES1.GL_POSITION, new float[] { .5f, 0, -.5f, 1 }, 0); + gl.glLightModeli(GL2.GL_LIGHT_MODEL_LOCAL_VIEWER, 1); + } + toggleLightingModel = false; + } + + if (recomputeElements) { + computeElements(); + recomputeElements = false; + } + + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + gl.glPushMatrix(); + + final float[] modelViewMatrix = new float[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, -1, 1 + }; + gl.glLoadMatrixf(modelViewMatrix, 0); + + // FIXME: add mouse interaction + // camera.apply_inverse_transform(); + // object.apply_transform(); + + int cur = 0; + int numSlabs = tileSize / STRIP_SIZE; + + for(int slab = numSlabs; --slab>=0; ) { + cur = slab % numBuffers; + if (slab >= numBuffers) { + if (!gl.glTestFenceNV(buffers[cur].fence)) { + gl.glFinishFenceNV(buffers[cur].fence); + } + } + + FloatBuffer v = buffers[cur].vertices; + int vertexIndex = 0; + + gl.glVertexPointer(3, GL.GL_FLOAT, 6 * SIZEOF_FLOAT, v); + gl.glNormalPointer(GL.GL_FLOAT, 6 * SIZEOF_FLOAT, buffers[cur].normals); + + for(int jj=STRIP_SIZE; --jj>=0; ) { + ysinlo[jj] = sinArray[loY.getIndex()]; + ycoslo[jj] = cosArray[loY.getIndex()]; loY.incr(); + ysinhi[jj] = sinArray[hiY.getIndex()]; + ycoshi[jj] = cosArray[hiY.getIndex()]; hiY.incr(); + } + loY.decr(); + hiY.decr(); + + for(int i = tileSize; --i>=0; ) { + float x = xyArray[i]; + int loXIndex = loX.getIndex(); + int hiXIndex = hiX.getIndex(); + + int jOffset = (STRIP_SIZE-1)*slab; + float nx = locoef * -cosArray[loXIndex] + hicoef * -cosArray[hiXIndex]; + + // Help the HotSpot Client Compiler by hoisting loop + // invariant variables into locals. Note that this may be + // good practice for innermost loops anyway since under + // the new memory model operations like accidental + // synchronization may force any compiler to reload these + // fields from memory, destroying their ability to + // optimize. + float locoef_tmp = locoef; + float hicoef_tmp = hicoef; + float[] ysinlo_tmp = ysinlo; + float[] ysinhi_tmp = ysinhi; + float[] ycoslo_tmp = ycoslo; + float[] ycoshi_tmp = ycoshi; + float[] sinArray_tmp = sinArray; + float[] xyArray_tmp = xyArray; + + for(int j = STRIP_SIZE; --j>=0; ) { + float y; + + y = xyArray_tmp[j + jOffset]; + + float ny; + + v.put(vertexIndex, x); + v.put(vertexIndex + 1, y); + v.put(vertexIndex + 2, (locoef_tmp * (sinArray_tmp[loXIndex] + ysinlo_tmp[j]) + + hicoef_tmp * (sinArray_tmp[hiXIndex] + ysinhi_tmp[j]))); + v.put(vertexIndex + 3, nx); + ny = locoef_tmp * -ycoslo_tmp[j] + hicoef_tmp * -ycoshi_tmp[j]; + v.put(vertexIndex + 4, ny); + v.put(vertexIndex + 5, .15f); //.15f * (1.f - sqrt(nx * nx + ny * ny)); + vertexIndex += 6; + } + loX.incr(); + hiX.incr(); + } + loX.reset(); + hiX.reset(); + + for (int i = 0; i < elements.length; i++) { + ++numDrawElementsCalls; + gl.glDrawElements(primitive, elements[i].capacity(), GL2.GL_UNSIGNED_INT, elements[i]); + if(getFlag('f')) { + gl.glFlush(); + } + } + + gl.glSetFenceNV(buffers[cur].fence, GL2.GL_ALL_COMPLETED_NV); + } + + gl.glPopMatrix(); + + gl.glFinishFenceNV(buffers[cur].fence); + + if (getFlag('r')) { + if (!firstProfiledFrame) { + if (++profiledFrameCount == 30) { + long endTimeMillis = System.currentTimeMillis(); + double secs = (endTimeMillis - startTimeMillis) / 1000.0; + double fps = 30.0 / secs; + double ppf = tileSize * tileSize * 2; + double mpps = ppf * fps / 1000000.0; + System.err.println("fps: " + fps + " polys/frame: " + ppf + " million polys/sec: " + mpps + + " DrawElements calls/frame: " + (numDrawElementsCalls / 30)); + profiledFrameCount = 0; + numDrawElementsCalls = 0; + startTimeMillis = System.currentTimeMillis(); + } + } else { + startTimeMillis = System.currentTimeMillis(); + firstProfiledFrame = false; + + } + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + + // Unused routines + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + private void allocateBigArray(GL2 gl, boolean tryAgain) { + float priority = .5f; + + bigArraySystem = setupBuffer(ByteBuffer.allocateDirect(bufferSize)); + + float megabytes = (bufferSize / 1000000.f); + try { + bigArrayVar = setupBuffer(gl.glAllocateMemoryNV(bufferSize, 0, 0, priority)); + } + catch (OutOfMemoryError e1) { + // Try a higher priority + try { + bigArrayVar = setupBuffer(gl.glAllocateMemoryNV(bufferSize, 0, 0, 1.f)); + } + catch (OutOfMemoryError e2) { + if (!tryAgain) { + throw new RuntimeException("Unable to allocate " + megabytes + + " megabytes of fast memory. Giving up."); + } + + System.err.println("Unable to allocate " + megabytes + + " megabytes of fast memory. Trying less."); + bufferSize /= 2; + numBuffers /= 2; + allocateBigArray(gl, false); + return; + } + } + + System.err.println("Allocated " + megabytes + " megabytes of fast memory"); + } + + private FloatBuffer setupBuffer(ByteBuffer buf) { + buf.order(ByteOrder.nativeOrder()); + return buf.asFloatBuffer(); + } + + private FloatBuffer sliceBuffer(FloatBuffer array, + int sliceStartIndex, int sliceLength) { + array.position(sliceStartIndex); + FloatBuffer ret = array.slice(); + array.position(0); + ret.limit(sliceLength); + return ret; + } + + private void computeElements() { + xyArray = new float[tileSize]; + for (int i = 0; i < tileSize; i++) { + xyArray[i] = i / (tileSize - 1.0f) - 0.5f; + } + + elements = new IntBuffer[tileSize - 1]; + for (int i = 0; i < tileSize - 1; i++) { + elements[i] = IntBuffer.allocate(2 * STRIP_SIZE); + for (int j = 0; j < 2 * STRIP_SIZE; j += 2) { + elements[i].put(j, i * STRIP_SIZE + (j / 2)); + elements[i].put(j+1, (i + 1) * STRIP_SIZE + (j / 2)); + } + } + } + + private static void runExit(final Animator animator) { + // Note: calling System.exit() synchronously inside the draw, + // reshape or init callbacks can lead to deadlocks on certain + // platforms (in particular, X11) because the JAWT's locking + // routines cause a global AWT lock to be grabbed. Run the + // exit routine in another thread. + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } +} -- cgit v1.2.3