diff --git a/engine/src/android/com/jme3/audio/android/AndroidAudioData.java b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java index 37f41a888..e7f4a0f98 100644 --- a/engine/src/android/com/jme3/audio/android/AndroidAudioData.java +++ b/engine/src/android/com/jme3/audio/android/AndroidAudioData.java @@ -59,4 +59,9 @@ public class AndroidAudioData extends AudioData { public NativeObject createDestructableClone() { return new AndroidAudioData(id); } + + @Override + public long getUniqueId() { + return ((long)OBJTYPE_AUDIOBUFFER << 32) | ((long)id); + } } diff --git a/engine/src/core/com/jme3/audio/AudioBuffer.java b/engine/src/core/com/jme3/audio/AudioBuffer.java index 5811fe4f5..230299f5d 100644 --- a/engine/src/core/com/jme3/audio/AudioBuffer.java +++ b/engine/src/core/com/jme3/audio/AudioBuffer.java @@ -120,4 +120,8 @@ public class AudioBuffer extends AudioData { return new AudioBuffer(id); } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_AUDIOBUFFER << 32) | ((long)id); + } } diff --git a/engine/src/core/com/jme3/audio/AudioStream.java b/engine/src/core/com/jme3/audio/AudioStream.java index 67c4a6a70..d528e01fe 100644 --- a/engine/src/core/com/jme3/audio/AudioStream.java +++ b/engine/src/core/com/jme3/audio/AudioStream.java @@ -199,5 +199,8 @@ public class AudioStream extends AudioData implements Closeable{ } } - + @Override + public long getUniqueId() { + return ((long)OBJTYPE_AUDIOSTREAM << 32) | ((long)ids[0]); + } } diff --git a/engine/src/core/com/jme3/audio/LowPassFilter.java b/engine/src/core/com/jme3/audio/LowPassFilter.java index 3e9727e4c..4445c2463 100644 --- a/engine/src/core/com/jme3/audio/LowPassFilter.java +++ b/engine/src/core/com/jme3/audio/LowPassFilter.java @@ -96,4 +96,8 @@ public class LowPassFilter extends Filter { return new LowPassFilter(id); } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_FILTER << 32) | ((long)id); + } } diff --git a/engine/src/core/com/jme3/scene/VertexBuffer.java b/engine/src/core/com/jme3/scene/VertexBuffer.java index 4b7896b61..6405fd7aa 100644 --- a/engine/src/core/com/jme3/scene/VertexBuffer.java +++ b/engine/src/core/com/jme3/scene/VertexBuffer.java @@ -1004,6 +1004,11 @@ public class VertexBuffer extends NativeObject implements Savable, Cloneable { return new VertexBuffer(id); } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_VERTEXBUFFER << 32) | ((long)id); + } + public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(components, "components", 0); diff --git a/engine/src/core/com/jme3/shader/Shader.java b/engine/src/core/com/jme3/shader/Shader.java index 308b5dfd8..1469c045f 100644 --- a/engine/src/core/com/jme3/shader/Shader.java +++ b/engine/src/core/com/jme3/shader/Shader.java @@ -155,6 +155,11 @@ public final class Shader extends NativeObject { return defines; } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_SHADERSOURCE << 32) | ((long)id); + } + @Override public String toString(){ String nameTxt = ""; @@ -322,4 +327,8 @@ public final class Shader extends NativeObject { return new Shader(this); } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_SHADER << 32) | ((long)id); + } } diff --git a/engine/src/core/com/jme3/texture/FrameBuffer.java b/engine/src/core/com/jme3/texture/FrameBuffer.java index ae067903d..bb5317b34 100644 --- a/engine/src/core/com/jme3/texture/FrameBuffer.java +++ b/engine/src/core/com/jme3/texture/FrameBuffer.java @@ -501,4 +501,9 @@ public class FrameBuffer extends NativeObject { public NativeObject createDestructableClone(){ return new FrameBuffer(this); } + + @Override + public long getUniqueId() { + return ((long)OBJTYPE_FRAMEBUFFER << 32) | ((long)id); + } } diff --git a/engine/src/core/com/jme3/texture/Image.java b/engine/src/core/com/jme3/texture/Image.java index a3dc2d2a5..855ab392b 100644 --- a/engine/src/core/com/jme3/texture/Image.java +++ b/engine/src/core/com/jme3/texture/Image.java @@ -393,6 +393,11 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ { return new Image(id); } + @Override + public long getUniqueId() { + return ((long)OBJTYPE_TEXTURE << 32) | ((long)id); + } + /** * @return A shallow clone of this image. The data is not cloned. */ diff --git a/engine/src/core/com/jme3/util/NativeObject.java b/engine/src/core/com/jme3/util/NativeObject.java index 7e88fd4f5..508e6623d 100644 --- a/engine/src/core/com/jme3/util/NativeObject.java +++ b/engine/src/core/com/jme3/util/NativeObject.java @@ -45,6 +45,15 @@ public abstract class NativeObject implements Cloneable { public static final int INVALID_ID = -1; + protected static final int OBJTYPE_VERTEXBUFFER = 1, + OBJTYPE_TEXTURE = 2, + OBJTYPE_FRAMEBUFFER = 3, + OBJTYPE_SHADER = 4, + OBJTYPE_SHADERSOURCE = 5, + OBJTYPE_AUDIOBUFFER = 6, + OBJTYPE_AUDIOSTREAM = 7, + OBJTYPE_FILTER = 8; + /** * The object manager to which this NativeObject is registered to. */ @@ -111,7 +120,7 @@ public abstract class NativeObject implements Cloneable { public int getId(){ return id; } - + /** * Internal use only. Indicates that the object has changed * and its state needs to be updated. @@ -199,6 +208,14 @@ public abstract class NativeObject implements Cloneable { */ public abstract NativeObject createDestructableClone(); + /** + * Returns a unique ID for this NativeObject. No other NativeObject shall + * have the same ID. + * + * @return unique ID for this NativeObject. + */ + public abstract long getUniqueId(); + /** * Reclaims native resources used by this NativeObject. * It should be safe to call this method or even use the object diff --git a/engine/src/core/com/jme3/util/NativeObjectManager.java b/engine/src/core/com/jme3/util/NativeObjectManager.java index cc8e3f144..acd03d910 100644 --- a/engine/src/core/com/jme3/util/NativeObjectManager.java +++ b/engine/src/core/com/jme3/util/NativeObjectManager.java @@ -37,7 +37,7 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Queue; +import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -74,7 +74,7 @@ public class NativeObjectManager { /** * List of currently active GLObjects. */ - private IntMap refMap = new IntMap(); + private HashMap refMap = new HashMap(); /** * List of real objects requested by user for deletion. @@ -91,7 +91,8 @@ public class NativeObjectManager { assert obj.handleRef != null; this.realObj = new WeakReference(obj); - this.objClone = obj.createDestructableClone(); + this.objClone = obj.createDestructableClone(); + assert objClone.getId() == obj.getId(); } } @@ -104,7 +105,7 @@ public class NativeObjectManager { } NativeObjectRef ref = new NativeObjectRef(refQueue, obj); - refMap.put(obj.getId(), ref); + refMap.put(obj.getUniqueId(), ref); obj.setNativeObjectManager(this); @@ -125,30 +126,34 @@ public class NativeObjectManager { assert realObj == null || obj.getId() == realObj.getId(); - if (deleteGL && obj.getId() > 0) { - // Unregister it from cleanup list. - NativeObjectRef ref2 = refMap.remove(obj.getId()); - if (ref2 == null) { - throw new IllegalArgumentException("This NativeObject is not " + - "registered in this NativeObjectManager"); - } - - assert ref == null || ref == ref2; - - int id = obj.getId(); - - // Delete object from the GL driver - obj.deleteObject(rendererObject); - assert obj.getId() == NativeObject.INVALID_ID; - - if (logger.isLoggable(Level.FINEST)) { - logger.log(Level.FINEST, "Deleted: {0}", obj.getClass().getSimpleName() + "/" + id); - } - - if (realObj != null){ - // Note: make sure to reset them as well - // They may get used in a new renderer in the future - realObj.resetObject(); + if (deleteGL) { + if (obj.getId() <= 0) { + logger.log(Level.WARNING, "Object already deleted: {0}", obj.getClass().getSimpleName() + "/" + obj.getId()); + } else { + // Unregister it from cleanup list. + NativeObjectRef ref2 = refMap.remove(obj.getUniqueId()); + if (ref2 == null) { + throw new IllegalArgumentException("This NativeObject is not " + + "registered in this NativeObjectManager"); + } + + assert ref == null || ref == ref2; + + int id = obj.getId(); + + // Delete object from the GL driver + obj.deleteObject(rendererObject); + assert obj.getId() == NativeObject.INVALID_ID; + + if (logger.isLoggable(Level.FINEST)) { + logger.log(Level.FINEST, "Deleted: {0}", obj.getClass().getSimpleName() + "/" + id); + } + + if (realObj != null){ + // Note: make sure to reset them as well + // They may get used in a new renderer in the future + realObj.resetObject(); + } } } if (deleteBufs && UNSAFE && realObj != null) { @@ -194,8 +199,8 @@ public class NativeObjectManager { */ public void deleteAllObjects(Object rendererObject){ deleteUnused(rendererObject); - for (IntMap.Entry entry : refMap) { - NativeObjectRef ref = entry.getValue(); + ArrayList refMapCopy = new ArrayList(refMap.values()); + for (NativeObjectRef ref : refMapCopy) { deleteNativeObject(rendererObject, ref.objClone, ref, true, false); } assert refMap.size() == 0; @@ -219,9 +224,9 @@ public class NativeObjectManager { * This is typically called when the context is restarted. */ public void resetObjects(){ - for (IntMap.Entry entry : refMap) { + for (NativeObjectRef ref : refMap.values()) { // Must use the real object here, for this to be effective. - NativeObject realObj = entry.getValue().realObj.get(); + NativeObject realObj = ref.realObj.get(); if (realObj == null) { continue; }