Revert "Refactor texture function handling in OutputHLSL"

It triggered an include guard warning on Windows Clang

This reverts commit 6f6c5580553d1f3c584df692823c2f5640e23d88.

Change-Id: Ibd4f2851f311a494f16376d8eed38f3119594761
Reviewed-on: https://chromium-review.googlesource.com/338933
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 98f7437..cd95b02 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -21,7 +21,6 @@
 #include "compiler/translator/RemoveSwitchFallThrough.h"
 #include "compiler/translator/SearchSymbol.h"
 #include "compiler/translator/StructureHLSL.h"
-#include "compiler/translator/TextureFunctionHLSL.h"
 #include "compiler/translator/TranslatorHLSL.h"
 #include "compiler/translator/UniformHLSL.h"
 #include "compiler/translator/UtilsHLSL.h"
@@ -75,11 +74,101 @@
     return constUnionIterated;
 }
 
+void OutputIntTexCoordWrap(TInfoSinkBase &out,
+                           const char *wrapMode,
+                           const char *size,
+                           const TString &texCoord,
+                           const TString &texCoordOffset,
+                           const char *texCoordOutName)
+{
+    // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
+    // but rather use equivalent formulas that map better to HLSL.
+    out << "int " << texCoordOutName << ";\n";
+    out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
+        << ") / " << size << ";\n";
+
+    // CLAMP_TO_EDGE
+    out << "if (" << wrapMode << " == 1)\n";
+    out << "{\n";
+    out << "    " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
+        << "Offset)), 0, int(" << size << ") - 1);\n";
+    out << "}\n";
+
+    // MIRRORED_REPEAT
+    out << "else if (" << wrapMode << " == 3)\n";
+    out << "{\n";
+    out << "    float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
+        << "Offset) * 0.5) * 2.0 - 1.0);\n";
+    out << "    " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
+    out << "}\n";
+
+    // REPEAT
+    out << "else\n";
+    out << "{\n";
+    out << "    " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
+        << "Offset)));\n";
+    out << "}\n";
+}
+
 } // namespace
 
 namespace sh
 {
 
+TString OutputHLSL::TextureFunction::name() const
+{
+    TString name = "gl_texture";
+
+    // We need to include full the sampler type in the function name to make the signature unique
+    // on D3D11, where samplers are passed to texture functions as indices.
+    name += TextureTypeSuffix(this->sampler);
+
+    if (proj)
+    {
+        name += "Proj";
+    }
+
+    if (offset)
+    {
+        name += "Offset";
+    }
+
+    switch(method)
+    {
+      case IMPLICIT:                  break;
+      case BIAS:                      break;   // Extra parameter makes the signature unique
+      case LOD:      name += "Lod";   break;
+      case LOD0:     name += "Lod0";  break;
+      case LOD0BIAS: name += "Lod0";  break;   // Extra parameter makes the signature unique
+      case SIZE:     name += "Size";  break;
+      case FETCH:    name += "Fetch"; break;
+      case GRAD:     name += "Grad";  break;
+      default: UNREACHABLE();
+    }
+
+    return name + "(";
+}
+
+bool OutputHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
+{
+    if (sampler < rhs.sampler) return true;
+    if (sampler > rhs.sampler) return false;
+
+    if (coords < rhs.coords)   return true;
+    if (coords > rhs.coords)   return false;
+
+    if (!proj && rhs.proj)     return true;
+    if (proj && !rhs.proj)     return false;
+
+    if (!offset && rhs.offset) return true;
+    if (offset && !rhs.offset) return false;
+
+    if (method < rhs.method)   return true;
+    if (method > rhs.method)   return false;
+
+    return false;
+}
+
 OutputHLSL::OutputHLSL(sh::GLenum shaderType, int shaderVersion,
     const TExtensionBehavior &extensionBehavior,
     const char *sourcePath, ShShaderOutput outputType,
@@ -122,7 +211,6 @@
 
     mStructureHLSL = new StructureHLSL;
     mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms);
-    mTextureFunctionHLSL = new TextureFunctionHLSL;
 
     if (mOutputType == SH_HLSL_3_0_OUTPUT)
     {
@@ -140,7 +228,6 @@
 {
     SafeDelete(mStructureHLSL);
     SafeDelete(mUniformHLSL);
-    SafeDelete(mTextureFunctionHLSL);
     for (auto &eqFunction : mStructEqualityFunctions)
     {
         SafeDelete(eqFunction);
@@ -605,7 +692,809 @@
         }
     }
 
-    mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType);
+    for (TextureFunctionSet::const_iterator textureFunction = mUsesTexture.begin(); textureFunction != mUsesTexture.end(); textureFunction++)
+    {
+        // Return type
+        if (textureFunction->method == TextureFunction::SIZE)
+        {
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:            out << "int2 "; break;
+              case EbtSampler3D:            out << "int3 "; break;
+              case EbtSamplerCube:          out << "int2 "; break;
+              case EbtSampler2DArray:       out << "int3 "; break;
+              case EbtISampler2D:           out << "int2 "; break;
+              case EbtISampler3D:           out << "int3 "; break;
+              case EbtISamplerCube:         out << "int2 "; break;
+              case EbtISampler2DArray:      out << "int3 "; break;
+              case EbtUSampler2D:           out << "int2 "; break;
+              case EbtUSampler3D:           out << "int3 "; break;
+              case EbtUSamplerCube:         out << "int2 "; break;
+              case EbtUSampler2DArray:      out << "int3 "; break;
+              case EbtSampler2DShadow:      out << "int2 "; break;
+              case EbtSamplerCubeShadow:    out << "int2 "; break;
+              case EbtSampler2DArrayShadow: out << "int3 "; break;
+              default: UNREACHABLE();
+            }
+        }
+        else   // Sampling function
+        {
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:            out << "float4 "; break;
+              case EbtSampler3D:            out << "float4 "; break;
+              case EbtSamplerCube:          out << "float4 "; break;
+              case EbtSampler2DArray:       out << "float4 "; break;
+              case EbtISampler2D:           out << "int4 ";   break;
+              case EbtISampler3D:           out << "int4 ";   break;
+              case EbtISamplerCube:         out << "int4 ";   break;
+              case EbtISampler2DArray:      out << "int4 ";   break;
+              case EbtUSampler2D:           out << "uint4 ";  break;
+              case EbtUSampler3D:           out << "uint4 ";  break;
+              case EbtUSamplerCube:         out << "uint4 ";  break;
+              case EbtUSampler2DArray:      out << "uint4 ";  break;
+              case EbtSampler2DShadow:      out << "float ";  break;
+              case EbtSamplerCubeShadow:    out << "float ";  break;
+              case EbtSampler2DArrayShadow: out << "float ";  break;
+              default: UNREACHABLE();
+            }
+        }
+
+        // Function name
+        out << textureFunction->name();
+
+        // Argument list
+        int hlslCoords = 4;
+
+        if (mOutputType == SH_HLSL_3_0_OUTPUT)
+        {
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:   out << "sampler2D s";   hlslCoords = 2; break;
+              case EbtSamplerCube: out << "samplerCUBE s"; hlslCoords = 3; break;
+              default: UNREACHABLE();
+            }
+
+            switch(textureFunction->method)
+            {
+              case TextureFunction::IMPLICIT:                 break;
+              case TextureFunction::BIAS:     hlslCoords = 4; break;
+              case TextureFunction::LOD:      hlslCoords = 4; break;
+              case TextureFunction::LOD0:     hlslCoords = 4; break;
+              case TextureFunction::LOD0BIAS: hlslCoords = 4; break;
+              default: UNREACHABLE();
+            }
+        }
+        else
+        {
+            hlslCoords = HLSLTextureCoordsCount(textureFunction->sampler);
+            if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+            {
+                out << TextureString(textureFunction->sampler) << " x, "
+                    << SamplerString(textureFunction->sampler) << " s";
+            }
+            else
+            {
+                ASSERT(mOutputType == SH_HLSL_4_1_OUTPUT);
+                out << "const uint samplerIndex";
+            }
+        }
+
+        if (textureFunction->method == TextureFunction::FETCH)   // Integer coordinates
+        {
+            switch(textureFunction->coords)
+            {
+              case 2: out << ", int2 t"; break;
+              case 3: out << ", int3 t"; break;
+              default: UNREACHABLE();
+            }
+        }
+        else   // Floating-point coordinates (except textureSize)
+        {
+            switch(textureFunction->coords)
+            {
+              case 1: out << ", int lod";  break;   // textureSize()
+              case 2: out << ", float2 t"; break;
+              case 3: out << ", float3 t"; break;
+              case 4: out << ", float4 t"; break;
+              default: UNREACHABLE();
+            }
+        }
+
+        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;
+          case TextureFunction::BIAS:                            break;   // Comes after the offset parameter
+          case TextureFunction::LOD:      out << ", float lod";  break;
+          case TextureFunction::LOD0:                            break;
+          case TextureFunction::LOD0BIAS:                        break;   // Comes after the offset parameter
+          case TextureFunction::SIZE:                            break;
+          case TextureFunction::FETCH:    out << ", int mip";    break;
+          case TextureFunction::GRAD:                            break;
+          default: UNREACHABLE();
+        }
+
+        if (textureFunction->offset)
+        {
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:            out << ", int2 offset"; break;
+              case EbtSampler3D:            out << ", int3 offset"; break;
+              case EbtSampler2DArray:       out << ", int2 offset"; break;
+              case EbtISampler2D:           out << ", int2 offset"; break;
+              case EbtISampler3D:           out << ", int3 offset"; break;
+              case EbtISampler2DArray:      out << ", int2 offset"; break;
+              case EbtUSampler2D:           out << ", int2 offset"; break;
+              case EbtUSampler3D:           out << ", int3 offset"; break;
+              case EbtUSampler2DArray:      out << ", int2 offset"; break;
+              case EbtSampler2DShadow:      out << ", int2 offset"; break;
+              case EbtSampler2DArrayShadow: out << ", int2 offset"; break;
+              default: UNREACHABLE();
+            }
+        }
+
+        if (textureFunction->method == TextureFunction::BIAS ||
+            textureFunction->method == TextureFunction::LOD0BIAS)
+        {
+            out << ", float bias";
+        }
+
+        out << ")\n"
+               "{\n";
+
+        // In some cases we use a variable to store the texture/sampler objects, but to work around
+        // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
+        // sampling we need to call the function directly on a reference to the array. The bug was
+        // found using dEQP-GLES3.functional.shaders.discard*loop_texture* tests.
+        TString textureReference("x");
+        TString samplerReference("s");
+        if (mOutputType == SH_HLSL_4_1_OUTPUT)
+        {
+            TString suffix = TextureGroupSuffix(textureFunction->sampler);
+            if (TextureGroup(textureFunction->sampler) == HLSL_TEXTURE_2D)
+            {
+                textureReference = TString("textures") + suffix + "[samplerIndex]";
+                samplerReference = TString("samplers") + suffix + "[samplerIndex]";
+            }
+            else
+            {
+                out << "    const uint textureIndex = samplerIndex - textureIndexOffset" << suffix
+                    << ";\n";
+                textureReference = TString("textures") + suffix + "[textureIndex]";
+                out << "    const uint samplerArrayIndex = samplerIndex - samplerIndexOffset"
+                    << suffix << ";\n";
+                samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]";
+            }
+        }
+
+        if (textureFunction->method == TextureFunction::SIZE)
+        {
+            out << "int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
+            if (IsSampler2D(textureFunction->sampler) || IsSamplerCube(textureFunction->sampler))
+            {
+                if (IsSamplerArray(textureFunction->sampler) ||
+                    (IsIntegerSampler(textureFunction->sampler) &&
+                     IsSamplerCube(textureFunction->sampler)))
+                {
+                    out << "    uint width; uint height; uint layers; uint numberOfLevels;\n"
+                        << "    " << textureReference << ".GetDimensions(baseLevel + lod, width, "
+                                                         "height, layers, numberOfLevels);\n";
+                }
+                else
+                {
+                    out << "    uint width; uint height; uint numberOfLevels;\n"
+                        << "    " << textureReference
+                        << ".GetDimensions(baseLevel + lod, width, height, numberOfLevels);\n";
+                }
+            }
+            else if (IsSampler3D(textureFunction->sampler))
+            {
+                out << "    uint width; uint height; uint depth; uint numberOfLevels;\n"
+                    << "    " << textureReference
+                    << ".GetDimensions(baseLevel + lod, width, height, depth, numberOfLevels);\n";
+            }
+            else UNREACHABLE();
+
+            switch(textureFunction->sampler)
+            {
+              case EbtSampler2D:            out << "    return int2(width, height);";         break;
+              case EbtSampler3D:            out << "    return int3(width, height, depth);";  break;
+              case EbtSamplerCube:          out << "    return int2(width, height);";         break;
+              case EbtSampler2DArray:       out << "    return int3(width, height, layers);"; break;
+              case EbtISampler2D:           out << "    return int2(width, height);";         break;
+              case EbtISampler3D:           out << "    return int3(width, height, depth);";  break;
+              case EbtISamplerCube:         out << "    return int2(width, height);";         break;
+              case EbtISampler2DArray:      out << "    return int3(width, height, layers);"; break;
+              case EbtUSampler2D:           out << "    return int2(width, height);";         break;
+              case EbtUSampler3D:           out << "    return int3(width, height, depth);";  break;
+              case EbtUSamplerCube:         out << "    return int2(width, height);";         break;
+              case EbtUSampler2DArray:      out << "    return int3(width, height, layers);"; break;
+              case EbtSampler2DShadow:      out << "    return int2(width, height);";         break;
+              case EbtSamplerCubeShadow:    out << "    return int2(width, height);";         break;
+              case EbtSampler2DArrayShadow: out << "    return int3(width, height, layers);"; break;
+              default: UNREACHABLE();
+            }
+        }
+        else
+        {
+            TString texCoordX("t.x");
+            TString texCoordY("t.y");
+            TString texCoordZ("t.z");
+            TString proj = "";
+
+            if (textureFunction->proj)
+            {
+                switch (textureFunction->coords)
+                {
+                    case 3:
+                        proj = " / t.z";
+                        break;
+                    case 4:
+                        proj = " / t.w";
+                        break;
+                    default:
+                        UNREACHABLE();
+                }
+                if (proj != "")
+                {
+                    texCoordX = "(" + texCoordX + proj + ")";
+                    texCoordY = "(" + texCoordY + proj + ")";
+                    texCoordZ = "(" + texCoordZ + proj + ")";
+                }
+            }
+
+            if (IsIntegerSampler(textureFunction->sampler) && IsSamplerCube(textureFunction->sampler))
+            {
+                out << "    float width; float height; float layers; float levels;\n";
+
+                out << "    uint mip = 0;\n";
+
+                out << "    " << textureReference
+                    << ".GetDimensions(mip, width, height, layers, levels);\n";
+
+                out << "    bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n";
+                out << "    bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n";
+                out << "    bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
+                out << "    bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || (zMajor && t.z < 0.0f);\n";
+
+                // FACE_POSITIVE_X = 000b
+                // FACE_NEGATIVE_X = 001b
+                // FACE_POSITIVE_Y = 010b
+                // FACE_NEGATIVE_Y = 011b
+                // FACE_POSITIVE_Z = 100b
+                // FACE_NEGATIVE_Z = 101b
+                out << "    int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
+
+                out << "    float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
+                out << "    float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
+                out << "    float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
+
+                out << "    t.x = (u * 0.5f / m) + 0.5f;\n";
+                out << "    t.y = (v * 0.5f / m) + 0.5f;\n";
+
+                // Mip level computation.
+                if (textureFunction->method == TextureFunction::IMPLICIT ||
+                    textureFunction->method == TextureFunction::LOD ||
+                    textureFunction->method == TextureFunction::GRAD)
+                {
+                    if (textureFunction->method == TextureFunction::IMPLICIT)
+                    {
+                        out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
+                               "    float2 dx = ddx(tSized);\n"
+                               "    float2 dy = ddy(tSized);\n"
+                               "    float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n";
+                    }
+                    else if (textureFunction->method == TextureFunction::GRAD)
+                    {
+                        // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
+                        // derivatives of P are assumed to be in the coordinate system used before
+                        // texture coordinates are projected onto the appropriate cube face."
+                        // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
+                        // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
+                        // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
+                        // Determine the derivatives of u, v and m
+                        out << "    float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
+                               ": ddx[0]);\n"
+                               "    float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
+                               ": ddy[0]);\n"
+                               "    float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
+                               "    float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
+                               "    float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
+                               "    float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
+                        // Now determine the derivatives of the face coordinates, using the
+                        // derivatives calculated above.
+                        // d / dx (u(x) * 0.5 / m(x) + 0.5)
+                        // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
+                        out << "    float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
+                               "    float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
+                               "    float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
+                               "    float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
+                               "    float2 sizeVec = float2(width, height);\n"
+                               "    float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
+                               "    float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
+                        // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
+                        // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
+                        out << "    float lengthfaceddx2 = dot(faceddx, faceddx);\n"
+                               "    float lengthfaceddy2 = dot(faceddy, faceddy);\n"
+                               "    float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * "
+                               "0.5f;\n";
+                    }
+                    out << "    mip = uint(min(max(round(lod), 0), levels - 1));\n"
+                        << "    " << textureReference
+                        << ".GetDimensions(mip, width, height, layers, levels);\n";
+                }
+
+                // Convert from normalized floating-point to integer
+                texCoordX = "int(floor(width * frac(" + texCoordX + ")))";
+                texCoordY = "int(floor(height * frac(" + texCoordY + ")))";
+                texCoordZ = "face";
+            }
+            else if (IsIntegerSampler(textureFunction->sampler) &&
+                     textureFunction->method != TextureFunction::FETCH)
+            {
+                if (IsSampler2D(textureFunction->sampler))
+                {
+                    if (IsSamplerArray(textureFunction->sampler))
+                    {
+                        out << "    float width; float height; float layers; float levels;\n";
+
+                        if (textureFunction->method == TextureFunction::LOD0)
+                        {
+                            out << "    uint mip = 0;\n";
+                        }
+                        else if (textureFunction->method == TextureFunction::LOD0BIAS)
+                        {
+                            out << "    uint mip = bias;\n";
+                        }
+                        else
+                        {
+
+                            out << "    " << textureReference
+                                << ".GetDimensions(0, width, height, layers, levels);\n";
+                            if (textureFunction->method == TextureFunction::IMPLICIT ||
+                                textureFunction->method == TextureFunction::BIAS)
+                            {
+                                out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
+                                       "    float dx = length(ddx(tSized));\n"
+                                       "    float dy = length(ddy(tSized));\n"
+                                       "    float lod = log2(max(dx, dy));\n";
+
+                                if (textureFunction->method == TextureFunction::BIAS)
+                                {
+                                    out << "    lod += bias;\n";
+                                }
+                            }
+                            else if (textureFunction->method == TextureFunction::GRAD)
+                            {
+                                out << "    float2 sizeVec = float2(width, height);\n"
+                                       "    float2 sizeDdx = ddx * sizeVec;\n"
+                                       "    float2 sizeDdy = ddy * sizeVec;\n"
+                                       "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
+                                       "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+                            }
+
+                            out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+                        }
+
+                        out << "    " << textureReference
+                            << ".GetDimensions(mip, width, height, layers, levels);\n";
+                    }
+                    else
+                    {
+                        out << "    float width; float height; float levels;\n";
+
+                        if (textureFunction->method == TextureFunction::LOD0)
+                        {
+                            out << "    uint mip = 0;\n";
+                        }
+                        else if (textureFunction->method == TextureFunction::LOD0BIAS)
+                        {
+                            out << "    uint mip = bias;\n";
+                        }
+                        else
+                        {
+                            out << "    " << textureReference
+                                << ".GetDimensions(0, width, height, levels);\n";
+
+                            if (textureFunction->method == TextureFunction::IMPLICIT ||
+                                textureFunction->method == TextureFunction::BIAS)
+                            {
+                                out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
+                                       "    float dx = length(ddx(tSized));\n"
+                                       "    float dy = length(ddy(tSized));\n"
+                                       "    float lod = log2(max(dx, dy));\n";
+
+                                if (textureFunction->method == TextureFunction::BIAS)
+                                {
+                                    out << "    lod += bias;\n";
+                                }
+                            }
+                            else if (textureFunction->method == TextureFunction::GRAD)
+                            {
+                                out << "    float2 sizeVec = float2(width, height);\n"
+                                       "    float2 sizeDdx = ddx * sizeVec;\n"
+                                       "    float2 sizeDdy = ddy * sizeVec;\n"
+                                       "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
+                                       "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+                            }
+
+                            out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+                        }
+
+                        out << "    " << textureReference
+                            << ".GetDimensions(mip, width, height, levels);\n";
+                    }
+                }
+                else if (IsSampler3D(textureFunction->sampler))
+                {
+                    out << "    float width; float height; float depth; float levels;\n";
+
+                    if (textureFunction->method == TextureFunction::LOD0)
+                    {
+                        out << "    uint mip = 0;\n";
+                    }
+                    else if (textureFunction->method == TextureFunction::LOD0BIAS)
+                    {
+                        out << "    uint mip = bias;\n";
+                    }
+                    else
+                    {
+                        out << "    " << textureReference
+                            << ".GetDimensions(0, width, height, depth, levels);\n";
+
+                        if (textureFunction->method == TextureFunction::IMPLICIT ||
+                            textureFunction->method == TextureFunction::BIAS)
+                        {
+                            out << "    float3 tSized = float3(t.x * width, t.y * height, t.z * "
+                                   "depth);\n"
+                                   "    float dx = length(ddx(tSized));\n"
+                                   "    float dy = length(ddy(tSized));\n"
+                                   "    float lod = log2(max(dx, dy));\n";
+
+                            if (textureFunction->method == TextureFunction::BIAS)
+                            {
+                                out << "    lod += bias;\n";
+                            }
+                        }
+                        else if (textureFunction->method == TextureFunction::GRAD)
+                        {
+                            out << "    float3 sizeVec = float3(width, height, depth);\n"
+                                   "    float3 sizeDdx = ddx * sizeVec;\n"
+                                   "    float3 sizeDdy = ddy * sizeVec;\n"
+                                   "    float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
+                                   "sizeDdy))) * 0.5f;\n";
+                        }
+
+                        out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+                    }
+
+                    out << "    " << textureReference
+                        << ".GetDimensions(mip, width, height, depth, levels);\n";
+                }
+                else UNREACHABLE();
+
+                // Convert from normalized floating-point to integer
+                out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
+                if (textureFunction->offset)
+                {
+                    OutputIntTexCoordWrap(out, "wrapS", "width", texCoordX, "offset.x", "tix");
+                }
+                else
+                {
+                    OutputIntTexCoordWrap(out, "wrapS", "width", texCoordX, "0", "tix");
+                }
+                texCoordX = "tix";
+                out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
+                if (textureFunction->offset)
+                {
+                    OutputIntTexCoordWrap(out, "wrapT", "height", texCoordY, "offset.y", "tiy");
+                }
+                else
+                {
+                    OutputIntTexCoordWrap(out, "wrapT", "height", texCoordY, "0", "tiy");
+                }
+                texCoordY = "tiy";
+
+                if (IsSamplerArray(textureFunction->sampler))
+                {
+                    texCoordZ = "int(max(0, min(layers - 1, floor(0.5 + t.z))))";
+                }
+                else if (!IsSamplerCube(textureFunction->sampler) &&
+                         !IsSampler2D(textureFunction->sampler))
+                {
+                    out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
+                    if (textureFunction->offset)
+                    {
+                        OutputIntTexCoordWrap(out, "wrapR", "depth", texCoordZ, "offset.z", "tiz");
+                    }
+                    else
+                    {
+                        OutputIntTexCoordWrap(out, "wrapR", "depth", texCoordZ, "0", "tiz");
+                    }
+                    texCoordZ = "tiz";
+                }
+            }
+
+            out << "    return ";
+
+            // HLSL intrinsic
+            if (mOutputType == SH_HLSL_3_0_OUTPUT)
+            {
+                switch(textureFunction->sampler)
+                {
+                  case EbtSampler2D:   out << "tex2D";   break;
+                  case EbtSamplerCube: out << "texCUBE"; break;
+                  default: UNREACHABLE();
+                }
+
+                switch(textureFunction->method)
+                {
+                    case TextureFunction::IMPLICIT:
+                        out << "(" << samplerReference << ", ";
+                        break;
+                    case TextureFunction::BIAS:
+                        out << "bias(" << samplerReference << ", ";
+                        break;
+                    case TextureFunction::LOD:
+                        out << "lod(" << samplerReference << ", ";
+                        break;
+                    case TextureFunction::LOD0:
+                        out << "lod(" << samplerReference << ", ";
+                        break;
+                    case TextureFunction::LOD0BIAS:
+                        out << "lod(" << samplerReference << ", ";
+                        break;
+                  default: UNREACHABLE();
+                }
+            }
+            else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+            {
+                if (textureFunction->method == TextureFunction::GRAD)
+                {
+                    if (IsIntegerSampler(textureFunction->sampler))
+                    {
+                        out << "" << textureReference << ".Load(";
+                    }
+                    else if (IsShadowSampler(textureFunction->sampler))
+                    {
+                        out << "" << textureReference << ".SampleCmpLevelZero(" << samplerReference
+                            << ", ";
+                    }
+                    else
+                    {
+                        out << "" << textureReference << ".SampleGrad(" << samplerReference << ", ";
+                    }
+                }
+                else if (IsIntegerSampler(textureFunction->sampler) ||
+                         textureFunction->method == TextureFunction::FETCH)
+                {
+                    out << "" << textureReference << ".Load(";
+                }
+                else if (IsShadowSampler(textureFunction->sampler))
+                {
+                    switch(textureFunction->method)
+                    {
+                        case TextureFunction::IMPLICIT:
+                            out << "" << textureReference << ".SampleCmp(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::BIAS:
+                            out << "" << textureReference << ".SampleCmp(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::LOD:
+                            out << "" << textureReference << ".SampleCmp(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::LOD0:
+                            out << "" << textureReference << ".SampleCmpLevelZero("
+                                << samplerReference << ", ";
+                            break;
+                        case TextureFunction::LOD0BIAS:
+                            out << "" << textureReference << ".SampleCmpLevelZero("
+                                << samplerReference << ", ";
+                            break;
+                      default: UNREACHABLE();
+                    }
+                }
+                else
+                {
+                    switch(textureFunction->method)
+                    {
+                        case TextureFunction::IMPLICIT:
+                            out << "" << textureReference << ".Sample(" << samplerReference << ", ";
+                            break;
+                        case TextureFunction::BIAS:
+                            out << "" << textureReference << ".SampleBias(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::LOD:
+                            out << "" << textureReference << ".SampleLevel(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::LOD0:
+                            out << "" << textureReference << ".SampleLevel(" << samplerReference
+                                << ", ";
+                            break;
+                        case TextureFunction::LOD0BIAS:
+                            out << "" << textureReference << ".SampleLevel(" << samplerReference
+                                << ", ";
+                            break;
+                      default: UNREACHABLE();
+                    }
+                }
+            }
+            else UNREACHABLE();
+
+            if (IsIntegerSampler(textureFunction->sampler) ||
+                textureFunction->method == TextureFunction::FETCH)
+            {
+                switch(hlslCoords)
+                {
+                  case 2: out << "int3("; break;
+                  case 3: out << "int4("; break;
+                  default: UNREACHABLE();
+                }
+            }
+            else
+            {
+                switch(hlslCoords)
+                {
+                  case 2: out << "float2("; break;
+                  case 3: out << "float3("; break;
+                  case 4: out << "float4("; break;
+                  default: UNREACHABLE();
+                }
+            }
+
+            out << texCoordX << ", " << texCoordY;
+
+            if (mOutputType == SH_HLSL_3_0_OUTPUT)
+            {
+                if (hlslCoords >= 3)
+                {
+                    if (textureFunction->coords < 3)
+                    {
+                        out << ", 0";
+                    }
+                    else
+                    {
+                        out << ", t.z" << proj;
+                    }
+                }
+
+                if (hlslCoords == 4)
+                {
+                    switch(textureFunction->method)
+                    {
+                      case TextureFunction::BIAS:     out << ", bias"; break;
+                      case TextureFunction::LOD:      out << ", lod";  break;
+                      case TextureFunction::LOD0:     out << ", 0";    break;
+                      case TextureFunction::LOD0BIAS: out << ", bias"; break;
+                      default: UNREACHABLE();
+                    }
+                }
+
+                out << "));\n";
+            }
+            else if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+            {
+                if (hlslCoords >= 3)
+                {
+                    ASSERT(!IsIntegerSampler(textureFunction->sampler) ||
+                           !IsSamplerCube(textureFunction->sampler) || texCoordZ == "face");
+                    out << ", " << texCoordZ;
+                }
+
+                if (textureFunction->method == TextureFunction::GRAD)
+                {
+                    if (IsIntegerSampler(textureFunction->sampler))
+                    {
+                        out << ", mip)";
+                    }
+                    else if (IsShadowSampler(textureFunction->sampler))
+                    {
+                        // Compare value
+                        if (textureFunction->proj)
+                        {
+                            // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+                            // The resulting third component of P' in the shadow forms is used as Dref
+                            out << "), t.z" << proj;
+                        }
+                        else
+                        {
+                            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)";
+                }
+                else if (IsShadowSampler(textureFunction->sampler))
+                {
+                    // Compare value
+                    if (textureFunction->proj)
+                    {
+                        // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+                        // The resulting third component of P' in the shadow forms is used as Dref
+                        out << "), t.z" << proj;
+                    }
+                    else
+                    {
+                        switch(textureFunction->coords)
+                        {
+                          case 3: out << "), t.z"; break;
+                          case 4: out << "), t.w"; break;
+                          default: UNREACHABLE();
+                        }
+                    }
+                }
+                else
+                {
+                    switch(textureFunction->method)
+                    {
+                      case TextureFunction::IMPLICIT: out << ")";       break;
+                      case TextureFunction::BIAS:     out << "), bias"; break;
+                      case TextureFunction::LOD:      out << "), lod";  break;
+                      case TextureFunction::LOD0:     out << "), 0";    break;
+                      case TextureFunction::LOD0BIAS: out << "), bias"; break;
+                      default: UNREACHABLE();
+                    }
+                }
+
+                if (textureFunction->offset && (!IsIntegerSampler(textureFunction->sampler) ||
+                                                textureFunction->method == TextureFunction::FETCH))
+                {
+                    out << ", offset";
+                }
+
+                out << ");";
+            }
+            else UNREACHABLE();
+        }
+
+        out << "\n"
+               "}\n"
+               "\n";
+    }
 
     if (mUsesFragCoord)
     {
@@ -1683,10 +2572,121 @@
             {
                 TString name           = TFunction::unmangleName(node->getNameObj().getString());
                 TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType();
-                int coords                  = (*arguments)[1]->getAsTyped()->getNominalSize();
-                TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction(
-                    name, samplerType, coords, arguments->size(), lod0, mShaderType);
-                out << textureFunctionName << "(";
+
+                TextureFunction textureFunction;
+                textureFunction.sampler = samplerType;
+                textureFunction.coords = (*arguments)[1]->getAsTyped()->getNominalSize();
+                textureFunction.method = TextureFunction::IMPLICIT;
+                textureFunction.proj = false;
+                textureFunction.offset = false;
+
+                if (name == "texture2D" || name == "textureCube" || name == "texture")
+                {
+                    textureFunction.method = TextureFunction::IMPLICIT;
+                }
+                else if (name == "texture2DProj" || name == "textureProj")
+                {
+                    textureFunction.method = TextureFunction::IMPLICIT;
+                    textureFunction.proj = true;
+                }
+                else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
+                         name == "texture2DLodEXT" || name == "textureCubeLodEXT")
+                {
+                    textureFunction.method = TextureFunction::LOD;
+                }
+                else if (name == "texture2DProjLod" || name == "textureProjLod" || name == "texture2DProjLodEXT")
+                {
+                    textureFunction.method = TextureFunction::LOD;
+                    textureFunction.proj = true;
+                }
+                else if (name == "textureSize")
+                {
+                    textureFunction.method = TextureFunction::SIZE;
+                }
+                else if (name == "textureOffset")
+                {
+                    textureFunction.method = TextureFunction::IMPLICIT;
+                    textureFunction.offset = true;
+                }
+                else if (name == "textureProjOffset")
+                {
+                    textureFunction.method = TextureFunction::IMPLICIT;
+                    textureFunction.offset = true;
+                    textureFunction.proj = true;
+                }
+                else if (name == "textureLodOffset")
+                {
+                    textureFunction.method = TextureFunction::LOD;
+                    textureFunction.offset = true;
+                }
+                else if (name == "textureProjLodOffset")
+                {
+                    textureFunction.method = TextureFunction::LOD;
+                    textureFunction.proj = true;
+                    textureFunction.offset = true;
+                }
+                else if (name == "texelFetch")
+                {
+                    textureFunction.method = TextureFunction::FETCH;
+                }
+                else if (name == "texelFetchOffset")
+                {
+                    textureFunction.method = TextureFunction::FETCH;
+                    textureFunction.offset = true;
+                }
+                else if (name == "textureGrad" || name == "texture2DGradEXT")
+                {
+                    textureFunction.method = TextureFunction::GRAD;
+                }
+                else if (name == "textureGradOffset")
+                {
+                    textureFunction.method = TextureFunction::GRAD;
+                    textureFunction.offset = true;
+                }
+                else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" || name == "textureCubeGradEXT")
+                {
+                    textureFunction.method = TextureFunction::GRAD;
+                    textureFunction.proj = true;
+                }
+                else if (name == "textureProjGradOffset")
+                {
+                    textureFunction.method = TextureFunction::GRAD;
+                    textureFunction.proj = true;
+                    textureFunction.offset = true;
+                }
+                else UNREACHABLE();
+
+                if (textureFunction.method == TextureFunction::IMPLICIT)   // Could require lod 0 or have a bias argument
+                {
+                    unsigned int mandatoryArgumentCount = 2;   // All functions have sampler and coordinate arguments
+
+                    if (textureFunction.offset)
+                    {
+                        mandatoryArgumentCount++;
+                    }
+
+                    bool bias = (arguments->size() > mandatoryArgumentCount);   // Bias argument is optional
+
+                    if (lod0 || mShaderType == GL_VERTEX_SHADER)
+                    {
+                        if (bias)
+                        {
+                            textureFunction.method = TextureFunction::LOD0BIAS;
+                        }
+                        else
+                        {
+                            textureFunction.method = TextureFunction::LOD0;
+                        }
+                    }
+                    else if (bias)
+                    {
+                        textureFunction.method = TextureFunction::BIAS;
+                    }
+                }
+
+                mUsesTexture.insert(textureFunction);
+
+                out << textureFunction.name();
             }
 
             for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++)