diff options
author | Sven Gothel <[email protected]> | 2012-12-16 07:47:04 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-12-16 07:47:04 +0100 |
commit | e92823cddc54b0f4fa71e234061a21de6ee5248c (patch) | |
tree | eaee49829bbaa1dd2fe4e073f00eb8e20caeb81d /src/jogl | |
parent | 74c254d42bb2389dc9bf257898053700c8c72568 (diff) |
GLJPanel: Impl. is GLProfile agnostic; Use GLDrawableFactory.createOffscreenDrawable(..) for common OffscreenBackend (dropping pbuffer/software); Flip FBO w/ GLSL texture renderer.
- Implementation is GLProfile agnostic
- Shall work on ES2, GL2, .. etc
- Use GLDrawableFactory.createOffscreenDrawable(..) for common OffscreenBackend (dropping pbuffer/software)
- Leave offscreen selection to common factory code, favoring FBO
- Flip FBO w/ GLSL texture renderer
- Faster on low CPU machines
- Enabled if GL2ES2 and FBO offscreen
Diffstat (limited to 'src/jogl')
6 files changed, 463 insertions, 427 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java index 0320c63ae..4ba4def9a 100644 --- a/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java +++ b/src/jogl/classes/com/jogamp/opengl/swt/GLCanvas.java @@ -207,7 +207,7 @@ public class GLCanvas extends Canvas implements GLAutoDrawable { // so we can continue with the destruction. try { if( !GLCanvas.this.isDisposed() ) { - helper.disposeGL(GLCanvas.this, context); + helper.disposeGL(GLCanvas.this, context, true); } else { context.destroy(); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java index a40cdcf88..e5f39e5de 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLCanvas.java @@ -915,7 +915,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable, WindowClosing // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.disposeGL(GLCanvas.this, context); + helper.disposeGL(GLCanvas.this, context, true); } catch (GLException gle) { gle.printStackTrace(); } diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index a9d4989dc..23dedaa66 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -49,10 +49,8 @@ import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.beans.Beans; -import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.List; @@ -73,7 +71,7 @@ import javax.media.opengl.GLDrawable; import javax.media.opengl.GLDrawableFactory; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLException; -import javax.media.opengl.GLPbuffer; +import javax.media.opengl.GLFBODrawable; import javax.media.opengl.GLProfile; import javax.media.opengl.GLRunnable; import javax.media.opengl.Threading; @@ -86,10 +84,11 @@ import jogamp.opengl.GLDrawableHelper; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.awt.Java2D; import jogamp.opengl.awt.Java2DGLContext; +import jogamp.opengl.util.glsl.GLSLTextureRaster; import com.jogamp.nativewindow.awt.AWTWindowClosingProtocol; import com.jogamp.opengl.FBObject; -import com.jogamp.opengl.util.GLBuffers; +import com.jogamp.opengl.util.GLPixelStorageModes; // FIXME: Subclasses need to call resetGLFunctionAvailability() on their // context whenever the displayChanged() function is called on their @@ -122,14 +121,17 @@ import com.jogamp.opengl.util.GLBuffers; * </P> */ -@SuppressWarnings({ "serial", "deprecation" }) +@SuppressWarnings("serial") public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosingProtocol { private static final boolean DEBUG = Debug.debug("GLJPanel"); private GLDrawableHelper helper = new GLDrawableHelper(); private volatile boolean isInitialized; + // // Data used for either pbuffers or pixmap-based offscreen surfaces + // + /** Single buffered offscreen caps */ private GLCapabilitiesImmutable offscreenCaps; private GLProfile glProfile; private GLDrawableFactoryImpl factory; @@ -150,16 +152,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Used by all backends either directly or indirectly to hook up callbacks private Updater updater = new Updater(); - // Turns off the pbuffer-based backend (used by default, unless the - // Java 2D / OpenGL pipeline is in use) - private static boolean hardwareAccelerationDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nohw", true); - - // Turns off the fallback to software-based rendering from - // pbuffer-based rendering - private static boolean softwareRenderingDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nosw", true); - // Indicates whether the Java 2D OpenGL pipeline is enabled private boolean oglPipelineEnabled = Java2D.isOGLPipelineActive() && @@ -354,7 +346,8 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // re-creating it -- tricky to do properly while the context is // current if (handleReshape) { - handleReshape(); + handleReshape = false; + sendReshape = handleReshape(); } updater.setGraphics(g); @@ -395,8 +388,10 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing called on all registered {@link GLEventListener}s. Subclasses which override this method must call super.reshape() in their reshape() method in order to function properly. <P> - - <DL><DD><CODE>reshape</CODE> in class <CODE>java.awt.Component</CODE></DD></DL> */ + * + * {@inheritDoc} + */ + @SuppressWarnings("deprecation") @Override public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); @@ -642,13 +637,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // private void createAndInitializeBackend() { - if (panelWidth == 0 || - panelHeight == 0) { + if ( 0 >= panelWidth || 0 >= panelHeight ) { // See whether we have a non-zero size yet and can go ahead with // initialization - if (reshapeWidth == 0 || - reshapeHeight == 0) { - return; + if (0 >= reshapeWidth || 0 >= reshapeHeight ) { + return; } // Pull down reshapeWidth and reshapeHeight into panelWidth and @@ -658,31 +651,18 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing panelHeight = reshapeHeight; } - do { - if (backend == null) { + if ( null == backend ) { if (oglPipelineEnabled) { - backend = new J2DOGLBackend(); + backend = new J2DOGLBackend(); } else { - backend = new SoftwareBackend(); - /** - if (!hardwareAccelerationDisabled && - factory.canCreateGLPbuffer(null)) { - backend = new PbufferBackend(); - } else { - if (softwareRenderingDisabled) { - throw new GLException("Fallback to software rendering disabled by user"); - } - backend = new SoftwareBackend(); - } */ + backend = new OffscreenBackend(); } - } + isInitialized = false; + } - if (!isInitialized) { + if (!isInitialized) { backend.initialize(); - } - // The backend might set itself to null, indicating it punted to - // a different implementation -- try again - } while (backend == null); + } awtWindowClosingProtocol.addClosingListenerOneShot(); } @@ -697,7 +677,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing return awtWindowClosingProtocol.setDefaultCloseOperation(op); } - private void handleReshape() { + private boolean handleReshape() { panelWidth = reshapeWidth; panelHeight = reshapeHeight; @@ -706,9 +686,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing panelWidth + "," + panelHeight + ")"); } - sendReshape = true; - backend.handleReshape(); - handleReshape = false; + return backend.handleReshape(); } // This is used as the GLEventListener for the pbuffer-based backend @@ -770,16 +748,17 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing public void run() { if ( null != backend ) { final GLContext _context = backend.getContext(); + final boolean backendDestroy = !backend.isUsingOwnLifecycle(); if( null != _context && _context.isCreated() ) { // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.disposeGL(GLJPanel.this, _context); + helper.disposeGL(GLJPanel.this, _context, !backendDestroy); } catch (GLException gle) { gle.printStackTrace(); } } - if ( !backend.isUsingOwnThreadManagment() ) { + if ( backendDestroy ) { backend.destroy(); backend = null; isInitialized = false; @@ -823,15 +802,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } }; - private int getNextPowerOf2(int number) { - // Workaround for problems where 0 width or height are transiently - // seen during layout - if (number == 0) { - return 2; - } - return GLBuffers.getNextPowerOf2(number); - } - private int getGLInteger(GL gl, int which) { int[] tmp = new int[1]; gl.glGetIntegerv(which, tmp, 0); @@ -851,7 +821,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // / JOGL bridge static interface Backend { // Create, Destroy, .. - public boolean isUsingOwnThreadManagment(); + public boolean isUsingOwnLifecycle(); // Called each time the backend needs to initialize itself public void initialize(); @@ -884,7 +854,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Called to handle a reshape event. When this is called, the // OpenGL context associated with the backend is not current, to // make it easier to destroy and re-create pbuffers if necessary. - public void handleReshape(); + public boolean handleReshape(); // Called before the OpenGL work is done in init() and display(). // If false is returned, this render is aborted. @@ -902,12 +872,11 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Base class used by both the software (pixmap) and pbuffer // backends, both of which rely on reading back the OpenGL frame // buffer and drawing it with a BufferedImage - abstract class AbstractReadbackBackend implements Backend { + class OffscreenBackend implements Backend { // This image is exactly the correct size to render into the panel protected BufferedImage offscreenImage; // One of these is used to store the read back pixels before storing // in the BufferedImage - protected ByteBuffer readBackBytes; protected IntBuffer readBackInts; protected int readBackWidthInPixels; protected int readBackHeightInPixels; @@ -915,12 +884,118 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private int glFormat; private int glType; + // Implementation using software rendering + private GLDrawableImpl offscreenDrawable; + private FBObject fboFlipped; + private GLSLTextureRaster glslTextureRaster; + private final int fboTextureUnit = 0; + + private GLContextImpl offscreenContext; + private boolean flippedVertical; + // For saving/restoring of OpenGL state during ReadPixels - private int[] swapbytes = new int[1]; - private int[] rowlength = new int[1]; - private int[] skiprows = new int[1]; - private int[] skippixels = new int[1]; - private int[] alignment = new int[1]; + private final GLPixelStorageModes psm = new GLPixelStorageModes(); + + @Override + public boolean isUsingOwnLifecycle() { return false; } + + @Override + public void initialize() { + if(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend: initialize()"); + } + try { + offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable( + null /* default platform device */, + offscreenCaps, + chooser, + panelWidth, panelHeight); + offscreenDrawable.setRealized(true); + offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith); + offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + isInitialized = true; + final GL gl = offscreenContext.getGL(); + flippedVertical = offscreenContext.isGLOrientationFlippedVertical(); + final GLCapabilitiesImmutable chosenCaps = offscreenDrawable.getChosenGLCapabilities(); + if( chosenCaps.isFBO() && flippedVertical && gl.isGL2ES2() ) { + helper.setAutoSwapBufferMode(false); + final GLFBODrawable fboDrawable = (GLFBODrawable) offscreenDrawable; + try { + fboFlipped = new FBObject(); + fboFlipped.reset(gl, fboDrawable.getWidth(), fboDrawable.getHeight(), 0, false); + fboFlipped.attachTexture2D(gl, 0, chosenCaps.getAlphaBits()>0); + // fboFlipped.attachRenderbuffer(gl, Attachment.Type.DEPTH, 24); + glslTextureRaster = new GLSLTextureRaster(fboTextureUnit, true); + glslTextureRaster.init(gl.getGL2ES2()); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, fboDrawable.getWidth(), fboDrawable.getHeight()); + } catch (Exception ex) { + ex.printStackTrace(); + if(null != glslTextureRaster) { + glslTextureRaster.dispose(gl.getGL2ES2()); + glslTextureRaster = null; + } + if(null != fboFlipped) { + fboFlipped.destroy(gl); + fboFlipped = null; + } + } + } else { + fboFlipped = null; + glslTextureRaster = null; + } + offscreenContext.release(); + } else { + isInitialized = false; + } + } finally { + if( !isInitialized ) { + if(null != offscreenContext) { + offscreenContext.destroy(); + offscreenContext = null; + } + if(null != offscreenDrawable) { + offscreenDrawable.setRealized(false); + offscreenDrawable = null; + } + } + } + } + + @Override + public void destroy() { + if(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)); + } + if ( null != offscreenContext && offscreenContext.isCreated() ) { + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + try { + final GL gl = offscreenContext.getGL(); + if(null != glslTextureRaster) { + glslTextureRaster.dispose(gl.getGL2ES2()); + } + if(null != fboFlipped) { + fboFlipped.destroy(gl); + } + } finally { + offscreenContext.destroy(); + } + } + } + offscreenContext = null; + glslTextureRaster = null; + fboFlipped = null; + offscreenContext = null; + + if (offscreenDrawable != null) { + final AbstractGraphicsDevice adevice = offscreenDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); + offscreenDrawable.setRealized(false); + offscreenDrawable = null; + if(null != adevice) { + adevice.close(); + } + } + } @Override public void setOpaque(boolean opaque) { @@ -943,128 +1018,85 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing if (isDisplay) { // Must now copy pixels from offscreen context into surface if (offscreenImage == null) { - 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; - - // Should be more flexible in these BufferedImage formats; - // perhaps see what the preferred image types are on the - // given platform - if (isOpaque()) { - awtFormat = BufferedImage.TYPE_INT_RGB; - } else { - awtFormat = BufferedImage.TYPE_INT_ARGB; - } - - offscreenImage = new BufferedImage(panelWidth, - panelHeight, - awtFormat); - switch (awtFormat) { - case BufferedImage.TYPE_3BYTE_BGR: - glFormat = GL2.GL_BGR; - glType = GL.GL_UNSIGNED_BYTE; - readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3); - break; - - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_INT_ARGB: - glFormat = GL.GL_BGRA; - glType = getGLPixelType(); + if (0 >= panelWidth || 0 >= panelHeight ) { + return; + } + final boolean withAlpha = !isOpaque(); + + glFormat = GL.GL_BGRA; + glType = GL.GL_UNSIGNED_BYTE; // offscreenContext.getDefaultPixelDataType(); + + if(!flippedVertical || null != glslTextureRaster) { + offscreenImage = new BufferedImage(panelWidth, panelHeight, withAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); + final int[] readBackIntBuffer = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); + readBackInts = IntBuffer.wrap(readBackIntBuffer); + } else { + offscreenImage = new BufferedImage(panelWidth, panelHeight, withAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB); readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); - 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(DEBUG) { + System.err.println(getThreadName()+": OffscreenBackend postGL offscreenImage-init: flippedVertical "+flippedVertical+", glslTextureRaster "+(null!=glslTextureRaster)); } } - if (offscreenImage != null) { - GL2 gl = getGL().getGL2(); - // Save current modes - gl.glGetIntegerv(GL2.GL_PACK_SWAP_BYTES, swapbytes, 0); - gl.glGetIntegerv(GL2.GL_PACK_ROW_LENGTH, rowlength, 0); - gl.glGetIntegerv(GL2.GL_PACK_SKIP_ROWS, skiprows, 0); - gl.glGetIntegerv(GL2.GL_PACK_SKIP_PIXELS, skippixels, 0); - gl.glGetIntegerv(GL2.GL_PACK_ALIGNMENT, alignment, 0); - - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, GL.GL_FALSE); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, readBackWidthInPixels); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, 1); - - // Actually read the pixels. - gl.glReadBuffer(gl.getDefaultReadBuffer()); - if (readBackBytes != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes); - } else if (readBackInts != null) { + final GL gl = offscreenContext.getGL(); + + // Save current modes + psm.setAlignment(gl, 1, 1); + if(gl.isGL2GL3()) { + final GL2GL3 gl2gl3 = gl.getGL2GL3(); + gl2gl3.glPixelStorei(GL2GL3.GL_PACK_ROW_LENGTH, readBackWidthInPixels); + gl2gl3.glReadBuffer(gl2gl3.getDefaultReadBuffer()); + } + + if(null != glslTextureRaster) { // implies flippedVertical + // perform vert-flipping via OpenGL/FBO + final GLFBODrawable fboDrawable = (GLFBODrawable)offscreenDrawable; + final FBObject.TextureAttachment fboTex = fboDrawable.getTextureBuffer(GL.GL_FRONT); + + fboDrawable.swapBuffers(); + fboFlipped.bind(gl); + + // gl.glActiveTexture(fboDrawable.getTextureUnit()); // implicit! + gl.glBindTexture(GL.GL_TEXTURE_2D, fboTex.getName()); + // gl.glClear(GL.GL_DEPTH_BUFFER_BIT); // fboFlipped runs w/o DEPTH! + glslTextureRaster.display(gl.getGL2ES2()); gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); - } - // Restore saved modes. - gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL2.GL_PACK_ALIGNMENT, alignment[0]); - - if (readBackBytes != null || readBackInts != null) { - // Copy temporary data into raster of BufferedImage for faster - // blitting Note that we could avoid this copy in the cases - // where !offscreenContext.offscreenImageNeedsVerticalFlip(), - // but that's the software rendering path which is very slow - // anyway - Object src = null; - Object dest = null; - int srcIncr = 0; - int destIncr = 0; - - if (readBackBytes != null) { - src = readBackBytes.array(); - dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels * 3; - destIncr = offscreenImage.getWidth() * 3; - } else { - src = readBackInts.array(); - dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels; - destIncr = offscreenImage.getWidth(); + fboFlipped.unbind(gl); + } else { + gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); + + if ( flippedVertical ) { + // Copy temporary data into raster of BufferedImage for faster + // blitting Note that we could avoid this copy in the cases + // where !offscreenContext.offscreenImageNeedsVerticalFlip(), + // but that's the software rendering path which is very slow + // anyway + final Object src = readBackInts.array(); + final Object dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); + final int srcIncr = readBackWidthInPixels; + final int destIncr = offscreenImage.getWidth(); + int srcPos = 0; + int destPos = (offscreenImage.getHeight() - 1) * destIncr; + for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { + System.arraycopy(src, srcPos, dest, destPos, destIncr); + } } + } - if (flipVertically()) { - int srcPos = 0; - int destPos = (offscreenImage.getHeight() - 1) * destIncr; - for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); - } - } else { - int srcPos = 0; - int destEnd = destIncr * offscreenImage.getHeight(); - for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); - } - } + // Restore saved modes. + psm.restore(gl); - // Note: image will be drawn back in paintComponent() for - // correctness on all platforms - } - } + // Note: image will be drawn back in paintComponent() for + // correctness on all platforms } } @Override public void doPaintComponent(Graphics g) { - doPaintComponentImpl(); + helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); + if (offscreenImage != null) { // Draw resulting image in one shot g.drawImage(offscreenImage, 0, 0, @@ -1074,57 +1106,52 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } } - protected abstract void doPaintComponentImpl(); - protected abstract int getGLPixelType(); - protected abstract boolean flipVertically(); - } - - class SoftwareBackend extends AbstractReadbackBackend { - // Implementation using software rendering - private GLDrawableImpl offscreenDrawable; - private GLContextImpl offscreenContext; - @Override - public boolean isUsingOwnThreadManagment() { return false; } - - @Override - public void initialize() { - if(DEBUG) { - System.err.println(getThreadName()+": SoftwareBackend: initialize()"); - } - // Fall-through path: create an offscreen context instead - offscreenDrawable = (GLDrawableImpl) factory.createOffscreenDrawable( - null /* default platform device */, - offscreenCaps, - chooser, - Math.max(1, panelWidth), - Math.max(1, panelHeight)); - offscreenDrawable.setRealized(true); - offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith); - offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); - - isInitialized = true; - } + public boolean handleReshape() { + /** FIXME: Shall we utilize such resize optimization (snippet kept alive from removed pbuffer backend) ? + // Use factor larger than 2 during shrinks for some hysteresis + float shrinkFactor = 2.5f; + if ( (panelWidth > readBackWidthInPixels) || (panelHeight > readBackHeightInPixels) || + (panelWidth < (readBackWidthInPixels / shrinkFactor)) || (panelHeight < (readBackHeightInPixels / shrinkFactor))) { + if (DEBUG) { + System.err.println(getThreadName()+": Resizing offscreen from (" + readBackWidthInPixels + ", " + readBackHeightInPixels + ") " + + " to fit (" + panelWidth + ", " + panelHeight + ")"); + } + } */ + + GLDrawableImpl _drawable = offscreenDrawable; + { + final GLDrawableImpl _drawableNew = GLDrawableHelper.resizeOffscreenDrawable(_drawable, offscreenContext, panelWidth, panelHeight); + if(_drawable != _drawableNew) { + // write back + _drawable = _drawableNew; + offscreenDrawable = _drawableNew; + } + } + panelWidth = _drawable.getWidth(); + panelHeight = _drawable.getHeight(); + readBackWidthInPixels = panelWidth; + readBackHeightInPixels = panelHeight; + + if( null != glslTextureRaster ) { + if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { + try { + final GL gl = offscreenContext.getGL(); + fboFlipped.reset(gl, _drawable.getWidth(), _drawable.getHeight(), 0, false); + glslTextureRaster.reshape(gl.getGL2ES2(), 0, 0, _drawable.getWidth(), _drawable.getHeight()); + } finally { + offscreenContext.release(); + } + } + } - @Override - public void destroy() { - if(DEBUG) { - System.err.println(getThreadName()+": SoftwareBackend: destroy() - offscreenContext: "+(null!=offscreenContext)+" - offscreenDrawable: "+(null!=offscreenDrawable)); - } - if (offscreenContext != null) { - offscreenContext.destroy(); - offscreenContext = null; - } - if (offscreenDrawable != null) { - final AbstractGraphicsDevice adevice = offscreenDrawable.getNativeSurface().getGraphicsConfiguration().getScreen().getDevice(); - offscreenDrawable.setRealized(false); - offscreenDrawable = null; - if(null != adevice) { - adevice.close(); + if (offscreenImage != null) { + offscreenImage.flush(); + offscreenImage = null; } - } + return _drawable.isRealized(); } - + @Override public GLContext createContext(GLContext shareWith) { return (null != offscreenDrawable) ? offscreenDrawable.createContext(shareWith) : null; @@ -1160,201 +1187,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } return offscreenDrawable.getGLProfile(); } - - @Override - public void handleReshape() { - destroy(); - initialize(); - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); - - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } - - @Override - protected void doPaintComponentImpl() { - helper.invokeGL(offscreenDrawable, offscreenContext, updaterDisplayAction, updaterInitAction); - } - - @Override - protected int getGLPixelType() { - return offscreenContext.getDefaultPixelDataType(); - } - - @Override - protected boolean flipVertically() { - return offscreenContext.isGLOrientationFlippedVertical(); - } - } - - class PbufferBackend extends AbstractReadbackBackend { - private GLPbuffer pbuffer; - private int pbufferWidth = 256; - private int pbufferHeight = 256; - - @Override - public boolean isUsingOwnThreadManagment() { return false; } - - @Override - public void initialize() { - if (pbuffer != null) { - throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)"); - } - if(DEBUG) { - System.err.println(getThreadName()+": PbufferBackend: initialize()"); - } - try { - pbuffer = factory.createGLPbuffer(null /* default platform device */, - offscreenCaps, - null, - pbufferWidth, - pbufferHeight, - shareWith); - pbuffer.setContextCreationFlags(additionalCtxCreationFlags); - pbuffer.addGLEventListener(updater); - isInitialized = true; - } catch (GLException e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println(getThreadName()+": GLJPanel: Falling back on software rendering because of problems creating pbuffer"); - } - hardwareAccelerationDisabled = true; - backend = null; - isInitialized = false; - createAndInitializeBackend(); - } - } - - @Override - public void destroy() { - if(DEBUG) { - System.err.println(getThreadName()+": PbufferBackend: destroy() - pbuffer: "+(null!=pbuffer)); - } - if (pbuffer != null) { - pbuffer.destroy(); - pbuffer = null; - } - } - - @Override - public GLContext createContext(GLContext shareWith) { - return (null != pbuffer) ? pbuffer.createContext(shareWith) : null; - } - - @Override - public void setContext(GLContext ctx) { - if (pbuffer == null || Beans.isDesignTime()) { - return; - } - pbuffer.setContext(ctx); - } - - @Override - public GLContext getContext() { - // Workaround for crashes in NetBeans GUI builder - if (null == pbuffer || Beans.isDesignTime()) { - return null; - } - return pbuffer.getContext(); - } - - @Override - public GLDrawable getDrawable() { - return pbuffer; - } - - @Override - public GLCapabilitiesImmutable getChosenGLCapabilities() { - if (pbuffer == null) { - return null; - } - return pbuffer.getChosenGLCapabilities(); - } - - @Override - public GLProfile getGLProfile() { - if (pbuffer == null) { - return null; - } - return pbuffer.getGLProfile(); - } - - @Override - public void handleReshape() { - // Use factor larger than 2 during shrinks for some hysteresis - float shrinkFactor = 2.5f; - if ((panelWidth > pbufferWidth) || (panelHeight > pbufferHeight) || - (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) { - if (DEBUG) { - System.err.println(getThreadName()+": Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " + - " to fit (" + panelWidth + ", " + panelHeight + ")"); - } - // Must destroy and recreate pbuffer to fit - if (pbuffer != null) { - // Watch for errors during pbuffer destruction (due to - // buggy / bad OpenGL drivers, in particular SiS) and fall - // back to software rendering - try { - pbuffer.destroy(); - } catch (GLException e) { - hardwareAccelerationDisabled = true; - backend = null; - isInitialized = false; - // Just disabled hardware acceleration during this resize operation; do a fixup - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); - if (DEBUG) { - System.err.println(getThreadName()+": Warning: falling back to software rendering due to bugs in OpenGL drivers"); - e.printStackTrace(); - } - createAndInitializeBackend(); - return; - } - } - pbuffer = null; - isInitialized = false; - pbufferWidth = getNextPowerOf2(panelWidth); - pbufferHeight = getNextPowerOf2(panelHeight); - if (DEBUG && !hardwareAccelerationDisabled) { - System.err.println(getThreadName()+": New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")"); - } - initialize(); - } - - // 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... - readBackWidthInPixels = pbufferWidth; - readBackHeightInPixels = panelHeight; - - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } - - @Override - protected void doPaintComponentImpl() { - pbuffer.display(); - } - - @Override - protected int getGLPixelType() { - // This seems to be a good choice on all platforms - return GL2.GL_UNSIGNED_INT_8_8_8_8_REV; - } - - @Override - protected boolean flipVertically() { - return true; - } } class J2DOGLBackend implements Backend { @@ -1407,7 +1239,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private GraphicsConfiguration workaroundConfig; @Override - public boolean isUsingOwnThreadManagment() { return true; } + public boolean isUsingOwnLifecycle() { return true; } @Override public void initialize() { @@ -1480,8 +1312,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override - public void handleReshape() { + public boolean handleReshape() { // Empty in this implementation + return true; } @Override @@ -1626,7 +1459,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing // Cause OpenGL pipeline to flush its results because // otherwise it's possible we will buffer up multiple frames' // rendering results, resulting in apparent mouse lag - GL gl = getGL(); + GL gl = joglContext.getGL(); gl.glFinish(); if (Java2D.isFBOEnabled() && diff --git a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java index 68fbe3dd5..cbb7cd699 100644 --- a/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java +++ b/src/jogl/classes/jogamp/opengl/GLAutoDrawableBase.java @@ -228,7 +228,7 @@ public abstract class GLAutoDrawableBase implements GLAutoDrawable, FPSCounter { // Catch dispose GLExceptions by GLEventListener, just 'print' them // so we can continue with the destruction. try { - helper.disposeGL(this, context); + helper.disposeGL(this, context, true); } catch (GLException gle) { gle.printStackTrace(); } diff --git a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java index 0f8b6b816..dc5d50cf2 100644 --- a/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java +++ b/src/jogl/classes/jogamp/opengl/GLDrawableHelper.java @@ -398,7 +398,7 @@ public class GLDrawableHelper { * </p> * <p> * Please consider using {@link #disposeAllGLEventListener(GLAutoDrawable, GLContext, boolean)} - * or {@link #disposeGL(GLAutoDrawable, GLContext)} + * or {@link #disposeGL(GLAutoDrawable, GLContext, boolean)} * for correctness, i.e. encapsulating all calls w/ makeCurrent etc. * </p> * @param autoDrawable @@ -826,12 +826,16 @@ public class GLDrawableHelper { /** * Principal helper method which runs * {@link #disposeAllGLEventListener(GLAutoDrawable, boolean) disposeAllGLEventListener(autoDrawable, false)} - * with the context made current <b>and</b> destroys the context afterwards while holding the lock. + * with the context made current. + * <p> + * If <code>destroyContext</code> is <code>true</code> the context is destroyed in the end while holding the lock.<br/> + * </p> * @param autoDrawable * @param context + * @param destroyContext destroy context in the end while holding the lock */ public final void disposeGL(final GLAutoDrawable autoDrawable, - final GLContext context) { + final GLContext context, boolean destroyContext) { // Support for recursive makeCurrent() calls as well as calling // other drawables' display() methods from within another one's GLContext lastContext = GLContext.getCurrent(); @@ -858,7 +862,11 @@ public class GLDrawableHelper { } } finally { try { - context.destroy(); + if(destroyContext) { + context.destroy(); + } else { + context.release(); + } flushGLRunnables(); } catch (Exception e) { System.err.println("Catched: "+e.getMessage()); diff --git a/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java new file mode 100644 index 000000000..20c251635 --- /dev/null +++ b/src/jogl/classes/jogamp/opengl/util/glsl/GLSLTextureRaster.java @@ -0,0 +1,195 @@ +/** + * Copyright 2012 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions 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. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.opengl.util.glsl; + +import java.nio.FloatBuffer; + +import com.jogamp.opengl.util.GLArrayDataServer; +import com.jogamp.opengl.util.PMVMatrix; +import com.jogamp.opengl.util.glsl.ShaderCode; +import com.jogamp.opengl.util.glsl.ShaderProgram; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES2; +import javax.media.opengl.GLArrayData; +import javax.media.opengl.GLException; +import javax.media.opengl.GLUniformData; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +public class GLSLTextureRaster { + private final boolean textureVertFlipped; + private final int textureUnit; + + private ShaderProgram sp; + private PMVMatrix pmvMatrix; + private GLUniformData pmvMatrixUniform; + private GLUniformData activeTexUniform; + private GLArrayDataServer interleavedVBO; + + public GLSLTextureRaster(int textureUnit, boolean textureVertFlipped) { + this.textureVertFlipped = textureVertFlipped; + this.textureUnit = textureUnit; + } + + public int getTextureUnit() { return textureUnit; } + + static final String shaderBasename = "texture01_xxx"; + static final String shaderSrcPath = "../../shader"; + static final String shaderBinPath = "../../shader/bin"; + + public void init(GL2ES2 gl) { + // Create & Compile the shader objects + final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, this.getClass(), + shaderSrcPath, shaderBinPath, shaderBasename, true); + final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, this.getClass(), + shaderSrcPath, shaderBinPath, shaderBasename, true); + rsVp.defaultShaderCustomization(gl, true, ShaderCode.es2_default_precision_vp); + rsFp.defaultShaderCustomization(gl, true, ShaderCode.es2_default_precision_fp); + + // Create & Link the shader program + sp = new ShaderProgram(); + sp.add(rsVp); + sp.add(rsFp); + if(!sp.link(gl, System.err)) { + throw new GLException("Couldn't link program: "+sp); + } + sp.useProgram(gl, true); + + // setup mgl_PMVMatrix + pmvMatrix = new PMVMatrix(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glMatrixMode(PMVMatrix.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + pmvMatrixUniform = new GLUniformData("mgl_PMVMatrix", 4, 4, pmvMatrix.glGetPMvMatrixf()); // P, Mv + if( pmvMatrixUniform.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+pmvMatrixUniform+" in shader: "+sp); + } + gl.glUniform(pmvMatrixUniform); + + activeTexUniform = new GLUniformData("mgl_Texture0", textureUnit); + if( activeTexUniform.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+activeTexUniform+" in shader: "+sp); + } + gl.glUniform(activeTexUniform); + + final float[] s_quadTexCoords; + if( textureVertFlipped ) { + s_quadTexCoords = s_quadTexCoords01; + } else { + s_quadTexCoords = s_quadTexCoords00; + } + + interleavedVBO = GLArrayDataServer.createGLSLInterleaved(3+2, GL.GL_FLOAT, false, 2*4, GL.GL_STATIC_DRAW); + { + final GLArrayData vArrayData = interleavedVBO.addGLSLSubArray("mgl_Vertex", 3, GL.GL_ARRAY_BUFFER); + if( vArrayData.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+vArrayData+" in shader: "+sp); + } + final GLArrayData tArrayData = interleavedVBO.addGLSLSubArray("mgl_MultiTexCoord", 2, GL.GL_ARRAY_BUFFER); + if( tArrayData.setLocation(gl, sp.program()) < 0 ) { + throw new GLException("Couldn't locate "+tArrayData+" in shader: "+sp); + } + final FloatBuffer ib = (FloatBuffer)interleavedVBO.getBuffer(); + for(int i=0; i<4; i++) { + ib.put(s_quadVertices, i*3, 3); + ib.put(s_quadTexCoords, i*2, 2); + } + } + interleavedVBO.seal(gl, true); + interleavedVBO.enableBuffer(gl, false); + + sp.useProgram(gl, false); + } + + public void reshape(GL2ES2 gl, int x, int y, int width, int height) { + if(null != sp) { + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); + pmvMatrix.glLoadIdentity(); + pmvMatrix.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 10.0f); + + pmvMatrix.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + pmvMatrix.glLoadIdentity(); + + sp.useProgram(gl, true); + gl.glUniform(pmvMatrixUniform); + sp.useProgram(gl, false); + } + } + + public void dispose(GL2ES2 gl) { + if(null != pmvMatrixUniform) { + pmvMatrixUniform = null; + } + if(null != pmvMatrix) { + pmvMatrix.destroy(); + pmvMatrix=null; + } + if(null != interleavedVBO) { + interleavedVBO.destroy(gl); + interleavedVBO=null; + } + if(null != sp) { + sp.destroy(gl); + sp=null; + } + } + + public void display(GL2ES2 gl) { + if(null != sp) { + sp.useProgram(gl, true); + interleavedVBO.enableBuffer(gl, true); + + gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4); + + interleavedVBO.enableBuffer(gl, false); + sp.useProgram(gl, false); + } + } + + private static final float[] s_quadVertices = { + -1f, -1f, 0f, // LB + 1f, -1f, 0f, // RB + -1f, 1f, 0f, // LT + 1f, 1f, 0f // RT + }; + private static final float[] s_quadTexCoords00 = { + 0f, 0f, // LB + 1f, 0f, // RB + 0f, 1f, // LT + 1f, 1f // RT + }; + private static final float[] s_quadTexCoords01 = { + 0f, 1f, // LB + 1f, 1f, // RB + 0f, 0f, // LT + 1f, 0f // RT + }; +} + |