diff options
Diffstat (limited to 'src/net/java/games')
13 files changed, 563 insertions, 156 deletions
diff --git a/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java b/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java index 8d5ecabc9..01e15f1fa 100644 --- a/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java +++ b/src/net/java/games/jogl/DefaultGLCapabilitiesChooser.java @@ -39,6 +39,8 @@ package net.java.games.jogl; +import net.java.games.jogl.impl.Debug; + /** <P> The default implementation of the {@link GLCapabilitiesChooser} interface, which provides consistent visual selection behavior across platforms. The precise algorithm is @@ -78,7 +80,7 @@ package net.java.games.jogl; */ public class DefaultGLCapabilitiesChooser implements GLCapabilitiesChooser { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("DefaultGLCapabilitiesChooser"); public int chooseCapabilities(GLCapabilities desired, GLCapabilities[] available, diff --git a/src/net/java/games/jogl/GLJPanel.java b/src/net/java/games/jogl/GLJPanel.java index 9ebec3079..b22f91b58 100644 --- a/src/net/java/games/jogl/GLJPanel.java +++ b/src/net/java/games/jogl/GLJPanel.java @@ -40,6 +40,8 @@ package net.java.games.jogl; import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; import java.awt.Rectangle; @@ -57,24 +59,43 @@ import net.java.games.jogl.impl.*; /** A lightweight Swing component which provides OpenGL rendering support. Provided for compatibility with Swing user interfaces when adding a heavyweight doesn't work either because of - Z-ordering or LayoutManager problems. Currently implemented using - offscreen (i.e., non-hardware accelerated) rendering, so - performance will likely be poor. This class can not be + Z-ordering or LayoutManager problems. This component attempts to + use hardware-accelerated rendering via pbuffers and falls back on + to software rendering if problems occur. This class can not be instantiated directly; use {@link GLDrawableFactory} to construct them. */ public final class GLJPanel extends JPanel implements GLDrawable { private GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private GLContext context; - private BufferedImage offscreenImage; - private int awtFormat; - private int glFormat; - private int glType; - private int glComps; + + // Data used for either pbuffers or pixmap-based offscreen surfaces + private GLCapabilities offscreenCaps; + private GLCapabilitiesChooser chooser; + private GLDrawable shareWith; + private BufferedImage offscreenImage; + private int neededOffscreenImageWidth; + private int neededOffscreenImageHeight; private DataBufferByte dbByte; private DataBufferInt dbInt; private Object semaphore = new Object(); - private boolean repaintDone; + private int panelWidth = 0; + private int panelHeight = 0; + private Updater updater; + private int awtFormat; + private int glFormat; + private int glType; + + // Implementation using pbuffers + private static boolean hardwareAccelerationDisabled = true; + private boolean pbufferInitializationCompleted; + private GLPbuffer pbuffer; + private int pbufferWidth = 256; + private int pbufferHeight = 256; + private GLCanvas heavyweight; + private Frame toplevel; + + // Implementation using software rendering + private GLContext offscreenContext; // For saving/restoring of OpenGL state during ReadPixels private int[] swapbytes = new int[1]; @@ -86,35 +107,76 @@ public final class GLJPanel extends JPanel implements GLDrawable { GLJPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLDrawable shareWith) { super(); + // Works around problems on many vendors' cards; we don't need a // back buffer for the offscreen surface anyway - GLCapabilities myCaps = (GLCapabilities) capabilities.clone(); - myCaps.setDoubleBuffered(false); - context = GLContextFactory.getFactory().createGLContext(null, myCaps, chooser, - GLContextHelper.getContext(shareWith)); + offscreenCaps = (GLCapabilities) capabilities.clone(); + offscreenCaps.setDoubleBuffered(false); + this.chooser = chooser; + this.shareWith = shareWith; + + initialize(); } public void display() { - // Multithreaded redrawing of Swing components is not allowed - try { - synchronized(semaphore) { - repaintDone = false; - repaint(); - while (!repaintDone) { - semaphore.wait(); + if (EventQueue.isDispatchThread()) { + // Can't block this thread + repaint(); + } else { + // Multithreaded redrawing of Swing components is not allowed, + // so do everything on the event dispatch thread + try { + // Wait a reasonable period of time for the repaint to + // complete, so that we don't swamp the AWT event queue thread + // with repaint requests. We used to have an explicit flag to + // detect when the repaint completed; unfortunately, under + // some circumstances, the top-level window can be torn down + // while we're waiting for the repaint to complete, which will + // never happen. It doesn't look like there's enough + // information in the EventQueue to figure out whether there + // are pending events without posting to the queue, which we + // don't want to do during shutdown, and adding a + // HierarchyListener and watching for displayability events + // might be fragile since we don't know exactly how this + // component will be used in users' applications. For these + // reasons we simply wait up to a brief period of time for the + // repaint to complete. + synchronized(semaphore) { + repaint(); + semaphore.wait(100); } + } catch (InterruptedException e) { } - } catch (InterruptedException e) { } } - /** Overridden from JComponent; calls {@link #display}. Should not - be invoked by applications directly. */ + /** Overridden from JComponent; calls {@link + GLEventListener#display}. Should not be invoked by applications + directly. */ public void paintComponent(Graphics g) { - displayAction.setGraphics(g); - context.invokeGL(displayAction, false, initAction); + updater.setGraphics(g); + if (!hardwareAccelerationDisabled) { + if (!pbufferInitializationCompleted) { + try { + heavyweight.display(); + pbuffer.display(); + } catch (GLException e) { + // We consider any exception thrown during updating of the + // heavyweight or pbuffer during the initialization phases + // to be an indication that there was a problem + // instantiating the pbuffer, regardless of whether the + // exception originated in the user's GLEventListener. In + // these cases we immediately back off and use software + // rendering. + disableHardwareRendering(); + } + } else { + pbuffer.display(); + } + } else { + offscreenContext.invokeGL(displayAction, false, initAction); + } synchronized(semaphore) { - repaintDone = true; semaphore.notifyAll(); } } @@ -125,23 +187,57 @@ public final class GLJPanel extends JPanel implements GLDrawable { directly. */ public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); - // NOTE: we don't pay attention to the x and y provided since we - // are blitting into this component directly + + GLContext context = null; + neededOffscreenImageWidth = 0; + neededOffscreenImageHeight = 0; + + if (!hardwareAccelerationDisabled) { + if (width > pbufferWidth || height > pbufferHeight) { + // Must destroy and recreate pbuffer to fit + pbuffer.destroy(); + if (width > pbufferWidth) { + pbufferWidth = getNextPowerOf2(width); + } + if (height > pbufferHeight) { + pbufferHeight = getNextPowerOf2(height); + } + initialize(); + } + GLPbufferImpl pbufferImpl = (GLPbufferImpl) pbuffer; + context = pbufferImpl.getContext(); + // It looks like NVidia's drivers (at least the ones on my + // notebook) are buggy and don't allow a rectangle of less than + // the pbuffer's width to be read...this doesn't really matter + // because it's the Graphics.drawImage() calls that are the + // bottleneck. Should probably make the size of the offscreen + // image be the exact size of the pbuffer to save some work on + // resize operations... + neededOffscreenImageWidth = pbufferWidth; + neededOffscreenImageHeight = height; + } else { + offscreenContext.resizeOffscreenContext(width, height); + context = offscreenContext; + neededOffscreenImageWidth = width; + neededOffscreenImageHeight = height; + } + + if (offscreenImage != null && + (offscreenImage.getWidth() != neededOffscreenImageWidth || + offscreenImage.getHeight() != neededOffscreenImageHeight)) { + offscreenImage.flush(); + offscreenImage = null; + } + + panelWidth = width; + panelHeight = height; final int fx = 0; final int fy = 0; - final int fwidth = width; - final int fheight = height; - context.resizeOffscreenContext(width, height); + context.invokeGL(new Runnable() { public void run() { - getGL().glViewport(fx, fy, fwidth, fheight); - drawableHelper.reshape(GLJPanel.this, fx, fy, fwidth, fheight); - if (offscreenImage != null && - (offscreenImage.getWidth() != context.getOffscreenContextWidth() || - offscreenImage.getHeight() != context.getOffscreenContextHeight())) { - offscreenImage.flush(); - offscreenImage = null; - } + getGL().glViewport(fx, fy, panelWidth, panelHeight); + drawableHelper.reshape(GLJPanel.this, fx, fy, panelWidth, panelHeight); } }, true, initAction); } @@ -155,19 +251,35 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public GL getGL() { - return context.getGL(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getGL(); + } else { + return offscreenContext.getGL(); + } } public void setGL(GL gl) { - context.setGL(gl); + if (!hardwareAccelerationDisabled) { + pbuffer.setGL(gl); + } else { + offscreenContext.setGL(gl); + } } public GLU getGLU() { - return context.getGLU(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getGLU(); + } else { + return offscreenContext.getGLU(); + } } public void setGLU(GLU glu) { - context.setGLU(glu); + if (!hardwareAccelerationDisabled) { + pbuffer.setGLU(glu); + } else { + offscreenContext.setGLU(glu); + } } public void setRenderingThread(Thread currentThreadOrNull) throws GLException { @@ -176,7 +288,7 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public Thread getRenderingThread() { - return context.getRenderingThread(); + return null; } public void setNoAutoRedrawMode(boolean noAutoRedraws) { @@ -187,21 +299,32 @@ public final class GLJPanel extends JPanel implements GLDrawable { } public void setAutoSwapBufferMode(boolean onOrOff) { - context.setAutoSwapBufferMode(onOrOff); + if (!hardwareAccelerationDisabled) { + pbuffer.setAutoSwapBufferMode(onOrOff); + } else { + offscreenContext.setAutoSwapBufferMode(onOrOff); + } } public boolean getAutoSwapBufferMode() { - return context.getAutoSwapBufferMode(); + if (!hardwareAccelerationDisabled) { + return pbuffer.getAutoSwapBufferMode(); + } else { + return offscreenContext.getAutoSwapBufferMode(); + } } public void swapBuffers() { - context.invokeGL(swapBuffersAction, false, initAction); + if (!hardwareAccelerationDisabled) { + pbuffer.swapBuffers(); + } else { + offscreenContext.invokeGL(swapBuffersAction, false, initAction); + } } public boolean canCreateOffscreenDrawable() { - // For now let's say no; maybe we can reimplement this class in - // terms of pbuffers (though not all vendors support them, and - // they seem to require an onscreen context) + // For now let's say no, although we could using the heavyweight + // if hardware acceleration is still enabled return false; } @@ -212,119 +335,267 @@ public final class GLJPanel extends JPanel implements GLDrawable { } GLContext getContext() { - return context; + if (!hardwareAccelerationDisabled) { + return ((GLPbufferImpl) pbuffer).getContext(); + } else { + return offscreenContext; + } } //---------------------------------------------------------------------- // Internals only below this point // - class InitAction implements Runnable { - public void run() { - drawableHelper.init(GLJPanel.this); + private void disableHardwareRendering() { + if (Debug.verbose()) { + System.err.println("GLJPanel: Falling back on software rendering due to pbuffer problems"); } + hardwareAccelerationDisabled = true; + pbufferInitializationCompleted = false; + EventQueue.invokeLater(new Runnable() { + public void run() { + toplevel.setVisible(false); + // Should dispose of this -- not sure about stability on + // various cards -- should test (FIXME) + // toplevel.dispose(); + } + }); + initialize(); } - private InitAction initAction = new InitAction(); - - class DisplayAction implements Runnable { + + private void initialize() { + // Initialize either the hardware-accelerated rendering path or + // the lightweight rendering path + if (!hardwareAccelerationDisabled) { + boolean firstTime = false; + if (heavyweight == null) { + // Make the heavyweight share with the "shareWith" parameter. + // The pbuffer shares textures and display lists with the + // heavyweight, so by transitivity the pbuffer will share with + // it as well. + heavyweight = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities(), shareWith); + firstTime = true; + } + if (heavyweight.canCreateOffscreenDrawable()) { + if (firstTime) { + toplevel = new Frame(); + toplevel.setUndecorated(true); + } + pbuffer = heavyweight.createOffscreenDrawable(offscreenCaps, pbufferWidth, pbufferHeight); + updater = new Updater(); + pbuffer.addGLEventListener(updater); + pbufferInitializationCompleted = false; + if (firstTime) { + toplevel.add(heavyweight); + toplevel.setSize(0, 0); + } + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + toplevel.setVisible(true); + } catch (GLException e) { + disableHardwareRendering(); + } + } + }); + return; + } else { + // If the heavyweight reports that it can't create an + // offscreen drawable (pbuffer), don't try again the next + // time, and fall through to the software rendering path + hardwareAccelerationDisabled = true; + } + } + + // Create an offscreen context instead + offscreenContext = GLContextFactory.getFactory().createGLContext(null, offscreenCaps, chooser, + GLContextHelper.getContext(shareWith)); + offscreenContext.resizeOffscreenContext(panelWidth, panelHeight); + updater = new Updater(); + if (panelWidth > 0 && panelHeight > 0) { + offscreenContext.invokeGL(new Runnable() { + public void run() { + getGL().glViewport(0, 0, panelWidth, panelHeight); + drawableHelper.reshape(GLJPanel.this, 0, 0, panelWidth, panelHeight); + } + }, true, initAction); + } + } + + class Updater implements GLEventListener { private Graphics g; public void setGraphics(Graphics g) { this.g = g; } - public void run() { + public void init(GLDrawable drawable) { + if (!hardwareAccelerationDisabled) { + pbufferInitializationCompleted = true; + EventQueue.invokeLater(new Runnable() { + public void run() { + toplevel.setVisible(false); + } + }); + } + drawableHelper.init(GLJPanel.this); + } + + public void display(GLDrawable drawable) { drawableHelper.display(GLJPanel.this); + // Must now copy pixels from offscreen context into surface if (offscreenImage == null) { - int awtFormat = context.getOffscreenContextBufferedImageType(); - offscreenImage = new BufferedImage(context.getOffscreenContextWidth(), context.getOffscreenContextHeight(), awtFormat); - switch (awtFormat) { - case BufferedImage.TYPE_3BYTE_BGR: - glFormat = GL.GL_BGR; - glType = GL.GL_UNSIGNED_BYTE; - glComps = 3; - dbByte = (DataBufferByte) offscreenImage.getRaster().getDataBuffer(); - break; - - case BufferedImage.TYPE_INT_RGB: - glFormat = GL.GL_BGRA; - glType = GL.GL_UNSIGNED_BYTE; - glComps = 4; - dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); - break; - - case BufferedImage.TYPE_INT_ARGB: - glFormat = GL.GL_BGRA; - glType = context.getOffscreenContextPixelDataType(); - glComps = 4; - dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); - break; - - default: - // FIXME: Support more off-screen image types (current - // offscreen context implementations don't use others, and - // some of the OpenGL formats aren't supported in the 1.1 - // headers, which we're currently using) - throw new GLException("Unsupported offscreen image type " + awtFormat); + if (panelWidth > 0 && panelHeight > 0) { + // It looks like NVidia's drivers (at least the ones on my + // notebook) are buggy and don't allow a sub-rectangle to be + // read from a pbuffer...this doesn't really matter because + // it's the Graphics.drawImage() calls that are the + // bottleneck + + int awtFormat = 0; + if (!hardwareAccelerationDisabled) { + // Should be more flexible in these BufferedImage formats; + // perhaps see what the preferred image types are on the + // given platform + if (offscreenCaps.getAlphaBits() > 0) { + awtFormat = BufferedImage.TYPE_INT_ARGB; + } else { + awtFormat = BufferedImage.TYPE_3BYTE_BGR; + } + } else { + awtFormat = offscreenContext.getOffscreenContextBufferedImageType(); + } + + offscreenImage = new BufferedImage(neededOffscreenImageWidth, + neededOffscreenImageHeight, + awtFormat); + switch (awtFormat) { + case BufferedImage.TYPE_3BYTE_BGR: + glFormat = GL.GL_BGR; + glType = GL.GL_UNSIGNED_BYTE; + dbByte = (DataBufferByte) offscreenImage.getRaster().getDataBuffer(); + break; + + case BufferedImage.TYPE_INT_RGB: + glFormat = GL.GL_BGRA; + glType = GL.GL_UNSIGNED_BYTE; + dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); + break; + + case BufferedImage.TYPE_INT_ARGB: + glFormat = GL.GL_BGRA; + glType = (hardwareAccelerationDisabled + ? offscreenContext.getOffscreenContextPixelDataType() + : GL.GL_UNSIGNED_BYTE); + dbInt = (DataBufferInt) offscreenImage.getRaster().getDataBuffer(); + break; + + default: + // FIXME: Support more off-screen image types (current + // offscreen context implementations don't use others, and + // some of the OpenGL formats aren't supported in the 1.1 + // headers, which we're currently using) + throw new GLException("Unsupported offscreen image type " + awtFormat); + } } } - GL gl = getGL(); - // Save current modes - gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes); - gl.glGetIntegerv(GL.GL_PACK_LSB_FIRST, lsbfirst); - gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength); - gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows); - gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels); - gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment); - - // Little endian machines (DEC Alpha, Intel X86, PPC (in LSB - // mode)... for example) could benefit from setting - // GL_PACK_LSB_FIRST to GL_TRUE instead of GL_FALSE, but this - // would require changing the generated bitmaps too. - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE); - gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, GL.GL_TRUE); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, offscreenImage.getWidth()); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - - // Actually read the pixels. - gl.glReadBuffer(context.getOffscreenContextReadBuffer()); - if (dbByte != null) { - gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbByte.getData()); - } else if (dbInt != null) { - gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbInt.getData()); - } + if (offscreenImage != null) { + GL gl = getGL(); + // Save current modes + gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes); + gl.glGetIntegerv(GL.GL_PACK_LSB_FIRST, lsbfirst); + gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength); + gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows); + gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels); + gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment); + + // Little endian machines (DEC Alpha, Intel X86, PPC (in LSB + // mode)... for example) could benefit from setting + // GL_PACK_LSB_FIRST to GL_TRUE instead of GL_FALSE, but this + // would require changing the generated bitmaps too. + gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE); + gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, GL.GL_TRUE); + gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, offscreenImage.getWidth()); + gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + + // Actually read the pixels. + gl.glReadBuffer(GL.GL_FRONT); + if (dbByte != null) { + gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbByte.getData()); + } else if (dbInt != null) { + gl.glReadPixels(0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), glFormat, glType, dbInt.getData()); + } - // Restore saved modes. - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, lsbfirst[0]); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]); + // Restore saved modes. + gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]); + gl.glPixelStorei(GL.GL_PACK_LSB_FIRST, lsbfirst[0]); + gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]); + gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]); + gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]); + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]); - gl.glFlush(); - gl.glFinish(); - - if (context.offscreenImageNeedsVerticalFlip()) { - g.drawImage(offscreenImage, - 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), - 0, offscreenImage.getHeight(), offscreenImage.getWidth(), 0, - GLJPanel.this); - } else { - g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), GLJPanel.this); + if (!hardwareAccelerationDisabled || + offscreenContext.offscreenImageNeedsVerticalFlip()) { + // This performs reasonably well; the snippet below does not. + // Should figure out if we need to set the image scaling + // preference to FAST since it doesn't require subsampling + // of pixels -- FIXME + for (int i = 0; i < panelHeight - 1; i++) { + g.drawImage(offscreenImage, + 0, i, panelWidth, i+1, + 0, panelHeight - i - 2, panelWidth, panelHeight - i - 1, + GLJPanel.this); + } + } else { + g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), GLJPanel.this); + } } } + + public void reshape(GLDrawable drawable, int x, int y, int width, int height) { + // This is handled above and dispatched directly to the appropriate context + } + + public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) { + } + } + + class InitAction implements Runnable { + public void run() { + updater.init(GLJPanel.this); + } + } + private InitAction initAction = new InitAction(); + + class DisplayAction implements Runnable { + public void run() { + updater.display(GLJPanel.this); + } } private DisplayAction displayAction = new DisplayAction(); + // This one is used exclusively in the non-hardware-accelerated case class SwapBuffersAction implements Runnable { public void run() { - context.swapBuffers(); + offscreenContext.swapBuffers(); } } private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); + + private int getNextPowerOf2(int number) { + if (((number-1) & number) == 0) { + //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0 + return number; + } + int power = 0; + while (number > 0) { + number = number>>1; + power++; + } + return (1<<power); + } } diff --git a/src/net/java/games/jogl/GLPbuffer.java b/src/net/java/games/jogl/GLPbuffer.java index d41ca1bd6..fe2c19974 100644 --- a/src/net/java/games/jogl/GLPbuffer.java +++ b/src/net/java/games/jogl/GLPbuffer.java @@ -63,4 +63,9 @@ public interface GLPbuffer extends GLDrawable { /** Queries initialization status of this pBuffer. */ public boolean isInitialized(); + + /** Destroys the native resources associated with this pbuffer. It + is not valid to call display() or any other routines on this + pbuffer after it has been destroyed. */ + public void destroy(); } diff --git a/src/net/java/games/jogl/impl/Debug.java b/src/net/java/games/jogl/impl/Debug.java new file mode 100755 index 000000000..ad0092a6f --- /dev/null +++ b/src/net/java/games/jogl/impl/Debug.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package net.java.games.jogl.impl; + +import java.security.*; + +/** Helper routines for logging and debugging. */ + +public class Debug { + // Some common properties + private static boolean verbose; + private static boolean debugAll; + + static { + verbose = isPropertyDefined("jogl.verbose"); + debugAll = isPropertyDefined("jogl.debug"); + } + + public static boolean getBooleanProperty(final String property) { + Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + boolean val = Boolean.getBoolean(property); + return (val ? Boolean.TRUE : Boolean.FALSE); + } + }); + return b.booleanValue(); + } + + public static boolean isPropertyDefined(final String property) { + Boolean b = (Boolean) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + String val = System.getProperty(property); + return (val != null ? Boolean.TRUE : Boolean.FALSE); + } + }); + return b.booleanValue(); + } + + public static boolean verbose() { + return verbose; + } + + public static boolean debugAll() { + return debugAll; + } + + public static boolean debug(String subcomponent) { + return debugAll() || isPropertyDefined("jogl.debug." + subcomponent); + } +} diff --git a/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java b/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java index bceb66f9b..01a70e4b7 100644 --- a/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java +++ b/src/net/java/games/jogl/impl/FunctionAvailabilityCache.java @@ -52,7 +52,7 @@ import java.lang.reflect.*; * and display. */ public final class FunctionAvailabilityCache { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("FunctionAvailabilityCache"); FunctionAvailabilityCache(GLContext context) { diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java index cc5d465d6..aab3f9dd0 100644 --- a/src/net/java/games/jogl/impl/GLContext.java +++ b/src/net/java/games/jogl/impl/GLContext.java @@ -44,7 +44,7 @@ import net.java.games.jogl.*; import net.java.games.gluegen.runtime.*; public abstract class GLContext { - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = Debug.debug("GLContext"); static { NativeLibLoader.load(); diff --git a/src/net/java/games/jogl/impl/GLPbufferImpl.java b/src/net/java/games/jogl/impl/GLPbufferImpl.java index 2fb927c92..03d161fdd 100644 --- a/src/net/java/games/jogl/impl/GLPbufferImpl.java +++ b/src/net/java/games/jogl/impl/GLPbufferImpl.java @@ -196,6 +196,10 @@ public class GLPbufferImpl implements GLPbuffer { return isInitialized; } + public void destroy() { + context.destroy(); + } + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java index 2ffd09e6a..eb7d8f09d 100755 --- a/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java +++ b/src/net/java/games/jogl/impl/SingleThreadedWorkaround.java @@ -52,7 +52,6 @@ public class SingleThreadedWorkaround { // If the user specified the workaround's system property (either // true or false), don't let the automatic detection have any effect private static boolean systemPropertySpecified = false; - private static boolean verbose = false; static { AccessController.doPrivileged(new PrivilegedAction() { @@ -66,7 +65,6 @@ public class SingleThreadedWorkaround { systemPropertySpecified = true; singleThreadedWorkaround = Boolean.valueOf(workaround).booleanValue(); } - verbose = (System.getProperty("jogl.verbose") != null); printWorkaroundNotice(); return null; } @@ -85,7 +83,7 @@ public class SingleThreadedWorkaround { } private static void printWorkaroundNotice() { - if (singleThreadedWorkaround && verbose) { + if (singleThreadedWorkaround && Debug.verbose()) { System.err.println("Using single-threaded workaround of dispatching display() on event thread"); } } diff --git a/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java b/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java index 6ac854090..8e65ce1f3 100644 --- a/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/macosx/MacOSXPbufferGLContext.java @@ -4,8 +4,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class MacOSXPbufferGLContext extends MacOSXGLContext { - - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("MacOSXPbufferGLContext"); protected int initWidth; protected int initHeight; @@ -131,6 +130,10 @@ public class MacOSXPbufferGLContext extends MacOSXGLContext { return false; } + protected void destroyImpl() throws GLException { + destroyPBuffer(); + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? } diff --git a/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java index 9e871caba..5743253a0 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsOffscreenGLContext.java @@ -172,7 +172,8 @@ public class WindowsOffscreenGLContext extends WindowsGLContext { } hbitmap = WGL.CreateDIBSection(hdc, info, WGL.DIB_RGB_COLORS, 0, 0, 0); if (hbitmap == 0) { - throw new GLException("Error creating offscreen bitmap"); + throw new GLException("Error creating offscreen bitmap of width " + width + + ", height " + height); } if ((origbitmap = WGL.SelectObject(hdc, hbitmap)) == 0) { throw new GLException("Error selecting bitmap into new device context"); diff --git a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java index d3795905f..9d803d58a 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java @@ -43,7 +43,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class WindowsPbufferGLContext extends WindowsGLContext { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("WindowsPbufferGLContext"); private int initWidth; private int initHeight; @@ -308,10 +308,16 @@ public class WindowsPbufferGLContext extends WindowsGLContext { if (buffer == 0) { // pbuffer not instantiated yet + if (DEBUG) { + System.err.println("pbuffer not instantiated yet"); + } return false; } boolean res = super.makeCurrent(initAction); + if (DEBUG) { + System.err.println("super.makeCurrent() = " + res + ", created = " + created); + } if (created) { // Initialize render-to-texture support if requested rtt = capabilities.getOffscreenRenderToTexture(); @@ -396,6 +402,22 @@ public class WindowsPbufferGLContext extends WindowsGLContext { } } + protected void destroyImpl() throws GLException { + if (hglrc != 0) { + super.destroyImpl(); + // Must release DC and pbuffer + GL gl = getGL(); + if (gl.wglReleasePbufferDCARB(buffer, hdc) == 0) { + throw new GLException("Error releasing pbuffer device context: error code " + WGL.GetLastError()); + } + hdc = 0; + if (!gl.wglDestroyPbufferARB(buffer)) { + throw new GLException("Error destroying pbuffer: error code " + WGL.GetLastError()); + } + buffer = 0; + } + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? // For now, just grab the pixels for the render-to-texture support. diff --git a/src/net/java/games/jogl/impl/x11/X11GLContext.java b/src/net/java/games/jogl/impl/x11/X11GLContext.java index efcd9a101..1f8023e66 100644 --- a/src/net/java/games/jogl/impl/x11/X11GLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11GLContext.java @@ -187,6 +187,7 @@ public abstract class X11GLContext extends GLContext { } protected void destroyImpl() throws GLException { + lockAWT(); if (context != 0) { GLX.glXDestroyContext(mostRecentDisplay, context); if (DEBUG) { @@ -194,6 +195,7 @@ public abstract class X11GLContext extends GLContext { } context = 0; } + unlockAWT(); } public abstract void swapBuffers() throws GLException; @@ -381,4 +383,14 @@ public abstract class X11GLContext extends GLContext { protected long getContext() { return context; } + + // These synchronization primitives prevent the AWT from making + // requests from the X server asynchronously to this code. + protected void lockAWT() { + getJAWT().Lock(); + } + + protected void unlockAWT() { + getJAWT().Unlock(); + } } diff --git a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java index a253b9615..5b6df1843 100644 --- a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java @@ -43,7 +43,7 @@ import net.java.games.jogl.*; import net.java.games.jogl.impl.*; public class X11PbufferGLContext extends X11GLContext { - private static final boolean DEBUG = false; + private static final boolean DEBUG = Debug.debug("X11PbufferGLContext"); private int initWidth; private int initHeight; @@ -315,6 +315,19 @@ public class X11PbufferGLContext extends X11GLContext { } } + protected void destroyImpl() throws GLException { + lockAWT(); + try { + if (context != 0) { + super.destroyImpl(); + GLX.glXDestroyPbuffer(display, buffer); + buffer = 0; + } + } finally { + unlockAWT(); + } + } + public void swapBuffers() throws GLException { // FIXME: do we need to do anything if the pbuffer is double-buffered? } @@ -326,15 +339,4 @@ public class X11PbufferGLContext extends X11GLContext { } return tmp[0]; } - - // These synchronization primitives, which prevent the AWT from - // making requests from the X server asynchronously to this code, - // are required for pbuffers to work properly on X11. - private void lockAWT() { - getJAWT().Lock(); - } - - private void unlockAWT() { - getJAWT().Unlock(); - } } |