Re-land "Move sampler validation to the GL layer."

This previously was D3D-only, but is required for every draw call.
This completes the work of removing the D3D-specific Impl methods
from ProgramImpl.

Also add several regression tests to cover texture and sampler
validation.

Re-land with a fix for duplicate sampler active uniforms.

BUG=angleproject:1123

Change-Id: Iefef06e7901873c98bf2ba7864efd16a4c6435d3
Reviewed-on: https://chromium-review.googlesource.com/301581
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 6922adf..14a50ba 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -174,6 +174,17 @@
     }
 }
 
+bool UniformInList(const std::vector<LinkedUniform> &list, const std::string &name)
+{
+    for (const LinkedUniform &uniform : list)
+    {
+        if (uniform.name == name)
+            return true;
+    }
+
+    return false;
+}
+
 }  // anonymous namespace
 
 AttributeBindings::AttributeBindings()
@@ -359,7 +370,8 @@
       mDeleteStatus(false),
       mRefCount(0),
       mResourceManager(manager),
-      mHandle(handle)
+      mHandle(handle),
+      mSamplerUniformRange(0, 0)
 {
     ASSERT(mProgram);
 
@@ -674,12 +686,15 @@
     {
         int locationIndex = stream.readInt<int>();
         VariableLocation locationData;
-        locationData.element                  = stream.readInt<unsigned int>();
-        locationData.index                    = stream.readInt<unsigned int>();
-        locationData.name                     = stream.readString();
+        stream.readInt(&locationData.element);
+        stream.readInt(&locationData.index);
+        stream.readString(&locationData.name);
         mData.mOutputVariables[locationIndex] = locationData;
     }
 
+    stream.readInt(&mSamplerUniformRange.start);
+    stream.readInt(&mSamplerUniformRange.end);
+
     rx::LinkResult result = mProgram->load(mInfoLog, &stream);
     if (result.error.isError() || !result.linkSuccess)
     {
@@ -769,6 +784,9 @@
         stream.writeString(outputPair.second.name);
     }
 
+    stream.writeInt(mSamplerUniformRange.start);
+    stream.writeInt(mSamplerUniformRange.end);
+
     gl::Error error = mProgram->save(&stream);
     if (error.isError())
     {
@@ -1297,7 +1315,78 @@
 
 bool Program::validateSamplers(InfoLog *infoLog, const Caps &caps)
 {
-    return mProgram->validateSamplers(infoLog, caps);
+    // Skip cache if we're using an infolog, so we get the full error.
+    // Also skip the cache if the sample mapping has changed, or if we haven't ever validated.
+    if (infoLog == nullptr && mCachedValidateSamplersResult.valid())
+    {
+        return mCachedValidateSamplersResult.value();
+    }
+
+    if (mTextureUnitTypesCache.empty())
+    {
+        mTextureUnitTypesCache.resize(caps.maxCombinedTextureImageUnits, GL_NONE);
+    }
+    else
+    {
+        std::fill(mTextureUnitTypesCache.begin(), mTextureUnitTypesCache.end(), GL_NONE);
+    }
+
+    // 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)
+    {
+        const LinkedUniform &uniform = mData.mUniforms[samplerIndex];
+        ASSERT(uniform.isSampler());
+
+        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)
+        {
+            GLuint textureUnit = dataPtr[arrayElement];
+
+            if (textureUnit >= caps.maxCombinedTextureImageUnits)
+            {
+                if (infoLog)
+                {
+                    (*infoLog) << "Sampler uniform (" << textureUnit
+                               << ") exceeds GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ("
+                               << caps.maxCombinedTextureImageUnits << ")";
+                }
+
+                mCachedValidateSamplersResult = false;
+                return false;
+            }
+
+            if (mTextureUnitTypesCache[textureUnit] != GL_NONE)
+            {
+                if (textureType != mTextureUnitTypesCache[textureUnit])
+                {
+                    if (infoLog)
+                    {
+                        (*infoLog) << "Samplers of conflicting types refer to the same texture "
+                                      "image unit ("
+                                   << textureUnit << ").";
+                    }
+
+                    mCachedValidateSamplersResult = false;
+                    return false;
+                }
+            }
+            else
+            {
+                mTextureUnitTypesCache[textureUnit] = textureType;
+            }
+        }
+    }
+
+    mCachedValidateSamplersResult = true;
+    return true;
 }
 
 bool Program::isValidated() const
@@ -2086,11 +2175,13 @@
     const gl::Shader *vertexShader = mData.getAttachedVertexShader();
     VectorAndSamplerCount vsCounts;
 
+    std::vector<LinkedUniform> samplerUniforms;
+
     for (const sh::Uniform &uniform : vertexShader->getUniforms())
     {
         if (uniform.staticUse)
         {
-            vsCounts += flattenUniform(uniform, uniform.name);
+            vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
         }
     }
 
@@ -2115,7 +2206,7 @@
     {
         if (uniform.staticUse)
         {
-            fsCounts += flattenUniform(uniform, uniform.name);
+            fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
         }
     }
 
@@ -2133,11 +2224,18 @@
         return false;
     }
 
+    mSamplerUniformRange.start = static_cast<unsigned int>(mData.mUniforms.size());
+    mSamplerUniformRange.end =
+        mSamplerUniformRange.start + static_cast<unsigned int>(samplerUniforms.size());
+
+    mData.mUniforms.insert(mData.mUniforms.end(), samplerUniforms.begin(), samplerUniforms.end());
+
     return true;
 }
 
 Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable &uniform,
-                                                       const std::string &fullName)
+                                                       const std::string &fullName,
+                                                       std::vector<LinkedUniform> *samplerUniforms)
 {
     VectorAndSamplerCount vectorAndSamplerCount;
 
@@ -2152,7 +2250,7 @@
                 const sh::ShaderVariable &field  = uniform.fields[fieldIndex];
                 const std::string &fieldFullName = (fullName + elementString + "." + field.name);
 
-                vectorAndSamplerCount += flattenUniform(field, fieldFullName);
+                vectorAndSamplerCount += flattenUniform(field, fieldFullName, samplerUniforms);
             }
         }
 
@@ -2160,18 +2258,28 @@
     }
 
     // Not a struct
-    if (mData.getUniformByName(fullName) == nullptr)
+    bool isSampler = IsSamplerType(uniform.type);
+    if (!UniformInList(mData.getUniforms(), fullName) && !UniformInList(*samplerUniforms, fullName))
     {
         gl::LinkedUniform linkedUniform(uniform.type, uniform.precision, fullName,
                                         uniform.arraySize, -1,
                                         sh::BlockMemberInfo::getDefaultBlockInfo());
         linkedUniform.staticUse = true;
-        mData.mUniforms.push_back(linkedUniform);
+
+        // Store sampler uniforms separately, so we'll append them to the end of the list.
+        if (isSampler)
+        {
+            samplerUniforms->push_back(linkedUniform);
+        }
+        else
+        {
+            mData.mUniforms.push_back(linkedUniform);
+        }
     }
 
-    vectorAndSamplerCount.vectorCount =
-        (VariableRegisterCount(uniform.type) * uniform.elementCount());
-    vectorAndSamplerCount.samplerCount = (IsSamplerType(uniform.type) ? uniform.elementCount() : 0);
+    unsigned int elementCount          = uniform.elementCount();
+    vectorAndSamplerCount.vectorCount  = (VariableRegisterCount(uniform.type) * elementCount);
+    vectorAndSamplerCount.samplerCount = (isSampler ? elementCount : 0);
 
     return vectorAndSamplerCount;
 }
@@ -2294,6 +2402,12 @@
     }
     else
     {
+        // Invalide the validation cache if we modify the sampler data.
+        if (linkedUniform->isSampler() && memcmp(destPointer, v, sizeof(T) * count) != 0)
+        {
+            mCachedValidateSamplersResult.reset();
+        }
+
         memcpy(destPointer, v, sizeof(T) * count);
     }
 }