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/Program.cpp b/src/libANGLE/Program.cpp
index 0a2ebe1..f1cd2d4 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -598,6 +598,15 @@
 
 }  // anonymous namespace
 
+// Saves the linking context for later use in resolveLink().
+struct Program::LinkingState
+{
+    const Context *context;
+    std::unique_ptr<ProgramLinkedResources> resources;
+    ProgramHash programHash;
+    std::unique_ptr<rx::LinkEvent> linkEvent;
+};
+
 const char *const g_fakepath = "C:\\fakepath";
 
 // InfoLog implementation.
@@ -901,6 +910,7 @@
 
 void Program::onDestroy(const Context *context)
 {
+    resolveLink();
     for (ShaderType shaderType : AllShaderTypes())
     {
         if (mState.mAttachedShaders[shaderType])
@@ -918,19 +928,32 @@
 
     delete this;
 }
+GLuint Program::id() const
+{
+    resolveLink();
+    return mHandle;
+}
 
 void Program::setLabel(const std::string &label)
 {
+    resolveLink();
     mState.mLabel = label;
 }
 
 const std::string &Program::getLabel() const
 {
+    resolveLink();
     return mState.mLabel;
 }
+rx::ProgramImpl *Program::getImplementation() const
+{
+    resolveLink();
+    return mProgram;
+}
 
 void Program::attachShader(Shader *shader)
 {
+    resolveLink();
     ShaderType shaderType = shader->getType();
     ASSERT(shaderType != ShaderType::InvalidEnum);
 
@@ -940,6 +963,7 @@
 
 void Program::detachShader(const Context *context, Shader *shader)
 {
+    resolveLink();
     ShaderType shaderType = shader->getType();
     ASSERT(shaderType != ShaderType::InvalidEnum);
 
@@ -950,6 +974,7 @@
 
 int Program::getAttachedShadersCount() const
 {
+    resolveLink();
     int numAttachedShaders = 0;
     for (const Shader *shader : mState.mAttachedShaders)
     {
@@ -964,26 +989,31 @@
 
 const Shader *Program::getAttachedShader(ShaderType shaderType) const
 {
+    resolveLink();
     return mState.getAttachedShader(shaderType);
 }
 
 void Program::bindAttributeLocation(GLuint index, const char *name)
 {
+    resolveLink();
     mAttributeBindings.bindLocation(index, name);
 }
 
 void Program::bindUniformLocation(GLuint index, const char *name)
 {
+    resolveLink();
     mUniformLocationBindings.bindLocation(index, name);
 }
 
 void Program::bindFragmentInputLocation(GLint index, const char *name)
 {
+    resolveLink();
     mFragmentInputBindings.bindLocation(index, name);
 }
 
 BindingInfo Program::getFragmentInputBindingInfo(const Context *context, GLint index) const
 {
+    resolveLink();
     BindingInfo ret;
     ret.type  = GL_NONE;
     ret.valid = false;
@@ -1040,6 +1070,7 @@
                                    GLint components,
                                    const GLfloat *coeffs)
 {
+    resolveLink();
     // If the location is -1 then the command is silently ignored
     if (index == -1)
         return;
@@ -1074,8 +1105,9 @@
         return NoError();
     }
 
-    ProgramHash programHash;
+    ProgramHash programHash   = {0};
     MemoryProgramCache *cache = context->getMemoryProgramCache();
+
     if (cache)
     {
         ANGLE_TRY_RESULT(cache->getProgram(context, this, &mState, &programHash), mLinked);
@@ -1096,18 +1128,19 @@
     // Re-link shaders after the unlink call.
     ASSERT(linkValidateShaders(context, mInfoLog));
 
+    std::unique_ptr<ProgramLinkedResources> resources;
     if (mState.mAttachedShaders[ShaderType::Compute])
     {
-        ProgramLinkedResources resources = {
-            {0, PackMode::ANGLE_RELAXED},
-            {&mState.mUniformBlocks, &mState.mUniforms},
-            {&mState.mShaderStorageBlocks, &mState.mBufferVariables},
-            {&mState.mAtomicCounterBuffers},
-            {}};
+        resources.reset(
+            new ProgramLinkedResources{{0, PackMode::ANGLE_RELAXED},
+                                       {&mState.mUniformBlocks, &mState.mUniforms},
+                                       {&mState.mShaderStorageBlocks, &mState.mBufferVariables},
+                                       {&mState.mAtomicCounterBuffers},
+                                       {}});
 
         GLuint combinedImageUniforms = 0u;
         if (!linkUniforms(context, mInfoLog, mUniformLocationBindings, &combinedImageUniforms,
-                          &resources.unusedUniforms))
+                          &resources->unusedUniforms))
         {
             return NoError();
         }
@@ -1134,14 +1167,8 @@
             return NoError();
         }
 
-        InitUniformBlockLinker(context, mState, &resources.uniformBlockLinker);
-        InitShaderStorageBlockLinker(context, mState, &resources.shaderStorageBlockLinker);
-
-        ANGLE_TRY_RESULT(mProgram->link(context, resources, mInfoLog), mLinked);
-        if (!mLinked)
-        {
-            return NoError();
-        }
+        InitUniformBlockLinker(context, mState, &resources->uniformBlockLinker);
+        InitShaderStorageBlockLinker(context, mState, &resources->shaderStorageBlockLinker);
     }
     else
     {
@@ -1158,12 +1185,12 @@
             packMode = PackMode::WEBGL_STRICT;
         }
 
-        ProgramLinkedResources resources = {
-            {data.getCaps().maxVaryingVectors, packMode},
-            {&mState.mUniformBlocks, &mState.mUniforms},
-            {&mState.mShaderStorageBlocks, &mState.mBufferVariables},
-            {&mState.mAtomicCounterBuffers},
-            {}};
+        resources.reset(
+            new ProgramLinkedResources{{data.getCaps().maxVaryingVectors, packMode},
+                                       {&mState.mUniformBlocks, &mState.mUniforms},
+                                       {&mState.mShaderStorageBlocks, &mState.mBufferVariables},
+                                       {&mState.mAtomicCounterBuffers},
+                                       {}});
 
         if (!linkAttributes(context, mInfoLog))
         {
@@ -1177,7 +1204,7 @@
 
         GLuint combinedImageUniforms = 0u;
         if (!linkUniforms(context, mInfoLog, mUniformLocationBindings, &combinedImageUniforms,
-                          &resources.unusedUniforms))
+                          &resources->unusedUniforms))
         {
             return NoError();
         }
@@ -1203,29 +1230,62 @@
         ASSERT(mState.mAttachedShaders[ShaderType::Vertex]);
         mState.mNumViews = mState.mAttachedShaders[ShaderType::Vertex]->getNumViews(context);
 
-        InitUniformBlockLinker(context, mState, &resources.uniformBlockLinker);
-        InitShaderStorageBlockLinker(context, mState, &resources.shaderStorageBlockLinker);
+        InitUniformBlockLinker(context, mState, &resources->uniformBlockLinker);
+        InitShaderStorageBlockLinker(context, mState, &resources->shaderStorageBlockLinker);
 
         if (!linkValidateTransformFeedback(context, mInfoLog, mergedVaryings, context->getCaps()))
         {
             return NoError();
         }
 
-        if (!resources.varyingPacking.collectAndPackUserVaryings(
+        if (!resources->varyingPacking.collectAndPackUserVaryings(
                 mInfoLog, mergedVaryings, mState.getTransformFeedbackVaryingNames()))
         {
             return NoError();
         }
 
-        ANGLE_TRY_RESULT(mProgram->link(context, resources, mInfoLog), mLinked);
-        if (!mLinked)
-        {
-            return NoError();
-        }
-
         gatherTransformFeedbackVaryings(mergedVaryings);
     }
 
+    mLinkingState.reset(new LinkingState());
+    mLinkingState->context     = context;
+    mLinkingState->programHash = programHash;
+    mLinkingState->linkEvent   = mProgram->link(context, *resources, mInfoLog);
+    mLinkingState->resources   = std::move(resources);
+
+    return NoError();
+}
+
+bool Program::isLinking() const
+{
+    return (mLinkingState.get() && mLinkingState->linkEvent->isLinking());
+}
+
+bool Program::isLinked() const
+{
+    resolveLink();
+    return mLinked;
+}
+
+void Program::resolveLink() const
+{
+    if (mLinkingState.get())
+    {
+        return const_cast<Program *>(this)->resolveLinkImpl();
+    }
+}
+
+void Program::resolveLinkImpl()
+{
+    ASSERT(mLinkingState.get());
+
+    mLinked           = mLinkingState->linkEvent->wait();
+    auto linkingState = std::move(mLinkingState);
+    if (!mLinked)
+    {
+        return;
+    }
+
     initInterfaceBlockBindings();
 
     // According to GLES 3.0/3.1 spec for LinkProgram and UseProgram,
@@ -1242,17 +1302,13 @@
     setUniformValuesFromBindingQualifiers();
 
     // Save to the program cache.
-    if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() ||
-                  !context->getWorkarounds().disableProgramCachingForTransformFeedback))
+    auto *cache = linkingState->context->getMemoryProgramCache();
+    if (cache &&
+        (mState.mLinkedTransformFeedbackVaryings.empty() ||
+         !linkingState->context->getWorkarounds().disableProgramCachingForTransformFeedback))
     {
-        cache->putProgram(programHash, context, this);
+        cache->putProgram(linkingState->programHash, linkingState->context, this);
     }
-
-    double delta = platform->currentTime(platform) - startTime;
-    int us       = static_cast<int>(delta * 1000000.0);
-    ANGLE_HISTOGRAM_COUNTS("GPU.ANGLE.ProgramCache.ProgramCacheMissTimeUS", us);
-
-    return NoError();
 }
 
 void Program::updateLinkedShaderStages()
@@ -1347,6 +1403,7 @@
                           const void *binary,
                           GLsizei length)
 {
+    resolveLink();
     unlink();
 
 #if ANGLE_PROGRAM_BINARY_LOAD != ANGLE_ENABLED
@@ -1376,6 +1433,7 @@
                           GLsizei bufSize,
                           GLsizei *length) const
 {
+    resolveLink();
     if (binaryFormat)
     {
         *binaryFormat = GL_PROGRAM_BINARY_ANGLE;
@@ -1420,6 +1478,7 @@
 
 GLint Program::getBinaryLength(const Context *context) const
 {
+    resolveLink();
     if (!mLinked)
     {
         return 0;
@@ -1437,6 +1496,7 @@
 
 void Program::setBinaryRetrievableHint(bool retrievable)
 {
+    resolveLink();
     // TODO(jmadill) : replace with dirty bits
     mProgram->setBinaryRetrievableHint(retrievable);
     mState.mBinaryRetrieveableHint = retrievable;
@@ -1444,11 +1504,13 @@
 
 bool Program::getBinaryRetrievableHint() const
 {
+    resolveLink();
     return mState.mBinaryRetrieveableHint;
 }
 
 void Program::setSeparable(bool separable)
 {
+    resolveLink();
     // TODO(yunchao) : replace with dirty bits
     if (mState.mSeparable != separable)
     {
@@ -1459,11 +1521,13 @@
 
 bool Program::isSeparable() const
 {
+    resolveLink();
     return mState.mSeparable;
 }
 
 void Program::release(const Context *context)
 {
+    resolveLink();
     mRefCount--;
 
     if (mRefCount == 0 && mDeleteStatus)
@@ -1474,6 +1538,7 @@
 
 void Program::addRef()
 {
+    resolveLink();
     mRefCount++;
 }
 
@@ -1484,16 +1549,19 @@
 
 int Program::getInfoLogLength() const
 {
+    resolveLink();
     return static_cast<int>(mInfoLog.getLength());
 }
 
 void Program::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const
 {
+    resolveLink();
     return mInfoLog.getLog(bufSize, length, infoLog);
 }
 
 void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders) const
 {
+    resolveLink();
     int total = 0;
 
     for (const Shader *shader : mState.mAttachedShaders)
@@ -1513,11 +1581,13 @@
 
 GLuint Program::getAttributeLocation(const std::string &name) const
 {
+    resolveLink();
     return mState.getAttributeLocation(name);
 }
 
 bool Program::isAttribLocationActive(size_t attribLocation) const
 {
+    resolveLink();
     ASSERT(attribLocation < mState.mActiveAttribLocationsMask.size());
     return mState.mActiveAttribLocationsMask[attribLocation];
 }
@@ -1529,6 +1599,7 @@
                                  GLenum *type,
                                  GLchar *name) const
 {
+    resolveLink();
     if (!mLinked)
     {
         if (bufsize > 0)
@@ -1561,6 +1632,7 @@
 
 GLint Program::getActiveAttributeCount() const
 {
+    resolveLink();
     if (!mLinked)
     {
         return 0;
@@ -1571,6 +1643,7 @@
 
 GLint Program::getActiveAttributeMaxLength() const
 {
+    resolveLink();
     if (!mLinked)
     {
         return 0;
@@ -1586,21 +1659,91 @@
     return static_cast<GLint>(maxLength);
 }
 
+const std::vector<sh::Attribute> &Program::getAttributes() const
+{
+    resolveLink();
+    return mState.mAttributes;
+}
+
+const AttributesMask &Program::getActiveAttribLocationsMask() const
+{
+    resolveLink();
+    return mState.mActiveAttribLocationsMask;
+}
+
+const std::vector<SamplerBinding> &Program::getSamplerBindings() const
+{
+    resolveLink();
+    return mState.mSamplerBindings;
+}
+
+const std::vector<ImageBinding> &Program::getImageBindings() const
+{
+    resolveLink();
+    return mState.mImageBindings;
+}
+const sh::WorkGroupSize &Program::getComputeShaderLocalSize() const
+{
+    resolveLink();
+    return mState.mComputeShaderLocalSize;
+}
+
+PrimitiveMode Program::getGeometryShaderInputPrimitiveType() const
+{
+    resolveLink();
+    return mState.mGeometryShaderInputPrimitiveType;
+}
+PrimitiveMode Program::getGeometryShaderOutputPrimitiveType() const
+{
+    resolveLink();
+    return mState.mGeometryShaderOutputPrimitiveType;
+}
+GLint Program::getGeometryShaderInvocations() const
+{
+    resolveLink();
+    return mState.mGeometryShaderInvocations;
+}
+GLint Program::getGeometryShaderMaxVertices() const
+{
+    resolveLink();
+    return mState.mGeometryShaderMaxVertices;
+}
+
+const ProgramState &Program::getState() const
+{
+    resolveLink();
+    return mState;
+}
+
 GLuint Program::getInputResourceIndex(const GLchar *name) const
 {
+    resolveLink();
     return GetResourceIndexFromName(mState.mAttributes, std::string(name));
 }
 
 GLuint Program::getOutputResourceIndex(const GLchar *name) const
 {
+    resolveLink();
     return GetResourceIndexFromName(mState.mOutputVariables, std::string(name));
 }
 
 size_t Program::getOutputResourceCount() const
 {
+    resolveLink();
     return (mLinked ? mState.mOutputVariables.size() : 0);
 }
 
+const std::vector<GLenum> &Program::getOutputVariableTypes() const
+{
+    resolveLink();
+    return mState.mOutputVariableTypes;
+}
+DrawBufferMask Program::getActiveOutputVariables() const
+{
+    resolveLink();
+    return mState.mActiveOutputVariables;
+}
+
 template <typename T>
 void Program::getResourceName(GLuint index,
                               const std::vector<T> &resources,
@@ -1635,6 +1778,7 @@
                                    GLsizei *length,
                                    GLchar *name) const
 {
+    resolveLink();
     getResourceName(index, mState.mAttributes, bufSize, length, name);
 }
 
@@ -1643,6 +1787,7 @@
                                     GLsizei *length,
                                     GLchar *name) const
 {
+    resolveLink();
     getResourceName(index, mState.mOutputVariables, bufSize, length, name);
 }
 
@@ -1651,6 +1796,7 @@
                                      GLsizei *length,
                                      GLchar *name) const
 {
+    resolveLink();
     getResourceName(index, mState.mUniforms, bufSize, length, name);
 }
 
@@ -1659,23 +1805,76 @@
                                             GLsizei *length,
                                             GLchar *name) const
 {
+    resolveLink();
     getResourceName(index, mState.mBufferVariables, bufSize, length, name);
 }
 
 const sh::Attribute &Program::getInputResource(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < mState.mAttributes.size());
     return mState.mAttributes[index];
 }
 
 const sh::OutputVariable &Program::getOutputResource(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < mState.mOutputVariables.size());
     return mState.mOutputVariables[index];
 }
 
+const ProgramBindings &Program::getAttributeBindings() const
+{
+    resolveLink();
+    return mAttributeBindings;
+}
+const ProgramBindings &Program::getUniformLocationBindings() const
+{
+    resolveLink();
+    return mUniformLocationBindings;
+}
+const ProgramBindings &Program::getFragmentInputBindings() const
+{
+    resolveLink();
+    return mFragmentInputBindings;
+}
+
+int Program::getNumViews() const
+{
+    resolveLink();
+    return mState.getNumViews();
+}
+bool Program::usesMultiview() const
+{
+    resolveLink();
+    return mState.usesMultiview();
+}
+
+ComponentTypeMask Program::getDrawBufferTypeMask() const
+{
+    resolveLink();
+    return mState.mDrawBufferTypeMask;
+}
+ComponentTypeMask Program::getAttributesTypeMask() const
+{
+    resolveLink();
+    return mState.mAttributesTypeMask;
+}
+AttributesMask Program::getAttributesMask() const
+{
+    resolveLink();
+    return mState.mAttributesMask;
+}
+
+const std::vector<GLsizei> &Program::getTransformFeedbackStrides() const
+{
+    resolveLink();
+    return mState.mTransformFeedbackStrides;
+}
+
 GLint Program::getFragDataLocation(const std::string &name) const
 {
+    resolveLink();
     return GetVariableLocation(mState.mOutputVariables, mState.mOutputLocations, name);
 }
 
@@ -1686,6 +1885,7 @@
                                GLenum *type,
                                GLchar *name) const
 {
+    resolveLink();
     if (mLinked)
     {
         // index must be smaller than getActiveUniformCount()
@@ -1720,6 +1920,7 @@
 
 GLint Program::getActiveUniformCount() const
 {
+    resolveLink();
     if (mLinked)
     {
         return static_cast<GLint>(mState.mUniforms.size());
@@ -1732,11 +1933,13 @@
 
 size_t Program::getActiveBufferVariableCount() const
 {
+    resolveLink();
     return mLinked ? mState.mBufferVariables.size() : 0;
 }
 
 GLint Program::getActiveUniformMaxLength() const
 {
+    resolveLink();
     size_t maxLength = 0;
 
     if (mLinked)
@@ -1760,6 +1963,7 @@
 
 bool Program::isValidUniformLocation(GLint location) const
 {
+    resolveLink();
     ASSERT(angle::IsValueInRangeForNumericType<GLint>(mState.mUniformLocations.size()));
     return (location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size() &&
             mState.mUniformLocations[static_cast<size_t>(location)].used());
@@ -1767,34 +1971,52 @@
 
 const LinkedUniform &Program::getUniformByLocation(GLint location) const
 {
+    resolveLink();
     ASSERT(location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size());
     return mState.mUniforms[mState.getUniformIndexFromLocation(location)];
 }
 
 const VariableLocation &Program::getUniformLocation(GLint location) const
 {
+    resolveLink();
     ASSERT(location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size());
     return mState.mUniformLocations[location];
 }
 
+const std::vector<VariableLocation> &Program::getUniformLocations() const
+{
+    resolveLink();
+    return mState.mUniformLocations;
+}
+const LinkedUniform &Program::getUniformByIndex(GLuint index) const
+{
+    resolveLink();
+    ASSERT(index < static_cast<size_t>(mState.mUniforms.size()));
+    return mState.mUniforms[index];
+}
+
 const BufferVariable &Program::getBufferVariableByIndex(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < static_cast<size_t>(mState.mBufferVariables.size()));
     return mState.mBufferVariables[index];
 }
 
 GLint Program::getUniformLocation(const std::string &name) const
 {
+    resolveLink();
     return GetVariableLocation(mState.mUniforms, mState.mUniformLocations, name);
 }
 
 GLuint Program::getUniformIndex(const std::string &name) const
 {
+    resolveLink();
     return mState.getUniformIndexFromName(name);
 }
 
 void Program::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 1, v);
     mProgram->setUniform1fv(location, clampedCount, v);
@@ -1802,6 +2024,7 @@
 
 void Program::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 2, v);
     mProgram->setUniform2fv(location, clampedCount, v);
@@ -1809,6 +2032,7 @@
 
 void Program::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 3, v);
     mProgram->setUniform3fv(location, clampedCount, v);
@@ -1816,6 +2040,7 @@
 
 void Program::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 4, v);
     mProgram->setUniform4fv(location, clampedCount, v);
@@ -1823,6 +2048,7 @@
 
 Program::SetUniformResult Program::setUniform1iv(GLint location, GLsizei count, const GLint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 1, v);
 
@@ -1839,6 +2065,7 @@
 
 void Program::setUniform2iv(GLint location, GLsizei count, const GLint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 2, v);
     mProgram->setUniform2iv(location, clampedCount, v);
@@ -1846,6 +2073,7 @@
 
 void Program::setUniform3iv(GLint location, GLsizei count, const GLint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 3, v);
     mProgram->setUniform3iv(location, clampedCount, v);
@@ -1853,6 +2081,7 @@
 
 void Program::setUniform4iv(GLint location, GLsizei count, const GLint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 4, v);
     mProgram->setUniform4iv(location, clampedCount, v);
@@ -1860,6 +2089,7 @@
 
 void Program::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 1, v);
     mProgram->setUniform1uiv(location, clampedCount, v);
@@ -1867,6 +2097,7 @@
 
 void Program::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 2, v);
     mProgram->setUniform2uiv(location, clampedCount, v);
@@ -1874,6 +2105,7 @@
 
 void Program::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 3, v);
     mProgram->setUniform3uiv(location, clampedCount, v);
@@ -1881,6 +2113,7 @@
 
 void Program::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
 {
+    resolveLink();
     const VariableLocation &locationInfo = mState.mUniformLocations[location];
     GLsizei clampedCount                 = clampUniformCount(locationInfo, count, 4, v);
     mProgram->setUniform4uiv(location, clampedCount, v);
@@ -1891,6 +2124,7 @@
                                   GLboolean transpose,
                                   const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<2, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix2fv(location, clampedCount, transpose, v);
 }
@@ -1900,6 +2134,7 @@
                                   GLboolean transpose,
                                   const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<3, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix3fv(location, clampedCount, transpose, v);
 }
@@ -1909,6 +2144,7 @@
                                   GLboolean transpose,
                                   const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<4, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix4fv(location, clampedCount, transpose, v);
 }
@@ -1918,6 +2154,7 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<2, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix2x3fv(location, clampedCount, transpose, v);
 }
@@ -1927,6 +2164,7 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<2, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix2x4fv(location, clampedCount, transpose, v);
 }
@@ -1936,6 +2174,7 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<3, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix3x2fv(location, clampedCount, transpose, v);
 }
@@ -1945,6 +2184,7 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<3, 4>(location, count, transpose, v);
     mProgram->setUniformMatrix3x4fv(location, clampedCount, transpose, v);
 }
@@ -1954,6 +2194,7 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<4, 2>(location, count, transpose, v);
     mProgram->setUniformMatrix4x2fv(location, clampedCount, transpose, v);
 }
@@ -1963,12 +2204,14 @@
                                     GLboolean transpose,
                                     const GLfloat *v)
 {
+    resolveLink();
     GLsizei clampedCount = clampMatrixUniformCount<4, 3>(location, count, transpose, v);
     mProgram->setUniformMatrix4x3fv(location, clampedCount, transpose, v);
 }
 
 GLuint Program::getSamplerUniformBinding(const VariableLocation &uniformLocation) const
 {
+    resolveLink();
     GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(uniformLocation.index);
     const std::vector<GLuint> &boundTextureUnits =
         mState.mSamplerBindings[samplerIndex].boundTextureUnits;
@@ -1977,6 +2220,7 @@
 
 void Program::getUniformfv(const Context *context, GLint location, GLfloat *v) const
 {
+    resolveLink();
     const VariableLocation &uniformLocation = mState.getUniformLocations()[location];
     const LinkedUniform &uniform            = mState.getUniforms()[uniformLocation.index];
 
@@ -1999,6 +2243,7 @@
 
 void Program::getUniformiv(const Context *context, GLint location, GLint *v) const
 {
+    resolveLink();
     const VariableLocation &uniformLocation = mState.getUniformLocations()[location];
     const LinkedUniform &uniform            = mState.getUniforms()[uniformLocation.index];
 
@@ -2021,6 +2266,7 @@
 
 void Program::getUniformuiv(const Context *context, GLint location, GLuint *v) const
 {
+    resolveLink();
     const VariableLocation &uniformLocation = mState.getUniformLocations()[location];
     const LinkedUniform &uniform            = mState.getUniforms()[uniformLocation.index];
 
@@ -2043,16 +2289,19 @@
 
 void Program::flagForDeletion()
 {
+    resolveLink();
     mDeleteStatus = true;
 }
 
 bool Program::isFlaggedForDeletion() const
 {
+    resolveLink();
     return mDeleteStatus;
 }
 
 void Program::validate(const Caps &caps)
 {
+    resolveLink();
     mInfoLog.reset();
 
     if (mLinked)
@@ -2067,6 +2316,8 @@
 
 bool Program::validateSamplersImpl(InfoLog *infoLog, const Caps &caps)
 {
+    resolveLink();
+
     // if any two active samplers in a program are of different types, but refer to the same
     // texture image unit, and this is the current program, then ValidateProgram will fail, and
     // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
@@ -2092,16 +2343,19 @@
 
 bool Program::isValidated() const
 {
+    resolveLink();
     return mValidated;
 }
 
 GLuint Program::getActiveAtomicCounterBufferCount() const
 {
+    resolveLink();
     return static_cast<GLuint>(mState.mAtomicCounterBuffers.size());
 }
 
 GLuint Program::getActiveShaderStorageBlockCount() const
 {
+    resolveLink();
     return static_cast<GLuint>(mState.mShaderStorageBlocks.size());
 }
 
@@ -2110,6 +2364,7 @@
                                         GLsizei *length,
                                         GLchar *blockName) const
 {
+    resolveLink();
     GetInterfaceBlockName(blockIndex, mState.mUniformBlocks, bufSize, length, blockName);
 }
 
@@ -2118,7 +2373,7 @@
                                               GLsizei *length,
                                               GLchar *blockName) const
 {
-
+    resolveLink();
     GetInterfaceBlockName(blockIndex, mState.mShaderStorageBlocks, bufSize, length, blockName);
 }
 
@@ -2144,38 +2399,45 @@
 
 GLint Program::getActiveUniformBlockMaxNameLength() const
 {
+    resolveLink();
     return getActiveInterfaceBlockMaxNameLength(mState.mUniformBlocks);
 }
 
 GLint Program::getActiveShaderStorageBlockMaxNameLength() const
 {
+    resolveLink();
     return getActiveInterfaceBlockMaxNameLength(mState.mShaderStorageBlocks);
 }
 
 GLuint Program::getUniformBlockIndex(const std::string &name) const
 {
+    resolveLink();
     return GetInterfaceBlockIndex(mState.mUniformBlocks, name);
 }
 
 GLuint Program::getShaderStorageBlockIndex(const std::string &name) const
 {
+    resolveLink();
     return GetInterfaceBlockIndex(mState.mShaderStorageBlocks, name);
 }
 
 const InterfaceBlock &Program::getUniformBlockByIndex(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < static_cast<GLuint>(mState.mUniformBlocks.size()));
     return mState.mUniformBlocks[index];
 }
 
 const InterfaceBlock &Program::getShaderStorageBlockByIndex(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < static_cast<GLuint>(mState.mShaderStorageBlocks.size()));
     return mState.mShaderStorageBlocks[index];
 }
 
 void Program::bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding)
 {
+    resolveLink();
     mState.mUniformBlocks[uniformBlockIndex].binding = uniformBlockBinding;
     mState.mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlockBinding != 0);
     mProgram->setUniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
@@ -2183,11 +2445,13 @@
 
 GLuint Program::getUniformBlockBinding(GLuint uniformBlockIndex) const
 {
+    resolveLink();
     return mState.getUniformBlockBinding(uniformBlockIndex);
 }
 
 GLuint Program::getShaderStorageBlockBinding(GLuint shaderStorageBlockIndex) const
 {
+    resolveLink();
     return mState.getShaderStorageBlockBinding(shaderStorageBlockIndex);
 }
 
@@ -2195,6 +2459,7 @@
                                            const GLchar *const *varyings,
                                            GLenum bufferMode)
 {
+    resolveLink();
     mState.mTransformFeedbackVaryingNames.resize(count);
     for (GLsizei i = 0; i < count; i++)
     {
@@ -2211,6 +2476,7 @@
                                           GLenum *type,
                                           GLchar *name) const
 {
+    resolveLink();
     if (mLinked)
     {
         ASSERT(index < mState.mLinkedTransformFeedbackVaryings.size());
@@ -2239,6 +2505,7 @@
 
 GLsizei Program::getTransformFeedbackVaryingCount() const
 {
+    resolveLink();
     if (mLinked)
     {
         return static_cast<GLsizei>(mState.mLinkedTransformFeedbackVaryings.size());
@@ -2251,6 +2518,7 @@
 
 GLsizei Program::getTransformFeedbackVaryingMaxLength() const
 {
+    resolveLink();
     if (mLinked)
     {
         GLsizei maxSize = 0;
@@ -2270,6 +2538,7 @@
 
 GLenum Program::getTransformFeedbackBufferMode() const
 {
+    resolveLink();
     return mState.mTransformFeedbackBufferMode;
 }
 
@@ -2395,6 +2664,7 @@
 
 GLuint Program::getTransformFeedbackVaryingResourceIndex(const GLchar *name) const
 {
+    resolveLink();
     for (GLuint tfIndex = 0; tfIndex < mState.mLinkedTransformFeedbackVaryings.size(); ++tfIndex)
     {
         const auto &tf = mState.mLinkedTransformFeedbackVaryings[tfIndex];
@@ -2408,6 +2678,7 @@
 
 const TransformFeedbackVarying &Program::getTransformFeedbackVaryingResource(GLuint index) const
 {
+    resolveLink();
     ASSERT(index < mState.mLinkedTransformFeedbackVaryings.size());
     return mState.mLinkedTransformFeedbackVaryings[index];
 }
@@ -3638,6 +3909,7 @@
 
 bool Program::samplesFromTexture(const gl::State &state, GLuint textureID) const
 {
+    resolveLink();
     // Must be called after samplers are validated.
     ASSERT(mCachedValidateSamplersResult.valid() && mCachedValidateSamplersResult.value());