Path: blob/main_old/src/compiler/translator/ASTMetadataHLSL.cpp
1693 views
//1// Copyright 2002 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//56// Analysis of the AST needed for HLSL generation78#include "compiler/translator/ASTMetadataHLSL.h"910#include "compiler/translator/CallDAG.h"11#include "compiler/translator/SymbolTable.h"12#include "compiler/translator/tree_util/IntermTraverse.h"1314namespace sh15{1617namespace18{1920// Class used to traverse the AST of a function definition, checking if the21// function uses a gradient, and writing the set of control flow using gradients.22// It assumes that the analysis has already been made for the function's23// callees.24class PullGradient : public TIntermTraverser25{26public:27PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)28: TIntermTraverser(true, false, true),29mMetadataList(metadataList),30mMetadata(&(*metadataList)[index]),31mIndex(index),32mDag(dag)33{34ASSERT(index < metadataList->size());3536// ESSL 100 builtin gradient functions37mGradientBuiltinFunctions.insert(ImmutableString("texture2D"));38mGradientBuiltinFunctions.insert(ImmutableString("texture2DProj"));39mGradientBuiltinFunctions.insert(ImmutableString("textureCube"));4041// ESSL 300 builtin gradient functions42mGradientBuiltinFunctions.insert(ImmutableString("dFdx"));43mGradientBuiltinFunctions.insert(ImmutableString("dFdy"));44mGradientBuiltinFunctions.insert(ImmutableString("fwidth"));45mGradientBuiltinFunctions.insert(ImmutableString("texture"));46mGradientBuiltinFunctions.insert(ImmutableString("textureProj"));47mGradientBuiltinFunctions.insert(ImmutableString("textureOffset"));48mGradientBuiltinFunctions.insert(ImmutableString("textureProjOffset"));4950// ESSL 310 doesn't add builtin gradient functions51}5253void traverse(TIntermFunctionDefinition *node)54{55node->traverse(this);56ASSERT(mParents.empty());57}5859// Called when a gradient operation or a call to a function using a gradient is found.60void onGradient()61{62mMetadata->mUsesGradient = true;63// Mark the latest control flow as using a gradient.64if (!mParents.empty())65{66mMetadata->mControlFlowsContainingGradient.insert(mParents.back());67}68}6970void visitControlFlow(Visit visit, TIntermNode *node)71{72if (visit == PreVisit)73{74mParents.push_back(node);75}76else if (visit == PostVisit)77{78ASSERT(mParents.back() == node);79mParents.pop_back();80// A control flow's using a gradient means its parents are too.81if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())82{83mMetadata->mControlFlowsContainingGradient.insert(mParents.back());84}85}86}8788bool visitLoop(Visit visit, TIntermLoop *loop) override89{90visitControlFlow(visit, loop);91return true;92}9394bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override95{96visitControlFlow(visit, ifElse);97return true;98}99100bool visitAggregate(Visit visit, TIntermAggregate *node) override101{102if (visit == PreVisit)103{104if (node->getOp() == EOpCallFunctionInAST)105{106size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());107ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);108109if ((*mMetadataList)[calleeIndex].mUsesGradient)110{111onGradient();112}113}114else if (BuiltInGroup::IsBuiltIn(node->getOp()) && !BuiltInGroup::IsMath(node->getOp()))115{116if (mGradientBuiltinFunctions.find(node->getFunction()->name()) !=117mGradientBuiltinFunctions.end())118{119onGradient();120}121}122}123124return true;125}126127private:128MetadataList *mMetadataList;129ASTMetadataHLSL *mMetadata;130size_t mIndex;131const CallDAG &mDag;132133// Contains a stack of the control flow nodes that are parents of the node being134// currently visited. It is used to mark control flows using a gradient.135std::vector<TIntermNode *> mParents;136137// A list of builtin functions that use gradients138std::set<ImmutableString> mGradientBuiltinFunctions;139};140141// Traverses the AST of a function definition to compute the the discontinuous loops142// and the if statements containing gradient loops. It assumes that the gradient loops143// (loops that contain a gradient) have already been computed and that it has already144// traversed the current function's callees.145class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser146{147public:148PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,149size_t index,150const CallDAG &dag)151: TIntermTraverser(true, false, true),152mMetadataList(metadataList),153mMetadata(&(*metadataList)[index]),154mIndex(index),155mDag(dag)156{}157158void traverse(TIntermFunctionDefinition *node)159{160node->traverse(this);161ASSERT(mLoopsAndSwitches.empty());162ASSERT(mIfs.empty());163}164165// Called when traversing a gradient loop or a call to a function with a166// gradient loop in its call graph.167void onGradientLoop()168{169mMetadata->mHasGradientLoopInCallGraph = true;170// Mark the latest if as using a discontinuous loop.171if (!mIfs.empty())172{173mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());174}175}176177bool visitLoop(Visit visit, TIntermLoop *loop) override178{179if (visit == PreVisit)180{181mLoopsAndSwitches.push_back(loop);182183if (mMetadata->hasGradientInCallGraph(loop))184{185onGradientLoop();186}187}188else if (visit == PostVisit)189{190ASSERT(mLoopsAndSwitches.back() == loop);191mLoopsAndSwitches.pop_back();192}193194return true;195}196197bool visitIfElse(Visit visit, TIntermIfElse *node) override198{199if (visit == PreVisit)200{201mIfs.push_back(node);202}203else if (visit == PostVisit)204{205ASSERT(mIfs.back() == node);206mIfs.pop_back();207// An if using a discontinuous loop means its parents ifs are also discontinuous.208if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())209{210mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());211}212}213214return true;215}216217bool visitBranch(Visit visit, TIntermBranch *node) override218{219if (visit == PreVisit)220{221switch (node->getFlowOp())222{223case EOpBreak:224{225ASSERT(!mLoopsAndSwitches.empty());226TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();227if (loop != nullptr)228{229mMetadata->mDiscontinuousLoops.insert(loop);230}231}232break;233case EOpContinue:234{235ASSERT(!mLoopsAndSwitches.empty());236TIntermLoop *loop = nullptr;237size_t i = mLoopsAndSwitches.size();238while (loop == nullptr && i > 0)239{240--i;241loop = mLoopsAndSwitches.at(i)->getAsLoopNode();242}243ASSERT(loop != nullptr);244mMetadata->mDiscontinuousLoops.insert(loop);245}246break;247case EOpKill:248case EOpReturn:249// A return or discard jumps out of all the enclosing loops250if (!mLoopsAndSwitches.empty())251{252for (TIntermNode *intermNode : mLoopsAndSwitches)253{254TIntermLoop *loop = intermNode->getAsLoopNode();255if (loop)256{257mMetadata->mDiscontinuousLoops.insert(loop);258}259}260}261break;262default:263UNREACHABLE();264}265}266267return true;268}269270bool visitAggregate(Visit visit, TIntermAggregate *node) override271{272if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)273{274size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());275ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);276277if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)278{279onGradientLoop();280}281}282283return true;284}285286bool visitSwitch(Visit visit, TIntermSwitch *node) override287{288if (visit == PreVisit)289{290mLoopsAndSwitches.push_back(node);291}292else if (visit == PostVisit)293{294ASSERT(mLoopsAndSwitches.back() == node);295mLoopsAndSwitches.pop_back();296}297return true;298}299300private:301MetadataList *mMetadataList;302ASTMetadataHLSL *mMetadata;303size_t mIndex;304const CallDAG &mDag;305306std::vector<TIntermNode *> mLoopsAndSwitches;307std::vector<TIntermIfElse *> mIfs;308};309310// Tags all the functions called in a discontinuous loop311class PushDiscontinuousLoops : public TIntermTraverser312{313public:314PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)315: TIntermTraverser(true, true, true),316mMetadataList(metadataList),317mMetadata(&(*metadataList)[index]),318mIndex(index),319mDag(dag),320mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)321{}322323void traverse(TIntermFunctionDefinition *node)324{325node->traverse(this);326ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));327}328329bool visitLoop(Visit visit, TIntermLoop *loop) override330{331bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;332333if (visit == PreVisit && isDiscontinuous)334{335mNestedDiscont++;336}337else if (visit == PostVisit && isDiscontinuous)338{339mNestedDiscont--;340}341342return true;343}344345bool visitAggregate(Visit visit, TIntermAggregate *node) override346{347switch (node->getOp())348{349case EOpCallFunctionInAST:350if (visit == PreVisit && mNestedDiscont > 0)351{352size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());353ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);354355(*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;356}357break;358default:359break;360}361return true;362}363364private:365MetadataList *mMetadataList;366ASTMetadataHLSL *mMetadata;367size_t mIndex;368const CallDAG &mDag;369370int mNestedDiscont;371};372} // namespace373374bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)375{376return mControlFlowsContainingGradient.count(node) > 0;377}378379bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)380{381return mIfsContainingGradientLoop.count(node) > 0;382}383384MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)385{386MetadataList metadataList(callDag.size());387388// Compute all the information related to when gradient operations are used.389// We want to know for each function and control flow operation if they have390// a gradient operation in their call graph (shortened to "using a gradient"391// in the rest of the file).392//393// This computation is logically split in three steps:394// 1 - For each function compute if it uses a gradient in its body, ignoring395// calls to other user-defined functions.396// 2 - For each function determine if it uses a gradient in its call graph,397// using the result of step 1 and the CallDAG to know its callees.398// 3 - For each control flow statement of each function, check if it uses a399// gradient in the function's body, or if it calls a user-defined function that400// uses a gradient.401//402// We take advantage of the call graph being a DAG and instead compute 1, 2 and 3403// for leaves first, then going down the tree. This is correct because 1 doesn't404// depend on other functions, and 2 and 3 depend only on callees.405for (size_t i = 0; i < callDag.size(); i++)406{407PullGradient pull(&metadataList, i, callDag);408pull.traverse(callDag.getRecordFromIndex(i).node);409}410411// Compute which loops are discontinuous and which function are called in412// these loops. The same way computing gradient usage is a "pull" process,413// computing "bing used in a discont. loop" is a push process. However we also414// need to know what ifs have a discontinuous loop inside so we do the same type415// of callgraph analysis as for the gradient.416417// First compute which loops are discontinuous (no specific order) and pull418// the ifs and functions using a gradient loop.419for (size_t i = 0; i < callDag.size(); i++)420{421PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);422pull.traverse(callDag.getRecordFromIndex(i).node);423}424425// Then push the information to callees, either from the a local discontinuous426// loop or from the caller being called in a discontinuous loop already427for (size_t i = callDag.size(); i-- > 0;)428{429PushDiscontinuousLoops push(&metadataList, i, callDag);430push.traverse(callDag.getRecordFromIndex(i).node);431}432433// We create "Lod0" version of functions with the gradient operations replaced434// by non-gradient operations so that the D3D compiler is happier with discont435// loops.436for (auto &metadata : metadataList)437{438metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;439}440441return metadataList;442}443444} // namespace sh445446447