diff options
author | Sven Gothel <[email protected]> | 2015-08-27 13:15:44 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2015-08-27 13:15:44 +0200 |
commit | deff49c901915e007f43a1df1a0d217a786e9f06 (patch) | |
tree | 46b5c4ed38772c1f5d32498904da9318b3ecab83 /src/jogl/classes | |
parent | 8b4f68a3a014c3ae62a64def3011ccc43e8c41c9 (diff) |
Bug 1202 - Add support of Adaptive Vsync via [GLX|WGL]_EXT_swap_control_tear
[GLX|WGL]_EXT_swap_control_tear extensions support
asynchronous buffer swaps, i.e. adaptive Vsync.
<https://www.opengl.org/wiki/SwapInterval_aka_vsync#Adaptive_Vsync>
<https://www.opengl.org/registry/specs/EXT/wgl_swap_control_tear.txt>
<https://www.opengl.org/registry/specs/EXT/glx_swap_control_tear.txt>
<http://keithp.com/blogs/async_flip/>
The extensions utilizes a negative interval value,
enabling late swaps to occur without synchronization to the video frame.
Hence '-1' has new semantics, previously it was the 'default value'
of 'untouched vsync interval'.
New default is:
- 0 for unrealized context
- 1 for realized context
+++
It requires [GLX|WGL]_EXT_swap_control,
hence we shall ensure to use use this extension
in the implementation of GLContext.setSwapInterval(..).
+++
Mesa3D seems to support GLX_SGI_swap_control only.
+++
Implemented on Windows and X11.
+++
On GNU/Linux using NVidia driver w/ my setup(*), sadly the query
GLX.glXQueryDrawable(displayHandle, drawable.getHandle(), GLX.GLX_LATE_SWAPS_TEAR_EXT, val);
always returns zero here, indicating async vsync is not supported.
(Queried the attribute for every frame in windowed or fullscreen mode)
Fullscreen
(*)
- Debian 8
- Kernel 3.16
- KDE/Kwin
- GL Version 4.5 (Core profile, arb, compat[ES2, ES3, ES31], FBO, hardware) - 4.5.0 NVIDIA 355.06 [GL 4.5.0, vendor 355.6.0 (NVIDIA 355.06)]
- GL_RENDERER GeForce GTX 660/PCIe/SSE2
- Samsung U28D590 (DFP-4): Internal DisplayPort
+++
Diffstat (limited to 'src/jogl/classes')
8 files changed, 208 insertions, 103 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/GLBase.java b/src/jogl/classes/com/jogamp/opengl/GLBase.java index dee5f1488..e0c784398 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLBase.java +++ b/src/jogl/classes/com/jogamp/opengl/GLBase.java @@ -446,29 +446,43 @@ public interface GLBase { public boolean isTextureFormatBGRA8888Available(); - /** Provides a platform-independent way to specify the minimum swap - interval for buffer swaps. An argument of 0 disables - sync-to-vertical-refresh completely, while an argument of 1 - causes the application to wait until the next vertical refresh - until swapping buffers. The default, which is platform-specific, - is usually either 0 or 1. This function is not guaranteed to - have an effect, and in particular only affects heavyweight - onscreen components. - - @see #getSwapInterval - @throws GLException if this context is not the current + /** + * Set the swap interval of the current context and attached <i>onscreen {@link GLDrawable}</i>. + * <p> + * <i>offscreen {@link GLDrawable}</i> are ignored and {@code false} is returned. + * </p> + * <p> + * The {@code interval} semantics: + * <ul> + * <li><i>0</i> disables the vertical synchronization</li> + * <li><i>≥1</i> is the number of vertical refreshes before a swap buffer occurs</li> + * <li><i><0</i> enables <i>late swaps to occur without synchronization to the video frame</i>, a.k.a <i>EXT_swap_control_tear</i>. + * If supported, the absolute value is the minimum number of + * video frames between buffer swaps. If not supported, the absolute value is being used, see above. + * </li> + * </ul> + * </p> + * @param interval see above + * @return true if the operation was successful, otherwise false + * @throws GLException if the context is not current. + * @see GLContext#setSwapInterval(int) + * @see #getSwapInterval() */ - public void setSwapInterval(int interval); - - /** Provides a platform-independent way to get the swap - interval set by {@link #setSwapInterval}. <br> + public void setSwapInterval(int interval) throws GLException; - If the interval is not set by {@link #setSwapInterval} yet, - -1 is returned, indicating that the platforms default - is being used. - - @see #setSwapInterval - */ + /** + * Return the current swap interval. + * <p> + * If the context has not been made current at all, + * the default value {@code 0} is returned. + * </p> + * <p> + * For a valid context w/ an <o>onscreen {@link GLDrawable}</i> the default value is {@code 1}, + * otherwise the default value is {@code 0}. + * </p> + * @see GLContext#getSwapInterval() + * @see #setSwapInterval(int) + */ public int getSwapInterval(); /** @@ -494,12 +508,12 @@ public interface GLBase { * for accessing it, including which class or interface contains the * functions. * - * <P> - * + * <p> * Note: it is the intent to add new extensions as quickly as possible * to the core GL API. Therefore it is unlikely that most vendors will * use this extension mechanism, but it is being provided for * completeness. + * </p> */ public Object getExtension(String extensionName); diff --git a/src/jogl/classes/com/jogamp/opengl/GLContext.java b/src/jogl/classes/com/jogamp/opengl/GLContext.java index f68c029df..3ac5df7d4 100644 --- a/src/jogl/classes/com/jogamp/opengl/GLContext.java +++ b/src/jogl/classes/com/jogamp/opengl/GLContext.java @@ -237,7 +237,6 @@ public abstract class GLContext { protected String ctxVersionString; protected VersionNumberString ctxVendorVersion; protected VersionNumber ctxGLSLVersion; - private int currentSwapInterval; protected GLRendererQuirks glRendererQuirks; /** Did the drawable association changed ? see {@link GLRendererQuirks#NoSetSwapIntervalPostRetarget} */ @@ -258,7 +257,6 @@ public abstract class GLContext { ctxGLSLVersion = VersionNumber.zeroVersion; attachedObjects.clear(); contextHandle=0; - currentSwapInterval = -1; glRendererQuirks = null; drawableRetargeted = false; } @@ -1246,50 +1244,41 @@ public abstract class GLContext { } /** - * Set the swap interval of the current context and attached drawable. - * @param interval Should be ≥ 0. 0 disables the vertical synchronization, - * where ≥ 1 is the number of vertical refreshes before a swap buffer occurs. - * A value < 0 is ignored. + * Set the swap interval of the current context and attached <i>onscreen {@link GLDrawable}</i>. + * <p> + * <i>offscreen {@link GLDrawable}</i> are ignored and {@code false} is returned. + * </p> + * <p> + * The {@code interval} semantics: + * <ul> + * <li><i>0</i> disables the vertical synchronization</li> + * <li><i>≥1</i> is the number of vertical refreshes before a swap buffer occurs</li> + * <li><i><0</i> enables <i>late swaps to occur without synchronization to the video frame</i>, a.k.a <i>EXT_swap_control_tear</i>. + * If supported, the absolute value is the minimum number of + * video frames between buffer swaps. If not supported, the absolute value is being used, see above. + * </li> + * </ul> + * </p> + * @param interval see above * @return true if the operation was successful, otherwise false - * * @throws GLException if the context is not current. + * @see #getSwapInterval() */ - public final boolean setSwapInterval(final int interval) throws GLException { - validateCurrent(); - if(0<=interval) { - if( !drawableRetargeted || !hasRendererQuirk(GLRendererQuirks.NoSetSwapIntervalPostRetarget) ) { - if( setSwapIntervalImpl(interval) ) { - currentSwapInterval = interval; - return true; - } - } - } - return false; - } - protected boolean setSwapIntervalImpl(final int interval) { - return false; - } - /** Return the current swap interval. + public abstract boolean setSwapInterval(final int interval) throws GLException; + + /** + * Return the current swap interval. * <p> * If the context has not been made current at all, - * the default value <code>-1</code> is returned. + * the default value {@code 0} is returned. * </p> * <p> - * For a valid context the default value is <code>1</code> - * in case of an EGL based profile (ES1 or ES2) and <code>-1</code> - * (undefined) for desktop. + * For a valid context w/ an <o>onscreen {@link GLDrawable}</i> the default value is {@code 1}, + * otherwise the default value is {@code 0}. * </p> + * @see #setSwapInterval(int) */ - public final int getSwapInterval() { - return currentSwapInterval; - } - protected final void setDefaultSwapInterval() { - if(this.isGLES()) { - currentSwapInterval = 1; - } else { - currentSwapInterval = -1; - } - } + public abstract int getSwapInterval(); public final boolean queryMaxSwapGroups(final int[] maxGroups, final int maxGroups_offset, final int[] maxBarriers, final int maxBarriers_offset) { diff --git a/src/jogl/classes/jogamp/opengl/GLContextImpl.java b/src/jogl/classes/jogamp/opengl/GLContextImpl.java index fa7aeaff8..39c5796f3 100644 --- a/src/jogl/classes/jogamp/opengl/GLContextImpl.java +++ b/src/jogl/classes/jogamp/opengl/GLContextImpl.java @@ -131,6 +131,8 @@ public abstract class GLContextImpl extends GLContext { private boolean pixelDataEvaluated; private int /* pixelDataInternalFormat, */ pixelDataFormat, pixelDataType; + private int currentSwapInterval; + protected GL gl; protected static final Object mappedContextTypeObjectLock; @@ -209,6 +211,7 @@ public abstract class GLContextImpl extends GLContext { surfacelessOK = false; pixelDataEvaluated = false; + currentSwapInterval = 0; super.resetStates(isInit); } @@ -1110,6 +1113,34 @@ public abstract class GLContextImpl extends GLContext { } } + @Override + public final boolean setSwapInterval(final int interval) throws GLException { + validateCurrent(); + return setSwapIntervalNC(interval); + } + protected final boolean setSwapIntervalNC(final int interval) throws GLException { + if( drawable.getChosenGLCapabilities().isOnscreen() && + ( !drawableRetargeted || !hasRendererQuirk(GLRendererQuirks.NoSetSwapIntervalPostRetarget) ) + ) + { + final Integer usedInterval = setSwapIntervalImpl(interval); + if( null != usedInterval ) { + currentSwapInterval = usedInterval.intValue(); + return true; + } + } + return false; + } + protected abstract Integer setSwapIntervalImpl(final int interval); + + public final int getSwapInterval() { + return currentSwapInterval; + } + protected final void setDefaultSwapInterval() { + currentSwapInterval = 0; + setSwapIntervalNC(1); + } + /** * Note: Since context creation is temporary, caller need to issue {@link #resetStates(boolean)}, if creation was successful, i.e. returns true. * This method does not reset the states, allowing the caller to utilize the state variables. diff --git a/src/jogl/classes/jogamp/opengl/GLXExtensions.java b/src/jogl/classes/jogamp/opengl/GLXExtensions.java index 9325c6f68..db4757e4d 100644 --- a/src/jogl/classes/jogamp/opengl/GLXExtensions.java +++ b/src/jogl/classes/jogamp/opengl/GLXExtensions.java @@ -34,4 +34,10 @@ public class GLXExtensions { public static final String GLX_MESA_swap_control = "GLX_MESA_swap_control"; public static final String GLX_SGI_swap_control = "GLX_SGI_swap_control"; public static final String GLX_NV_swap_group = "GLX_NV_swap_group"; + + public static final String GLX_EXT_swap_control = "GLX_EXT_swap_control"; + public static final String GLX_EXT_swap_control_tear = "GLX_EXT_swap_control_tear"; + + public static final String WGL_EXT_swap_control = "WGL_EXT_swap_control"; + public static final String WGL_EXT_swap_control_tear = "WGL_EXT_swap_control_tear"; } diff --git a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java index b6a05aeeb..b64bb375a 100644 --- a/src/jogl/classes/jogamp/opengl/egl/EGLContext.java +++ b/src/jogl/classes/jogamp/opengl/egl/EGLContext.java @@ -51,7 +51,6 @@ import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; import jogamp.opengl.egl.EGLExtImpl; import jogamp.opengl.egl.EGLExtProcAddressTable; -import jogamp.opengl.windows.wgl.WindowsWGLContext; import com.jogamp.common.ExceptionUtils; import com.jogamp.common.nio.Buffers; @@ -432,11 +431,20 @@ public class EGLContext extends GLContextImpl { } @Override - protected boolean setSwapIntervalImpl(final int interval) { + protected final Integer setSwapIntervalImpl(final int interval) { if( hasRendererQuirk(GLRendererQuirks.NoSetSwapInterval) ) { - return false; + return null; } - return EGL.eglSwapInterval(drawable.getNativeSurface().getDisplayHandle(), interval); + final int useInterval; + if( 0 > interval ) { + useInterval = Math.abs(interval); + } else { + useInterval = interval; + } + if( EGL.eglSwapInterval(drawable.getNativeSurface().getDisplayHandle(), useInterval) ) { + return Integer.valueOf(useInterval); + } + return null; } static long eglGetProcAddress(final long eglGetProcAddressHandle, final String procname) diff --git a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java index d4fc0b005..2f88e7619 100644 --- a/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java +++ b/src/jogl/classes/jogamp/opengl/macosx/cgl/MacOSXCGLContext.java @@ -423,8 +423,17 @@ public class MacOSXCGLContext extends GLContextImpl } @Override - protected boolean setSwapIntervalImpl(final int interval) { - return impl.setSwapInterval(interval); + protected final Integer setSwapIntervalImpl(final int interval) { + final int useInterval; + if( 0 > interval ) { + useInterval = Math.abs(interval); + } else { + useInterval = interval; + } + if( impl.setSwapInterval(useInterval) ) { + return Integer.valueOf(useInterval); + } + return null; } @Override diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java index a8269ad5c..6ff8c57c8 100644 --- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java +++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLContext.java @@ -50,6 +50,7 @@ import com.jogamp.nativewindow.NativeSurface; import com.jogamp.opengl.GLContext; import com.jogamp.opengl.GLException; import com.jogamp.opengl.GLCapabilitiesImmutable; +import com.jogamp.common.ExceptionUtils; import com.jogamp.common.nio.Buffers; import com.jogamp.gluegen.runtime.ProcAddressTable; import com.jogamp.gluegen.runtime.opengl.GLProcAddressResolver; @@ -59,6 +60,7 @@ import com.jogamp.opengl.GLRendererQuirks; import jogamp.nativewindow.windows.GDI; import jogamp.opengl.GLContextImpl; import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.GLXExtensions; public class WindowsWGLContext extends GLContextImpl { static final Map<String, String> extensionNameMap; @@ -70,7 +72,8 @@ public class WindowsWGLContext extends GLContextImpl { // Table that holds the addresses of the native C-language entry points for // WGL extension functions. private WGLExtProcAddressTable wglExtProcAddressTable; - private int hasSwapIntervalSGI = 0; + /** 2 WGL_EXT_swap_control_tear, 1 WGL_EXT_swap_control, 0 undefined, -1 none */ + private int hasSwapInterval = 0; private int hasSwapGroupNV = 0; static { @@ -93,7 +96,7 @@ public class WindowsWGLContext extends GLContextImpl { wglGLReadDrawableAvailable=false; // no inner state _wglExt=null; wglExtProcAddressTable=null; - hasSwapIntervalSGI = 0; + hasSwapInterval = 0; hasSwapGroupNV = 0; super.resetStates(isInit); } @@ -487,19 +490,39 @@ public class WindowsWGLContext extends GLContextImpl { } @Override - protected boolean setSwapIntervalImpl(final int interval) { - final WGLExt wglExt = getWGLExt(); - if(0==hasSwapIntervalSGI) { + protected final Integer setSwapIntervalImpl(final int interval) { + if( 0 == hasSwapInterval ) { try { - hasSwapIntervalSGI = wglExt.isExtensionAvailable("WGL_EXT_swap_control")?1:-1; - } catch (final Throwable t) { hasSwapIntervalSGI=1; } + if ( isExtensionAvailable(GLXExtensions.WGL_EXT_swap_control) ) { + hasSwapInterval = 1; + if ( isExtensionAvailable(GLXExtensions.WGL_EXT_swap_control_tear) ) { + hasSwapInterval = 2; + if(DEBUG) { System.err.println("WGLContext.setSwapInterval.2 using: "+GLXExtensions.WGL_EXT_swap_control_tear + ", " + GLXExtensions.WGL_EXT_swap_control_tear); } + } else { + hasSwapInterval = 1; + if(DEBUG) { System.err.println("WGLContext.setSwapInterval.1 using: "+GLXExtensions.WGL_EXT_swap_control); } + } + } else { + hasSwapInterval = -1; + if(DEBUG) { System.err.println("WGLContext.setSwapInterval.0 N/A"); } + } + } catch (final Throwable t) { hasSwapInterval=-1; if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } } - if (hasSwapIntervalSGI>0) { + if ( 0 < hasSwapInterval ) { // 2 || 1 + final int useInterval; + if( 1 == hasSwapInterval && 0 > interval ) { + useInterval = Math.abs(interval); + } else { + useInterval = interval; + } try { - return wglExt.wglSwapIntervalEXT(interval); - } catch (final Throwable t) { hasSwapIntervalSGI=-1; } + final WGLExt wglExt = getWGLExt(); + if( wglExt.wglSwapIntervalEXT(useInterval) ) { + return Integer.valueOf(useInterval); + } + } catch (final Throwable t) { hasSwapInterval=-1; if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } } - return false; + return null; } private final int initSwapGroupImpl(final WGLExt wglExt) { diff --git a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java index 63b0b35c0..3f856e4f8 100644 --- a/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java +++ b/src/jogl/classes/jogamp/opengl/x11/glx/X11GLXContext.java @@ -72,7 +72,7 @@ public class X11GLXContext extends GLContextImpl { // Table that holds the addresses of the native C-language entry points for // GLX extension functions. private GLXExtProcAddressTable glXExtProcAddressTable; - /** 1 MESA, 2 SGI, 0 undefined, -1 none */ + /** 3 SGI, 2 GLX_EXT_swap_control_tear, 1 GLX_EXT_swap_control, 0 undefined, -1 none */ private int hasSwapInterval = 0; private int hasSwapGroupNV = 0; @@ -549,39 +549,64 @@ public class X11GLXContext extends GLContextImpl { } @Override - protected boolean setSwapIntervalImpl(final int interval) { - if( !drawable.getChosenGLCapabilities().isOnscreen() ) { return false; } - - final GLXExt glXExt = getGLXExt(); - if(0==hasSwapInterval) { + protected final Integer setSwapIntervalImpl(final int interval) { + final long displayHandle = drawable.getNativeSurface().getDisplayHandle(); + if( 0 == hasSwapInterval ) { try { - /** Same impl. .. - if( glXExt.isExtensionAvailable(GLXExtensions.GLX_MESA_swap_control) ) { - if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval using: "+GLXExtensions.GLX_MESA_swap_control); } - hasSwapInterval = 1; - } else */ - if ( glXExt.isExtensionAvailable(GLXExtensions.GLX_SGI_swap_control) ) { - if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval using: "+GLXExtensions.GLX_SGI_swap_control); } - hasSwapInterval = 2; + if ( isExtensionAvailable(GLXExtensions.GLX_EXT_swap_control) ) { + hasSwapInterval = 1; + if ( isExtensionAvailable(GLXExtensions.GLX_EXT_swap_control_tear) ) { + try { + final IntBuffer val = Buffers.newDirectIntBuffer(1); + GLX.glXQueryDrawable(displayHandle, drawable.getHandle(), GLX.GLX_LATE_SWAPS_TEAR_EXT, val); + if( 1 == val.get(0) ) { + hasSwapInterval = 2; + if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval.2 using: "+GLXExtensions.GLX_EXT_swap_control_tear + ", " + GLXExtensions.GLX_EXT_swap_control_tear); } + } else if(DEBUG) { + System.err.println("X11GLXContext.setSwapInterval.2 n/a: "+GLXExtensions.GLX_EXT_swap_control_tear+", query: "+val.get(0)); + } + } catch (final Throwable t) { if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } + } + if(DEBUG) { + if( 1 == hasSwapInterval ) { + System.err.println("X11GLXContext.setSwapInterval.1 using: "+GLXExtensions.GLX_EXT_swap_control); + } + } + } else if ( isExtensionAvailable(GLXExtensions.GLX_SGI_swap_control) ) { + hasSwapInterval = 3; + if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval.3 using: "+GLXExtensions.GLX_SGI_swap_control); } } else { hasSwapInterval = -1; + if(DEBUG) { System.err.println("X11GLXContext.setSwapInterval.0 N/A"); } } - } catch (final Throwable t) { hasSwapInterval=-1; } + } catch (final Throwable t) { hasSwapInterval=-1; if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } } - /* try { - switch( hasSwapInterval ) { - case 1: - return 0 == glXExt.glXSwapIntervalMESA(interval); - case 2: - return 0 == glXExt.glXSwapIntervalSGI(interval); + if (3 == hasSwapInterval) { + final int useInterval; + if( 0 > interval ) { + useInterval = Math.abs(interval); + } else { + useInterval = interval; + } + try { + final GLXExt glXExt = getGLXExt(); + if( 0 == glXExt.glXSwapIntervalSGI(useInterval) ) { + return Integer.valueOf(useInterval); + } + } catch (final Throwable t) { hasSwapInterval=-1; if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } + } else if ( 0 < hasSwapInterval ) { // 2 || 1 + final int useInterval; + if( 1 == hasSwapInterval && 0 > interval ) { + useInterval = Math.abs(interval); + } else { + useInterval = interval; } - } catch (Throwable t) { hasSwapInterval = -1; } */ - if (2 == hasSwapInterval) { try { - return 0 == glXExt.glXSwapIntervalSGI(interval); - } catch (final Throwable t) { hasSwapInterval=-1; } + GLX.glXSwapIntervalEXT(displayHandle, drawable.getHandle(), useInterval); + return Integer.valueOf(useInterval); + } catch (final Throwable t) { hasSwapInterval=-1; if(DEBUG) { ExceptionUtils.dumpThrowable("", t); } } } - return false; + return null; } private final int initSwapGroupImpl(final GLXExt glXExt) { |