ParallelCompile: Parallelize shader translation

This changes to construct a new ShHandle of compiler for each Shader,
and use it to translate the shader source in a background thread.

Bug: chromium:849576

Change-Id: Ib49952c7292321ee6aa1c5996f8f7927f40d8f04
Reviewed-on: https://chromium-review.googlesource.com/1177195
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Compiler.cpp b/src/libANGLE/Compiler.cpp
index 9540651..7cf4b34 100644
--- a/src/libANGLE/Compiler.cpp
+++ b/src/libANGLE/Compiler.cpp
@@ -19,9 +19,8 @@
 namespace
 {
 
-// Global count of active shader compiler handles. Needed to know when to call sh::Initialize and
-// sh::Finalize.
-size_t activeCompilerHandles = 0;
+// To know when to call sh::Initialize and sh::Finalize.
+size_t gActiveCompilers = 0;
 
 ShShaderSpec SelectShaderSpec(GLint majorVersion, GLint minorVersion, bool isWebGL)
 {
@@ -54,8 +53,7 @@
                              state.getClientMinorVersion(),
                              state.getExtensions().webglCompatibility)),
       mOutputType(mImplementation->getTranslatorOutputType()),
-      mResources(),
-      mShaderCompilers({})
+      mResources()
 {
     ASSERT(state.getClientMajorVersion() == 1 || state.getClientMajorVersion() == 2 ||
            state.getClientMajorVersion() == 3);
@@ -63,6 +61,12 @@
     const gl::Caps &caps             = state.getCaps();
     const gl::Extensions &extensions = state.getExtensions();
 
+    if (gActiveCompilers == 0)
+    {
+        sh::Initialize();
+    }
+    ++gActiveCompilers;
+
     sh::InitBuiltInResources(&mResources);
     mResources.MaxVertexAttribs             = caps.maxVertexAttributes;
     mResources.MaxVertexUniformVectors      = caps.maxVertexUniformVectors;
@@ -161,50 +165,111 @@
 
 Compiler::~Compiler()
 {
-    for (ShaderType shaderType : AllShaderTypes())
+    for (auto &pool : mPools)
     {
-        ShHandle compilerHandle = mShaderCompilers[shaderType];
-        if (compilerHandle)
+        for (ShCompilerInstance &instance : pool)
         {
-            sh::Destruct(compilerHandle);
-            mShaderCompilers[shaderType] = nullptr;
-
-            ASSERT(activeCompilerHandles > 0);
-            activeCompilerHandles--;
+            instance.destroy();
         }
     }
-
-    if (activeCompilerHandles == 0)
+    --gActiveCompilers;
+    if (gActiveCompilers == 0)
     {
         sh::Finalize();
     }
-
     ANGLE_SWALLOW_ERR(mImplementation->release());
 }
 
-ShHandle Compiler::getCompilerHandle(ShaderType type)
+ShCompilerInstance Compiler::getInstance(ShaderType type)
 {
     ASSERT(type != ShaderType::InvalidEnum);
-    ShHandle *compiler = &mShaderCompilers[type];
-
-    if (!(*compiler))
+    auto &pool = mPools[type];
+    if (pool.empty())
     {
-        if (activeCompilerHandles == 0)
-        {
-            sh::Initialize();
-        }
-
-        *compiler = sh::ConstructCompiler(ToGLenum(type), mSpec, mOutputType, &mResources);
-        ASSERT(*compiler);
-        activeCompilerHandles++;
+        ShHandle handle = sh::ConstructCompiler(ToGLenum(type), mSpec, mOutputType, &mResources);
+        ASSERT(handle);
+        return ShCompilerInstance(handle, mOutputType, type);
     }
-
-    return *compiler;
+    else
+    {
+        ShCompilerInstance instance = std::move(pool.back());
+        pool.pop_back();
+        return instance;
+    }
 }
 
-const std::string &Compiler::getBuiltinResourcesString(ShaderType type)
+void Compiler::putInstance(ShCompilerInstance &&instance)
 {
-    return sh::GetBuiltInResourcesString(getCompilerHandle(type));
+    static constexpr size_t kMaxPoolSize = 32;
+    auto &pool                           = mPools[instance.getShaderType()];
+    if (pool.size() < kMaxPoolSize)
+    {
+        pool.push_back(std::move(instance));
+    }
+    else
+    {
+        instance.destroy();
+    }
+}
+
+ShCompilerInstance::ShCompilerInstance() : mHandle(nullptr)
+{
+}
+
+ShCompilerInstance::ShCompilerInstance(ShHandle handle,
+                                       ShShaderOutput outputType,
+                                       ShaderType shaderType)
+    : mHandle(handle), mOutputType(outputType), mShaderType(shaderType)
+{
+}
+
+ShCompilerInstance::~ShCompilerInstance()
+{
+    ASSERT(mHandle == nullptr);
+}
+
+void ShCompilerInstance::destroy()
+{
+    if (mHandle != nullptr)
+    {
+        sh::Destruct(mHandle);
+        mHandle = nullptr;
+    }
+}
+
+ShCompilerInstance::ShCompilerInstance(ShCompilerInstance &&other)
+    : mHandle(other.mHandle), mOutputType(other.mOutputType), mShaderType(other.mShaderType)
+{
+    other.mHandle = nullptr;
+}
+
+ShCompilerInstance &ShCompilerInstance::operator=(ShCompilerInstance &&other)
+{
+    mHandle       = other.mHandle;
+    mOutputType   = other.mOutputType;
+    mShaderType   = other.mShaderType;
+    other.mHandle = nullptr;
+    return *this;
+}
+
+ShHandle ShCompilerInstance::getHandle()
+{
+    return mHandle;
+}
+
+ShaderType ShCompilerInstance::getShaderType() const
+{
+    return mShaderType;
+}
+
+const std::string &ShCompilerInstance::getBuiltinResourcesString()
+{
+    return sh::GetBuiltInResourcesString(mHandle);
+}
+
+ShShaderOutput ShCompilerInstance::getShaderOutputType() const
+{
+    return mOutputType;
 }
 
 }  // namespace gl
diff --git a/src/libANGLE/Compiler.h b/src/libANGLE/Compiler.h
index 6c0401a..aa0b277 100644
--- a/src/libANGLE/Compiler.h
+++ b/src/libANGLE/Compiler.h
@@ -10,6 +10,8 @@
 #ifndef LIBANGLE_COMPILER_H_
 #define LIBANGLE_COMPILER_H_
 
+#include <vector>
+
 #include "GLSLANG/ShaderLang.h"
 #include "common/PackedEnums.h"
 #include "libANGLE/Error.h"
@@ -24,15 +26,16 @@
 namespace gl
 {
 class ContextState;
+class ShCompilerInstance;
 
 class Compiler final : public RefCountObjectNoID
 {
   public:
     Compiler(rx::GLImplFactory *implFactory, const ContextState &data);
 
-    ShHandle getCompilerHandle(ShaderType shaderType);
+    ShCompilerInstance getInstance(ShaderType shaderType);
+    void putInstance(ShCompilerInstance &&instance);
     ShShaderOutput getShaderOutputType() const { return mOutputType; }
-    const std::string &getBuiltinResourcesString(ShaderType type);
 
   private:
     ~Compiler() override;
@@ -40,8 +43,29 @@
     ShShaderSpec mSpec;
     ShShaderOutput mOutputType;
     ShBuiltInResources mResources;
+    ShaderMap<std::vector<ShCompilerInstance>> mPools;
+};
 
-    ShaderMap<ShHandle> mShaderCompilers;
+class ShCompilerInstance final : public angle::NonCopyable
+{
+  public:
+    ShCompilerInstance();
+    ShCompilerInstance(ShHandle handle, ShShaderOutput outputType, ShaderType shaderType);
+    ~ShCompilerInstance();
+    void destroy();
+
+    ShCompilerInstance(ShCompilerInstance &&other);
+    ShCompilerInstance &operator=(ShCompilerInstance &&other);
+
+    ShHandle getHandle();
+    ShaderType getShaderType() const;
+    const std::string &getBuiltinResourcesString();
+    ShShaderOutput getShaderOutputType() const;
+
+  private:
+    ShHandle mHandle;
+    ShShaderOutput mOutputType;
+    ShaderType mShaderType;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp
index a2697da..3ebcab2 100644
--- a/src/libANGLE/Shader.cpp
+++ b/src/libANGLE/Shader.cpp
@@ -10,6 +10,7 @@
 
 #include "libANGLE/Shader.h"
 
+#include <functional>
 #include <sstream>
 
 #include "GLSLANG/ShaderLang.h"
@@ -19,6 +20,7 @@
 #include "libANGLE/Constants.h"
 #include "libANGLE/Context.h"
 #include "libANGLE/ResourceManager.h"
+#include "libANGLE/WorkerThread.h"
 #include "libANGLE/renderer/GLImplFactory.h"
 #include "libANGLE/renderer/ShaderImpl.h"
 
@@ -96,6 +98,50 @@
     }
 }
 
+class ScopedExit final : angle::NonCopyable
+{
+  public:
+    ScopedExit(std::function<void()> exit) : mExit(exit) {}
+    ~ScopedExit() { mExit(); }
+
+  private:
+    std::function<void()> mExit;
+};
+
+class CompileTask : public angle::Closure
+{
+  public:
+    CompileTask(ShHandle handle,
+                std::string &&sourcePath,
+                std::string &&source,
+                ShCompileOptions options)
+        : mHandle(handle),
+          mSourcePath(sourcePath),
+          mSource(source),
+          mOptions(options),
+          mResult(false)
+    {
+    }
+    void operator()() override
+    {
+        std::vector<const char *> srcStrings;
+        if (!mSourcePath.empty())
+        {
+            srcStrings.push_back(mSourcePath.c_str());
+        }
+        srcStrings.push_back(mSource.c_str());
+        mResult = sh::Compile(mHandle, &srcStrings[0], srcStrings.size(), mOptions);
+    }
+    bool getResult() { return mResult; }
+
+  private:
+    ShHandle mHandle;
+    std::string mSourcePath;
+    std::string mSource;
+    ShCompileOptions mOptions;
+    bool mResult;
+};
+
 ShaderState::ShaderState(ShaderType shaderType)
     : mLabel(),
       mShaderType(shaderType),
@@ -131,6 +177,7 @@
 
 void Shader::onDestroy(const gl::Context *context)
 {
+    resolveCompile();
     mImplementation->destroy();
     mBoundCompiler.set(context, nullptr);
     mImplementation.reset(nullptr);
@@ -284,6 +331,8 @@
 
 void Shader::compile(const Context *context)
 {
+    resolveCompile();
+
     mState.mTranslatedSource.clear();
     mInfoLog.clear();
     mState.mShaderVersion = 100;
@@ -307,19 +356,19 @@
     // can change before the link call or another call that resolves the compile.
 
     std::stringstream sourceStream;
-
-    mLastCompileOptions = mImplementation->prepareSourceAndReturnOptions(context, &sourceStream,
-                                                                         &mLastCompiledSourcePath);
-    mLastCompileOptions |= (SH_OBJECT_CODE | SH_VARIABLES);
-    mLastCompiledSource = sourceStream.str();
+    std::string sourcePath;
+    ShCompileOptions options =
+        mImplementation->prepareSourceAndReturnOptions(context, &sourceStream, &sourcePath);
+    options |= (SH_OBJECT_CODE | SH_VARIABLES);
+    auto source = sourceStream.str();
 
     // Add default options to WebGL shaders to prevent unexpected behavior during compilation.
     if (context->getExtensions().webglCompatibility)
     {
-        mLastCompileOptions |= SH_INIT_GL_POSITION;
-        mLastCompileOptions |= SH_LIMIT_CALL_STACK_DEPTH;
-        mLastCompileOptions |= SH_LIMIT_EXPRESSION_COMPLEXITY;
-        mLastCompileOptions |= SH_ENFORCE_PACKING_RESTRICTIONS;
+        options |= SH_INIT_GL_POSITION;
+        options |= SH_LIMIT_CALL_STACK_DEPTH;
+        options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
+        options |= SH_ENFORCE_PACKING_RESTRICTIONS;
     }
 
     // Some targets (eg D3D11 Feature Level 9_3 and below) do not support non-constant loop indexes
@@ -327,10 +376,21 @@
     // instruct the compiler to pre-validate.
     if (mRendererLimitations.shadersRequireIndexedLoopValidation)
     {
-        mLastCompileOptions |= SH_VALIDATE_LOOP_INDEXING;
+        options |= SH_VALIDATE_LOOP_INDEXING;
     }
 
     mCurrentMaxComputeWorkGroupInvocations = context->getCaps().maxComputeWorkGroupInvocations;
+
+    ASSERT(mBoundCompiler.get());
+    mShCompilerInstance     = mBoundCompiler->getInstance(mState.mShaderType);
+    ShHandle compilerHandle = mShCompilerInstance.getHandle();
+    ASSERT(compilerHandle);
+    mCompilerResourcesString = mShCompilerInstance.getBuiltinResourcesString();
+
+    mCompileTask  = std::make_shared<CompileTask>(compilerHandle, std::move(sourcePath),
+                                                 std::move(source), options);
+    mWorkerPool   = context->getWorkerThreadPool();
+    mCompileEvent = mWorkerPool->postWorkerTask(mCompileTask);
 }
 
 void Shader::resolveCompile()
@@ -340,19 +400,21 @@
         return;
     }
 
-    ASSERT(mBoundCompiler.get());
-    ShHandle compilerHandle = mBoundCompiler->getCompilerHandle(mState.mShaderType);
+    ASSERT(mCompileEvent.get());
+    ASSERT(mCompileTask.get());
 
-    std::vector<const char *> srcStrings;
+    mCompileEvent->wait();
 
-    if (!mLastCompiledSourcePath.empty())
-    {
-        srcStrings.push_back(mLastCompiledSourcePath.c_str());
-    }
+    mCompileEvent.reset();
+    mWorkerPool.reset();
 
-    srcStrings.push_back(mLastCompiledSource.c_str());
+    bool compiled = mCompileTask->getResult();
+    mCompileTask.reset();
 
-    if (!sh::Compile(compilerHandle, &srcStrings[0], srcStrings.size(), mLastCompileOptions))
+    ScopedExit exit([this]() { mBoundCompiler->putInstance(std::move(mShCompilerInstance)); });
+
+    ShHandle compilerHandle = mShCompilerInstance.getHandle();
+    if (!compiled)
     {
         mInfoLog = sh::GetInfoLog(compilerHandle);
         WARN() << std::endl << mInfoLog;
@@ -469,7 +531,7 @@
 
     ASSERT(!mState.mTranslatedSource.empty());
 
-    bool success          = mImplementation->postTranslateCompile(mBoundCompiler.get(), &mInfoLog);
+    bool success          = mImplementation->postTranslateCompile(&mShCompilerInstance, &mInfoLog);
     mState.mCompileStatus = success ? CompileStatus::COMPILED : CompileStatus::NOT_COMPILED;
 }
 
@@ -509,6 +571,11 @@
     return mState.mCompileStatus == CompileStatus::COMPILED;
 }
 
+bool Shader::isCompleted()
+{
+    return (!mState.compilePending() || mCompileEvent->isReady());
+}
+
 int Shader::getShaderVersion()
 {
     resolveCompile();
@@ -640,8 +707,7 @@
 
 const std::string &Shader::getCompilerResourcesString() const
 {
-    ASSERT(mBoundCompiler.get());
-    return mBoundCompiler->getBuiltinResourcesString(mState.mShaderType);
+    return mCompilerResourcesString;
 }
 
 }  // namespace gl
diff --git a/src/libANGLE/Shader.h b/src/libANGLE/Shader.h
index ec46d66..27be6ef 100644
--- a/src/libANGLE/Shader.h
+++ b/src/libANGLE/Shader.h
@@ -22,6 +22,7 @@
 
 #include "common/Optional.h"
 #include "common/angleutils.h"
+#include "libANGLE/Compiler.h"
 #include "libANGLE/Debug.h"
 #include "libANGLE/angletypes.h"
 
@@ -32,13 +33,19 @@
 class ShaderSh;
 }
 
+namespace angle
+{
+class WaitableEvent;
+class WorkerThreadPool;
+}  // namespace angle
+
 namespace gl
 {
-class Compiler;
 class ContextState;
 struct Limitations;
 class ShaderProgramManager;
 class Context;
+class CompileTask;
 
 // We defer the compile until link time, or until properties are queried.
 enum class CompileStatus
@@ -146,6 +153,7 @@
 
     void compile(const Context *context);
     bool isCompiled();
+    bool isCompleted();
 
     void addRef();
     void release(const Context *context);
@@ -190,9 +198,6 @@
     void resolveCompile();
 
     ShaderState mState;
-    std::string mLastCompiledSource;
-    std::string mLastCompiledSourcePath;
-    ShCompileOptions mLastCompileOptions;
     std::unique_ptr<rx::ShaderImpl> mImplementation;
     const gl::Limitations &mRendererLimitations;
     const GLuint mHandle;
@@ -203,6 +208,11 @@
 
     // We keep a reference to the translator in order to defer compiles while preserving settings.
     BindingPointer<Compiler> mBoundCompiler;
+    ShCompilerInstance mShCompilerInstance;
+    std::shared_ptr<CompileTask> mCompileTask;
+    std::shared_ptr<angle::WorkerThreadPool> mWorkerPool;
+    std::shared_ptr<angle::WaitableEvent> mCompileEvent;
+    std::string mCompilerResourcesString;
 
     ShaderProgramManager *mResourceManager;
 
diff --git a/src/libANGLE/queryutils.cpp b/src/libANGLE/queryutils.cpp
index 12a51a3..66d2d48 100644
--- a/src/libANGLE/queryutils.cpp
+++ b/src/libANGLE/queryutils.cpp
@@ -1167,9 +1167,7 @@
             *params = shader->isCompiled() ? GL_TRUE : GL_FALSE;
             return;
         case GL_COMPLETION_STATUS_KHR:
-            // TODO(jie.a.chen@intel.com): Parallelize shader compilation.
-            // http://crbug.com/849576
-            *params = shader->isCompiled() ? GL_TRUE : GL_FALSE;
+            *params = shader->isCompleted() ? GL_TRUE : GL_FALSE;
             return;
         case GL_INFO_LOG_LENGTH:
             *params = shader->getInfoLogLength();
diff --git a/src/libANGLE/renderer/ShaderImpl.h b/src/libANGLE/renderer/ShaderImpl.h
index 2b5c404..2f9d57f 100644
--- a/src/libANGLE/renderer/ShaderImpl.h
+++ b/src/libANGLE/renderer/ShaderImpl.h
@@ -28,7 +28,7 @@
                                                            std::stringstream *sourceStream,
                                                            std::string *sourcePath) = 0;
     // Returns success for compiling on the driver. Returns success.
-    virtual bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) = 0;
+    virtual bool postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog) = 0;
 
     virtual std::string getDebugInfo() const = 0;
 
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.cpp b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
index 0d27f78..ff50019 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.cpp
@@ -174,7 +174,7 @@
     return *uniformRegisterMap;
 }
 
-bool ShaderD3D::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog)
+bool ShaderD3D::postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog)
 {
     // TODO(jmadill): We shouldn't need to cache this.
     mCompilerOutputType = compiler->getShaderOutputType();
@@ -199,7 +199,7 @@
     mRequiresIEEEStrictCompiling =
         translatedSource.find("ANGLE_REQUIRES_IEEE_STRICT_COMPILING") != std::string::npos;
 
-    ShHandle compilerHandle = compiler->getCompilerHandle(mData.getShaderType());
+    ShHandle compilerHandle = compiler->getHandle();
 
     mUniformRegisterMap = GetUniformRegisterMap(sh::GetUniformRegisterMap(compilerHandle));
 
diff --git a/src/libANGLE/renderer/d3d/ShaderD3D.h b/src/libANGLE/renderer/d3d/ShaderD3D.h
index 9239df7..2e357e3 100644
--- a/src/libANGLE/renderer/d3d/ShaderD3D.h
+++ b/src/libANGLE/renderer/d3d/ShaderD3D.h
@@ -42,7 +42,7 @@
     ShCompileOptions prepareSourceAndReturnOptions(const gl::Context *context,
                                                    std::stringstream *sourceStream,
                                                    std::string *sourcePath) override;
-    bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override;
+    bool postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog) override;
     std::string getDebugInfo() const override;
 
     // D3D-specific methods
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index 6891219..898a06d 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -142,7 +142,7 @@
     return options;
 }
 
-bool ShaderGL::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog)
+bool ShaderGL::postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog)
 {
     // Translate the ESSL into GLSL
     const char *translatedSourceCString = mData.getTranslatedSource().c_str();
diff --git a/src/libANGLE/renderer/gl/ShaderGL.h b/src/libANGLE/renderer/gl/ShaderGL.h
index 0ba8843..7181d24 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.h
+++ b/src/libANGLE/renderer/gl/ShaderGL.h
@@ -32,7 +32,7 @@
     ShCompileOptions prepareSourceAndReturnOptions(const gl::Context *context,
                                                    std::stringstream *sourceStream,
                                                    std::string *sourcePath) override;
-    bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override;
+    bool postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog) override;
     std::string getDebugInfo() const override;
 
     GLuint getShaderID() const;
diff --git a/src/libANGLE/renderer/null/ShaderNULL.cpp b/src/libANGLE/renderer/null/ShaderNULL.cpp
index 4e3b9e7..f6edc87 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.cpp
+++ b/src/libANGLE/renderer/null/ShaderNULL.cpp
@@ -30,7 +30,7 @@
     return 0;
 }
 
-bool ShaderNULL::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog)
+bool ShaderNULL::postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog)
 {
     return true;
 }
diff --git a/src/libANGLE/renderer/null/ShaderNULL.h b/src/libANGLE/renderer/null/ShaderNULL.h
index 04a58c0..0892fa1 100644
--- a/src/libANGLE/renderer/null/ShaderNULL.h
+++ b/src/libANGLE/renderer/null/ShaderNULL.h
@@ -26,7 +26,7 @@
                                                    std::stringstream *sourceStream,
                                                    std::string *sourcePath) override;
     // Returns success for compiling on the driver. Returns success.
-    bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override;
+    bool postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog) override;
 
     std::string getDebugInfo() const override;
 };
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.cpp b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
index c1e21b6..7e2fad7 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.cpp
@@ -30,7 +30,7 @@
     return SH_INITIALIZE_UNINITIALIZED_LOCALS;
 }
 
-bool ShaderVk::postTranslateCompile(gl::Compiler *compiler, std::string *infoLog)
+bool ShaderVk::postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog)
 {
     // No work to do here.
     return true;
diff --git a/src/libANGLE/renderer/vulkan/ShaderVk.h b/src/libANGLE/renderer/vulkan/ShaderVk.h
index 5e42907..5990177 100644
--- a/src/libANGLE/renderer/vulkan/ShaderVk.h
+++ b/src/libANGLE/renderer/vulkan/ShaderVk.h
@@ -26,7 +26,7 @@
                                                    std::stringstream *sourceStream,
                                                    std::string *sourcePath) override;
     // Returns success for compiling on the driver. Returns success.
-    bool postTranslateCompile(gl::Compiler *compiler, std::string *infoLog) override;
+    bool postTranslateCompile(gl::ShCompilerInstance *compiler, std::string *infoLog) override;
 
     std::string getDebugInfo() const override;
 };
diff --git a/src/tests/gl_tests/ParallelShaderCompileTest.cpp b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
index 21788d2..fd3277e 100644
--- a/src/tests/gl_tests/ParallelShaderCompileTest.cpp
+++ b/src/tests/gl_tests/ParallelShaderCompileTest.cpp
@@ -51,11 +51,39 @@
       public:
         ClearColorWithDraw(GLubyte color) : mColor(color, color, color, 255) {}
 
-        bool compileAndLink()
+        bool compile()
         {
-            mProgram =
-                CompileProgramParallel(insertRandomString(essl1_shaders::vs::Simple()),
-                                       insertRandomString(essl1_shaders::fs::UniformColor()));
+            mVertexShader =
+                compileShader(GL_VERTEX_SHADER, insertRandomString(essl1_shaders::vs::Simple()));
+            mFragmentShader = compileShader(GL_FRAGMENT_SHADER,
+                                            insertRandomString(essl1_shaders::fs::UniformColor()));
+            return (mVertexShader != 0 && mFragmentShader != 0);
+        }
+
+        bool isCompileCompleted()
+        {
+            GLint status;
+            glGetShaderiv(mVertexShader, GL_COMPLETION_STATUS_KHR, &status);
+            if (status == GL_TRUE)
+            {
+                glGetShaderiv(mFragmentShader, GL_COMPLETION_STATUS_KHR, &status);
+                return (status == GL_TRUE);
+            }
+            return false;
+        }
+
+        bool link()
+        {
+            mProgram = 0;
+            if (checkShader(mVertexShader) && checkShader(mFragmentShader))
+            {
+                mProgram = glCreateProgram();
+                glAttachShader(mProgram, mVertexShader);
+                glAttachShader(mProgram, mFragmentShader);
+                glLinkProgram(mProgram);
+            }
+            glDeleteShader(mVertexShader);
+            glDeleteShader(mFragmentShader);
             return (mProgram != 0);
         }
 
@@ -95,7 +123,47 @@
             return ostream.str();
         }
 
+        GLuint compileShader(GLenum type, const std::string &source)
+        {
+            GLuint shader = glCreateShader(type);
+
+            const char *sourceArray[1] = {source.c_str()};
+            glShaderSource(shader, 1, sourceArray, nullptr);
+            glCompileShader(shader);
+            return shader;
+        }
+
+        bool checkShader(GLuint shader)
+        {
+            GLint compileResult;
+            glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
+
+            if (compileResult == 0)
+            {
+                GLint infoLogLength;
+                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
+
+                // Info log length includes the null terminator, so 1 means that the info log is an
+                // empty string.
+                if (infoLogLength > 1)
+                {
+                    std::vector<GLchar> infoLog(infoLogLength);
+                    glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr,
+                                       &infoLog[0]);
+                    std::cerr << "shader compilation failed: " << &infoLog[0];
+                }
+                else
+                {
+                    std::cerr << "shader compilation failed. <Empty log message>";
+                }
+                std::cerr << std::endl;
+            }
+            return (compileResult == GL_TRUE);
+        }
+
         GLColor mColor;
+        GLuint mVertexShader;
+        GLuint mFragmentShader;
         GLuint mProgram;
     };
 };
@@ -118,25 +186,48 @@
 {
     ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
 
-    std::vector<std::unique_ptr<ClearColorWithDraw>> tasks;
+    std::vector<std::unique_ptr<ClearColorWithDraw>> compileTasks;
     constexpr int kTaskCount = 32;
     for (int i = 0; i < kTaskCount; ++i)
     {
         std::unique_ptr<ClearColorWithDraw> task(new ClearColorWithDraw(i * 255 / kTaskCount));
-        bool isLinking = task->compileAndLink();
-        ASSERT_TRUE(isLinking);
-        tasks.push_back(std::move(task));
+        bool isCompiling = task->compile();
+        ASSERT_TRUE(isCompiling);
+        compileTasks.push_back(std::move(task));
     }
+
     constexpr unsigned int kPollInterval = 100;
-    while (!tasks.empty())
+
+    std::vector<std::unique_ptr<ClearColorWithDraw>> linkTasks;
+    while (!compileTasks.empty())
     {
-        for (unsigned int i = 0; i < tasks.size();)
+        for (unsigned int i = 0; i < compileTasks.size();)
         {
-            auto &task = tasks[i];
+            auto &task = compileTasks[i];
+
+            if (task->isCompileCompleted())
+            {
+                bool isLinking = task->link();
+                ASSERT_TRUE(isLinking);
+                linkTasks.push_back(std::move(task));
+                compileTasks.erase(compileTasks.begin() + i);
+                continue;
+            }
+            ++i;
+        }
+        Sleep(kPollInterval);
+    }
+
+    while (!linkTasks.empty())
+    {
+        for (unsigned int i = 0; i < linkTasks.size();)
+        {
+            auto &task = linkTasks[i];
+
             if (task->isLinkCompleted())
             {
                 task->drawAndVerify(this);
-                tasks.erase(tasks.begin() + i);
+                linkTasks.erase(linkTasks.begin() + i);
                 continue;
             }
             ++i;