/** * Copyright 2012 - 2019 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 com.jogamp.opengl; import java.lang.reflect.Field; import java.util.IdentityHashMap; import com.jogamp.nativewindow.AbstractGraphicsDevice; import com.jogamp.opengl.GLCapabilitiesImmutable; import com.jogamp.common.os.Platform; import com.jogamp.common.util.PropertyAccess; import com.jogamp.opengl.egl.EGL; import com.jogamp.opengl.egl.EGLExt; /** * GLRendererQuirks contains information of known bugs of various GL renderer. * This information allows us to workaround them. *
* Using centralized quirk identifier enables us to * locate code dealing w/ it and hence eases it's maintenance. *
*
* Some GL_VENDOR
and GL_RENDERER
strings are
* listed here
* For testing purpose or otherwise, you may override the implemented * quirk bit setting behavior using {@link GLRendererQuirks.Override}. *
*/ public class GLRendererQuirks { /** * Allow overriding any quirk settings * via the two properties: ** java -Djogl.quirks.force=GL3CompatNonCompliant,NoFullFBOSupport -cp my_classpath some.main.Class **
* Naturally, one quirk can only be listed in one override list. * Hence the two override sets force and ignore are unique. *
*/ public static enum Override { /** * No override. */ NONE, /** * Enforce the quirk, i.e. allowing the code path to be injected w/o actual cause. */ FORCE, /** * Ignore the quirk, i.e. don't set the quirk if otherwise caused. */ IGNORE } /** * Crashes XServer when using double buffered PBuffer with hardware GL_RENDERER on Mesa < 18.2.2: *discard
command leads to undefined behavior or won't get compiled if being used.
*
* Appears to have happened on Nvidia Tegra2, but seems to be fine now.
* FIXME: Constrain version.
*
* Mesa versions in the range [9.1.3 .. 18.2.0[ are not fully compliant with the * OpenGL 3.1 compatibility profile. * Most programs will give completely broken output (or no * output at all. *
** The above has been confirmed for the following Mesa 9.* GL_RENDERER strings: *
* Default implementation sets this quirk on all Mesa < 18.2.0 drivers. *
*/ public static final int GL3CompatNonCompliant = 6; /** * The OpenGL context needs aglFlush()
before releasing it, otherwise driver may freeze:
* * Some drivers may require X11 displays to be closed in the same order as they were created, * some may not allow them to be closed at all while resources are being used somehow. *
** Drivers known exposing such bug: *
Mesa X11
, not with GLX/DRI renderer.* See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515 * and {@link jogamp.nativewindow.x11.X11Util#ATI_HAS_XCLOSEDISPLAY_BUG}. *
** See Bug 705 - https://jogamp.org/bugzilla/show_bug.cgi?id=705 *
*/ public static final int DontCloseX11Display = 8; /** * Need current GL context when calling new ARB pixel format query functions, * otherwise driver crashes the VM. ** Drivers known exposing such bug: *
* See Bug 480 - https://jogamp.org/bugzilla/show_bug.cgi?id=480 *
*/ public static final int NeedCurrCtx4ARBPixFmtQueries = 9; /** * Need current GL context when calling new ARB CreateContext function, * otherwise driver crashes the VM. ** Drivers known exposing such bug: *
12.102.3.0
( amd_catalyst_13.5_mobility_beta2 )
* See Bug 706 - https://jogamp.org/bugzilla/show_bug.cgi?id=706
* See Bug 520 - https://jogamp.org/bugzilla/show_bug.cgi?id=520
*
* Note: Also enabled via {@link #BuggyColorRenderbuffer}. *
*/ public static final int NoFullFBOSupport = 11; /** * GLSL is not compliant or even not stable (crash) ** X11 Mesa DRI Intel(R) driver >= 9.2.1 cannot handle multithreaded shared GLContext usage * with non-blocking exclusive X11 display connections. * References: *
* However, not all multithreaded use-cases are broken, e.g. our GLMediaPlayer does work. *
* The above has been confirmed for the following Mesa 9.* strings: ** On Android 4.*, Huawei's Ascend G615 w/ Immersion.16 could not make a shared context * current, which uses a pbuffer drawable: *
*
*/ public static final int GLSharedContextBuggy = 14; /** * Bug 925 - Accept an ES3 Context, if reported via GL-Version-String w/o {@link EGLExt#EGL_OPENGL_ES3_BIT_KHR}. ** The ES3 Context can be used via {@link EGL#EGL_OPENGL_ES2_BIT}. *
** The ES3 Context {@link EGL#eglCreateContext(long, long, long, java.nio.IntBuffer) must be created} with version attributes: *
* EGL.EGL_CONTEXT_CLIENT_VERSION, 2, .. ** *
* Subsequent calls to {@link EGL#eglGetDisplay(long)} fail. *
** Reusing global EGL display works. *
** The quirk is autodetected within EGLDrawableFactory's initial default device setup! *
** Appears on: *
* Appears on: *
* final AbstractGraphicsDevice adevice = GLDrawableFactory.getDesktopFactory(); // or similar * if( GLRendererQuirks.existStickyDeviceQuirk(adevice, GLRendererQuirks.NoMultiSamplingBuffers) ) { * // don't use MSAA * } **/ public static final int NoMultiSamplingBuffers = 17; /** * Buggy FBO color renderbuffer target, * i.e. driver may crash. *
* Appears on: *
* Note: Also enables {@link #NoFullFBOSupport}. *
** Note: GLFBODrawable always uses texture attachments if set. *
*/ public static final int BuggyColorRenderbuffer = 18; /** * No pbuffer supporting accumulation buffers available, * even if driver claims otherwise. ** Some drivers wrongly claim to support pbuffers * with accumulation buffers. However, the creation of such pbuffer fails: *
* com.jogamp.opengl.GLException: pbuffer creation error: Couldn't find a suitable pixel format ** *
* Appears on: *
* Usually synchronization should not be required, if the shared GL objects
* are created and immutable before concurrent usage.
* However, using drivers exposing this issue always require the user to
* synchronize access of shared GL objects.
*
* Synchronization can be avoided if accessing the shared GL objects * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example. *
** Appears on: *
* See Bug 1088 - https://jogamp.org/bugzilla/show_bug.cgi?id=1088 *
*/ public static final int NeedSharedObjectSync = 20; /** * No reliable ARB_create_context implementation, * even if driver claims otherwise. ** Some drivers wrongly claim to support ARB_create_context. * However, the creation of such context fails: *
* com.jogamp.opengl.GLException: AWT-EventQueue-0: WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2 * requested (OpenGL >= 3.0.1). Requested: GLProfile[GL3bc/GL3bc.hw], current: 2.1 (Compat profile, FBO, hardware) * - 2.1.8787 ** *
* Appears on: *
* See OpenGL spec 3.0, chapter 2.1 OpenGL Fundamentals, page 7 or
* OpenGL ES spec 3.0.2, chapter 2.1 OpenGL Fundamentals, page 6:
*
* It is possible to use a GL context without a default framebuffer, in which case * a framebuffer object must be used to perform all rendering. This is useful for * applications neeting to perform offscreen rendering. ** *
* The feature will be attempted at initialization and this quirk will be set if failing. *
** Known drivers failing the specification: *
* This quirk currently exist to be injected by the user via the properties only, * see {@link GLRendererQuirks.Override}. *
*/ public static final int NoFBOSupport = 23; /** * Don't use the ChooseFBConfig's best match, * instead utilize the given {@link GLCapabilitiesChooser} or {@link DefaultGLCapabilitiesChooser} * without any recommendation. ** The default behavior without this quirk is using a given {@link GLCapabilitiesChooser} * and pass the ChooseFBConfig's best match as a recommendation. *
** This quirk currently exist to be injected by the user via the properties, * see {@link GLRendererQuirks.Override}. *
*/ public static final int DontChooseFBConfigBestMatch = 24; /** * On Mesa >= 18.0.0, {@code glXChooseFBConfig} selects better * {@link GLCapabilities} FBConfig than actually supported by * {@link glXCreatePbuffer} and {@code glXCreateGLXPixmap}. ** As tested on Mesa 18.3.6, requesting an RGB 8bit color component * FBConfig for {@code GLX_PBUFFER_BIT} and {@code GLX_PIXMAP_BIT} {@code GLX_DRAWABLE_TYPE}s * via {@code glXChooseFBConfig} returns an RGB 10bit color component * FBConfig as its best match. * Subsequent {@code glXCreatePbuffer} and {@code glXCreateGLXPixmap} calls fail. *
** This bugs seems to occur in Mesa >= 18.0.0 using allow_rgb10_configs, which is the default now. * While the 10 bit color components are not listed for * on-screen {@code GLX.GLX_WINDOW_BIT} {@code GLX_DRAWABLE_TYPE}s, * they are listed for above mentioned off-screen types without {@code XVisualInfo} reference. *
** This quirk disables using any color component > 8 bit for * {@code GLX_PBUFFER_BIT} and {@code GLX_PIXMAP_BIT} types * and forces using an optional given {@link GLCapabilitiesChooser} * or the {@link DefaultGLCapabilitiesChooser}. *
** Note: Also implies {@link #DontChooseFBConfigBestMatch} for {@code GLX_PBUFFER_BIT} and {@code GLX_PIXMAP_BIT} types. *
*/ public static final int No10BitColorCompOffscreen = 25; /** Return the number of known quirks, aka quirk bit count. */ public static final int getCount() { return 26; } private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval", "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard", "GL3CompatNonCompliant", "GLFlushBeforeRelease", "DontCloseX11Display", "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext", "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request", "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly", "NoMultiSamplingBuffers", "BuggyColorRenderbuffer", "NoPBufferWithAccum", "NeedSharedObjectSync", "NoARBCreateContext", "NoSurfacelessCtx", "NoFBOSupport", "DontChooseFBConfigBestMatch", "No10BitColorCompOffscreen" }; private static final IdentityHashMap* The {@link AbstractGraphicsDevice}s are mapped via their {@link AbstractGraphicsDevice#getUniqueID()}. *
** Not thread safe. *
* @see #areSameStickyDevice(AbstractGraphicsDevice, AbstractGraphicsDevice) */ public static GLRendererQuirks getStickyDeviceQuirks(final AbstractGraphicsDevice device) { final String key = device.getUniqueID(); final GLRendererQuirks has = stickyDeviceQuirks.get(key); final GLRendererQuirks res; if( null == has ) { res = new GLRendererQuirks(); stickyDeviceQuirks.put(key, res); } else { res = has; } return res; } /** * Returns true if both devices have the same {@link AbstractGraphicsDevice#getUniqueID()}, * otherwise false. */ public static boolean areSameStickyDevice(final AbstractGraphicsDevice device1, final AbstractGraphicsDevice device2) { return device1.getUniqueID() == device2.getUniqueID(); // uses .intern()! } /** * {@link #addQuirk(int) Adding given quirk} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. ** Not thread safe. *
* @see #getStickyDeviceQuirks(AbstractGraphicsDevice) */ public static void addStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) throws IllegalArgumentException { final GLRendererQuirks sq = getStickyDeviceQuirks(device); sq.addQuirk(quirk); } /** * {@link #addQuirks(GLRendererQuirks) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. ** Not thread safe. *
* @see #getStickyDeviceQuirks(AbstractGraphicsDevice) */ public static void addStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks quirks) throws IllegalArgumentException { final GLRendererQuirks sq = getStickyDeviceQuirks(device); sq.addQuirks(quirks); } /** * {@link #exist(int) Query} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}. ** Not thread safe. However, use after changing the sticky quirks is safe. *
* @see #getStickyDeviceQuirks(AbstractGraphicsDevice) */ public static boolean existStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirkBit) { return getStickyDeviceQuirks(device).exist(quirkBit); } /** * {@link #addQuirks(GLRendererQuirks) Pushing} the sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks} * to the given {@link GLRendererQuirks destination}. ** Not thread safe. However, use after changing the sticky quirks is safe. *
* @see #getStickyDeviceQuirks(AbstractGraphicsDevice) */ public static void pushStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks dest) { dest.addQuirks(getStickyDeviceQuirks(device)); } public static final Override getOverride(final int quirkBit) throws IllegalArgumentException { validateQuirk(quirkBit); if( 0 != ( ( 1 << quirkBit ) & _bitmaskOverrideForce ) ) { return Override.FORCE; } if( 0 != ( ( 1 << quirkBit ) & _bitmaskOverrideIgnore ) ) { return Override.IGNORE; } return Override.NONE; } private static int _bitmaskOverrideForce = 0; private static int _bitmaskOverrideIgnore = 0; static { _bitmaskOverrideForce = _queryQuirkMaskOfPropertyList("jogl.quirks.force", Override.FORCE); _bitmaskOverrideIgnore = _queryQuirkMaskOfPropertyList("jogl.quirks.ignore", Override.IGNORE); if( 0 != ( _bitmaskOverrideForce & GLRendererQuirks.BuggyColorRenderbuffer) ) { _bitmaskOverrideForce |= GLRendererQuirks.NoFullFBOSupport; } final int uniqueTest = _bitmaskOverrideForce & _bitmaskOverrideIgnore; if( 0 != uniqueTest ) { throw new InternalError("Override properties force 0x"+Integer.toHexString(_bitmaskOverrideForce)+ " and ignore 0x"+Integer.toHexString(_bitmaskOverrideIgnore)+ " have intersecting bits 0x"+Integer.toHexString(uniqueTest)+" "+Integer.toBinaryString(uniqueTest)); } } private static int _queryQuirkMaskOfPropertyList(final String propertyName, final Override override) { final String quirkNameList = PropertyAccess.getProperty(propertyName, true); if( null == quirkNameList ) { return 0; } int res = 0; final String quirkNames[] = quirkNameList.split(","); for(int i=0; i