Added support for array access to mapped objects.
Added SpriteShootout test that uses mapped objects.
This commit is contained in:
parent
087d0f4ba9
commit
20b9d3f89f
|
@ -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/"/>
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
libs/asm.jar
BIN
libs/asm.jar
Binary file not shown.
|
@ -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 @@ public class MappedObjectBench {
|
|||
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++ ) {
|
||||
|
|
|
@ -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 @@ public class MappedObjectTests1 {
|
|||
|
||||
// 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);
|
||||
|
|
|
@ -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 @@ public class MappedObjectTests3 {
|
|||
|
||||
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 @@ public class MappedObjectTests3 {
|
|||
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 @@ public class MappedObjectTests3 {
|
|||
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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -71,6 +71,11 @@ public class TestMappedObject {
|
|||
MappedObjectTests3.testConstructor();
|
||||
MappedObjectTests3.testMappedSet();
|
||||
|
||||
MappedObjectTests4.testLocalView();
|
||||
|
||||
//MappedObjectTests4.testLWJGL();
|
||||
|
||||
|
||||
System.out.println("done");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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 @@ import java.nio.ByteBuffer;
|
|||
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 @@ public class MappedHelper {
|
|||
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,38 +143,70 @@ public class MappedHelper {
|
|||
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
|
||||
|
@ -170,28 +215,52 @@ public class MappedHelper {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 @@ public class MappedObject {
|
|||
}
|
||||
|
||||
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 @@ public class MappedObject {
|
|||
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 @@ public class MappedObject {
|
|||
|
||||
/**
|
||||
* 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 @@ public class MappedObject {
|
|||
|
||||
/**
|
||||
* 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 @@ public class MappedObject {
|
|||
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 @@ public class MappedObject {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -117,7 +117,7 @@ public class MappedObjectClassLoader extends URLClassLoader {
|
|||
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);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,7 +32,7 @@
|
|||
package org.lwjgl.util.mapped;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.Buffer;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
@ -49,10 +49,6 @@ public class MappedObjectUnsafe {
|
|||
private static final long BUFFER_ADDRESS_OFFSET = getObjectFieldOffset(ByteBuffer.class, "address");
|
||||
private static final long BUFFER_CAPACITY_OFFSET = getObjectFieldOffset(ByteBuffer.class, "capacity");
|
||||
|
||||
public static long getBufferBaseAddress(Buffer buffer) {
|
||||
return INSTANCE.getLong(buffer, BUFFER_ADDRESS_OFFSET);
|
||||
}
|
||||
|
||||
private static final ByteBuffer global = ByteBuffer.allocateDirect(4 * 1024);
|
||||
|
||||
static ByteBuffer newBuffer(long address, int capacity) {
|
||||
|
@ -75,20 +71,39 @@ public class MappedObjectUnsafe {
|
|||
type = type.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new InternalError();
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static Unsafe getUnsafeInstance() {
|
||||
try {
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(1);
|
||||
Field unsafeField = buffer.getClass().getDeclaredField("unsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
Unsafe instance = (Unsafe)unsafeField.get(buffer);
|
||||
buffer.flip(); // prevented 'buffer' from being gc'ed
|
||||
return instance;
|
||||
} catch (Exception exc) {
|
||||
throw new InternalError();
|
||||
final Field[] fields = Unsafe.class.getDeclaredFields();
|
||||
|
||||
/*
|
||||
Different runtimes use different names for the Unsafe singleton,
|
||||
so we cannot use .getDeclaredField and we scan instead. For example:
|
||||
|
||||
Oracle: theUnsafe
|
||||
PERC : m_unsafe_instance
|
||||
Android: THE_ONE
|
||||
*/
|
||||
for ( Field field : fields ) {
|
||||
if ( !field.getType().equals(Unsafe.class) )
|
||||
continue;
|
||||
|
||||
final int modifiers = field.getModifiers();
|
||||
if ( !(Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) )
|
||||
continue;
|
||||
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
return (Unsafe)field.get(null);
|
||||
} catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -49,8 +49,8 @@ public class MappedSet2 {
|
|||
}
|
||||
|
||||
public void next() {
|
||||
this.a.next();
|
||||
this.b.next();
|
||||
this.a.nextSet();
|
||||
this.b.nextSet();
|
||||
}
|
||||
|
||||
}
|
|
@ -51,9 +51,9 @@ public class MappedSet3 {
|
|||
}
|
||||
|
||||
public void next() {
|
||||
this.a.next();
|
||||
this.b.next();
|
||||
this.c.next();
|
||||
this.a.nextSet();
|
||||
this.b.nextSet();
|
||||
this.c.nextSet();
|
||||
}
|
||||
|
||||
}
|
|
@ -53,9 +53,9 @@ public class MappedSet4 {
|
|||
}
|
||||
|
||||
public void next() {
|
||||
this.a.next();
|
||||
this.b.next();
|
||||
this.c.next();
|
||||
this.d.next();
|
||||
this.a.nextSet();
|
||||
this.b.nextSet();
|
||||
this.c.nextSet();
|
||||
this.d.nextSet();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue