tor-browser

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

RewriteCubeMapSamplersAs2DArray.cpp (46198B)


      1 //
      2 // Copyright 2019 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 // RewriteCubeMapSamplersAs2DArray: Change samplerCube samplers to sampler2DArray for seamful cube
      7 // map emulation.
      8 //
      9 // Relies on MonomorphizeUnsupportedFunctions to ensure samplerCube variables are not
     10 // passed to functions (for simplicity).
     11 //
     12 
     13 #include "compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h"
     14 
     15 #include "compiler/translator/Compiler.h"
     16 #include "compiler/translator/ImmutableStringBuilder.h"
     17 #include "compiler/translator/StaticType.h"
     18 #include "compiler/translator/SymbolTable.h"
     19 #include "compiler/translator/tree_util/IntermNode_util.h"
     20 #include "compiler/translator/tree_util/IntermTraverse.h"
     21 #include "compiler/translator/tree_util/ReplaceVariable.h"
     22 
     23 namespace sh
     24 {
     25 namespace
     26 {
     27 constexpr ImmutableString kCoordTransformFuncName("ANGLECubeMapCoordTransform");
     28 constexpr ImmutableString kCoordTransformFuncNameImplicit("ANGLECubeMapCoordTransformImplicit");
     29 
     30 TIntermTyped *DerivativeQuotient(TIntermTyped *u,
     31                                 TIntermTyped *du,
     32                                 TIntermTyped *v,
     33                                 TIntermTyped *dv,
     34                                 TIntermTyped *vRecip)
     35 {
     36    // (du v - dv u) / v^2
     37    return new TIntermBinary(
     38        EOpMul,
     39        new TIntermBinary(EOpSub, new TIntermBinary(EOpMul, du->deepCopy(), v->deepCopy()),
     40                          new TIntermBinary(EOpMul, dv->deepCopy(), u->deepCopy())),
     41        new TIntermBinary(EOpMul, vRecip->deepCopy(), vRecip->deepCopy()));
     42 }
     43 
     44 TIntermTyped *Swizzle1(TIntermTyped *array, int i)
     45 {
     46    return new TIntermSwizzle(array, {i});
     47 }
     48 
     49 TIntermTyped *IndexDirect(TIntermTyped *array, int i)
     50 {
     51    return new TIntermBinary(EOpIndexDirect, array, CreateIndexNode(i));
     52 }
     53 
     54 // Generated the common transformation in each coord transformation case.  See comment in
     55 // declareCoordTranslationFunction().  Called with P, dPdx and dPdy.
     56 void TransformXMajor(const TSymbolTable &symbolTable,
     57                     TIntermBlock *block,
     58                     TIntermTyped *x,
     59                     TIntermTyped *y,
     60                     TIntermTyped *z,
     61                     TIntermTyped *uc,
     62                     TIntermTyped *vc)
     63 {
     64    // uc = -sign(x)*z
     65    // vc = -y
     66    TIntermTyped *signX =
     67        CreateBuiltInUnaryFunctionCallNode("sign", x->deepCopy(), symbolTable, 100);
     68 
     69    TIntermTyped *ucValue =
     70        new TIntermUnary(EOpNegative, new TIntermBinary(EOpMul, signX, z->deepCopy()), nullptr);
     71    TIntermTyped *vcValue = new TIntermUnary(EOpNegative, y->deepCopy(), nullptr);
     72 
     73    block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue));
     74    block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue));
     75 }
     76 
     77 void TransformDerivativeXMajor(TIntermBlock *block,
     78                               TSymbolTable *symbolTable,
     79                               TIntermTyped *x,
     80                               TIntermTyped *y,
     81                               TIntermTyped *z,
     82                               TIntermTyped *dx,
     83                               TIntermTyped *dy,
     84                               TIntermTyped *dz,
     85                               TIntermTyped *du,
     86                               TIntermTyped *dv,
     87                               TIntermTyped *xRecip)
     88 {
     89    // Only the magnitude of the derivative matters, so we ignore the sign(x)
     90    // and the negations.
     91    TIntermTyped *duValue = DerivativeQuotient(z, dz, x, dx, xRecip);
     92    TIntermTyped *dvValue = DerivativeQuotient(y, dy, x, dx, xRecip);
     93    duValue               = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium));
     94    dvValue               = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium));
     95    block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue));
     96    block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue));
     97 }
     98 
     99 void TransformImplicitDerivativeXMajor(TIntermBlock *block,
    100                                       TIntermTyped *dOuter,
    101                                       TIntermTyped *du,
    102                                       TIntermTyped *dv)
    103 {
    104    block->appendStatement(
    105        new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 2)));
    106    block->appendStatement(
    107        new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 1)));
    108 }
    109 
    110 void TransformYMajor(const TSymbolTable &symbolTable,
    111                     TIntermBlock *block,
    112                     TIntermTyped *x,
    113                     TIntermTyped *y,
    114                     TIntermTyped *z,
    115                     TIntermTyped *uc,
    116                     TIntermTyped *vc)
    117 {
    118    // uc = x
    119    // vc = sign(y)*z
    120    TIntermTyped *signY =
    121        CreateBuiltInUnaryFunctionCallNode("sign", y->deepCopy(), symbolTable, 100);
    122 
    123    TIntermTyped *ucValue = x->deepCopy();
    124    TIntermTyped *vcValue = new TIntermBinary(EOpMul, signY, z->deepCopy());
    125 
    126    block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue));
    127    block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue));
    128 }
    129 
    130 void TransformDerivativeYMajor(TIntermBlock *block,
    131                               TSymbolTable *symbolTable,
    132                               TIntermTyped *x,
    133                               TIntermTyped *y,
    134                               TIntermTyped *z,
    135                               TIntermTyped *dx,
    136                               TIntermTyped *dy,
    137                               TIntermTyped *dz,
    138                               TIntermTyped *du,
    139                               TIntermTyped *dv,
    140                               TIntermTyped *yRecip)
    141 {
    142    // Only the magnitude of the derivative matters, so we ignore the sign(x)
    143    // and the negations.
    144    TIntermTyped *duValue = DerivativeQuotient(x, dx, y, dy, yRecip);
    145    TIntermTyped *dvValue = DerivativeQuotient(z, dz, y, dy, yRecip);
    146    duValue               = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium));
    147    dvValue               = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium));
    148    block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue));
    149    block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue));
    150 }
    151 
    152 void TransformImplicitDerivativeYMajor(TIntermBlock *block,
    153                                       TIntermTyped *dOuter,
    154                                       TIntermTyped *du,
    155                                       TIntermTyped *dv)
    156 {
    157    block->appendStatement(
    158        new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 0)));
    159    block->appendStatement(
    160        new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 2)));
    161 }
    162 
    163 void TransformZMajor(const TSymbolTable &symbolTable,
    164                     TIntermBlock *block,
    165                     TIntermTyped *x,
    166                     TIntermTyped *y,
    167                     TIntermTyped *z,
    168                     TIntermTyped *uc,
    169                     TIntermTyped *vc)
    170 {
    171    // uc = size(z)*x
    172    // vc = -y
    173    TIntermTyped *signZ =
    174        CreateBuiltInUnaryFunctionCallNode("sign", z->deepCopy(), symbolTable, 100);
    175 
    176    TIntermTyped *ucValue = new TIntermBinary(EOpMul, signZ, x->deepCopy());
    177    TIntermTyped *vcValue = new TIntermUnary(EOpNegative, y->deepCopy(), nullptr);
    178 
    179    block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue));
    180    block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue));
    181 }
    182 
    183 void TransformDerivativeZMajor(TIntermBlock *block,
    184                               TSymbolTable *symbolTable,
    185                               TIntermTyped *x,
    186                               TIntermTyped *y,
    187                               TIntermTyped *z,
    188                               TIntermTyped *dx,
    189                               TIntermTyped *dy,
    190                               TIntermTyped *dz,
    191                               TIntermTyped *du,
    192                               TIntermTyped *dv,
    193                               TIntermTyped *zRecip)
    194 {
    195    // Only the magnitude of the derivative matters, so we ignore the sign(x)
    196    // and the negations.
    197    TIntermTyped *duValue = DerivativeQuotient(x, dx, z, dz, zRecip);
    198    TIntermTyped *dvValue = DerivativeQuotient(y, dy, z, dz, zRecip);
    199    duValue               = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium));
    200    dvValue               = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium));
    201    block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue));
    202    block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue));
    203 }
    204 
    205 void TransformImplicitDerivativeZMajor(TIntermBlock *block,
    206                                       TIntermTyped *dOuter,
    207                                       TIntermTyped *du,
    208                                       TIntermTyped *dv)
    209 {
    210    block->appendStatement(
    211        new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 0)));
    212    block->appendStatement(
    213        new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 1)));
    214 }
    215 
    216 class RewriteCubeMapSamplersAs2DArrayTraverser : public TIntermTraverser
    217 {
    218  public:
    219    RewriteCubeMapSamplersAs2DArrayTraverser(TSymbolTable *symbolTable, bool isFragmentShader)
    220        : TIntermTraverser(true, false, false, symbolTable),
    221          mCubeXYZToArrayUVL(nullptr),
    222          mCubeXYZToArrayUVLImplicit(nullptr),
    223          mIsFragmentShader(isFragmentShader),
    224          mCoordTranslationFunctionDecl(nullptr),
    225          mCoordTranslationFunctionImplicitDecl(nullptr)
    226    {}
    227 
    228    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
    229    {
    230        const TIntermSequence &sequence = *(node->getSequence());
    231 
    232        TIntermTyped *variable = sequence.front()->getAsTyped();
    233        const TType &type      = variable->getType();
    234        bool isSamplerCube     = type.getQualifier() == EvqUniform && type.isSamplerCube();
    235 
    236        if (isSamplerCube)
    237        {
    238            // Samplers cannot have initializers, so the declaration must necessarily be a symbol.
    239            TIntermSymbol *samplerVariable = variable->getAsSymbolNode();
    240            ASSERT(samplerVariable != nullptr);
    241 
    242            declareSampler2DArray(&samplerVariable->variable(), node);
    243            return false;
    244        }
    245 
    246        return true;
    247    }
    248 
    249    bool visitAggregate(Visit visit, TIntermAggregate *node) override
    250    {
    251        if (BuiltInGroup::IsBuiltIn(node->getOp()))
    252        {
    253            bool converted = convertBuiltinFunction(node);
    254            return !converted;
    255        }
    256 
    257        // AST functions don't require modification as samplerCube function parameters are removed
    258        // by MonomorphizeUnsupportedFunctions.
    259        return true;
    260    }
    261 
    262    TIntermFunctionDefinition *getCoordTranslationFunctionDecl()
    263    {
    264        return mCoordTranslationFunctionDecl;
    265    }
    266 
    267    TIntermFunctionDefinition *getCoordTranslationFunctionDeclImplicit()
    268    {
    269        return mCoordTranslationFunctionImplicitDecl;
    270    }
    271 
    272  private:
    273    void declareSampler2DArray(const TVariable *samplerCubeVar, TIntermDeclaration *node)
    274    {
    275        if (mCubeXYZToArrayUVL == nullptr)
    276        {
    277            // If not done yet, declare the function that transforms cube map texture sampling
    278            // coordinates to face index and uv coordinates.
    279            declareCoordTranslationFunction(false, kCoordTransformFuncName, &mCubeXYZToArrayUVL,
    280                                            &mCoordTranslationFunctionDecl);
    281        }
    282        if (mCubeXYZToArrayUVLImplicit == nullptr && mIsFragmentShader)
    283        {
    284            declareCoordTranslationFunction(true, kCoordTransformFuncNameImplicit,
    285                                            &mCubeXYZToArrayUVLImplicit,
    286                                            &mCoordTranslationFunctionImplicitDecl);
    287        }
    288 
    289        TType *newType = new TType(samplerCubeVar->getType());
    290        newType->setBasicType(EbtSampler2DArray);
    291 
    292        TVariable *sampler2DArrayVar = new TVariable(mSymbolTable, samplerCubeVar->name(), newType,
    293                                                     samplerCubeVar->symbolType());
    294 
    295        TIntermDeclaration *sampler2DArrayDecl = new TIntermDeclaration();
    296        sampler2DArrayDecl->appendDeclarator(new TIntermSymbol(sampler2DArrayVar));
    297 
    298        queueReplacement(sampler2DArrayDecl, OriginalNode::IS_DROPPED);
    299 
    300        // Remember the sampler2DArray variable.
    301        mSamplerMap[samplerCubeVar] = sampler2DArrayVar;
    302    }
    303 
    304    void declareCoordTranslationFunction(bool implicit,
    305                                         const ImmutableString &name,
    306                                         TFunction **functionOut,
    307                                         TIntermFunctionDefinition **declOut)
    308    {
    309        // GLES2.0 (as well as desktop OpenGL 2.0) define the coordination transformation as
    310        // follows.  Given xyz cube coordinates, where each channel is in [-1, 1], the following
    311        // table calculates uc, vc and ma as well as the cube map face.
    312        //
    313        //    Major    Axis Direction Target     uc  vc  ma
    314        //     +x   TEXTURE_CUBE_MAP_POSITIVE_X  -z  -y  |x|
    315        //     -x   TEXTURE_CUBE_MAP_NEGATIVE_X   z  -y  |x|
    316        //     +y   TEXTURE_CUBE_MAP_POSITIVE_Y   x   z  |y|
    317        //     -y   TEXTURE_CUBE_MAP_NEGATIVE_Y   x  -z  |y|
    318        //     +z   TEXTURE_CUBE_MAP_POSITIVE_Z   x  -y  |z|
    319        //     -z   TEXTURE_CUBE_MAP_NEGATIVE_Z  -x  -y  |z|
    320        //
    321        // "Major" is an indication of the axis with the largest value.  The cube map face indicates
    322        // the layer to sample from.  The uv coordinates to sample from are calculated as,
    323        // effectively transforming the uv values to [0, 1]:
    324        //
    325        //     u = (1 + uc/ma) / 2
    326        //     v = (1 + vc/ma) / 2
    327        //
    328        // The function can be implemented as 6 ifs, though it would be far from efficient.  The
    329        // following calculations implement the table above in a smaller number of instructions.
    330        //
    331        // First, ma can be calculated as the max of the three axes.
    332        //
    333        //     ma = max3(|x|, |y|, |z|)
    334        //
    335        // We have three cases:
    336        //
    337        //     ma == |x|:      uc = -sign(x)*z
    338        //                     vc = -y
    339        //                  layer = float(x < 0)
    340        //
    341        //     ma == |y|:      uc = x
    342        //                     vc = sign(y)*z
    343        //                  layer = 2 + float(y < 0)
    344        //
    345        //     ma == |z|:      uc = size(z)*x
    346        //                     vc = -y
    347        //                  layer = 4 + float(z < 0)
    348        //
    349        // This can be implemented with a number of ?: instructions or 3 ifs. ?: would require all
    350        // expressions to be evaluated (vector ALU) while if would require exec mask and jumps
    351        // (scalar operations).  We implement this using ifs as there would otherwise be many vector
    352        // operations and not much of anything else.
    353        //
    354        // If textureCubeGrad is used, we also need to transform the provided dPdx and dPdy (both
    355        // vec3) to a dUVdx and dUVdy.  Assume P=(r,s,t) and we are investigating dx (note the
    356        // change from xyz to rst to not confuse with dx and dy):
    357        //
    358        //     uv = (f(r,s,t)/ma + 1)/2
    359        //
    360        // Where f is one of the transformations above for uc and vc.  Between two neighbors along
    361        // the x axis, we have P0=(r0,s0,t0) and P1=(r1,s1,t1)
    362        //
    363        //     dP = (r1-r0, s1-s0, t1-t0)
    364        //     dUV = (f(r1,s1,t1)/ma1 - g(r0,s0,t0)/ma0) / 2
    365        //
    366        // f and g may not necessarily be the same because the two points may have different major
    367        // axes.  Even with the same major access, the sign that's used in the formulas may not be
    368        // the same.  Furthermore, ma0 and ma1 may not be the same.  This makes it impossible to
    369        // derive dUV from dP exactly.
    370        //
    371        // However, gradient transformation is implementation dependant, so we will simplify and
    372        // assume all the above complications are non-existent.  We therefore have:
    373        //
    374        //      dUV = (f(r1,s1,t1)/ma0 - f(r0,s0,t0)/ma0)/2
    375        //
    376        // Given that we assumed the sign functions are returning identical results for the two
    377        // points, f becomes a linear transformation.  Thus:
    378        //
    379        //      dUV = f(r1-r0,s1-0,t1-t0)/ma0/2
    380        //
    381        // In other words, we use the same formulae that transform XYZ (RST here) to UV to
    382        // transform the derivatives.
    383        //
    384        //     ma == |x|:    dUdx = -sign(x)*dPdx.z / ma / 2
    385        //                   dVdx = -dPdx.y / ma / 2
    386        //
    387        //     ma == |y|:    dUdx = dPdx.x / ma / 2
    388        //                   dVdx = sign(y)*dPdx.z / ma / 2
    389        //
    390        //     ma == |z|:    dUdx = size(z)*dPdx.x / ma / 2
    391        //                   dVdx = -dPdx.y / ma / 2
    392        //
    393        // Similarly for dy.
    394 
    395        // Create the function parameters: vec3 P, vec3 dPdx, vec3 dPdy,
    396        //                                 out vec2 dUVdx, out vec2 dUVdy
    397        const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>();
    398        TType *inVec3Type     = new TType(*vec3Type);
    399        inVec3Type->setQualifier(EvqParamIn);
    400 
    401        TVariable *pVar    = new TVariable(mSymbolTable, ImmutableString("P"), inVec3Type,
    402                                        SymbolType::AngleInternal);
    403        TVariable *dPdxVar = new TVariable(mSymbolTable, ImmutableString("dPdx"), inVec3Type,
    404                                           SymbolType::AngleInternal);
    405        TVariable *dPdyVar = new TVariable(mSymbolTable, ImmutableString("dPdy"), inVec3Type,
    406                                           SymbolType::AngleInternal);
    407 
    408        const TType *vec2Type = StaticType::GetBasic<EbtFloat, EbpHigh, 2>();
    409        TType *outVec2Type    = new TType(*vec2Type);
    410        outVec2Type->setQualifier(EvqParamOut);
    411 
    412        TVariable *dUVdxVar = new TVariable(mSymbolTable, ImmutableString("dUVdx"), outVec2Type,
    413                                            SymbolType::AngleInternal);
    414        TVariable *dUVdyVar = new TVariable(mSymbolTable, ImmutableString("dUVdy"), outVec2Type,
    415                                            SymbolType::AngleInternal);
    416 
    417        TIntermSymbol *p     = new TIntermSymbol(pVar);
    418        TIntermSymbol *dPdx  = new TIntermSymbol(dPdxVar);
    419        TIntermSymbol *dPdy  = new TIntermSymbol(dPdyVar);
    420        TIntermSymbol *dUVdx = new TIntermSymbol(dUVdxVar);
    421        TIntermSymbol *dUVdy = new TIntermSymbol(dUVdyVar);
    422 
    423        // Create the function body as statements are generated.
    424        TIntermBlock *body = new TIntermBlock;
    425 
    426        // Create the swizzle nodes that will be used in multiple expressions:
    427        TIntermSwizzle *x = new TIntermSwizzle(p->deepCopy(), {0});
    428        TIntermSwizzle *y = new TIntermSwizzle(p->deepCopy(), {1});
    429        TIntermSwizzle *z = new TIntermSwizzle(p->deepCopy(), {2});
    430 
    431        // Create abs and "< 0" expressions from the channels.
    432        const TType *floatType = StaticType::GetBasic<EbtFloat, EbpHigh>();
    433 
    434        TIntermTyped *isNegX = new TIntermBinary(EOpLessThan, x, CreateZeroNode(*floatType));
    435        TIntermTyped *isNegY = new TIntermBinary(EOpLessThan, y, CreateZeroNode(*floatType));
    436        TIntermTyped *isNegZ = new TIntermBinary(EOpLessThan, z, CreateZeroNode(*floatType));
    437 
    438        TIntermSymbol *absX = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    439        TIntermSymbol *absY = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    440        TIntermSymbol *absZ = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    441 
    442        TIntermDeclaration *absXDecl = CreateTempInitDeclarationNode(
    443            &absX->variable(),
    444            CreateBuiltInUnaryFunctionCallNode("abs", x->deepCopy(), *mSymbolTable, 100));
    445        TIntermDeclaration *absYDecl = CreateTempInitDeclarationNode(
    446            &absY->variable(),
    447            CreateBuiltInUnaryFunctionCallNode("abs", y->deepCopy(), *mSymbolTable, 100));
    448        TIntermDeclaration *absZDecl = CreateTempInitDeclarationNode(
    449            &absZ->variable(),
    450            CreateBuiltInUnaryFunctionCallNode("abs", z->deepCopy(), *mSymbolTable, 100));
    451 
    452        body->appendStatement(absXDecl);
    453        body->appendStatement(absYDecl);
    454        body->appendStatement(absZDecl);
    455 
    456        // Create temporary variable for division outer product matrix and its
    457        // derivatives.
    458        // recipOuter[i][j] = 0.5 * P[j] / P[i]
    459        const TType *mat3Type     = StaticType::GetBasic<EbtFloat, EbpHigh, 3, 3>();
    460        TIntermSymbol *recipOuter = new TIntermSymbol(CreateTempVariable(mSymbolTable, mat3Type));
    461 
    462        TIntermTyped *pRecip =
    463            new TIntermBinary(EOpDiv, CreateFloatNode(1.0, EbpMedium), p->deepCopy());
    464        TIntermSymbol *pRecipVar = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    465 
    466        body->appendStatement(CreateTempInitDeclarationNode(&pRecipVar->variable(), pRecip));
    467 
    468        TIntermSequence args = {
    469            p->deepCopy(), new TIntermBinary(EOpVectorTimesScalar, CreateFloatNode(0.5, EbpMedium),
    470                                             pRecipVar->deepCopy())};
    471        TIntermDeclaration *recipOuterDecl = CreateTempInitDeclarationNode(
    472            &recipOuter->variable(),
    473            CreateBuiltInFunctionCallNode("outerProduct", &args, *mSymbolTable, 300));
    474        body->appendStatement(recipOuterDecl);
    475 
    476        TIntermSymbol *dPDXdx = nullptr;
    477        TIntermSymbol *dPDYdx = nullptr;
    478        TIntermSymbol *dPDZdx = nullptr;
    479        TIntermSymbol *dPDXdy = nullptr;
    480        TIntermSymbol *dPDYdy = nullptr;
    481        TIntermSymbol *dPDZdy = nullptr;
    482        if (implicit)
    483        {
    484            dPDXdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    485            dPDYdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    486            dPDZdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    487            dPDXdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    488            dPDYdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    489            dPDZdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    490 
    491            TIntermDeclaration *dPDXdxDecl = CreateTempInitDeclarationNode(
    492                &dPDXdx->variable(),
    493                CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 0)->deepCopy(),
    494                                                   *mSymbolTable, 300));
    495            TIntermDeclaration *dPDYdxDecl = CreateTempInitDeclarationNode(
    496                &dPDYdx->variable(),
    497                CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 1)->deepCopy(),
    498                                                   *mSymbolTable, 300));
    499            TIntermDeclaration *dPDZdxDecl = CreateTempInitDeclarationNode(
    500                &dPDZdx->variable(),
    501                CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 2)->deepCopy(),
    502                                                   *mSymbolTable, 300));
    503            TIntermDeclaration *dPDXdyDecl = CreateTempInitDeclarationNode(
    504                &dPDXdy->variable(),
    505                CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 0)->deepCopy(),
    506                                                   *mSymbolTable, 300));
    507            TIntermDeclaration *dPDYdyDecl = CreateTempInitDeclarationNode(
    508                &dPDYdy->variable(),
    509                CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 1)->deepCopy(),
    510                                                   *mSymbolTable, 300));
    511            TIntermDeclaration *dPDZdyDecl = CreateTempInitDeclarationNode(
    512                &dPDZdy->variable(),
    513                CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 2)->deepCopy(),
    514                                                   *mSymbolTable, 300));
    515 
    516            body->appendStatement(dPDXdxDecl);
    517            body->appendStatement(dPDYdxDecl);
    518            body->appendStatement(dPDZdxDecl);
    519            body->appendStatement(dPDXdyDecl);
    520            body->appendStatement(dPDYdyDecl);
    521            body->appendStatement(dPDZdyDecl);
    522        }
    523 
    524        // Create temporary variables for ma, uc, vc, and l (layer), as well as dUdx, dVdx, dUdy
    525        // and dVdy.
    526        TIntermSymbol *ma   = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    527        TIntermSymbol *l    = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    528        TIntermSymbol *uc   = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    529        TIntermSymbol *vc   = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    530        TIntermSymbol *dUdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    531        TIntermSymbol *dVdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    532        TIntermSymbol *dUdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    533        TIntermSymbol *dVdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    534 
    535        body->appendStatement(CreateTempDeclarationNode(&ma->variable()));
    536        body->appendStatement(CreateTempDeclarationNode(&l->variable()));
    537        body->appendStatement(CreateTempDeclarationNode(&uc->variable()));
    538        body->appendStatement(CreateTempDeclarationNode(&vc->variable()));
    539        body->appendStatement(CreateTempDeclarationNode(&dUdx->variable()));
    540        body->appendStatement(CreateTempDeclarationNode(&dVdx->variable()));
    541        body->appendStatement(CreateTempDeclarationNode(&dUdy->variable()));
    542        body->appendStatement(CreateTempDeclarationNode(&dVdy->variable()));
    543 
    544        // ma = max(|x|, max(|y|, |z|))
    545        TIntermSequence argsMaxYZ = {absY->deepCopy(), absZ->deepCopy()};
    546        TIntermTyped *maxYZ = CreateBuiltInFunctionCallNode("max", &argsMaxYZ, *mSymbolTable, 100);
    547        TIntermSequence argsMaxValue = {absX->deepCopy(), maxYZ};
    548        TIntermTyped *maValue =
    549            CreateBuiltInFunctionCallNode("max", &argsMaxValue, *mSymbolTable, 100);
    550        body->appendStatement(new TIntermBinary(EOpAssign, ma, maValue));
    551 
    552        // ma == |x| and ma == |y| expressions
    553        TIntermTyped *isXMajor = new TIntermBinary(EOpEqual, ma->deepCopy(), absX->deepCopy());
    554        TIntermTyped *isYMajor = new TIntermBinary(EOpEqual, ma->deepCopy(), absY->deepCopy());
    555 
    556        // Determine the cube face:
    557 
    558        // The case where x is major:
    559        //     layer = float(x < 0)
    560        TIntermSequence argsNegX = {isNegX};
    561        TIntermTyped *xl         = TIntermAggregate::CreateConstructor(*floatType, &argsNegX);
    562 
    563        TIntermBlock *calculateXL = new TIntermBlock;
    564        calculateXL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), xl));
    565 
    566        // The case where y is major:
    567        //     layer = 2 + float(y < 0)
    568        TIntermSequence argsNegY = {isNegY};
    569        TIntermTyped *yl =
    570            new TIntermBinary(EOpAdd, CreateFloatNode(2.0f, EbpMedium),
    571                              TIntermAggregate::CreateConstructor(*floatType, &argsNegY));
    572 
    573        TIntermBlock *calculateYL = new TIntermBlock;
    574        calculateYL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), yl));
    575 
    576        // The case where z is major:
    577        //     layer = 4 + float(z < 0)
    578        TIntermSequence argsNegZ = {isNegZ};
    579        TIntermTyped *zl =
    580            new TIntermBinary(EOpAdd, CreateFloatNode(4.0f, EbpMedium),
    581                              TIntermAggregate::CreateConstructor(*floatType, &argsNegZ));
    582 
    583        TIntermBlock *calculateZL = new TIntermBlock;
    584        calculateZL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), zl));
    585 
    586        // Create the if-else paths:
    587        TIntermIfElse *calculateYZL     = new TIntermIfElse(isYMajor, calculateYL, calculateZL);
    588        TIntermBlock *calculateYZLBlock = new TIntermBlock;
    589        calculateYZLBlock->appendStatement(calculateYZL);
    590        TIntermIfElse *calculateXYZL = new TIntermIfElse(isXMajor, calculateXL, calculateYZLBlock);
    591        body->appendStatement(calculateXYZL);
    592 
    593        // layer < 1.5 (covering faces 0 and 1, corresponding to major axis being X) and layer < 3.5
    594        // (covering faces 2 and 3, corresponding to major axis being Y).  Used to determine which
    595        // of the three transformations to apply.  Previously, ma == |X| and ma == |Y| was used,
    596        // which is no longer correct for helper invocations.  The value of ma is updated in each
    597        // case for these invocations.
    598        isXMajor = new TIntermBinary(EOpLessThan, l->deepCopy(), CreateFloatNode(1.5f, EbpMedium));
    599        isYMajor = new TIntermBinary(EOpLessThan, l->deepCopy(), CreateFloatNode(3.5f, EbpMedium));
    600 
    601        TIntermSwizzle *dPdxX = new TIntermSwizzle(dPdx->deepCopy(), {0});
    602        TIntermSwizzle *dPdxY = new TIntermSwizzle(dPdx->deepCopy(), {1});
    603        TIntermSwizzle *dPdxZ = new TIntermSwizzle(dPdx->deepCopy(), {2});
    604 
    605        TIntermSwizzle *dPdyX = new TIntermSwizzle(dPdy->deepCopy(), {0});
    606        TIntermSwizzle *dPdyY = new TIntermSwizzle(dPdy->deepCopy(), {1});
    607        TIntermSwizzle *dPdyZ = new TIntermSwizzle(dPdy->deepCopy(), {2});
    608 
    609        TIntermBlock *calculateXUcVc = new TIntermBlock;
    610        calculateXUcVc->appendStatement(
    611            new TIntermBinary(EOpAssign, ma->deepCopy(), absX->deepCopy()));
    612        TransformXMajor(*mSymbolTable, calculateXUcVc, x, y, z, uc, vc);
    613 
    614        TIntermBlock *calculateYUcVc = new TIntermBlock;
    615        calculateYUcVc->appendStatement(
    616            new TIntermBinary(EOpAssign, ma->deepCopy(), absY->deepCopy()));
    617        TransformYMajor(*mSymbolTable, calculateYUcVc, x, y, z, uc, vc);
    618 
    619        TIntermBlock *calculateZUcVc = new TIntermBlock;
    620        calculateZUcVc->appendStatement(
    621            new TIntermBinary(EOpAssign, ma->deepCopy(), absZ->deepCopy()));
    622        TransformZMajor(*mSymbolTable, calculateZUcVc, x, y, z, uc, vc);
    623 
    624        // Compute derivatives.
    625        if (implicit)
    626        {
    627            TransformImplicitDerivativeXMajor(calculateXUcVc, dPDXdx, dUdx, dVdx);
    628            TransformImplicitDerivativeXMajor(calculateXUcVc, dPDXdy, dUdy, dVdy);
    629            TransformImplicitDerivativeYMajor(calculateYUcVc, dPDYdx, dUdx, dVdx);
    630            TransformImplicitDerivativeYMajor(calculateYUcVc, dPDYdy, dUdy, dVdy);
    631            TransformImplicitDerivativeZMajor(calculateZUcVc, dPDZdx, dUdx, dVdx);
    632            TransformImplicitDerivativeZMajor(calculateZUcVc, dPDZdy, dUdy, dVdy);
    633        }
    634        else
    635        {
    636            TransformDerivativeXMajor(calculateXUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ,
    637                                      dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 0));
    638            TransformDerivativeXMajor(calculateXUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ,
    639                                      dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 0));
    640            TransformDerivativeYMajor(calculateYUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ,
    641                                      dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 1));
    642            TransformDerivativeYMajor(calculateYUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ,
    643                                      dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 1));
    644            TransformDerivativeZMajor(calculateZUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ,
    645                                      dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 2));
    646            TransformDerivativeZMajor(calculateZUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ,
    647                                      dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 2));
    648        }
    649 
    650        // Create the if-else paths:
    651        TIntermIfElse *calculateYZUcVc =
    652            new TIntermIfElse(isYMajor, calculateYUcVc, calculateZUcVc);
    653        TIntermBlock *calculateYZUcVcBlock = new TIntermBlock;
    654        calculateYZUcVcBlock->appendStatement(calculateYZUcVc);
    655        TIntermIfElse *calculateXYZUcVc =
    656            new TIntermIfElse(isXMajor, calculateXUcVc, calculateYZUcVcBlock);
    657        body->appendStatement(calculateXYZUcVc);
    658 
    659        // u = (1 + uc/|ma|) / 2
    660        // v = (1 + vc/|ma|) / 2
    661        TIntermTyped *maTimesTwoRecip = new TIntermBinary(
    662            EOpAssign, ma->deepCopy(),
    663            new TIntermBinary(EOpDiv, CreateFloatNode(0.5f, EbpMedium), ma->deepCopy()));
    664        body->appendStatement(maTimesTwoRecip);
    665 
    666        TIntermTyped *ucDivMa = new TIntermBinary(EOpMul, uc, ma->deepCopy());
    667        TIntermTyped *vcDivMa = new TIntermBinary(EOpMul, vc, ma->deepCopy());
    668        TIntermTyped *uNormalized =
    669            new TIntermBinary(EOpAdd, CreateFloatNode(0.5f, EbpMedium), ucDivMa);
    670        TIntermTyped *vNormalized =
    671            new TIntermBinary(EOpAdd, CreateFloatNode(0.5f, EbpMedium), vcDivMa);
    672 
    673        body->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), uNormalized));
    674        body->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vNormalized));
    675 
    676        TIntermSequence argsDUVdx = {dUdx, dVdx};
    677        TIntermTyped *dUVdxValue  = TIntermAggregate::CreateConstructor(*vec2Type, &argsDUVdx);
    678 
    679        TIntermSequence argsDUVdy = {dUdy, dVdy};
    680        TIntermTyped *dUVdyValue  = TIntermAggregate::CreateConstructor(*vec2Type, &argsDUVdy);
    681 
    682        body->appendStatement(new TIntermBinary(EOpAssign, dUVdx, dUVdxValue));
    683        body->appendStatement(new TIntermBinary(EOpAssign, dUVdy, dUVdyValue));
    684 
    685        // return vec3(u, v, l)
    686        TIntermSequence argsUVL = {uc->deepCopy(), vc->deepCopy(), l};
    687        TIntermBranch *returnStatement =
    688            new TIntermBranch(EOpReturn, TIntermAggregate::CreateConstructor(*vec3Type, &argsUVL));
    689        body->appendStatement(returnStatement);
    690 
    691        TFunction *function;
    692        function = new TFunction(mSymbolTable, name, SymbolType::AngleInternal, vec3Type, true);
    693        function->addParameter(pVar);
    694        function->addParameter(dPdxVar);
    695        function->addParameter(dPdyVar);
    696        function->addParameter(dUVdxVar);
    697        function->addParameter(dUVdyVar);
    698 
    699        *functionOut = function;
    700 
    701        *declOut = CreateInternalFunctionDefinitionNode(*function, body);
    702    }
    703 
    704    TIntermTyped *createCoordTransformationCall(TIntermTyped *P,
    705                                                TIntermTyped *dPdx,
    706                                                TIntermTyped *dPdy,
    707                                                TIntermTyped *dUVdx,
    708                                                TIntermTyped *dUVdy)
    709    {
    710        TIntermSequence args = {P, dPdx, dPdy, dUVdx, dUVdy};
    711        return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVL, &args);
    712    }
    713 
    714    TIntermTyped *createImplicitCoordTransformationCall(TIntermTyped *P,
    715                                                        TIntermTyped *dUVdx,
    716                                                        TIntermTyped *dUVdy)
    717    {
    718        const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>();
    719        TIntermTyped *dPdx    = CreateZeroNode(*vec3Type);
    720        TIntermTyped *dPdy    = CreateZeroNode(*vec3Type);
    721        TIntermSequence args  = {P, dPdx, dPdy, dUVdx, dUVdy};
    722        return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVLImplicit, &args);
    723    }
    724 
    725    TIntermTyped *getMappedSamplerExpression(TIntermNode *samplerCubeExpression)
    726    {
    727        // The argument passed to a function can either be the sampler, if not array, or a subscript
    728        // into the sampler array.
    729        TIntermSymbol *asSymbol = samplerCubeExpression->getAsSymbolNode();
    730        TIntermBinary *asBinary = samplerCubeExpression->getAsBinaryNode();
    731 
    732        if (asBinary)
    733        {
    734            // Only constant indexing is supported in ES2.0.
    735            ASSERT(asBinary->getOp() == EOpIndexDirect);
    736            asSymbol = asBinary->getLeft()->getAsSymbolNode();
    737        }
    738 
    739        // Arrays of arrays are not available in ES2.0.
    740        ASSERT(asSymbol != nullptr);
    741        const TVariable *samplerCubeVar = &asSymbol->variable();
    742 
    743        ASSERT(mSamplerMap.find(samplerCubeVar) != mSamplerMap.end());
    744        const TVariable *mappedSamplerVar = mSamplerMap.at(samplerCubeVar);
    745 
    746        TIntermTyped *mappedExpression = new TIntermSymbol(mappedSamplerVar);
    747        if (asBinary)
    748        {
    749            mappedExpression =
    750                new TIntermBinary(asBinary->getOp(), mappedExpression, asBinary->getRight());
    751        }
    752 
    753        return mappedExpression;
    754    }
    755 
    756    bool convertBuiltinFunction(TIntermAggregate *node)
    757    {
    758        const TFunction *function = node->getFunction();
    759        if (!function->name().beginsWith("textureCube"))
    760        {
    761            return false;
    762        }
    763 
    764        // All textureCube* functions are in the form:
    765        //
    766        //     textureCube??(samplerCube, vec3, ??)
    767        //
    768        // They should be converted to:
    769        //
    770        //     texture??(sampler2DArray, convertCoords(vec3), ??)
    771        //
    772        // We assume the target platform supports texture() functions (currently only used in
    773        // Vulkan).
    774        //
    775        // The intrinsics map as follows:
    776        //
    777        //     textureCube -> textureGrad
    778        //     textureCubeLod -> textureLod
    779        //     textureCubeLodEXT -> textureLod
    780        //     textureCubeGrad -> textureGrad
    781        //     textureCubeGradEXT -> textureGrad
    782        //
    783        // Note that dPdx and dPdy in textureCubeGrad* are vec3, while the textureGrad equivalent
    784        // for sampler2DArray is vec2.  The EXT_shader_texture_lod that introduces thid function
    785        // says:
    786        //
    787        // > For the "Grad" functions, dPdx is the explicit derivative of P with respect
    788        // > to window x, and similarly dPdy with respect to window y. ...  For a cube map texture,
    789        // > dPdx and dPdy are vec3.
    790        // >
    791        // > Let
    792        // >
    793        // >     dSdx = dPdx.s;
    794        // >     dSdy = dPdy.s;
    795        // >     dTdx = dPdx.t;
    796        // >     dTdy = dPdy.t;
    797        // >
    798        // > and
    799        // >
    800        // >             / 0.0;    for two-dimensional texture
    801        // >     dRdx = (
    802        // >             \ dPdx.p; for cube map texture
    803        // >
    804        // >             / 0.0;    for two-dimensional texture
    805        // >     dRdy = (
    806        // >             \ dPdy.p; for cube map texture
    807        // >
    808        // > (See equation 3.12a in The OpenGL ES 2.0 Specification.)
    809        //
    810        // It's unclear to me what dRdx and dRdy are.  EXT_gpu_shader4 that promotes this function
    811        // has the following additional information:
    812        //
    813        // > For the "Cube" versions, the partial
    814        // > derivatives ddx and ddy are assumed to be in the coordinate system used
    815        // > before texture coordinates are projected onto the appropriate cube
    816        // > face. The partial derivatives of the post-projection texture coordinates,
    817        // > which are used for level-of-detail and anisotropic filtering
    818        // > calculations, are derived from coord, ddx and ddy in an
    819        // > implementation-dependent manner.
    820        //
    821        // The calculation of dPdx and dPdy is declared as implementation-dependent, so we have
    822        // freedom to calculate it as fit, even if not precisely the same as hardware might.
    823 
    824        const char *substituteFunctionName = "textureGrad";
    825        bool isGrad                        = false;
    826        bool isTranslatedGrad              = true;
    827        bool hasBias                       = false;
    828        if (function->name().beginsWith("textureCubeLod"))
    829        {
    830            substituteFunctionName = "textureLod";
    831            isTranslatedGrad       = false;
    832        }
    833        else if (function->name().beginsWith("textureCubeGrad"))
    834        {
    835            isGrad = true;
    836        }
    837        else if (!mIsFragmentShader)
    838        {
    839            substituteFunctionName = "texture";
    840            isTranslatedGrad       = false;
    841        }
    842 
    843        TIntermSequence *arguments = node->getSequence();
    844        ASSERT(arguments->size() >= 2);
    845 
    846        const TType *vec2Type = StaticType::GetBasic<EbtFloat, EbpHigh, 2>();
    847        const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>();
    848        TIntermSymbol *uvl    = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type));
    849        TIntermSymbol *dUVdx  = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type));
    850        TIntermSymbol *dUVdy  = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type));
    851 
    852        TIntermTyped *dPdx = nullptr;
    853        TIntermTyped *dPdy = nullptr;
    854        if (isGrad)
    855        {
    856            ASSERT(arguments->size() == 4);
    857            dPdx = (*arguments)[2]->getAsTyped()->deepCopy();
    858            dPdy = (*arguments)[3]->getAsTyped()->deepCopy();
    859        }
    860        else if (isTranslatedGrad && mIsFragmentShader && arguments->size() == 3)
    861        {
    862            hasBias = true;
    863        }
    864        else
    865        {
    866            dPdx = CreateZeroNode(*vec3Type);
    867            dPdy = CreateZeroNode(*vec3Type);
    868        }
    869 
    870        if (isTranslatedGrad && !mIsFragmentShader)
    871        {
    872            substituteFunctionName = "texture";
    873            isTranslatedGrad       = false;
    874        }
    875 
    876        // The function call to transform the coordinates, dPdx and dPdy.  If not textureCubeGrad,
    877        // the driver compiler will optimize out the unnecessary calculations.
    878        TIntermSequence coordTransform;
    879        coordTransform.push_back(CreateTempDeclarationNode(&dUVdx->variable()));
    880        coordTransform.push_back(CreateTempDeclarationNode(&dUVdy->variable()));
    881        TIntermTyped *coordTransformCall;
    882        if (isGrad || !isTranslatedGrad)
    883        {
    884            coordTransformCall = createCoordTransformationCall(
    885                (*arguments)[1]->getAsTyped()->deepCopy(), dPdx, dPdy, dUVdx, dUVdy);
    886        }
    887        else
    888        {
    889            coordTransformCall = createImplicitCoordTransformationCall(
    890                (*arguments)[1]->getAsTyped()->deepCopy(), dUVdx, dUVdy);
    891        }
    892        coordTransform.push_back(
    893            CreateTempInitDeclarationNode(&uvl->variable(), coordTransformCall));
    894 
    895        TIntermTyped *dUVdxArg = dUVdx;
    896        TIntermTyped *dUVdyArg = dUVdy;
    897        if (hasBias)
    898        {
    899            const TType *floatType   = StaticType::GetBasic<EbtFloat, EbpHigh>();
    900            TIntermTyped *bias       = (*arguments)[2]->getAsTyped()->deepCopy();
    901            TIntermSequence exp2Args = {bias};
    902            TIntermTyped *exp2Call =
    903                CreateBuiltInFunctionCallNode("exp2", &exp2Args, *mSymbolTable, 100);
    904            TIntermSymbol *biasFac = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType));
    905            coordTransform.push_back(CreateTempInitDeclarationNode(&biasFac->variable(), exp2Call));
    906            dUVdxArg =
    907                new TIntermBinary(EOpVectorTimesScalar, biasFac->deepCopy(), dUVdx->deepCopy());
    908            dUVdyArg =
    909                new TIntermBinary(EOpVectorTimesScalar, biasFac->deepCopy(), dUVdy->deepCopy());
    910        }
    911 
    912        insertStatementsInParentBlock(coordTransform);
    913 
    914        TIntermSequence substituteArguments;
    915        // Replace the first argument (samplerCube) with the sampler2DArray.
    916        substituteArguments.push_back(getMappedSamplerExpression((*arguments)[0]));
    917        // Replace the second argument with the coordination transformation.
    918        substituteArguments.push_back(uvl->deepCopy());
    919        if (isTranslatedGrad)
    920        {
    921            substituteArguments.push_back(dUVdxArg->deepCopy());
    922            substituteArguments.push_back(dUVdyArg->deepCopy());
    923        }
    924        else
    925        {
    926            // Pass the rest of the parameters as is.
    927            for (size_t argIndex = 2; argIndex < arguments->size(); ++argIndex)
    928            {
    929                substituteArguments.push_back((*arguments)[argIndex]->getAsTyped()->deepCopy());
    930            }
    931        }
    932 
    933        TIntermTyped *substituteCall = CreateBuiltInFunctionCallNode(
    934            substituteFunctionName, &substituteArguments, *mSymbolTable, 300);
    935 
    936        queueReplacement(substituteCall, OriginalNode::IS_DROPPED);
    937 
    938        return true;
    939    }
    940 
    941    // A map from the samplerCube variable to the sampler2DArray one.
    942    angle::HashMap<const TVariable *, const TVariable *> mSamplerMap;
    943 
    944    // A helper function to convert xyz coordinates passed to a cube map sampling function into the
    945    // array layer (cube map face) and uv coordinates.
    946    TFunction *mCubeXYZToArrayUVL;
    947    // A specialized version of the same function which uses implicit derivatives.
    948    TFunction *mCubeXYZToArrayUVLImplicit;
    949 
    950    bool mIsFragmentShader;
    951 
    952    // Stored to be put before the first function after the pass.
    953    TIntermFunctionDefinition *mCoordTranslationFunctionDecl;
    954    TIntermFunctionDefinition *mCoordTranslationFunctionImplicitDecl;
    955 };
    956 }  // anonymous namespace
    957 
    958 bool RewriteCubeMapSamplersAs2DArray(TCompiler *compiler,
    959                                     TIntermBlock *root,
    960                                     TSymbolTable *symbolTable,
    961                                     bool isFragmentShader)
    962 {
    963    RewriteCubeMapSamplersAs2DArrayTraverser traverser(symbolTable, isFragmentShader);
    964    root->traverse(&traverser);
    965 
    966    TIntermFunctionDefinition *coordTranslationFunctionDecl =
    967        traverser.getCoordTranslationFunctionDecl();
    968    TIntermFunctionDefinition *coordTranslationFunctionDeclImplicit =
    969        traverser.getCoordTranslationFunctionDeclImplicit();
    970    size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root);
    971    if (coordTranslationFunctionDecl)
    972    {
    973        root->insertChildNodes(firstFunctionIndex, TIntermSequence({coordTranslationFunctionDecl}));
    974    }
    975    if (coordTranslationFunctionDeclImplicit)
    976    {
    977        root->insertChildNodes(firstFunctionIndex,
    978                               TIntermSequence({coordTranslationFunctionDeclImplicit}));
    979    }
    980 
    981    return traverser.updateTree(compiler, root);
    982 }
    983 
    984 }  // namespace sh