Extracted an Allocator interface for DirectByteBuffers

Signed-off-by: Kai Boernert <kai-boernert@visiongamestudios.de>
define_list_fix
Kai Boernert 9 years ago
parent 848a9217d0
commit bc701c174b
  1. 139
      jme3-core/src/main/java/com/jme3/util/BufferUtils.java
  2. 55
      jme3-core/src/main/java/com/jme3/util/PrimitiveAllocator.java
  3. 2
      jme3-core/src/main/java/com/jme3/util/ReflectionAllocator.java

@ -40,6 +40,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.ref.PhantomReference; import java.lang.ref.PhantomReference;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.nio.Buffer; import java.nio.Buffer;
@ -62,24 +63,40 @@ import java.util.logging.Logger;
* @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $ * @version $Id: BufferUtils.java,v 1.16 2007/10/29 16:56:18 nca Exp $
*/ */
public final class BufferUtils { public final class BufferUtils {
private static BufferAllocator allocator = new ReflectionBufferUtils(); private static BufferAllocator allocator = new PrimitiveAllocator();
private static boolean trackDirectMemory = false; private static boolean trackDirectMemory = false;
private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>(); private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>(); private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
static ClearReferences cleanupthread; static ClearReferences cleanupthread;
private static boolean used;
static {
try {
allocator = new ReflectionAllocator();
} catch (InaccessibleObjectException t) {
t.printStackTrace();
System.err.println("Error using ReflectionAllocator");
}
}
/** /**
* Warning! do only set this before JME is started! * Warning! do only set this before JME is started!
*/ */
public static void setAllocator(BufferAllocator allocator) { public static void setAllocator(BufferAllocator allocator) {
if (used) {
throw new IllegalStateException(
"An Buffer was already allocated, since it is quite likely that other dispose methods will create native dangling pointers or other fun things, this is forbidden to be changed at runtime");
}
BufferUtils.allocator = allocator; BufferUtils.allocator = allocator;
} }
/** /**
* Set it to true if you want to enable direct memory tracking for debugging purpose. * Set it to true if you want to enable direct memory tracking for debugging
* Default is false. * purpose. Default is false. To print direct memory usage use
* To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store); * BufferUtils.printCurrentDirectMemory(StringBuilder store);
*
* @param enabled * @param enabled
*/ */
public static void setTrackDirectMemoryEnabled(boolean enabled) { public static void setTrackDirectMemoryEnabled(boolean enabled) {
@ -87,10 +104,11 @@ public final class BufferUtils {
} }
/** /**
* Creates a clone of the given buffer. The clone's capacity is * Creates a clone of the given buffer. The clone's capacity is equal to the
* equal to the given buffer's limit. * given buffer's limit.
* *
* @param buf The buffer to clone * @param buf
* The buffer to clone
* @return The cloned buffer * @return The cloned buffer
*/ */
public static Buffer clone(Buffer buf) { public static Buffer clone(Buffer buf) {
@ -110,6 +128,7 @@ public final class BufferUtils {
} }
private static void onBufferAllocated(Buffer buffer) { private static void onBufferAllocated(Buffer buffer) {
used = true;
if (BufferUtils.trackDirectMemory) { if (BufferUtils.trackDirectMemory) {
if (BufferUtils.cleanupthread == null) { if (BufferUtils.cleanupthread == null) {
BufferUtils.cleanupthread = new ClearReferences(); BufferUtils.cleanupthread = new ClearReferences();
@ -136,11 +155,12 @@ public final class BufferUtils {
} }
/** /**
* Generate a new FloatBuffer using the given array of Vector3f objects. * Generate a new FloatBuffer using the given array of Vector3f objects. The
* The FloatBuffer will be 3 * data.length long and contain the vector data * FloatBuffer will be 3 * data.length long and contain the vector data as
* as data[0].x, data[0].y, data[0].z, data[1].x... etc. * data[0].x, data[0].y, data[0].z, data[1].x... etc.
* *
* @param data array of Vector3f objects to place into a new FloatBuffer * @param data
* array of Vector3f objects to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(Vector3f... data) { public static FloatBuffer createFloatBuffer(Vector3f... data) {
if (data == null) { if (data == null) {
@ -162,7 +182,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of Quaternion objects. * Generate a new FloatBuffer using the given array of Quaternion objects.
* The FloatBuffer will be 4 * data.length long and contain the vector data. * The FloatBuffer will be 4 * data.length long and contain the vector data.
* *
* @param data array of Quaternion objects to place into a new FloatBuffer * @param data
* array of Quaternion objects to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(Quaternion... data) { public static FloatBuffer createFloatBuffer(Quaternion... data) {
if (data == null) { if (data == null) {
@ -181,10 +202,11 @@ public final class BufferUtils {
} }
/** /**
* Generate a new FloatBuffer using the given array of Vector4 objects. * Generate a new FloatBuffer using the given array of Vector4 objects. The
* The FloatBuffer will be 4 * data.length long and contain the vector data. * FloatBuffer will be 4 * data.length long and contain the vector data.
* *
* @param data array of Vector4 objects to place into a new FloatBuffer * @param data
* array of Vector4 objects to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(Vector4f... data) { public static FloatBuffer createFloatBuffer(Vector4f... data) {
if (data == null) { if (data == null) {
@ -206,7 +228,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of ColorRGBA objects. * Generate a new FloatBuffer using the given array of ColorRGBA objects.
* The FloatBuffer will be 4 * data.length long and contain the color data. * The FloatBuffer will be 4 * data.length long and contain the color data.
* *
* @param data array of ColorRGBA objects to place into a new FloatBuffer * @param data
* array of ColorRGBA objects to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(ColorRGBA... data) { public static FloatBuffer createFloatBuffer(ColorRGBA... data) {
if (data == null) { if (data == null) {
@ -226,7 +249,9 @@ public final class BufferUtils {
/** /**
* Generate a new FloatBuffer using the given array of float primitives. * Generate a new FloatBuffer using the given array of float primitives.
* @param data array of float primitives to place into a new FloatBuffer *
* @param data
* array of float primitives to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(float... data) { public static FloatBuffer createFloatBuffer(float... data) {
if (data == null) { if (data == null) {
@ -285,8 +310,7 @@ public final class BufferUtils {
* @param index * @param index
* the postion to place the data; in terms of colors not floats * the postion to place the data; in terms of colors not floats
*/ */
public static void setInBuffer(ColorRGBA color, FloatBuffer buf, public static void setInBuffer(ColorRGBA color, FloatBuffer buf, int index) {
int index) {
buf.position(index * 4); buf.position(index * 4);
buf.put(color.r); buf.put(color.r);
buf.put(color.g); buf.put(color.g);
@ -295,18 +319,18 @@ public final class BufferUtils {
} }
/** /**
* Sets the data contained in the given quaternion into the FloatBuffer at the * Sets the data contained in the given quaternion into the FloatBuffer at
* specified index. * the specified index.
* *
* @param quat * @param quat
* the {@link Quaternion} to insert * the {@link Quaternion} to insert
* @param buf * @param buf
* the buffer to insert into * the buffer to insert into
* @param index * @param index
* the position to place the data; in terms of quaternions not floats * the position to place the data; in terms of quaternions not
* floats
*/ */
public static void setInBuffer(Quaternion quat, FloatBuffer buf, public static void setInBuffer(Quaternion quat, FloatBuffer buf, int index) {
int index) {
buf.position(index * 4); buf.position(index * 4);
buf.put(quat.getX()); buf.put(quat.getX());
buf.put(quat.getY()); buf.put(quat.getY());
@ -325,8 +349,7 @@ public final class BufferUtils {
* @param index * @param index
* the position to place the data; in terms of vector4 not floats * the position to place the data; in terms of vector4 not floats
*/ */
public static void setInBuffer(Vector4f vec, FloatBuffer buf, public static void setInBuffer(Vector4f vec, FloatBuffer buf, int index) {
int index) {
buf.position(index * 4); buf.position(index * 4);
buf.put(vec.getX()); buf.put(vec.getX());
buf.put(vec.getY()); buf.put(vec.getY());
@ -416,8 +439,8 @@ public final class BufferUtils {
/** /**
* Copies a Vector3f from one position in the buffer to another. The index * Copies a Vector3f from one position in the buffer to another. The index
* values are in terms of vector number (eg, vector number 0 is positions 0-2 * values are in terms of vector number (eg, vector number 0 is positions
* in the FloatBuffer.) * 0-2 in the FloatBuffer.)
* *
* @param buf * @param buf
* the buffer to copy from/to * the buffer to copy from/to
@ -512,11 +535,12 @@ public final class BufferUtils {
// // -- VECTOR2F METHODS -- //// // // -- VECTOR2F METHODS -- ////
/** /**
* Generate a new FloatBuffer using the given array of Vector2f objects. * Generate a new FloatBuffer using the given array of Vector2f objects. The
* The FloatBuffer will be 2 * data.length long and contain the vector data * FloatBuffer will be 2 * data.length long and contain the vector data as
* as data[0].x, data[0].y, data[1].x... etc. * data[0].x, data[0].y, data[1].x... etc.
* *
* @param data array of Vector2f objects to place into a new FloatBuffer * @param data
* array of Vector2f objects to place into a new FloatBuffer
*/ */
public static FloatBuffer createFloatBuffer(Vector2f... data) { public static FloatBuffer createFloatBuffer(Vector2f... data) {
if (data == null) { if (data == null) {
@ -621,8 +645,8 @@ public final class BufferUtils {
/** /**
* Copies a Vector2f from one position in the buffer to another. The index * Copies a Vector2f from one position in the buffer to another. The index
* values are in terms of vector number (eg, vector number 0 is positions 0-1 * values are in terms of vector number (eg, vector number 0 is positions
* in the FloatBuffer.) * 0-1 in the FloatBuffer.)
* *
* @param buf * @param buf
* the buffer to copy from/to * the buffer to copy from/to
@ -1093,9 +1117,9 @@ public final class BufferUtils {
} }
/** /**
* Creates a new ShortBuffer with the same contents as the given ShortBuffer. * Creates a new ShortBuffer with the same contents as the given
* The new ShortBuffer is separate from the old one and changes are not * ShortBuffer. The new ShortBuffer is separate from the old one and changes
* reflected across. If you want to reflect changes, consider using * are not reflected across. If you want to reflect changes, consider using
* Buffer.duplicate(). * Buffer.duplicate().
* *
* @param buf * @param buf
@ -1120,12 +1144,18 @@ public final class BufferUtils {
} }
/** /**
* Ensures there is at least the <code>required</code> number of entries left after the current position of the * Ensures there is at least the <code>required</code> number of entries
* buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer. * left after the current position of the buffer. If the buffer is too small
* @param buffer buffer that should be checked/copied (may be null) * a larger one is created and the old one copied to the new buffer.
* @param required minimum number of elements that should be remaining in the returned buffer *
* @return a buffer large enough to receive at least the <code>required</code> number of entries, same position as * @param buffer
* the input buffer, not null * buffer that should be checked/copied (may be null)
* @param required
* minimum number of elements that should be remaining in the
* returned buffer
* @return a buffer large enough to receive at least the
* <code>required</code> number of entries, same position as the
* input buffer, not null
*/ */
public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) { public static FloatBuffer ensureLargeEnough(FloatBuffer buffer, int required) {
if (buffer != null) { if (buffer != null) {
@ -1161,7 +1191,6 @@ public final class BufferUtils {
return buffer; return buffer;
} }
public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) { public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
if (buffer != null) { if (buffer != null) {
buffer.limit(buffer.capacity()); buffer.limit(buffer.capacity());
@ -1236,7 +1265,8 @@ public final class BufferUtils {
store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n"); store.append("(b: ").append(bBufs).append(" f: ").append(fBufs).append(" i: ").append(iBufs).append(" s: ").append(sBufs).append(" d: ").append(dBufs).append(")").append("\n");
store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n"); store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n"); store.append("Total direct memory held: ").append(totalHeld / 1024).append("kb\n");
store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ").append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ").append(dBufsM / 1024).append("kb)").append("\n"); store.append("(b: ").append(bBufsM / 1024).append("kb f: ").append(fBufsM / 1024).append("kb i: ").append(iBufsM / 1024).append("kb s: ").append(sBufsM / 1024).append("kb d: ")
.append(dBufsM / 1024).append("kb)").append("\n");
} else { } else {
store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n"); store.append("Total heap memory held: ").append(heapMem / 1024).append("kb\n");
store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n"); store.append("Only heap memory available, if you want to monitor direct memory use BufferUtils.setTrackDirectMemoryEnabled(true) during initialization.").append("\n");
@ -1248,10 +1278,11 @@ public final class BufferUtils {
/** /**
* Direct buffers are garbage collected by using a phantom reference and a * Direct buffers are garbage collected by using a phantom reference and a
* reference queue. Every once a while, the JVM checks the reference queue and * reference queue. Every once a while, the JVM checks the reference queue
* cleans the direct buffers. However, as this doesn't happen * and cleans the direct buffers. However, as this doesn't happen
* immediately after discarding all references to a direct buffer, it's * immediately after discarding all references to a direct buffer, it's easy
* easy to OutOfMemoryError yourself using direct buffers.**/ * to OutOfMemoryError yourself using direct buffers.
**/
public static void destroyDirectBuffer(Buffer toBeDestroyed) { public static void destroyDirectBuffer(Buffer toBeDestroyed) {
if (!isDirect(toBeDestroyed)) { if (!isDirect(toBeDestroyed)) {
return; return;
@ -1260,11 +1291,13 @@ public final class BufferUtils {
} }
/* /*
* FIXME when java 1.5 supprt is dropped - replace calls to this method with Buffer.isDirect * FIXME when java 1.5 supprt is dropped - replace calls to this method with
* Buffer.isDirect
* *
* Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer subclasses : * Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer
* FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer. * subclasses : FloatBuffer, IntBuffer, ShortBuffer,
* CharBuffer has been excluded as we don't use it. * ByteBuffer,DoubleBuffer, LongBuffer. CharBuffer has been excluded as we
* don't use it.
* *
*/ */
private static boolean isDirect(Buffer buf) { private static boolean isDirect(Buffer buf) {

@ -0,0 +1,55 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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 com.jme3.util;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
* This class contains a primitve allocator with no special logic, should work
* on any jvm
*/
public final class PrimitiveAllocator implements BufferAllocator {
@Override
public void destroyDirectBuffer(Buffer toBeDestroyed) {
// no exception by intent, as this way naivly written java7/8
// applications wont crash on 9 assuming they can dispose buffers
System.err.println("Warning destroyBuffer not supported");
}
@Override
public ByteBuffer allocate(int size) {
return ByteBuffer.allocateDirect(size);
}
}

@ -43,7 +43,7 @@ import java.util.logging.Logger;
* This class contains the reflection based way to remove DirectByteBuffers in java < 9, * This class contains the reflection based way to remove DirectByteBuffers in java < 9,
* allocation is done via ByteBuffer.allocateDirect * allocation is done via ByteBuffer.allocateDirect
*/ */
public final class ReflectionBufferUtils implements BufferAllocator { public final class ReflectionAllocator implements BufferAllocator {
private static Method cleanerMethod = null; private static Method cleanerMethod = null;
private static Method cleanMethod = null; private static Method cleanMethod = null;
private static Method viewedBufferMethod = null; private static Method viewedBufferMethod = null;
Loading…
Cancel
Save