From 007f120cd8d33e4231ef4d207b85ed156d1e0c82 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 27 Jul 2014 04:00:26 +0200
Subject: Fixed and Changed NVidia Windows Driver Threaded optimization bug
 workaround of commit 5166d6a6b617ccb15c40fcb8d4eac2800527aa7b

Commit 5166d6a6b617ccb15c40fcb8d4eac2800527aa7b added a workaround
for the NVidia driver 260.99 for Window from 2010-12-11 issue.

[1] The workaround sets a process affinity while JOGL initialization
to mitigate NVidia driver's 'Threaded optimization := On' race conditions.
The process affinity is reset reset after initialization.

[2] The process affinity reset code had a bug, i.e. instead to restore the
original process's affinity mask, we restored the system's default affinity mask.

[3] Further more, there seem to be issues with changing a process affinity mask
regarding the process group.

This patch:
  - Solves issue [2] by using the original process affinity mask

  - Solves issue [3] by allowing a custom
    affinity mode via the property 'jogl.debug.windows.cpu_affinity_mode':

      - 0 - none (default, no affinity required for Windows NV driver >= 266.58 from 2011-01-24)
      - 1 - process affinity (was required w/ Windows NV driver 260.99 from 2010-12-11, see commit 5166d6a6b617ccb15c40fcb8d4eac2800527aa7b)
      - 2 - thread affinity (experimental)

    Hence the workaround is disabled by default,
    since the crash as dicumented in commit 5166d6a6b617ccb15c40fcb8d4eac2800527aa7b
    could not be reproduced with NV driver 266.58 from 2011-01-24.
---
 .../windows/wgl/WindowsWGLDrawableFactory.java     | 197 ++++++++++++++++++---
 1 file changed, 169 insertions(+), 28 deletions(-)

(limited to 'src/jogl')

diff --git a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
index 4d8c85137..42e802a95 100644
--- a/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
+++ b/src/jogl/classes/jogamp/opengl/windows/wgl/WindowsWGLDrawableFactory.java
@@ -68,6 +68,7 @@ import jogamp.nativewindow.windows.GDI;
 import jogamp.nativewindow.windows.GDIDummyUpstreamSurfaceHook;
 import jogamp.nativewindow.windows.GDISurface;
 import jogamp.nativewindow.windows.RegisteredClassFactory;
+import jogamp.opengl.Debug;
 import jogamp.opengl.DesktopGLDynamicLookupHelper;
 import jogamp.opengl.GLContextImpl;
 import jogamp.opengl.GLDrawableFactoryImpl;
@@ -83,11 +84,35 @@ import com.jogamp.opengl.GLExtensions;
 import com.jogamp.opengl.GLRendererQuirks;
 
 public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
+  /**
+   * Property integer value <code>jogl.debug.windows.cpu_affinity_mode</code>:
+   * <ul>
+   *   <li>0 - none (default, no affinity required for Windows NV driver >= 266.58 from 2011-01-24)</li>
+   *   <li>1 - process affinity (was required w/ Windows NV driver 260.99 from 2010-12-11, see commit 5166d6a6b617ccb15c40fcb8d4eac2800527aa7b)</li>
+   *   <li>2 - thread affinity (experimental)</li>
+   * </ul>
+   */
+  private static final int CPU_AFFINITY_MODE = Debug.getIntProperty("jogl.debug.windows.cpu_affinity_mode", true, 0);
+
   private static DesktopGLDynamicLookupHelper windowsWGLDynamicLookupHelper = null;
 
+  private final CPUAffinity cpuAffinity;
+
   public WindowsWGLDrawableFactory() {
     super();
 
+    switch( CPU_AFFINITY_MODE ) {
+        case 1:
+            cpuAffinity = new WindowsProcessAffinity();
+            break;
+        case 2:
+            cpuAffinity = new WindowsThreadAffinity();
+            break;
+        default:
+            cpuAffinity = new NopCPUAffinity();
+            break;
+    }
+
     synchronized(WindowsWGLDrawableFactory.class) {
         if( null == windowsWGLDynamicLookupHelper ) {
             windowsWGLDynamicLookupHelper = AccessController.doPrivileged(new PrivilegedAction<DesktopGLDynamicLookupHelper>() {
@@ -168,45 +193,23 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
       return windowsWGLDynamicLookupHelper;
   }
 
+  /* pp */ static String toHexString(final long l) { return "0x"+Long.toHexString(l); }
+
   private WindowsGraphicsDevice defaultDevice;
   private SharedResourceRunner sharedResourceRunner;
   private HashMap<String /*connection*/, SharedResourceRunner.Resource> sharedMap;
 
-  private long processAffinityChanges = 0;
-  private final PointerBuffer procMask = PointerBuffer.allocateDirect(1);
-  private final PointerBuffer sysMask = PointerBuffer.allocateDirect(1);
-
   @Override
   protected void enterThreadCriticalZone() {
-    synchronized (sysMask) {
-        if( 0 == processAffinityChanges) {
-            final long pid = GDI.GetCurrentProcess();
-            if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) {
-                if(DEBUG) {
-                    System.err.println("WindowsWGLDrawableFactory.enterThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName());
-                    // Thread.dumpStack();
-                }
-                processAffinityChanges = pid;
-                GDI.SetProcessAffinityMask(pid, 1);
-            }
-        }
+    synchronized (cpuAffinity) {
+        cpuAffinity.set(1);
     }
   }
 
   @Override
   protected void leaveThreadCriticalZone() {
-    synchronized (sysMask) {
-        if( 0 != processAffinityChanges) {
-            final long pid = GDI.GetCurrentProcess();
-            if( pid != processAffinityChanges) {
-                throw new GLException("PID doesn't match: set PID 0x" + Long.toHexString(processAffinityChanges) +
-                                                       " this PID 0x" + Long.toHexString(pid) );
-            }
-            if(DEBUG) {
-                System.err.println("WindowsWGLDrawableFactory.leaveThreadCriticalZone() - 0x" + Long.toHexString(pid) + " - " + getThreadName());
-            }
-            GDI.SetProcessAffinityMask(pid, sysMask.get(0));
-        }
+    synchronized (cpuAffinity) {
+        cpuAffinity.reset();
     }
   }
 
@@ -598,4 +601,142 @@ public class WindowsWGLDrawableFactory extends GLDrawableFactoryImpl {
     GDI.SetDeviceGammaRamp(screenDC, originalGammaRamp);
     GDI.ReleaseDC(0, screenDC);
   }
+
+  static interface CPUAffinity {
+      boolean set(final int newAffinity);
+      boolean reset();
+  }
+  static final class WindowsThreadAffinity implements CPUAffinity {
+      private long threadHandle;
+      private long threadOrigAffinity;
+      private long threadNewAffinity;
+      public WindowsThreadAffinity() {
+          threadHandle = 0;
+          threadOrigAffinity = 0;
+          threadNewAffinity = 0;
+      }
+      @Override
+      public boolean set(final int newAffinity) {
+          final long tid = GDI.GetCurrentThread();
+          if( 0 != threadHandle ) {
+              throw new IllegalStateException("Affinity already set");
+          }
+          final long threadLastAffinity = GDI.SetThreadAffinityMask(tid, newAffinity);
+          final int werr = GDI.GetLastError();
+          final boolean res;
+          if( 0 != threadLastAffinity ) {
+              res = true;
+              this.threadHandle = tid;
+              this.threadNewAffinity = newAffinity;
+              this.threadOrigAffinity = threadLastAffinity;
+          } else {
+              res = false;
+          }
+          if(DEBUG) {
+              System.err.println("WindowsThreadAffinity.set() - tid " + toHexString(tid) + " - " + getThreadName() +
+                      ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(threadOrigAffinity) + " -> " + toHexString(newAffinity));
+          }
+          return res;
+      }
+      @Override
+      public boolean reset() {
+          if( 0 == threadHandle ) {
+              return true;
+          }
+          final long tid = GDI.GetCurrentThread();
+          if( tid != threadHandle) {
+              throw new IllegalStateException("TID doesn't match: set TID " + toHexString(threadHandle) +
+                                                               " this TID " + toHexString(tid) );
+          }
+          final long preThreadAffinity = GDI.SetThreadAffinityMask(threadHandle, threadOrigAffinity);
+          final boolean res = 0 != preThreadAffinity;
+          if(DEBUG) {
+              System.err.println("WindowsThreadAffinity.reset() - tid " + toHexString(threadHandle) + " - " + getThreadName() +
+                      ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: "+toHexString(threadNewAffinity)+" -> orig "+ toHexString(threadOrigAffinity));
+          }
+          this.threadHandle = 0;
+          this.threadNewAffinity = this.threadOrigAffinity;
+          return res;
+      }
+  }
+  static final class WindowsProcessAffinity implements CPUAffinity {
+      private long processHandle;
+      private long newAffinity;
+      private final PointerBuffer procMask;
+      private final PointerBuffer sysMask;
+
+      public WindowsProcessAffinity() {
+          processHandle = 0;
+          newAffinity = 0;
+          procMask = PointerBuffer.allocateDirect(1);
+          sysMask = PointerBuffer.allocateDirect(1);
+      }
+      @Override
+      public boolean set(final int newAffinity) {
+          if( 0 != processHandle ) {
+              throw new IllegalStateException("Affinity already set");
+          }
+          final long pid = GDI.GetCurrentProcess();
+          final boolean res;
+          if ( GDI.GetProcessAffinityMask(pid, procMask, sysMask) ) {
+              if( GDI.SetProcessAffinityMask(pid, newAffinity) ) {
+                  this.processHandle = pid;
+                  this.newAffinity = newAffinity;
+                  res = true;
+              } else {
+                  res = false;
+              }
+              if(DEBUG) {
+                  System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() +
+                          ": OK "+res+" (werr "+GDI.GetLastError()+"), Affinity: procMask "+ toHexString(procMask.get(0)) + ", sysMask "+ toHexString(sysMask.get(0)) +
+                          " -> "+toHexString(newAffinity));
+              }
+          } else {
+              if(DEBUG) {
+                  System.err.println("WindowsProcessAffinity.set() - pid " + toHexString(pid) + " - " + getThreadName() +
+                          ": Error, could not GetProcessAffinityMask, werr "+GDI.GetLastError());
+              }
+              res = false;
+          }
+          return res;
+      }
+      @Override
+      public boolean reset() {
+          if( 0 == processHandle ) {
+              return true;
+          }
+          final long pid = GDI.GetCurrentProcess();
+          if( pid != processHandle) {
+              throw new IllegalStateException("PID doesn't match: set PID " + toHexString(processHandle) +
+                                                               " this PID " + toHexString(pid) );
+          }
+          final long origProcAffinity = procMask.get(0);
+          final boolean res = GDI.SetProcessAffinityMask(processHandle, origProcAffinity);
+          if(DEBUG) {
+              final int werr = GDI.GetLastError();
+              System.err.println("WindowsProcessAffinity.reset() - pid " + toHexString(processHandle) + " - " + getThreadName() +
+                      ": OK "+res+" (werr "+werr+"), Affinity: "+toHexString(newAffinity)+" -> procMask "+ toHexString(origProcAffinity));
+          }
+          this.processHandle = 0;
+          this.newAffinity = origProcAffinity;
+          return res;
+      }
+  }
+  static final class NopCPUAffinity implements CPUAffinity {
+      public NopCPUAffinity() { }
+      @Override
+      public boolean set(final int newAffinity) {
+          if(DEBUG) {
+              System.err.println("NopCPUAffinity.set() - " + getThreadName());
+          }
+          return false;
+      }
+      @Override
+      public boolean reset() {
+          if(DEBUG) {
+              System.err.println("NopCPUAffinity.reset() - " + getThreadName());
+          }
+          return false;
+      }
+  }
 }
-- 
cgit v1.2.3