diff options
author | Sven Gothel <[email protected]> | 2014-02-14 06:37:13 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2014-02-14 06:37:13 +0100 |
commit | 82f679b064784213591b460fc5eaa1f5f196fbd1 (patch) | |
tree | 58baaf8ee0461d8f481c287223192e78583983f6 /src | |
parent | fb12b0b6fa37d3a4136bb04597b3c32b15832c82 (diff) |
Bug 975 - GLJPanel's OffscreenDrawable shall not double swap - Use default auto-swap mechanims
Refines commit 908ebd99d1eb57ce773a1fdd67c76886da86b9e6
Note that the test case decide whether to auto-swap (after read-pixels)
or not auto-swap (manual swap before read-pixels).
See UITestCase.swapBuffersBeforeRead(GLCapabilitiesImmutable chosenCaps):
Determines whether the chosen GLCapabilitiesImmutable requires a swap-buffers before reading pixels.
Usually one uses the default-read-buffer, i.e. GL.GL_FRONT for single-buffer
and GL.GL_BACK for double-buffer GLDrawables
and GL.GL_COLOR_ATTACHMENT0 for offscreen framebuffer objects.
Here swap-buffers shall happen after calling reading pixels, the default.
However, multisampling offscreen GLFBODrawables utilize swap-buffers to downsample
the multisamples into the readable sampling sink.
In this case, we require a swap-buffers before reading pixels.
Returns: chosenCaps.isFBO() && chosenCaps.getSampleBuffers()
+++
- GLJPanel:
- Remove SurfaceUpdatedListener mechanism in favor of
default auto-swap-buffer via GLDrawableHelper.
This removes complexity.
- postGL does not need to perform explicit swapBuffer operation,
but rely on GLDrawableHelper and the default mechanism.
This is also compatible w/ J2D backend.
- Use GLDrawableHelper for setAutoSwapBufferMode(..) and getAutoSwapBufferMode()
+++
UnitTests:
- UITestCase:
- Add 'boolean swapBuffersBeforeRead(GLCapabilitiesImmutable chosenCaps)'
to determine whether swapBuffers() must occure before read-pixels. See above.
- GLReadBuffer00Base*
- remove explicit addSnapshotGLEL/removeSnapshotGLEL
- add TextRendererGLEL, to display frame-count and -dimension
- SnapshotGLEL*
- simply toggle auto-swap in their init(..) and dispose(..) method!
- clear back-buffer if 'swapBuffersBeforeRead'
to test whether the right buffer is being used for read-pixels.
Diffstat (limited to 'src')
5 files changed, 265 insertions, 113 deletions
diff --git a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java index 66a757d10..c80b405b5 100644 --- a/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java +++ b/src/jogl/classes/javax/media/opengl/awt/GLJPanel.java @@ -60,7 +60,6 @@ import java.util.List; import javax.media.nativewindow.AbstractGraphicsDevice; import javax.media.nativewindow.NativeSurface; -import javax.media.nativewindow.SurfaceUpdatedListener; import javax.media.nativewindow.WindowClosingProtocol; import javax.media.opengl.GL; import javax.media.opengl.GL2; @@ -355,7 +354,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing this.chooser = chooser; helper = new GLDrawableHelper(); - helper.setAutoSwapBufferMode(false); /** Always handles buffer swapping in Backend! */ if( null != shareWith ) { helper.setSharedContext(null, shareWith); } @@ -945,18 +943,13 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing } @Override - public void setAutoSwapBufferMode(boolean onOrOff) { - // In the current implementation this is a no-op. - // All Backend's require control of buffer swapping, - // i.e. as required for MSAA offscreen FBO buffer. + public void setAutoSwapBufferMode(boolean enable) { + helper.setAutoSwapBufferMode(enable); } @Override public boolean getAutoSwapBufferMode() { - // In the current implementation this is a no-op. - // All Backend's require control of buffer swapping, - // i.e. as required for MSAA offscreen FBO buffer. - return true; + return helper.getAutoSwapBufferMode(); } @Override @@ -1402,8 +1395,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing private volatile GLContextImpl offscreenContext; // volatile: avoid locking for read-only access private boolean flipVertical; - private boolean needsSwapBuffers = true; - // For saving/restoring of OpenGL state during ReadPixels private final GLPixelStorageModes psm = new GLPixelStorageModes(); @@ -1439,12 +1430,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing chooser, panelWidth, panelHeight); offscreenDrawable.setRealized(true); - offscreenDrawable.getNativeSurface().addSurfaceUpdatedListener(new SurfaceUpdatedListener() { - @Override - public final void surfaceUpdated(Object updater, NativeSurface ns, long when) { - needsSwapBuffers = false; - } } ); - offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith[0]); offscreenContext.setContextCreationFlags(additionalCtxCreationFlags); if( GLContext.CONTEXT_NOT_CURRENT < offscreenContext.makeCurrent() ) { @@ -1564,7 +1549,7 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public final boolean preGL(Graphics g) { - needsSwapBuffers = true; + // Empty in this implementation return true; } @@ -1579,6 +1564,9 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing @Override public final void postGL(Graphics g, boolean isDisplay) { if (isDisplay) { + // offscreenDrawable is already swapped, + // either by GLDrawableHelper.invoke or user's GLEL according to auto-swap-buffer-mode. + final GL gl = offscreenContext.getGL(); final int componentCount; @@ -1666,10 +1654,6 @@ public class GLJPanel extends JPanel implements AWTGLAutoDrawable, WindowClosing gl2es3.glReadBuffer(gl2es3.getDefaultReadBuffer()); } - if( needsSwapBuffers ) { - offscreenDrawable.swapBuffers(); - } - if(null != glslTextureRaster) { // implies flippedVertical final boolean viewportChange; final int[] usrViewport = new int[] { 0, 0, 0, 0 }; diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java index c5885d6b4..2ad622d14 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/GLReadBuffer00Base.java @@ -27,8 +27,10 @@ */ package com.jogamp.opengl.test.junit.jogl.acore; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import javax.media.opengl.GL2ES2; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; import javax.media.opengl.GLCapabilitiesImmutable; @@ -40,7 +42,15 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; +import com.jogamp.graph.curve.Region; +import com.jogamp.graph.curve.opengl.RenderState; +import com.jogamp.graph.curve.opengl.TextRenderer; +import com.jogamp.graph.font.Font; +import com.jogamp.graph.font.FontFactory; +import com.jogamp.graph.geom.opengl.SVertex; +import com.jogamp.opengl.math.geom.AABBox; import com.jogamp.opengl.test.junit.util.UITestCase; +import com.jogamp.opengl.util.glsl.ShaderState; /** * Multiple GLJPanels in a JFrame @@ -48,24 +58,128 @@ import com.jogamp.opengl.test.junit.util.UITestCase; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public abstract class GLReadBuffer00Base extends UITestCase { - @BeforeClass - public static void initClass() { - GLProfile.initSingleton(); + public static abstract class TextRendererGLELBase implements GLEventListener { + final float[] textPosition = new float[] {0,0,0}; + final int[] texSize = new int[] { 0 }; + final int fontSize = 24; + + final Font font; + final RenderState rs; + final TextRenderer renderer; + + boolean flipVerticalInGLOrientation = false; + + public TextRendererGLELBase() { + { + Font _font = null; + try { + _font = FontFactory.get(FontFactory.UBUNTU).getDefault(); + } catch (IOException e) { + e.printStackTrace(); + } + this.font = _font; + } + if( null != font ) { + this.rs = RenderState.createRenderState(new ShaderState(), SVertex.factory()); + this.renderer = TextRenderer.create(rs, Region.VBAA_RENDERING_BIT); + } else { + this.rs = null; + this.renderer = null; + } + } + + public void setFlipVerticalInGLOrientation(boolean v) { flipVerticalInGLOrientation=v; } + public final TextRenderer getRenderer() { return renderer; } + + public void init(GLAutoDrawable drawable) { + if( null != renderer ) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + renderer.init(gl); + renderer.setAlpha(gl, 0.99f); + renderer.setColorStatic(gl, 1.0f, 1.0f, 1.0f); + final ShaderState st = rs.getShaderState(); + st.useProgram(gl, false); + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + if( null != renderer ) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + final ShaderState st = rs.getShaderState(); + st.useProgram(gl, true); + // renderer.reshapePerspective(gl, 45.0f, width, height, 0.1f, 1000.0f); + renderer.reshapeOrtho(gl, width, height, 0.1f, 1000.0f); + st.useProgram(gl, false); + texSize[0] = width * 2; + } + } + + public abstract void display(GLAutoDrawable drawable); + + public void dispose(GLAutoDrawable drawable) { + if( null != renderer ) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + renderer.destroy(gl); + } + } + + int lastRow = -1; + + public void renderString(GLAutoDrawable drawable, String text, int column, int row, int z0) { + if( null != renderer ) { + final GL2ES2 gl = drawable.getGL().getGL2ES2(); + final int height = drawable.getHeight(); + + int dx = 0; + int dy = height; + if(0>row) { + row = lastRow + 1; + } + AABBox textBox = font.getStringBounds(text, fontSize); + dx += font.getAdvanceWidth('X', fontSize) * column; + dy -= (int)textBox.getHeight() * ( row + 1 ); + + final ShaderState st = rs.getShaderState(); + st.useProgram(gl, true); + gl.glEnable(GL2ES2.GL_BLEND); + renderer.resetModelview(null); + renderer.translate(gl, dx, dy, z0); + if( flipVerticalInGLOrientation && drawable.isGLOriented() ) { + renderer.scale(gl, 1f, -1f, 1f); + } + renderer.drawString3D(gl, font, text, textPosition, fontSize, texSize); + st.useProgram(gl, false); + gl.glDisable(GL2ES2.GL_BLEND); + + lastRow = row; + } + } } + public static class TextRendererGLEL extends TextRendererGLELBase { + int frameNo = 0; - protected abstract void test(final GLCapabilitiesImmutable caps, final boolean useSwingDoubleBuffer, final boolean skipGLOrientationVerticalFlip); + public TextRendererGLEL() { + super(); + } - private boolean defAutoSwapBufferMode = true; - protected void addSnapshotGLEL(final GLAutoDrawable glad, final GLEventListener snapshotGLEL) { - defAutoSwapBufferMode = glad.getAutoSwapBufferMode(); - glad.setAutoSwapBufferMode(false); - glad.addGLEventListener(snapshotGLEL); + @Override + public void display(GLAutoDrawable drawable) { + frameNo++; + final String text = String.format("Frame %04d: %04dx%04d", frameNo, drawable.getWidth(), drawable.getHeight()); + if( null != renderer ) { + renderString(drawable, text, 0, 0, -1); + } else { + System.err.println(text); + } + } } - protected void removeSnapshotGLEL(final GLAutoDrawable glad, final GLEventListener snapshotGLEL) { - glad.removeGLEventListener(snapshotGLEL); - glad.setAutoSwapBufferMode(defAutoSwapBufferMode); + + @BeforeClass + public static void initClass() throws IOException { + GLProfile.initSingleton(); } + protected abstract void test(final GLCapabilitiesImmutable caps, final boolean useSwingDoubleBuffer, final boolean skipGLOrientationVerticalFlip); @Test public void test00_MSAA0_DefFlip() throws InterruptedException, InvocationTargetException { @@ -78,17 +192,17 @@ public abstract class GLReadBuffer00Base extends UITestCase { } @Test - public void test10_MSAA4_DefFlip() throws InterruptedException, InvocationTargetException { + public void test10_MSAA8_DefFlip() throws InterruptedException, InvocationTargetException { final GLCapabilities caps = new GLCapabilities(null); - caps.setNumSamples(4); + caps.setNumSamples(8); caps.setSampleBuffers(true); test(caps, false /*useSwingDoubleBuffer*/, false /* skipGLOrientationVerticalFlip */); } @Test - public void test11_MSAA4_UsrFlip() throws InterruptedException, InvocationTargetException { + public void test11_MSAA8_UsrFlip() throws InterruptedException, InvocationTargetException { final GLCapabilities caps = new GLCapabilities(null); - caps.setNumSamples(4); + caps.setNumSamples(8); caps.setSampleBuffers(true); test(caps, false /*useSwingDoubleBuffer*/, true /* skipGLOrientationVerticalFlip */); } diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java index 4ad84aa73..ee9eea725 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLJPanelAWT.java @@ -52,6 +52,7 @@ import org.junit.runners.MethodSorters; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.awt.AWTGLReadBufferUtil; import com.jogamp.opengl.util.texture.TextureIO; @@ -72,6 +73,7 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { final JFrame frame = new JFrame(); final Dimension d = new Dimension(320, 240); final GLJPanel glad = createGLJPanel(skipGLOrientationVerticalFlip, useSwingDoubleBuffer, caps, d); + final SnapshotGLELAWT snapshotGLEL = new SnapshotGLELAWT(awtGLReadBufferUtil, skipGLOrientationVerticalFlip); try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -81,10 +83,14 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { panel.setDoubleBuffered(useSwingDoubleBuffer); frame.getContentPane().add(panel); - final GearsES2 gears = new GearsES2(0); + final GearsES2 gears = new GearsES2(1); gears.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); gears.setVerbose(false); glad.addGLEventListener(gears); + final TextRendererGLEL textRendererGLEL = new TextRendererGLEL(); + textRendererGLEL.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); + glad.addGLEventListener(textRendererGLEL); + glad.addGLEventListener(snapshotGLEL); panel.add(glad); frame.pack(); frame.setVisible(true); @@ -93,18 +99,11 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } - final GLEventListener snapshotGLEL = new SnapshotGLEL(awtGLReadBufferUtil, skipGLOrientationVerticalFlip); + glad.display(); // trigger initialization to get chosen-caps! final Dimension size0 = frame.getSize(); final Dimension size1 = new Dimension(size0.width+100, size0.height+100); final Dimension size2 = new Dimension(size0.width-100, size0.height-100); try { - final Animator anim = new Animator(glad); - anim.start(); - try { Thread.sleep(2*duration); } catch (InterruptedException e) { } - anim.stop(); - addSnapshotGLEL(glad, snapshotGLEL); - glad.display(); - try { Thread.sleep(duration); } catch (InterruptedException e) { } javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { @@ -125,7 +124,8 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { } } ); try { Thread.sleep(duration); } catch (InterruptedException e) { } - removeSnapshotGLEL(glad, snapshotGLEL); + glad.disposeGLEventListener(snapshotGLEL, true /* remove */); + final Animator anim = new Animator(glad); anim.start(); try { Thread.sleep(2*duration); } catch (InterruptedException e) { } anim.stop(); @@ -152,21 +152,31 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { return canvas; } - private class SnapshotGLEL implements GLEventListener { + private class SnapshotGLELAWT implements GLEventListener { final AWTGLReadBufferUtil glReadBufferUtil; final boolean skipGLOrientationVerticalFlip; + boolean defAutoSwapMode; + boolean swapBuffersBeforeRead; int i; - SnapshotGLEL(final AWTGLReadBufferUtil glReadBufferUtil, final boolean skipGLOrientationVerticalFlip) { + SnapshotGLELAWT(final AWTGLReadBufferUtil glReadBufferUtil, final boolean skipGLOrientationVerticalFlip) { this.glReadBufferUtil = glReadBufferUtil; this.skipGLOrientationVerticalFlip = skipGLOrientationVerticalFlip; + this.defAutoSwapMode = true; + this.swapBuffersBeforeRead = false; i = 0; } @Override - public void init(GLAutoDrawable drawable) { } + public void init(GLAutoDrawable drawable) { + defAutoSwapMode = drawable.getAutoSwapBufferMode(); + swapBuffersBeforeRead = UITestCase.swapBuffersBeforeRead(drawable.getChosenGLCapabilities()); + drawable.setAutoSwapBufferMode( !swapBuffersBeforeRead ); + } @Override - public void dispose(GLAutoDrawable drawable) { } + public void dispose(GLAutoDrawable drawable) { + drawable.setAutoSwapBufferMode( defAutoSwapMode ); + } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } @Override @@ -178,10 +188,17 @@ public class TestGLReadBuffer01GLJPanelAWT extends GLReadBuffer00Base { final String filenameAWT = getSnapshotFilename(sn, "awt", drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - // gl.glFinish(); // just make sure rendering finished .. - drawable.swapBuffers(); + if( swapBuffersBeforeRead ) { + drawable.swapBuffers(); + // Just to test whether we use the right buffer, + // i.e. back-buffer shall no more be required .. + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + } else { + gl.glFinish(); // just make sure rendering finished .. + } + final boolean awtOrientation = !( drawable.isGLOriented() && skipGLOrientationVerticalFlip ); - System.err.println(Thread.currentThread().getName()+": ** screenshot: awtOrient/v-flip "+awtOrientation+", "+filenameAWT); + System.err.println(Thread.currentThread().getName()+": ** screenshot: awtOrient/v-flip "+awtOrientation+", swapBuffersBeforeRead "+swapBuffersBeforeRead+", "+filenameAWT); final BufferedImage image = glReadBufferUtil.readPixelsToBufferedImage(gl, awtOrientation); final File fout = new File(filenameAWT); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java index 598ec1021..dde4c32fa 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/acore/TestGLReadBuffer01GLWindowNEWT.java @@ -34,7 +34,6 @@ import javax.media.nativewindow.util.DimensionImmutable; import javax.media.opengl.GL; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilitiesImmutable; -import javax.media.opengl.GLDrawable; import javax.media.opengl.GLEventListener; import javax.media.opengl.GLProfile; @@ -46,6 +45,7 @@ import org.junit.runners.MethodSorters; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2; import com.jogamp.opengl.test.junit.util.MiscUtils; +import com.jogamp.opengl.test.junit.util.UITestCase; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.GLReadBufferUtil; import com.jogamp.opengl.util.texture.TextureIO; @@ -67,29 +67,26 @@ public class TestGLReadBuffer01GLWindowNEWT extends GLReadBuffer00Base { } final GLReadBufferUtil glReadBufferUtil = new GLReadBufferUtil(false, false); final GLWindow glad= GLWindow.create(caps); + final SnapshotGLEL snapshotGLEL = new SnapshotGLEL(glReadBufferUtil); try { glad.setPosition(64, 64); glad.setSize(320, 240); - final GearsES2 gears = new GearsES2(0); + final GearsES2 gears = new GearsES2(1); gears.setVerbose(false); glad.addGLEventListener(gears); + final TextRendererGLEL textRendererGLEL = new TextRendererGLEL(); + textRendererGLEL.setFlipVerticalInGLOrientation(skipGLOrientationVerticalFlip); + glad.addGLEventListener(textRendererGLEL); + glad.addGLEventListener(snapshotGLEL); glad.setVisible(true); } catch( Throwable throwable ) { throwable.printStackTrace(); Assume.assumeNoException( throwable ); } - final GLEventListener snapshotGLEL = new SnapshotGLEL(glReadBufferUtil); final DimensionImmutable size0 = new Dimension(glad.getWidth(), glad.getHeight()); final DimensionImmutable size1 = new Dimension(size0.getWidth()+100, size0.getHeight()+100); final DimensionImmutable size2 = new Dimension(size0.getWidth()-100, size0.getHeight()-100); try { - final Animator anim = new Animator(glad); - anim.start(); - try { Thread.sleep(2*duration); } catch (InterruptedException e) { } - anim.stop(); - addSnapshotGLEL(glad, snapshotGLEL); - glad.display(); - try { Thread.sleep(duration); } catch (InterruptedException e) { } glad.setSize(size1.getWidth(), size1.getHeight()); try { Thread.sleep(duration); } catch (InterruptedException e) { } @@ -98,7 +95,8 @@ public class TestGLReadBuffer01GLWindowNEWT extends GLReadBuffer00Base { glad.setSize(size0.getWidth(), size0.getHeight()); try { Thread.sleep(duration); } catch (InterruptedException e) { } - removeSnapshotGLEL(glad, snapshotGLEL); + glad.disposeGLEventListener(snapshotGLEL, true /* remove */); + final Animator anim = new Animator(glad); anim.start(); try { Thread.sleep(2*duration); } catch (InterruptedException e) { } anim.stop(); @@ -110,32 +108,48 @@ public class TestGLReadBuffer01GLWindowNEWT extends GLReadBuffer00Base { private class SnapshotGLEL implements GLEventListener { final GLReadBufferUtil glReadBufferUtil; + boolean defAutoSwapMode; + boolean swapBuffersBeforeRead; int i; SnapshotGLEL(final GLReadBufferUtil glReadBufferUtil) { this.glReadBufferUtil = glReadBufferUtil; + this.defAutoSwapMode = true; + this.swapBuffersBeforeRead = false; i = 0; } @Override - public void init(GLAutoDrawable drawable) { } + public void init(GLAutoDrawable drawable) { + defAutoSwapMode = drawable.getAutoSwapBufferMode(); + swapBuffersBeforeRead = UITestCase.swapBuffersBeforeRead(drawable.getChosenGLCapabilities()); + drawable.setAutoSwapBufferMode( !swapBuffersBeforeRead ); + } @Override - public void dispose(GLAutoDrawable drawable) { } + public void dispose(GLAutoDrawable drawable) { + drawable.setAutoSwapBufferMode( defAutoSwapMode ); + } @Override public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { } @Override public void display(GLAutoDrawable drawable) { - snapshot(i++, drawable.getGL(), TextureIO.PNG, null); + snapshot(i++, drawable, TextureIO.PNG, null); } - public void snapshot(int sn, GL gl, String fileSuffix, String destPath) { - final GLDrawable drawable = gl.getContext().getGLReadDrawable(); + public void snapshot(int sn, GLAutoDrawable drawable, String fileSuffix, String destPath) { + final GL gl = drawable.getGL(); final String filenameJGL = getSnapshotFilename(sn, "jgl", drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), glReadBufferUtil.hasAlpha(), fileSuffix, destPath); - // gl.glFinish(); // just make sure rendering finished .. - drawable.swapBuffers(); + if( swapBuffersBeforeRead ) { + drawable.swapBuffers(); + // Just to test whether we use the right buffer, + // i.e. back-buffer shall no more be required .. + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + } else { + gl.glFinish(); // just make sure rendering finished .. + } final boolean mustFlipVertically = !drawable.isGLOriented(); - System.err.println(Thread.currentThread().getName()+": ** screenshot: v-flip "+mustFlipVertically+", "+filenameJGL); + System.err.println(Thread.currentThread().getName()+": ** screenshot: v-flip "+mustFlipVertically+", swapBuffersBeforeRead "+swapBuffersBeforeRead+", "+filenameJGL); if(glReadBufferUtil.readPixels(gl, mustFlipVertically)) { glReadBufferUtil.write(new File(filenameJGL)); diff --git a/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java b/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java index 82247a59b..79c76e49f 100644 --- a/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java +++ b/src/test/com/jogamp/opengl/test/junit/util/UITestCase.java @@ -3,14 +3,14 @@ * * 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 @@ -20,12 +20,12 @@ * 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 com.jogamp.opengl.test.junit.util; import java.io.BufferedReader; @@ -69,15 +69,15 @@ public abstract class UITestCase { public static final String SINGLE_INSTANCE_LOCK_FILE = "UITestCase.lock"; public static final int SINGLE_INSTANCE_LOCK_PORT = 59999; - + public static final long SINGLE_INSTANCE_LOCK_TO = 6*60*1000; // wait up to 6 mins public static final long SINGLE_INSTANCE_LOCK_POLL = 1000; // poll every 1s private static volatile SingletonInstance singletonInstance; - + private static volatile boolean testSupported = true; private static volatile boolean resetXRandRIfX11AfterClass = false; - + private static volatile int maxMethodNameLen = 0; private static final synchronized void initSingletonInstance() { @@ -89,11 +89,11 @@ public abstract class UITestCase { } } } - + public static boolean isTestSupported() { return testSupported; } - + public static void setTestSupported(boolean v) { System.err.println("setTestSupported: "+v); testSupported = v; @@ -102,13 +102,13 @@ public abstract class UITestCase { public static void setResetXRandRIfX11AfterClass() { resetXRandRIfX11AfterClass = true; } - + /** * Iterates through all outputs and sets the preferred mode and normal rotation using RandR 1.3. * <p> * With NV drivers, one need to add the Modes in proper order to the Screen's Subsection "Display", * otherwise they are either in unsorted resolution order or even n/a! - * </p> + * </p> */ @SuppressWarnings("unused") public static void resetXRandRIfX11() { @@ -122,7 +122,7 @@ public abstract class UITestCase { final ProcessBuilder pb = new ProcessBuilder("xrandr", "-q"); pb.redirectErrorStream(true); System.err.println("XRandR Query: "+pb.command()); - final Process p = pb.start(); + final Process p = pb.start(); final MiscUtils.StreamDump dump = new MiscUtils.StreamDump( out, p.getInputStream(), ioSync ); dump.start(); while( !dump.eos() ) { @@ -185,7 +185,7 @@ public abstract class UITestCase { } return null; } - + public static int processCommand(String[] cmdline, OutputStream outstream, String outPrefix) { int errorCode = 0; final Object ioSync = new Object(); @@ -207,9 +207,9 @@ public abstract class UITestCase { e.printStackTrace(); errorCode = Integer.MIN_VALUE; } - return errorCode; + return errorCode; } - + public int getMaxTestNameLen() { if(0 == maxMethodNameLen) { int ml = 0; @@ -221,13 +221,13 @@ public abstract class UITestCase { } maxMethodNameLen = ml; } - return maxMethodNameLen; + return maxMethodNameLen; } - + public final String getTestMethodName() { return _unitTestName.getMethodName(); } - + public final String getSimpleTestName(String separator) { return getClass().getSimpleName()+separator+getTestMethodName(); } @@ -235,10 +235,10 @@ public abstract class UITestCase { public final String getFullTestName(String separator) { return getClass().getName()+separator+getTestMethodName(); } - + @BeforeClass public static void oneTimeSetUp() { - // one-time initialization code + // one-time initialization code initSingletonInstance(); } @@ -259,14 +259,14 @@ public abstract class UITestCase { System.err.println(" - "+unsupportedTestMsg); Assume.assumeTrue(testSupported); // abort } - System.err.println(); + System.err.println(); } @After public void tearDown() { System.err.println("++++ UITestCase.tearDown: "+getFullTestName(" - ")); } - + public static void waitForKey(String preMessage) { BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); System.err.println(preMessage+"> Press enter to continue"); @@ -274,9 +274,32 @@ public abstract class UITestCase { System.err.println(stdin.readLine()); } catch (IOException e) { } } - + static final String unsupportedTestMsg = "Test not supported on this platform."; + /** + * Determines whether the chosen {@link GLCapabilitiesImmutable} + * requires a {@link GLDrawable#swapBuffers() swap-buffers} + * before reading pixels. + * <p> + * Usually one uses the {@link GL#getDefaultReadBuffer() default-read-buffer}, i.e. + * {@link GL#GL_FRONT} for single-buffer and {@link GL#GL_BACK} for double-buffer {@link GLDrawable}s + * and {@link GL#GL_COLOR_ATTACHMENT0} for offscreen framebuffer objects.<br> + * Here {@link GLDrawable#swapBuffers() swap-buffers} shall happen <b>after</b> calling reading pixels, the default. + * </p> + * <p> + * However, <i>multisampling</i> offscreen {@link javax.media.opengl.GLFBODrawable}s + * utilize {@link GLDrawable#swapBuffers() swap-buffers} to <i>downsample</i> + * the multisamples into the readable sampling sink. + * In this case, we require a {@link GLDrawable#swapBuffers() swap-buffers} <b>before</b> reading pixels. + * </p> + * @param chosenCaps the chosen {@link GLCapabilitiesImmutable} + * @return chosenCaps.isFBO() && chosenCaps.getSampleBuffers() + */ + public static final boolean swapBuffersBeforeRead(GLCapabilitiesImmutable chosenCaps) { + return chosenCaps.isFBO() && chosenCaps.getSampleBuffers(); + } + public String getSnapshotFilename(int sn, String postSNDetail, GLCapabilitiesImmutable caps, int width, int height, boolean sinkHasAlpha, String fileSuffix, String destPath) { if(null == fileSuffix) { fileSuffix = TextureIO.PNG; @@ -284,7 +307,7 @@ public abstract class UITestCase { final int maxSimpleTestNameLen = getMaxTestNameLen()+getClass().getSimpleName().length()+1; final String simpleTestName = this.getSimpleTestName("."); final String filenameBaseName; - { + { final String accel = caps.getHardwareAccelerated() ? "hw" : "sw" ; final String scrnm; if(caps.isOnscreen()) { @@ -307,22 +330,22 @@ public abstract class UITestCase { final String aaext = caps.getSampleExtension(); postSNDetail = null != postSNDetail ? "-"+postSNDetail : ""; - filenameBaseName = String.format("%-"+maxSimpleTestNameLen+"s-n%04d%s-%-6s-%s-%s-B%s-F%s_I%s-D%02d-St%02d-Sa%02d_%s-%04dx%04d.%s", - simpleTestName, sn, postSNDetail, caps.getGLProfile().getName(), accel, - scrnm, dblb, F_pfmt, pfmt, depthBits, stencilBits, samples, aaext, + filenameBaseName = String.format("%-"+maxSimpleTestNameLen+"s-n%04d%s-%-6s-%s-%s-B%s-F%s_I%s-D%02d-St%02d-Sa%02d_%s-%04dx%04d.%s", + simpleTestName, sn, postSNDetail, caps.getGLProfile().getName(), accel, + scrnm, dblb, F_pfmt, pfmt, depthBits, stencilBits, samples, aaext, width, height, fileSuffix).replace(' ', '_'); } - return null != destPath ? destPath + File.separator + filenameBaseName : filenameBaseName; + return null != destPath ? destPath + File.separator + filenameBaseName : filenameBaseName; } - + /** - * Takes a snapshot of the drawable's current front framebuffer. Example filenames: + * Takes a snapshot of the drawable's current front framebuffer. Example filenames: * <pre> * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testES2OffScreenFBOSglBuf____-n0001-msaa0-GLES2_-sw-fbobject-Bdbl-Frgb__Irgba8888_-D24-St00-Sa00_default-0400x0300.png * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testES2OffScreenPbufferDblBuf-n0003-msaa0-GLES2_-sw-pbuffer_-Bdbl-Frgb__Irgba8880-D24-St00-Sa00_default-0200x0150.png * TestGLDrawableAutoDelegateOnOffscrnCapsNEWT.testGL2OffScreenPbufferSglBuf-n0003-msaa0-GL2___-hw-pbuffer_-Bone-Frgb__Irgba5551-D24-St00-Sa00_default-0200x0150.png * </pre> - * @param sn sequential number + * @param sn sequential number * @param postSNDetail optional detail to be added to the filename after <code>sn</code> * @param gl the current GL context object. It's read drawable is being used as the pixel source and to gather some details which will end up in the filename. * @param readBufferUtil the {@link GLReadBufferUtil} to be used to read the pixels for the screenshot. @@ -330,20 +353,20 @@ public abstract class UITestCase { * If <code>null</code> the <code>"png"</code> as defined in {@link TextureIO#PNG} is being used. * @param destPath Optional platform dependent file path. It shall use {@link File#separatorChar} as is directory separator. * It shall not end with a directory separator, {@link File#separatorChar}. - * If <code>null</code> the current working directory is being used. + * If <code>null</code> the current working directory is being used. */ public void snapshot(int sn, String postSNDetail, GL gl, GLReadBufferUtil readBufferUtil, String fileSuffix, String destPath) { final GLDrawable drawable = gl.getContext().getGLReadDrawable(); - final String filename = getSnapshotFilename(sn, postSNDetail, - drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), + final String filename = getSnapshotFilename(sn, postSNDetail, + drawable.getChosenGLCapabilities(), drawable.getWidth(), drawable.getHeight(), readBufferUtil.hasAlpha(), fileSuffix, destPath); System.err.println(Thread.currentThread().getName()+": ** screenshot: "+filename); gl.glFinish(); // just make sure rendering finished .. if(readBufferUtil.readPixels(gl, false)) { readBufferUtil.write(new File(filename)); - } + } } - + public class SnapshotGLEventListener implements GLEventListener { private final GLReadBufferUtil screenshot; private volatile boolean makeShot = false; @@ -394,6 +417,6 @@ public abstract class UITestCase { postSNDetail = v; } }; - + } |