Move ProgramGL's sampler bindings to gl::Program.

This saves code duplication and makes the sampler validation a bit
simpler. In the future we might be able to use it to replace the
custom D3D logic for caching sampler info, but this relies on
some shader-specific non-merged data.

This also introduces some code duplication in Uniform1iv.

BUG=angleproject:1685

Change-Id: I258607a21886ca20766607bddf958cd511fac3f1
Reviewed-on: https://chromium-review.googlesource.com/426781
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 9252b5a..8aa6a17 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -246,6 +246,7 @@
       mAttachedVertexShader(nullptr),
       mAttachedComputeShader(nullptr),
       mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
+      mSamplerUniformRange(0, 0),
       mBinaryRetrieveableHint(false)
 {
     mComputeShaderLocalSize.fill(1);
@@ -325,7 +326,7 @@
     return -1;
 }
 
-GLuint ProgramState::getUniformIndex(const std::string &name) const
+GLuint ProgramState::getUniformIndexFromName(const std::string &name) const
 {
     size_t subscript     = GL_INVALID_INDEX;
     std::string baseName = ParseUniformName(name, &subscript);
@@ -351,6 +352,34 @@
     return GL_INVALID_INDEX;
 }
 
+GLuint ProgramState::getUniformIndexFromLocation(GLint location) const
+{
+    ASSERT(location >= 0 && static_cast<size_t>(location) < mUniformLocations.size());
+    return mUniformLocations[location].index;
+}
+
+Optional<GLuint> ProgramState::getSamplerIndex(GLint location) const
+{
+    GLuint index = getUniformIndexFromLocation(location);
+    if (!isSamplerUniformIndex(index))
+    {
+        return Optional<GLuint>::Invalid();
+    }
+
+    return getSamplerIndexFromUniformIndex(index);
+}
+
+bool ProgramState::isSamplerUniformIndex(GLuint index) const
+{
+    return index >= mSamplerUniformRange.start && index < mSamplerUniformRange.end;
+}
+
+GLuint ProgramState::getSamplerIndexFromUniformIndex(GLuint uniformIndex) const
+{
+    ASSERT(isSamplerUniformIndex(uniformIndex));
+    return uniformIndex - mSamplerUniformRange.start;
+}
+
 Program::Program(rx::GLImplFactory *factory, ResourceManager *manager, GLuint handle)
     : mProgram(factory->createProgram(mState)),
       mValidated(false),
@@ -358,8 +387,7 @@
       mDeleteStatus(false),
       mRefCount(0),
       mResourceManager(manager),
-      mHandle(handle),
-      mSamplerUniformRange(0, 0)
+      mHandle(handle)
 {
     ASSERT(mProgram);
 
@@ -723,6 +751,7 @@
     mState.mUniformBlocks.clear();
     mState.mOutputVariables.clear();
     mState.mComputeShaderLocalSize.fill(1);
+    mState.mSamplerBindings.clear();
 
     mValidated = false;
 
@@ -877,8 +906,16 @@
         mState.mOutputVariables[locationIndex] = locationData;
     }
 
-    stream.readInt(&mSamplerUniformRange.start);
-    stream.readInt(&mSamplerUniformRange.end);
+    stream.readInt(&mState.mSamplerUniformRange.start);
+    stream.readInt(&mState.mSamplerUniformRange.end);
+
+    unsigned int samplerCount = stream.readInt<unsigned int>();
+    for (unsigned int samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex)
+    {
+        GLenum textureType  = stream.readInt<GLenum>();
+        size_t bindingCount = stream.readInt<size_t>();
+        mState.mSamplerBindings.emplace_back(SamplerBinding(textureType, bindingCount));
+    }
 
     ANGLE_TRY_RESULT(mProgram->load(context->getImplementation(), mInfoLog, &stream), mLinked);
 
@@ -992,8 +1029,15 @@
         stream.writeString(outputPair.second.name);
     }
 
-    stream.writeInt(mSamplerUniformRange.start);
-    stream.writeInt(mSamplerUniformRange.end);
+    stream.writeInt(mState.mSamplerUniformRange.start);
+    stream.writeInt(mState.mSamplerUniformRange.end);
+
+    stream.writeInt(mState.mSamplerBindings.size());
+    for (const auto &samplerBinding : mState.mSamplerBindings)
+    {
+        stream.writeInt(samplerBinding.textureType);
+        stream.writeInt(samplerBinding.boundTextureUnits.size());
+    }
 
     ANGLE_TRY(mProgram->save(&stream));
 
@@ -1373,7 +1417,7 @@
 const LinkedUniform &Program::getUniformByLocation(GLint location) const
 {
     ASSERT(location >= 0 && static_cast<size_t>(location) < mState.mUniformLocations.size());
-    return mState.mUniforms[mState.mUniformLocations[location].index];
+    return mState.mUniforms[mState.getUniformIndexFromLocation(location)];
 }
 
 GLint Program::getUniformLocation(const std::string &name) const
@@ -1383,7 +1427,7 @@
 
 GLuint Program::getUniformIndex(const std::string &name) const
 {
-    return mState.getUniformIndex(name);
+    return mState.getUniformIndexFromName(name);
 }
 
 void Program::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
@@ -1572,22 +1616,12 @@
     // 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.
-    for (unsigned int samplerIndex = mSamplerUniformRange.start;
-         samplerIndex < mSamplerUniformRange.end; ++samplerIndex)
+    for (const auto &samplerBinding : mState.mSamplerBindings)
     {
-        const LinkedUniform &uniform = mState.mUniforms[samplerIndex];
-        ASSERT(uniform.isSampler());
+        GLenum textureType = samplerBinding.textureType;
 
-        if (!uniform.staticUse)
-            continue;
-
-        const GLuint *dataPtr = reinterpret_cast<const GLuint *>(uniform.getDataPtrToElement(0));
-        GLenum textureType    = SamplerTypeToTextureType(uniform.type);
-
-        for (unsigned int arrayElement = 0; arrayElement < uniform.elementCount(); ++arrayElement)
+        for (GLuint textureUnit : samplerBinding.boundTextureUnits)
         {
-            GLuint textureUnit = dataPtr[arrayElement];
-
             if (textureUnit >= caps.maxCombinedTextureImageUnits)
             {
                 if (infoLog)
@@ -2677,12 +2711,20 @@
         }
     }
 
-    mSamplerUniformRange.start = static_cast<unsigned int>(mState.mUniforms.size());
-    mSamplerUniformRange.end =
-        mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size());
+    mState.mSamplerUniformRange.start = static_cast<unsigned int>(mState.mUniforms.size());
+    mState.mSamplerUniformRange.end =
+        mState.mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size());
 
     mState.mUniforms.insert(mState.mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
 
+    // If uniform is a sampler type, insert it into the mSamplerBindings array.
+    for (const auto &samplerUniform : samplerUniforms)
+    {
+        GLenum textureType = SamplerTypeToTextureType(samplerUniform.type);
+        mState.mSamplerBindings.emplace_back(
+            SamplerBinding(textureType, samplerUniform.elementCount()));
+    }
+
     return true;
 }
 
@@ -2945,6 +2987,33 @@
     }
 }
 
+template <>
+void Program::updateSamplerUniform(const VariableLocation &locationInfo,
+                                   const uint8_t *destPointer,
+                                   GLsizei clampedCount,
+                                   const GLint *v)
+{
+    // Invalidate the validation cache only if we modify the sampler data.
+    if (mState.isSamplerUniformIndex(locationInfo.index) &&
+        memcmp(destPointer, v, sizeof(GLint) * clampedCount) != 0)
+    {
+        GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(locationInfo.index);
+        std::vector<GLuint> *boundTextureUnits =
+            &mState.mSamplerBindings[samplerIndex].boundTextureUnits;
+
+        std::copy(v, v + clampedCount, boundTextureUnits->begin() + locationInfo.element);
+        mCachedValidateSamplersResult.reset();
+    }
+}
+
+template <typename T>
+void Program::updateSamplerUniform(const VariableLocation &locationInfo,
+                                   const uint8_t *destPointer,
+                                   GLsizei clampedCount,
+                                   const T *v)
+{
+}
+
 template <typename T>
 GLsizei Program::setUniformInternal(GLint location, GLsizei countIn, int vectorSize, const T *v)
 {
@@ -2978,12 +3047,7 @@
     }
     else
     {
-        // Invalide the validation cache if we modify the sampler data.
-        if (linkedUniform->isSampler() && memcmp(destPointer, v, sizeof(T) * clampedCount) != 0)
-        {
-            mCachedValidateSamplersResult.reset();
-        }
-
+        updateSamplerUniform(locationInfo, destPointer, clampedCount, v);
         memcpy(destPointer, v, sizeof(T) * clampedCount);
     }