Support CHROMIUM_path_rendering fragment operations

This brings two new APIs, BindFragmentInputLocation and
ProgramPathFragmentInputGen that together dictate how the
fragment shader varyings are used.

BUG=angleproject:1382

Change-Id: I4b52fd8a3555235a73aecd4f3dba2d500789cbb0
Reviewed-on: https://chromium-review.googlesource.com/357071
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Sami Väisänen <svaisanen@nvidia.com>
Commit-Queue: Sami Väisänen <svaisanen@nvidia.com>
diff --git a/src/libANGLE/renderer/gl/ProgramGL.cpp b/src/libANGLE/renderer/gl/ProgramGL.cpp
index 69479b6..6fd1f1d 100644
--- a/src/libANGLE/renderer/gl/ProgramGL.cpp
+++ b/src/libANGLE/renderer/gl/ProgramGL.cpp
@@ -8,7 +8,9 @@
 
 #include "libANGLE/renderer/gl/ProgramGL.h"
 
+#include "common/angleutils.h"
 #include "common/debug.h"
+#include "common/string_utils.h"
 #include "common/utilities.h"
 #include "libANGLE/renderer/gl/FunctionsGL.h"
 #include "libANGLE/renderer/gl/ShaderGL.h"
@@ -23,11 +25,13 @@
 ProgramGL::ProgramGL(const gl::ProgramState &data,
                      const FunctionsGL *functions,
                      const WorkaroundsGL &workarounds,
-                     StateManagerGL *stateManager)
+                     StateManagerGL *stateManager,
+                     bool enablePathRendering)
     : ProgramImpl(data),
       mFunctions(functions),
       mWorkarounds(workarounds),
       mStateManager(stateManager),
+      mEnablePathRendering(enablePathRendering),
       mProgramID(0)
 {
     ASSERT(mFunctions);
@@ -382,6 +386,26 @@
     return true;
 }
 
+void ProgramGL::setPathFragmentInputGen(const std::string &inputName,
+                                        GLenum genMode,
+                                        GLint components,
+                                        const GLfloat *coeffs)
+{
+    ASSERT(mEnablePathRendering);
+
+    for (const auto &input : mPathRenderingFragmentInputs)
+    {
+        if (input.name == inputName)
+        {
+            mFunctions->programPathFragmentInputGenNV(mProgramID, input.location, genMode,
+                                                      components, coeffs);
+            ASSERT(mFunctions->getError() == GL_NO_ERROR);
+            return;
+        }
+    }
+
+}
+
 void ProgramGL::preLink()
 {
     // Reset the program state
@@ -389,6 +413,7 @@
     mUniformBlockRealLocationMap.clear();
     mSamplerBindings.clear();
     mUniformIndexToSamplerIndex.clear();
+    mPathRenderingFragmentInputs.clear();
 }
 
 bool ProgramGL::checkLinkStatus(gl::InfoLog &infoLog)
@@ -478,6 +503,70 @@
         samplerBinding.boundTextureUnits.resize(linkedUniform.elementCount(), 0);
         mSamplerBindings.push_back(samplerBinding);
     }
+
+    // Discover CHROMIUM_path_rendering fragment inputs if enabled.
+    if (!mEnablePathRendering)
+        return;
+
+    GLint numFragmentInputs = 0;
+    mFunctions->getProgramInterfaceiv(mProgramID, GL_FRAGMENT_INPUT_NV, GL_ACTIVE_RESOURCES,
+                                      &numFragmentInputs);
+    if (numFragmentInputs <= 0)
+        return;
+
+    GLint maxNameLength = 0;
+    mFunctions->getProgramInterfaceiv(mProgramID, GL_FRAGMENT_INPUT_NV, GL_MAX_NAME_LENGTH,
+                                      &maxNameLength);
+    ASSERT(maxNameLength);
+
+    for (GLint i = 0; i < numFragmentInputs; ++i)
+    {
+        std::string name;
+        name.resize(maxNameLength);
+
+        GLsizei nameLen = 0;
+        mFunctions->getProgramResourceName(mProgramID, GL_FRAGMENT_INPUT_NV, i, maxNameLength,
+                                           &nameLen, &name[0]);
+        name.resize(nameLen);
+
+        // Ignore built-ins
+        if (angle::BeginsWith(name, "gl_"))
+            continue;
+
+        const GLenum kQueryProperties[] = {GL_LOCATION, GL_ARRAY_SIZE};
+        GLint queryResults[ArraySize(kQueryProperties)];
+        GLsizei queryLength = 0;
+
+        mFunctions->getProgramResourceiv(mProgramID, GL_FRAGMENT_INPUT_NV, i,
+                                         ArraySize(kQueryProperties), kQueryProperties,
+                                         ArraySize(queryResults), &queryLength, queryResults);
+
+        ASSERT(queryLength == ArraySize(kQueryProperties));
+
+        PathRenderingFragmentInput input;
+        input.name     = name;
+        input.location = queryResults[0];
+        mPathRenderingFragmentInputs.push_back(std::move(input));
+
+        // If the input is an array it's denoted by [0] suffix on the variable
+        // name. We'll then create an entry per each array index where index > 0
+        if (angle::EndsWith(name, "[0]"))
+        {
+            // drop the suffix
+            name.resize(name.size() - 3);
+
+            const auto arraySize    = queryResults[1];
+            const auto baseLocation = queryResults[0];
+
+            for (GLint i = 1; i < arraySize; ++i)
+            {
+                PathRenderingFragmentInput input;
+                input.name     = name + "[" + std::to_string(i) + "]";
+                input.location = baseLocation + i;
+                mPathRenderingFragmentInputs.push_back(std::move(input));
+            }
+        }
+    }
 }
 
 }  // namespace rx