tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TextureFunctionHLSL.cpp (61161B)


      1 //
      2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 // TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
      7 // output. Some of the implementations are straightforward and just call the HLSL equivalent of the
      8 // ESSL texture function, others do more work to emulate ESSL texture sampling or size query
      9 // behavior.
     10 //
     11 
     12 #include "compiler/translator/TextureFunctionHLSL.h"
     13 
     14 #include "compiler/translator/ImmutableStringBuilder.h"
     15 #include "compiler/translator/UtilsHLSL.h"
     16 
     17 namespace sh
     18 {
     19 
     20 namespace
     21 {
     22 
     23 void OutputIntTexCoordWrap(TInfoSinkBase &out,
     24                           const char *wrapMode,
     25                           const char *size,
     26                           const ImmutableString &texCoord,
     27                           const char *texCoordOffset,
     28                           const char *texCoordOutName)
     29 {
     30    // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
     31    // but rather use equivalent formulas that map better to HLSL.
     32    out << "int " << texCoordOutName << ";\n";
     33    out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
     34        << ") / " << size << ";\n";
     35    out << "bool " << texCoordOutName << "UseBorderColor = false;\n";
     36 
     37    // CLAMP_TO_EDGE
     38    out << "if (" << wrapMode << " == 0)\n";
     39    out << "{\n";
     40    out << "    " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
     41        << "Offset)), 0, int(" << size << ") - 1);\n";
     42    out << "}\n";
     43 
     44    // CLAMP_TO_BORDER
     45    out << "else if (" << wrapMode << " == 3)\n";
     46    out << "{\n";
     47    out << "    int texCoordInt = int(floor(" << size << " * " << texCoordOutName << "Offset));\n";
     48    out << "    " << texCoordOutName << " = clamp(texCoordInt, 0, int(" << size << ") - 1);\n";
     49    out << "    " << texCoordOutName << "UseBorderColor = (texCoordInt != " << texCoordOutName
     50        << ");\n";
     51    out << "}\n";
     52 
     53    // MIRRORED_REPEAT
     54    out << "else if (" << wrapMode << " == 2)\n";
     55    out << "{\n";
     56    out << "    float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
     57        << "Offset) * 0.5) * 2.0 - 1.0);\n";
     58    out << "    " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
     59    out << "}\n";
     60 
     61    // REPEAT
     62    out << "else\n";
     63    out << "{\n";
     64    out << "    " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
     65        << "Offset)));\n";
     66    out << "}\n";
     67 }
     68 
     69 void OutputIntTexCoordWraps(TInfoSinkBase &out,
     70                            const TextureFunctionHLSL::TextureFunction &textureFunction,
     71                            ImmutableString *texCoordX,
     72                            ImmutableString *texCoordY,
     73                            ImmutableString *texCoordZ)
     74 {
     75    // Convert from normalized floating-point to integer
     76    out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
     77    if (textureFunction.offset)
     78    {
     79        OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix");
     80    }
     81    else
     82    {
     83        OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix");
     84    }
     85    *texCoordX = ImmutableString("tix");
     86    out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
     87    if (textureFunction.offset)
     88    {
     89        OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy");
     90    }
     91    else
     92    {
     93        OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy");
     94    }
     95    *texCoordY = ImmutableString("tiy");
     96 
     97    bool tizAvailable = false;
     98 
     99    if (IsSamplerArray(textureFunction.sampler))
    100    {
    101        *texCoordZ = ImmutableString("int(max(0, min(layers - 1, floor(0.5 + t.z))))");
    102    }
    103    else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler))
    104    {
    105        out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
    106        if (textureFunction.offset)
    107        {
    108            OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz");
    109        }
    110        else
    111        {
    112            OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz");
    113        }
    114        *texCoordZ   = ImmutableString("tiz");
    115        tizAvailable = true;
    116    }
    117 
    118    out << "bool useBorderColor = tixUseBorderColor || tiyUseBorderColor"
    119        << (tizAvailable ? " || tizUseBorderColor" : "") << ";\n";
    120 }
    121 
    122 void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out,
    123                                     const TextureFunctionHLSL::TextureFunction &textureFunction,
    124                                     const ImmutableString &textureReference,
    125                                     const ImmutableString &samplerReference)
    126 {
    127    out << textureReference;
    128    if (IsIntegerSampler(textureFunction.sampler) ||
    129        textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
    130    {
    131        out << ".Load(";
    132        return;
    133    }
    134 
    135    if (IsShadowSampler(textureFunction.sampler))
    136    {
    137        switch (textureFunction.method)
    138        {
    139            case TextureFunctionHLSL::TextureFunction::IMPLICIT:
    140            case TextureFunctionHLSL::TextureFunction::BIAS:
    141            case TextureFunctionHLSL::TextureFunction::LOD:
    142                out << ".SampleCmp(";
    143                break;
    144            case TextureFunctionHLSL::TextureFunction::LOD0:
    145            case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
    146            case TextureFunctionHLSL::TextureFunction::GRAD:
    147                out << ".SampleCmpLevelZero(";
    148                break;
    149            default:
    150                UNREACHABLE();
    151        }
    152    }
    153    else
    154    {
    155        switch (textureFunction.method)
    156        {
    157            case TextureFunctionHLSL::TextureFunction::IMPLICIT:
    158                out << ".Sample(";
    159                break;
    160            case TextureFunctionHLSL::TextureFunction::BIAS:
    161                out << ".SampleBias(";
    162                break;
    163            case TextureFunctionHLSL::TextureFunction::LOD:
    164            case TextureFunctionHLSL::TextureFunction::LOD0:
    165            case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
    166                out << ".SampleLevel(";
    167                break;
    168            case TextureFunctionHLSL::TextureFunction::GRAD:
    169                out << ".SampleGrad(";
    170                break;
    171            default:
    172                UNREACHABLE();
    173        }
    174    }
    175    out << samplerReference << ", ";
    176 }
    177 
    178 const char *GetSamplerCoordinateTypeString(
    179    const TextureFunctionHLSL::TextureFunction &textureFunction,
    180    int hlslCoords)
    181 {
    182    // Gather[Red|Green|Blue|Alpha] accepts float texture coordinates on textures in integer or
    183    // unsigned integer formats.
    184    // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-to-gather
    185    if ((IsIntegerSampler(textureFunction.sampler) &&
    186         textureFunction.method != TextureFunctionHLSL::TextureFunction::GATHER) ||
    187        textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
    188    {
    189        switch (hlslCoords)
    190        {
    191            case 1:
    192                return "int";
    193            case 2:
    194                if (IsSampler2DMS(textureFunction.sampler))
    195                {
    196                    return "int2";
    197                }
    198                else
    199                {
    200                    return "int3";
    201                }
    202            case 3:
    203                if (IsSampler2DMSArray(textureFunction.sampler))
    204                {
    205                    return "int3";
    206                }
    207                else
    208                {
    209                    return "int4";
    210                }
    211            default:
    212                UNREACHABLE();
    213        }
    214    }
    215    else
    216    {
    217        switch (hlslCoords)
    218        {
    219            case 1:
    220                return "float";
    221            case 2:
    222                return "float2";
    223            case 3:
    224                return "float3";
    225            case 4:
    226                return "float4";
    227            default:
    228                UNREACHABLE();
    229        }
    230    }
    231    return "";
    232 }
    233 
    234 int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction,
    235                      ShShaderOutput outputType)
    236 {
    237    if (outputType == SH_HLSL_3_0_OUTPUT)
    238    {
    239        int hlslCoords = 2;
    240        switch (textureFunction.sampler)
    241        {
    242            case EbtSamplerBuffer:
    243                hlslCoords = 1;
    244                break;
    245            case EbtSampler2D:
    246            case EbtSamplerExternalOES:
    247            case EbtSampler2DMS:
    248            case EbtSamplerVideoWEBGL:
    249                hlslCoords = 2;
    250                break;
    251            case EbtSamplerCube:
    252                hlslCoords = 3;
    253                break;
    254            default:
    255                UNREACHABLE();
    256        }
    257 
    258        switch (textureFunction.method)
    259        {
    260            case TextureFunctionHLSL::TextureFunction::IMPLICIT:
    261            case TextureFunctionHLSL::TextureFunction::GRAD:
    262                return hlslCoords;
    263            case TextureFunctionHLSL::TextureFunction::BIAS:
    264            case TextureFunctionHLSL::TextureFunction::LOD:
    265            case TextureFunctionHLSL::TextureFunction::LOD0:
    266            case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
    267                return 4;
    268            default:
    269                UNREACHABLE();
    270        }
    271    }
    272    else
    273    {
    274        if (IsSamplerBuffer(textureFunction.sampler))
    275        {
    276            return 1;
    277        }
    278        else if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
    279                 IsSamplerCube(textureFunction.sampler))
    280        {
    281            return 3;
    282        }
    283        ASSERT(IsSampler2D(textureFunction.sampler));
    284        return 2;
    285    }
    286    return 0;
    287 }
    288 
    289 void OutputTextureFunctionArgumentList(TInfoSinkBase &out,
    290                                       const TextureFunctionHLSL::TextureFunction &textureFunction,
    291                                       const ShShaderOutput outputType)
    292 {
    293    if (outputType == SH_HLSL_3_0_OUTPUT)
    294    {
    295        switch (textureFunction.sampler)
    296        {
    297            case EbtSampler2D:
    298            case EbtSamplerVideoWEBGL:
    299            case EbtSamplerExternalOES:
    300                out << "sampler2D s";
    301                break;
    302            case EbtSamplerCube:
    303                out << "samplerCUBE s";
    304                break;
    305            default:
    306                UNREACHABLE();
    307        }
    308    }
    309    else
    310    {
    311        if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
    312        {
    313            out << TextureString(textureFunction.sampler) << " x, "
    314                << SamplerString(textureFunction.sampler) << " s";
    315        }
    316        else
    317        {
    318            ASSERT(outputType == SH_HLSL_4_1_OUTPUT);
    319            // A bug in the D3D compiler causes some nested sampling operations to fail.
    320            // See http://anglebug.com/1923
    321            // TODO(jmadill): Reinstate the const keyword when possible.
    322            out << /*"const"*/ "uint samplerIndex";
    323        }
    324    }
    325 
    326    if (textureFunction.method ==
    327        TextureFunctionHLSL::TextureFunction::FETCH)  // Integer coordinates
    328    {
    329        switch (textureFunction.coords)
    330        {
    331            case 1:
    332                out << ", int t";
    333                break;
    334            case 2:
    335                out << ", int2 t";
    336                break;
    337            case 3:
    338                out << ", int3 t";
    339                break;
    340            default:
    341                UNREACHABLE();
    342        }
    343    }
    344    else  // Floating-point coordinates (except textureSize)
    345    {
    346        switch (textureFunction.coords)
    347        {
    348            case 0:
    349                break;  // textureSize(gSampler2DMS sampler)
    350            case 1:
    351                out << ", int lod";
    352                break;  // textureSize()
    353            case 2:
    354                out << ", float2 t";
    355                break;
    356            case 3:
    357                out << ", float3 t";
    358                break;
    359            case 4:
    360                out << ", float4 t";
    361                break;
    362            default:
    363                UNREACHABLE();
    364        }
    365    }
    366 
    367    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    368    {
    369        switch (textureFunction.sampler)
    370        {
    371            case EbtSampler2D:
    372            case EbtISampler2D:
    373            case EbtUSampler2D:
    374            case EbtSampler2DArray:
    375            case EbtISampler2DArray:
    376            case EbtUSampler2DArray:
    377            case EbtSampler2DShadow:
    378            case EbtSampler2DArrayShadow:
    379            case EbtSamplerExternalOES:
    380            case EbtSamplerVideoWEBGL:
    381                out << ", float2 ddx, float2 ddy";
    382                break;
    383            case EbtSampler3D:
    384            case EbtISampler3D:
    385            case EbtUSampler3D:
    386            case EbtSamplerCube:
    387            case EbtISamplerCube:
    388            case EbtUSamplerCube:
    389            case EbtSamplerCubeShadow:
    390                out << ", float3 ddx, float3 ddy";
    391                break;
    392            default:
    393                UNREACHABLE();
    394        }
    395    }
    396 
    397    switch (textureFunction.method)
    398    {
    399        case TextureFunctionHLSL::TextureFunction::IMPLICIT:
    400            break;
    401        case TextureFunctionHLSL::TextureFunction::BIAS:
    402            break;  // Comes after the offset parameter
    403        case TextureFunctionHLSL::TextureFunction::LOD:
    404            out << ", float lod";
    405            break;
    406        case TextureFunctionHLSL::TextureFunction::LOD0:
    407            break;
    408        case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
    409            break;  // Comes after the offset parameter
    410        case TextureFunctionHLSL::TextureFunction::SIZE:
    411            break;
    412        case TextureFunctionHLSL::TextureFunction::FETCH:
    413            if (IsSampler2DMS(textureFunction.sampler) ||
    414                IsSampler2DMSArray(textureFunction.sampler))
    415                out << ", int index";
    416            else if (!IsSamplerBuffer(textureFunction.sampler))
    417                out << ", int mip";
    418            break;
    419        case TextureFunctionHLSL::TextureFunction::GRAD:
    420            break;
    421        case TextureFunctionHLSL::TextureFunction::GATHER:
    422            break;
    423        default:
    424            UNREACHABLE();
    425    }
    426 
    427    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER &&
    428        IsShadowSampler(textureFunction.sampler))
    429    {
    430        out << ", float refZ";
    431    }
    432 
    433    if (textureFunction.offset)
    434    {
    435        switch (textureFunction.sampler)
    436        {
    437            case EbtSampler3D:
    438            case EbtISampler3D:
    439            case EbtUSampler3D:
    440                out << ", int3 offset";
    441                break;
    442            case EbtSampler2D:
    443            case EbtSampler2DArray:
    444            case EbtISampler2D:
    445            case EbtISampler2DArray:
    446            case EbtUSampler2D:
    447            case EbtUSampler2DArray:
    448            case EbtSampler2DShadow:
    449            case EbtSampler2DArrayShadow:
    450            case EbtSamplerExternalOES:
    451            case EbtSamplerVideoWEBGL:
    452                out << ", int2 offset";
    453                break;
    454            default:
    455                // Offset is not supported for multisampled textures.
    456                UNREACHABLE();
    457        }
    458    }
    459 
    460    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS ||
    461        textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
    462    {
    463        out << ", float bias";
    464    }
    465    else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GATHER &&
    466             !IsShadowSampler(textureFunction.sampler))
    467    {
    468        out << ", int comp = 0";
    469    }
    470 }
    471 
    472 void GetTextureReference(TInfoSinkBase &out,
    473                         const TextureFunctionHLSL::TextureFunction &textureFunction,
    474                         const ShShaderOutput outputType,
    475                         ImmutableString *textureReference,
    476                         ImmutableString *samplerReference)
    477 {
    478    if (outputType == SH_HLSL_4_1_OUTPUT)
    479    {
    480        static const ImmutableString kTexturesStr("textures");
    481        static const ImmutableString kSamplersStr("samplers");
    482        static const ImmutableString kSamplerIndexStr("[samplerIndex]");
    483        static const ImmutableString kTextureIndexStr("[textureIndex]");
    484        static const ImmutableString kSamplerArrayIndexStr("[samplerArrayIndex]");
    485        ImmutableString suffix(TextureGroupSuffix(textureFunction.sampler));
    486 
    487        if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D)
    488        {
    489            ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() +
    490                                                     kSamplerIndexStr.length());
    491            textureRefBuilder << kTexturesStr << suffix << kSamplerIndexStr;
    492            *textureReference = textureRefBuilder;
    493            ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() +
    494                                                     kSamplerIndexStr.length());
    495            samplerRefBuilder << kSamplersStr << suffix << kSamplerIndexStr;
    496            *samplerReference = samplerRefBuilder;
    497        }
    498        else
    499        {
    500            out << "    const uint textureIndex = samplerIndex - textureIndexOffset"
    501                << suffix.data() << ";\n";
    502            ImmutableStringBuilder textureRefBuilder(kTexturesStr.length() + suffix.length() +
    503                                                     kTextureIndexStr.length());
    504            textureRefBuilder << kTexturesStr << suffix << kTextureIndexStr;
    505            *textureReference = textureRefBuilder;
    506 
    507            out << "    const uint samplerArrayIndex = samplerIndex - samplerIndexOffset"
    508                << suffix.data() << ";\n";
    509            ImmutableStringBuilder samplerRefBuilder(kSamplersStr.length() + suffix.length() +
    510                                                     kSamplerArrayIndexStr.length());
    511            samplerRefBuilder << kSamplersStr << suffix << kSamplerArrayIndexStr;
    512            *samplerReference = samplerRefBuilder;
    513        }
    514    }
    515    else
    516    {
    517        *textureReference = ImmutableString("x");
    518        *samplerReference = ImmutableString("s");
    519    }
    520 }
    521 
    522 void OutputTextureSizeFunctionBody(TInfoSinkBase &out,
    523                                   const TextureFunctionHLSL::TextureFunction &textureFunction,
    524                                   const ImmutableString &textureReference,
    525                                   bool getDimensionsIgnoresBaseLevel)
    526 {
    527    if (IsSampler2DMS(textureFunction.sampler))
    528    {
    529        out << "    uint width; uint height; uint samples;\n"
    530            << "    " << textureReference << ".GetDimensions(width, height, samples);\n";
    531    }
    532    else if (IsSampler2DMSArray(textureFunction.sampler))
    533    {
    534        out << "    uint width; uint height; uint depth; uint samples;\n"
    535            << "    " << textureReference << ".GetDimensions(width, height, depth, samples);\n";
    536    }
    537    else if (IsSamplerBuffer(textureFunction.sampler))
    538    {
    539        out << "    uint width;\n"
    540            << "    " << textureReference << ".GetDimensions(width);\n";
    541    }
    542    else
    543    {
    544        if (getDimensionsIgnoresBaseLevel)
    545        {
    546            out << "    int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
    547        }
    548        else
    549        {
    550            out << "    int baseLevel = 0;\n";
    551        }
    552 
    553        if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
    554            (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler)))
    555        {
    556            // "depth" stores either the number of layers in an array texture or 3D depth
    557            out << "    uint width; uint height; uint depth; uint numberOfLevels;\n"
    558                << "    " << textureReference
    559                << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n"
    560                << "    width = max(width >> lod, 1);\n"
    561                << "    height = max(height >> lod, 1);\n";
    562 
    563            if (!IsSamplerArray(textureFunction.sampler))
    564            {
    565                out << "    depth = max(depth >> lod, 1);\n";
    566            }
    567        }
    568        else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler))
    569        {
    570            out << "    uint width; uint height; uint numberOfLevels;\n"
    571                << "    " << textureReference
    572                << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n"
    573                << "    width = max(width >> lod, 1);\n"
    574                << "    height = max(height >> lod, 1);\n";
    575        }
    576        else
    577            UNREACHABLE();
    578    }
    579 
    580    const char *returnType = textureFunction.getReturnType();
    581    if (strcmp(returnType, "int3") == 0)
    582    {
    583        out << "    return int3(width, height, depth);\n";
    584    }
    585    else if (strcmp(returnType, "int2") == 0)
    586    {
    587        out << "    return int2(width, height);\n";
    588    }
    589    else
    590    {
    591        out << "    return int(width);\n";
    592    }
    593 }
    594 
    595 void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction,
    596                               ImmutableString *texCoordX,
    597                               ImmutableString *texCoordY,
    598                               ImmutableString *texCoordZ)
    599 {
    600    if (textureFunction.proj)
    601    {
    602        ImmutableString proj("");
    603        switch (textureFunction.coords)
    604        {
    605            case 3:
    606                proj = ImmutableString(" / t.z");
    607                break;
    608            case 4:
    609                proj = ImmutableString(" / t.w");
    610                break;
    611            default:
    612                UNREACHABLE();
    613        }
    614        ImmutableStringBuilder texCoordXBuilder(texCoordX->length() + proj.length() + 2u);
    615        texCoordXBuilder << '(' << *texCoordX << proj << ')';
    616        *texCoordX = texCoordXBuilder;
    617        ImmutableStringBuilder texCoordYBuilder(texCoordY->length() + proj.length() + 2u);
    618        texCoordYBuilder << '(' << *texCoordY << proj << ')';
    619        *texCoordY = texCoordYBuilder;
    620        ImmutableStringBuilder texCoordZBuilder(texCoordZ->length() + proj.length() + 2u);
    621        texCoordZBuilder << '(' << *texCoordZ << proj << ')';
    622        *texCoordZ = texCoordZBuilder;
    623    }
    624 }
    625 
    626 void OutputIntegerTextureSampleFunctionComputations(
    627    TInfoSinkBase &out,
    628    const TextureFunctionHLSL::TextureFunction &textureFunction,
    629    const ShShaderOutput outputType,
    630    const ImmutableString &textureReference,
    631    ImmutableString *texCoordX,
    632    ImmutableString *texCoordY,
    633    ImmutableString *texCoordZ,
    634    bool getDimensionsIgnoresBaseLevel)
    635 {
    636    if (!IsIntegerSampler(textureFunction.sampler))
    637    {
    638        return;
    639    }
    640 
    641    if (getDimensionsIgnoresBaseLevel)
    642    {
    643        out << "    int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
    644    }
    645    else
    646    {
    647        out << "    int baseLevel = 0;\n";
    648    }
    649 
    650    if (IsSamplerCube(textureFunction.sampler))
    651    {
    652        out << "    float width; float height; float layers; float levels;\n";
    653 
    654        out << "    uint mip = 0;\n";
    655 
    656        out << "    " << textureReference
    657            << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
    658 
    659        out << "    bool xMajor = abs(t.x) >= abs(t.y) && abs(t.x) >= abs(t.z);\n";
    660        out << "    bool yMajor = abs(t.y) >= abs(t.z) && abs(t.y) > abs(t.x);\n";
    661        out << "    bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
    662        out << "    bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || "
    663               "(zMajor && t.z < 0.0f);\n";
    664 
    665        // FACE_POSITIVE_X = 000b
    666        // FACE_NEGATIVE_X = 001b
    667        // FACE_POSITIVE_Y = 010b
    668        // FACE_NEGATIVE_Y = 011b
    669        // FACE_POSITIVE_Z = 100b
    670        // FACE_NEGATIVE_Z = 101b
    671        out << "    int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
    672 
    673        out << "    float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
    674        out << "    float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
    675        out << "    float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
    676 
    677        out << "    float3 r = any(t) ? t : float3(1, 0, 0);\n";
    678        out << "    t.x = (u * 0.5f / m) + 0.5f;\n";
    679        out << "    t.y = (v * 0.5f / m) + 0.5f;\n";
    680 
    681        // Mip level computation.
    682        if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
    683            textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD ||
    684            textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    685        {
    686            if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT)
    687            {
    688                // We would like to calculate tha maximum of how many texels we move in the major
    689                // face's texture as we move across the screen in any direction. Namely, we want the
    690                // length of the directional derivative of the function p (defined below), maximized
    691                // over screen space directions. (For short: we want the norm of Dp.) For
    692                // simplicity, assume that z-axis is the major axis. By symmetry, we can assume that
    693                // the positive z direction is major. (The calculated value will be the same even if
    694                // this is false.) Let r denote the function from screen position to cube texture
    695                // coordinates. Then p can be written as p = s . P . r, where P(r) = (r.x, r.y)/r.z
    696                // is the projection onto the major cube face, and s = diag(width, height)/2. (s
    697                // linearly maps from the cube face into texture space, so that p(r) is in units of
    698                // texels.) The derivative is
    699                // Dp(r) = s |1 0 -r.x/r.z|
    700                //           |0 1 -r.y/r.z| |ddx(r) ddy(r)| / r.z
    701                //       = |dot(a, ddx(r)) dot(a, ddy(r))|
    702                //         |dot(b, ddx(r)) dot(b, ddy(r))| / (2 r.z)
    703                // where a = w * vec3(1, 0, -r.x/r.z)
    704                //       b = h * vec3(0, 1, -r.y/r.z)
    705                // We would like to know max(L(x)) over unit vectors x, where L(x) = |Dp(r) x|^2.
    706                // Since ddx(r) and ddy(r) are unknown, the best we can do is to sample L in some
    707                // directions and take the maximum across the samples.
    708                //
    709                // Some implementations use max(L(n1), L(n2)) where n1 = vec2(1,0) and n2 =
    710                // vec2(0,1).
    711                //
    712                // Some implementations use max(L(n1), L(n2), L(n3), L(n4)),
    713                // where n3 = (n1 + n2) / |n1 + n2| = (n1 + n2)/sqrt(2)
    714                //       n4 = (n1 - n2) / |n1 - n2| = (n1 - n2)/sqrt(2).
    715                // In other words, two samples along the diagonal screen space directions have been
    716                // added, giving a strictly better estimate of the true maximum.
    717                //
    718                // It turns out we can get twice the sample count very cheaply.
    719                // We can use the linearity of Dp(r) to get these extra samples of L cheaply in
    720                // terms of the already taken samples, L(n1) and L(n2):
    721                // Denoting
    722                // dpx = Dp(r)n1
    723                // dpy = Dp(r)n2
    724                // dpxx = dot(dpx, dpx)
    725                // dpyy = dot(dpy, dpy)
    726                // dpxy = dot(dpx, dpy)
    727                // we obtain
    728                // L(n3) = |Dp(r)n1 + Dp(r)n2|^2/2 = (dpxx + dpyy)/2 + dpxy
    729                // L(n4) = |Dp(r)n1 - Dp(r)n2|^2/2 = (dpxx + dpyy)/2 - dpxy
    730                // max(L(n1), L(n2), L(n3), L(n4))
    731                // = max(max(L(n1), L(n2)), max(L(n3), L(n4)))
    732                // = max(max(dpxx, dpyy), (dpxx + dpyy)/2 + abs(dpxy))
    733                // So the extra cost is: one dot, one abs, one add, one multiply-add and one max.
    734                // (All scalar.)
    735                //
    736                // In section 3.8.10.1, the OpenGL ES 3 specification defines the "scale factor",
    737                // rho. In our terminology, this definition works out to taking sqrt(max(L(n1),
    738                // L(n2))). Some implementations will use this estimate, here we use the strictly
    739                // better sqrt(max(L(n1), L(n2), L(n3), L(n4))), since it's not much more expensive
    740                // to calculate.
    741 
    742                // Swap coordinates such that we can assume that the positive z-axis is major, in
    743                // what follows.
    744                out << "    float3 ddxr = xMajor ? ddx(r).yzx : yMajor ? ddx(r).zxy : ddx(r).xyz;\n"
    745                       "    float3 ddyr = xMajor ? ddy(r).yzx : yMajor ? ddy(r).zxy : ddy(r).xyz;\n"
    746                       "    r = xMajor ? r.yzx : yMajor ? r.zxy : r.xyz;\n";
    747 
    748                out << "    float2 s = 0.5*float2(width, height);\n"
    749                       "    float2 dpx = s * (ddxr.xy - ddxr.z*r.xy/r.z)/r.z;\n"
    750                       "    float2 dpy = s * (ddyr.xy - ddyr.z*r.xy/r.z)/r.z;\n"
    751                       "    float dpxx = dot(dpx, dpx);\n;"
    752                       "    float dpyy = dot(dpy, dpy);\n;"
    753                       "    float dpxy = dot(dpx, dpy);\n"
    754                       "    float ma = max(dpxx, dpyy);\n"
    755                       "    float mb = 0.5 * (dpxx + dpyy) + abs(dpxy);\n"
    756                       "    float mab = max(ma, mb);\n"
    757                       "    float lod = 0.5f * log2(mab);\n";
    758            }
    759            else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    760            {
    761                // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
    762                // derivatives of P are assumed to be in the coordinate system used before
    763                // texture coordinates are projected onto the appropriate cube face."
    764                // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
    765                // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
    766                // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
    767                // Determine the derivatives of u, v and m
    768                out << "    float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
    769                       ": ddx[0]);\n"
    770                       "    float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
    771                       ": ddy[0]);\n"
    772                       "    float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
    773                       "    float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
    774                       "    float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
    775                       "    float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
    776                // Now determine the derivatives of the face coordinates, using the
    777                // derivatives calculated above.
    778                // d / dx (u(x) * 0.5 / m(x) + 0.5)
    779                // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
    780                out << "    float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
    781                       "    float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
    782                       "    float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
    783                       "    float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
    784                       "    float2 sizeVec = float2(width, height);\n"
    785                       "    float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
    786                       "    float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
    787                // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
    788                // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
    789                out << "    float lengthfaceddx2 = dot(faceddx, faceddx);\n"
    790                       "    float lengthfaceddy2 = dot(faceddy, faceddy);\n"
    791                       "    float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n";
    792            }
    793            out << "    mip = uint(min(max(round(lod), 0), levels - 1));\n"
    794                << "    " << textureReference
    795                << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
    796        }
    797 
    798        // Convert from normalized floating-point to integer
    799        static const ImmutableString kXPrefix("int(floor(width * frac(");
    800        static const ImmutableString kYPrefix("int(floor(height * frac(");
    801        static const ImmutableString kSuffix(")))");
    802        ImmutableStringBuilder texCoordXBuilder(kXPrefix.length() + texCoordX->length() +
    803                                                kSuffix.length());
    804        texCoordXBuilder << kXPrefix << *texCoordX << kSuffix;
    805        *texCoordX = texCoordXBuilder;
    806        ImmutableStringBuilder texCoordYBuilder(kYPrefix.length() + texCoordX->length() +
    807                                                kSuffix.length());
    808        texCoordYBuilder << kYPrefix << *texCoordY << kSuffix;
    809        *texCoordY = texCoordYBuilder;
    810        *texCoordZ = ImmutableString("face");
    811    }
    812    else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
    813    {
    814        if (IsSamplerArray(textureFunction.sampler))
    815        {
    816            out << "    float width; float height; float layers; float levels;\n";
    817            if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
    818            {
    819                out << "    uint mip = 0;\n";
    820            }
    821            else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
    822            {
    823                out << "    uint mip = bias;\n";
    824            }
    825            else
    826            {
    827 
    828                out << "    " << textureReference
    829                    << ".GetDimensions(baseLevel, width, height, layers, levels);\n";
    830                if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
    831                    textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    832                {
    833                    out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
    834                           "    float dx = length(ddx(tSized));\n"
    835                           "    float dy = length(ddy(tSized));\n"
    836                           "    float lod = log2(max(dx, dy));\n";
    837 
    838                    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    839                    {
    840                        out << "    lod += bias;\n";
    841                    }
    842                }
    843                else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    844                {
    845                    out << "    float2 sizeVec = float2(width, height);\n"
    846                           "    float2 sizeDdx = ddx * sizeVec;\n"
    847                           "    float2 sizeDdy = ddy * sizeVec;\n"
    848                           "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
    849                           "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
    850                }
    851 
    852                out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
    853            }
    854 
    855            out << "    " << textureReference
    856                << ".GetDimensions(baseLevel + mip, width, height, layers, levels);\n";
    857        }
    858        else if (IsSampler2D(textureFunction.sampler))
    859        {
    860            out << "    float width; float height; float levels;\n";
    861 
    862            if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
    863            {
    864                out << "    uint mip = 0;\n";
    865            }
    866            else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
    867            {
    868                out << "    uint mip = bias;\n";
    869            }
    870            else
    871            {
    872                out << "    " << textureReference
    873                    << ".GetDimensions(baseLevel, width, height, levels);\n";
    874 
    875                if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
    876                    textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    877                {
    878                    out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
    879                           "    float dx = length(ddx(tSized));\n"
    880                           "    float dy = length(ddy(tSized));\n"
    881                           "    float lod = log2(max(dx, dy));\n";
    882 
    883                    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    884                    {
    885                        out << "    lod += bias;\n";
    886                    }
    887                }
    888                else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    889                {
    890                    out << "    float2 sizeVec = float2(width, height);\n"
    891                           "    float2 sizeDdx = ddx * sizeVec;\n"
    892                           "    float2 sizeDdy = ddy * sizeVec;\n"
    893                           "    float lod = log2(max(dot(sizeDdx, sizeDdx), "
    894                           "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
    895                }
    896 
    897                out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
    898            }
    899 
    900            out << "    " << textureReference
    901                << ".GetDimensions(baseLevel + mip, width, height, levels);\n";
    902        }
    903        else if (IsSampler3D(textureFunction.sampler))
    904        {
    905            out << "    float width; float height; float depth; float levels;\n";
    906 
    907            if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
    908            {
    909                out << "    uint mip = 0;\n";
    910            }
    911            else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
    912            {
    913                out << "    uint mip = bias;\n";
    914            }
    915            else
    916            {
    917                out << "    " << textureReference
    918                    << ".GetDimensions(baseLevel, width, height, depth, levels);\n";
    919 
    920                if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
    921                    textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    922                {
    923                    out << "    float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n"
    924                           "    float dx = length(ddx(tSized));\n"
    925                           "    float dy = length(ddy(tSized));\n"
    926                           "    float lod = log2(max(dx, dy));\n";
    927 
    928                    if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
    929                    {
    930                        out << "    lod += bias;\n";
    931                    }
    932                }
    933                else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
    934                {
    935                    out << "    float3 sizeVec = float3(width, height, depth);\n"
    936                           "    float3 sizeDdx = ddx * sizeVec;\n"
    937                           "    float3 sizeDdy = ddy * sizeVec;\n"
    938                           "    float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
    939                           "sizeDdy))) * 0.5f;\n";
    940                }
    941 
    942                out << "    uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
    943            }
    944 
    945            out << "    " << textureReference
    946                << ".GetDimensions(baseLevel + mip, width, height, depth, levels);\n";
    947        }
    948        else
    949            UNREACHABLE();
    950 
    951        OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ);
    952    }
    953 }
    954 
    955 void OutputTextureGatherFunctionBody(TInfoSinkBase &out,
    956                                     const TextureFunctionHLSL::TextureFunction &textureFunction,
    957                                     ShShaderOutput outputType,
    958                                     const ImmutableString &textureReference,
    959                                     const ImmutableString &samplerReference,
    960                                     const ImmutableString &texCoordX,
    961                                     const ImmutableString &texCoordY,
    962                                     const ImmutableString &texCoordZ)
    963 {
    964    const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
    965    ImmutableString samplerCoordTypeString(
    966        GetSamplerCoordinateTypeString(textureFunction, hlslCoords));
    967    ImmutableStringBuilder samplerCoordBuilder(
    968        samplerCoordTypeString.length() + strlen("(") + texCoordX.length() + strlen(", ") +
    969        texCoordY.length() + strlen(", ") + texCoordZ.length() + strlen(")"));
    970 
    971    samplerCoordBuilder << samplerCoordTypeString << "(" << texCoordX << ", " << texCoordY;
    972    if (hlslCoords >= 3)
    973    {
    974        if (textureFunction.coords < 3)
    975        {
    976            samplerCoordBuilder << ", 0";
    977        }
    978        else
    979        {
    980            samplerCoordBuilder << ", " << texCoordZ;
    981        }
    982    }
    983    samplerCoordBuilder << ")";
    984 
    985    ImmutableString samplerCoordString(samplerCoordBuilder);
    986 
    987    if (IsShadowSampler(textureFunction.sampler))
    988    {
    989        out << "return " << textureReference << ".GatherCmp(" << samplerReference << ", "
    990            << samplerCoordString << ", refZ";
    991        if (textureFunction.offset)
    992        {
    993            out << ", offset";
    994        }
    995        out << ");\n";
    996        return;
    997    }
    998 
    999    constexpr std::array<const char *, 4> kHLSLGatherFunctions = {
   1000        {"GatherRed", "GatherGreen", "GatherBlue", "GatherAlpha"}};
   1001 
   1002    out << "    switch(comp)\n"
   1003           "    {\n";
   1004    for (size_t component = 0; component < kHLSLGatherFunctions.size(); ++component)
   1005    {
   1006        out << "        case " << component << ":\n"
   1007            << "            return " << textureReference << "." << kHLSLGatherFunctions[component]
   1008            << "(" << samplerReference << ", " << samplerCoordString;
   1009        if (textureFunction.offset)
   1010        {
   1011            out << ", offset";
   1012        }
   1013        out << ");\n";
   1014    }
   1015 
   1016    out << "        default:\n"
   1017           "            return float4(0.0, 0.0, 0.0, 1.0);\n"
   1018           "    }\n";
   1019 }
   1020 
   1021 void OutputTextureSampleFunctionReturnStatement(
   1022    TInfoSinkBase &out,
   1023    const TextureFunctionHLSL::TextureFunction &textureFunction,
   1024    const ShShaderOutput outputType,
   1025    const ImmutableString &textureReference,
   1026    const ImmutableString &samplerReference,
   1027    const ImmutableString &texCoordX,
   1028    const ImmutableString &texCoordY,
   1029    const ImmutableString &texCoordZ)
   1030 {
   1031    out << "    return ";
   1032 
   1033    if (IsIntegerSampler(textureFunction.sampler) && !IsSamplerCube(textureFunction.sampler) &&
   1034        textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
   1035    {
   1036        out << " useBorderColor ? ";
   1037        if (IsIntegerSamplerUnsigned(textureFunction.sampler))
   1038        {
   1039            out << "asuint";
   1040        }
   1041        out << "(samplerMetadata[samplerIndex].intBorderColor) : ";
   1042    }
   1043 
   1044    // HLSL intrinsic
   1045    if (outputType == SH_HLSL_3_0_OUTPUT)
   1046    {
   1047        switch (textureFunction.sampler)
   1048        {
   1049            case EbtSampler2D:
   1050            case EbtSamplerVideoWEBGL:
   1051            case EbtSamplerExternalOES:
   1052                out << "tex2D";
   1053                break;
   1054            case EbtSamplerCube:
   1055                out << "texCUBE";
   1056                break;
   1057            default:
   1058                UNREACHABLE();
   1059        }
   1060 
   1061        switch (textureFunction.method)
   1062        {
   1063            case TextureFunctionHLSL::TextureFunction::IMPLICIT:
   1064                out << "(" << samplerReference << ", ";
   1065                break;
   1066            case TextureFunctionHLSL::TextureFunction::BIAS:
   1067                out << "bias(" << samplerReference << ", ";
   1068                break;
   1069            case TextureFunctionHLSL::TextureFunction::LOD:
   1070                out << "lod(" << samplerReference << ", ";
   1071                break;
   1072            case TextureFunctionHLSL::TextureFunction::LOD0:
   1073                out << "lod(" << samplerReference << ", ";
   1074                break;
   1075            case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
   1076                out << "lod(" << samplerReference << ", ";
   1077                break;
   1078            case TextureFunctionHLSL::TextureFunction::GRAD:
   1079                out << "grad(" << samplerReference << ", ";
   1080                break;
   1081            default:
   1082                UNREACHABLE();
   1083        }
   1084    }
   1085    else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
   1086    {
   1087        OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference);
   1088    }
   1089    else
   1090        UNREACHABLE();
   1091 
   1092    const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
   1093    out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords);
   1094 
   1095    if (hlslCoords >= 2)
   1096    {
   1097        out << "(" << texCoordX << ", " << texCoordY;
   1098    }
   1099    else if (hlslCoords == 1)
   1100    {
   1101        std::string varName(texCoordX.data());
   1102        if (size_t pos = varName.find_last_of('.') != std::string::npos)
   1103        {
   1104            varName = varName.substr(0, pos);
   1105        }
   1106        out << "(" << varName;
   1107    }
   1108    else
   1109    {
   1110        out << "(";
   1111    }
   1112 
   1113    if (outputType == SH_HLSL_3_0_OUTPUT)
   1114    {
   1115        if (hlslCoords >= 3)
   1116        {
   1117            if (textureFunction.coords < 3)
   1118            {
   1119                out << ", 0";
   1120            }
   1121            else
   1122            {
   1123                out << ", " << texCoordZ;
   1124            }
   1125        }
   1126 
   1127        if (hlslCoords == 4)
   1128        {
   1129            switch (textureFunction.method)
   1130            {
   1131                case TextureFunctionHLSL::TextureFunction::BIAS:
   1132                    out << ", bias";
   1133                    break;
   1134                case TextureFunctionHLSL::TextureFunction::LOD:
   1135                    out << ", lod";
   1136                    break;
   1137                case TextureFunctionHLSL::TextureFunction::LOD0:
   1138                    out << ", 0";
   1139                    break;
   1140                case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
   1141                    out << ", bias";
   1142                    break;
   1143                default:
   1144                    UNREACHABLE();
   1145            }
   1146        }
   1147 
   1148        out << ")";
   1149    }
   1150    else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
   1151    {
   1152        if (hlslCoords >= 3)
   1153        {
   1154            ASSERT(!IsIntegerSampler(textureFunction.sampler) ||
   1155                   !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face");
   1156            out << ", " << texCoordZ;
   1157        }
   1158 
   1159        if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
   1160        {
   1161            if (IsIntegerSampler(textureFunction.sampler))
   1162            {
   1163                out << ", mip)";
   1164            }
   1165            else if (IsShadowSampler(textureFunction.sampler))
   1166            {
   1167                // Compare value
   1168                if (textureFunction.proj)
   1169                {
   1170                    // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
   1171                    // The resulting third component of P' in the shadow forms is used as
   1172                    // Dref
   1173                    out << "), " << texCoordZ;
   1174                }
   1175                else
   1176                {
   1177                    switch (textureFunction.coords)
   1178                    {
   1179                        case 3:
   1180                            out << "), t.z";
   1181                            break;
   1182                        case 4:
   1183                            out << "), t.w";
   1184                            break;
   1185                        default:
   1186                            UNREACHABLE();
   1187                    }
   1188                }
   1189            }
   1190            else
   1191            {
   1192                out << "), ddx, ddy";
   1193            }
   1194        }
   1195        else if (IsIntegerSampler(textureFunction.sampler) ||
   1196                 textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
   1197        {
   1198            if (IsSampler2DMS(textureFunction.sampler) ||
   1199                IsSampler2DMSArray(textureFunction.sampler))
   1200                out << "), index";
   1201            else if (IsSamplerBuffer(textureFunction.sampler))
   1202                out << ")";
   1203            else
   1204                out << ", mip)";
   1205        }
   1206        else if (IsShadowSampler(textureFunction.sampler))
   1207        {
   1208            // Compare value
   1209            if (textureFunction.proj)
   1210            {
   1211                // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
   1212                // The resulting third component of P' in the shadow forms is used as Dref
   1213                out << "), " << texCoordZ;
   1214            }
   1215            else
   1216            {
   1217                switch (textureFunction.coords)
   1218                {
   1219                    case 3:
   1220                        out << "), t.z";
   1221                        break;
   1222                    case 4:
   1223                        out << "), t.w";
   1224                        break;
   1225                    default:
   1226                        UNREACHABLE();
   1227                }
   1228            }
   1229        }
   1230        else
   1231        {
   1232            switch (textureFunction.method)
   1233            {
   1234                case TextureFunctionHLSL::TextureFunction::IMPLICIT:
   1235                    out << ")";
   1236                    break;
   1237                case TextureFunctionHLSL::TextureFunction::BIAS:
   1238                    out << "), bias";
   1239                    break;
   1240                case TextureFunctionHLSL::TextureFunction::LOD:
   1241                    out << "), lod";
   1242                    break;
   1243                case TextureFunctionHLSL::TextureFunction::LOD0:
   1244                    out << "), 0";
   1245                    break;
   1246                case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
   1247                    out << "), bias";
   1248                    break;
   1249                default:
   1250                    UNREACHABLE();
   1251            }
   1252        }
   1253 
   1254        if (textureFunction.offset &&
   1255            (!IsIntegerSampler(textureFunction.sampler) ||
   1256             textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH))
   1257        {
   1258            out << ", offset";
   1259        }
   1260    }
   1261    else
   1262        UNREACHABLE();
   1263 
   1264    out << ");\n";  // Close the sample function call and return statement
   1265 }
   1266 
   1267 }  // Anonymous namespace
   1268 
   1269 ImmutableString TextureFunctionHLSL::TextureFunction::name() const
   1270 {
   1271    static const ImmutableString kGlTextureName("gl_texture");
   1272 
   1273    ImmutableString suffix(TextureTypeSuffix(this->sampler));
   1274 
   1275    ImmutableStringBuilder name(kGlTextureName.length() + suffix.length() + 4u + 6u + 5u);
   1276 
   1277    name << kGlTextureName;
   1278 
   1279    // We need to include full the sampler type in the function name to make the signature unique
   1280    // on D3D11, where samplers are passed to texture functions as indices.
   1281    name << suffix;
   1282 
   1283    if (proj)
   1284    {
   1285        name << "Proj";
   1286    }
   1287 
   1288    if (offset)
   1289    {
   1290        name << "Offset";
   1291    }
   1292 
   1293    switch (method)
   1294    {
   1295        case IMPLICIT:
   1296            break;
   1297        case BIAS:
   1298            break;  // Extra parameter makes the signature unique
   1299        case LOD:
   1300            name << "Lod";
   1301            break;
   1302        case LOD0:
   1303            name << "Lod0";
   1304            break;
   1305        case LOD0BIAS:
   1306            name << "Lod0";
   1307            break;  // Extra parameter makes the signature unique
   1308        case SIZE:
   1309            name << "Size";
   1310            break;
   1311        case FETCH:
   1312            name << "Fetch";
   1313            break;
   1314        case GRAD:
   1315            name << "Grad";
   1316            break;
   1317        case GATHER:
   1318            name << "Gather";
   1319            break;
   1320        default:
   1321            UNREACHABLE();
   1322    }
   1323 
   1324    return name;
   1325 }
   1326 
   1327 const char *TextureFunctionHLSL::TextureFunction::getReturnType() const
   1328 {
   1329    if (method == TextureFunction::SIZE)
   1330    {
   1331        switch (sampler)
   1332        {
   1333            case EbtSampler2D:
   1334            case EbtISampler2D:
   1335            case EbtUSampler2D:
   1336            case EbtSampler2DShadow:
   1337            case EbtSamplerCube:
   1338            case EbtISamplerCube:
   1339            case EbtUSamplerCube:
   1340            case EbtSamplerCubeShadow:
   1341            case EbtSamplerExternalOES:
   1342            case EbtSampler2DMS:
   1343            case EbtISampler2DMS:
   1344            case EbtUSampler2DMS:
   1345            case EbtSamplerVideoWEBGL:
   1346                return "int2";
   1347            case EbtSampler3D:
   1348            case EbtISampler3D:
   1349            case EbtUSampler3D:
   1350            case EbtSampler2DArray:
   1351            case EbtISampler2DArray:
   1352            case EbtUSampler2DArray:
   1353            case EbtSampler2DMSArray:
   1354            case EbtISampler2DMSArray:
   1355            case EbtUSampler2DMSArray:
   1356            case EbtSampler2DArrayShadow:
   1357                return "int3";
   1358            case EbtISamplerBuffer:
   1359            case EbtUSamplerBuffer:
   1360            case EbtSamplerBuffer:
   1361                return "int";
   1362            default:
   1363                UNREACHABLE();
   1364        }
   1365    }
   1366    else  // Sampling function
   1367    {
   1368        switch (sampler)
   1369        {
   1370            case EbtSampler2D:
   1371            case EbtSampler2DMS:
   1372            case EbtSampler2DMSArray:
   1373            case EbtSampler3D:
   1374            case EbtSamplerCube:
   1375            case EbtSampler2DArray:
   1376            case EbtSamplerExternalOES:
   1377            case EbtSamplerVideoWEBGL:
   1378            case EbtSamplerBuffer:
   1379                return "float4";
   1380            case EbtISampler2D:
   1381            case EbtISampler2DMS:
   1382            case EbtISampler2DMSArray:
   1383            case EbtISampler3D:
   1384            case EbtISamplerCube:
   1385            case EbtISampler2DArray:
   1386            case EbtISamplerBuffer:
   1387                return "int4";
   1388            case EbtUSampler2D:
   1389            case EbtUSampler2DMS:
   1390            case EbtUSampler2DMSArray:
   1391            case EbtUSampler3D:
   1392            case EbtUSamplerCube:
   1393            case EbtUSampler2DArray:
   1394            case EbtUSamplerBuffer:
   1395                return "uint4";
   1396            case EbtSampler2DShadow:
   1397            case EbtSamplerCubeShadow:
   1398            case EbtSampler2DArrayShadow:
   1399                if (method == TextureFunctionHLSL::TextureFunction::GATHER)
   1400                {
   1401                    return "float4";
   1402                }
   1403                else
   1404                {
   1405                    return "float";
   1406                }
   1407            default:
   1408                UNREACHABLE();
   1409        }
   1410    }
   1411    return "";
   1412 }
   1413 
   1414 bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
   1415 {
   1416    return std::tie(sampler, coords, proj, offset, method) <
   1417           std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
   1418 }
   1419 
   1420 ImmutableString TextureFunctionHLSL::useTextureFunction(const ImmutableString &name,
   1421                                                        TBasicType samplerType,
   1422                                                        int coords,
   1423                                                        size_t argumentCount,
   1424                                                        bool lod0,
   1425                                                        sh::GLenum shaderType)
   1426 {
   1427    TextureFunction textureFunction;
   1428    textureFunction.sampler = samplerType;
   1429    textureFunction.coords  = coords;
   1430    textureFunction.method  = TextureFunction::IMPLICIT;
   1431    textureFunction.proj    = false;
   1432    textureFunction.offset  = false;
   1433 
   1434    if (name == "texture2D" || name == "textureCube" || name == "texture")
   1435    {
   1436        textureFunction.method = TextureFunction::IMPLICIT;
   1437    }
   1438    else if (name == "texture2DProj" || name == "textureProj")
   1439    {
   1440        textureFunction.method = TextureFunction::IMPLICIT;
   1441        textureFunction.proj   = true;
   1442    }
   1443    else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
   1444             name == "texture2DLodEXT" || name == "textureCubeLodEXT")
   1445    {
   1446        textureFunction.method = TextureFunction::LOD;
   1447    }
   1448    else if (name == "texture2DProjLod" || name == "textureProjLod" ||
   1449             name == "texture2DProjLodEXT")
   1450    {
   1451        textureFunction.method = TextureFunction::LOD;
   1452        textureFunction.proj   = true;
   1453    }
   1454    else if (name == "textureSize")
   1455    {
   1456        textureFunction.method = TextureFunction::SIZE;
   1457    }
   1458    else if (name == "textureOffset")
   1459    {
   1460        textureFunction.method = TextureFunction::IMPLICIT;
   1461        textureFunction.offset = true;
   1462    }
   1463    else if (name == "textureProjOffset")
   1464    {
   1465        textureFunction.method = TextureFunction::IMPLICIT;
   1466        textureFunction.offset = true;
   1467        textureFunction.proj   = true;
   1468    }
   1469    else if (name == "textureLodOffset")
   1470    {
   1471        textureFunction.method = TextureFunction::LOD;
   1472        textureFunction.offset = true;
   1473    }
   1474    else if (name == "textureProjLodOffset")
   1475    {
   1476        textureFunction.method = TextureFunction::LOD;
   1477        textureFunction.proj   = true;
   1478        textureFunction.offset = true;
   1479    }
   1480    else if (name == "texelFetch")
   1481    {
   1482        textureFunction.method = TextureFunction::FETCH;
   1483    }
   1484    else if (name == "texelFetchOffset")
   1485    {
   1486        textureFunction.method = TextureFunction::FETCH;
   1487        textureFunction.offset = true;
   1488    }
   1489    else if (name == "textureGrad" || name == "texture2DGradEXT")
   1490    {
   1491        textureFunction.method = TextureFunction::GRAD;
   1492    }
   1493    else if (name == "textureGradOffset")
   1494    {
   1495        textureFunction.method = TextureFunction::GRAD;
   1496        textureFunction.offset = true;
   1497    }
   1498    else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" ||
   1499             name == "textureCubeGradEXT")
   1500    {
   1501        textureFunction.method = TextureFunction::GRAD;
   1502        textureFunction.proj   = true;
   1503    }
   1504    else if (name == "textureProjGradOffset")
   1505    {
   1506        textureFunction.method = TextureFunction::GRAD;
   1507        textureFunction.proj   = true;
   1508        textureFunction.offset = true;
   1509    }
   1510    else if (name == "textureGather")
   1511    {
   1512        textureFunction.method = TextureFunction::GATHER;
   1513    }
   1514    else if (name == "textureGatherOffset")
   1515    {
   1516        textureFunction.method = TextureFunction::GATHER;
   1517        textureFunction.offset = true;
   1518    }
   1519    else if (name == "textureVideoWEBGL")
   1520    {
   1521        textureFunction.method = TextureFunction::IMPLICIT;
   1522    }
   1523    else
   1524        UNREACHABLE();
   1525 
   1526    if (textureFunction.method ==
   1527        TextureFunction::IMPLICIT)  // Could require lod 0 or have a bias argument
   1528    {
   1529        size_t mandatoryArgumentCount = 2;  // All functions have sampler and coordinate arguments
   1530 
   1531        if (textureFunction.offset)
   1532        {
   1533            mandatoryArgumentCount++;
   1534        }
   1535 
   1536        bool bias = (argumentCount > mandatoryArgumentCount);  // Bias argument is optional
   1537 
   1538        if (lod0 || shaderType == GL_VERTEX_SHADER || shaderType == GL_COMPUTE_SHADER)
   1539        {
   1540            if (bias)
   1541            {
   1542                textureFunction.method = TextureFunction::LOD0BIAS;
   1543            }
   1544            else
   1545            {
   1546                textureFunction.method = TextureFunction::LOD0;
   1547            }
   1548        }
   1549        else if (bias)
   1550        {
   1551            textureFunction.method = TextureFunction::BIAS;
   1552        }
   1553    }
   1554 
   1555    mUsesTexture.insert(textureFunction);
   1556    return textureFunction.name();
   1557 }
   1558 
   1559 void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out,
   1560                                                const ShShaderOutput outputType,
   1561                                                bool getDimensionsIgnoresBaseLevel)
   1562 {
   1563    for (const TextureFunction &textureFunction : mUsesTexture)
   1564    {
   1565        // Function header
   1566        out << textureFunction.getReturnType() << " " << textureFunction.name() << "(";
   1567 
   1568        OutputTextureFunctionArgumentList(out, textureFunction, outputType);
   1569 
   1570        out << ")\n"
   1571               "{\n";
   1572 
   1573        // In some cases we use a variable to store the texture/sampler objects, but to work around
   1574        // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
   1575        // sampling we need to call the function directly on references to the texture and sampler
   1576        // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture*
   1577        // tests.
   1578        ImmutableString textureReference("");
   1579        ImmutableString samplerReference("");
   1580        GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference);
   1581 
   1582        if (textureFunction.method == TextureFunction::SIZE)
   1583        {
   1584            OutputTextureSizeFunctionBody(out, textureFunction, textureReference,
   1585                                          getDimensionsIgnoresBaseLevel);
   1586        }
   1587        else
   1588        {
   1589            ImmutableString texCoordX("t.x");
   1590            ImmutableString texCoordY("t.y");
   1591            ImmutableString texCoordZ("t.z");
   1592            if (textureFunction.method == TextureFunction::GATHER)
   1593            {
   1594                OutputTextureGatherFunctionBody(out, textureFunction, outputType, textureReference,
   1595                                                samplerReference, texCoordX, texCoordY, texCoordZ);
   1596            }
   1597            else
   1598            {
   1599                ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ);
   1600                OutputIntegerTextureSampleFunctionComputations(
   1601                    out, textureFunction, outputType, textureReference, &texCoordX, &texCoordY,
   1602                    &texCoordZ, getDimensionsIgnoresBaseLevel);
   1603                OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType,
   1604                                                           textureReference, samplerReference,
   1605                                                           texCoordX, texCoordY, texCoordZ);
   1606            }
   1607        }
   1608 
   1609        out << "}\n"
   1610               "\n";
   1611    }
   1612 }
   1613 
   1614 }  // namespace sh