1 /*
2  * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40 
41 package jogamp.opengl.windows.wgl;
42 
43 import java.nio.Buffer;
44 import java.nio.ShortBuffer;
45 import java.security.AccessController;
46 import java.security.PrivilegedAction;
47 import java.util.List;
48 
49 import com.jogamp.nativewindow.AbstractGraphicsConfiguration;
50 import com.jogamp.nativewindow.AbstractGraphicsDevice;
51 import com.jogamp.nativewindow.AbstractGraphicsScreen;
52 import com.jogamp.nativewindow.DefaultGraphicsScreen;
53 import com.jogamp.nativewindow.NativeSurface;
54 import com.jogamp.nativewindow.ProxySurface;
55 import com.jogamp.nativewindow.UpstreamSurfaceHook;
56 import com.jogamp.opengl.GLCapabilities;
57 import com.jogamp.opengl.GLCapabilitiesChooser;
58 import com.jogamp.opengl.GLCapabilitiesImmutable;
59 import com.jogamp.opengl.GLContext;
60 import com.jogamp.opengl.GLDrawable;
61 import com.jogamp.opengl.GLException;
62 import com.jogamp.opengl.GLProfile;
63 
64 import jogamp.nativewindow.WrappedSurface;
65 import jogamp.nativewindow.windows.GDI;
66 import jogamp.nativewindow.windows.GDIDummyUpstreamSurfaceHook;
67 import jogamp.nativewindow.windows.GDISurface;
68 import jogamp.nativewindow.windows.RegisteredClassFactory;
69 import jogamp.opengl.Debug;
70 import jogamp.opengl.DesktopGLDynamicLookupHelper;
71 import jogamp.opengl.GLContextImpl;
72 import jogamp.opengl.GLDrawableFactoryImpl;
73 import jogamp.opengl.GLDrawableImpl;
74 import jogamp.opengl.GLDynamicLookupHelper;
75 import jogamp.opengl.GLGraphicsConfigurationUtil;
76 import jogamp.opengl.SharedResourceRunner;
77 
78 import com.jogamp.common.nio.PointerBuffer;
79 import com.jogamp.common.util.PropertyAccess;
80 import com.jogamp.common.util.ReflectionUtil;
81 import com.jogamp.nativewindow.GenericUpstreamSurfacelessHook;
82 import com.jogamp.nativewindow.windows.WindowsGraphicsDevice;
83 import com.jogamp.opengl.GLExtensions;
84 import com.jogamp.opengl.GLRendererQuirks;
85 
86 public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
87   private static final boolean DEBUG_SHAREDCTX = DEBUG  || GLContext.DEBUG;
88 
89   /**
90    * Bug 1036: NVidia Windows Driver 'Threaded optimization' workaround.
91    * <p>
92    * https://jogamp.org/bugzilla/show_bug.cgi?id=1036
93    * </p>
94    * <p>
95    * Since NV driver 260.99 from 2010-12-11 a 'Threaded optimization' feature has been introduced.
96    * The driver spawns off a dedicated thread to off-load certain OpenGL tasks from the calling thread
97    * to perform them async and off-thread.
98    * </p>
99    * <p>
100    * If 'Threaded optimization' is manually enabled 'on', the driver may crash with JOGL's consistent
101    * multi-threaded usage - this is a driver bug.
102    * </p>
103    * <p>
104    * If 'Threaded optimization' is manually disabled 'off', the driver always works correctly.
105    * </p>
106    * <p>
107    * 'Threaded optimization' default setting is 'auto' and the driver may crash without this workaround.
108    * </p>
109    * <p>
110    * If setting the process affinity to '1' (1st CPU) while initialization and launching
111    * the  {@link SharedResourceRunner}, the driver does not crash anymore in 'auto' mode.
112    * This might be either because the driver does not enable 'Threaded optimization'
113    * or because the driver's worker thread is bound to the same CPU.
114    * </p>
115    * <p>
116    * Property integer value <code>jogl.windows.cpu_affinity_mode</code>:
117    * <ul>
118    *   <li>0 - none (no affinity, may cause driver crash with 'Threaded optimization' = ['auto', 'on'])</li>
119    *   <li>1 - process affinity (default, workaround for driver crash for 'Threaded optimization' = 'auto', still crashes if set to 'on')</li>
120    * </ul>
121    * </p>
122    * <p>
123    * Test case reproducing the crash reliable is: com.jogamp.opengl.test.junit.jogl.caps.TestTranslucencyNEWT<br>
124    * (don't ask why ..)
125    * </p>
126    */
127   private static final int CPU_AFFINITY_MODE;
128 
129   static {
Debug.initSingleton()130       Debug.initSingleton();
131       CPU_AFFINITY_MODE = PropertyAccess.getIntProperty("jogl.windows.cpu_affinity_mode", true, 1);
132   }
133 
134   private static DesktopGLDynamicLookupHelper windowsWGLDynamicLookupHelper = null;
135 
136   private final CPUAffinity cpuAffinity;
137 
WindowsWGLDrawableFactory()138   public WindowsWGLDrawableFactory() {
139     super();
140 
141     switch( CPU_AFFINITY_MODE ) {
142         case 0:
143             cpuAffinity = new NopCPUAffinity();
144             break;
145         /**
146          * Doesn't work !
147         case 2:
148             cpuAffinity = new WindowsThreadAffinity();
149             break;
150          */
151         default:
152             cpuAffinity = new WindowsProcessAffinity();
153             break;
154     }
155 
156     synchronized(WindowsWGLDrawableFactory.class) {
157         if( null == windowsWGLDynamicLookupHelper ) {
158             windowsWGLDynamicLookupHelper = AccessController.doPrivileged(new PrivilegedAction<DesktopGLDynamicLookupHelper>() {
159                 @Override
160                 public DesktopGLDynamicLookupHelper run() {
161                     DesktopGLDynamicLookupHelper tmp;
162                     try {
163                         tmp = new DesktopGLDynamicLookupHelper(new WindowsWGLDynamicLibraryBundleInfo());
164                         if(null!=tmp && tmp.isLibComplete()) {
165                             WGL.getWGLProcAddressTable().reset(tmp);
166                         }
167                     } catch (final Exception ex) {
168                         tmp = null;
169                         if(DEBUG) {
170                             ex.printStackTrace();
171                         }
172                     }
173                     return tmp;
174                 }
175             } );
176         }
177     }
178 
179     defaultDevice = new WindowsGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT);
180 
181     if(null!=windowsWGLDynamicLookupHelper) {
182         // Register our GraphicsConfigurationFactory implementations
183         // The act of constructing them causes them to be registered
184         WindowsWGLGraphicsConfigurationFactory.registerFactory();
185         if(GLProfile.isAWTAvailable()) {
186             try {
187               ReflectionUtil.callStaticMethod("jogamp.opengl.windows.wgl.awt.WindowsAWTWGLGraphicsConfigurationFactory",
188                                               "registerFactory", null, null, getClass().getClassLoader());
189             } catch (final Exception jre) { /* n/a .. */ }
190         }
191 
192         // Init shared resources off thread
193         // Will be released via ShutdownHook
194         sharedResourceImplementation = new SharedResourceImplementation();
195         sharedResourceRunner = new SharedResourceRunner(sharedResourceImplementation);
196         sharedResourceRunner.start();
197     }
198   }
199 
200   @Override
isComplete()201   protected final boolean isComplete() {
202       return null != windowsWGLDynamicLookupHelper;
203   }
204 
205 
206   @Override
shutdownImpl()207   protected final void shutdownImpl() {
208     if( DEBUG ) {
209         System.err.println("WindowsWGLDrawableFactory.shutdown");
210     }
211     if(null != sharedResourceRunner) {
212         sharedResourceRunner.stop();
213         sharedResourceRunner = null;
214     }
215     if(null != sharedResourceImplementation) {
216         sharedResourceImplementation.clear();
217         sharedResourceImplementation = null;
218     }
219     defaultDevice = null;
220     /**
221      * Pulling away the native library may cause havoc ..
222      *
223        windowsWGLDynamicLookupHelper.destroy();
224      */
225     windowsWGLDynamicLookupHelper = null;
226 
227     RegisteredClassFactory.shutdownSharedClasses();
228   }
229 
230   @Override
getGLDynamicLookupHelper(final int majorVersion, final int contextOptions)231   public final GLDynamicLookupHelper getGLDynamicLookupHelper(final int majorVersion, final int contextOptions) {
232       return windowsWGLDynamicLookupHelper;
233   }
234 
toHexString(final long l)235   /* pp */ static String toHexString(final long l) { return "0x"+Long.toHexString(l); }
236 
237   private WindowsGraphicsDevice defaultDevice;
238   private SharedResourceImplementation sharedResourceImplementation;
239   private SharedResourceRunner sharedResourceRunner;
240 
241   @Override
enterThreadCriticalZone()242   protected void enterThreadCriticalZone() {
243     synchronized (cpuAffinity) {
244         cpuAffinity.set(1);
245     }
246   }
247 
248   @Override
leaveThreadCriticalZone()249   protected void leaveThreadCriticalZone() {
250     synchronized (cpuAffinity) {
251         cpuAffinity.reset();
252     }
253   }
254 
255   static class SharedResource implements SharedResourceRunner.Resource {
256       private final boolean hasARBPixelFormat;
257       private final boolean hasARBMultisample;
258       private final boolean hasARBPBuffer;
259       private final boolean hasARBReadDrawable;
260       private WindowsGraphicsDevice device;
261       private AbstractGraphicsScreen screen;
262       private GLDrawableImpl drawable;
263       private GLContextImpl context;
264 
SharedResource(final WindowsGraphicsDevice dev, final AbstractGraphicsScreen scrn, final GLDrawableImpl draw, final GLContextImpl ctx, final boolean arbPixelFormat, final boolean arbMultisample, final boolean arbPBuffer, final boolean arbReadDrawable)265       SharedResource(final WindowsGraphicsDevice dev, final AbstractGraphicsScreen scrn, final GLDrawableImpl draw, final GLContextImpl ctx,
266                      final boolean arbPixelFormat, final boolean arbMultisample, final boolean arbPBuffer, final boolean arbReadDrawable) {
267           device = dev;
268           screen = scrn;
269           drawable = draw;
270           context = ctx;
271           hasARBPixelFormat = arbPixelFormat;
272           hasARBMultisample = arbMultisample;
273           hasARBPBuffer = arbPBuffer;
274           hasARBReadDrawable = arbReadDrawable;
275       }
276 
277       @Override
isAvailable()278       public final boolean isAvailable() {
279           return null != context;
280       }
281       @Override
getDevice()282       final public AbstractGraphicsDevice getDevice() { return device; }
283       @Override
getScreen()284       final public AbstractGraphicsScreen getScreen() { return screen; }
285       @Override
getDrawable()286       final public GLDrawableImpl getDrawable() { return drawable; }
287       @Override
getContext()288       final public GLContextImpl getContext() { return context; }
289       @Override
getRendererQuirks(final GLProfile glp)290       public GLRendererQuirks getRendererQuirks(final GLProfile glp) {
291           return null != context ? context.getRendererQuirks() : null;
292       }
293 
hasARBPixelFormat()294       final boolean hasARBPixelFormat() { return hasARBPixelFormat; }
hasARBMultisample()295       final boolean hasARBMultisample() { return hasARBMultisample; }
hasARBPBuffer()296       final boolean hasARBPBuffer() { return hasARBPBuffer; }
hasReadDrawable()297       final boolean hasReadDrawable() { return hasARBReadDrawable; }
298   }
299 
300   class SharedResourceImplementation extends SharedResourceRunner.AImplementation {
301         @Override
isDeviceSupported(final AbstractGraphicsDevice device)302         public boolean isDeviceSupported(final AbstractGraphicsDevice device) {
303             return true;
304         }
305 
306         @Override
createSharedResource(final AbstractGraphicsDevice adevice)307         public SharedResourceRunner.Resource createSharedResource(final AbstractGraphicsDevice adevice) {
308             final WindowsGraphicsDevice device = new WindowsGraphicsDevice(adevice.getConnection(), adevice.getUnitID());
309             GLContextImpl context = null;
310             boolean contextIsCurrent = false;
311             device.lock();
312             try {
313                 final AbstractGraphicsScreen absScreen = new DefaultGraphicsScreen(device, 0);
314                 final GLProfile glp = GLProfile.get(device, GLProfile.GL_PROFILE_LIST_MIN_DESKTOP, false);
315                 if (null == glp) {
316                     throw new GLException("Couldn't get default GLProfile for device: "+device);
317                 }
318                 final GLCapabilitiesImmutable caps = new GLCapabilities(glp);
319                 final GLDrawableImpl drawable = createOnscreenDrawableImpl(createDummySurfaceImpl(device, false, caps, caps, null, 64, 64));
320                 drawable.setRealized(true);
321 
322                 context  = (GLContextImpl) drawable.createContext(null);
323                 if (null == context) {
324                     throw new GLException("Couldn't create shared context for drawable: "+drawable);
325                 }
326                 contextIsCurrent = GLContext.CONTEXT_NOT_CURRENT != context.makeCurrent();
327 
328                 final boolean allowsSurfacelessCtx;
329                 final boolean hasARBPixelFormat;
330                 final boolean hasARBMultisample;
331                 final boolean hasARBPBuffer;
332                 final boolean hasARBReadDrawableAvailable;
333                 if( contextIsCurrent ) {
334                     if( context.getGLVersionNumber().compareTo(GLContext.Version3_0) >= 0 ) {
335                         allowsSurfacelessCtx = probeSurfacelessCtx(context, true /* restoreDrawable */);
336                     } else {
337                         setNoSurfacelessCtxQuirk(context);
338                         allowsSurfacelessCtx = false;
339                     }
340                     hasARBPixelFormat = context.isExtensionAvailable(WGL_ARB_pixel_format);
341                     hasARBMultisample = context.isExtensionAvailable(WGL_ARB_multisample);
342                     hasARBPBuffer = context.isExtensionAvailable(GLExtensions.ARB_pbuffer);
343                     hasARBReadDrawableAvailable = context.isExtensionAvailable(WGL_ARB_make_current_read) &&
344                                                   context.isFunctionAvailable(wglMakeContextCurrent);
345                 } else {
346                     allowsSurfacelessCtx = false;
347                     hasARBPixelFormat = false;
348                     hasARBMultisample = false;
349                     hasARBPBuffer = false;
350                     hasARBReadDrawableAvailable = false;
351                 }
352                 if ( DEBUG_SHAREDCTX ) {
353                     System.err.println("SharedDevice:  " + device);
354                     System.err.println("SharedScreen:  " + absScreen);
355                     System.err.println("SharedContext: " + context + ", madeCurrent " + contextIsCurrent);
356                     System.err.println("  allowsSurfacelessCtx "+allowsSurfacelessCtx);
357                     System.err.println("pixelformat:   " + hasARBPixelFormat);
358                     System.err.println("multisample:   " + hasARBMultisample);
359                     System.err.println("pbuffer:       " + hasARBPBuffer);
360                     System.err.println("readDrawable:  " + hasARBReadDrawableAvailable);
361                 }
362                 return new SharedResource(device, absScreen, drawable, context,
363                                           hasARBPixelFormat, hasARBMultisample,
364                                           hasARBPBuffer, hasARBReadDrawableAvailable);
365             } catch (final Throwable t) {
366                 throw new GLException("WindowsWGLDrawableFactory - Could not initialize shared resources for "+adevice, t);
367             } finally {
368                 if ( contextIsCurrent ) {
369                     context.release();
370                 }
371                 device.unlock();
372             }
373         }
374 
375         @Override
releaseSharedResource(final SharedResourceRunner.Resource shared)376         public void releaseSharedResource(final SharedResourceRunner.Resource shared) {
377             final SharedResource sr = (SharedResource) shared;
378             if ( DEBUG_SHAREDCTX ) {
379               System.err.println("Shutdown Shared:");
380               System.err.println("Device  : " + sr.device);
381               System.err.println("Screen  : " + sr.screen);
382               System.err.println("Drawable: " + sr.drawable);
383               System.err.println("CTX     : " + sr.context);
384             }
385 
386             if (null != sr.context) {
387                 // may cause JVM SIGSEGV: sharedContext.destroy();
388                 sr.context.destroy(); // will also pull the dummy MutableSurface
389                 sr.context = null;
390             }
391 
392             if (null != sr.drawable) {
393                 sr.drawable.setRealized(false);
394                 sr.drawable = null;
395             }
396 
397             if (null != sr.screen) {
398                 sr.screen = null;
399             }
400 
401             if (null != sr.device) {
402                 sr.device.close();
403                 sr.device = null;
404             }
405         }
406   }
407 
408   @Override
getDefaultDevice()409   public final AbstractGraphicsDevice getDefaultDevice() {
410       return defaultDevice;
411   }
412 
413   @Override
getIsDeviceCompatible(final AbstractGraphicsDevice device)414   public final boolean getIsDeviceCompatible(final AbstractGraphicsDevice device) {
415       if(null!=windowsWGLDynamicLookupHelper && device instanceof WindowsGraphicsDevice) {
416           return true;
417       }
418       return false;
419   }
420 
421   final static String WGL_ARB_pbuffer      = "WGL_ARB_pbuffer";
422   final static String WGL_ARB_pixel_format = "WGL_ARB_pixel_format";
423   final static String WGL_ARB_multisample = "WGL_ARB_multisample";
424   final static String WGL_NV_float_buffer = "WGL_NV_float_buffer";
425   final static String WGL_ARB_make_current_read = "WGL_ARB_make_current_read";
426   final static String wglMakeContextCurrent = "wglMakeContextCurrent";
427 
428   @Override
getSharedResourceThread()429   protected final Thread getSharedResourceThread() {
430     return sharedResourceRunner.start();
431   }
432 
433   @Override
getOrCreateSharedResourceImpl(final AbstractGraphicsDevice device)434   protected final SharedResource getOrCreateSharedResourceImpl(final AbstractGraphicsDevice device) {
435     return (SharedResource) sharedResourceRunner.getOrCreateShared(device);
436   }
437 
getOrCreateSharedDrawable(final AbstractGraphicsDevice device)438   protected final WindowsWGLDrawable getOrCreateSharedDrawable(final AbstractGraphicsDevice device) {
439     final SharedResourceRunner.Resource sr = getOrCreateSharedResourceImpl(device);
440     if(null!=sr) {
441         return (WindowsWGLDrawable) sr.getDrawable();
442     }
443     return null;
444   }
445 
446   /**
447    * {@inheritDoc}
448    * <p>
449    * This factory always supports native desktop OpenGL profiles.
450    * </p>
451    */
452   @Override
hasOpenGLDesktopSupport()453   public final boolean hasOpenGLDesktopSupport() { return true; }
454 
455   /**
456    * {@inheritDoc}
457    * <p>
458    * This factory never supports native GLES profiles.
459    * </p>
460    */
461   @Override
hasOpenGLESSupport()462   public final boolean hasOpenGLESSupport() { return false; }
463 
464   /**
465    * {@inheritDoc}
466    * <p>
467    * Always returns true.
468    * </p>
469    */
470   @Override
hasMajorMinorCreateContextARB()471   public final boolean hasMajorMinorCreateContextARB() { return true; }
472 
473   @Override
getAvailableCapabilitiesImpl(final AbstractGraphicsDevice device)474   protected List<GLCapabilitiesImmutable> getAvailableCapabilitiesImpl(final AbstractGraphicsDevice device) {
475     return WindowsWGLGraphicsConfigurationFactory.getAvailableCapabilities(this, device);
476   }
477 
478   @Override
createOnscreenDrawableImpl(final NativeSurface target)479   protected final GLDrawableImpl createOnscreenDrawableImpl(final NativeSurface target) {
480     if (target == null) {
481       throw new IllegalArgumentException("Null target");
482     }
483     return new WindowsOnscreenWGLDrawable(this, target);
484   }
485 
486   @Override
createOffscreenDrawableImpl(final NativeSurface target)487   protected final GLDrawableImpl createOffscreenDrawableImpl(final NativeSurface target) {
488     if (target == null) {
489       throw new IllegalArgumentException("Null target");
490     }
491     final AbstractGraphicsConfiguration config = target.getGraphicsConfiguration();
492     final GLCapabilitiesImmutable chosenCaps = (GLCapabilitiesImmutable) config.getChosenCapabilities();
493     if(!chosenCaps.isPBuffer()) {
494         return WindowsBitmapWGLDrawable.create(this, target);
495     }
496 
497     // PBuffer GLDrawable Creation
498     GLDrawableImpl pbufferDrawable;
499     final AbstractGraphicsDevice device = config.getScreen().getDevice();
500 
501     /**
502      * Similar to ATI Bug https://bugzilla.mozilla.org/show_bug.cgi?id=486277,
503      * we need to have a context current on the same Display to create a PBuffer.
504      */
505     final SharedResource sr = getOrCreateSharedResourceImpl(device);
506     if(null!=sr) {
507         final GLContext lastContext = GLContext.getCurrent();
508         if (lastContext != null) {
509             lastContext.release();
510         }
511         sr.context.makeCurrent();
512         try {
513             pbufferDrawable = new WindowsPbufferWGLDrawable(WindowsWGLDrawableFactory.this, target);
514         } finally {
515             sr.context.release();
516             if (lastContext != null) {
517               lastContext.makeCurrent();
518             }
519         }
520     } else {
521         pbufferDrawable = new WindowsPbufferWGLDrawable(WindowsWGLDrawableFactory.this, target);
522     }
523     return pbufferDrawable;
524   }
525 
526   /**
527    * @return 1 if read drawable extension is available, 0 if not
528    *           and -1 if undefined yet, ie no shared device exist at this point.
529    */
isReadDrawableAvailable(final AbstractGraphicsDevice device)530   public final int isReadDrawableAvailable(final AbstractGraphicsDevice device) {
531     final SharedResource sr = getOrCreateSharedResourceImpl( ( null != device ) ? device : defaultDevice );
532     if(null!=sr) {
533         return sr.hasReadDrawable() ? 1 : 0 ;
534     }
535     return -1; // undefined
536   }
537 
538   @Override
canCreateGLPbuffer(final AbstractGraphicsDevice device, final GLProfile glp)539   public final boolean canCreateGLPbuffer(final AbstractGraphicsDevice device, final GLProfile glp) {
540     final SharedResource sr = getOrCreateSharedResourceImpl( ( null != device ) ? device : defaultDevice );
541     if(null!=sr) {
542         return sr.hasARBPBuffer();
543     }
544     return false;
545   }
546 
547   @Override
createMutableSurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstreamHook)548   protected final ProxySurface createMutableSurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice,
549                                                         final GLCapabilitiesImmutable capsChosen, final GLCapabilitiesImmutable capsRequested,
550                                                         final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstreamHook) {
551     final WindowsGraphicsDevice device;
552     if(createNewDevice || !(deviceReq instanceof WindowsGraphicsDevice)) {
553         device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID());
554     } else {
555         device = (WindowsGraphicsDevice)deviceReq;
556     }
557     final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0);
558     final WindowsWGLGraphicsConfiguration config = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsChosen, capsRequested, chooser, screen);
559     if(null == config) {
560         throw new GLException("Choosing GraphicsConfiguration failed w/ "+capsChosen+" on "+screen);
561     }
562     return new WrappedSurface(config, 0, upstreamHook, createNewDevice);
563   }
564 
565   @Override
createDummySurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height)566   public final ProxySurface createDummySurfaceImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice,
567                                                    GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height) {
568     final WindowsGraphicsDevice device;
569     if( createNewDevice || !(deviceReq instanceof WindowsGraphicsDevice) ) {
570         device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID());
571     } else {
572         device = (WindowsGraphicsDevice)deviceReq;
573     }
574     final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, 0);
575     chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps);
576     final WindowsWGLGraphicsConfiguration config = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(chosenCaps, requestedCaps, chooser, screen);
577     if(null == config) {
578         throw new GLException("Choosing GraphicsConfiguration failed w/ "+chosenCaps+" on "+screen);
579     }
580     return new GDISurface(config, 0, new GDIDummyUpstreamSurfaceHook(width, height), createNewDevice);
581   }
582 
583   @Override
createSurfacelessImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice, GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height)584   public final ProxySurface createSurfacelessImpl(final AbstractGraphicsDevice deviceReq, final boolean createNewDevice,
585           GLCapabilitiesImmutable chosenCaps, final GLCapabilitiesImmutable requestedCaps, final GLCapabilitiesChooser chooser, final int width, final int height) {
586     chosenCaps = GLGraphicsConfigurationUtil.fixOnscreenGLCapabilities(chosenCaps);
587     return createMutableSurfaceImpl(deviceReq, createNewDevice, chosenCaps, requestedCaps, chooser, new GenericUpstreamSurfacelessHook(width, height));
588   }
589 
590   @Override
createProxySurfaceImpl(final AbstractGraphicsDevice deviceReq, final int screenIdx, final long windowHandle, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstream)591   protected final ProxySurface createProxySurfaceImpl(final AbstractGraphicsDevice deviceReq, final int screenIdx, final long windowHandle, final GLCapabilitiesImmutable capsRequested, final GLCapabilitiesChooser chooser, final UpstreamSurfaceHook upstream) {
592     final WindowsGraphicsDevice device = new WindowsGraphicsDevice(deviceReq.getConnection(), deviceReq.getUnitID());
593     final AbstractGraphicsScreen screen = new DefaultGraphicsScreen(device, screenIdx);
594     final WindowsWGLGraphicsConfiguration cfg = WindowsWGLGraphicsConfigurationFactory.chooseGraphicsConfigurationStatic(capsRequested, capsRequested, chooser, screen);
595     return new GDISurface(cfg, windowHandle, upstream, true);
596   }
597 
598   @Override
createExternalGLContextImpl()599   protected final GLContext createExternalGLContextImpl() {
600     return WindowsExternalWGLContext.create(this, null);
601   }
602 
603   @Override
canCreateExternalGLDrawable(final AbstractGraphicsDevice device)604   public final boolean canCreateExternalGLDrawable(final AbstractGraphicsDevice device) {
605     return true;
606   }
607 
608   @Override
createExternalGLDrawableImpl()609   protected final GLDrawable createExternalGLDrawableImpl() {
610     return WindowsExternalWGLDrawable.create(this, null);
611   }
612 
wglGetLastError()613   static String wglGetLastError() {
614     final long err = GDI.GetLastError();
615     String detail = null;
616     switch ((int) err) {
617       case GDI.ERROR_SUCCESS:              detail = "ERROR_SUCCESS";                    break;
618       case GDI.ERROR_INVALID_PIXEL_FORMAT: detail = "ERROR_INVALID_PIXEL_FORMAT";       break;
619       case GDI.ERROR_NO_SYSTEM_RESOURCES:  detail = "ERROR_NO_SYSTEM_RESOURCES";        break;
620       case GDI.ERROR_INVALID_DATA:         detail = "ERROR_INVALID_DATA";               break;
621       case GDI.ERROR_PROC_NOT_FOUND:       detail = "ERROR_PROC_NOT_FOUND";             break;
622       case GDI.ERROR_INVALID_WINDOW_HANDLE:detail = "ERROR_INVALID_WINDOW_HANDLE";      break;
623       default:                             detail = "(Unknown error code " + err + ")"; break;
624     }
625     return detail;
626   }
627 
628   //------------------------------------------------------
629   // Gamma-related functionality
630   //
631 
632   private static final int GAMMA_RAMP_LENGTH = 256;
633 
634   @Override
getGammaRampLength(final NativeSurface surface)635   protected final int getGammaRampLength(final NativeSurface surface) {
636     return GAMMA_RAMP_LENGTH;
637   }
638 
639   @Override
setGammaRamp(final NativeSurface surface, final float[] ramp)640   protected final boolean setGammaRamp(final NativeSurface surface, final float[] ramp) {
641     final short[] rampData = new short[3 * GAMMA_RAMP_LENGTH];
642     for (int i = 0; i < GAMMA_RAMP_LENGTH; i++) {
643       final short scaledValue = (short) (ramp[i] * 65535);
644       rampData[i] = scaledValue;
645       rampData[i +     GAMMA_RAMP_LENGTH] = scaledValue;
646       rampData[i + 2 * GAMMA_RAMP_LENGTH] = scaledValue;
647     }
648 
649     final long hDC = surface.getSurfaceHandle();
650     if( 0 == hDC ) {
651         return false;
652     }
653     // final long screenDC = GDI.GetDC(0);
654     final boolean res = GDI.SetDeviceGammaRamp(hDC, ShortBuffer.wrap(rampData));
655     // GDI.ReleaseDC(0, screenDC);
656     return res;
657   }
658 
659   @Override
getGammaRamp(final NativeSurface surface)660   protected final Buffer getGammaRamp(final NativeSurface surface) {
661     final ShortBuffer rampData = ShortBuffer.wrap(new short[3 * GAMMA_RAMP_LENGTH]);
662     final long hDC = surface.getSurfaceHandle();
663     if( 0 == hDC ) {
664         return null;
665     }
666     // final long screenDC = GDI.GetDC(0);
667     final boolean res = GDI.GetDeviceGammaRamp(hDC, rampData);
668     // GDI.ReleaseDC(0, screenDC);
669     if (!res) {
670       return null;
671     }
672     return rampData;
673   }
674 
675   @Override
resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp)676   protected final void resetGammaRamp(final NativeSurface surface, final Buffer originalGammaRamp) {
677     if (originalGammaRamp == null) {
678       // getGammaRamp failed earlier
679       return;
680     }
681     final long hDC = surface.getSurfaceHandle();
682     if( 0 == hDC ) {
683         return;
684     }
685     // final long screenDC = GDI.GetDC(0);
686     GDI.SetDeviceGammaRamp(hDC, originalGammaRamp);
687     // GDI.ReleaseDC(0, hDC);
688   }
689 
690   @Override
resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp)691   protected final void resetGammaRamp(final DeviceScreenID deviceScreenID, final Buffer originalGammaRamp) {
692     if (originalGammaRamp == null) {
693       // getGammaRamp failed earlier
694       return;
695     }
696     final long screenDC = GDI.GetDC(0);
697     GDI.SetDeviceGammaRamp(screenDC, originalGammaRamp);
698     GDI.ReleaseDC(0, screenDC);
699   }
700 
701 
702   static interface CPUAffinity {
set(final int newAffinity)703       boolean set(final int newAffinity);
reset()704       boolean reset();
705   }
706   static final class WindowsThreadAffinity implements CPUAffinity {
707       private long threadHandle;
708       private long threadOrigAffinity;
709       private long threadNewAffinity;
WindowsThreadAffinity()710       public WindowsThreadAffinity() {
711           threadHandle = 0;
712           threadOrigAffinity = 0;
713           threadNewAffinity = 0;
714       }
715       @Override
set(final int newAffinity)716       public boolean set(final int newAffinity) {
717           final long tid = GDI.GetCurrentThread();
718           if( 0 != threadHandle ) {
719               throw new IllegalStateException("Affinity already set");
720           }
721           final long threadLastAffinity = GDI.SetThreadAffinityMask(tid, newAffinity);
722           final int werr = GDI.GetLastError();
723           final boolean res;
724           if( 0 != threadLastAffinity ) {
725               res = true;
726               this.threadHandle = tid;
727               this.threadNewAffinity = newAffinity;
728               this.threadOrigAffinity = threadLastAffinity;
729           } else {
730               res = false;
731           }
732           if(DEBUG) {
733               System.err.println("WindowsThreadAffinity.set() - tid " + toHexString(tid) + " - " + getThreadName() +
734                       ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(threadOrigAffinity) + " -> " + toHexString(newAffinity));
735           }
736           return res;
737       }
738       @Override
reset()739       public boolean reset() {
740           if( 0 == threadHandle ) {
741               return true;
742           }
743           final long tid = GDI.GetCurrentThread();
744           if( tid != threadHandle) {
745               throw new IllegalStateException("TID doesn't match: set TID " + toHexString(threadHandle) +
746                                                                " this TID " + toHexString(tid) );
747           }
748           final long preThreadAffinity = GDI.SetThreadAffinityMask(threadHandle, threadOrigAffinity);
749           final boolean res = 0 != preThreadAffinity;
750           if(DEBUG) {
751               System.err.println("WindowsThreadAffinity.reset() - tid " + toHexString(threadHandle) + " - " + getThreadName() +
752                       ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: "+toHexString(threadNewAffinity)+" -> orig "+ toHexString(threadOrigAffinity));
753           }
754           this.threadHandle = 0;
755           this.threadNewAffinity = this.threadOrigAffinity;
756           return res;
757       }
758   }
759   static final class WindowsProcessAffinity implements CPUAffinity {
760       private long processHandle;
761       private long newAffinity;
762       private final PointerBuffer procMask;
763       private final PointerBuffer sysMask;
764 
WindowsProcessAffinity()765       public WindowsProcessAffinity() {
766           processHandle = 0;
767           newAffinity = 0;
768           procMask = PointerBuffer.allocateDirect(1);
769           sysMask = PointerBuffer.allocateDirect(1);
770       }
771       @Override
set(final int newAffinity)772       public boolean set(final int newAffinity) {
773           if( 0 != processHandle ) {
774               throw new IllegalStateException("Affinity already set");
775           }
776           final long pid = GDI.GetCurrentProcess();
777           final boolean res;
778           if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) {
779               if( GDI.SetProcessAffinityMask(pid, newAffinity) ) {
780                   this.processHandle = pid;
781                   this.newAffinity = newAffinity;
782                   res = true;
783               } else {
784                   res = false;
785               }
786               if(DEBUG) {
787                   System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() +
788                           ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: procMask "+ toHexString(procMask.get(0)) + ", sysMask "+ toHexString(sysMask.get(0)) +
789                           " -> "+toHexString(newAffinity));
790               }
791           } else {
792               if(DEBUG) {
793                   System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() +
794                           ": Error, could not GetProcessAffinityMask, werr "+GDI.GetLastError());
795               }
796               res = false;
797           }
798           return res;
799       }
800       @Override
reset()801       public boolean reset() {
802           if( 0 == processHandle ) {
803               return true;
804           }
805           final long pid = GDI.GetCurrentProcess();
806           if( pid != processHandle) {
807               throw new IllegalStateException("PID doesn't match: set PID " + toHexString(processHandle) +
808                                                                " this PID " + toHexString(pid) );
809           }
810           final long origProcAffinity = procMask.get(0);
811           final boolean res = GDI.SetProcessAffinityMask(processHandle, origProcAffinity);
812           if(DEBUG) {
813               final int werr = GDI.GetLastError();
814               System.err.println("WindowsProcessAffinity.reset() - pid " + toHexString(processHandle) + " - " + getThreadName() +
815                       ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(newAffinity)+" -> procMask "+ toHexString(origProcAffinity));
816           }
817           this.processHandle = 0;
818           this.newAffinity = origProcAffinity;
819           return res;
820       }
821   }
822   static final class NopCPUAffinity implements CPUAffinity {
NopCPUAffinity()823       public NopCPUAffinity() { }
824       @Override
set(final int newAffinity)825       public boolean set(final int newAffinity) {
826           if(DEBUG) {
827               System.err.println("NopCPUAffinity.set() - " + getThreadName());
828           }
829           return false;
830       }
831       @Override
reset()832       public boolean reset() {
833           if(DEBUG) {
834               System.err.println("NopCPUAffinity.reset() - " + getThreadName());
835           }
836           return false;
837       }
838   }
839 }
840