|
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] |