diff options
author | Kenneth Russel <[email protected]> | 2005-02-03 23:25:53 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2005-02-03 23:25:53 +0000 |
commit | 8da5bfa2d72282bfdcd2889a70a93a4072221a1c (patch) | |
tree | 13b9ecce060f1ba23ba83e7f502fa928a4c76848 /src/net/java/games | |
parent | dfbee835b5a50b1c6219bd53e15823ea83d73dd6 (diff) |
Fixed Issue 44: Add pbuffer-based GLJPanel implementation
Fixed Issue 61: Make debug variables load from system properties
Added a hardware acceleration path for GLJPanel implemented using the
GLPbuffer interfaces. If instantiation of the pbuffer fails, the
GLJPanel implementation attempts to catch the resulting exception and
fall back on the existing software rendering path.
The new code has been successfully tested on Windows. This checkin
attempts to make the needed changes to the pbuffer implementation on
other platforms, but the GLJPanel continues to use software rendering
until all platforms can be tested.
Additionally, references to static final DEBUG flags have been made
settable via system properties. "jogl.verbose" enables certain
printing statements. "jogl.debug" enables all debugging printing.
"jogl.debug.GLContext" enables the debug printing inside the GLContext
class. The system property names are chosen by convention and are not
enforced.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@209 232f8b59-042b-4e1e-8c03-345bb8c30851
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(); - } } |