ParallelCompile: Parallelize D3D linking

This adds a new linking state to Program. If a Program is in linking
state, on the one hand the foreground thread may continue issuing more
GL calls, and on the other hand the background linking threads may be
accessing Program internally too. Without a proper constraint there
must be conflicts between them. For this purpose, we block any further
GL calls to Program until it's actually linked. In addition, we
prohibit parallel linking an active program, so that ProgramD3D does
not have to worry about such similar conflicts.

Also changes the WorkerThread to support limiting the number of
concurrently running worker threads.

BUG=chromium:849576

Change-Id: I52618647539323f8bf27201320bdf7301c4982e6
Reviewed-on: https://chromium-review.googlesource.com/1127495
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 48fd5fc..3e1f2f5 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -356,7 +356,8 @@
       mDrawFramebufferObserverBinding(this, kDrawFramebufferSubjectIndex),
       mReadFramebufferObserverBinding(this, kReadFramebufferSubjectIndex),
       mScratchBuffer(1000u),
-      mZeroFilledBuffer(1000u)
+      mZeroFilledBuffer(1000u),
+      mThreadPool(nullptr)
 {
     // Needed to solve a Clang warning of unused variables.
     ANGLE_UNUSED_VARIABLE(mSavedArgsType);
@@ -577,6 +578,8 @@
     mState.mFramebuffers->release(this);
     mState.mPipelines->release(this);
 
+    mThreadPool.reset();
+
     mImplementation->onDestroy(this);
 
     return egl::NoError();
@@ -3368,6 +3371,8 @@
         mValidBufferBindings.set(BufferBinding::DrawIndirect);
         mValidBufferBindings.set(BufferBinding::DispatchIndirect);
     }
+
+    mThreadPool = angle::WorkerThreadPool::Create(mExtensions.parallelShaderCompile);
 }
 
 void Context::initWorkarounds()
@@ -5558,8 +5563,20 @@
     Program *programObject = getProgram(program);
     ASSERT(programObject);
     handleError(programObject->link(this));
-    mGLState.onProgramExecutableChange(programObject);
-    mStateCache.onProgramExecutableChange(this);
+
+    // Don't parallel link a program which is active in any GL contexts. With this assumption, we
+    // don't need to worry that:
+    //   1. Draw calls after link use the new executable code or the old one depending on the link
+    //      result.
+    //   2. When a backend program, e.g., ProgramD3D is linking, other backend classes like
+    //      StateManager11, Renderer11, etc., may have a chance to make unexpected calls to
+    //      ProgramD3D.
+    if (programObject->isInUse())
+    {
+        // isLinked() which forces to resolve linking, will be called.
+        mGLState.onProgramExecutableChange(programObject);
+        mStateCache.onProgramExecutableChange(this);
+    }
 }
 
 void Context::releaseShaderCompiler()
@@ -7551,7 +7568,14 @@
 
 void Context::maxShaderCompilerThreads(GLuint count)
 {
+    GLuint oldCount = mGLState.getMaxShaderCompilerThreads();
     mGLState.setMaxShaderCompilerThreads(count);
+    // A count of zero specifies a request for no parallel compiling or linking.
+    if ((oldCount == 0 || count == 0) && (oldCount != 0 || count != 0))
+    {
+        mThreadPool = angle::WorkerThreadPool::Create(count > 0);
+    }
+    mThreadPool->setMaxThreads(count);
 }
 
 bool Context::isGLES1() const