Added support for non-direct buffers for all functions that doesn't cache the buffer address at the native side (e.g. glVertexPointer). Reasons:

1. We can now support calls like "glLight(..., ..., FloatBuffer.wrap(new float[] {...}));" without worrying about running out of direct memory heap, 
since both the FloatBuffer and the array are allocated on the java heap. Future JVMs with stack allocation support could improve this even further.
2. We avoid getting in the way of users that doesn't (yet) know why direct buffers are important.

Obviously, we'd like direct support for arrays, but non-direct buffers are a nice compromise that avoids the API bloat that results when almost all 
functions gain an overloaded versions that take arrays instead of buffers.

Notes:

1. Non-direct buffer support should not affect the performance in the direct buffer case, since the non-direct buffer code path is only activated 
when the isDirect() check fails, and we were already checking isDirect() for sanity checking.
2. When using non-direct buffers, the buffer contents (remaining() bytes) are copied to a resizable ThreadLocal cached direct buffer (which is 
resized as needed) and used instead of the non-direct buffer. Thus, performance of non-direct buffers is lower than direct buffers.
This commit is contained in:
Elias Naur 2007-04-11 17:30:13 +00:00
parent 5a2c33423a
commit 7c6511cf66
5 changed files with 318 additions and 14 deletions

View File

@ -43,6 +43,8 @@ import java.nio.LongBuffer;
* <p>A class to check buffer boundaries in general. If there is unsufficient space
* in the buffer when the call is made then a buffer overflow would otherwise
* occur and cause unexpected behaviour, a crash, or worse, a security risk.
*
* Internal class, don't use.
* </p>
* @author cix_foo <cix_foo@users.sourceforge.net>
* @author elias_naur <elias_naur@users.sourceforge.net>
@ -177,7 +179,7 @@ public class BufferChecks {
* The minimum buffer size
* @throws IllegalArgumentException
*/
private static void checkBufferSize(Buffer buf, int size) {
public static void checkBufferSize(Buffer buf, int size) {
if (buf.remaining() < size) {
throwBufferSizeException(buf, size);
}

View File

@ -0,0 +1,301 @@
/*
* Copyright (c) 2002-2004 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;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.nio.IntBuffer;
import java.nio.FloatBuffer;
import java.nio.LongBuffer;
import java.nio.DoubleBuffer;
import java.nio.ByteOrder;
/**
* Utility class to cache thread local direct buffers so when we are passed a non-direct buffer,
* we can put its contents into a cached direct buffer and use that at the native side instead.
*
* Internal class, don't use.
* @author elias_naur <elias_naur@users.sourceforge.net>
* @version $Revision: 2762 $
* $Id: BufferChecks.java 2762 2007-04-11 16:13:05Z elias_naur $
*/
public final class NondirectBufferWrapper {
private final static int INITIAL_BUFFER_SIZE = 1;
private final static ThreadLocal thread_buffer = new ThreadLocal() {
protected final Object initialValue() {
return new CachedBuffers(INITIAL_BUFFER_SIZE);
}
};
private static CachedBuffers getCachedBuffers(int minimum_byte_size) {
CachedBuffers buffers = (CachedBuffers)thread_buffer.get();
int current_byte_size = buffers.byte_buffer.capacity();
if (minimum_byte_size > current_byte_size) {
buffers = new CachedBuffers(minimum_byte_size);
thread_buffer.set(buffers);
}
return buffers;
}
public static ByteBuffer wrapBufferOrNull(ByteBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static ShortBuffer wrapBufferOrNull(ShortBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static IntBuffer wrapBufferOrNull(IntBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static LongBuffer wrapBufferOrNull(LongBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static FloatBuffer wrapBufferOrNull(FloatBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static DoubleBuffer wrapBufferOrNull(DoubleBuffer buf, int size) {
if (buf != null)
return wrapBuffer(buf, size);
return buf;
}
public static ByteBuffer wrapBuffer(ByteBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static ShortBuffer wrapBuffer(ShortBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static IntBuffer wrapBuffer(IntBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static LongBuffer wrapBuffer(LongBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static FloatBuffer wrapBuffer(FloatBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static DoubleBuffer wrapBuffer(DoubleBuffer buf, int size) {
BufferChecks.checkBufferSize(buf, size);
return wrapDirect(buf);
}
public static ByteBuffer wrapDirectOrNull(ByteBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static ShortBuffer wrapDirectOrNull(ShortBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static FloatBuffer wrapDirectOrNull(FloatBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static IntBuffer wrapDirectOrNull(IntBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static LongBuffer wrapDirectOrNull(LongBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static DoubleBuffer wrapDirectOrNull(DoubleBuffer buffer) {
if (buffer != null)
return wrapDirect(buffer);
return buffer;
}
public static ByteBuffer wrapDirect(ByteBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
public static ShortBuffer wrapDirect(ShortBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
public static FloatBuffer wrapDirect(FloatBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
public static IntBuffer wrapDirect(IntBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
public static LongBuffer wrapDirect(LongBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
public static DoubleBuffer wrapDirect(DoubleBuffer buffer) {
if (!buffer.isDirect())
return doWrap(buffer);
return buffer;
}
private static ByteBuffer doWrap(ByteBuffer buffer) {
ByteBuffer direct_buffer = getCachedBuffers(buffer.remaining()).byte_buffer;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private static ShortBuffer doWrap(ShortBuffer buffer) {
CachedBuffers buffers = getCachedBuffers(buffer.remaining()*2);
ShortBuffer direct_buffer = buffer.order() == ByteOrder.LITTLE_ENDIAN ? buffers.short_buffer_little : buffers.short_buffer_big;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private static FloatBuffer doWrap(FloatBuffer buffer) {
CachedBuffers buffers = getCachedBuffers(buffer.remaining()*4);
FloatBuffer direct_buffer = buffer.order() == ByteOrder.LITTLE_ENDIAN ? buffers.float_buffer_little : buffers.float_buffer_big;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private static IntBuffer doWrap(IntBuffer buffer) {
CachedBuffers buffers = getCachedBuffers(buffer.remaining()*4);
IntBuffer direct_buffer = buffer.order() == ByteOrder.LITTLE_ENDIAN ? buffers.int_buffer_little : buffers.int_buffer_big;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private static LongBuffer doWrap(LongBuffer buffer) {
CachedBuffers buffers = getCachedBuffers(buffer.remaining()*8);
LongBuffer direct_buffer = buffer.order() == ByteOrder.LITTLE_ENDIAN ? buffers.long_buffer_little : buffers.long_buffer_big;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private static DoubleBuffer doWrap(DoubleBuffer buffer) {
CachedBuffers buffers = getCachedBuffers(buffer.remaining()*8);
DoubleBuffer direct_buffer = buffer.order() == ByteOrder.LITTLE_ENDIAN ? buffers.double_buffer_little : buffers.double_buffer_big;
direct_buffer.clear();
int saved_position = buffer.position();
direct_buffer.put(buffer);
buffer.position(saved_position);
direct_buffer.flip();
return direct_buffer;
}
private final static class CachedBuffers {
private final ByteBuffer byte_buffer;
private final ShortBuffer short_buffer_big;
private final IntBuffer int_buffer_big;
private final FloatBuffer float_buffer_big;
private final LongBuffer long_buffer_big;
private final DoubleBuffer double_buffer_big;
private final ShortBuffer short_buffer_little;
private final IntBuffer int_buffer_little;
private final FloatBuffer float_buffer_little;
private final LongBuffer long_buffer_little;
private final DoubleBuffer double_buffer_little;
private CachedBuffers(int size) {
this.byte_buffer = ByteBuffer.allocateDirect(size);
this.short_buffer_big = byte_buffer.asShortBuffer();
this.int_buffer_big = byte_buffer.asIntBuffer();
this.float_buffer_big = byte_buffer.asFloatBuffer();
this.long_buffer_big = byte_buffer.asLongBuffer();
this.double_buffer_big = byte_buffer.asDoubleBuffer();
this.byte_buffer.order(ByteOrder.LITTLE_ENDIAN);
this.short_buffer_little = byte_buffer.asShortBuffer();
this.int_buffer_little = byte_buffer.asIntBuffer();
this.float_buffer_little = byte_buffer.asFloatBuffer();
this.long_buffer_little = byte_buffer.asLongBuffer();
this.double_buffer_little = byte_buffer.asDoubleBuffer();
}
}
}

View File

@ -156,18 +156,14 @@ public class Gears {
Display.setLocation((Display.getDisplayMode().getWidth() - 300) / 2,
(Display.getDisplayMode().getHeight() - 300) / 2);
Display.setDisplayMode(new DisplayMode(300, 300));
Display.setTitle("Gears");
Display.setTitle("Gears");
Display.create();
// setup ogl
FloatBuffer pos = BufferUtils.createFloatBuffer(4).put(new float[] { 5.0f, 5.0f, 10.0f, 0.0f});
FloatBuffer red = BufferUtils.createFloatBuffer(4).put(new float[] { 0.8f, 0.1f, 0.0f, 1.0f});
FloatBuffer green = BufferUtils.createFloatBuffer(4).put(new float[] { 0.0f, 0.8f, 0.2f, 1.0f});
FloatBuffer blue = BufferUtils.createFloatBuffer(4).put(new float[] { 0.2f, 0.2f, 1.0f, 1.0f});
pos.flip();
red.flip();
green.flip();
blue.flip();
FloatBuffer pos = FloatBuffer.wrap(new float[] { 5.0f, 5.0f, 10.0f, 0.0f});
FloatBuffer red = FloatBuffer.wrap(new float[] { 0.8f, 0.1f, 0.0f, 1.0f});
FloatBuffer green = FloatBuffer.wrap(new float[] { 0.0f, 0.8f, 0.2f, 1.0f});
FloatBuffer blue = FloatBuffer.wrap(new float[] { 0.2f, 0.2f, 1.0f, 1.0f});
GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, pos);
GL11.glEnable(GL11.GL_CULL_FACE);

View File

@ -171,6 +171,7 @@ public class GeneratorVisitor extends SimpleDeclarationVisitor {
java_writer.println();
java_writer.println("import org.lwjgl.LWJGLException;");
java_writer.println("import org.lwjgl.BufferChecks;");
java_writer.println("import org.lwjgl.NondirectBufferWrapper;");
java_writer.println("import java.nio.*;");
java_writer.println();
java_writer.print("public ");

View File

@ -425,7 +425,8 @@ public class JavaMethodsGenerator {
}
boolean null_terminated = param.getAnnotation(NullTerminated.class) != null;
if (Buffer.class.isAssignableFrom(java_type)) {
printParameterCheck(writer, param.getSimpleName(), check_value, can_be_null, null_terminated);
boolean indirect_buffer_allowed = param.getAnnotation(CachedReference.class) == null;
printParameterCheck(writer, param.getSimpleName(), check_value, can_be_null, null_terminated, indirect_buffer_allowed);
} else if (String.class.equals(java_type)) {
if (!can_be_null)
writer.println("\t\tBufferChecks.checkNotNull(" + param.getSimpleName() + ");");
@ -433,11 +434,14 @@ public class JavaMethodsGenerator {
}
}
if (method.getAnnotation(CachedResult.class) != null)
printParameterCheck(writer, Utils.CACHED_BUFFER_NAME, null, true, false);
printParameterCheck(writer, Utils.CACHED_BUFFER_NAME, null, true, false, false);
}
private static void printParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null, boolean null_terminated) {
writer.print("\t\tBufferChecks.check");
private static void printParameterCheck(PrintWriter writer, String name, String check_value, boolean can_be_null, boolean null_terminated, boolean indirect_buffer_allowed) {
if (indirect_buffer_allowed)
writer.print("\t\t" + name + " = NondirectBufferWrapper.wrap");
else
writer.print("\t\tBufferChecks.check");
if (check_value != null && !"".equals(check_value) ) {
writer.print("Buffer");
if (can_be_null)