Add getUniform impl methods.

This will let us remove some of the uniform data management code in
the GL front-end, and simplify the GL back-end. It will also enable
us to implement uniform data more efficiently in the D3D11 back-end,
and probably Vulkan back-end later.

This also implements a new impl method for the ProgramGL class to
flag optimized-out uniforms as no longer used, post-link. This is
important because otherwise the optimized uniforms get assigned
valid locations, and then the getUniform calls are expected to
succeed.

We also use a workaround for uniform value queries for the GL
back-end. It seems as though some drivers (seen on NVIDIA and AMD)
may not properly clamp to the maximum representable integer value
when querying out-of-range floating point values. Work around this by
always calling the driver with the proper type and then casting the
value in ANGLE.

BUG=angleproject:1390

Change-Id: I03dc2382e7af52455c356a2bf3971a4d1bd46ec6
Reviewed-on: https://chromium-review.googlesource.com/616785
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 1f47b9d..cc37d2f 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -788,6 +788,19 @@
 
     setUniformValuesFromBindingQualifiers();
 
+    // Mark implementation-specific unreferenced uniforms as ignored.
+    mProgram->markUnusedUniformLocations(&mState.mUniformLocations);
+
+    // Update sampler bindings with unreferenced uniforms.
+    for (const auto &location : mState.mUniformLocations)
+    {
+        if (!location.used && mState.isSamplerUniformIndex(location.index))
+        {
+            GLuint samplerIndex = mState.getSamplerIndexFromUniformIndex(location.index);
+            mState.mSamplerBindings[samplerIndex].unreferenced = true;
+        }
+    }
+
     // Save to the program cache.
     if (cache && (mState.mLinkedTransformFeedbackVaryings.empty() ||
                   !context->getWorkarounds().disableProgramCachingForTransformFeedback))
@@ -1444,19 +1457,55 @@
     mProgram->setUniformMatrix4x3fv(location, clampedCount, transpose, v);
 }
 
-void Program::getUniformfv(GLint location, GLfloat *v) const
+void Program::getUniformfv(const Context *context, GLint location, GLfloat *v) const
 {
-    getUniformInternal(location, v);
+    const auto &uniformLocation = mState.getUniformLocations()[location];
+    const auto &uniform         = mState.getUniforms()[uniformLocation.index];
+
+    GLenum nativeType = gl::VariableComponentType(uniform.type);
+    if (nativeType == GL_FLOAT)
+    {
+        mProgram->getUniformfv(context, location, v);
+    }
+    else
+    {
+        getUniformInternal(context, v, location, nativeType,
+                           gl::VariableComponentCount(uniform.type));
+    }
 }
 
-void Program::getUniformiv(GLint location, GLint *v) const
+void Program::getUniformiv(const Context *context, GLint location, GLint *v) const
 {
-    getUniformInternal(location, v);
+    const auto &uniformLocation = mState.getUniformLocations()[location];
+    const auto &uniform         = mState.getUniforms()[uniformLocation.index];
+
+    GLenum nativeType = gl::VariableComponentType(uniform.type);
+    if (nativeType == GL_INT || nativeType == GL_BOOL)
+    {
+        mProgram->getUniformiv(context, location, v);
+    }
+    else
+    {
+        getUniformInternal(context, v, location, nativeType,
+                           gl::VariableComponentCount(uniform.type));
+    }
 }
 
-void Program::getUniformuiv(GLint location, GLuint *v) const
+void Program::getUniformuiv(const Context *context, GLint location, GLuint *v) const
 {
-    getUniformInternal(location, v);
+    const auto &uniformLocation = mState.getUniformLocations()[location];
+    const auto &uniform         = mState.getUniforms()[uniformLocation.index];
+
+    GLenum nativeType = gl::VariableComponentType(uniform.type);
+    if (nativeType == GL_UNSIGNED_INT)
+    {
+        mProgram->getUniformuiv(context, location, v);
+    }
+    else
+    {
+        getUniformInternal(context, v, location, nativeType,
+                           gl::VariableComponentCount(uniform.type));
+    }
 }
 
 void Program::flagForDeletion()
@@ -1506,6 +1555,9 @@
     // DrawArrays and DrawElements will issue the INVALID_OPERATION error.
     for (const auto &samplerBinding : mState.mSamplerBindings)
     {
+        if (samplerBinding.unreferenced)
+            continue;
+
         GLenum textureType = samplerBinding.textureType;
 
         for (GLuint textureUnit : samplerBinding.boundTextureUnits)
@@ -1876,7 +1928,7 @@
         const auto &samplerUniform = mState.mUniforms[samplerIndex];
         GLenum textureType         = SamplerTypeToTextureType(samplerUniform.type);
         mState.mSamplerBindings.emplace_back(
-            SamplerBinding(textureType, samplerUniform.elementCount()));
+            SamplerBinding(textureType, samplerUniform.elementCount(), false));
     }
 }
 
@@ -2984,39 +3036,52 @@
     return clampedCount;
 }
 
+// Driver differences mean that doing the uniform value cast ourselves gives consistent results.
+// EG: on NVIDIA drivers, it was observed that getUniformi for MAX_INT+1 returned MIN_INT.
 template <typename DestT>
-void Program::getUniformInternal(GLint location, DestT *dataOut) const
+void Program::getUniformInternal(const Context *context,
+                                 DestT *dataOut,
+                                 GLint location,
+                                 GLenum nativeType,
+                                 int components) const
 {
-    const VariableLocation &locationInfo = mState.mUniformLocations[location];
-    const LinkedUniform &uniform         = mState.mUniforms[locationInfo.index];
-
-    const uint8_t *srcPointer = uniform.getDataPtrToElement(locationInfo.element);
-
-    GLenum componentType = VariableComponentType(uniform.type);
-    if (componentType == GLTypeToGLenum<DestT>::value)
+    switch (nativeType)
     {
-        memcpy(dataOut, srcPointer, uniform.getElementSize());
-        return;
-    }
-
-    int components = VariableComponentCount(uniform.type);
-
-    switch (componentType)
-    {
-        case GL_INT:
-            UniformStateQueryCastLoop<GLint>(dataOut, srcPointer, components);
-            break;
-        case GL_UNSIGNED_INT:
-            UniformStateQueryCastLoop<GLuint>(dataOut, srcPointer, components);
-            break;
         case GL_BOOL:
-            UniformStateQueryCastLoop<GLboolean>(dataOut, srcPointer, components);
+        {
+            GLint tempValue[16] = {0};
+            mProgram->getUniformiv(context, location, tempValue);
+            UniformStateQueryCastLoop<GLboolean>(
+                dataOut, reinterpret_cast<const uint8_t *>(tempValue), components);
             break;
+        }
+        case GL_INT:
+        {
+            GLint tempValue[16] = {0};
+            mProgram->getUniformiv(context, location, tempValue);
+            UniformStateQueryCastLoop<GLint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
+                                             components);
+            break;
+        }
+        case GL_UNSIGNED_INT:
+        {
+            GLuint tempValue[16] = {0};
+            mProgram->getUniformuiv(context, location, tempValue);
+            UniformStateQueryCastLoop<GLuint>(dataOut, reinterpret_cast<const uint8_t *>(tempValue),
+                                              components);
+            break;
+        }
         case GL_FLOAT:
-            UniformStateQueryCastLoop<GLfloat>(dataOut, srcPointer, components);
+        {
+            GLfloat tempValue[16] = {0};
+            mProgram->getUniformfv(context, location, tempValue);
+            UniformStateQueryCastLoop<GLfloat>(
+                dataOut, reinterpret_cast<const uint8_t *>(tempValue), components);
             break;
+        }
         default:
             UNREACHABLE();
+            break;
     }
 }