| // |
| // 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. |
| // |
| |
| #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(TIntermBlock *block, |
| TIntermTyped *x, |
| TIntermTyped *y, |
| TIntermTyped *z, |
| TIntermTyped *uc, |
| TIntermTyped *vc) |
| { |
| // uc = -sign(x)*z |
| // vc = -y |
| TIntermTyped *signX = new TIntermUnary(EOpSign, x->deepCopy(), nullptr); |
| |
| 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)); |
| dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f)); |
| 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(TIntermBlock *block, |
| TIntermTyped *x, |
| TIntermTyped *y, |
| TIntermTyped *z, |
| TIntermTyped *uc, |
| TIntermTyped *vc) |
| { |
| // uc = x |
| // vc = sign(y)*z |
| TIntermTyped *signY = new TIntermUnary(EOpSign, y->deepCopy(), nullptr); |
| |
| 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)); |
| dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f)); |
| 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(TIntermBlock *block, |
| TIntermTyped *x, |
| TIntermTyped *y, |
| TIntermTyped *z, |
| TIntermTyped *uc, |
| TIntermTyped *vc) |
| { |
| // uc = size(z)*x |
| // vc = -y |
| TIntermTyped *signZ = new TIntermUnary(EOpSign, z->deepCopy(), nullptr); |
| |
| 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)); |
| dvValue = new TIntermBinary(EOpMul, dvValue, CreateFloatNode(0.5f)); |
| 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, true, true, symbolTable), |
| mCubeXYZToArrayUVL(nullptr), |
| mCubeXYZToArrayUVLImplicit(nullptr), |
| mIsFragmentShader(isFragmentShader), |
| mCoordTranslationFunctionDecl(nullptr), |
| mCoordTranslationFunctionImplicitDecl(nullptr) |
| {} |
| |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override |
| { |
| if (visit != PreVisit) |
| { |
| return true; |
| } |
| |
| 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; |
| } |
| |
| void visitFunctionPrototype(TIntermFunctionPrototype *node) override |
| { |
| const TFunction *function = node->getFunction(); |
| // Go over the parameters and replace the samplerCube arguments with a sampler2DArray. |
| mRetyper.visitFunctionPrototype(); |
| for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex) |
| { |
| const TVariable *param = function->getParam(paramIndex); |
| TVariable *replacement = convertFunctionParameter(node, param); |
| if (replacement) |
| { |
| mRetyper.replaceFunctionParam(param, replacement); |
| } |
| } |
| |
| TIntermFunctionPrototype *replacementPrototype = |
| mRetyper.convertFunctionPrototype(mSymbolTable, function); |
| if (replacementPrototype) |
| { |
| queueReplacement(replacementPrototype, OriginalNode::IS_DROPPED); |
| } |
| } |
| |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override |
| { |
| if (visit == PreVisit) |
| { |
| mRetyper.preVisitAggregate(); |
| } |
| |
| if (visit != PostVisit) |
| { |
| return true; |
| } |
| |
| if (node->getOp() == EOpCallBuiltInFunction) |
| { |
| convertBuiltinFunction(node); |
| } |
| else if (node->getOp() == EOpCallFunctionInAST) |
| { |
| TIntermAggregate *substituteCall = mRetyper.convertASTFunction(node); |
| if (substituteCall) |
| { |
| queueReplacement(substituteCall, OriginalNode::IS_DROPPED); |
| } |
| } |
| mRetyper.postVisitAggregate(); |
| |
| return true; |
| } |
| |
| void visitSymbol(TIntermSymbol *symbol) override |
| { |
| if (!symbol->getType().isSamplerCube()) |
| { |
| return; |
| } |
| |
| const TVariable *samplerCubeVar = &symbol->variable(); |
| |
| TIntermTyped *sampler2DArrayVar = |
| new TIntermSymbol(mRetyper.getVariableReplacement(samplerCubeVar)); |
| ASSERT(sampler2DArrayVar != nullptr); |
| |
| TIntermNode *argument = symbol; |
| |
| // We need to replace the whole function call argument with the symbol replaced. The |
| // argument can either be the sampler (array) itself, or a subscript into a sampler array. |
| TIntermBinary *arrayExpression = getParentNode()->getAsBinaryNode(); |
| if (arrayExpression) |
| { |
| ASSERT(arrayExpression->getOp() == EOpIndexDirect || |
| arrayExpression->getOp() == EOpIndexIndirect); |
| |
| argument = arrayExpression; |
| |
| sampler2DArrayVar = new TIntermBinary(arrayExpression->getOp(), sampler2DArrayVar, |
| arrayExpression->getRight()->deepCopy()); |
| } |
| |
| mRetyper.replaceFunctionCallArg(argument, sampler2DArrayVar); |
| } |
| |
| 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, SymbolType::UserDefined); |
| |
| TIntermDeclaration *sampler2DArrayDecl = new TIntermDeclaration(); |
| sampler2DArrayDecl->appendDeclarator(new TIntermSymbol(sampler2DArrayVar)); |
| |
| TIntermSequence replacement; |
| replacement.push_back(sampler2DArrayDecl); |
| mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, replacement); |
| |
| // Remember the sampler2DArray variable. |
| mRetyper.replaceGlobalVariable(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, 3>(); |
| TVariable *pVar = |
| new TVariable(mSymbolTable, ImmutableString("P"), vec3Type, SymbolType::AngleInternal); |
| TVariable *dPdxVar = new TVariable(mSymbolTable, ImmutableString("dPdx"), vec3Type, |
| SymbolType::AngleInternal); |
| TVariable *dPdyVar = new TVariable(mSymbolTable, ImmutableString("dPdy"), vec3Type, |
| SymbolType::AngleInternal); |
| |
| const TType *vec2Type = StaticType::GetBasic<EbtFloat, 2>(); |
| TType *outVec2Type = new TType(*vec2Type); |
| outVec2Type->setQualifier(EvqOut); |
| |
| 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>(); |
| |
| 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(), new TIntermUnary(EOpAbs, x->deepCopy(), nullptr)); |
| TIntermDeclaration *absYDecl = CreateTempInitDeclarationNode( |
| &absY->variable(), new TIntermUnary(EOpAbs, y->deepCopy(), nullptr)); |
| TIntermDeclaration *absZDecl = CreateTempInitDeclarationNode( |
| &absZ->variable(), new TIntermUnary(EOpAbs, z->deepCopy(), nullptr)); |
| |
| 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, 3, 3>(); |
| TIntermSymbol *recipOuter = new TIntermSymbol(CreateTempVariable(mSymbolTable, mat3Type)); |
| |
| TIntermTyped *pRecip = new TIntermBinary(EOpDiv, CreateFloatNode(1.0), p->deepCopy()); |
| TIntermSymbol *pRecipVar = new TIntermSymbol(CreateTempVariable(mSymbolTable, vec3Type)); |
| |
| body->appendStatement(CreateTempInitDeclarationNode(&pRecipVar->variable(), pRecip)); |
| |
| TIntermDeclaration *recipOuterDecl = CreateTempInitDeclarationNode( |
| &recipOuter->variable(), |
| CreateBuiltInFunctionCallNode( |
| "outerProduct", |
| new TIntermSequence( |
| {p->deepCopy(), new TIntermBinary(EOpVectorTimesScalar, CreateFloatNode(0.5), |
| pRecipVar->deepCopy())}), |
| *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(), |
| new TIntermUnary(EOpDFdx, IndexDirect(recipOuter, 0)->deepCopy(), nullptr)); |
| TIntermDeclaration *dPDYdxDecl = CreateTempInitDeclarationNode( |
| &dPDYdx->variable(), |
| new TIntermUnary(EOpDFdx, IndexDirect(recipOuter, 1)->deepCopy(), nullptr)); |
| TIntermDeclaration *dPDZdxDecl = CreateTempInitDeclarationNode( |
| &dPDZdx->variable(), |
| new TIntermUnary(EOpDFdx, IndexDirect(recipOuter, 2)->deepCopy(), nullptr)); |
| TIntermDeclaration *dPDXdyDecl = CreateTempInitDeclarationNode( |
| &dPDXdy->variable(), |
| new TIntermUnary(EOpDFdy, IndexDirect(recipOuter, 0)->deepCopy(), nullptr)); |
| TIntermDeclaration *dPDYdyDecl = CreateTempInitDeclarationNode( |
| &dPDYdy->variable(), |
| new TIntermUnary(EOpDFdy, IndexDirect(recipOuter, 1)->deepCopy(), nullptr)); |
| TIntermDeclaration *dPDZdyDecl = CreateTempInitDeclarationNode( |
| &dPDZdy->variable(), |
| new TIntermUnary(EOpDFdy, IndexDirect(recipOuter, 2)->deepCopy(), nullptr)); |
| |
| 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|)) |
| TIntermTyped *maxYZ = CreateBuiltInFunctionCallNode( |
| "max", new TIntermSequence({absY->deepCopy(), absZ->deepCopy()}), *mSymbolTable, 100); |
| TIntermTyped *maValue = CreateBuiltInFunctionCallNode( |
| "max", new TIntermSequence({absX->deepCopy(), maxYZ}), *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) |
| TIntermTyped *xl = |
| TIntermAggregate::CreateConstructor(*floatType, new TIntermSequence({isNegX})); |
| |
| TIntermBlock *calculateXL = new TIntermBlock; |
| calculateXL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), xl)); |
| |
| // The case where y is major: |
| // layer = 2 + float(y < 0) |
| TIntermTyped *yl = new TIntermBinary( |
| EOpAdd, CreateFloatNode(2.0f), |
| TIntermAggregate::CreateConstructor(*floatType, new TIntermSequence({isNegY}))); |
| |
| TIntermBlock *calculateYL = new TIntermBlock; |
| calculateYL->appendStatement(new TIntermBinary(EOpAssign, l->deepCopy(), yl)); |
| |
| // The case where z is major: |
| // layer = 4 + float(z < 0) |
| TIntermTyped *zl = new TIntermBinary( |
| EOpAdd, CreateFloatNode(4.0f), |
| TIntermAggregate::CreateConstructor(*floatType, new TIntermSequence({isNegZ}))); |
| |
| 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)); |
| isYMajor = new TIntermBinary(EOpLessThan, l->deepCopy(), CreateFloatNode(3.5f)); |
| |
| 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(calculateXUcVc, x, y, z, uc, vc); |
| |
| TIntermBlock *calculateYUcVc = new TIntermBlock; |
| calculateYUcVc->appendStatement( |
| new TIntermBinary(EOpAssign, ma->deepCopy(), absY->deepCopy())); |
| TransformYMajor(calculateYUcVc, x, y, z, uc, vc); |
| |
| TIntermBlock *calculateZUcVc = new TIntermBlock; |
| calculateZUcVc->appendStatement( |
| new TIntermBinary(EOpAssign, ma->deepCopy(), absZ->deepCopy())); |
| TransformZMajor(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), 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), ucDivMa); |
| TIntermTyped *vNormalized = new TIntermBinary(EOpAdd, CreateFloatNode(0.5f), vcDivMa); |
| |
| body->appendStatement(new TIntermBinary(EOpAssign, uc->deepCopy(), uNormalized)); |
| body->appendStatement(new TIntermBinary(EOpAssign, vc->deepCopy(), vNormalized)); |
| |
| TIntermTyped *dUVdxValue = |
| TIntermAggregate::CreateConstructor(*vec2Type, new TIntermSequence({dUdx, dVdx})); |
| TIntermTyped *dUVdyValue = |
| TIntermAggregate::CreateConstructor(*vec2Type, new TIntermSequence({dUdy, dVdy})); |
| |
| body->appendStatement(new TIntermBinary(EOpAssign, dUVdx, dUVdxValue)); |
| body->appendStatement(new TIntermBinary(EOpAssign, dUVdy, dUVdyValue)); |
| |
| // return vec3(u, v, l) |
| TIntermBranch *returnStatement = new TIntermBranch( |
| EOpReturn, TIntermAggregate::CreateConstructor( |
| *vec3Type, new TIntermSequence({uc->deepCopy(), vc->deepCopy(), l}))); |
| 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 = new TIntermSequence({P, dPdx, dPdy, dUVdx, dUVdy}); |
| return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVL, args); |
| } |
| |
| TIntermTyped *createImplicitCoordTransformationCall(TIntermTyped *P, |
| TIntermTyped *dUVdx, |
| TIntermTyped *dUVdy) |
| { |
| const TType *vec3Type = StaticType::GetBasic<EbtFloat, 3>(); |
| TIntermTyped *dPdx = CreateZeroNode(*vec3Type); |
| TIntermTyped *dPdy = CreateZeroNode(*vec3Type); |
| TIntermSequence *args = new TIntermSequence({P, dPdx, dPdy, dUVdx, dUVdy}); |
| return TIntermAggregate::CreateFunctionCall(*mCubeXYZToArrayUVLImplicit, args); |
| } |
| |
| TVariable *convertFunctionParameter(TIntermNode *parent, const TVariable *param) |
| { |
| if (!param->getType().isSamplerCube()) |
| { |
| return nullptr; |
| } |
| |
| TType *newType = new TType(param->getType()); |
| newType->setBasicType(EbtSampler2DArray); |
| |
| TVariable *replacementVar = |
| new TVariable(mSymbolTable, param->name(), newType, SymbolType::UserDefined); |
| |
| return replacementVar; |
| } |
| |
| void convertBuiltinFunction(TIntermAggregate *node) |
| { |
| const TFunction *function = node->getFunction(); |
| if (!function->name().beginsWith("textureCube")) |
| { |
| return; |
| } |
| |
| // 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, 2>(); |
| const TType *vec3Type = StaticType::GetBasic<EbtFloat, 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 = new TIntermSequence; |
| 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>(); |
| TIntermTyped *bias = (*arguments)[2]->getAsTyped()->deepCopy(); |
| TIntermTyped *exp2Call = CreateBuiltInFunctionCallNode( |
| "exp2", new TIntermSequence({bias}), *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 = new TIntermSequence; |
| // Replace the first argument (samplerCube) with the sampler2DArray. |
| substituteArguments->push_back(mRetyper.getFunctionCallArgReplacement((*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); |
| } |
| |
| RetypeOpaqueVariablesHelper mRetyper; |
| |
| // 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); |
| if (!traverser.updateTree(compiler, root)) |
| { |
| return false; |
| } |
| |
| 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 compiler->validateAST(root); |
| } |
| |
| } // namespace sh |