Implement textureGrad.

BUG=angle:564

Change-Id: I95dcd95a274f6d560250c197be0d6e64a5b23516
Reviewed-on: https://chromium-review.googlesource.com/187081
Tested-by: Nicolas Capens <nicolascapens@chromium.org>
Reviewed-by: Shannon Woods <shannonwoods@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 69a1faf..3979a79 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -511,6 +511,14 @@
     symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler3D, int3, int1, int3);
     symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2DArray, int3, int1, int2);
 
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2D, float2, float2, float2);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler3D, float3, float3, float3);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsamplerCube, float3, float3, float3);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DShadow, float3, float2, float2);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", samplerCubeShadow, float4, float3, float3);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2DArray, float3, float2, float2);
+    symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DArrayShadow, float4, float2, float2);
+
     //
     // Depth range in window coordinates
     //
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 03ff3fc..860e29a 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -61,6 +61,7 @@
       case LOD0:     name += "Lod0";  break;
       case SIZE:     name += "Size";  break;
       case FETCH:    name += "Fetch"; break;
+      case GRAD:     name += "Grad";  break;
       default: UNREACHABLE();
     }
 
@@ -1022,6 +1023,33 @@
             }
         }
 
+        if (textureFunction->method == TextureFunction::GRAD)
+        {
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:
+              case EbtISampler2D:
+              case EbtUSampler2D:
+              case EbtSampler2DArray:
+              case EbtISampler2DArray:
+              case EbtUSampler2DArray:
+              case EbtSampler2DShadow:
+              case EbtSampler2DArrayShadow:
+                out << ", float2 ddx, float2 ddy";
+                break;
+              case EbtSampler3D:
+              case EbtISampler3D:
+              case EbtUSampler3D:
+              case EbtSamplerCube:
+              case EbtISamplerCube:
+              case EbtUSamplerCube:
+              case EbtSamplerCubeShadow:
+                out << ", float3 ddx, float3 ddy";
+                break;
+              default: UNREACHABLE();
+            }
+        }
+
         switch(textureFunction->method)
         {
           case TextureFunction::IMPLICIT:                        break;
@@ -1030,6 +1058,7 @@
           case TextureFunction::LOD0:                            break;
           case TextureFunction::SIZE:                            break;
           case TextureFunction::FETCH:    out << ", int mip";    break;
+          case TextureFunction::GRAD:                            break;
           default: UNREACHABLE();
         }
 
@@ -1146,6 +1175,11 @@
                                     out << "    lod += bias;\n";
                                 }
                             }
+                            else if (textureFunction->method == TextureFunction::GRAD)
+                            {
+                                out << "    x.GetDimensions(0, width, height, layers, levels);\n"
+                                       "    float lod = log2(max(length(ddx), length(ddy)));\n";
+                            }
 
                             out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
                         }
@@ -1180,6 +1214,11 @@
                             {
                                 out << "    x.GetDimensions(0, width, height, levels);\n";
                             }
+                            else if (textureFunction->method == TextureFunction::GRAD)
+                            {
+                                out << "    x.GetDimensions(0, width, height, levels);\n"
+                                       "    float lod = log2(max(length(ddx), length(ddy)));\n";
+                            }
 
                             out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
                         }
@@ -1211,6 +1250,11 @@
                                 out << "    lod += bias;\n";
                             }
                         }
+                        else if (textureFunction->method == TextureFunction::GRAD)
+                        {
+                            out << "    x.GetDimensions(0, width, height, depth, levels);\n"
+                                   "    float lod = log2(max(length(ddx), length(ddy)));\n";
+                        }
 
                         out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
                     }
@@ -1243,8 +1287,23 @@
             }
             else if (mOutputType == SH_HLSL11_OUTPUT)
             {
-                if (IsIntegerSampler(textureFunction->sampler) ||
-                    textureFunction->method == TextureFunction::FETCH)
+                if (textureFunction->method == TextureFunction::GRAD)
+                {
+                    if (IsIntegerSampler(textureFunction->sampler))
+                    {
+                        out << "x.Load(";
+                    }
+                    else if (IsShadowSampler(textureFunction->sampler))
+                    {
+                        out << "x.SampleCmpLevelZero(s, ";
+                    }
+                    else
+                    {
+                        out << "x.SampleGrad(s, ";
+                    }
+                }
+                else if (IsIntegerSampler(textureFunction->sampler) ||
+                         textureFunction->method == TextureFunction::FETCH)
                 {
                     out << "x.Load(";
                 }
@@ -1359,8 +1418,29 @@
                     out << ", " + addressz + ("t.z" + proj) + close;
                 }
 
-                if (IsIntegerSampler(textureFunction->sampler) ||
-                    textureFunction->method == TextureFunction::FETCH)
+                if (textureFunction->method == TextureFunction::GRAD)
+                {
+                    if (IsIntegerSampler(textureFunction->sampler))
+                    {
+                         out << ", mip)";
+                    }
+                    else if (IsShadowSampler(textureFunction->sampler))
+                    {
+                       // Compare value
+                        switch(textureFunction->coords)
+                        {
+                          case 3: out << "), t.z"; break;
+                          case 4: out << "), t.w"; break;
+                          default: UNREACHABLE();
+                        }
+                    }
+                    else
+                    {
+                        out << "), ddx, ddy";
+                    }
+                }
+                else if (IsIntegerSampler(textureFunction->sampler) ||
+                         textureFunction->method == TextureFunction::FETCH)
                 {
                     out << ", mip)";
                 }
@@ -2400,6 +2480,10 @@
                     textureFunction.method = TextureFunction::FETCH;
                     textureFunction.offset = true;
                 }
+                else if (name == "textureGrad")
+                {
+                    textureFunction.method = TextureFunction::GRAD;
+                }
                 else UNREACHABLE();
 
                 if (textureFunction.method == TextureFunction::IMPLICIT)   // Could require lod 0 or have a bias argument
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index e8d24f8..79e5572 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -105,7 +105,8 @@
             LOD,
             LOD0,
             SIZE,   // textureSize()
-            FETCH
+            FETCH,
+            GRAD
         };
 
         TBasicType sampler;