From 8da5bfa2d72282bfdcd2889a70a93a4072221a1c Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Thu, 3 Feb 2005 23:25:53 +0000 Subject: 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 --- .../games/jogl/DefaultGLCapabilitiesChooser.java | 4 +- src/net/java/games/jogl/GLJPanel.java | 539 ++++++++++++++++----- src/net/java/games/jogl/GLPbuffer.java | 5 + src/net/java/games/jogl/impl/Debug.java | 87 ++++ .../games/jogl/impl/FunctionAvailabilityCache.java | 2 +- src/net/java/games/jogl/impl/GLContext.java | 2 +- src/net/java/games/jogl/impl/GLPbufferImpl.java | 4 + .../games/jogl/impl/SingleThreadedWorkaround.java | 4 +- .../jogl/impl/macosx/MacOSXPbufferGLContext.java | 7 +- .../impl/windows/WindowsOffscreenGLContext.java | 3 +- .../jogl/impl/windows/WindowsPbufferGLContext.java | 24 +- src/net/java/games/jogl/impl/x11/X11GLContext.java | 12 + .../games/jogl/impl/x11/X11PbufferGLContext.java | 26 +- 13 files changed, 563 insertions(+), 156 deletions(-) create mode 100755 src/net/java/games/jogl/impl/Debug.java (limited to 'src/net/java/games') 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; + /**

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<