From 6b66ec1550a1f022db2dda2db3fe473cbebfec85 Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Wed, 8 Mar 2006 17:14:04 +0000 Subject: Restructured how GLObjectTracker destroys tracked objects during context destruction. Now, in addition to tracking sharing between contexts requested by the user, also tracks the behind-the-scenes sharing going on with e.g. Java2D. Makes determination of whether objects can be immediately destroyed by checking current context and seeing whether it shares the same deleted object pool as the one being destroyed. If objects can not be destroyed immediately, their destruction is deferred until the next makeCurrent of a context sharing objects with the one currently being destroyed (if one exists -- the case of this being the last context actually referencing the objects is handled by the OpenGL drivers). This fixes the resizing problems seen when -Dsun.java2d.opengl.fobject=true is specified along with -Dsun.java2d.opengl=true in Mustang. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@654 232f8b59-042b-4e1e-8c03-345bb8c30851 --- src/classes/com/sun/opengl/impl/GLContextImpl.java | 45 +++++--- .../com/sun/opengl/impl/GLContextShareSet.java | 34 +++++- .../com/sun/opengl/impl/GLObjectTracker.java | 127 +++++++++++++++++++-- src/classes/javax/media/opengl/GLJPanel.java | 3 - 4 files changed, 175 insertions(+), 34 deletions(-) diff --git a/src/classes/com/sun/opengl/impl/GLContextImpl.java b/src/classes/com/sun/opengl/impl/GLContextImpl.java index 4a6885f45..8e38ec276 100644 --- a/src/classes/com/sun/opengl/impl/GLContextImpl.java +++ b/src/classes/com/sun/opengl/impl/GLContextImpl.java @@ -61,6 +61,9 @@ public abstract class GLContextImpl extends GLContext { // Tracks creation and deletion of server-side OpenGL objects when // the Java2D/OpenGL pipeline is active and using FBOs to render private GLObjectTracker tracker; + // Supports deletion of these objects when no other context is + // current which can support immediate deletion of them + private GLObjectTracker deletedObjectTracker; protected GL gl; public GLContextImpl(GLContext shareWith) { @@ -77,7 +80,11 @@ public abstract class GLContextImpl extends GLContext { if (shareContext != null) { GLContextShareSet.registerSharing(this, shareContext); } - GLContextShareSet.registerForObjectTracking(shareWith, this); + // Always indicate real behind-the-scenes sharing to track deleted objects + if (shareContext == null) { + shareContext = Java2D.filterShareContext(shareWith); + } + GLContextShareSet.registerForObjectTracking(shareWith, this, shareContext); } public int makeCurrent() throws GLException { @@ -119,6 +126,12 @@ public abstract class GLContextImpl extends GLContext { lock.unlock(); } else { setCurrent(this); + + // Try cleaning up any stale server-side OpenGL objects + // FIXME: not sure what to do here if this throws + if (deletedObjectTracker != null) { + deletedObjectTracker.clean(getGL()); + } } return res; } @@ -151,23 +164,11 @@ public abstract class GLContextImpl extends GLContext { // If we are tracking creation and destruction of server-side // OpenGL objects, we must decrement the reference count of the // GLObjectTracker upon context destruction. - try { - int res = makeCurrent(); - if (res != CONTEXT_CURRENT) { - // FIXME: we really need to behave better than this - throw new GLException("Unable to make context current to destroy tracked server-side OpenGL objects"); - } - try { - tracker.unref(getGL()); - } finally { - release(); - } - } catch (GLException e) { - // FIXME: should probably do something more intelligent here - if (DEBUG) { - e.printStackTrace(); - } - } + // + // Note that we can only eagerly delete these server-side + // objects if there is another context currrent right now + // which shares textures and display lists with this one. + tracker.unref(deletedObjectTracker); } } @@ -352,6 +353,14 @@ public abstract class GLContextImpl extends GLContext { return tracker; } + public void setDeletedObjectTracker(GLObjectTracker deletedObjectTracker) { + this.deletedObjectTracker = deletedObjectTracker; + } + + public GLObjectTracker getDeletedObjectTracker() { + return deletedObjectTracker; + } + public boolean hasWaiters() { return lock.hasWaiters(); } diff --git a/src/classes/com/sun/opengl/impl/GLContextShareSet.java b/src/classes/com/sun/opengl/impl/GLContextShareSet.java index 20a9364fb..293402899 100644 --- a/src/classes/com/sun/opengl/impl/GLContextShareSet.java +++ b/src/classes/com/sun/opengl/impl/GLContextShareSet.java @@ -52,6 +52,7 @@ import javax.media.opengl.*; public class GLContextShareSet { private static boolean forceTracking = Debug.isPropertyDefined("jogl.glcontext.forcetracking"); + private static final boolean DEBUG = Debug.debug("GLContextShareSet"); // This class is implemented with a WeakHashMap that goes from the // contexts as keys to a complex data structure as value that tracks @@ -157,7 +158,8 @@ public class GLContextShareSet { before any server-side OpenGL objects have been created in that context. */ public static synchronized void registerForObjectTracking(GLContext olderContextOrNull, - GLContext newContext) { + GLContext newContext, + GLContext realShareContext) { if (isObjectTrackingEnabled() || isObjectTrackingDebuggingEnabled()) { if (olderContextOrNull != null && newContext != null) { @@ -170,6 +172,36 @@ public class GLContextShareSet { GLContextImpl impl1 = (GLContextImpl) olderContextOrNull; GLContextImpl impl2 = (GLContextImpl) newContext; GLObjectTracker tracker = null; + + GLObjectTracker deletedObjectTracker = null; + GLContextImpl shareImpl = (GLContextImpl) realShareContext; + // Before we zap the "user-level" object trackers, make sure + // that all contexts in the share set share the destroyed object + // tracker + if (shareImpl != null) { + deletedObjectTracker = shareImpl.getDeletedObjectTracker(); + } + if (deletedObjectTracker == null) { + // Must create one and possibly set it up in the older context + deletedObjectTracker = new GLObjectTracker(); + if (DEBUG) { + System.err.println("Created deletedObjectTracker " + deletedObjectTracker + " because " + + ((shareImpl == null) ? "shareImpl was null" : "shareImpl's (" + shareImpl + ") deletedObjectTracker was null")); + } + + if (shareImpl != null) { + // FIXME: think should really assert in this case + shareImpl.setDeletedObjectTracker(deletedObjectTracker); + if (DEBUG) { + System.err.println("Set deletedObjectTracker " + deletedObjectTracker + " in shareImpl context " + shareImpl); + } + } + } + impl2.setDeletedObjectTracker(deletedObjectTracker); + if (DEBUG) { + System.err.println("Set deletedObjectTracker " + deletedObjectTracker + " in impl2 context " + impl2); + } + // Don't share object trackers with the primordial share context from Java2D if (Java2D.isOGLPipelineActive()) { // FIXME: probably need to do something different here diff --git a/src/classes/com/sun/opengl/impl/GLObjectTracker.java b/src/classes/com/sun/opengl/impl/GLObjectTracker.java index 663a7df70..dec7892de 100755 --- a/src/classes/com/sun/opengl/impl/GLObjectTracker.java +++ b/src/classes/com/sun/opengl/impl/GLObjectTracker.java @@ -402,22 +402,96 @@ public class GLObjectTracker { remove(getList(VERTEX_SHADERS_EXT), obj, 1); } + //---------------------------------------------------------------------- + // Reference count maintenance and manual deletion + // + + public synchronized void transferAll(GLObjectTracker other) { + for (int i = 0; i < lists.length; i++) { + getList(i).addAll(other.lists[i]); + if (other.lists[i] != null) { + other.lists[i].clear(); + } + } + dirty = true; + } + public synchronized void ref() { ++refCount; } - public synchronized void unref(GL gl) { - if (--refCount == 0) { - for (int i = 0; i < lists.length; i++) { - ObjectList list = lists[i]; - if (list != null) { - list.delete(gl); - lists[i] = null; + public void unref(GLObjectTracker deletedObjectPool) { + boolean tryDelete = false; + synchronized (this) { + if (--refCount == 0) { + tryDelete = true; + } + } + if (tryDelete) { + // See whether we should try to do the work now or whether we + // have to postpone + GLContext cur = GLContext.getCurrent(); + if ((cur != null) && + (cur instanceof GLContextImpl)) { + GLContextImpl curImpl = (GLContextImpl) cur; + if (deletedObjectPool != null && + deletedObjectPool == curImpl.getDeletedObjectTracker()) { + // Should be safe to delete these objects now + try { + delete(curImpl.getGL()); + return; + } catch (GLException e) { + // Shouldn't happen, but if it does, transfer all objects + // to the deleted object pool hoping we can later clean + // them up + deletedObjectPool.transferAll(this); + throw(e); + } + } + } + // If we get here, we couldn't attempt to delete the objects + // right now; instead try to transfer them to the + // deletedObjectPool for later cleanup (FIXME: should consider + // throwing an exception if deletedObjectPool is null, since + // that shouldn't happen) + if (DEBUG) { + String s = null; + if (cur == null) { + s = "current context was null"; + } else if (!(cur instanceof GLContextImpl)) { + s = "current context was not a GLContextImpl"; + } else if (deletedObjectPool == null) { + s = "no current deletedObjectPool"; + } else if (deletedObjectPool != ((GLContextImpl) cur).getDeletedObjectTracker()) { + s = "deletedObjectTracker didn't match"; + if (((GLContextImpl) cur).getDeletedObjectTracker() == null) { + s += " (other was null)"; + } + } else { + s = "unknown reason"; } + System.err.println("Deferred destruction of server-side OpenGL objects into " + deletedObjectPool + ": " + s); + } + + if (deletedObjectPool != null) { + deletedObjectPool.transferAll(this); + } + } + } + + public void clean(GL gl) { + if (dirty) { + try { + delete(gl); + dirty = false; + } catch (GLException e) { + // FIXME: not sure what to do here; probably a bad idea to be + // throwing exceptions during an otherwise-successful makeCurrent } } } + //---------------------------------------------------------------------- // Internals only below this point // @@ -459,8 +533,7 @@ public class GLObjectTracker { public ObjectList(Deleter deleter) { this.deleter = deleter; - capacity = MIN_CAPACITY; - data = new int[capacity]; + clear(); } public void add(int obj) { @@ -475,6 +548,15 @@ public class GLObjectTracker { data[size++] = obj; } + public void addAll(ObjectList other) { + if (other == null) { + return; + } + for (int i = 0; i < other.size; i++) { + add(other.data[i]); + } + } + public boolean remove(int value) { for (int i = 0; i < size; i++) { if (data[i] == value) { @@ -506,19 +588,30 @@ public class GLObjectTracker { } public void delete(GL gl) { - for (int i = 0; i < size; i++) { + // Just in case we start throwing exceptions during deletion, + // make sure we make progress rather than going into an infinite + // loop + while (size > 0) { + int obj = data[size - 1]; + --size; if (DEBUG) { - System.err.println("Deleting server-side OpenGL object " + data[i] + + System.err.println("Deleting server-side OpenGL object " + obj + ((name != null) ? (" (" + name + ")") : "")); } - deleter.delete(gl, data[i]); + deleter.delete(gl, obj); } + } + + public void clear() { size = 0; + capacity = MIN_CAPACITY; + data = new int[capacity]; } } private ObjectList[] lists = new ObjectList[NUM_OBJECT_TYPES]; private int refCount; + private boolean dirty; private void add(ObjectList list, int n, IntBuffer ids) { int pos = ids.position(); @@ -727,4 +820,14 @@ public class GLObjectTracker { } return list; } + + private void delete(GL gl) { + for (int i = 0; i < lists.length; i++) { + ObjectList list = lists[i]; + if (list != null) { + list.delete(gl); + lists[i] = null; + } + } + } } diff --git a/src/classes/javax/media/opengl/GLJPanel.java b/src/classes/javax/media/opengl/GLJPanel.java index 8d9b3c851..445ea3c4d 100644 --- a/src/classes/javax/media/opengl/GLJPanel.java +++ b/src/classes/javax/media/opengl/GLJPanel.java @@ -506,13 +506,10 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { if (curSurface != null) { if (j2dSurface != curSurface) { if (joglContext != null) { - // Note that the following operation may make the - // joglContext current briefly joglContext.destroy(); joglContext = null; joglDrawable = null; sendReshape = true; - j2dContext.makeCurrent(); if (DEBUG) { System.err.println("Sending reshape because surface changed"); System.err.println("New surface = " + curSurface); -- cgit v1.2.3