From: <sp...@us...> - 2011-07-22 02:02:05
|
Revision: 3597 http://java-game-lib.svn.sourceforge.net/java-game-lib/?rev=3597&view=rev Author: spasi Date: 2011-07-22 02:01:56 +0000 (Fri, 22 Jul 2011) Log Message: ----------- Added support for array access to mapped objects. Added SpriteShootout test that uses mapped objects. Modified Paths: -------------- trunk/LWJGL/build.xml trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectBench.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObject.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectUnsafe.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedSet2.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedSet3.java trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedSet4.java Added Paths: ----------- trunk/LWJGL/libs/asm-debug-all.jar trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java Removed Paths: ------------- trunk/LWJGL/libs/asm-util.jar trunk/LWJGL/libs/asm.jar Modified: trunk/LWJGL/build.xml =================================================================== --- trunk/LWJGL/build.xml 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/build.xml 2011-07-22 02:01:56 UTC (rev 3597) @@ -410,7 +410,7 @@ <!-- Compiles the Java source code --> <target name="compile" description="Compiles the java source code" depends="-initialize"> - <javac debug="yes" destdir="${lwjgl.bin}" source="1.5" target="1.5" classpath="${lwjgl.lib}/jinput.jar:${lwjgl.lib}/AppleJavaExtensions.jar;${lwjgl.lib}/asm.jar;${lwjgl.lib}/asm-util.jar" taskname="core"> + <javac debug="yes" destdir="${lwjgl.bin}" source="1.5" target="1.5" classpath="${lwjgl.lib}/jinput.jar:${lwjgl.lib}/AppleJavaExtensions.jar:${lwjgl.lib}/asm-debug-all.jar" taskname="core"> <!--<compilerarg value="-Xlint:unchecked"/>--> <src path="${lwjgl.src}/java/"/> <src path="${lwjgl.src}/generated/"/> Added: trunk/LWJGL/libs/asm-debug-all.jar =================================================================== (Binary files differ) Property changes on: trunk/LWJGL/libs/asm-debug-all.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Deleted: trunk/LWJGL/libs/asm-util.jar =================================================================== (Binary files differ) Deleted: trunk/LWJGL/libs/asm.jar =================================================================== (Binary files differ) Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectBench.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectBench.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectBench.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -31,6 +31,7 @@ */ package org.lwjgl.test.mapped; +import org.lwjgl.MemoryUtil; import org.lwjgl.util.mapped.MappedObjectUnsafe; import java.nio.ByteBuffer; @@ -212,7 +213,7 @@ final int iterations = 64 * 1024; ByteBuffer bb = ByteBuffer.allocateDirect(200); - long addr = MappedObjectUnsafe.getBufferBaseAddress(bb); + long addr = MemoryUtil.getAddress(bb); long[] took = new long[runs]; for ( int run = 0; run < runs; run++ ) { Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests1.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -31,6 +31,7 @@ */ package org.lwjgl.test.mapped; +import org.lwjgl.MemoryUtil; import org.lwjgl.util.mapped.MappedHelper; import org.lwjgl.util.mapped.MappedObjectUnsafe; @@ -143,9 +144,9 @@ // test newBuffer { - long addr1 = MappedObjectUnsafe.getBufferBaseAddress(bb); + long addr1 = MemoryUtil.getAddress(bb); ByteBuffer bb2 = MappedHelper.newBuffer(addr1, bb.capacity()); - long addr2 = MappedObjectUnsafe.getBufferBaseAddress(bb); + long addr2 = MemoryUtil.getAddress(bb); System.out.println(bb); System.out.println(bb2); Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests3.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -31,7 +31,11 @@ */ package org.lwjgl.test.mapped; -import org.lwjgl.util.mapped.*; +import org.lwjgl.MemoryUtil; +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedSet; +import org.lwjgl.util.mapped.MappedSet2; +import org.lwjgl.util.mapped.MappedType; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -49,10 +53,10 @@ assert (some.data != some.data); - long addr1 = MappedObjectUnsafe.getBufferBaseAddress(some.backingByteBuffer()); + long addr1 = MemoryUtil.getAddress(some.backingByteBuffer()); ByteBuffer mapped = some.data; // creates new ByteBuffer instance - long addr2 = MappedObjectUnsafe.getBufferBaseAddress(mapped); + long addr2 = MemoryUtil.getAddress(mapped); assert (addr2 - addr1 == 4); assert (mapped.capacity() == MappedSomething.SIZEOF - 4); @@ -60,7 +64,7 @@ some.view++; mapped = some.data; // creates new ByteBuffer instance - long addr3 = MappedObjectUnsafe.getBufferBaseAddress(mapped); + long addr3 = MemoryUtil.getAddress(mapped); assert (addr3 - addr1 == 4 + MappedSomething.SIZEOF); assert (addr3 - addr2 == 0 + MappedSomething.SIZEOF); assert (mapped.capacity() == MappedSomething.SIZEOF - 4); @@ -87,7 +91,7 @@ static void testConstructor() { int capacity = 1024; ByteBuffer bb = ByteBuffer.allocateDirect(capacity).order(ByteOrder.nativeOrder()); - long address = MappedObjectUnsafe.getBufferBaseAddress(bb); + long address = MemoryUtil.getAddress(bb); MappedFloat mf = MappedFloat.map(address, capacity); Added: trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java (rev 0) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/MappedObjectTests4.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2002-2011 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.mapped; + +import org.lwjgl.MemoryUtil; +import org.lwjgl.opengl.Display; + +import java.io.File; +import java.nio.ByteBuffer; + +/** @author Riven */ +@SuppressWarnings("static-access") +public class MappedObjectTests4 { + + public static void testLWJGL() throws Exception { + System.out.println(new File(System.getProperty("java.library.path")).getCanonicalPath()); + Display.create(); + } + + public static void testLocalView() { + MappedFloat mapped1 = MappedFloat.malloc(5); + MappedFloat mapped2 = MappedFloat.malloc(5); + + testLocalView(mapped1); + testLocalView(mapped1, mapped2); + + MappedSomething some = MappedSomething.malloc(5); + testLocalView(some); + } + + private static void testLocalView(MappedFloat mapped1) { + final MappedFloat[] array = mapped1.asArray(); + + assert (array.length == 5); + + int l = 10 * array.length; + for ( int localView1 = 0; localView1 < 5; localView1++ ) { + array[localView1].value = localView1 * 5; + array[localView1].value *= 2.0f; + } + System.out.println(); + float x = 0.0f; + for ( int localView1 = 0; localView1 < 5; localView1++ ) { + System.out.println("[" + localView1 + "] =>" + array[localView1].value); + x += array[localView1].value; + } + System.out.println("x = " + x); + } + + private static void testLocalView(MappedFloat mo1, MappedFloat mo2) { + final MappedFloat[] array1 = mo1.<MappedFloat>asArray(); + for ( int v1 = 0; v1 < 5; v1++ ) { + array1[v1].value = (float)Math.random(); + array1[v1].value *= 2.0f; + } + final MappedFloat[] array2 = mo2.<MappedFloat>asArray(); + for ( int v2 = 0; v2 < 5; v2++ ) { + array2[v2].value = (float)Math.random(); + array2[v2].value *= 2.0f; + } + + System.out.println(); + + for ( int v1 = 0; v1 < 5; v1++ ) { + System.out.println("[" + v1 + "] =>" + array1[v1].value); + } + for ( int v2 = 0; v2 < 5; v2++ ) { + System.out.println("[" + v2 + "] =>" + array2[v2].value); + } + } + + private static void testLocalView(MappedSomething some) { + final MappedSomething[] array = some.asArray(); + + assert (array.length == 5); + + final long baseAddress = MemoryUtil.getAddress(some.backingByteBuffer()); + for ( int i = 0; i < array.length; i++ ) { + ByteBuffer data = array[i].data; + + assert (data.capacity() == (64 - 4)); + assert (MemoryUtil.getAddress(data) == baseAddress + i * MappedSomething.SIZEOF + 4); + } + } + +} \ No newline at end of file Modified: trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/test/mapped/TestMappedObject.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -71,6 +71,11 @@ MappedObjectTests3.testConstructor(); MappedObjectTests3.testMappedSet(); + MappedObjectTests4.testLocalView(); + + //MappedObjectTests4.testLWJGL(); + + System.out.println("done"); } Added: trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java (rev 0) +++ trunk/LWJGL/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2002-2011 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.opengl.sprites; + +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.Sys; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.*; +import org.lwjgl.util.mapped.MappedObject; +import org.lwjgl.util.mapped.MappedObjectClassLoader; +import org.lwjgl.util.mapped.MappedObjectTransformer; +import org.lwjgl.util.mapped.MappedType; + +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Random; +import javax.imageio.ImageIO; + +import static org.lwjgl.opengl.EXTTransformFeedback.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL12.*; +import static org.lwjgl.opengl.GL15.*; +import static org.lwjgl.opengl.GL20.*; +import static org.lwjgl.opengl.GL30.*; + +/** + * Sprite rendering demo. Three implementations are supported: + * a) CPU animation + BufferData VBO update. + * b) CPU animation + MapBufferRange VBO update. + * c) GPU animation using transform feedback with a vertex shader. + * + * @author Spasi + * @since 18/3/2011 + */ +public final class SpriteShootoutMapped { + + static final int SCREEN_WIDTH = 800; + static final int SCREEN_HEIGHT = 600; + + private static final int ANIMATION_TICKS = 60; + + private boolean run = true; + private boolean render = true; + private boolean colorMask = true; + private boolean animate = true; + private boolean smooth; + private boolean vsync; + + int ballSize = 42; + int ballCount = 100 * 1000; + + private SpriteRenderer renderer; + + // OpenGL stuff + private int texID; + private int texBigID; + private int texSmallID; + + private SpriteShootoutMapped() { + } + + public static void main(String[] args) { + MappedObjectTransformer.register(Pixel4b.class); + MappedObjectTransformer.register(Pixel3b.class); + MappedObjectTransformer.register(Sprite.class); + MappedObjectTransformer.register(SpriteRender.class); + + if ( MappedObjectClassLoader.fork(SpriteShootoutMapped.class, args) ) + return; + + try { + new SpriteShootoutMapped().start(); + } catch (LWJGLException e) { + e.printStackTrace(); + } + } + + private void start() throws LWJGLException { + try { + initGL(); + + final ContextCapabilities caps = GLContext.getCapabilities(); + if ( !true && (caps.OpenGL30 || caps.GL_EXT_transform_feedback) ) + renderer = new SpriteRendererTF(); + else if ( true && caps.GL_ARB_map_buffer_range ) + renderer = new SpriteRendererMapped(); + else + renderer = new SpriteRendererPlain(); + + updateBalls(ballCount); + run(); + } catch (Throwable t) { + t.printStackTrace(); + } finally { + destroy(); + } + } + + private void initGL() throws LWJGLException { + Display.setLocation((Display.getDisplayMode().getWidth() - SCREEN_WIDTH) / 2, + (Display.getDisplayMode().getHeight() - SCREEN_HEIGHT) / 2); + Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT)); + Display.setTitle("Sprite Shootout"); + Display.create(); + //Display.create(new PixelFormat(), new ContextAttribs(4, 1).withProfileCompatibility(true).withDebug(true)); + //AMDDebugOutput.glDebugMessageCallbackAMD(new AMDDebugOutputCallback()); + + if ( !GLContext.getCapabilities().OpenGL20 ) + throw new RuntimeException("OpenGL 2.0 is required for this demo."); + + // Setup viewport + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + + glClearColor(1.0f, 1.0f, 1.0f, 0.0f); + + // Create textures + + try { + texSmallID = createTexture("res/ball_sm.png"); + texBigID = createTexture("res/ball.png"); + } catch (IOException e) { + e.printStackTrace(); + System.exit(-1); + } + texID = texBigID; + + // Setup rendering state + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.0f); + + glColorMask(colorMask, colorMask, colorMask, false); + glDepthMask(false); + glDisable(GL_DEPTH_TEST); + + // Setup geometry + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + Util.checkGLError(); + } + + private static int createTexture(final String path) throws IOException { + final BufferedImage img = ImageIO.read(SpriteShootoutMapped.class.getClassLoader().getResource(path)); + + final int w = img.getWidth(); + final int h = img.getHeight(); + + final ByteBuffer buffer = readImage(img); + + final int texID = glGenTextures(); + + glBindTexture(GL_TEXTURE_2D, texID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer); + + return texID; + } + + @MappedType(sizeof = 4) + public static class Pixel4b extends MappedObject { + + public byte r, g, b, a; + + } + + @MappedType(sizeof = 3, align = 3) + public static class Pixel3b extends MappedObject { + + public byte r, g, b; + + } + + private static ByteBuffer readImage(final BufferedImage img) throws IOException { + final Raster raster = img.getRaster(); + + final int bands = raster.getNumBands(); + + final int w = img.getWidth(); + final int h = img.getHeight(); + + final int count = w * h; + + final byte[] pixels = new byte[count * bands]; + raster.getDataElements(0, 0, w, h, pixels); + + if ( bands == 4 ) { + Pixel4b p = Pixel4b.malloc(count); + + int b = 0; + for ( int i = 0; i < count; i++, b += 4 ) { + // Pre-multiply alpha + final float a = unpackUByte01(pixels[b + 3]); + + p.view = i; + p.r = packUByte01(unpackUByte01(pixels[b + 2]) * a); + p.g = packUByte01(unpackUByte01(pixels[b + 1]) * a); + p.b = packUByte01(unpackUByte01(pixels[b + 0]) * a); + p.a = pixels[b + 3]; + } + + return p.backingByteBuffer(); + } else if ( bands == 3 ) { + Pixel3b p = Pixel3b.malloc(count); + + int b = 0; + for ( int i = 0; i < count; i++, b += 3 ) { + p.view = i; + p.r = pixels[b + 2]; + p.g = pixels[b + 1]; + p.b = pixels[b + 0]; + } + + return p.backingByteBuffer(); + } else { + ByteBuffer p = BufferUtils.createByteBuffer(count * bands); + p.put(pixels, 0, p.capacity()); + p.flip(); + return p; + } + + } + + private static float unpackUByte01(final byte x) { + return (x & 0xFF) / 255.0f; + } + + private static byte packUByte01(final float x) { + return (byte)(x * 255.0f); + } + + private void updateBalls(final int count) { + System.out.println("NUMBER OF BALLS: " + count); + renderer.updateBalls(ballCount); + } + + private void run() { + long startTime = System.currentTimeMillis() + 5000; + long fps = 0; + + long time = Sys.getTime(); + final int ticksPerUpdate = (int)(Sys.getTimerResolution() / ANIMATION_TICKS); + + renderer.render(false, true, 0); + + while ( run ) { + Display.processMessages(); + handleInput(); + + glClear(GL_COLOR_BUFFER_BIT); + + final long currTime = Sys.getTime(); + final int delta = (int)(currTime - time); + if ( smooth || delta >= ticksPerUpdate ) { + renderer.render(render, animate, delta); + time = currTime; + } else + renderer.render(render, false, 0); + + Display.update(false); + //Display.sync(60); + + if ( startTime > System.currentTimeMillis() ) { + fps++; + } else { + long timeUsed = 5000 + (startTime - System.currentTimeMillis()); + startTime = System.currentTimeMillis() + 5000; + System.out.println("FPS: " + (Math.round(fps / (timeUsed / 1000.0) * 10) / 10.0) + ", Balls: " + ballCount); + fps = 0; + } + } + } + + private void handleInput() { + if ( Display.isCloseRequested() ) + run = false; + + while ( Keyboard.next() ) { + if ( Keyboard.getEventKeyState() ) + continue; + + switch ( Keyboard.getEventKey() ) { + case Keyboard.KEY_1: + case Keyboard.KEY_2: + case Keyboard.KEY_3: + case Keyboard.KEY_4: + case Keyboard.KEY_5: + case Keyboard.KEY_6: + case Keyboard.KEY_7: + case Keyboard.KEY_8: + case Keyboard.KEY_9: + case Keyboard.KEY_0: + ballCount = 1 << (Keyboard.getEventKey() - Keyboard.KEY_1); + updateBalls(ballCount); + break; + case Keyboard.KEY_ADD: + case Keyboard.KEY_SUBTRACT: + int mult; + if ( Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) ) + mult = 1000; + else if ( Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU) ) + mult = 100; + else if ( Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL) ) + mult = 10; + else + mult = 1; + if ( Keyboard.getEventKey() == Keyboard.KEY_SUBTRACT ) + mult = -mult; + ballCount += mult * 100; + if ( ballCount <= 0 ) + ballCount = 1; + updateBalls(ballCount); + break; + case Keyboard.KEY_ESCAPE: + run = false; + break; + case Keyboard.KEY_A: + animate = !animate; + System.out.println("Animation is now " + (animate ? "on" : "off") + "."); + break; + case Keyboard.KEY_C: + colorMask = !colorMask; + glColorMask(colorMask, colorMask, colorMask, false); + System.out.println("Color mask is now " + (colorMask ? "on" : "off") + "."); + // Disable alpha test when color mask is off, else we get no benefit. + if ( colorMask ) { + glEnable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + } else { + glDisable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + } + break; + case Keyboard.KEY_R: + render = !render; + System.out.println("Rendering is now " + (render ? "on" : "off") + "."); + break; + case Keyboard.KEY_S: + smooth = !smooth; + System.out.println("Smooth animation is now " + (smooth ? "on" : "off") + "."); + break; + case Keyboard.KEY_T: + if ( texID == texBigID ) { + texID = texSmallID; + ballSize = 16; + } else { + texID = texBigID; + ballSize = 42; + } + renderer.updateBallSize(); + glBindTexture(GL_TEXTURE_2D, texID); + System.out.println("Now using the " + (texID == texBigID ? "big" : "small") + " texture."); + break; + case Keyboard.KEY_V: + vsync = !vsync; + Display.setVSyncEnabled(vsync); + System.out.println("VSYNC is now " + (vsync ? "enabled" : "disabled") + "."); + break; + } + } + + while ( Mouse.next() ) ; + } + + private void destroy() { + Display.destroy(); + } + + @MappedType(sizeof = 4 * 4) + public static class Sprite extends MappedObject { + + public float x, y; + public float dx, dy; + + } + + @MappedType(sizeof = 2 * 4) + public static class SpriteRender extends MappedObject { + + public float x, y; + + } + + private abstract class SpriteRenderer { + + protected Sprite sprites; + + protected int spriteCount; + + protected int vshID; + protected int progID; + + protected void createProgram() { + final int fshID = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fshID, "uniform sampler2D COLOR_MAP;\n" + + "void main(void) {\n" + + " gl_FragColor = texture2D(COLOR_MAP, gl_PointCoord);\n" + + "}"); + glCompileShader(fshID); + if ( glGetShader(fshID, GL_COMPILE_STATUS) == GL_FALSE ) { + System.out.println(glGetShaderInfoLog(fshID, glGetShader(fshID, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to compile fragment shader."); + } + + progID = glCreateProgram(); + glAttachShader(progID, vshID); + glAttachShader(progID, fshID); + glLinkProgram(progID); + if ( glGetProgram(progID, GL_LINK_STATUS) == GL_FALSE ) { + System.out.println(glGetProgramInfoLog(progID, glGetProgram(progID, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to link shader program."); + } + + glUseProgram(progID); + glUniform1i(glGetUniformLocation(progID, "COLOR_MAP"), 0); + + updateBallSize(); + + glEnableClientState(GL_VERTEX_ARRAY); + } + + public void updateBallSize() { + glPointSize(ballSize); + } + + public abstract void updateBalls(int count); + + protected abstract void render(boolean render, boolean animate, int delta); + + } + + private abstract class SpriteRendererBatched extends SpriteRenderer { + + protected static final int BALLS_PER_BATCH = 10 * 1000; + + SpriteRendererBatched() { + vshID = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshID, "void main(void) {\n" + + " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" + + "}"); + glCompileShader(vshID); + if ( glGetShader(vshID, GL_COMPILE_STATUS) == GL_FALSE ) { + System.out.println(glGetShaderInfoLog(vshID, glGetShader(vshID, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to compile vertex shader."); + } + + createProgram(); + } + + public void updateBalls(final int count) { + final Random random = new Random(); + + final Sprite newSprites = Sprite.malloc(count); + if ( sprites != null ) { + sprites.view = 0; + sprites.copyRange(newSprites, Math.min(count, spriteCount)); + } + + if ( count > spriteCount ) { + for ( int i = spriteCount; i < count; i++ ) { + newSprites.view = i; + + newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f); + newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f); + newSprites.dx = random.nextFloat() * 0.4f - 0.2f; + newSprites.dy = random.nextFloat() * 0.4f - 0.2f; + } + } + + sprites = newSprites; + spriteCount = count; + } + + protected void animate( + final Sprite sprite, + final SpriteRender spriteRender, + final int ballSize, final int ballIndex, final int batchSize, final int delta + ) { + final float ballRadius = ballSize * 0.5f; + final float boundW = SCREEN_WIDTH - ballRadius; + final float boundH = SCREEN_HEIGHT - ballRadius; + + final Sprite[] sprites = sprite.asArray(); + final SpriteRender[] spritesRender = spriteRender.asArray(); + for ( int b = ballIndex, r = 0, len = (ballIndex + batchSize); b < len; b++, r++ ) { + float x = sprites[b].x; + float dx = sprites[b].dx; + + x += dx * delta; + if ( x < ballRadius ) { + x = ballRadius; + sprites[b].dx = -dx; + } else if ( x > boundW ) { + x = boundW; + sprites[b].dx = -dx; + } + sprites[b].x = x; + + float y = sprites[b].y; + float dy = sprites[b].dy; + + y += dy * delta; + if ( y < ballRadius ) { + y = ballRadius; + sprites[b].dy = -dy; + } else if ( y > boundH ) { + y = boundH; + sprites[b].dy = -dy; + } + sprites[b].y = y; + + spritesRender[r].x = x; + spritesRender[r].y = y; + } + } + + } + + private class SpriteRendererPlain extends SpriteRendererBatched { + + private final int DATA_PER_BATCH = BALLS_PER_BATCH * 2 * 4; // balls * 2 floats * 4 bytes + + protected int[] animVBO; + + private SpriteRender spritesRender; + + SpriteRendererPlain() { + System.out.println("Shootout Implementation: CPU animation & BufferData"); + spritesRender = SpriteRender.malloc(BALLS_PER_BATCH); + } + + public void updateBalls(final int count) { + super.updateBalls(count); + + final int batchCount = count / BALLS_PER_BATCH + (count % BALLS_PER_BATCH == 0 ? 0 : 1); + if ( animVBO != null && batchCount == animVBO.length ) + return; + + final int[] newAnimVBO = new int[batchCount]; + if ( animVBO != null ) { + System.arraycopy(animVBO, 0, newAnimVBO, 0, Math.min(animVBO.length, newAnimVBO.length)); + for ( int i = newAnimVBO.length; i < animVBO.length; i++ ) + glDeleteBuffers(animVBO[i]); + } + for ( int i = animVBO == null ? 0 : animVBO.length; i < newAnimVBO.length; i++ ) { + newAnimVBO[i] = glGenBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, newAnimVBO[i]); + } + + animVBO = newAnimVBO; + } + + public void render(final boolean render, final boolean animate, final int delta) { + int batchSize = Math.min(ballCount, BALLS_PER_BATCH); + int ballIndex = 0; + int batchIndex = 0; + while ( ballIndex < ballCount ) { + glBindBuffer(GL_ARRAY_BUFFER, animVBO[batchIndex]); + + if ( animate ) + animate(ballIndex, batchSize, delta); + + if ( render ) { + glVertexPointer(2, GL_FLOAT, 0, 0); + glDrawArrays(GL_POINTS, 0, batchSize); + } + + ballIndex += batchSize; + batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH); + batchIndex++; + } + } + + private void animate(final int ballIndex, final int batchSize, final int delta) { + animate( + sprites, spritesRender, + ballSize, ballIndex, batchSize, delta + ); + + glBufferData(GL_ARRAY_BUFFER, DATA_PER_BATCH, GL_STREAM_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, 0, spritesRender.backingByteBuffer()); + } + + } + + private class SpriteRendererMapped extends SpriteRendererBatched { + + private StreamVBO animVBO; + + SpriteRendererMapped() { + System.out.println("Shootout Implementation: CPU animation & MapBufferRange"); + } + + public void updateBalls(final int count) { + super.updateBalls(count); + + if ( animVBO != null ) + animVBO.destroy(); + + animVBO = new StreamVBO(GL_ARRAY_BUFFER, ballCount * (2 * 4)); + } + + public void render(final boolean render, final boolean animate, final int delta) { + int batchSize = Math.min(ballCount, BALLS_PER_BATCH); + int ballIndex = 0; + while ( ballIndex < ballCount ) { + if ( animate ) { + final ByteBuffer buffer = animVBO.map(batchSize * (2 * 4)); + + animate(sprites, SpriteRender.<SpriteRender>map(buffer), ballSize, ballIndex, batchSize, delta); + + animVBO.unmap(); + } + + if ( render ) { + glVertexPointer(2, GL_FLOAT, 0, ballIndex * (2 * 4)); + glDrawArrays(GL_POINTS, 0, batchSize); + } + + ballIndex += batchSize; + batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH); + } + } + + } + + private class SpriteRendererTF extends SpriteRenderer { + + private int progIDTF; + private int ballSizeLoc; + private int deltaLoc; + + private int[] tfVBO = new int[2]; + private int currVBO; + + SpriteRendererTF() { + System.out.println("Shootout Implementation: TF GPU animation"); + + // Transform-feedback program + + final int vshID = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vshID, "#version 130\n" + + "const float WIDTH = " + SCREEN_WIDTH + ";\n" + + "const float HEIGHT = " + SCREEN_HEIGHT + ";\n" + + "uniform float ballSize;\n" + // ballSize / 2 + "uniform float delta;\n" + + "void main(void) {\n" + + " vec4 anim = gl_Vertex;\n" + + " anim.xy = anim.xy + anim.zw * delta;\n" + + " vec2 animC = clamp(anim.xy, vec2(ballSize), vec2(WIDTH - ballSize, HEIGHT - ballSize));\n" + + " if ( anim.x != animC.x ) anim.z = -anim.z;\n" + + " if ( anim.y != animC.y ) anim.w = -anim.w;\n" + + " gl_Position = vec4(animC, anim.zw);\n" + + "}"); + glCompileShader(vshID); + if ( glGetShader(vshID, GL_COMPILE_STATUS) == GL_FALSE ) { + System.out.println(glGetShaderInfoLog(vshID, glGetShader(vshID, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to compile vertex shader."); + } + + progIDTF = glCreateProgram(); + glAttachShader(progIDTF, vshID); + glTransformFeedbackVaryings(progIDTF, new CharSequence[] { "gl_Position" }, GL_SEPARATE_ATTRIBS); + glLinkProgram(progIDTF); + if ( glGetProgram(progIDTF, GL_LINK_STATUS) == GL_FALSE ) { + System.out.println(glGetProgramInfoLog(progIDTF, glGetProgram(progIDTF, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to link shader program."); + } + + glUseProgram(progIDTF); + + ballSizeLoc = glGetUniformLocation(progIDTF, "ballSize"); + deltaLoc = glGetUniformLocation(progIDTF, "delta"); + + glUniform1f(ballSizeLoc, ballSize * 0.5f); + + // ----------------- + + this.vshID = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(this.vshID, "void main(void) {\n" + + " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" + + "}"); + glCompileShader(this.vshID); + if ( glGetShader(this.vshID, GL_COMPILE_STATUS) == GL_FALSE ) { + System.out.println(glGetShaderInfoLog(this.vshID, glGetShader(this.vshID, GL_INFO_LOG_LENGTH))); + throw new RuntimeException("Failed to compile vertex shader."); + } + + createProgram(); + } + + public void updateBallSize() { + glUseProgram(progIDTF); + glUniform1f(ballSizeLoc, ballSize * 0.5f); + + glUseProgram(progID); + super.updateBallSize(); + } + + private void doUpdateBalls(final int count) { + final Random random = new Random(); + + final Sprite newSprites = Sprite.malloc(count); + if ( sprites != null ) { + sprites.view = 0; + sprites.copyRange(newSprites, Math.min(count, spriteCount)); + } + + if ( count > spriteCount ) { + for ( int i = spriteCount; i < count; i++ ) { + newSprites.view = i; + + newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f); + newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f); + newSprites.dx = random.nextFloat() * 0.4f - 0.2f; + newSprites.dy = random.nextFloat() * 0.4f - 0.2f; + } + } + + sprites = newSprites; + spriteCount = count; + } + + public void updateBalls(final int count) { + if ( tfVBO[0] != 0 ) { + // Fetch current animation state + glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sprites.backingByteBuffer()); + } + + doUpdateBalls(count); + + if ( tfVBO[0] != 0 ) { + for ( int i = 0; i < tfVBO.length; i++ ) + glDeleteBuffers(tfVBO[i]); + } + + for ( int i = 0; i < tfVBO.length; i++ ) { + tfVBO[i] = glGenBuffers(); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tfVBO[i]); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sprites.backingByteBuffer(), GL_STATIC_DRAW); + } + + glBindBuffer(GL_ARRAY_BUFFER, tfVBO[0]); + glVertexPointer(2, GL_FLOAT, (4 * 4), 0); + } + + public void render(final boolean render, final boolean animate, final int delta) { + if ( animate ) { + glUseProgram(progIDTF); + glUniform1f(deltaLoc, delta); + + final int vbo = currVBO; + currVBO = 1 - currVBO; + + glBindBuffer(GL_ARRAY_BUFFER, tfVBO[vbo]); + glVertexPointer(4, GL_FLOAT, 0, 0); + + glEnable(GL_RASTERIZER_DISCARD); + if ( GLContext.getCapabilities().OpenGL30 ) { + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfVBO[1 - vbo]); + + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, ballCount); + glEndTransformFeedback(); + } else { + glBindBufferBaseEXT(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, tfVBO[1 - vbo]); + + glBeginTransformFeedbackEXT(GL_POINTS); + glDrawArrays(GL_POINTS, 0, ballCount); + glEndTransformFeedbackEXT(); + } + glDisable(GL_RASTERIZER_DISCARD); + + glUseProgram(progID); + glVertexPointer(2, GL_FLOAT, (4 * 4), 0); + } + + if ( render ) + glDrawArrays(GL_POINTS, 0, ballCount); + } + + } + +} \ No newline at end of file Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedHelper.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -31,6 +31,9 @@ */ package org.lwjgl.util.mapped; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.MemoryUtil; + import java.nio.ByteBuffer; /** @@ -43,30 +46,32 @@ public class MappedHelper { public static void setup(MappedObject mo, ByteBuffer buffer, int align, int sizeof) { - if ( mo.baseAddress != 0L ) + if ( LWJGLUtil.CHECKS && mo.baseAddress != 0L ) throw new IllegalStateException("this method should not be called by user-code"); - if ( buffer == null ) - throw new NullPointerException("buffer"); - if ( !buffer.isDirect() ) + if ( LWJGLUtil.CHECKS && !buffer.isDirect() ) throw new IllegalArgumentException("bytebuffer must be direct"); mo.preventGC = buffer; - if ( align <= 0 ) + if ( LWJGLUtil.CHECKS && align <= 0 ) throw new IllegalArgumentException("invalid alignment"); mo.align = align; - if ( sizeof % align != 0 ) + if ( LWJGLUtil.CHECKS && (sizeof <= 0 || sizeof % align != 0) ) throw new IllegalStateException("sizeof not a multiple of alignment"); mo.sizeof = sizeof; - long addr = MappedObjectUnsafe.getBufferBaseAddress(buffer) + buffer.position(); - if ( addr % align != 0 ) + long addr = MemoryUtil.getAddress(buffer); + if ( LWJGLUtil.CHECKS && addr % align != 0 ) throw new IllegalStateException("buffer address not aligned on " + align + " bytes"); mo.baseAddress = mo.viewAddress = addr; } + public static void checkAddress(MappedObject mapped, long viewAddress) { + mapped.checkAddress(viewAddress); + } + public static void put_views(MappedSet2 set, int view) { set.view(view); } @@ -87,6 +92,14 @@ return (int)(mapped.viewAddress - mapped.baseAddress) / sizeof; } + public static void put_view_shift(MappedObject mapped, int view, int sizeof_shift) { + mapped.setViewAddress(mapped.baseAddress + (view << sizeof_shift)); + } + + public static int get_view_shift(MappedObject mapped, int sizeof_shift) { + return ((int)(mapped.viewAddress - mapped.baseAddress)) >> sizeof_shift; + } + public static void put_view_next(MappedObject mapped, int sizeof) { mapped.setViewAddress(mapped.viewAddress + sizeof); } @@ -130,68 +143,124 @@ MappedObjectUnsafe.INSTANCE.putByte(addr, value); } + public static void bput(MappedObject mapped, byte value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putByte(mapped.viewAddress + fieldOffset, value); + } + public static byte bget(long addr) { return MappedObjectUnsafe.INSTANCE.getByte(addr); } + public static byte bget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getByte(mapped.viewAddress + fieldOffset); + } + // short public static void sput(short value, long addr) { MappedObjectUnsafe.INSTANCE.putShort(addr, value); } + public static void sput(MappedObject mapped, short value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putShort(mapped.viewAddress + fieldOffset, value); + } + public static short sget(long addr) { return MappedObjectUnsafe.INSTANCE.getShort(addr); } + public static short sget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getShort(mapped.viewAddress + fieldOffset); + } + // char public static void cput(char value, long addr) { MappedObjectUnsafe.INSTANCE.putChar(addr, value); } + public static void cput(MappedObject mapped, char value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putChar(mapped.viewAddress + fieldOffset, value); + } + public static char cget(long addr) { return MappedObjectUnsafe.INSTANCE.getChar(addr); } + public static char cget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getChar(mapped.viewAddress + fieldOffset); + } + // int public static void iput(int value, long addr) { MappedObjectUnsafe.INSTANCE.putInt(addr, value); } - public static int iget(long addr) { - return MappedObjectUnsafe.INSTANCE.getInt(addr); + public static void iput(MappedObject mapped, int value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putInt(mapped.viewAddress + fieldOffset, value); } + public static int iget(long address) { + return MappedObjectUnsafe.INSTANCE.getInt(address); + } + + public static int iget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getInt(mapped.viewAddress + fieldOffset); + } + // float public static void fput(float value, long addr) { MappedObjectUnsafe.INSTANCE.putFloat(addr, value); } + public static void fput(MappedObject mapped, float value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putFloat(mapped.viewAddress + fieldOffset, value); + } + public static float fget(long addr) { return MappedObjectUnsafe.INSTANCE.getFloat(addr); } + public static float fget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getFloat(mapped.viewAddress + fieldOffset); + } + // long public static void jput(long value, long addr) { MappedObjectUnsafe.INSTANCE.putLong(addr, value); } + public static void jput(MappedObject mapped, long value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putLong(mapped.viewAddress + fieldOffset, value); + } + public static long jget(long addr) { return MappedObjectUnsafe.INSTANCE.getLong(addr); } + public static long lget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getLong(mapped.viewAddress + fieldOffset); + } + // double public static void dput(double value, long addr) { MappedObjectUnsafe.INSTANCE.putDouble(addr, value); } + public static void dput(MappedObject mapped, double value, int fieldOffset) { + MappedObjectUnsafe.INSTANCE.putDouble(mapped.viewAddress + fieldOffset, value); + } + public static double dget(long addr) { return MappedObjectUnsafe.INSTANCE.getDouble(addr); } + public static double dget(MappedObject mapped, int fieldOffset) { + return MappedObjectUnsafe.INSTANCE.getDouble(mapped.viewAddress + fieldOffset); + } + } \ No newline at end of file Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObject.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObject.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObject.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -32,6 +32,7 @@ package org.lwjgl.util.mapped; import org.lwjgl.LWJGLUtil; +import org.lwjgl.MemoryUtil; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; @@ -93,7 +94,7 @@ } final void checkAddress(final long address) { - final long base = MappedObjectUnsafe.getBufferBaseAddress(preventGC); + final long base = MemoryUtil.getAddress0(preventGC); final int offset = (int)(address - base); if ( address < base || preventGC.capacity() < (offset + this.sizeof) ) throw new IndexOutOfBoundsException(Integer.toString(offset / sizeof)); @@ -103,7 +104,7 @@ if ( bytes < 0 ) throw new IllegalArgumentException(); - if ( preventGC.capacity() < (viewAddress - MappedObjectUnsafe.getBufferBaseAddress(preventGC) + bytes) ) + if ( preventGC.capacity() < (viewAddress - MemoryUtil.getAddress0(preventGC) + bytes) ) throw new BufferOverflowException(); } @@ -155,7 +156,7 @@ /** * Creates an identical new MappedObject instance, comparable to the - * contract of {@link ByteBuffer#duplicate}. This is useful when more than one + * contract of {@link java.nio.ByteBuffer#duplicate}. This is useful when more than one * views of the mapped object are required at the same time, e.g. in * multithreaded access. */ @@ -166,7 +167,7 @@ /** * Creates a new MappedObject instance, with a base offset equal to - * the offset of the current view, comparable to the contract of {@link ByteBuffer#slice}. + * the offset of the current view, comparable to the contract of {@link java.nio.ByteBuffer#slice}. */ public final <T extends MappedObject> T slice() { // any method that calls this method will have its call-site modified @@ -188,6 +189,11 @@ throw new InternalError("type not registered"); } + /** Moves the current view to the next element. Non-transformed implementation for MappedSets. */ + final void nextSet() { + setViewAddress(viewAddress + sizeof); + } + /** * Copies and amount of <code>SIZEOF</code> bytes, from the current * mapped object, to the specified mapped object. @@ -220,10 +226,16 @@ return new MappedForeach<T>(mapped, elementCount); } + @SuppressWarnings("unused") + public final <T extends MappedObject> T[] asArray() { + // any method that calls this method will have its call-site modified + throw new InternalError("type not registered"); + } + ByteBuffer preventGC; /** - * Returns the {@link ByteBuffer} that backs this mapped object. + * Returns the {@link java.nio.ByteBuffer} that backs this mapped object. * * @return the backing buffer */ Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -117,7 +117,7 @@ byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class"))); long t0 = System.nanoTime(); - bytecode = MappedObjectTransformer.transformFieldAccess(className, bytecode); + bytecode = MappedObjectTransformer.transformMappedAPI(className, bytecode); long t1 = System.nanoTime(); total_time_transforming += (t1 - t0); Modified: trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java 2011-07-21 22:52:16 UTC (rev 3596) +++ trunk/LWJGL/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java 2011-07-22 02:01:56 UTC (rev 3597) @@ -1,21 +1,51 @@ /* - * Created on Jun 23, 2011 + * Copyright (c) 2002-2011 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.util.mapped; import org.lwjgl.LWJGLUtil; import org.objectweb.asm.*; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.*; import org.objectweb.asm.util.TraceClassVisitor; -import java.io.*; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; -import java.util.StringTokenizer; +import static org.objectweb.asm.ClassWriter.*; import static org.objectweb.asm.Opcodes.*; /** @@ -25,7 +55,7 @@ * The transformer supports some debugging tools, enabled through JVM system properties:<br/> * org.lwjgl.util.mapped.PrintTiming=true, prints timing information for the transformation step.<br/> * org.lwjgl.util.mapped.PrintActivity=true, prints activity information.<br/> - * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode. [not working atm]<br/> + * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode.<br/> * org.lwjgl.util.Debug must also be set to true for the above to work. * * @author Riven @@ -41,7 +71,24 @@ static final String MAPPED_OBJECT_JVM = jvmClassName(MappedObject.class); static final String MAPPED_HELPER_JVM = jvmClassName(MappedHelper.class); + static final String MAPPEDSET_PREFIX = jvmClassName(MappedSet.class); + static final String MAPPED_SET2_JVM = jvmClassName(MappedSet2.class); + static final String MAPPED_SET3_JVM = jvmClassName(MappedSet3.class); + static final String MAPPED_SET4_JVM = jvmClassName(MappedSet4.class); + + static final String LENGTH_METHOD_NAME = "length$LWJGL"; + static final String VIEWADDRESS_METHOD_NAME = "getViewAddress$LWJGL"; + static final String VIEW_CONSTRUCTOR_NAME = "constructView$LWJGL"; + + static final Map<Integer, String> OPCODE_TO_NAME = new HashMap<Integer, String>(); + static final Map<Integer, String> INSNTYPE_TO_NAME = new HashMap<Integer, String>(); + + static boolean is_currently_computing_frames; + static { + getClassEnums(Opcodes.class, OPCODE_TO_NAME, "V1_", "ACC_", "T_", "F_", "MH_"); + getClassEnums(AbstractInsnNode.class, INSNTYPE_TO_NAME); + className_to_subtype = new HashMap<String, MappedSubtypeInfo>(); { @@ -59,11 +106,10 @@ // => IADD // => PUTFIELD MyMappedType.view // - MappedSubtypeInfo info = new MappedSubtypeInfo(MAPPED_OBJECT_JVM, -1, -1); - className_to_subtype.put(info.className, info); + className_to_subtype.put(MAPPED_OBJECT_JVM, new MappedSubtypeInfo(MAPPED_OBJECT_JVM, -1, -1)); } - String vmName = System.getProperty("java.vm.name"); + final String vmName = System.getProperty("java.vm.name"); if ( vmName != null && !vmName.contains("Server") ) { System.err.println("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM."); } @@ -71,7 +117,7 @@ /** * Registers a class as a mapped object. - * The class must extend {@link MappedObject} and be annotated with {@link MappedField}. + * The class must extend {@link org.lwjgl.util.mapped.MappedObject} and be annotated with {@link org.lwjgl.util.mapped.MappedField}. * * @param type the mapped object class. */ @@ -79,585 +125,992 @@ if ( MappedObjectClassLoader.FORKED ) return; - MappedType mapped = type.getAnnotation(MappedType.class); + final MappedType mapped = type.getAnnotation(MappedType.class); if ( mapped == null ) throw new InternalError("missing " + MappedType.class.getName() + " annotation"); if ( type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers()) ) throw new InternalError("only top-level or static inner classes are allowed"); - String className = jvmClassName(type); + final MappedSubtypeInfo mappedType = new MappedSubtypeInfo(jvmClassName(type), mapped.sizeof(), mapped.align()); - MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, mapped.sizeof(), mapped.align()); - int advancingOffset = 0; + for ( Field field : type.getDeclaredFields() ) + advancingOffset += registerField(mapped, mappedType.className, mappedType, advancingOffset, field); - for ( Field field : type.getDeclaredFields() ) { - // static fields are never mapped - if ( Modifier.isStatic(field.getModifiers()) ) - continue; + if ( className_to_subtype.put(mappedType.className, mappedType) != null ) + throw new InternalError("duplicate mapped type: " + mappedType.className); + } - // we only support primitives and ByteBuffers - if ( !field.getType().isPrimitive() && field.getType() != ByteBuffer.class ) - throw new InternalError("field '" + className + "." + field.getName() + "' not supported: " + field.getType()); + private static int registerField(final MappedType mapped, final String className, final MappedSubtypeInfo mappedType, int advancingOffset, final Field field) { + if ( Modifier.isStatic(field.getModifiers()) ) // static fields are never mapped + return 0; - MappedField meta = field.getAnnotation(MappedField.class); - if ( meta == null && !mapped.autoGenerateOffsets() ) - throw new InternalError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className); + // we only support primitives and ByteBuffers + if ( !field.getType().isPrimitive() && field.getType() != ByteBuffer.class ) + throw new InternalError("field '" + className + "." + field.getName() + "' not supported: " + field.getType()); - // quick hack - long byteOffset = meta == null ? advancingOffset : meta.byteOffset(); - long byteLength; - if ( field.getType() == long.class || field.getType() == double.class ) - byteLength = 8; - else if ( field.getType() == int.class || field.getType() == float.class ) - byteLength = 4; - else if ( field.getType() == char.class || field.getType() == short.class ) - byteLength = 2; - else if ( field.getType() == byte.class ) - byteLength = 1; - else if ( field.getType() == ByteBuffer.class ) { - byteLength = meta.byteLength(); - if ( byteLength < 0 ) - throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + byteLength + "]"); - } else - throw new IllegalStateException(field.getType().getName()); + MappedField meta = field.getAnnotation(MappedField.class); + if ( meta == null && !mapped.autoGenerateOffsets() ) + throw new InternalError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className); - if ( field.getType() != ByteBuffer.class ) - if ( (advancingOffset % byteLength) != 0 ) - throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName()); + // quick hack + long byteOffset = meta == null ? advancingOffset : meta.byteOffset(); + long byteLength; + if ( field.getType() == long.class || field.getType() == double.class ) + byteLength = 8; + else if ( field.getType() == int.class || field.getType() == float.class ) + byteLength = 4; + else if ( field.getType() == char.class || field.getType() == short.class ) + byteLength = 2; + else if ( field.getType() == byte.class ) + byteLength = 1; + else if ( field.getType() == ByteBuffer.class ) { + byteLength = meta.byteLength(); + if ( byteLength < 0 ) + throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + byteLength + "]"); + } else + throw new InternalError(field.getType().getName()); - if ( PRINT_ACTIVITY ) - LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]"); + if ( field.getType() != ByteBuffer.class && (advancingOffset % byteLength) != 0 ) + throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName()); - mappedType.fieldToOffset.put(field.getName(), byteOffset); - mappedType.fieldToLength.put(field.getName(), byteLength); + if ( PRINT_ACTIVITY ) + LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]"); - advancingOffset += byteLength; - } + mappedType.fieldToOffset.put(field.getName(), byteOffset); + mappedType.fieldToLength.put(field.getName(), byteLength); + mappedType.fieldToType.put(field.getName(), Type.getType(field.getType())); - if ( className_to_subtype.put(className, mappedType) != null ) { - throw new InternalError("duplicate mapped type: " + className); - } + return (int)byteLength; } - static boolean is_currently_computing_frames = false; - static final String view_constructor_method = "_construct_view_"; + static byte[] transformMappedAPI(final String className, byte[] bytecode) { + final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES) { - static byte[] transformFieldAccess(final String className, byte[] bytecode) { - int flags = ClassWriter.COMPUTE_FRAMES; - - ClassWriter writer = new ClassWriter(flags) { - // HACK: prevent user-code static-initialization-blocks to be executed - @Override protected String getCommonSuperClass(String a, String b) { - if ( is_currently_computing_frames ) - if ( !a.startsWith("java/") || !b.startsWith("java/") ) - return "java/lang/Object"; + // HACK: prevent user-code static-initialization-blocks to be executed + if ( is_currently_computing_frames && !a.startsWith("java/") || !b.startsWith("java/") ) + return "java/lang/Object"; + return super.getCommonSuperClass(a, b); } + }; - ClassAdapter adapter = new ClassAdapter(writer) { + ClassVisitor cv = getTransformationAdapter(className, cw); + if ( className_to_subtype.containsKey(className) ) // Do a first pass to generate address getters + cv = getMethodGenAdapter(className, cv); + + //cr.accept(cv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + new ClassReader(bytecode).accept(cv, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + bytecode = cw.toByteArray(); + + if ( PRINT_BYTECODE ) + printBytecode(bytecode); + + return bytecode; + } + + private static ClassAdapter getMethodGenAdapter(final String className, final ClassVisitor cv) { + return new ClassAdapter(cv) { + @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - { - MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className); + public void visitEnd() { + generateViewAddressGetter(); + generateLengthGetter(); - if ( mappedSubtype != null && !mappedSubtype.className.equals(MAPPED_OBJECT_JVM) ) { - if ( "<init>".equals(name) ) { - if ( !"()V".equals(desc) ) - throw new IllegalStateException(className + " can only have a default constructor, found: " + desc); + final MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className); - MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - mv.visitCode(... [truncated message content] |