1 /**
2  * Copyright 2012 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 package com.jogamp.opengl;
29 
30 import java.util.IdentityHashMap;
31 
32 import com.jogamp.nativewindow.AbstractGraphicsDevice;
33 import com.jogamp.opengl.GLCapabilitiesImmutable;
34 
35 import com.jogamp.common.os.Platform;
36 import com.jogamp.opengl.egl.EGL;
37 import com.jogamp.opengl.egl.EGLExt;
38 
39 /**
40  * GLRendererQuirks contains information of known bugs of various GL renderer.
41  * This information allows us to workaround them.
42  * <p>
43  * Using centralized quirk identifier enables us to
44  * locate code dealing w/ it and hence eases it's maintenance.
45  * </p>
46  * <p>
47  * <i>Some</i> <code>GL_VENDOR</code> and <code>GL_RENDERER</code> strings are
48  * listed here <http://feedback.wildfiregames.com/report/opengl/feature/GL_VENDOR>.
49  * </p>
50  */
51 public class GLRendererQuirks {
52     /**
53      * Crashes XServer when using double buffered PBuffer with GL_RENDERER:
54      * <ul>
55      *  <li>Mesa DRI Intel(R) Sandybridge Desktop</li>
56      *  <li>Mesa DRI Intel(R) Ivybridge Mobile - 3.0 Mesa 8.0.4</li>
57      *  <li>Gallium 0.4 on AMD CYPRESS</li>
58      * </ul>
59      * For now, it is safe to disable it w/ hw-acceleration.
60      */
61     public static final int NoDoubleBufferedPBuffer = 0;
62 
63     /** On Windows no double buffered bitmaps are guaranteed to be available. */
64     public static final int NoDoubleBufferedBitmap  = 1;
65 
66     /** Crashes application when trying to set EGL swap interval on Android 4.0.3 / Pandaboard ES / PowerVR SGX 540 */
67     public static final int NoSetSwapInterval       = 2;
68 
69     /** No offscreen bitmap available, currently true for JOGL's OSX implementation. */
70     public static final int NoOffscreenBitmap       = 3;
71 
72     /** SIGSEGV on setSwapInterval() after changing the context's drawable w/ 'Mesa 8.0.4' dri2SetSwapInterval/DRI2 (soft & intel) */
73     public static final int NoSetSwapIntervalPostRetarget = 4;
74 
75     /**
76      * GLSL <code>discard</code> command leads to undefined behavior or won't get compiled if being used.
77      * <p>
78      * Appears to <i>have</i> happened on Nvidia Tegra2, but seems to be fine now.<br/>
79      * FIXME: Constrain version.
80      * </p>
81      */
82     public static final int GLSLBuggyDiscard = 5;
83 
84     /**
85      * Non compliant GL context due to a buggy implementation not suitable for use.
86      * <p>
87      * Currently, Mesa >= 9.1.3 (may extend back as far as 9.0) OpenGL 3.1 compatibility
88      * context is not compliant. Most programs will give completely broken output (or no
89      * output at all. For now, this context is not trusted.
90      * </p>
91      * The above has been confirmed for the following Mesa 9.* GL_RENDERER strings:
92      * <ul>
93      *   <li>Mesa .* Intel(R) Sandybridge Desktop</li>
94      *   <li>Gallium 0.4 on AMD RS880</li>
95      * </ul>
96      * </p>
97      * <p>
98      * It still has to be verified whether the AMD OpenGL 3.1 core driver is compliant enought.
99      */
100     public static final int GLNonCompliant = 6;
101 
102     /**
103      * The OpenGL context needs a <code>glFlush()</code> before releasing it, otherwise driver may freeze:
104      * <ul>
105      *   <li>OSX < 10.7.3 - NVidia Driver. Bug 533 and Bug 548 @ https://jogamp.org/bugzilla/.</li>
106      * </ul>
107      */
108     public static final int GLFlushBeforeRelease = 7;
109 
110     /**
111      * Closing X11 displays may cause JVM crashes or X11 errors with some buggy drivers
112      * while being used in concert w/ OpenGL.
113      * <p>
114      * Some drivers may require X11 displays to be closed in the same order as they were created,
115      * some may not allow them to be closed at all while resources are being used somehow.
116      * </p>
117      * <p>
118      * Drivers known exposing such bug:
119      * <ul>
120      *   <li>Mesa &lt; 8.0 _with_ X11 software renderer <code>Mesa X11</code>, not with GLX/DRI renderer.</li>
121      *   <li>ATI proprietary Catalyst X11 driver versions:
122      *     <ul>
123      *       <li>8.78.6</li>
124      *       <li>8.881</li>
125      *       <li>8.911</li>
126      *       <li>9.01.8</li>
127      *     </ul></li>
128      * </ul>
129      * </p>
130      * <p>
131      * See Bug 515 - https://jogamp.org/bugzilla/show_bug.cgi?id=515
132      * and {@link jogamp.nativewindow.x11.X11Util#ATI_HAS_XCLOSEDISPLAY_BUG}.
133      * </p>
134      * <p>
135      * See Bug 705 - https://jogamp.org/bugzilla/show_bug.cgi?id=705
136      * </p>
137      */
138     public static final int DontCloseX11Display = 8;
139 
140     /**
141      * Need current GL context when calling new ARB <i>pixel format query</i> functions,
142      * otherwise driver crashes the VM.
143      * <p>
144      * Drivers known exposing such bug:
145      * <ul>
146      *   <li>ATI proprietary Catalyst driver on Windows version &le; XP.
147      *       TODO: Validate if bug actually relates to 'old' ATI Windows drivers for old GPU's like X300
148      *             regardless of the Windows version.</li>
149      * </ul>
150      * <p>
151      * See Bug 480 - https://jogamp.org/bugzilla/show_bug.cgi?id=480
152      * </p>
153      */
154     public static final int NeedCurrCtx4ARBPixFmtQueries = 9;
155 
156     /**
157      * Need current GL context when calling new ARB <i>CreateContext</i> function,
158      * otherwise driver crashes the VM.
159      * <p>
160      * Drivers known exposing such bug:
161      * <ul>
162      *   <li>ATI proprietary Catalyst Windows driver on laptops with a driver version as reported in <i>GL_VERSION</i>:
163      *     <ul>
164      *       <li> <i>null</i> </li>
165      *       <li> &lt; <code>12.102.3.0</code> ( <i>amd_catalyst_13.5_mobility_beta2</i> ) </li>
166      *     </ul></li>
167      * </ul>
168      * </p>
169      * <p>
170      * See Bug 706 - https://jogamp.org/bugzilla/show_bug.cgi?id=706<br/>
171      * See Bug 520 - https://jogamp.org/bugzilla/show_bug.cgi?id=520
172      * </p>
173      */
174     public static final int NeedCurrCtx4ARBCreateContext = 10;
175 
176     /**
177      * No full FBO support, i.e. not compliant w/
178      * <ul>
179      *   <li>GL_ARB_framebuffer_object</li>
180      *   <li>EXT_framebuffer_object</li>
181      *   <li>EXT_framebuffer_multisample</li>
182      *   <li>EXT_framebuffer_blit</li>
183      *   <li>EXT_packed_depth_stencil</li>
184      * </ul>.
185      * Drivers known exposing such bug:
186      * <ul>
187      *   <li>Mesa <i>7.12-devel</i> on Windows with VMware <i>SVGA3D</i> renderer:
188      *     <ul>
189      *       <li>GL_VERSION:  <i>2.1 Mesa 7.12-devel (git-d6c318e)</i> </li>
190      *       <li>GL_RENDERER: <i>Gallium 0.4 on SVGA3D; build: RELEASE;</i> </li>
191      *     </ul></li>
192      * </ul>
193      * <p>
194      * Also enabled via {@link #BuggyColorRenderbuffer}.
195      * </p>
196      * <p>
197      * Quirk can also be enabled via property: <code>jogl.fbo.force.min</code>.
198      * </p>
199      */
200     public static final int NoFullFBOSupport = 11;
201 
202     /**
203      * GLSL is not compliant or even not stable (crash)
204      * <ul>
205      *   <li>OSX < 10.7.0 (?) - NVidia Driver. Bug 818 @ https://jogamp.org/bugzilla/.</li>
206      * </ul>
207      */
208     public static final int GLSLNonCompliant = 12;
209 
210     /**
211      * GL4 context needs to be requested via GL3 profile attribute
212      * <ul>
213      *   <li>OSX >= 10.9.0 - kCGLOGLPVersion_GL4_Core may not produce hw-accel context. Bug 867 @ https://jogamp.org/bugzilla/.</li>
214      * </ul>
215      */
216     public static final int GL4NeedsGL3Request = 13;
217 
218     /**
219      * Buggy shared OpenGL context support within a multithreaded use-case, not suitable for stable usage.
220      * <p>
221      * <i>X11 Mesa DRI Intel(R) driver >= 9.2.1</i> cannot handle multithreaded shared GLContext usage
222      * with non-blocking exclusive X11 display connections.
223      * References:
224      * <ul>
225      *    <li>Bug 873: https://jogamp.org/bugzilla/show_bug.cgi?id=873</li>
226      *    <li>https://bugs.freedesktop.org/show_bug.cgi?id=41736#c8</li>
227      * </ul>
228      * <p>
229      * However, not all multithreaded use-cases are broken, e.g. our GLMediaPlayer does work.
230      * </p>
231      * The above has been confirmed for the following Mesa 9.* strings:
232      * <ul>
233      *    <li>GL_VENDOR      Intel Open Source Technology Center</li>
234      *    <li>GL_RENDERER    Mesa DRI Intel(R) Sandybridge Desktop</li>
235      *    <li>GL_RENDERER    Mesa DRI Intel(R) Ivybridge Mobile</li>
236      *    <li>GL_VERSION     3.1 (Core Profile) Mesa 9.2.1</li>
237      * </ul>
238      * </p>
239      * <p>
240      * On Android 4.*, <i>Huawei's Ascend G615 w/ Immersion.16</i> could not make a shared context
241      * current, which uses a pbuffer drawable:
242      * <ul>
243      *    <li>Android 4.*</li>
244      *    <li>GL_VENDOR      Hisilicon Technologies</li>
245      *    <li>GL_RENDERER    Immersion.16</li>
246      *    <li>GL_VERSION     OpenGL ES 2.0</li>
247      * </ul>
248      * </p>
249      * <p>
250      * </p>
251      */
252     public static final int GLSharedContextBuggy = 14;
253 
254     /**
255      * Bug 925 - Accept an ES3 Context, if reported via GL-Version-String w/o {@link EGLExt#EGL_OPENGL_ES3_BIT_KHR}.
256      * <p>
257      * The ES3 Context can be used via {@link EGL#EGL_OPENGL_ES2_BIT}.
258      * </p>
259      * <p>
260      * The ES3 Context {@link EGL#eglCreateContext(long, long, long, java.nio.IntBuffer) must be created} with version attributes:
261      * <pre>
262      *  EGL.EGL_CONTEXT_CLIENT_VERSION, 2, ..
263      * </pre>
264      * </p>
265      * <ul>
266      *   <li>Mesa/AMD >= 9.2.1</li>
267      *   <li>Some Android ES3 drivers ..</li>
268      * </ul>
269      */
270     public static final int GLES3ViaEGLES2Config = 15;
271 
272     /**
273      * Bug 948 - NVIDIA 331.38 (Linux X11) EGL impl. only supports _one_ EGL Device via {@link EGL#eglGetDisplay(long)}.
274      * <p>
275      * Subsequent calls to {@link EGL#eglGetDisplay(long)} fail.
276      * </p>
277      * <p>
278      * Reusing global EGL display works.
279      * </p>
280      * <p>
281      * The quirk is autodetected within EGLDrawableFactory's initial default device setup!
282      * </p>
283      * <p>
284      * Appears on:
285      * <ul>
286      *   <li>EGL_VENDOR      NVIDIA</li>
287      *   <li>EGL_VERSION     1.4</li>
288      *   <li>GL_VENDOR       NVIDIA Corporation</li>
289      *   <li>GL_VERSION      OpenGL ES 3.0 331.38 (probably w/ 1st NV EGL lib on x86)</li>
290      *   <li>GL_VERSION      OpenGL ES 3.1 NVIDIA 355.06 (unstable)</li>
291      *   <li>Platform        X11</li>
292      *   <li>CPU Family      {@link Platform.CPUFamily#X86}</li>
293      * </ul>
294      * </p>
295      */
296     public static final int SingletonEGLDisplayOnly = 16;
297 
298     /**
299      * No reliable MSAA / FSAA {@link GLCapabilitiesImmutable#getSampleBuffers() multi}
300      * {@link GLCapabilitiesImmutable#getNumSamples() sampling} available,
301      * i.e. driver <i>may crash</i>.
302      * <p>
303      * Appears on:
304      * <ul>
305      *   <li>GL_VENDOR       nouveau</li>
306      *   <li>GL_RENDERER     Gallium 0.4 on NV34</li>
307      * </ul>
308      * TODO: We have to determine the exact version range, i.e. not adding the quirk with fixed driver version!
309      * </p>
310      * TODO: Since we currently don't handle this quirk internally, a user may need to do the following:
311      * <pre>
312      * final AbstractGraphicsDevice adevice = GLDrawableFactory.getDesktopFactory(); // or similar
313      * if( GLRendererQuirks.existStickyDeviceQuirk(adevice, GLRendererQuirks.NoMultiSamplingBuffers) ) {
314      *    // don't use MSAA
315      * }
316      * </pre>
317      */
318     public static final int NoMultiSamplingBuffers  = 17;
319 
320     /**
321      * Buggy FBO color renderbuffer target,
322      * i.e. driver <i>may crash</i>.
323      * <p>
324      * Appears on:
325      * <ul>
326      *   <li>GL_VENDOR       Brian Paul</li>
327      *   <li>GL_RENDERER     Mesa X11</li>
328      *   <li>GL_VERSION      2.1 Mesa 7.2</li>
329      * </ul>
330      * TODO: We have to determine the exact version range, i.e. not adding the quirk with fixed driver version!
331      * </p>
332      * <p>
333      * Note: Also enables {@link #NoFullFBOSupport}.
334      * </p>
335      * <p>
336      * Note: GLFBODrawable always uses texture attachments if set.
337      * </p>
338      * <p>
339      * Quirk can also be enabled via property: <code>jogl.fbo.force.nocolorrenderbuffer</code>.
340      * </p>
341      */
342     public static final int BuggyColorRenderbuffer  = 18;
343 
344     /**
345      * No pbuffer supporting accumulation buffers available,
346      * even if driver claims otherwise.
347      * <p>
348      * Some drivers wrongly claim to support pbuffers
349      * with accumulation buffers. However, the creation of such pbuffer fails:
350      * <pre>
351      *   com.jogamp.opengl.GLException: pbuffer creation error: Couldn't find a suitable pixel format
352      * </pre>
353      * </p>
354      * <p>
355      * Appears on:
356      * <ul>
357      *   <li>GL_VENDOR       Intel</li>
358      *   <li>GL_RENDERER     Intel Bear Lake B</li>
359      *   <li>GL_VERSION      1.4.0 - Build 8.14.10.1930</li>
360      *   <li>Platform        Windows</li>
361      * </ul>
362      * </p>
363      */
364     public static final int NoPBufferWithAccum = 19;
365 
366     /**
367      * Need GL objects (VBO, ..) to be synchronized when utilized
368      * concurrently from multiple threads via a shared GL context,
369      * otherwise driver crashes the VM.
370      * <p>
371      * Usually synchronization should not be required, if the shared GL objects
372      * are created and immutable before concurrent usage.<br>
373      * However, using drivers exposing this issue always require the user to
374      * synchronize access of shared GL objects.
375      * </p>
376      * <p>
377      * Synchronization can be avoided if accessing the shared GL objects
378      * exclusively via a queue or {@link com.jogamp.common.util.Ringbuffer Ringbuffer}, see GLMediaPlayerImpl as an example.
379      * </p>
380      * <p>
381      * Appears on:
382      * <ul>
383      *   <li>Platform        OSX
384      *     <ul>
385      *       <li>detected on OSX 10.9.5 first</li>
386      *       <li>any driver</li>
387      *       <li>enabled for all OSX versions</li>
388      *     </ul>
389      *   </li>
390      * </ul>
391      * </p>
392      * <p>
393      * See Bug 1088 - https://jogamp.org/bugzilla/show_bug.cgi?id=1088
394      * </p>
395      */
396     public static final int NeedSharedObjectSync = 20;
397 
398     /**
399      * No reliable ARB_create_context implementation,
400      * even if driver claims otherwise.
401      * <p>
402      * Some drivers wrongly claim to support ARB_create_context.
403      * However, the creation of such context fails:
404      * <pre>
405      *   com.jogamp.opengl.GLException: AWT-EventQueue-0: WindowsWGLContex.createContextImpl ctx !ARB, profile > GL2
406      *   requested (OpenGL >= 3.0.1). Requested: GLProfile[GL3bc/GL3bc.hw], current: 2.1 (Compat profile, FBO, hardware)
407      *   - 2.1.8787
408      * </pre>
409      * </p>
410      * <p>
411      * Appears on:
412      * <ul>
413      *   <li>GL_VENDOR       ATI Technologies Inc.</li>
414      *   <li>GL_RENDERER     ATI Radeon 3100 Graphics</li>
415      *   <li>GL_VERSION      2.1.8787</li>
416      *   <li>Platform        Windows</li>
417      * </ul>
418      * </p>
419      */
420     public static final int NoARBCreateContext = 21;
421 
422     /**
423      * No support for ES or desktop GL >= 3.0 current context without surface,
424      * i.e. without a default framebuffer as read- and write drawables.
425      * <p>
426      * See <i>OpenGL spec 3.0, chapter 2.1 OpenGL Fundamentals, page 7</i> or<br>
427      * <i>OpenGL ES spec 3.0.2, chapter 2.1 OpenGL Fundamentals, page 6</i>:
428      * <pre>
429      * It is possible to use a GL context without a default framebuffer, in which case
430      * a framebuffer object must be used to perform all rendering. This is useful for
431      * applications neeting to perform offscreen rendering.
432      * </pre>
433      * </p>
434      * <p>
435      * The feature will be attempted at initialization and this quirk will be set if failing.
436      * </p>
437      * <p>
438      * Known drivers failing the specification:
439      * <ul>
440      *   <li>GNU/Linux X11 Nvidia proprietary driver
441      *   <ul>
442      *     <li>GL_VERSION      4.4.0 NVIDIA 340.24</li>
443      *     <li>Platform        GNU/Linux X11</li>
444      *   </ul></li>
445      * </ul>
446      * </p>
447      */
448     public static final int NoSurfacelessCtx = 22;
449 
450     /** Return the number of known quirks. */
getCount()451     public static final int getCount() { return 23; }
452 
453     private static final String[] _names = new String[] { "NoDoubleBufferedPBuffer", "NoDoubleBufferedBitmap", "NoSetSwapInterval",
454                                                           "NoOffscreenBitmap", "NoSetSwapIntervalPostRetarget", "GLSLBuggyDiscard",
455                                                           "GLNonCompliant", "GLFlushBeforeRelease", "DontCloseX11Display",
456                                                           "NeedCurrCtx4ARBPixFmtQueries", "NeedCurrCtx4ARBCreateContext",
457                                                           "NoFullFBOSupport", "GLSLNonCompliant", "GL4NeedsGL3Request",
458                                                           "GLSharedContextBuggy", "GLES3ViaEGLES2Config", "SingletonEGLDisplayOnly",
459                                                           "NoMultiSamplingBuffers", "BuggyColorRenderbuffer", "NoPBufferWithAccum",
460                                                           "NeedSharedObjectSync", "NoARBCreateContext", "NoSurfacelessCtx"
461                                                         };
462 
463     private static final IdentityHashMap<String, GLRendererQuirks> stickyDeviceQuirks = new IdentityHashMap<String, GLRendererQuirks>();
464 
465     /**
466      * Retrieval of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}.
467      * <p>
468      * The {@link AbstractGraphicsDevice}s are mapped via their {@link AbstractGraphicsDevice#getUniqueID()}.
469      * </p>
470      * <p>
471      * Not thread safe.
472      * </p>
473      * @see #areSameStickyDevice(AbstractGraphicsDevice, AbstractGraphicsDevice)
474      */
getStickyDeviceQuirks(final AbstractGraphicsDevice device)475     public static GLRendererQuirks getStickyDeviceQuirks(final AbstractGraphicsDevice device) {
476         final String key = device.getUniqueID();
477         final GLRendererQuirks has = stickyDeviceQuirks.get(key);
478         final GLRendererQuirks res;
479         if( null == has ) {
480             res = new GLRendererQuirks();
481             stickyDeviceQuirks.put(key, res);
482         } else {
483             res = has;
484         }
485         return res;
486     }
487 
488     /**
489      * Returns true if both devices have the same {@link AbstractGraphicsDevice#getUniqueID()},
490      * otherwise false.
491      */
areSameStickyDevice(final AbstractGraphicsDevice device1, final AbstractGraphicsDevice device2)492     public static boolean areSameStickyDevice(final AbstractGraphicsDevice device1, final AbstractGraphicsDevice device2) {
493         return device1.getUniqueID() == device2.getUniqueID(); // uses .intern()!
494     }
495 
496     /**
497      * {@link #addQuirk(int) Adding given quirk} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}.
498      * <p>
499      * Not thread safe.
500      * </p>
501      * @see #getStickyDeviceQuirks(AbstractGraphicsDevice)
502      */
addStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk)503     public static void addStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) throws IllegalArgumentException {
504         final GLRendererQuirks sq = getStickyDeviceQuirks(device);
505         sq.addQuirk(quirk);
506     }
507     /**
508      * {@link #addQuirks(int[], int, int) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}.
509      * <p>
510      * Not thread safe.
511      * </p>
512      * @see #getStickyDeviceQuirks(AbstractGraphicsDevice)
513      */
addStickyDeviceQuirks(final AbstractGraphicsDevice device, final int[] quirks, final int offset, final int len)514     public static void addStickyDeviceQuirks(final AbstractGraphicsDevice device, final int[] quirks, final int offset, final int len) throws IllegalArgumentException {
515         final GLRendererQuirks sq = getStickyDeviceQuirks(device);
516         sq.addQuirks(quirks, offset, len);
517     }
518     /**
519      * {@link #addQuirks(GLRendererQuirks) Adding given quirks} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}.
520      * <p>
521      * Not thread safe.
522      * </p>
523      * @see #getStickyDeviceQuirks(AbstractGraphicsDevice)
524      */
addStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks quirks)525     public static void addStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks quirks) throws IllegalArgumentException {
526         final GLRendererQuirks sq = getStickyDeviceQuirks(device);
527         sq.addQuirks(quirks);
528     }
529     /**
530      * {@link #exist(int) Query} of sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}.
531      * <p>
532      * Not thread safe. However, use after changing the sticky quirks is safe.
533      * </p>
534      * @see #getStickyDeviceQuirks(AbstractGraphicsDevice)
535      */
existStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk)536     public static boolean existStickyDeviceQuirk(final AbstractGraphicsDevice device, final int quirk) {
537         return getStickyDeviceQuirks(device).exist(quirk);
538     }
539     /**
540      * {@link #addQuirks(GLRendererQuirks) Pushing} the sticky {@link AbstractGraphicsDevice}'s {@link GLRendererQuirks}
541      * to the given {@link GLRendererQuirks destination}.
542      * <p>
543      * Not thread safe. However, use after changing the sticky quirks is safe.
544      * </p>
545      * @see #getStickyDeviceQuirks(AbstractGraphicsDevice)
546      */
pushStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks dest)547     public static void pushStickyDeviceQuirks(final AbstractGraphicsDevice device, final GLRendererQuirks dest) {
548         dest.addQuirks(getStickyDeviceQuirks(device));
549     }
550 
551     private int _bitmask;
552 
GLRendererQuirks()553     public GLRendererQuirks() {
554         _bitmask = 0;
555     }
556 
557     /**
558      * @param quirks an array of valid quirks
559      * @param offset offset in quirks array to start reading
560      * @param len number of quirks to read from offset within quirks array
561      * @throws IllegalArgumentException if one of the quirks is out of range
562      */
GLRendererQuirks(final int[] quirks, final int offset, final int len)563     public GLRendererQuirks(final int[] quirks, final int offset, final int len) throws IllegalArgumentException {
564         this();
565         addQuirks(quirks, offset, len);
566     }
567 
568     /**
569      * @param quirk valid quirk to be added
570      * @throws IllegalArgumentException if the quirk is out of range
571      */
addQuirk(final int quirk)572     public final void addQuirk(final int quirk) throws IllegalArgumentException {
573         validateQuirk(quirk);
574         _bitmask |= 1 << quirk;
575     }
576 
577     /**
578      * @param quirks an array of valid quirks to be added
579      * @param offset offset in quirks array to start reading
580      * @param len number of quirks to read from offset within quirks array
581      * @throws IllegalArgumentException if one of the quirks is out of range
582      */
addQuirks(final int[] quirks, final int offset, final int len)583     public final void addQuirks(final int[] quirks, final int offset, final int len) throws IllegalArgumentException {
584         int bitmask = 0;
585         if( !( 0 <= offset + len && offset + len <= quirks.length ) ) {
586             throw new IllegalArgumentException("offset and len out of bounds: offset "+offset+", len "+len+", array-len "+quirks.length);
587         }
588         for(int i=offset; i<offset+len; i++) {
589             final int quirk = quirks[i];
590             validateQuirk(quirk);
591             bitmask |= 1 << quirk;
592         }
593         _bitmask |= bitmask;
594     }
595 
596     /**
597      * @param quirks valid GLRendererQuirks to be added
598      */
addQuirks(final GLRendererQuirks quirks)599     public final void addQuirks(final GLRendererQuirks quirks) {
600         _bitmask |= quirks._bitmask;
601     }
602 
603     /**
604      * @param quirk the quirk to be tested
605      * @return true if quirk exist, otherwise false
606      * @throws IllegalArgumentException if quirk is out of range
607      */
exist(final int quirk)608     public final boolean exist(final int quirk) throws IllegalArgumentException {
609         validateQuirk(quirk);
610         return 0 != ( ( 1 << quirk )  & _bitmask );
611     }
612 
toString(StringBuilder sb)613     public final StringBuilder toString(StringBuilder sb) {
614         if(null == sb) {
615             sb = new StringBuilder();
616         }
617         sb.append("[");
618         boolean first=true;
619         for(int i=0; i<getCount(); i++) {
620             final int testmask = 1 << i;
621             if( 0 != ( _bitmask & testmask ) ) {
622                 if(!first) { sb.append(", "); }
623                 sb.append(toString(i));
624                 first=false;
625             }
626         }
627         sb.append("]");
628         return sb;
629     }
630 
631     @Override
toString()632     public final String toString() {
633         return toString(null).toString();
634     }
635 
636     /**
637      * @param quirk the quirk to be validated, i.e. whether it is out of range
638      * @throws IllegalArgumentException if quirk is out of range
639      */
validateQuirk(final int quirk)640     public static void validateQuirk(final int quirk) throws IllegalArgumentException {
641         if( !( 0 <= quirk && quirk < getCount() ) ) {
642             throw new IllegalArgumentException("Quirks must be in range [0.."+getCount()+"[, but quirk: "+quirk);
643         }
644     }
645 
646     /**
647      * @param quirk the quirk to be converted to String
648      * @return the String equivalent of this quirk
649      * @throws IllegalArgumentException if quirk is out of range
650      */
toString(final int quirk)651     public static final String toString(final int quirk) throws IllegalArgumentException {
652         validateQuirk(quirk);
653         return _names[quirk];
654     }
655 }
656