diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp b/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp new file mode 100644 index 0000000000..f7e5814361 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.cpp @@ -0,0 +1,984 @@ +// +// Copyright 2019 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// RewriteCubeMapSamplersAs2DArray: Change samplerCube samplers to sampler2DArray for seamful cube +// map emulation. +// +// Relies on MonomorphizeUnsupportedFunctions to ensure samplerCube variables are not +// passed to functions (for simplicity). +// + +#include "compiler/translator/tree_ops/RewriteCubeMapSamplersAs2DArray.h" + +#include "compiler/translator/Compiler.h" +#include "compiler/translator/ImmutableStringBuilder.h" +#include "compiler/translator/StaticType.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/tree_util/IntermNode_util.h" +#include "compiler/translator/tree_util/IntermTraverse.h" +#include "compiler/translator/tree_util/ReplaceVariable.h" + +namespace sh +{ +namespace +{ +constexpr ImmutableString kCoordTransformFuncName("ANGLECubeMapCoordTransform"); +constexpr ImmutableString kCoordTransformFuncNameImplicit("ANGLECubeMapCoordTransformImplicit"); + +TIntermTyped *DerivativeQuotient(TIntermTyped *u, + TIntermTyped *du, + TIntermTyped *v, + TIntermTyped *dv, + TIntermTyped *vRecip) +{ + // (du v - dv u) / v^2 + return new TIntermBinary( + EOpMul, + new TIntermBinary(EOpSub, new TIntermBinary(EOpMul, du->deepCopy(), v->deepCopy()), + new TIntermBinary(EOpMul, dv->deepCopy(), u->deepCopy())), + new TIntermBinary(EOpMul, vRecip->deepCopy(), vRecip->deepCopy())); +} + +TIntermTyped *Swizzle1(TIntermTyped *array, int i) +{ + return new TIntermSwizzle(array, {i}); +} + +TIntermTyped *IndexDirect(TIntermTyped *array, int i) +{ + return new TIntermBinary(EOpIndexDirect, array, CreateIndexNode(i)); +} + +// Generated the common transformation in each coord transformation case. See comment in +// declareCoordTranslationFunction(). Called with P, dPdx and dPdy. +void TransformXMajor(const TSymbolTable &symbolTable, + TIntermBlock *block, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *uc, + TIntermTyped *vc) +{ + // uc = -sign(x)*z + // vc = -y + TIntermTyped *signX = + CreateBuiltInUnaryFunctionCallNode("sign", x->deepCopy(), symbolTable, 100); + + TIntermTyped *ucValue = + new TIntermUnary(EOpNegative, new TIntermBinary(EOpMul, signX, z->deepCopy()), nullptr); + TIntermTyped *vcValue = new TIntermUnary(EOpNegative, y->deepCopy(), nullptr); + + block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue)); + block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue)); +} + +void TransformDerivativeXMajor(TIntermBlock *block, + TSymbolTable *symbolTable, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *dx, + TIntermTyped *dy, + TIntermTyped *dz, + TIntermTyped *du, + TIntermTyped *dv, + TIntermTyped *xRecip) +{ + // Only the magnitude of the derivative matters, so we ignore the sign(x) + // and the negations. + TIntermTyped *duValue = DerivativeQuotient(z, dz, x, dx, xRecip); + TIntermTyped *dvValue = DerivativeQuotient(y, dy, x, dx, xRecip); + duValue = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium)); + dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium)); + block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue)); + block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue)); +} + +void TransformImplicitDerivativeXMajor(TIntermBlock *block, + TIntermTyped *dOuter, + TIntermTyped *du, + TIntermTyped *dv) +{ + block->appendStatement( + new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 2))); + block->appendStatement( + new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 1))); +} + +void TransformYMajor(const TSymbolTable &symbolTable, + TIntermBlock *block, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *uc, + TIntermTyped *vc) +{ + // uc = x + // vc = sign(y)*z + TIntermTyped *signY = + CreateBuiltInUnaryFunctionCallNode("sign", y->deepCopy(), symbolTable, 100); + + TIntermTyped *ucValue = x->deepCopy(); + TIntermTyped *vcValue = new TIntermBinary(EOpMul, signY, z->deepCopy()); + + block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue)); + block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue)); +} + +void TransformDerivativeYMajor(TIntermBlock *block, + TSymbolTable *symbolTable, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *dx, + TIntermTyped *dy, + TIntermTyped *dz, + TIntermTyped *du, + TIntermTyped *dv, + TIntermTyped *yRecip) +{ + // Only the magnitude of the derivative matters, so we ignore the sign(x) + // and the negations. + TIntermTyped *duValue = DerivativeQuotient(x, dx, y, dy, yRecip); + TIntermTyped *dvValue = DerivativeQuotient(z, dz, y, dy, yRecip); + duValue = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium)); + dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium)); + block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue)); + block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue)); +} + +void TransformImplicitDerivativeYMajor(TIntermBlock *block, + TIntermTyped *dOuter, + TIntermTyped *du, + TIntermTyped *dv) +{ + block->appendStatement( + new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 0))); + block->appendStatement( + new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 2))); +} + +void TransformZMajor(const TSymbolTable &symbolTable, + TIntermBlock *block, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *uc, + TIntermTyped *vc) +{ + // uc = size(z)*x + // vc = -y + TIntermTyped *signZ = + CreateBuiltInUnaryFunctionCallNode("sign", z->deepCopy(), symbolTable, 100); + + TIntermTyped *ucValue = new TIntermBinary(EOpMul, signZ, x->deepCopy()); + TIntermTyped *vcValue = new TIntermUnary(EOpNegative, y->deepCopy(), nullptr); + + block->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), ucValue)); + block->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vcValue)); +} + +void TransformDerivativeZMajor(TIntermBlock *block, + TSymbolTable *symbolTable, + TIntermTyped *x, + TIntermTyped *y, + TIntermTyped *z, + TIntermTyped *dx, + TIntermTyped *dy, + TIntermTyped *dz, + TIntermTyped *du, + TIntermTyped *dv, + TIntermTyped *zRecip) +{ + // Only the magnitude of the derivative matters, so we ignore the sign(x) + // and the negations. + TIntermTyped *duValue = DerivativeQuotient(x, dx, z, dz, zRecip); + TIntermTyped *dvValue = DerivativeQuotient(y, dy, z, dz, zRecip); + duValue = new TIntermBinary(EOpMul, duValue, CreateFloatNode(0.5f, EbpMedium)); + dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f, EbpMedium)); + block->appendStatement(new TIntermBinary(EOpAssign, du->deepCopy(), duValue)); + block->appendStatement(new TIntermBinary(EOpAssign, dv->deepCopy(), dvValue)); +} + +void TransformImplicitDerivativeZMajor(TIntermBlock *block, + TIntermTyped *dOuter, + TIntermTyped *du, + TIntermTyped *dv) +{ + block->appendStatement( + new TIntermBinary(EOpAssign, du->deepCopy(), Swizzle1(dOuter->deepCopy(), 0))); + block->appendStatement( + new TIntermBinary(EOpAssign, dv->deepCopy(), Swizzle1(dOuter->deepCopy(), 1))); +} + +class RewriteCubeMapSamplersAs2DArrayTraverser : public TIntermTraverser +{ + public: + RewriteCubeMapSamplersAs2DArrayTraverser(TSymbolTable *symbolTable, bool isFragmentShader) + : TIntermTraverser(true, false, false, symbolTable), + mCubeXYZToArrayUVL(nullptr), + mCubeXYZToArrayUVLImplicit(nullptr), + mIsFragmentShader(isFragmentShader), + mCoordTranslationFunctionDecl(nullptr), + mCoordTranslationFunctionImplicitDecl(nullptr) + {} + + bool visitDeclaration(Visit visit, TIntermDeclaration *node) override + { + const TIntermSequence &sequence = *(node->getSequence()); + + TIntermTyped *variable = sequence.front()->getAsTyped(); + const TType &type = variable->getType(); + bool isSamplerCube = type.getQualifier() == EvqUniform && type.isSamplerCube(); + + if (isSamplerCube) + { + // Samplers cannot have initializers, so the declaration must necessarily be a symbol. + TIntermSymbol *samplerVariable = variable->getAsSymbolNode(); + ASSERT(samplerVariable != nullptr); + + declareSampler2DArray(&samplerVariable->variable(), node); + return false; + } + + return true; + } + + bool visitAggregate(Visit visit, TIntermAggregate *node) override + { + if (BuiltInGroup::IsBuiltIn(node->getOp())) + { + bool converted = convertBuiltinFunction(node); + return !converted; + } + + // AST functions don't require modification as samplerCube function parameters are removed + // by MonomorphizeUnsupportedFunctions. + return true; + } + + TIntermFunctionDefinition *getCoordTranslationFunctionDecl() + { + return mCoordTranslationFunctionDecl; + } + + TIntermFunctionDefinition *getCoordTranslationFunctionDeclImplicit() + { + return mCoordTranslationFunctionImplicitDecl; + } + + private: + void declareSampler2DArray(const TVariable *samplerCubeVar, TIntermDeclaration *node) + { + if (mCubeXYZToArrayUVL == nullptr) + { + // If not done yet, declare the function that transforms cube map texture sampling + // coordinates to face index and uv coordinates. + declareCoordTranslationFunction(false, kCoordTransformFuncName, &mCubeXYZToArrayUVL, + &mCoordTranslationFunctionDecl); + } + if (mCubeXYZToArrayUVLImplicit == nullptr && mIsFragmentShader) + { + declareCoordTranslationFunction(true, kCoordTransformFuncNameImplicit, + &mCubeXYZToArrayUVLImplicit, + &mCoordTranslationFunctionImplicitDecl); + } + + TType *newType = new TType(samplerCubeVar->getType()); + newType->setBasicType(EbtSampler2DArray); + + TVariable *sampler2DArrayVar = new TVariable(mSymbolTable, samplerCubeVar->name(), newType, + samplerCubeVar->symbolType()); + + TIntermDeclaration *sampler2DArrayDecl = new TIntermDeclaration(); + sampler2DArrayDecl->appendDeclarator(new TIntermSymbol(sampler2DArrayVar)); + + queueReplacement(sampler2DArrayDecl, OriginalNode::IS_DROPPED); + + // Remember the sampler2DArray variable. + mSamplerMap[samplerCubeVar] = sampler2DArrayVar; + } + + void declareCoordTranslationFunction(bool implicit, + const ImmutableString &name, + TFunction **functionOut, + TIntermFunctionDefinition **declOut) + { + // GLES2.0 (as well as desktop OpenGL 2.0) define the coordination transformation as + // follows. Given xyz cube coordinates, where each channel is in [-1, 1], the following + // table calculates uc, vc and ma as well as the cube map face. + // + // Major Axis Direction Target uc vc ma + // +x TEXTURE_CUBE_MAP_POSITIVE_X -z -y |x| + // -x TEXTURE_CUBE_MAP_NEGATIVE_X z -y |x| + // +y TEXTURE_CUBE_MAP_POSITIVE_Y x z |y| + // -y TEXTURE_CUBE_MAP_NEGATIVE_Y x -z |y| + // +z TEXTURE_CUBE_MAP_POSITIVE_Z x -y |z| + // -z TEXTURE_CUBE_MAP_NEGATIVE_Z -x -y |z| + // + // "Major" is an indication of the axis with the largest value. The cube map face indicates + // the layer to sample from. The uv coordinates to sample from are calculated as, + // effectively transforming the uv values to [0, 1]: + // + // u = (1 + uc/ma) / 2 + // v = (1 + vc/ma) / 2 + // + // The function can be implemented as 6 ifs, though it would be far from efficient. The + // following calculations implement the table above in a smaller number of instructions. + // + // First, ma can be calculated as the max of the three axes. + // + // ma = max3(|x|, |y|, |z|) + // + // We have three cases: + // + // ma == |x|: uc = -sign(x)*z + // vc = -y + // layer = float(x < 0) + // + // ma == |y|: uc = x + // vc = sign(y)*z + // layer = 2 + float(y < 0) + // + // ma == |z|: uc = size(z)*x + // vc = -y + // layer = 4 + float(z < 0) + // + // This can be implemented with a number of ?: instructions or 3 ifs. ?: would require all + // expressions to be evaluated (vector ALU) while if would require exec mask and jumps + // (scalar operations). We implement this using ifs as there would otherwise be many vector + // operations and not much of anything else. + // + // If textureCubeGrad is used, we also need to transform the provided dPdx and dPdy (both + // vec3) to a dUVdx and dUVdy. Assume P=(r,s,t) and we are investigating dx (note the + // change from xyz to rst to not confuse with dx and dy): + // + // uv = (f(r,s,t)/ma + 1)/2 + // + // Where f is one of the transformations above for uc and vc. Between two neighbors along + // the x axis, we have P0=(r0,s0,t0) and P1=(r1,s1,t1) + // + // dP = (r1-r0, s1-s0, t1-t0) + // dUV = (f(r1,s1,t1)/ma1 - g(r0,s0,t0)/ma0) / 2 + // + // f and g may not necessarily be the same because the two points may have different major + // axes. Even with the same major access, the sign that's used in the formulas may not be + // the same. Furthermore, ma0 and ma1 may not be the same. This makes it impossible to + // derive dUV from dP exactly. + // + // However, gradient transformation is implementation dependant, so we will simplify and + // assume all the above complications are non-existent. We therefore have: + // + // dUV = (f(r1,s1,t1)/ma0 - f(r0,s0,t0)/ma0)/2 + // + // Given that we assumed the sign functions are returning identical results for the two + // points, f becomes a linear transformation. Thus: + // + // dUV = f(r1-r0,s1-0,t1-t0)/ma0/2 + // + // In other words, we use the same formulae that transform XYZ (RST here) to UV to + // transform the derivatives. + // + // ma == |x|: dUdx = -sign(x)*dPdx.z / ma / 2 + // dVdx = -dPdx.y / ma / 2 + // + // ma == |y|: dUdx = dPdx.x / ma / 2 + // dVdx = sign(y)*dPdx.z / ma / 2 + // + // ma == |z|: dUdx = size(z)*dPdx.x / ma / 2 + // dVdx = -dPdx.y / ma / 2 + // + // Similarly for dy. + + // Create the function parameters: vec3 P, vec3 dPdx, vec3 dPdy, + // out vec2 dUVdx, out vec2 dUVdy + const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>(); + TType *inVec3Type = new TType(*vec3Type); + inVec3Type->setQualifier(EvqParamIn); + + TVariable *pVar = new TVariable(mSymbolTable, ImmutableString("P"), inVec3Type, + SymbolType::AngleInternal); + TVariable *dPdxVar = new TVariable(mSymbolTable, ImmutableString("dPdx"), inVec3Type, + SymbolType::AngleInternal); + TVariable *dPdyVar = new TVariable(mSymbolTable, ImmutableString("dPdy"), inVec3Type, + SymbolType::AngleInternal); + + const TType *vec2Type = StaticType::GetBasic<EbtFloat, EbpHigh, 2>(); + TType *outVec2Type = new TType(*vec2Type); + outVec2Type->setQualifier(EvqParamOut); + + TVariable *dUVdxVar = new TVariable(mSymbolTable, ImmutableString("dUVdx"), outVec2Type, + SymbolType::AngleInternal); + TVariable *dUVdyVar = new TVariable(mSymbolTable, ImmutableString("dUVdy"), outVec2Type, + SymbolType::AngleInternal); + + TIntermSymbol *p = new TIntermSymbol(pVar); + TIntermSymbol *dPdx = new TIntermSymbol(dPdxVar); + TIntermSymbol *dPdy = new TIntermSymbol(dPdyVar); + TIntermSymbol *dUVdx = new TIntermSymbol(dUVdxVar); + TIntermSymbol *dUVdy = new TIntermSymbol(dUVdyVar); + + // Create the function body as statements are generated. + TIntermBlock *body = new TIntermBlock; + + // Create the swizzle nodes that will be used in multiple expressions: + TIntermSwizzle *x = new TIntermSwizzle(p->deepCopy(), {0}); + TIntermSwizzle *y = new TIntermSwizzle(p->deepCopy(), {1}); + TIntermSwizzle *z = new TIntermSwizzle(p->deepCopy(), {2}); + + // Create abs and "< 0" expressions from the channels. + const TType *floatType = StaticType::GetBasic<EbtFloat, EbpHigh>(); + + TIntermTyped *isNegX = new TIntermBinary(EOpLessThan, x, CreateZeroNode(*floatType)); + TIntermTyped *isNegY = new TIntermBinary(EOpLessThan, y, CreateZeroNode(*floatType)); + TIntermTyped *isNegZ = new TIntermBinary(EOpLessThan, z, CreateZeroNode(*floatType)); + + TIntermSymbol *absX = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *absY = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *absZ = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + + TIntermDeclaration *absXDecl = CreateTempInitDeclarationNode( + &absX->variable(), + CreateBuiltInUnaryFunctionCallNode("abs", x->deepCopy(), *mSymbolTable, 100)); + TIntermDeclaration *absYDecl = CreateTempInitDeclarationNode( + &absY->variable(), + CreateBuiltInUnaryFunctionCallNode("abs", y->deepCopy(), *mSymbolTable, 100)); + TIntermDeclaration *absZDecl = CreateTempInitDeclarationNode( + &absZ->variable(), + CreateBuiltInUnaryFunctionCallNode("abs", z->deepCopy(), *mSymbolTable, 100)); + + body->appendStatement(absXDecl); + body->appendStatement(absYDecl); + body->appendStatement(absZDecl); + + // Create temporary variable for division outer product matrix and its + // derivatives. + // recipOuter[i][j] = 0.5 * P[j] / P[i] + const TType *mat3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3, 3>(); + TIntermSymbol *recipOuter = new TIntermSymbol(CreateTempVariable(mSymbolTable, mat3Type)); + + TIntermTyped *pRecip = + new TIntermBinary(EOpDiv, CreateFloatNode(1.0, EbpMedium), p->deepCopy()); + TIntermSymbol *pRecipVar = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + + body->appendStatement(CreateTempInitDeclarationNode(&pRecipVar->variable(), pRecip)); + + TIntermSequence args = { + p->deepCopy(), new TIntermBinary(EOpVectorTimesScalar, CreateFloatNode(0.5, EbpMedium), + pRecipVar->deepCopy())}; + TIntermDeclaration *recipOuterDecl = CreateTempInitDeclarationNode( + &recipOuter->variable(), + CreateBuiltInFunctionCallNode("outerProduct", &args, *mSymbolTable, 300)); + body->appendStatement(recipOuterDecl); + + TIntermSymbol *dPDXdx = nullptr; + TIntermSymbol *dPDYdx = nullptr; + TIntermSymbol *dPDZdx = nullptr; + TIntermSymbol *dPDXdy = nullptr; + TIntermSymbol *dPDYdy = nullptr; + TIntermSymbol *dPDZdy = nullptr; + if (implicit) + { + dPDXdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + dPDYdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + dPDZdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + dPDXdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + dPDYdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + dPDZdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + + TIntermDeclaration *dPDXdxDecl = CreateTempInitDeclarationNode( + &dPDXdx->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 0)->deepCopy(), + *mSymbolTable, 300)); + TIntermDeclaration *dPDYdxDecl = CreateTempInitDeclarationNode( + &dPDYdx->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 1)->deepCopy(), + *mSymbolTable, 300)); + TIntermDeclaration *dPDZdxDecl = CreateTempInitDeclarationNode( + &dPDZdx->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdx", IndexDirect(recipOuter, 2)->deepCopy(), + *mSymbolTable, 300)); + TIntermDeclaration *dPDXdyDecl = CreateTempInitDeclarationNode( + &dPDXdy->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 0)->deepCopy(), + *mSymbolTable, 300)); + TIntermDeclaration *dPDYdyDecl = CreateTempInitDeclarationNode( + &dPDYdy->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 1)->deepCopy(), + *mSymbolTable, 300)); + TIntermDeclaration *dPDZdyDecl = CreateTempInitDeclarationNode( + &dPDZdy->variable(), + CreateBuiltInUnaryFunctionCallNode("dFdy", IndexDirect(recipOuter, 2)->deepCopy(), + *mSymbolTable, 300)); + + body->appendStatement(dPDXdxDecl); + body->appendStatement(dPDYdxDecl); + body->appendStatement(dPDZdxDecl); + body->appendStatement(dPDXdyDecl); + body->appendStatement(dPDYdyDecl); + body->appendStatement(dPDZdyDecl); + } + + // Create temporary variables for ma, uc, vc, and l (layer), as well as dUdx, dVdx, dUdy + // and dVdy. + TIntermSymbol *ma = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *l = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *uc = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *vc = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *dUdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *dVdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *dUdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + TIntermSymbol *dVdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + + body->appendStatement(CreateTempDeclarationNode(&ma->variable())); + body->appendStatement(CreateTempDeclarationNode(&l->variable())); + body->appendStatement(CreateTempDeclarationNode(&uc->variable())); + body->appendStatement(CreateTempDeclarationNode(&vc->variable())); + body->appendStatement(CreateTempDeclarationNode(&dUdx->variable())); + body->appendStatement(CreateTempDeclarationNode(&dVdx->variable())); + body->appendStatement(CreateTempDeclarationNode(&dUdy->variable())); + body->appendStatement(CreateTempDeclarationNode(&dVdy->variable())); + + // ma = max(|x|, max(|y|, |z|)) + TIntermSequence argsMaxYZ = {absY->deepCopy(), absZ->deepCopy()}; + TIntermTyped *maxYZ = CreateBuiltInFunctionCallNode("max", &argsMaxYZ, *mSymbolTable, 100); + TIntermSequence argsMaxValue = {absX->deepCopy(), maxYZ}; + TIntermTyped *maValue = + CreateBuiltInFunctionCallNode("max", &argsMaxValue, *mSymbolTable, 100); + body->appendStatement(new TIntermBinary(EOpAssign, ma, maValue)); + + // ma == |x| and ma == |y| expressions + TIntermTyped *isXMajor = new TIntermBinary(EOpEqual, ma->deepCopy(), absX->deepCopy()); + TIntermTyped *isYMajor = new TIntermBinary(EOpEqual, ma->deepCopy(), absY->deepCopy()); + + // Determine the cube face: + + // The case where x is major: + // layer = float(x < 0) + TIntermSequence argsNegX = {isNegX}; + TIntermTyped *xl = TIntermAggregate::CreateConstructor(*floatType, &argsNegX); + + TIntermBlock *calculateXL = new TIntermBlock; + calculateXL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), xl)); + + // The case where y is major: + // layer = 2 + float(y < 0) + TIntermSequence argsNegY = {isNegY}; + TIntermTyped *yl = + new TIntermBinary(EOpAdd, CreateFloatNode(2.0f, EbpMedium), + TIntermAggregate::CreateConstructor(*floatType, &argsNegY)); + + TIntermBlock *calculateYL = new TIntermBlock; + calculateYL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), yl)); + + // The case where z is major: + // layer = 4 + float(z < 0) + TIntermSequence argsNegZ = {isNegZ}; + TIntermTyped *zl = + new TIntermBinary(EOpAdd, CreateFloatNode(4.0f, EbpMedium), + TIntermAggregate::CreateConstructor(*floatType, &argsNegZ)); + + TIntermBlock *calculateZL = new TIntermBlock; + calculateZL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), zl)); + + // Create the if-else paths: + TIntermIfElse *calculateYZL = new TIntermIfElse(isYMajor, calculateYL, calculateZL); + TIntermBlock *calculateYZLBlock = new TIntermBlock; + calculateYZLBlock->appendStatement(calculateYZL); + TIntermIfElse *calculateXYZL = new TIntermIfElse(isXMajor, calculateXL, calculateYZLBlock); + body->appendStatement(calculateXYZL); + + // layer < 1.5 (covering faces 0 and 1, corresponding to major axis being X) and layer < 3.5 + // (covering faces 2 and 3, corresponding to major axis being Y). Used to determine which + // of the three transformations to apply. Previously, ma == |X| and ma == |Y| was used, + // which is no longer correct for helper invocations. The value of ma is updated in each + // case for these invocations. + isXMajor = new TIntermBinary(EOpLessThan, l->deepCopy(), CreateFloatNode(1.5f, EbpMedium)); + isYMajor = new TIntermBinary(EOpLessThan, l->deepCopy(), CreateFloatNode(3.5f, EbpMedium)); + + TIntermSwizzle *dPdxX = new TIntermSwizzle(dPdx->deepCopy(), {0}); + TIntermSwizzle *dPdxY = new TIntermSwizzle(dPdx->deepCopy(), {1}); + TIntermSwizzle *dPdxZ = new TIntermSwizzle(dPdx->deepCopy(), {2}); + + TIntermSwizzle *dPdyX = new TIntermSwizzle(dPdy->deepCopy(), {0}); + TIntermSwizzle *dPdyY = new TIntermSwizzle(dPdy->deepCopy(), {1}); + TIntermSwizzle *dPdyZ = new TIntermSwizzle(dPdy->deepCopy(), {2}); + + TIntermBlock *calculateXUcVc = new TIntermBlock; + calculateXUcVc->appendStatement( + new TIntermBinary(EOpAssign, ma->deepCopy(), absX->deepCopy())); + TransformXMajor(*mSymbolTable, calculateXUcVc, x, y, z, uc, vc); + + TIntermBlock *calculateYUcVc = new TIntermBlock; + calculateYUcVc->appendStatement( + new TIntermBinary(EOpAssign, ma->deepCopy(), absY->deepCopy())); + TransformYMajor(*mSymbolTable, calculateYUcVc, x, y, z, uc, vc); + + TIntermBlock *calculateZUcVc = new TIntermBlock; + calculateZUcVc->appendStatement( + new TIntermBinary(EOpAssign, ma->deepCopy(), absZ->deepCopy())); + TransformZMajor(*mSymbolTable, calculateZUcVc, x, y, z, uc, vc); + + // Compute derivatives. + if (implicit) + { + TransformImplicitDerivativeXMajor(calculateXUcVc, dPDXdx, dUdx, dVdx); + TransformImplicitDerivativeXMajor(calculateXUcVc, dPDXdy, dUdy, dVdy); + TransformImplicitDerivativeYMajor(calculateYUcVc, dPDYdx, dUdx, dVdx); + TransformImplicitDerivativeYMajor(calculateYUcVc, dPDYdy, dUdy, dVdy); + TransformImplicitDerivativeZMajor(calculateZUcVc, dPDZdx, dUdx, dVdx); + TransformImplicitDerivativeZMajor(calculateZUcVc, dPDZdy, dUdy, dVdy); + } + else + { + TransformDerivativeXMajor(calculateXUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ, + dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 0)); + TransformDerivativeXMajor(calculateXUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ, + dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 0)); + TransformDerivativeYMajor(calculateYUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ, + dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 1)); + TransformDerivativeYMajor(calculateYUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ, + dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 1)); + TransformDerivativeZMajor(calculateZUcVc, mSymbolTable, x, y, z, dPdxX, dPdxY, dPdxZ, + dUdx, dVdx, Swizzle1(pRecipVar->deepCopy(), 2)); + TransformDerivativeZMajor(calculateZUcVc, mSymbolTable, x, y, z, dPdyX, dPdyY, dPdyZ, + dUdy, dVdy, Swizzle1(pRecipVar->deepCopy(), 2)); + } + + // Create the if-else paths: + TIntermIfElse *calculateYZUcVc = + new TIntermIfElse(isYMajor, calculateYUcVc, calculateZUcVc); + TIntermBlock *calculateYZUcVcBlock = new TIntermBlock; + calculateYZUcVcBlock->appendStatement(calculateYZUcVc); + TIntermIfElse *calculateXYZUcVc = + new TIntermIfElse(isXMajor, calculateXUcVc, calculateYZUcVcBlock); + body->appendStatement(calculateXYZUcVc); + + // u = (1 + uc/|ma|) / 2 + // v = (1 + vc/|ma|) / 2 + TIntermTyped *maTimesTwoRecip = new TIntermBinary( + EOpAssign, ma->deepCopy(), + new TIntermBinary(EOpDiv, CreateFloatNode(0.5f, EbpMedium), ma->deepCopy())); + body->appendStatement(maTimesTwoRecip); + + TIntermTyped *ucDivMa = new TIntermBinary(EOpMul, uc, ma->deepCopy()); + TIntermTyped *vcDivMa = new TIntermBinary(EOpMul, vc, ma->deepCopy()); + TIntermTyped *uNormalized = + new TIntermBinary(EOpAdd, CreateFloatNode(0.5f, EbpMedium), ucDivMa); + TIntermTyped *vNormalized = + new TIntermBinary(EOpAdd, CreateFloatNode(0.5f, EbpMedium), vcDivMa); + + body->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), uNormalized)); + body->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vNormalized)); + + TIntermSequence argsDUVdx = {dUdx, dVdx}; + TIntermTyped *dUVdxValue = TIntermAggregate::CreateConstructor(*vec2Type, &argsDUVdx); + + TIntermSequence argsDUVdy = {dUdy, dVdy}; + TIntermTyped *dUVdyValue = TIntermAggregate::CreateConstructor(*vec2Type, &argsDUVdy); + + body->appendStatement(new TIntermBinary(EOpAssign, dUVdx, dUVdxValue)); + body->appendStatement(new TIntermBinary(EOpAssign, dUVdy, dUVdyValue)); + + // return vec3(u, v, l) + TIntermSequence argsUVL = {uc->deepCopy(), vc->deepCopy(), l}; + TIntermBranch *returnStatement = + new TIntermBranch(EOpReturn, TIntermAggregate::CreateConstructor(*vec3Type, &argsUVL)); + body->appendStatement(returnStatement); + + TFunction *function; + function = new TFunction(mSymbolTable, name, SymbolType::AngleInternal, vec3Type, true); + function->addParameter(pVar); + function->addParameter(dPdxVar); + function->addParameter(dPdyVar); + function->addParameter(dUVdxVar); + function->addParameter(dUVdyVar); + + *functionOut = function; + + *declOut = CreateInternalFunctionDefinitionNode(*function, body); + } + + TIntermTyped *createCoordTransformationCall(TIntermTyped *P, + TIntermTyped *dPdx, + TIntermTyped *dPdy, + TIntermTyped *dUVdx, + TIntermTyped *dUVdy) + { + TIntermSequence args = {P, dPdx, dPdy, dUVdx, dUVdy}; + return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVL, &args); + } + + TIntermTyped *createImplicitCoordTransformationCall(TIntermTyped *P, + TIntermTyped *dUVdx, + TIntermTyped *dUVdy) + { + const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>(); + TIntermTyped *dPdx = CreateZeroNode(*vec3Type); + TIntermTyped *dPdy = CreateZeroNode(*vec3Type); + TIntermSequence args = {P, dPdx, dPdy, dUVdx, dUVdy}; + return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVLImplicit, &args); + } + + TIntermTyped *getMappedSamplerExpression(TIntermNode *samplerCubeExpression) + { + // The argument passed to a function can either be the sampler, if not array, or a subscript + // into the sampler array. + TIntermSymbol *asSymbol = samplerCubeExpression->getAsSymbolNode(); + TIntermBinary *asBinary = samplerCubeExpression->getAsBinaryNode(); + + if (asBinary) + { + // Only constant indexing is supported in ES2.0. + ASSERT(asBinary->getOp() == EOpIndexDirect); + asSymbol = asBinary->getLeft()->getAsSymbolNode(); + } + + // Arrays of arrays are not available in ES2.0. + ASSERT(asSymbol != nullptr); + const TVariable *samplerCubeVar = &asSymbol->variable(); + + ASSERT(mSamplerMap.find(samplerCubeVar) != mSamplerMap.end()); + const TVariable *mappedSamplerVar = mSamplerMap.at(samplerCubeVar); + + TIntermTyped *mappedExpression = new TIntermSymbol(mappedSamplerVar); + if (asBinary) + { + mappedExpression = + new TIntermBinary(asBinary->getOp(), mappedExpression, asBinary->getRight()); + } + + return mappedExpression; + } + + bool convertBuiltinFunction(TIntermAggregate *node) + { + const TFunction *function = node->getFunction(); + if (!function->name().beginsWith("textureCube")) + { + return false; + } + + // All textureCube* functions are in the form: + // + // textureCube??(samplerCube, vec3, ??) + // + // They should be converted to: + // + // texture??(sampler2DArray, convertCoords(vec3), ??) + // + // We assume the target platform supports texture() functions (currently only used in + // Vulkan). + // + // The intrinsics map as follows: + // + // textureCube -> textureGrad + // textureCubeLod -> textureLod + // textureCubeLodEXT -> textureLod + // textureCubeGrad -> textureGrad + // textureCubeGradEXT -> textureGrad + // + // Note that dPdx and dPdy in textureCubeGrad* are vec3, while the textureGrad equivalent + // for sampler2DArray is vec2. The EXT_shader_texture_lod that introduces thid function + // says: + // + // > For the "Grad" functions, dPdx is the explicit derivative of P with respect + // > to window x, and similarly dPdy with respect to window y. ... For a cube map texture, + // > dPdx and dPdy are vec3. + // > + // > Let + // > + // > dSdx = dPdx.s; + // > dSdy = dPdy.s; + // > dTdx = dPdx.t; + // > dTdy = dPdy.t; + // > + // > and + // > + // > / 0.0; for two-dimensional texture + // > dRdx = ( + // > \ dPdx.p; for cube map texture + // > + // > / 0.0; for two-dimensional texture + // > dRdy = ( + // > \ dPdy.p; for cube map texture + // > + // > (See equation 3.12a in The OpenGL ES 2.0 Specification.) + // + // It's unclear to me what dRdx and dRdy are. EXT_gpu_shader4 that promotes this function + // has the following additional information: + // + // > For the "Cube" versions, the partial + // > derivatives ddx and ddy are assumed to be in the coordinate system used + // > before texture coordinates are projected onto the appropriate cube + // > face. The partial derivatives of the post-projection texture coordinates, + // > which are used for level-of-detail and anisotropic filtering + // > calculations, are derived from coord, ddx and ddy in an + // > implementation-dependent manner. + // + // The calculation of dPdx and dPdy is declared as implementation-dependent, so we have + // freedom to calculate it as fit, even if not precisely the same as hardware might. + + const char *substituteFunctionName = "textureGrad"; + bool isGrad = false; + bool isTranslatedGrad = true; + bool hasBias = false; + if (function->name().beginsWith("textureCubeLod")) + { + substituteFunctionName = "textureLod"; + isTranslatedGrad = false; + } + else if (function->name().beginsWith("textureCubeGrad")) + { + isGrad = true; + } + else if (!mIsFragmentShader) + { + substituteFunctionName = "texture"; + isTranslatedGrad = false; + } + + TIntermSequence *arguments = node->getSequence(); + ASSERT(arguments->size() >= 2); + + const TType *vec2Type = StaticType::GetBasic<EbtFloat, EbpHigh, 2>(); + const TType *vec3Type = StaticType::GetBasic<EbtFloat, EbpHigh, 3>(); + TIntermSymbol *uvl = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); + TIntermSymbol *dUVdx = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type)); + TIntermSymbol *dUVdy = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec2Type)); + + TIntermTyped *dPdx = nullptr; + TIntermTyped *dPdy = nullptr; + if (isGrad) + { + ASSERT(arguments->size() == 4); + dPdx = (*arguments)[2]->getAsTyped()->deepCopy(); + dPdy = (*arguments)[3]->getAsTyped()->deepCopy(); + } + else if (isTranslatedGrad && mIsFragmentShader && arguments->size() == 3) + { + hasBias = true; + } + else + { + dPdx = CreateZeroNode(*vec3Type); + dPdy = CreateZeroNode(*vec3Type); + } + + if (isTranslatedGrad && !mIsFragmentShader) + { + substituteFunctionName = "texture"; + isTranslatedGrad = false; + } + + // The function call to transform the coordinates, dPdx and dPdy. If not textureCubeGrad, + // the driver compiler will optimize out the unnecessary calculations. + TIntermSequence coordTransform; + coordTransform.push_back(CreateTempDeclarationNode(&dUVdx->variable())); + coordTransform.push_back(CreateTempDeclarationNode(&dUVdy->variable())); + TIntermTyped *coordTransformCall; + if (isGrad || !isTranslatedGrad) + { + coordTransformCall = createCoordTransformationCall( + (*arguments)[1]->getAsTyped()->deepCopy(), dPdx, dPdy, dUVdx, dUVdy); + } + else + { + coordTransformCall = createImplicitCoordTransformationCall( + (*arguments)[1]->getAsTyped()->deepCopy(), dUVdx, dUVdy); + } + coordTransform.push_back( + CreateTempInitDeclarationNode(&uvl->variable(), coordTransformCall)); + + TIntermTyped *dUVdxArg = dUVdx; + TIntermTyped *dUVdyArg = dUVdy; + if (hasBias) + { + const TType *floatType = StaticType::GetBasic<EbtFloat, EbpHigh>(); + TIntermTyped *bias = (*arguments)[2]->getAsTyped()->deepCopy(); + TIntermSequence exp2Args = {bias}; + TIntermTyped *exp2Call = + CreateBuiltInFunctionCallNode("exp2", &exp2Args, *mSymbolTable, 100); + TIntermSymbol *biasFac = new TIntermSymbol(CreateTempVariable(mSymbolTable, floatType)); + coordTransform.push_back(CreateTempInitDeclarationNode(&biasFac->variable(), exp2Call)); + dUVdxArg = + new TIntermBinary(EOpVectorTimesScalar, biasFac->deepCopy(), dUVdx->deepCopy()); + dUVdyArg = + new TIntermBinary(EOpVectorTimesScalar, biasFac->deepCopy(), dUVdy->deepCopy()); + } + + insertStatementsInParentBlock(coordTransform); + + TIntermSequence substituteArguments; + // Replace the first argument (samplerCube) with the sampler2DArray. + substituteArguments.push_back(getMappedSamplerExpression((*arguments)[0])); + // Replace the second argument with the coordination transformation. + substituteArguments.push_back(uvl->deepCopy()); + if (isTranslatedGrad) + { + substituteArguments.push_back(dUVdxArg->deepCopy()); + substituteArguments.push_back(dUVdyArg->deepCopy()); + } + else + { + // Pass the rest of the parameters as is. + for (size_t argIndex = 2; argIndex < arguments->size(); ++argIndex) + { + substituteArguments.push_back((*arguments)[argIndex]->getAsTyped()->deepCopy()); + } + } + + TIntermTyped *substituteCall = CreateBuiltInFunctionCallNode( + substituteFunctionName, &substituteArguments, *mSymbolTable, 300); + + queueReplacement(substituteCall, OriginalNode::IS_DROPPED); + + return true; + } + + // A map from the samplerCube variable to the sampler2DArray one. + angle::HashMap<const TVariable *, const TVariable *> mSamplerMap; + + // A helper function to convert xyz coordinates passed to a cube map sampling function into the + // array layer (cube map face) and uv coordinates. + TFunction *mCubeXYZToArrayUVL; + // A specialized version of the same function which uses implicit derivatives. + TFunction *mCubeXYZToArrayUVLImplicit; + + bool mIsFragmentShader; + + // Stored to be put before the first function after the pass. + TIntermFunctionDefinition *mCoordTranslationFunctionDecl; + TIntermFunctionDefinition *mCoordTranslationFunctionImplicitDecl; +}; +} // anonymous namespace + +bool RewriteCubeMapSamplersAs2DArray(TCompiler *compiler, + TIntermBlock *root, + TSymbolTable *symbolTable, + bool isFragmentShader) +{ + RewriteCubeMapSamplersAs2DArrayTraverser traverser(symbolTable, isFragmentShader); + root->traverse(&traverser); + + TIntermFunctionDefinition *coordTranslationFunctionDecl = + traverser.getCoordTranslationFunctionDecl(); + TIntermFunctionDefinition *coordTranslationFunctionDeclImplicit = + traverser.getCoordTranslationFunctionDeclImplicit(); + size_t firstFunctionIndex = FindFirstFunctionDefinitionIndex(root); + if (coordTranslationFunctionDecl) + { + root->insertChildNodes(firstFunctionIndex, TIntermSequence({coordTranslationFunctionDecl})); + } + if (coordTranslationFunctionDeclImplicit) + { + root->insertChildNodes(firstFunctionIndex, + TIntermSequence({coordTranslationFunctionDeclImplicit})); + } + + return traverser.updateTree(compiler, root); +} + +} // namespace sh |