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