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.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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 $
*/
public final class BufferUtils {
private static BufferAllocator allocator = new ReflectionBufferUtils();
private static BufferAllocator allocator = new PrimitiveAllocator();
private static boolean trackDirectMemory = false;
private static ReferenceQueue<Buffer> removeCollected = new ReferenceQueue<Buffer>();
private static ConcurrentHashMap<BufferInfo, BufferInfo> trackedBuffers = new ConcurrentHashMap<BufferInfo, BufferInfo>();
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!
*/
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;
}
/**
* Set it to true if you want to enable direct memory tracking for debugging purpose.
* Default is false.
* To print direct memory usage use BufferUtils.printCurrentDirectMemory(StringBuilder store);
* Set it to true if you want to enable direct memory tracking for debugging
* purpose. Default is false. To print direct memory usage use
* BufferUtils.printCurrentDirectMemory(StringBuilder store);
*
* @param 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
* equal to the given buffer's limit.
* Creates a clone of the given buffer. The clone's capacity is equal to the
* given buffer's limit.
*
* @param buf The buffer to clone
* @param buf
* The buffer to clone
* @return The cloned buffer
*/
public static Buffer clone(Buffer buf) {
@ -110,6 +128,7 @@ public final class BufferUtils {
}
private static void onBufferAllocated(Buffer buffer) {
used = true;
if (BufferUtils.trackDirectMemory) {
if (BufferUtils.cleanupthread == null) {
BufferUtils.cleanupthread = new ClearReferences();
@ -136,11 +155,12 @@ public final class BufferUtils {
}
/**
* Generate a new FloatBuffer using the given array of Vector3f objects.
* The FloatBuffer will be 3 * data.length long and contain the vector data
* as data[0].x, data[0].y, data[0].z, data[1].x... etc.
* Generate a new FloatBuffer using the given array of Vector3f objects. The
* FloatBuffer will be 3 * data.length long and contain the vector data as
* 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) {
if (data == null) {
@ -162,7 +182,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of Quaternion objects.
* 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) {
if (data == null) {
@ -181,10 +202,11 @@ public final class BufferUtils {
}
/**
* Generate a new FloatBuffer using the given array of Vector4 objects.
* The FloatBuffer will be 4 * data.length long and contain the vector data.
* Generate a new FloatBuffer using the given array of Vector4 objects. The
* 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) {
if (data == null) {
@ -206,7 +228,8 @@ public final class BufferUtils {
* Generate a new FloatBuffer using the given array of ColorRGBA objects.
* 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) {
if (data == null) {
@ -226,7 +249,9 @@ public final class BufferUtils {
/**
* 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) {
if (data == null) {
@ -285,8 +310,7 @@ public final class BufferUtils {
* @param index
* the postion to place the data; in terms of colors not floats
*/
public static void setInBuffer(ColorRGBA color, FloatBuffer buf,
int index) {
public static void setInBuffer(ColorRGBA color, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(color.r);
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
* specified index.
* Sets the data contained in the given quaternion into the FloatBuffer at
* the specified index.
*
* @param quat
* the {@link Quaternion} to insert
* @param buf
* the buffer to insert into
* @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,
int index) {
public static void setInBuffer(Quaternion quat, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(quat.getX());
buf.put(quat.getY());
@ -325,8 +349,7 @@ public final class BufferUtils {
* @param index
* the position to place the data; in terms of vector4 not floats
*/
public static void setInBuffer(Vector4f vec, FloatBuffer buf,
int index) {
public static void setInBuffer(Vector4f vec, FloatBuffer buf, int index) {
buf.position(index * 4);
buf.put(vec.getX());
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
* values are in terms of vector number (eg, vector number 0 is positions 0-2
* in the FloatBuffer.)
* values are in terms of vector number (eg, vector number 0 is positions
* 0-2 in the FloatBuffer.)
*
* @param buf
* the buffer to copy from/to
@ -512,11 +535,12 @@ public final class BufferUtils {
// // -- VECTOR2F METHODS -- ////
/**
* Generate a new FloatBuffer using the given array of Vector2f objects.
* The FloatBuffer will be 2 * data.length long and contain the vector data
* as data[0].x, data[0].y, data[1].x... etc.
* Generate a new FloatBuffer using the given array of Vector2f objects. The
* FloatBuffer will be 2 * data.length long and contain the vector data as
* 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) {
if (data == null) {
@ -621,8 +645,8 @@ public final class BufferUtils {
/**
* 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
* in the FloatBuffer.)
* values are in terms of vector number (eg, vector number 0 is positions
* 0-1 in the FloatBuffer.)
*
* @param buf
* 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.
* The new ShortBuffer is separate from the old one and changes are not
* reflected across. If you want to reflect changes, consider using
* Creates a new ShortBuffer with the same contents as the given
* ShortBuffer. The new ShortBuffer is separate from the old one and changes
* are not reflected across. If you want to reflect changes, consider using
* Buffer.duplicate().
*
* @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
* buffer. If the buffer is too small a larger one is created and the old one copied to the new buffer.
* @param buffer 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
* Ensures there is at least the <code>required</code> number of entries
* left after the current position of the buffer. If the buffer is too small
* a larger one is created and the old one copied to the new buffer.
*
* @param buffer
* 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) {
if (buffer != null) {
@ -1161,7 +1191,6 @@ public final class BufferUtils {
return buffer;
}
public static ShortBuffer ensureLargeEnough(ShortBuffer buffer, int required) {
if (buffer != null) {
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("Total heap memory held: ").append(heapMem / 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 {
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");
@ -1248,10 +1278,11 @@ public final class BufferUtils {
/**
* 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
* cleans the direct buffers. However, as this doesn't happen
* immediately after discarding all references to a direct buffer, it's
* easy to OutOfMemoryError yourself using direct buffers.**/
* reference queue. Every once a while, the JVM checks the reference queue
* and cleans the direct buffers. However, as this doesn't happen
* immediately after discarding all references to a direct buffer, it's easy
* to OutOfMemoryError yourself using direct buffers.
**/
public static void destroyDirectBuffer(Buffer toBeDestroyed) {
if (!isDirect(toBeDestroyed)) {
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 :
* FloatBuffer, IntBuffer, ShortBuffer, ByteBuffer,DoubleBuffer, LongBuffer.
* CharBuffer has been excluded as we don't use it.
* Buffer.isDirect() is only java 6. Java 5 only have this method on Buffer
* subclasses : FloatBuffer, IntBuffer, ShortBuffer,
* ByteBuffer,DoubleBuffer, LongBuffer. CharBuffer has been excluded as we
* don't use it.
*
*/
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,
* 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 cleanMethod = null;
private static Method viewedBufferMethod = null;
Loading…
Cancel
Save