From a302c87258a1a17dc0eef5ae112cb0d4357be238 Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Mon, 6 Feb 2006 04:52:15 +0000 Subject: Further work on FBO support in Java2D/JOGL bridge. Recast how GLObjectTrackers are specified between contexts and separated this from the maintenance of the GLContextShareSet, although the API (registerForObjectTracking) is in the GLContextShareSet class. If the Java2D/OpenGL pipeline and FBOs are active, causes all JOGL contexts created to share textures and display lists with a context from Java2D (currently acquired from a VolatileImage, but will probably need to change how this is done). GLObjectTrackers however are only shared between JOGL contexts where the user has explicitly requested sharing. This yields the expected semantics of server-side object deletion when the context is destroyed. Upgraded GLJPanel to handle FBO manipulation. Not working yet; more debugging necessary on Java2D side as well. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@585 232f8b59-042b-4e1e-8c03-345bb8c30851 --- src/classes/com/sun/opengl/impl/GLContextImpl.java | 6 +- .../com/sun/opengl/impl/GLContextShareSet.java | 110 +++++++++++---------- src/classes/com/sun/opengl/impl/Java2D.java | 104 ++++++++++++++++++- .../impl/windows/WindowsExternalGLContext.java | 3 + src/classes/javax/media/opengl/GLJPanel.java | 57 +++++++++++ 5 files changed, 227 insertions(+), 53 deletions(-) (limited to 'src/classes') diff --git a/src/classes/com/sun/opengl/impl/GLContextImpl.java b/src/classes/com/sun/opengl/impl/GLContextImpl.java index 5dbf08df6..d79de9f5a 100644 --- a/src/classes/com/sun/opengl/impl/GLContextImpl.java +++ b/src/classes/com/sun/opengl/impl/GLContextImpl.java @@ -66,9 +66,11 @@ public abstract class GLContextImpl extends GLContext { public GLContextImpl(GLContext shareWith) { setGL(createGL()); functionAvailability = new FunctionAvailabilityCache(this); - if (shareWith != null || GLContextShareSet.isObjectTrackingDebuggingEnabled()) { - GLContextShareSet.registerSharing(this, shareWith); + GLContext shareContext = Java2D.filterShareContext(shareWith); + if (shareContext != null) { + GLContextShareSet.registerSharing(this, shareContext); } + GLContextShareSet.registerForObjectTracking(shareWith, this); } public int makeCurrent() throws GLException { diff --git a/src/classes/com/sun/opengl/impl/GLContextShareSet.java b/src/classes/com/sun/opengl/impl/GLContextShareSet.java index b0d1b1da4..283a56f85 100644 --- a/src/classes/com/sun/opengl/impl/GLContextShareSet.java +++ b/src/classes/com/sun/opengl/impl/GLContextShareSet.java @@ -62,17 +62,6 @@ public class GLContextShareSet { private Map createdShares = new WeakHashMap(); private Map destroyedShares = new WeakHashMap(); - // When the Java2D/OpenGL pipeline is active and using FBOs to - // render, we need to track the creation and destruction of - // server-side OpenGL objects among contexts sharing these objects - private GLObjectTracker tracker; - - public ShareSet() { - if (isObjectTrackingEnabled()) { - tracker = new GLObjectTracker(); - } - } - public void add(GLContext ctx) { if (allShares.put(ctx, dummyValue) == null) { // FIXME: downcast to GLContextImpl undesirable @@ -111,57 +100,26 @@ public class GLContextShareSet { assert res == null : "State of ShareSet corrupted; thought context " + ctx + " shouldn't have been in destroyed set but was"; } - - public GLObjectTracker getObjectTracker() { - return tracker; - } - } - - private static boolean isObjectTrackingEnabled() { - return (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()); - } - - /** Indicates to callers whether sharing must be registered even for - contexts which don't share textures and display lists with any - others. */ - public static boolean isObjectTrackingDebuggingEnabled() { - return forceTracking; } /** Indicate that contexts share1 and - share2 will share textures and display lists. */ + share2 will share textures and display lists. Both + must be non-null. */ public static synchronized void registerSharing(GLContext share1, GLContext share2) { + if (share1 == null || share2 == null) { + throw new IllegalArgumentException("Both share1 and share2 must be non-null"); + } ShareSet share = entryFor(share1); - if (share == null && (share2 != null)) { + if (share == null) { share = entryFor(share2); } if (share == null) { share = new ShareSet(); } share.add(share1); - if (share2 != null) { - share.add(share2); - } + share.add(share2); addEntry(share1, share); - if (share2 != null) { - addEntry(share2, share); - } - GLObjectTracker tracker = share.getObjectTracker(); - if (tracker != null) { - // FIXME: downcast to GLContextImpl undesirable - GLContextImpl impl1 = (GLContextImpl) share1; - GLContextImpl impl2 = (GLContextImpl) share2; - if (impl1.getObjectTracker() == null) { - impl1.setObjectTracker(tracker); - } - if ((impl2 != null) && (impl2.getObjectTracker() == null)) { - impl2.setObjectTracker(tracker); - } - assert impl1.getObjectTracker() == tracker : "State of ShareSet corrupted; " + - "got different-than-expected GLObjectTracker for context 1"; - assert (impl2 == null) || (impl2.getObjectTracker() == tracker) : "State of ShareSet corrupted; " + - "got different-than-expected GLObjectTracker for context 2"; - } + addEntry(share2, share); } public static synchronized GLContext getShareContext(GLContext contextToCreate) { @@ -186,6 +144,49 @@ public class GLContextShareSet { } } + /** Indicates that the two supplied contexts (which must be able to + share textures and display lists) should be in the same + namespace for tracking of server-side object creation and + deletion. Because the sharing necessary behind the scenes is + different than that requested at the user level, the two notions + are different. This must be called immediately after the + creation of the new context (which is the second argument) + before any server-side OpenGL objects have been created in that + context. */ + public static synchronized void registerForObjectTracking(GLContext olderContextOrNull, + GLContext newContext) { + if (isObjectTrackingEnabled() || isObjectTrackingDebuggingEnabled()) { + if (olderContextOrNull != null && + newContext != null) { + if (entryFor(olderContextOrNull) != entryFor(newContext)) { + throw new IllegalArgumentException("old and new contexts must be able to share textures and display lists"); + } + } + + // FIXME: downcast to GLContextImpl undesirable + GLContextImpl impl1 = (GLContextImpl) olderContextOrNull; + GLContextImpl impl2 = (GLContextImpl) newContext; + GLObjectTracker tracker = null; + // Don't share object trackers with the primordial share context from Java2D + GLContext j2dShareContext = Java2D.getShareContext(); + if (impl1 != null && impl1 == j2dShareContext) { + impl1 = null; + } + if (impl1 != null) { + tracker = impl1.getObjectTracker(); + assert (tracker != null) + : "registerForObjectTracking was not called properly for the older context"; + } + if (tracker == null) { + tracker = new GLObjectTracker(); + } + // Note that we don't assert that the tracker is non-null for + // impl2 because the way we use this functionality we actually + // overwrite the initially-set object tracker in the new context + impl2.setObjectTracker(tracker); + } + } + //---------------------------------------------------------------------- // Internals only below this point // @@ -199,4 +200,13 @@ public class GLContextShareSet { shareMap.put(context, share); } } + + private static boolean isObjectTrackingEnabled() { + return ((Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) || + isObjectTrackingDebuggingEnabled()); + } + + private static boolean isObjectTrackingDebuggingEnabled() { + return forceTracking; + } } diff --git a/src/classes/com/sun/opengl/impl/Java2D.java b/src/classes/com/sun/opengl/impl/Java2D.java index c3eaa3b7c..cf9e8ff1e 100755 --- a/src/classes/com/sun/opengl/impl/Java2D.java +++ b/src/classes/com/sun/opengl/impl/Java2D.java @@ -40,6 +40,7 @@ package com.sun.opengl.impl; import java.awt.*; +import java.awt.image.*; import java.lang.reflect.*; import java.security.*; @@ -59,6 +60,18 @@ public class Java2D { private static Method getOGLScissorBoxMethod; private static Method getOGLSurfaceIdentifierMethod; + // If FBOs are enabled in the Java2D/OpenGL pipeline, all contexts + // created by JOGL must share textures and display lists with the + // Java2D contexts in order to access the frame buffer object for + // potential rendering, and to simultaneously support sharing of + // textures and display lists with one another. Java2D has the + // notion of a single shared context with which all other contexts + // (on the same display device?) share textures and display lists; + // this is an approximation to that notion which will be refined + // later. + private static VolatileImage j2dFBOVolatileImage; // just a dummy image + private static GLContext j2dFBOShareContext; + static { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { @@ -174,6 +187,12 @@ public class Java2D { } } + /** Returns the OpenGL viewport associated with the given Graphics + object, assuming that the Graphics object is associated with a + component of the specified width and height. The user should + call glViewport() with the returned rectangle's bounds in order + to get correct rendering results. Should only be called from the + Queue Flusher Thread. */ public static Rectangle getOGLViewport(Graphics g, int componentWidth, int componentHeight) { @@ -192,6 +211,12 @@ public class Java2D { } } + /** Returns the OpenGL scissor region associated with the given + Graphics object, taking into account all clipping regions, etc. + To avoid destroying Java2D's previous rendering results, this + method should be called and the resulting rectangle's bounds + passed to a call to glScissor(). Should only be called from the + Queue Flusher Thread. */ public static Rectangle getOGLScissorBox(Graphics g) { if (!isOGLPipelineActive()) { throw new GLException("Java2D OpenGL pipeline not active (or necessary support not present)"); @@ -206,7 +231,12 @@ public class Java2D { } } - + /** Returns an opaque "surface identifier" associated with the given + Graphics object. If this changes from invocation to invocation, + the underlying OpenGL drawable for the Graphics object has + changed and a new external GLDrawable and GLContext should be + created (and the old ones destroyed). Should only be called from + the Queue Flusher Thread.*/ public static Object getOGLSurfaceIdentifier(Graphics g) { if (!isOGLPipelineActive()) { throw new GLException("Java2D OpenGL pipeline not active (or necessary support not present)"); @@ -220,4 +250,76 @@ public class Java2D { throw (InternalError) new InternalError().initCause(e); } } + + /** Returns either the given GLContext or a substitute one with + which clients should share textures and display lists. Needed + when the Java2D/OpenGL pipeline is active and FBOs are being + used for rendering. FIXME: may need to alter the API in the + future to indicate which GraphicsDevice the source context is + associated with. */ + public static GLContext filterShareContext(GLContext shareContext) { + initFBOShareContext(); + if (j2dFBOShareContext != null) { + return j2dFBOShareContext; + } + return shareContext; + } + + /** Returns the GLContext associated with the Java2D "share + context", with which all contexts created by JOGL must share + textures and display lists when the FBO option is enabled for + the Java2D/OpenGL pipeline. */ + public static GLContext getShareContext() { + initFBOShareContext(); + return j2dFBOShareContext; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static void initFBOShareContext() { + // Note 1: this must not be done in the static initalizer due to + // deadlock problems. + + // Note 2: the first execution of this method must not be from the + // Java2D Queue Flusher Thread. + + // Note that at this point it's basically impossible that we're + // executing on the Queue Flusher Thread since all calls (even + // from end users) should be going through this interface and + // we're still in the static initializer + if (isOGLPipelineActive() && + isFBOEnabled() && + j2dFBOVolatileImage == null) { + // Create a compatible VolatileImage (FIXME: may need one per + // display device, and may need to create them lazily, which may + // cause problems) and create a JOGL GLContext to wrap its + // GLContext. + // + // FIXME: this technique is not really adequate. The + // VolatileImage may be punted at any time, meaning that its + // OpenGL context will be destroyed and any shares of + // server-side objects with it will be gone. This context is + // currently the "pinch point" through which all of the shares + // with the set of contexts created by JOGL go through. Java2D + // has the notion of its own share context with which all of the + // contexts it creates internally share server-side objects; + // what is really needed is another API in OGLUtilities to + // invoke a Runnable with that share context current rather than + // the context associated with a particular Graphics object, so + // that JOGL can grab a handle to that persistent context. + j2dFBOVolatileImage = + GraphicsEnvironment. + getLocalGraphicsEnvironment(). + getDefaultScreenDevice(). + getDefaultConfiguration(). + createCompatibleVolatileImage(2, 2); + invokeWithOGLContextCurrent(j2dFBOVolatileImage.getGraphics(), new Runnable() { + public void run() { + j2dFBOShareContext = GLDrawableFactory.getFactory().createExternalGLContext(); + } + }); + } + } } diff --git a/src/classes/com/sun/opengl/impl/windows/WindowsExternalGLContext.java b/src/classes/com/sun/opengl/impl/windows/WindowsExternalGLContext.java index 694aa6b0d..a12f156f5 100755 --- a/src/classes/com/sun/opengl/impl/windows/WindowsExternalGLContext.java +++ b/src/classes/com/sun/opengl/impl/windows/WindowsExternalGLContext.java @@ -51,6 +51,9 @@ public class WindowsExternalGLContext extends WindowsGLContext { public WindowsExternalGLContext() { super(null, null); hglrc = WGL.wglGetCurrentContext(); + if (DEBUG) { + System.err.println(getThreadName() + ": !!! Created external OpenGL context " + toHexString(hglrc) + " for " + this); + } GLContextShareSet.contextCreated(this); resetGLFunctionAvailability(); } diff --git a/src/classes/javax/media/opengl/GLJPanel.java b/src/classes/javax/media/opengl/GLJPanel.java index ad5a7f769..1dd204239 100644 --- a/src/classes/javax/media/opengl/GLJPanel.java +++ b/src/classes/javax/media/opengl/GLJPanel.java @@ -80,6 +80,10 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { private static final boolean DEBUG = Debug.debug("GLJPanel"); private static final boolean VERBOSE = Debug.verbose(); + // FIXME: remove these once debugging is done + private static final boolean HACK1 = Debug.debug("GLJPanel.hack1"); + private static final boolean HACK2 = Debug.debug("GLJPanel.hack2"); + private GLDrawableHelper drawableHelper = new GLDrawableHelper(); private volatile boolean isInitialized; private volatile boolean shouldInitialize = false; @@ -159,11 +163,23 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { // properly render into Java2D back buffer private int[] drawBuffer = new int[1]; private int[] readBuffer = new int[1]; + // This is required when the FBO option of the Java2D / OpenGL + // pipeline is active + private int[] frameBuffer = new int[1]; // These are always set to (0, 0) except when the Java2D / OpenGL // pipeline is active private int viewportX; private int viewportY; + static { + // Force eager initialization of part of the Java2D class since + // otherwise it's likely it will try to be initialized while on + // the Queue Flusher Thread, which is not allowed + if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) { + Java2D.getShareContext(); + } + } + /** Creates a new GLJPanel component with a default set of OpenGL capabilities and using the default OpenGL capabilities selection mechanism. */ @@ -220,6 +236,9 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { private void captureJ2DState(GL gl) { gl.glGetIntegerv(GL.GL_DRAW_BUFFER, drawBuffer, 0); gl.glGetIntegerv(GL.GL_READ_BUFFER, readBuffer, 0); + if (Java2D.isFBOEnabled()) { + gl.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING_EXT, frameBuffer, 0); + } } private boolean preGL(Graphics g) { @@ -249,6 +268,22 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { gl.glDrawBuffer(drawBuffer[0]); gl.glReadBuffer(readBuffer[0]); + + // If the FBO option is active, bind to the FBO from the Java2D + // context. + // Note that all of the plumbing in the context sharing stuff will + // allow us to bind to this object since it's in our namespace. + if (Java2D.isFBOEnabled()) { + if (DEBUG && VERBOSE) { + System.err.println("Binding to framebuffer object " + frameBuffer[0]); + } + + gl.glBindTexture(GL.GL_TEXTURE_2D, 0); + gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, frameBuffer[0]); + // FIXME: do we need to do anything else? Bind Texture2D state + // or something else? + } + return true; } @@ -346,6 +381,28 @@ public class GLJPanel extends JPanel implements GLAutoDrawable { joglDrawable = GLDrawableFactory.getFactory().createExternalGLDrawable(); joglContext = joglDrawable.createContext(shareWith); } + + // FIXME: remove these once debugging is done + if (HACK1) { + // Skip all GLContext manipulation This fixes the + // display of the icons and text (done with Java2D) + // in the JGears demo when FBO is active + return; + } + if (HACK2) { + // Do a little GLContext manipulation but skip all + // FBO-related manipulation and other stuff (as well + // as the user rendering). + + // Note that the icons and text in the JGears demo + // disappear when this flag is used, so clearly any + // OpenGL context manipulation is messing up the + // Java2D context state when FBO is active. + joglContext.makeCurrent(); + joglContext.release(); + return; + } + drawableHelper.invokeGL(joglDrawable, joglContext, displayAction, initAction); } } finally { -- cgit v1.2.3