* Add NativeObject.dispose() which deletes the object from GL driver, and if UNSAFE=true, also native buffers.
* Rename NativeObjectManager.registerForCleanup() -> registerObject() so that its not confused with enqueueUnusedObject() git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10618 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
bf2a663022
commit
d6fbd97482
@ -296,7 +296,7 @@ public class AndroidOpenALSoftAudioRenderer implements AndroidAudioRenderer, Run
|
||||
id = ib.get(0);
|
||||
f.setId(id);
|
||||
|
||||
objManager.registerForCleanup(f);
|
||||
objManager.registerObject(f);
|
||||
}
|
||||
|
||||
if (f instanceof LowPassFilter) {
|
||||
@ -1212,7 +1212,7 @@ public class AndroidOpenALSoftAudioRenderer implements AndroidAudioRenderer, Run
|
||||
id = ib.get(0);
|
||||
ab.setId(id);
|
||||
|
||||
objManager.registerForCleanup(ab);
|
||||
objManager.registerObject(ab);
|
||||
}
|
||||
|
||||
ab.getData().clear();
|
||||
|
@ -942,7 +942,7 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
shader.clearUpdateNeeded();
|
||||
if (needRegister) {
|
||||
// Register shader for clean up if it was created in this method.
|
||||
objManager.registerForCleanup(shader);
|
||||
objManager.registerObject(shader);
|
||||
statistics.onNewShader();
|
||||
} else {
|
||||
// OpenGL spec: uniform locations may change after re-link
|
||||
@ -1318,7 +1318,7 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
|
||||
id = intBuf1.get(0);
|
||||
fb.setId(id);
|
||||
objManager.registerForCleanup(fb);
|
||||
objManager.registerObject(fb);
|
||||
|
||||
statistics.onNewFrameBuffer();
|
||||
}
|
||||
@ -1673,7 +1673,7 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
|
||||
texId = intBuf1.get(0);
|
||||
img.setId(texId);
|
||||
objManager.registerForCleanup(img);
|
||||
objManager.registerObject(img);
|
||||
|
||||
statistics.onNewTexture();
|
||||
}
|
||||
@ -1880,7 +1880,7 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
|
||||
bufId = intBuf1.get(0);
|
||||
vb.setId(bufId);
|
||||
objManager.registerForCleanup(vb);
|
||||
objManager.registerObject(vb);
|
||||
|
||||
created = true;
|
||||
}
|
||||
@ -2252,14 +2252,9 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
|
||||
/**
|
||||
* renderMeshVertexArray renders a mesh using vertex arrays
|
||||
* @param mesh
|
||||
* @param lod
|
||||
* @param count
|
||||
*/
|
||||
private void renderMeshVertexArray(Mesh mesh, int lod, int count) {
|
||||
// IntMap<VertexBuffer> buffers = mesh.getBuffers();
|
||||
for (VertexBuffer vb : mesh.getBufferList().getArray()) {
|
||||
|
||||
if (vb.getBufferType() == Type.InterleavedData
|
||||
|| vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
|
||||
|| vb.getBufferType() == Type.Index) {
|
||||
@ -2306,7 +2301,6 @@ public class OGLESShaderRenderer implements Renderer {
|
||||
indices = mesh.getBuffer(Type.Index);// buffers.get(Type.Index.ordinal());
|
||||
}
|
||||
for (VertexBuffer vb : mesh.getBufferList().getArray()){
|
||||
|
||||
if (vb.getBufferType() == Type.InterleavedData
|
||||
|| vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers
|
||||
|| vb.getBufferType() == Type.Index) {
|
||||
|
@ -45,6 +45,11 @@ public abstract class NativeObject implements Cloneable {
|
||||
|
||||
public static final int INVALID_ID = -1;
|
||||
|
||||
/**
|
||||
* The object manager to which this NativeObject is registered to.
|
||||
*/
|
||||
protected NativeObjectManager objectManager = null;
|
||||
|
||||
/**
|
||||
* The ID of the object, usually depends on its type.
|
||||
* Typically returned from calls like glGenTextures, glGenBuffers, etc.
|
||||
@ -82,9 +87,14 @@ public abstract class NativeObject implements Cloneable {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
void setNativeObjectManager(NativeObjectManager objectManager) {
|
||||
this.objectManager = objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the GLObject. This method is used in Renderer and must
|
||||
* Sets the ID of the NativeObject. This method is used in Renderer and must
|
||||
* not be called by the user.
|
||||
*
|
||||
* @param id The ID to set
|
||||
*/
|
||||
public void setId(int id){
|
||||
@ -138,6 +148,7 @@ public abstract class NativeObject implements Cloneable {
|
||||
try {
|
||||
NativeObject obj = (NativeObject) super.clone();
|
||||
obj.handleRef = new Object();
|
||||
obj.objectManager = null;
|
||||
obj.id = INVALID_ID;
|
||||
obj.updateNeeded = true;
|
||||
return obj;
|
||||
@ -187,4 +198,17 @@ public abstract class NativeObject implements Cloneable {
|
||||
* should be functional for this object.
|
||||
*/
|
||||
public abstract NativeObject createDestructableClone();
|
||||
|
||||
/**
|
||||
* Reclaims native resources used by this NativeObject.
|
||||
* It should be safe to call this method or even use the object
|
||||
* after it has been reclaimed, unless {@link NativeObjectManager#UNSAFE} is
|
||||
* set to true, in that case native buffers are also reclaimed which may
|
||||
* introduce instability.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (objectManager != null) {
|
||||
objectManager.markUnusedObject(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,13 @@
|
||||
*/
|
||||
package com.jme3.util;
|
||||
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Queue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -51,6 +53,13 @@ public class NativeObjectManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(NativeObjectManager.class.getName());
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> to enable deletion of native buffers together with GL objects
|
||||
* when requested. Note that usage of object after deletion could cause undefined results
|
||||
* or native crashes, therefore by default this is set to <code>false</code>.
|
||||
*/
|
||||
public static boolean UNSAFE = false;
|
||||
|
||||
/**
|
||||
* The maximum number of objects that should be removed per frame.
|
||||
* If the limit is reached, no more objects will be removed for that frame.
|
||||
@ -58,23 +67,26 @@ public class NativeObjectManager {
|
||||
private static final int MAX_REMOVES_PER_FRAME = 100;
|
||||
|
||||
/**
|
||||
* The queue will receive notifications of {@link NativeObject}s which
|
||||
* are no longer referenced.
|
||||
* Reference queue for {@link NativeObjectRef native object references}.
|
||||
*/
|
||||
private ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
|
||||
|
||||
/**
|
||||
* List of currently active GLObjects.
|
||||
*/
|
||||
private HashSet<NativeObjectRef> refList
|
||||
= new HashSet<NativeObjectRef>();
|
||||
private IntMap<NativeObjectRef> refMap = new IntMap<NativeObjectRef>();
|
||||
|
||||
private class NativeObjectRef extends PhantomReference<Object>{
|
||||
/**
|
||||
* List of real objects requested by user for deletion.
|
||||
*/
|
||||
private ArrayDeque<NativeObject> userDeletionQueue = new ArrayDeque<NativeObject>();
|
||||
|
||||
private static class NativeObjectRef extends PhantomReference<Object> {
|
||||
|
||||
private NativeObject objClone;
|
||||
private WeakReference<NativeObject> realObj;
|
||||
|
||||
public NativeObjectRef(NativeObject obj){
|
||||
public NativeObjectRef(ReferenceQueue<Object> refQueue, NativeObject obj){
|
||||
super(obj.handleRef, refQueue);
|
||||
assert obj.handleRef != null;
|
||||
|
||||
@ -84,18 +96,68 @@ public class NativeObjectManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a GLObject with the manager.
|
||||
* (Internal use only) Register a <code>NativeObject</code> with the manager.
|
||||
*/
|
||||
public void registerForCleanup(NativeObject obj){
|
||||
NativeObjectRef ref = new NativeObjectRef(obj);
|
||||
refList.add(ref);
|
||||
public void registerObject(NativeObject obj) {
|
||||
if (obj.getId() <= 0) {
|
||||
throw new IllegalArgumentException("object id must be greater than zero");
|
||||
}
|
||||
|
||||
NativeObjectRef ref = new NativeObjectRef(refQueue, obj);
|
||||
refMap.put(obj.getId(), ref);
|
||||
|
||||
obj.setNativeObjectManager(this);
|
||||
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.log(Level.FINEST, "Registered: {0}", new String[]{obj.toString()});
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteNativeObject(Object rendererObject, NativeObject obj, NativeObjectRef ref,
|
||||
boolean deleteGL, boolean deleteBufs) {
|
||||
assert rendererObject != null;
|
||||
|
||||
// "obj" is considered the real object (with buffers and everything else)
|
||||
// if "ref" is null.
|
||||
NativeObject realObj = ref != null ?
|
||||
ref.realObj.get() :
|
||||
obj;
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Only the real object has native buffers.
|
||||
// The destructable clone has nothing and cannot be used in this case.
|
||||
realObj.deleteNativeBuffersInternal();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes unused NativeObjects.
|
||||
* (Internal use only) Deletes unused NativeObjects.
|
||||
* Will delete at most {@link #MAX_REMOVES_PER_FRAME} objects.
|
||||
*
|
||||
* @param rendererObject The renderer object.
|
||||
@ -103,14 +165,20 @@ public class NativeObjectManager {
|
||||
*/
|
||||
public void deleteUnused(Object rendererObject){
|
||||
int removed = 0;
|
||||
while (removed < MAX_REMOVES_PER_FRAME && !userDeletionQueue.isEmpty()) {
|
||||
// Remove user requested objects.
|
||||
NativeObject obj = userDeletionQueue.pop();
|
||||
deleteNativeObject(rendererObject, obj, null, true, true);
|
||||
removed++;
|
||||
}
|
||||
while (removed < MAX_REMOVES_PER_FRAME) {
|
||||
// Remove objects reclaimed by GC.
|
||||
NativeObjectRef ref = (NativeObjectRef) refQueue.poll();
|
||||
if (ref == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
refList.remove(ref);
|
||||
ref.objClone.deleteObject(rendererObject);
|
||||
deleteNativeObject(rendererObject, ref.objClone, ref, true, false);
|
||||
removed++;
|
||||
}
|
||||
if (removed >= 1) {
|
||||
@ -119,38 +187,49 @@ public class NativeObjectManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all objects. Must only be called when display is destroyed.
|
||||
* (Internal use only) Deletes all objects.
|
||||
* Must only be called when display is destroyed.
|
||||
*/
|
||||
public void deleteAllObjects(Object rendererObject){
|
||||
deleteUnused(rendererObject);
|
||||
for (NativeObjectRef ref : refList){
|
||||
ref.objClone.deleteObject(rendererObject);
|
||||
NativeObject realObj = ref.realObj.get();
|
||||
if (realObj != null){
|
||||
// Note: make sure to reset them as well
|
||||
// They may get used in a new renderer in the future
|
||||
realObj.resetObject();
|
||||
for (IntMap.Entry<NativeObjectRef> entry : refMap) {
|
||||
NativeObjectRef ref = entry.getValue();
|
||||
deleteNativeObject(rendererObject, ref.objClone, ref, true, false);
|
||||
}
|
||||
}
|
||||
refList.clear();
|
||||
assert refMap.size() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all {@link NativeObject}s.
|
||||
* Marks the given <code>NativeObject</code> as unused,
|
||||
* to be deleted on the next frame.
|
||||
* Usage of this object after deletion will cause an exception.
|
||||
* Note that native buffers are only reclaimed if
|
||||
* {@link #UNSAFE} is set to <code>true</code>.
|
||||
*
|
||||
* @param obj The object to mark as unused.
|
||||
*/
|
||||
void enqueueUnusedObject(NativeObject obj) {
|
||||
userDeletionQueue.push(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Internal use only) Resets all {@link NativeObject}s.
|
||||
* This is typically called when the context is restarted.
|
||||
*/
|
||||
public void resetObjects(){
|
||||
for (NativeObjectRef ref : refList){
|
||||
// here we use the actual obj not the clone,
|
||||
// otherwise its useless
|
||||
NativeObject realObj = ref.realObj.get();
|
||||
if (realObj == null)
|
||||
for (IntMap.Entry<NativeObjectRef> entry : refMap) {
|
||||
// Must use the real object here, for this to be effective.
|
||||
NativeObject realObj = entry.getValue().realObj.get();
|
||||
if (realObj == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
realObj.resetObject();
|
||||
if (logger.isLoggable(Level.FINEST))
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.log(Level.FINEST, "Reset: {0}", realObj);
|
||||
}
|
||||
refList.clear();
|
||||
}
|
||||
refMap.clear();
|
||||
}
|
||||
|
||||
// public void printObjects(){
|
||||
|
@ -294,7 +294,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable {
|
||||
id = ib.get(0);
|
||||
f.setId(id);
|
||||
|
||||
objManager.registerForCleanup(f);
|
||||
objManager.registerObject(f);
|
||||
}
|
||||
|
||||
if (f instanceof LowPassFilter) {
|
||||
@ -1033,7 +1033,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable {
|
||||
id = ib.get(0);
|
||||
ab.setId(id);
|
||||
|
||||
objManager.registerForCleanup(ab);
|
||||
objManager.registerObject(ab);
|
||||
}
|
||||
|
||||
ab.getData().clear();
|
||||
|
@ -782,7 +782,7 @@ public class JoglGL1Renderer implements GL1Renderer {
|
||||
gl.glGenTextures(1, ib1);
|
||||
texId = ib1.get(0);
|
||||
img.setId(texId);
|
||||
objManager.registerForCleanup(img);
|
||||
objManager.registerObject(img);
|
||||
|
||||
statistics.onNewTexture();
|
||||
}
|
||||
|
@ -1143,7 +1143,7 @@ public class JoglRenderer implements Renderer {
|
||||
shader.clearUpdateNeeded();
|
||||
if (needRegister) {
|
||||
// Register shader for clean up if it was created in this method.
|
||||
objManager.registerForCleanup(shader);
|
||||
objManager.registerObject(shader);
|
||||
statistics.onNewShader();
|
||||
} else {
|
||||
// OpenGL spec: uniform locations may change after re-link
|
||||
@ -1506,7 +1506,7 @@ public class JoglRenderer implements Renderer {
|
||||
gl.glGenFramebuffers(1, intBuf1);
|
||||
id = intBuf1.get(0);
|
||||
fb.setId(id);
|
||||
objManager.registerForCleanup(fb);
|
||||
objManager.registerObject(fb);
|
||||
|
||||
statistics.onNewFrameBuffer();
|
||||
}
|
||||
@ -1899,7 +1899,7 @@ public class JoglRenderer implements Renderer {
|
||||
gl.glGenTextures(1, intBuf1);
|
||||
texId = intBuf1.get(0);
|
||||
img.setId(texId);
|
||||
objManager.registerForCleanup(img);
|
||||
objManager.registerObject(img);
|
||||
|
||||
statistics.onNewTexture();
|
||||
}
|
||||
@ -2141,7 +2141,7 @@ public class JoglRenderer implements Renderer {
|
||||
gl.glGenBuffers(1, intBuf1);
|
||||
bufId = intBuf1.get(0);
|
||||
vb.setId(bufId);
|
||||
objManager.registerForCleanup(vb);
|
||||
objManager.registerObject(vb);
|
||||
|
||||
//statistics.onNewVertexBuffer();
|
||||
|
||||
|
@ -266,7 +266,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
|
||||
id = ib.get(0);
|
||||
f.setId(id);
|
||||
|
||||
objManager.registerForCleanup(f);
|
||||
objManager.registerObject(f);
|
||||
}
|
||||
|
||||
if (f instanceof LowPassFilter) {
|
||||
@ -1002,7 +1002,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable {
|
||||
id = ib.get(0);
|
||||
ab.setId(id);
|
||||
|
||||
objManager.registerForCleanup(ab);
|
||||
objManager.registerObject(ab);
|
||||
}
|
||||
|
||||
ab.getData().clear();
|
||||
|
@ -729,7 +729,7 @@ public class LwjglGL1Renderer implements GL1Renderer {
|
||||
glGenTextures(ib1);
|
||||
texId = ib1.get(0);
|
||||
img.setId(texId);
|
||||
objManager.registerForCleanup(img);
|
||||
objManager.registerObject(img);
|
||||
|
||||
statistics.onNewTexture();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.WrapAxis;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.ListMap;
|
||||
import com.jme3.util.NativeObject;
|
||||
import com.jme3.util.NativeObjectManager;
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import java.nio.*;
|
||||
@ -129,10 +130,12 @@ public class LwjglRenderer implements Renderer {
|
||||
nameBuf.rewind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statistics getStatistics() {
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Caps> getCaps() {
|
||||
return caps;
|
||||
}
|
||||
@ -1081,7 +1084,7 @@ public class LwjglRenderer implements Renderer {
|
||||
shader.clearUpdateNeeded();
|
||||
if (needRegister) {
|
||||
// Register shader for clean up if it was created in this method.
|
||||
objManager.registerForCleanup(shader);
|
||||
objManager.registerObject(shader);
|
||||
statistics.onNewShader();
|
||||
} else {
|
||||
// OpenGL spec: uniform locations may change after re-link
|
||||
@ -1431,7 +1434,7 @@ public class LwjglRenderer implements Renderer {
|
||||
glGenFramebuffersEXT(intBuf1);
|
||||
id = intBuf1.get(0);
|
||||
fb.setId(id);
|
||||
objManager.registerForCleanup(fb);
|
||||
objManager.registerObject(fb);
|
||||
|
||||
statistics.onNewFrameBuffer();
|
||||
}
|
||||
@ -1802,7 +1805,7 @@ public class LwjglRenderer implements Renderer {
|
||||
glGenTextures(intBuf1);
|
||||
texId = intBuf1.get(0);
|
||||
img.setId(texId);
|
||||
objManager.registerForCleanup(img);
|
||||
objManager.registerObject(img);
|
||||
|
||||
statistics.onNewTexture();
|
||||
}
|
||||
@ -2041,7 +2044,7 @@ public class LwjglRenderer implements Renderer {
|
||||
glGenBuffers(intBuf1);
|
||||
bufId = intBuf1.get(0);
|
||||
vb.setId(bufId);
|
||||
objManager.registerForCleanup(vb);
|
||||
objManager.registerObject(vb);
|
||||
|
||||
//statistics.onNewVertexBuffer();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user